From ca84cca3d1366ea127ccf51f0e88547c65c87659 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Wed, 17 Dec 2008 16:24:47 +0000 Subject: [PATCH] Import xen-unstable, changeset 18641 6bf61b830153 --- COPYING | 370 + Config.mk | 114 + Makefile | 273 + README | 229 + buildconfigs/Rules.mk | 42 + buildconfigs/enable-xen-config | 48 + buildconfigs/interface.exclude | 7 + buildconfigs/ketchup | 742 + buildconfigs/mk.linux-2.6 | 10 + buildconfigs/mk.linux-2.6-common | 154 + buildconfigs/mk.linux-2.6-git | 2 + buildconfigs/mk.linux-2.6-mm | 2 + buildconfigs/mk.linux-2.6-native | 5 + buildconfigs/mk.linux-2.6-rc | 2 + buildconfigs/mk.linux-2.6-tip | 2 + buildconfigs/mk.linux-2.6-tip-latest | 14 + buildconfigs/mk.linux-2.6-xen | 6 + buildconfigs/mk.linux-2.6-xen0 | 2 + buildconfigs/mk.linux-2.6-xenU | 2 + buildconfigs/mk.linux-2.6.5-SLES-xen | 10 + buildconfigs/mk.linux-2.6.9-RHEL-xen | 10 + buildconfigs/select-linux-arch | 30 + buildconfigs/select-linux-image | 33 + buildconfigs/select-repository | 69 + buildconfigs/src.git-clone | 60 + buildconfigs/src.hg-clone | 32 + buildconfigs/src.tarball | 19 + config/FreeBSD.mk | 1 + config/Linux.mk | 8 + config/MiniOS.mk | 9 + config/NetBSD.mk | 4 + config/OpenBSD.mk | 1 + config/StdGNU.mk | 51 + config/SunOS.mk | 55 + config/ia64.mk | 6 + config/x86_32.mk | 15 + config/x86_64.mk | 23 + docs/ChangeLog | 130 + docs/Docs.mk | 9 + docs/Doxyfile | 1218 ++ docs/Doxyfilter | 16 + docs/Makefile | 116 + docs/README.xen-bugtool | 16 + docs/check_pkgs | 20 + docs/figs/acm_ezpolicy_gui.eps | 1756 ++ docs/figs/acm_overview.eps | 1463 ++ docs/figs/xenlogo.eps | 1479 ++ docs/html.sty | 887 + docs/man/xend-config.sxp.pod.5 | 148 + docs/man/xm.pod.1 | 1399 ++ docs/man/xmdomain.cfg.pod.5 | 360 + docs/misc/VMX_changes.txt | 90 + docs/misc/blkif-drivers-explained.txt | 485 + docs/misc/crashdb.txt | 63 + docs/misc/dump-core-format.txt | 243 + docs/misc/grant-tables.txt | 325 + docs/misc/hg-cheatsheet.txt | 438 + docs/misc/kexec_and_kdump.txt | 213 + docs/misc/sedf_scheduler_mini-HOWTO.txt | 44 + docs/misc/vtd.txt | 133 + docs/misc/vtpm.txt | 152 + docs/misc/xen_config.html | 194 + docs/misc/xend.tex | 435 + docs/misc/xenstore.txt | 319 + docs/pythfilter.py | 658 + docs/src/interface.tex | 2257 +++ docs/src/user.tex | 5293 +++++ docs/xen-api/Makefile | 44 + docs/xen-api/coversheet.tex | 63 + docs/xen-api/fdl.tex | 488 + docs/xen-api/presentation.tex | 146 + docs/xen-api/revision-history.tex | 60 + docs/xen-api/todo.tex | 135 + docs/xen-api/vm-lifecycle.tex | 43 + docs/xen-api/vm_lifecycle.dot | 17 + docs/xen-api/wire-protocol.tex | 304 + docs/xen-api/xen.eps | 44 + docs/xen-api/xenapi-coversheet.tex | 38 + docs/xen-api/xenapi-datamodel-graph.dot | 44 + docs/xen-api/xenapi-datamodel.tex | 16911 ++++++++++++++++ docs/xen-api/xenapi.tex | 58 + extras/mini-os/Config.mk | 57 + extras/mini-os/Makefile | 133 + extras/mini-os/README | 46 + extras/mini-os/app.lds | 11 + extras/mini-os/arch/ia64/Makefile | 63 + extras/mini-os/arch/ia64/__divdi3.S | 141 + extras/mini-os/arch/ia64/__udivdi3.S | 142 + extras/mini-os/arch/ia64/__udivsi3.S | 124 + extras/mini-os/arch/ia64/__umoddi3.S | 154 + extras/mini-os/arch/ia64/arch.mk | 20 + extras/mini-os/arch/ia64/common.c | 268 + extras/mini-os/arch/ia64/debug.c | 179 + extras/mini-os/arch/ia64/efi.c | 237 + extras/mini-os/arch/ia64/fw.S | 540 + extras/mini-os/arch/ia64/gen_off.c | 141 + extras/mini-os/arch/ia64/ia64.S | 237 + extras/mini-os/arch/ia64/ivt.S | 872 + extras/mini-os/arch/ia64/minios-ia64.lds | 86 + extras/mini-os/arch/ia64/mm.c | 162 + extras/mini-os/arch/ia64/sal.c | 103 + extras/mini-os/arch/ia64/sched.c | 79 + extras/mini-os/arch/ia64/time.c | 288 + extras/mini-os/arch/ia64/xencomm.c | 270 + extras/mini-os/arch/x86/Makefile | 31 + extras/mini-os/arch/x86/arch.mk | 22 + extras/mini-os/arch/x86/minios-x86_32.lds | 76 + extras/mini-os/arch/x86/minios-x86_64.lds | 76 + extras/mini-os/arch/x86/mm.c | 617 + extras/mini-os/arch/x86/sched.c | 136 + extras/mini-os/arch/x86/setup.c | 118 + extras/mini-os/arch/x86/time.c | 238 + extras/mini-os/arch/x86/traps.c | 333 + extras/mini-os/arch/x86/x86_32.S | 305 + extras/mini-os/arch/x86/x86_64.S | 410 + extras/mini-os/blkfront.c | 505 + extras/mini-os/console/console.c | 157 + extras/mini-os/console/xencons_ring.c | 145 + extras/mini-os/daytime.c | 67 + extras/mini-os/domain_config | 19 + extras/mini-os/events.c | 239 + extras/mini-os/fbfront.c | 597 + extras/mini-os/fs-front.c | 1264 ++ extras/mini-os/gntmap.c | 252 + extras/mini-os/gnttab.c | 207 + extras/mini-os/hypervisor.c | 121 + extras/mini-os/include/arch/cc.h | 87 + extras/mini-os/include/arch/perf.h | 15 + extras/mini-os/include/arch/sys_arch.h | 35 + extras/mini-os/include/blkfront.h | 42 + extras/mini-os/include/byteswap.h | 33 + extras/mini-os/include/console.h | 65 + extras/mini-os/include/ctype.h | 60 + extras/mini-os/include/err.h | 31 + extras/mini-os/include/errno-base.h | 39 + extras/mini-os/include/errno.h | 122 + extras/mini-os/include/events.h | 51 + extras/mini-os/include/fbfront.h | 45 + extras/mini-os/include/fcntl.h | 99 + extras/mini-os/include/fs.h | 56 + extras/mini-os/include/gntmap.h | 35 + extras/mini-os/include/gnttab.h | 16 + extras/mini-os/include/hypervisor.h | 49 + extras/mini-os/include/ia64/arch_limits.h | 12 + extras/mini-os/include/ia64/arch_mm.h | 43 + extras/mini-os/include/ia64/arch_sched.h | 90 + extras/mini-os/include/ia64/arch_spinlock.h | 61 + extras/mini-os/include/ia64/asm.h | 18 + extras/mini-os/include/ia64/atomic.h | 508 + extras/mini-os/include/ia64/efi.h | 396 + extras/mini-os/include/ia64/endian.h | 75 + extras/mini-os/include/ia64/hypercall-ia64.h | 236 + extras/mini-os/include/ia64/ia64_cpu.h | 776 + extras/mini-os/include/ia64/ia64_fpu.h | 99 + extras/mini-os/include/ia64/os.h | 311 + extras/mini-os/include/ia64/page.h | 110 + extras/mini-os/include/ia64/pal.h | 87 + extras/mini-os/include/ia64/privop.h | 97 + extras/mini-os/include/ia64/sal.h | 188 + extras/mini-os/include/ia64/traps.h | 54 + extras/mini-os/include/kernel.h | 7 + extras/mini-os/include/lib.h | 201 + extras/mini-os/include/linux/types.h | 5 + extras/mini-os/include/list.h | 190 + extras/mini-os/include/lwipopts.h | 23 + extras/mini-os/include/mm.h | 81 + extras/mini-os/include/netfront.h | 24 + extras/mini-os/include/pcifront.h | 28 + extras/mini-os/include/posix/arpa/inet.h | 7 + extras/mini-os/include/posix/dirent.h | 24 + extras/mini-os/include/posix/err.h | 15 + extras/mini-os/include/posix/fcntl.h | 11 + extras/mini-os/include/posix/limits.h | 46 + extras/mini-os/include/posix/netdb.h | 9 + extras/mini-os/include/posix/netinet/in.h | 7 + extras/mini-os/include/posix/netinet/tcp.h | 6 + extras/mini-os/include/posix/pthread.h | 64 + extras/mini-os/include/posix/signal.h | 10 + extras/mini-os/include/posix/stdlib.h | 8 + extras/mini-os/include/posix/strings.h | 8 + extras/mini-os/include/posix/sys/ioctl.h | 16 + extras/mini-os/include/posix/sys/mman.h | 22 + extras/mini-os/include/posix/sys/poll.h | 79 + extras/mini-os/include/posix/sys/select.h | 8 + extras/mini-os/include/posix/sys/socket.h | 31 + extras/mini-os/include/posix/sys/stat.h | 7 + extras/mini-os/include/posix/syslog.h | 37 + extras/mini-os/include/posix/termios.h | 87 + extras/mini-os/include/posix/time.h | 11 + extras/mini-os/include/posix/unistd.h | 10 + extras/mini-os/include/sched.h | 58 + extras/mini-os/include/semaphore.h | 111 + extras/mini-os/include/spinlock.h | 55 + extras/mini-os/include/sys/lock.h | 52 + extras/mini-os/include/sys/time.h | 42 + extras/mini-os/include/time.h | 63 + extras/mini-os/include/types.h | 93 + extras/mini-os/include/wait.h | 95 + extras/mini-os/include/waittypes.h | 26 + extras/mini-os/include/x86/arch_limits.h | 20 + extras/mini-os/include/x86/arch_mm.h | 226 + extras/mini-os/include/x86/arch_sched.h | 25 + extras/mini-os/include/x86/arch_spinlock.h | 94 + extras/mini-os/include/x86/os.h | 571 + extras/mini-os/include/x86/traps.h | 78 + .../include/x86/x86_32/hypercall-x86_32.h | 324 + .../include/x86/x86_64/hypercall-x86_64.h | 332 + extras/mini-os/include/xenbus.h | 97 + extras/mini-os/include/xmalloc.h | 44 + extras/mini-os/kernel.c | 601 + extras/mini-os/lib/ctype.c | 29 + extras/mini-os/lib/math.c | 427 + extras/mini-os/lib/printf.c | 793 + extras/mini-os/lib/string.c | 178 + extras/mini-os/lib/sys.c | 1335 ++ extras/mini-os/lib/xmalloc.c | 302 + extras/mini-os/lib/xs.c | 187 + extras/mini-os/lock.c | 111 + extras/mini-os/lwip-arch.c | 293 + extras/mini-os/lwip-net.c | 388 + extras/mini-os/main.c | 195 + extras/mini-os/minios.mk | 73 + extras/mini-os/mm.c | 443 + extras/mini-os/netfront.c | 625 + extras/mini-os/pcifront.c | 371 + extras/mini-os/sched.c | 301 + extras/mini-os/xenbus/xenbus.c | 761 + install.sh | 54 + stubdom/Makefile | 356 + stubdom/README | 152 + stubdom/c/Makefile | 11 + stubdom/c/main.c | 8 + stubdom/caml/Makefile | 23 + stubdom/caml/hello.ml | 4 + stubdom/caml/main-caml.c | 42 + stubdom/grub.patches/00cvs | 1382 ++ stubdom/grub.patches/10graphics.diff | 2299 +++ stubdom/grub.patches/20print_func.diff | 80 + stubdom/grub.patches/30savedefault.diff | 186 + .../grub.patches/40ext3_256byte_inode.diff | 114 + stubdom/grub.patches/50fs_fulldisk.diff | 72 + stubdom/grub.patches/99minios | 1495 ++ stubdom/grub/Makefile | 84 + stubdom/grub/boot-x86_32.S | 112 + stubdom/grub/boot-x86_64.S | 108 + stubdom/grub/config.h | 11 + stubdom/grub/kexec.c | 324 + stubdom/grub/mini-os.c | 704 + stubdom/grub/mini-os.h | 5 + stubdom/grub/osdep.h | 30 + stubdom/libpci.config.h | 5 + stubdom/libpci.config.mak | 7 + stubdom/lwip.patch-cvs | 2398 +++ stubdom/newlib.patch | 749 + stubdom/pciutils.patch | 299 + stubdom/stubdom-dm | 101 + tools/Makefile | 101 + tools/Rules.mk | 62 + tools/blktap/Makefile | 13 + tools/blktap/README | 122 + tools/blktap/drivers/Makefile | 66 + tools/blktap/drivers/aes.c | 1319 ++ tools/blktap/drivers/aes.h | 28 + tools/blktap/drivers/blk.h | 3 + tools/blktap/drivers/blk_linux.c | 42 + tools/blktap/drivers/blktapctrl.c | 877 + tools/blktap/drivers/blktapctrl.h | 36 + tools/blktap/drivers/blktapctrl_linux.c | 109 + tools/blktap/drivers/block-aio.c | 259 + tools/blktap/drivers/block-qcow.c | 1430 ++ tools/blktap/drivers/block-qcow2.c | 1955 ++ tools/blktap/drivers/block-ram.c | 295 + tools/blktap/drivers/block-sync.c | 242 + tools/blktap/drivers/block-vmdk.c | 428 + tools/blktap/drivers/bswap.h | 214 + tools/blktap/drivers/check_gcrypt | 14 + tools/blktap/drivers/img2qcow.c | 282 + tools/blktap/drivers/qcow-create.c | 120 + tools/blktap/drivers/qcow2raw.c | 348 + tools/blktap/drivers/tapaio.c | 357 + tools/blktap/drivers/tapaio.h | 108 + tools/blktap/drivers/tapdisk.c | 872 + tools/blktap/drivers/tapdisk.h | 269 + tools/blktap/lib/Makefile | 62 + tools/blktap/lib/blkif.c | 185 + tools/blktap/lib/blktaplib.h | 230 + tools/blktap/lib/list.h | 59 + tools/blktap/lib/xenbus.c | 434 + tools/blktap/lib/xs_api.c | 360 + tools/blktap/lib/xs_api.h | 50 + tools/check/Makefile | 19 + tools/check/README | 20 + tools/check/check_brctl | 13 + tools/check/check_crypto_lib | 11 + tools/check/check_curl | 13 + tools/check/check_iproute | 15 + tools/check/check_logging | 31 + tools/check/check_openssl_devel | 6 + tools/check/check_python | 9 + tools/check/check_python_devel | 12 + tools/check/check_python_xml | 7 + tools/check/check_udev | 19 + tools/check/check_x11_devel | 8 + tools/check/check_xgettext | 6 + tools/check/check_xml2 | 14 + tools/check/check_zlib_devel | 6 + tools/check/check_zlib_lib | 12 + tools/check/chk | 63 + tools/check/funcs.sh | 85 + tools/console/Makefile | 35 + tools/console/client/main.c | 298 + tools/console/daemon/io.c | 1118 + tools/console/daemon/io.h | 27 + tools/console/daemon/main.c | 179 + tools/console/daemon/utils.c | 144 + tools/console/daemon/utils.h | 46 + tools/console/testsuite/Makefile | 14 + tools/console/testsuite/README | 29 + tools/console/testsuite/console-dom0.c | 117 + tools/console/testsuite/console-domU.c | 76 + tools/console/testsuite/procpipe.c | 133 + tools/cross-install | 8 + tools/debugger/gdb/README | 38 + .../gdb/gdbserver/Makefile.in | 308 + .../gdb/gdbserver/configure | 4650 +++++ .../gdb/gdbserver/configure.in | 121 + .../gdb/gdbserver/configure.srv | 75 + .../gdb/gdbserver/linux-xen-low.c | 667 + .../gdb/gdbserver/server.c | 676 + .../gdb/gdb-6.2.1-xen-sparse/mkbuildtree | 115 + tools/debugger/gdb/gdbbuild | 28 + tools/debugger/xenitp/Makefile | 53 + tools/debugger/xenitp/README | 36 + tools/debugger/xenitp/cpu-ia64-opc.c | 615 + tools/debugger/xenitp/dis-asm.h | 548 + tools/debugger/xenitp/ia64-asmtab.c | 8774 ++++++++ tools/debugger/xenitp/ia64-asmtab.h | 148 + tools/debugger/xenitp/ia64-dis.c | 309 + tools/debugger/xenitp/ia64-gen.c | 2865 +++ tools/debugger/xenitp/ia64-opc-a.c | 419 + tools/debugger/xenitp/ia64-opc-b.c | 512 + tools/debugger/xenitp/ia64-opc-d.c | 34 + tools/debugger/xenitp/ia64-opc-f.c | 656 + tools/debugger/xenitp/ia64-opc-i.c | 338 + tools/debugger/xenitp/ia64-opc-m.c | 1118 + tools/debugger/xenitp/ia64-opc-x.c | 188 + tools/debugger/xenitp/ia64-opc.c | 727 + tools/debugger/xenitp/ia64-opc.h | 133 + tools/debugger/xenitp/ia64.h | 396 + tools/debugger/xenitp/xenitp.c | 1720 ++ tools/examples/Makefile | 132 + tools/examples/README | 51 + tools/examples/README.incompatibilities | 38 + tools/examples/blktap | 93 + tools/examples/block | 381 + tools/examples/block-common.sh | 116 + tools/examples/block-enbd | 27 + tools/examples/block-nbd | 27 + tools/examples/bochsrc | 20 + tools/examples/external-device-migrate | 98 + tools/examples/init.d/sysconfig.xendomains | 137 + tools/examples/init.d/xend | 66 + tools/examples/init.d/xendomains | 531 + tools/examples/locking.sh | 98 + tools/examples/logging.sh | 22 + tools/examples/network-bridge | 310 + tools/examples/network-nat | 119 + tools/examples/network-route | 27 + tools/examples/vif-bridge | 100 + tools/examples/vif-common.sh | 151 + tools/examples/vif-nat | 192 + tools/examples/vif-route | 56 + tools/examples/vnc/Xservers | 5 + tools/examples/vnc/Xvnc-xen | 53 + tools/examples/vscsi | 22 + tools/examples/vtpm | 22 + tools/examples/vtpm-common.sh | 448 + tools/examples/vtpm-delete | 18 + tools/examples/vtpm-hotplug-common.sh | 35 + tools/examples/vtpm-impl | 208 + tools/examples/vtpm-migration.sh | 19 + tools/examples/xen-backend.agent | 39 + tools/examples/xen-backend.rules | 9 + tools/examples/xen-hotplug-cleanup | 22 + tools/examples/xen-hotplug-common.sh | 93 + tools/examples/xen-network-common.sh | 118 + tools/examples/xen-script-common.sh | 44 + tools/examples/xend-config.sxp | 255 + tools/examples/xend-pci-permissive.sxp | 27 + tools/examples/xend-pci-quirks.sxp | 96 + tools/examples/xeninfo.pl | 284 + tools/examples/xm-config.xml | 45 + tools/examples/xmexample.hvm | 312 + tools/examples/xmexample.hvm-dm | 14 + tools/examples/xmexample.hvm-stubdom | 320 + tools/examples/xmexample.nbd | 26 + tools/examples/xmexample.pv-grub | 186 + tools/examples/xmexample.vti | 180 + tools/examples/xmexample1 | 211 + tools/examples/xmexample2 | 246 + tools/examples/xmexample3 | 232 + tools/firmware/Makefile | 33 + tools/firmware/Rules.mk | 21 + tools/firmware/etherboot/Config | 8 + tools/firmware/etherboot/Makefile | 38 + tools/firmware/etherboot/README | 28 + tools/firmware/etherboot/eb-roms.h | 6371 ++++++ .../etherboot/gpxe-git-snapshot.tar.gz | Bin 0 -> 1679784 bytes .../patches/boot_prompt_option.patch | 31 + tools/firmware/etherboot/patches/series | 1 + tools/firmware/hvmloader/32bitbios_support.c | 168 + tools/firmware/hvmloader/Makefile | 61 + tools/firmware/hvmloader/acpi/Makefile | 67 + tools/firmware/hvmloader/acpi/README | 24 + tools/firmware/hvmloader/acpi/acpi2_0.h | 396 + tools/firmware/hvmloader/acpi/build.c | 397 + tools/firmware/hvmloader/acpi/dsdt.asl | 875 + tools/firmware/hvmloader/acpi/dsdt.c | 567 + tools/firmware/hvmloader/acpi/ssdt_pm.asl | 423 + tools/firmware/hvmloader/acpi/ssdt_pm.h | 202 + tools/firmware/hvmloader/acpi/ssdt_tpm.asl | 29 + tools/firmware/hvmloader/acpi/ssdt_tpm.h | 25 + tools/firmware/hvmloader/acpi/static_tables.c | 147 + tools/firmware/hvmloader/apic_regs.h | 108 + tools/firmware/hvmloader/cacheattr.c | 99 + tools/firmware/hvmloader/config.h | 44 + tools/firmware/hvmloader/e820.h | 23 + tools/firmware/hvmloader/hvmloader.c | 563 + tools/firmware/hvmloader/hypercall.h | 183 + tools/firmware/hvmloader/mkhex | 26 + tools/firmware/hvmloader/mp_tables.c | 365 + tools/firmware/hvmloader/option_rom.h | 50 + tools/firmware/hvmloader/pci_regs.h | 108 + tools/firmware/hvmloader/smbios.c | 604 + tools/firmware/hvmloader/smbios_types.h | 182 + tools/firmware/hvmloader/smp.c | 132 + tools/firmware/hvmloader/tests.c | 164 + tools/firmware/hvmloader/util.c | 658 + tools/firmware/hvmloader/util.h | 156 + tools/firmware/rombios/32bit/32bitbios.c | 53 + tools/firmware/rombios/32bit/Makefile | 29 + tools/firmware/rombios/32bit/mkhex | 26 + tools/firmware/rombios/32bit/rombios_compat.h | 92 + tools/firmware/rombios/32bit/tcgbios/Makefile | 18 + .../firmware/rombios/32bit/tcgbios/tcgbios.c | 1514 ++ .../firmware/rombios/32bit/tcgbios/tcgbios.h | 249 + .../rombios/32bit/tcgbios/tpm_drivers.c | 196 + .../rombios/32bit/tcgbios/tpm_drivers.h | 18 + tools/firmware/rombios/32bit/util.c | 466 + tools/firmware/rombios/32bit/util.h | 46 + tools/firmware/rombios/32bitgateway.c | 422 + tools/firmware/rombios/32bitgateway.h | 18 + tools/firmware/rombios/32bitprotos.h | 47 + tools/firmware/rombios/Makefile | 29 + tools/firmware/rombios/apmbios.S | 367 + tools/firmware/rombios/biossums.c | 478 + tools/firmware/rombios/makesym.perl | 31 + tools/firmware/rombios/rombios.c | 11241 ++++++++++ tools/firmware/rombios/tcgbios.c | 270 + tools/firmware/vgabios/BUGS | 3 + tools/firmware/vgabios/COPYING | 504 + tools/firmware/vgabios/ChangeLog | 1264 ++ tools/firmware/vgabios/Makefile | 86 + tools/firmware/vgabios/Notes | 11 + tools/firmware/vgabios/README | 219 + tools/firmware/vgabios/TODO | 26 + tools/firmware/vgabios/biossums.c | 282 + tools/firmware/vgabios/clext.c | 1717 ++ tools/firmware/vgabios/dataseghack | 23 + tools/firmware/vgabios/vbe.c | 1432 ++ tools/firmware/vgabios/vbe.h | 313 + tools/firmware/vgabios/vbe_display_api.txt | 237 + tools/firmware/vgabios/vbetables-gen.c | 240 + tools/firmware/vgabios/vgabios.c | 3853 ++++ tools/firmware/vgabios/vgabios.h | 47 + tools/firmware/vgabios/vgafonts.h | 784 + tools/firmware/vgabios/vgatables.h | 623 + tools/flask/Makefile | 10 + tools/flask/libflask/Makefile | 65 + tools/flask/libflask/flask_op.c | 72 + tools/flask/libflask/include/flask.h | 22 + tools/flask/loadpolicy/Makefile | 60 + tools/flask/loadpolicy/loadpolicy.c | 129 + tools/flask/policy/Makefile | 234 + tools/flask/policy/Rules.modular | 166 + tools/flask/policy/Rules.monolithic | 196 + tools/flask/policy/policy/constraints | 27 + tools/flask/policy/policy/flask/Makefile | 41 + .../flask/policy/policy/flask/access_vectors | 166 + tools/flask/policy/policy/flask/initial_sids | 17 + .../policy/policy/flask/mkaccess_vector.sh | 227 + tools/flask/policy/policy/flask/mkflask.sh | 95 + .../policy/policy/flask/security_classes | 20 + tools/flask/policy/policy/global_booleans | 5 + tools/flask/policy/policy/global_tunables | 6 + tools/flask/policy/policy/mcs | 324 + tools/flask/policy/policy/mls | 354 + tools/flask/policy/policy/modules.conf | 21 + tools/flask/policy/policy/modules/xen/xen.if | 1 + tools/flask/policy/policy/modules/xen/xen.te | 138 + .../policy/policy/support/loadable_module.spt | 166 + .../policy/policy/support/misc_macros.spt | 32 + tools/flask/policy/policy/systemuser | 19 + tools/flask/policy/policy/users | 39 + tools/fs-back/Makefile | 40 + tools/fs-back/fs-backend.c | 355 + tools/fs-back/fs-backend.h | 91 + tools/fs-back/fs-ops.c | 732 + tools/fs-back/fs-xenbus.c | 189 + tools/include/Makefile | 48 + tools/include/xen-foreign/Makefile | 35 + tools/include/xen-foreign/mkchecker.py | 53 + tools/include/xen-foreign/mkheader.py | 167 + tools/include/xen-foreign/reference.size | 18 + tools/include/xen-foreign/structs.py | 58 + tools/include/xen-sys/Linux/evtchn.h | 88 + tools/include/xen-sys/Linux/gntdev.h | 119 + tools/include/xen-sys/Linux/privcmd.h | 79 + tools/include/xen-sys/MiniOS/privcmd.h | 16 + tools/include/xen-sys/NetBSD/evtchn.h | 89 + tools/include/xen-sys/NetBSD/privcmd.h | 106 + tools/include/xen-sys/SunOS/evtchn.h | 94 + tools/include/xen-sys/SunOS/privcmd.h | 85 + tools/include/xen-sys/SunOS/xenbus.h | 42 + tools/libaio/COPYING | 515 + tools/libaio/ChangeLog | 43 + tools/libaio/INSTALL | 18 + tools/libaio/Makefile | 40 + tools/libaio/TODO | 4 + tools/libaio/harness/Makefile | 37 + tools/libaio/harness/README | 19 + tools/libaio/harness/attic/0.t | 9 + tools/libaio/harness/attic/1.t | 9 + tools/libaio/harness/cases/10.t | 53 + tools/libaio/harness/cases/11.t | 39 + tools/libaio/harness/cases/12.t | 49 + tools/libaio/harness/cases/13.t | 66 + tools/libaio/harness/cases/14.t | 90 + tools/libaio/harness/cases/2.t | 41 + tools/libaio/harness/cases/3.t | 25 + tools/libaio/harness/cases/4.t | 72 + tools/libaio/harness/cases/5.t | 47 + tools/libaio/harness/cases/6.t | 57 + tools/libaio/harness/cases/7.t | 27 + tools/libaio/harness/cases/8.t | 49 + tools/libaio/harness/cases/aio_setup.h | 98 + tools/libaio/harness/cases/common-7-8.h | 37 + tools/libaio/harness/main.c | 39 + tools/libaio/harness/runtests.sh | 19 + tools/libaio/libaio.spec | 187 + tools/libaio/man/aio.3 | 315 + tools/libaio/man/aio_cancel.3 | 137 + tools/libaio/man/aio_cancel64.3 | 50 + tools/libaio/man/aio_error.3 | 81 + tools/libaio/man/aio_error64.3 | 64 + tools/libaio/man/aio_fsync.3 | 139 + tools/libaio/man/aio_fsync64.3 | 51 + tools/libaio/man/aio_init.3 | 96 + tools/libaio/man/aio_read.3 | 146 + tools/libaio/man/aio_read64.3 | 60 + tools/libaio/man/aio_return.3 | 71 + tools/libaio/man/aio_return64.3 | 51 + tools/libaio/man/aio_suspend.3 | 123 + tools/libaio/man/aio_suspend64.3 | 51 + tools/libaio/man/aio_write.3 | 176 + tools/libaio/man/aio_write64.3 | 61 + tools/libaio/man/io.3 | 351 + tools/libaio/man/io_cancel.1 | 21 + tools/libaio/man/io_cancel.3 | 65 + tools/libaio/man/io_destroy.1 | 17 + tools/libaio/man/io_fsync.3 | 82 + tools/libaio/man/io_getevents.1 | 29 + tools/libaio/man/io_getevents.3 | 79 + tools/libaio/man/io_prep_fsync.3 | 89 + tools/libaio/man/io_prep_pread.3 | 79 + tools/libaio/man/io_prep_pwrite.3 | 77 + tools/libaio/man/io_queue_init.3 | 63 + tools/libaio/man/io_queue_release.3 | 48 + tools/libaio/man/io_queue_run.3 | 50 + tools/libaio/man/io_queue_wait.3 | 56 + tools/libaio/man/io_set_callback.3 | 44 + tools/libaio/man/io_setup.1 | 15 + tools/libaio/man/io_submit.1 | 109 + tools/libaio/man/io_submit.3 | 135 + tools/libaio/man/lio_listio.3 | 229 + tools/libaio/man/lio_listio64.3 | 39 + tools/libaio/src/Makefile | 67 + tools/libaio/src/compat-0_1.c | 62 + tools/libaio/src/io_cancel.c | 23 + tools/libaio/src/io_destroy.c | 23 + tools/libaio/src/io_getevents.c | 57 + tools/libaio/src/io_queue_init.c | 33 + tools/libaio/src/io_queue_release.c | 27 + tools/libaio/src/io_queue_run.c | 39 + tools/libaio/src/io_queue_wait.c | 31 + tools/libaio/src/io_setup.c | 23 + tools/libaio/src/io_submit.c | 23 + tools/libaio/src/libaio.h | 222 + tools/libaio/src/libaio.map | 22 + tools/libaio/src/raw_syscall.c | 19 + tools/libaio/src/syscall-alpha.h | 209 + tools/libaio/src/syscall-i386.h | 72 + tools/libaio/src/syscall-ia64.h | 45 + tools/libaio/src/syscall-ppc.h | 98 + tools/libaio/src/syscall-s390.h | 131 + tools/libaio/src/syscall-x86_64.h | 63 + tools/libaio/src/syscall.h | 27 + tools/libaio/src/vsys_def.h | 24 + tools/libfsimage/Makefile | 11 + tools/libfsimage/Rules.mk | 33 + tools/libfsimage/check-libext2fs | 21 + tools/libfsimage/common/Makefile | 46 + tools/libfsimage/common/fsimage.c | 169 + tools/libfsimage/common/fsimage.h | 56 + tools/libfsimage/common/fsimage_grub.c | 386 + tools/libfsimage/common/fsimage_grub.h | 101 + tools/libfsimage/common/fsimage_plugin.c | 212 + tools/libfsimage/common/fsimage_plugin.h | 64 + tools/libfsimage/common/fsimage_priv.h | 63 + tools/libfsimage/common/mapfile-GNU | 40 + tools/libfsimage/common/mapfile-SunOS | 38 + tools/libfsimage/ext2fs-lib/Makefile | 15 + tools/libfsimage/ext2fs-lib/ext2fs-lib.c | 174 + tools/libfsimage/ext2fs/Makefile | 13 + tools/libfsimage/ext2fs/fsys_ext2fs.c | 832 + tools/libfsimage/fat/Makefile | 13 + tools/libfsimage/fat/fat.h | 100 + tools/libfsimage/fat/fsys_fat.c | 485 + tools/libfsimage/iso9660/Makefile | 15 + tools/libfsimage/iso9660/fsys_iso9660.c | 463 + tools/libfsimage/iso9660/iso9660.h | 219 + tools/libfsimage/reiserfs/Makefile | 13 + tools/libfsimage/reiserfs/fsys_reiserfs.c | 1241 ++ tools/libfsimage/ufs/Makefile | 13 + tools/libfsimage/ufs/fsys_ufs.c | 278 + tools/libfsimage/ufs/ufs.h | 228 + tools/libfsimage/zfs/Makefile | 37 + tools/libfsimage/zfs/fsys_zfs.c | 1457 ++ tools/libfsimage/zfs/fsys_zfs.h | 203 + tools/libfsimage/zfs/mb_info.h | 217 + tools/libfsimage/zfs/zfs-include/dmu.h | 105 + tools/libfsimage/zfs/zfs-include/dmu_objset.h | 35 + tools/libfsimage/zfs/zfs-include/dnode.h | 76 + .../libfsimage/zfs/zfs-include/dsl_dataset.h | 53 + tools/libfsimage/zfs/zfs-include/dsl_dir.h | 49 + tools/libfsimage/zfs/zfs-include/spa.h | 283 + .../zfs/zfs-include/uberblock_impl.h | 49 + tools/libfsimage/zfs/zfs-include/vdev_impl.h | 70 + tools/libfsimage/zfs/zfs-include/zap_impl.h | 110 + tools/libfsimage/zfs/zfs-include/zap_leaf.h | 100 + tools/libfsimage/zfs/zfs-include/zfs.h | 112 + tools/libfsimage/zfs/zfs-include/zfs_acl.h | 55 + tools/libfsimage/zfs/zfs-include/zfs_znode.h | 68 + tools/libfsimage/zfs/zfs-include/zil.h | 51 + tools/libfsimage/zfs/zfs-include/zio.h | 81 + .../libfsimage/zfs/zfs-include/zio_checksum.h | 42 + tools/libfsimage/zfs/zfs_fletcher.c | 93 + tools/libfsimage/zfs/zfs_lzjb.c | 60 + tools/libfsimage/zfs/zfs_sha256.c | 124 + tools/libxc/Makefile | 159 + tools/libxc/ia64/Makefile | 56 + tools/libxc/ia64/aclinux.h | 111 + tools/libxc/ia64/dom_fw_acpi.c | 31 + tools/libxc/ia64/sal.h | 71 + tools/libxc/ia64/xc_dom_ia64_util.c | 192 + tools/libxc/ia64/xc_dom_ia64_util.h | 30 + tools/libxc/ia64/xc_ia64.h | 58 + tools/libxc/ia64/xc_ia64_dom_fwloader.c | 126 + tools/libxc/ia64/xc_ia64_hvm_build.c | 1182 ++ tools/libxc/ia64/xc_ia64_linux_restore.c | 730 + tools/libxc/ia64/xc_ia64_linux_save.c | 790 + tools/libxc/ia64/xc_ia64_save_restore.h | 67 + tools/libxc/ia64/xc_ia64_stubs.c | 303 + tools/libxc/rpm.spec | 28 + tools/libxc/xc_acm.c | 120 + tools/libxc/xc_core.c | 919 + tools/libxc/xc_core.h | 169 + tools/libxc/xc_core_ia64.c | 361 + tools/libxc/xc_core_ia64.h | 68 + tools/libxc/xc_core_x86.c | 137 + tools/libxc/xc_core_x86.h | 65 + tools/libxc/xc_cpu_hotplug.c | 53 + tools/libxc/xc_cpufeature.h | 115 + tools/libxc/xc_cpuid_x86.c | 575 + tools/libxc/xc_csched.c | 50 + tools/libxc/xc_dom.h | 275 + tools/libxc/xc_dom_binloader.c | 287 + tools/libxc/xc_dom_boot.c | 265 + tools/libxc/xc_dom_bzimageloader.c | 159 + tools/libxc/xc_dom_compat_linux.c | 157 + tools/libxc/xc_dom_core.c | 790 + tools/libxc/xc_dom_elfloader.c | 302 + tools/libxc/xc_dom_ia64.c | 320 + tools/libxc/xc_dom_x86.c | 829 + tools/libxc/xc_domain.c | 1072 + tools/libxc/xc_domain_restore.c | 1209 ++ tools/libxc/xc_domain_save.c | 1635 ++ tools/libxc/xc_e820.h | 20 + tools/libxc/xc_efi.h | 145 + tools/libxc/xc_elf.h | 1 + tools/libxc/xc_evtchn.c | 66 + tools/libxc/xc_flask.c | 46 + tools/libxc/xc_hvm_build.c | 446 + tools/libxc/xc_linux.c | 573 + tools/libxc/xc_minios.c | 425 + tools/libxc/xc_misc.c | 369 + tools/libxc/xc_netbsd.c | 274 + tools/libxc/xc_pagetab.c | 192 + tools/libxc/xc_physdev.c | 93 + tools/libxc/xc_pm.c | 170 + tools/libxc/xc_private.c | 638 + tools/libxc/xc_private.h | 218 + tools/libxc/xc_ptrace.c | 633 + tools/libxc/xc_ptrace.h | 160 + tools/libxc/xc_ptrace_core.c | 678 + tools/libxc/xc_resume.c | 225 + tools/libxc/xc_sedf.c | 64 + tools/libxc/xc_solaris.c | 252 + tools/libxc/xc_tbuf.c | 136 + tools/libxc/xenctrl.h | 1164 ++ tools/libxc/xenguest.h | 139 + tools/libxc/xg_private.c | 210 + tools/libxc/xg_private.h | 178 + tools/libxc/xg_save_restore.h | 153 + tools/libxen/COPYING | 510 + tools/libxen/Makefile | 74 + tools/libxen/Makefile.dist | 115 + tools/libxen/README | 55 + tools/libxen/include/xen/api/xen_acmpolicy.h | 132 + tools/libxen/include/xen/api/xen_all.h | 40 + tools/libxen/include/xen/api/xen_common.h | 211 + tools/libxen/include/xen/api/xen_console.h | 247 + .../libxen/include/xen/api/xen_console_decl.h | 30 + .../include/xen/api/xen_console_protocol.h | 82 + tools/libxen/include/xen/api/xen_crashdump.h | 199 + .../include/xen/api/xen_crashdump_decl.h | 30 + tools/libxen/include/xen/api/xen_event.h | 102 + tools/libxen/include/xen/api/xen_event_decl.h | 25 + .../include/xen/api/xen_event_operation.h | 82 + tools/libxen/include/xen/api/xen_host.h | 497 + tools/libxen/include/xen/api/xen_host_cpu.h | 247 + .../include/xen/api/xen_host_cpu_decl.h | 30 + tools/libxen/include/xen/api/xen_host_decl.h | 30 + .../libxen/include/xen/api/xen_host_metrics.h | 199 + .../include/xen/api/xen_host_metrics_decl.h | 30 + .../include/xen/api/xen_int_float_map.h | 53 + .../libxen/include/xen/api/xen_int_int_map.h | 53 + .../include/xen/api/xen_int_string_set_map.h | 53 + tools/libxen/include/xen/api/xen_network.h | 276 + .../libxen/include/xen/api/xen_network_decl.h | 30 + .../include/xen/api/xen_on_crash_behaviour.h | 97 + .../include/xen/api/xen_on_normal_exit.h | 77 + tools/libxen/include/xen/api/xen_pbd.h | 223 + tools/libxen/include/xen/api/xen_pbd_decl.h | 30 + tools/libxen/include/xen/api/xen_pif.h | 277 + tools/libxen/include/xen/api/xen_pif_decl.h | 30 + .../libxen/include/xen/api/xen_pif_metrics.h | 198 + .../include/xen/api/xen_pif_metrics_decl.h | 30 + tools/libxen/include/xen/api/xen_sr.h | 277 + tools/libxen/include/xen/api/xen_sr_decl.h | 30 + tools/libxen/include/xen/api/xen_string_set.h | 47 + .../include/xen/api/xen_string_string_map.h | 53 + tools/libxen/include/xen/api/xen_user.h | 204 + tools/libxen/include/xen/api/xen_user_decl.h | 30 + tools/libxen/include/xen/api/xen_vbd.h | 390 + tools/libxen/include/xen/api/xen_vbd_decl.h | 30 + .../libxen/include/xen/api/xen_vbd_metrics.h | 198 + .../include/xen/api/xen_vbd_metrics_decl.h | 30 + tools/libxen/include/xen/api/xen_vbd_mode.h | 77 + tools/libxen/include/xen/api/xen_vbd_type.h | 77 + tools/libxen/include/xen/api/xen_vdi.h | 360 + tools/libxen/include/xen/api/xen_vdi_decl.h | 30 + tools/libxen/include/xen/api/xen_vdi_type.h | 92 + tools/libxen/include/xen/api/xen_vif.h | 379 + tools/libxen/include/xen/api/xen_vif_decl.h | 30 + .../libxen/include/xen/api/xen_vif_metrics.h | 198 + .../include/xen/api/xen_vif_metrics_decl.h | 30 + tools/libxen/include/xen/api/xen_vm.h | 908 + tools/libxen/include/xen/api/xen_vm_decl.h | 30 + .../include/xen/api/xen_vm_guest_metrics.h | 234 + .../xen/api/xen_vm_guest_metrics_decl.h | 30 + tools/libxen/include/xen/api/xen_vm_metrics.h | 251 + .../include/xen/api/xen_vm_metrics_decl.h | 30 + .../include/xen/api/xen_vm_power_state.h | 97 + tools/libxen/include/xen/api/xen_vtpm.h | 218 + tools/libxen/include/xen/api/xen_vtpm_decl.h | 31 + tools/libxen/include/xen/api/xen_xspolicy.h | 293 + .../include/xen/api/xen_xspolicy_decl.h | 31 + .../include/xen_console_protocol_internal.h | 37 + .../include/xen_event_operation_internal.h | 37 + tools/libxen/include/xen_internal.h | 188 + .../include/xen_on_crash_behaviour_internal.h | 38 + .../include/xen_on_normal_exit_internal.h | 37 + tools/libxen/include/xen_vbd_mode_internal.h | 37 + tools/libxen/include/xen_vbd_type_internal.h | 37 + tools/libxen/include/xen_vdi_type_internal.h | 37 + .../include/xen_vm_power_state_internal.h | 37 + tools/libxen/src/xen_acmpolicy.c | 269 + tools/libxen/src/xen_common.c | 1772 ++ tools/libxen/src/xen_console.c | 298 + tools/libxen/src/xen_console_protocol.c | 82 + tools/libxen/src/xen_crashdump.c | 191 + tools/libxen/src/xen_event.c | 123 + tools/libxen/src/xen_event_operation.c | 75 + tools/libxen/src/xen_host.c | 891 + tools/libxen/src/xen_host_cpu.c | 317 + tools/libxen/src/xen_host_metrics.c | 190 + tools/libxen/src/xen_int_float_map.c | 39 + tools/libxen/src/xen_int_int_map.c | 39 + tools/libxen/src/xen_int_string_set_map.c | 54 + tools/libxen/src/xen_network.c | 371 + tools/libxen/src/xen_on_crash_behaviour.c | 85 + tools/libxen/src/xen_on_normal_exit.c | 81 + tools/libxen/src/xen_pbd.c | 249 + tools/libxen/src/xen_pif.c | 380 + tools/libxen/src/xen_pif_metrics.c | 190 + tools/libxen/src/xen_sr.c | 379 + tools/libxen/src/xen_string_set.c | 48 + tools/libxen/src/xen_string_set.h | 47 + tools/libxen/src/xen_string_string_map.c | 52 + tools/libxen/src/xen_user.c | 210 + tools/libxen/src/xen_vbd.c | 626 + tools/libxen/src/xen_vbd_metrics.c | 190 + tools/libxen/src/xen_vbd_mode.c | 81 + tools/libxen/src/xen_vbd_type.c | 81 + tools/libxen/src/xen_vdi.c | 575 + tools/libxen/src/xen_vdi_type.c | 84 + tools/libxen/src/xen_vif.c | 616 + tools/libxen/src/xen_vif_metrics.c | 190 + tools/libxen/src/xen_vm.c | 1783 ++ tools/libxen/src/xen_vm_guest_metrics.c | 279 + tools/libxen/src/xen_vm_metrics.c | 318 + tools/libxen/src/xen_vm_power_state.c | 85 + tools/libxen/src/xen_vtpm.c | 235 + tools/libxen/src/xen_xspolicy.c | 363 + tools/libxen/test/test_bindings.c | 804 + tools/libxen/test/test_event_handling.c | 210 + tools/misc/Makefile | 57 + tools/misc/fakei386xen | 32 + tools/misc/lomount/Makefile | 27 + tools/misc/lomount/lomount.c | 435 + tools/misc/miniterm/Makefile | 22 + tools/misc/miniterm/README | 13 + tools/misc/miniterm/miniterm.c | 195 + tools/misc/netfix | 69 + tools/misc/nsplitd/Makefile | 25 + tools/misc/nsplitd/nsplitd.c | 686 + tools/misc/sxp-pretty | 45 + tools/misc/xen-bugtool | 20 + tools/misc/xen-clone.README | 23 + tools/misc/xen-detect.c | 108 + tools/misc/xen-python-path | 47 + tools/misc/xencons | 92 + tools/misc/xend | 155 + tools/misc/xenperf.c | 227 + tools/misc/xenpm.c | 216 + tools/misc/xensymoops | 118 + tools/misc/xm | 10 + tools/misc/xsview | 11 + tools/pygrub/Makefile | 25 + tools/pygrub/README | 15 + tools/pygrub/setup.py | 29 + tools/pygrub/src/GrubConf.py | 257 + tools/pygrub/src/LiloConf.py | 165 + tools/pygrub/src/__init__.py | 0 tools/pygrub/src/fsimage/fsimage.c | 323 + tools/pygrub/src/pygrub | 695 + tools/python/Makefile | 87 + tools/python/README | 3 + tools/python/README.XendConfig | 161 + tools/python/README.sxpcfg | 118 + tools/python/ZPL-2.0 | 59 + tools/python/get-path | 22 + tools/python/install-wrap | 44 + tools/python/logging/logging-0.4.9.2/PKG-INFO | 25 + .../python/logging/logging-0.4.9.2/README.txt | 311 + .../logging/logging-0.4.9.2/default.css | 32 + .../logging/logging-0.4.9.2/liblogging.tex | 1281 ++ .../logging-0.4.9.2/logging/__init__.py | 1225 ++ .../logging/logging-0.4.9.2/logging/config.py | 301 + .../logging-0.4.9.2/logging/handlers.py | 787 + .../logging-0.4.9.2/python_logging.html | 1183 ++ tools/python/logging/logging-0.4.9.2/setup.py | 29 + .../logging/logging-0.4.9.2/test/app.py | 5 + .../logging/logging-0.4.9.2/test/critical.ini | 60 + .../logging/logging-0.4.9.2/test/debug.ini | 60 + .../logging/logging-0.4.9.2/test/error.ini | 60 + .../logging/logging-0.4.9.2/test/events.xml | 31 + .../logging/logging-0.4.9.2/test/log_test.py | 158 + .../logging/logging-0.4.9.2/test/log_test0.py | 118 + .../logging/logging-0.4.9.2/test/log_test1.py | 85 + .../logging-0.4.9.2/test/log_test10.py | 87 + .../logging-0.4.9.2/test/log_test11.py | 72 + .../logging-0.4.9.2/test/log_test12.py | 47 + .../logging-0.4.9.2/test/log_test13.py | 106 + .../logging-0.4.9.2/test/log_test14.py | 108 + .../logging-0.4.9.2/test/log_test15.py | 70 + .../logging-0.4.9.2/test/log_test16.py | 73 + .../logging-0.4.9.2/test/log_test17.py | 111 + .../logging-0.4.9.2/test/log_test18.py | 102 + .../logging-0.4.9.2/test/log_test19.py | 57 + .../logging/logging-0.4.9.2/test/log_test2.py | 119 + .../logging-0.4.9.2/test/log_test20.py | 84 + .../logging-0.4.9.2/test/log_test21.py | 141 + .../logging-0.4.9.2/test/log_test22.py | 50 + .../logging-0.4.9.2/test/log_test3.ini | 95 + .../logging/logging-0.4.9.2/test/log_test3.py | 70 + .../logging/logging-0.4.9.2/test/log_test4.py | 168 + .../logging/logging-0.4.9.2/test/log_test5.py | 44 + .../logging/logging-0.4.9.2/test/log_test6.py | 47 + .../logging/logging-0.4.9.2/test/log_test7.py | 48 + .../logging/logging-0.4.9.2/test/log_test8.py | 69 + .../logging/logging-0.4.9.2/test/log_test9.py | 71 + .../logging/logging-0.4.9.2/test/logconf.ini | 180 + .../logging/logging-0.4.9.2/test/logconf.py | 1738 ++ .../logging/logging-0.4.9.2/test/logging.dtd | 19 + .../logging/logging-0.4.9.2/test/logging.xml | 5 + .../logging/logging-0.4.9.2/test/logrecv.ini | 36 + .../logging/logging-0.4.9.2/test/logrecv.py | 443 + .../logging/logging-0.4.9.2/test/myapp.py | 13 + .../logging/logging-0.4.9.2/test/mymodule.py | 8 + .../logging/logging-0.4.9.2/test/stderr.exp | 566 + .../logging/logging-0.4.9.2/test/stdout.exp | 24 + .../logging/logging-0.4.9.2/test/warn.ini | 60 + tools/python/logging/setup.py | 11 + tools/python/ptsname/ptsname.c | 44 + tools/python/pylintrc | 307 + tools/python/remove-potcdate.sed | 19 + tools/python/scripts/README | 49 + tools/python/scripts/README.lifecycle | 136 + tools/python/scripts/test_hvm_create.py | 179 + tools/python/scripts/test_vm_create.py | 212 + tools/python/scripts/xapi.domcfg.py | 37 + tools/python/scripts/xapi.py | 875 + tools/python/scripts/xapi.vbdcfg.py | 12 + tools/python/scripts/xapi.vdicfg.py | 6 + tools/python/scripts/xapi.vifcfg.py | 10 + tools/python/scripts/xapi.vtpmcfg.py | 3 + tools/python/setup.py | 95 + tools/python/test.py | 1094 + tools/python/xen/__init__.py | 1 + tools/python/xen/lowlevel/__init__.py | 1 + tools/python/xen/lowlevel/acm/acm.c | 364 + tools/python/xen/lowlevel/flask/flask.c | 138 + tools/python/xen/lowlevel/scf/scf.c | 156 + tools/python/xen/lowlevel/xc/xc.c | 2060 ++ tools/python/xen/lowlevel/xs/xs.c | 989 + tools/python/xen/sv/CreateDomain.py | 205 + tools/python/xen/sv/DomInfo.py | 268 + tools/python/xen/sv/GenTabbed.py | 147 + tools/python/xen/sv/HTMLBase.py | 53 + tools/python/xen/sv/Main.py | 82 + tools/python/xen/sv/NodeInfo.py | 73 + tools/python/xen/sv/RestoreDomain.py | 50 + tools/python/xen/sv/Wizard.py | 245 + tools/python/xen/sv/__init__.py | 1 + tools/python/xen/sv/util.py | 126 + tools/python/xen/util/Brctl.py | 186 + tools/python/xen/util/SSHTransport.py | 102 + tools/python/xen/util/__init__.py | 1 + tools/python/xen/util/acmpolicy.py | 1615 ++ tools/python/xen/util/asserts.py | 27 + tools/python/xen/util/auxbin.py | 62 + tools/python/xen/util/blkif.py | 107 + tools/python/xen/util/bootloader.py | 626 + tools/python/xen/util/bugtool.py | 234 + tools/python/xen/util/diagnose.py | 185 + tools/python/xen/util/dictio.py | 50 + tools/python/xen/util/ip.py | 121 + tools/python/xen/util/mac.py | 11 + tools/python/xen/util/mkdir.py | 44 + tools/python/xen/util/oshelp.py | 20 + tools/python/xen/util/pci.py | 936 + tools/python/xen/util/utils.py | 76 + tools/python/xen/util/vscsi_util.py | 240 + tools/python/xen/util/xmlrpcclient.py | 123 + tools/python/xen/util/xmlrpclib2.py | 217 + tools/python/xen/util/xpopen.py | 169 + tools/python/xen/util/xsconstants.py | 114 + tools/python/xen/util/xsm/__init__.py | 2 + tools/python/xen/util/xsm/acm/__init__.py | 1 + tools/python/xen/util/xsm/acm/acm.py | 1725 ++ tools/python/xen/util/xsm/dummy/__init__.py | 1 + tools/python/xen/util/xsm/dummy/dummy.py | 136 + tools/python/xen/util/xsm/flask/__init__.py | 1 + tools/python/xen/util/xsm/flask/flask.py | 49 + tools/python/xen/util/xsm/xsm.py | 20 + tools/python/xen/util/xsm/xsm_core.py | 7 + tools/python/xen/util/xspolicy.py | 66 + tools/python/xen/web/SrvBase.py | 98 + tools/python/xen/web/SrvDir.py | 124 + tools/python/xen/web/__init__.py | 17 + tools/python/xen/web/connection.py | 294 + tools/python/xen/web/http.py | 518 + tools/python/xen/web/httpserver.py | 367 + tools/python/xen/web/protocol.py | 40 + tools/python/xen/web/resource.py | 108 + tools/python/xen/web/static.py | 61 + tools/python/xen/web/tcp.py | 118 + tools/python/xen/web/unix.py | 55 + tools/python/xen/xend/Args.py | 166 + tools/python/xen/xend/PrettyPrint.py | 323 + tools/python/xen/xend/Vifctl.py | 36 + tools/python/xen/xend/XendAPI.py | 2719 +++ tools/python/xen/xend/XendAPIConstants.py | 70 + tools/python/xen/xend/XendAPIStore.py | 59 + tools/python/xen/xend/XendAPIVersion.py | 22 + tools/python/xen/xend/XendAuthSessions.py | 131 + tools/python/xen/xend/XendBase.py | 126 + tools/python/xen/xend/XendBootloader.py | 186 + tools/python/xen/xend/XendCheckpoint.py | 405 + tools/python/xen/xend/XendClient.py | 40 + tools/python/xen/xend/XendConfig.py | 1995 ++ tools/python/xen/xend/XendConstants.py | 136 + tools/python/xen/xend/XendDPCI.py | 154 + tools/python/xen/xend/XendDSCSI.py | 174 + tools/python/xen/xend/XendDevices.py | 84 + tools/python/xen/xend/XendDmesg.py | 41 + tools/python/xen/xend/XendDomain.py | 1717 ++ tools/python/xen/xend/XendDomainInfo.py | 3620 ++++ tools/python/xen/xend/XendError.py | 220 + tools/python/xen/xend/XendLocalStorageRepo.py | 93 + tools/python/xen/xend/XendLogging.py | 149 + tools/python/xen/xend/XendMonitor.py | 340 + tools/python/xen/xend/XendNetwork.py | 238 + tools/python/xen/xend/XendNode.py | 791 + tools/python/xen/xend/XendOptions.py | 492 + tools/python/xen/xend/XendPBD.py | 99 + tools/python/xen/xend/XendPIF.py | 389 + tools/python/xen/xend/XendPIFMetrics.py | 59 + tools/python/xen/xend/XendPPCI.py | 158 + tools/python/xen/xend/XendPSCSI.py | 143 + tools/python/xen/xend/XendProtocol.py | 225 + tools/python/xen/xend/XendQCoWStorageRepo.py | 352 + tools/python/xen/xend/XendStateStore.py | 230 + .../python/xen/xend/XendStorageRepository.py | 105 + tools/python/xen/xend/XendTask.py | 224 + tools/python/xen/xend/XendTaskManager.py | 110 + tools/python/xen/xend/XendVDI.py | 211 + tools/python/xen/xend/XendVMMetrics.py | 145 + tools/python/xen/xend/XendVnet.py | 181 + tools/python/xen/xend/XendXSPolicy.py | 287 + tools/python/xen/xend/XendXSPolicyAdmin.py | 385 + tools/python/xen/xend/__init__.py | 1 + tools/python/xen/xend/arch.py | 32 + tools/python/xen/xend/balloon.py | 185 + tools/python/xen/xend/encode.py | 180 + tools/python/xen/xend/image.py | 941 + tools/python/xen/xend/osdep.py | 131 + .../xen/xend/server/BlktapController.py | 87 + .../xen/xend/server/ConsoleController.py | 38 + tools/python/xen/xend/server/DevController.py | 705 + .../python/xen/xend/server/SSLXMLRPCServer.py | 103 + tools/python/xen/xend/server/SrvDaemon.py | 418 + tools/python/xen/xend/server/SrvDmesg.py | 52 + tools/python/xen/xend/server/SrvDomain.py | 286 + tools/python/xen/xend/server/SrvDomainDir.py | 222 + tools/python/xen/xend/server/SrvNode.py | 64 + tools/python/xen/xend/server/SrvRoot.py | 43 + tools/python/xen/xend/server/SrvServer.py | 254 + tools/python/xen/xend/server/SrvVnetDir.py | 128 + tools/python/xen/xend/server/SrvXendLog.py | 37 + tools/python/xen/xend/server/XMLRPCServer.py | 257 + tools/python/xen/xend/server/__init__.py | 1 + tools/python/xen/xend/server/blkif.py | 208 + tools/python/xen/xend/server/iopif.py | 84 + tools/python/xen/xend/server/irqif.py | 78 + tools/python/xen/xend/server/netif.py | 198 + tools/python/xen/xend/server/params.py | 46 + tools/python/xen/xend/server/pciif.py | 551 + tools/python/xen/xend/server/pciquirk.py | 146 + tools/python/xen/xend/server/relocate.py | 171 + .../python/xen/xend/server/tests/__init__.py | 1 + .../xen/xend/server/tests/test_controllers.py | 81 + tools/python/xen/xend/server/tpmif.py | 141 + tools/python/xen/xend/server/vfbif.py | 91 + tools/python/xen/xend/server/vscsiif.py | 232 + tools/python/xen/xend/sxp.py | 763 + tools/python/xen/xend/tests/__init__.py | 1 + .../python/xen/xend/tests/test_XendConfig.py | 42 + tools/python/xen/xend/tests/test_sxp.py | 39 + tools/python/xen/xend/tests/test_uuid.py | 30 + tools/python/xen/xend/tests/xend-config.sxp | 131 + tools/python/xen/xend/uuid.py | 69 + tools/python/xen/xend/xenstore/__init__.py | 16 + .../xen/xend/xenstore/tests/__init__.py | 2 + .../xen/xend/xenstore/tests/stress_xs.py | 121 + tools/python/xen/xend/xenstore/xstransact.py | 368 + tools/python/xen/xend/xenstore/xsutil.py | 32 + tools/python/xen/xend/xenstore/xswatch.py | 80 + tools/python/xen/xm/XenAPI.py | 214 + tools/python/xen/xm/__init__.py | 0 tools/python/xen/xm/addlabel.py | 273 + tools/python/xen/xm/console.py | 83 + tools/python/xen/xm/create.dtd | 147 + tools/python/xen/xm/create.py | 1328 ++ tools/python/xen/xm/dry-run.py | 161 + tools/python/xen/xm/dumppolicy.py | 69 + tools/python/xen/xm/getlabel.py | 157 + tools/python/xen/xm/getpolicy.py | 135 + tools/python/xen/xm/help.py | 100 + tools/python/xen/xm/labels.py | 89 + tools/python/xen/xm/main.py | 3012 +++ tools/python/xen/xm/messages/en/xen-xm.po | 93 + tools/python/xen/xm/messages/xen-xm.pot | 68 + tools/python/xen/xm/migrate.py | 81 + tools/python/xen/xm/new.py | 79 + tools/python/xen/xm/opts.py | 618 + tools/python/xen/xm/resetpolicy.py | 106 + tools/python/xen/xm/resources.py | 65 + tools/python/xen/xm/rmlabel.py | 216 + tools/python/xen/xm/setpolicy.py | 172 + tools/python/xen/xm/shutdown.py | 164 + tools/python/xen/xm/tests/__init__.py | 2 + tools/python/xen/xm/tests/test_create.py | 205 + tools/python/xen/xm/xenapi_create.py | 1052 + tools/python/xen/xsview/__init__.py | 0 tools/python/xen/xsview/main.py | 10 + tools/python/xen/xsview/xsviewer.py | 168 + tools/security/Makefile | 100 + .../example/client_v1-security_policy.xml | 195 + .../policies/example/test-security_policy.xml | 97 + tools/security/policies/security_policy.xsd | 146 + tools/security/policy.txt | 296 + tools/security/policytools.txt | 148 + tools/security/python/setup.py | 30 + tools/security/python/xensec_gen/__init__.py | 1 + .../python/xensec_gen/cgi-bin/policy.cgi | 2376 +++ tools/security/python/xensec_gen/index.html | 72 + tools/security/python/xensec_gen/main.py | 185 + .../security/python/xensec_tools/acm_getlabel | 48 + tools/security/readme.txt | 33 + tools/security/secpol_tool.c | 532 + tools/security/xensec_ezpolicy | 1636 ++ tools/security/xensec_gen.py | 26 + tools/sv/Makefile | 3 + tools/sv/images/destroy.png | Bin 0 -> 2408 bytes tools/sv/images/finish.png | Bin 0 -> 1189 bytes tools/sv/images/next.png | Bin 0 -> 1270 bytes tools/sv/images/pause.png | Bin 0 -> 1662 bytes tools/sv/images/previous.png | Bin 0 -> 1285 bytes tools/sv/images/reboot.png | Bin 0 -> 3132 bytes tools/sv/images/shutdown.png | Bin 0 -> 2901 bytes tools/sv/images/small-destroy.png | Bin 0 -> 483 bytes tools/sv/images/small-pause.png | Bin 0 -> 434 bytes tools/sv/images/small-unpause.png | Bin 0 -> 500 bytes tools/sv/images/unpause.png | Bin 0 -> 1890 bytes tools/sv/images/xen.png | Bin 0 -> 10575 bytes tools/sv/inc/script.js | 31 + tools/sv/inc/style.css | 95 + tools/sv/index.psp | 35 + tools/tests/Makefile | 37 + tools/tests/blowfish.c | 439 + tools/tests/blowfish.mk | 21 + tools/tests/test_x86_emulator.c | 486 + tools/tests/x86_emulate.c | 7 + tools/vnet/00INSTALL | 55 + tools/vnet/00README | 15 + tools/vnet/Make.env | 28 + tools/vnet/Makefile | 89 + tools/vnet/doc/Makefile | 51 + tools/vnet/doc/man/vn.pod.1 | 176 + tools/vnet/doc/vnet-module.txt | 72 + tools/vnet/doc/vnet-xend.txt | 167 + tools/vnet/examples/Makefile | 18 + tools/vnet/examples/network-vnet | 10 + tools/vnet/examples/vnet-insert | 28 + tools/vnet/examples/vnet97.sxp | 2 + tools/vnet/examples/vnet98.sxp | 2 + tools/vnet/examples/vnet99.sxp | 2 + tools/vnet/libxutil/Makefile | 86 + tools/vnet/libxutil/allocate.c | 116 + tools/vnet/libxutil/allocate.h | 45 + tools/vnet/libxutil/debug.h | 72 + tools/vnet/libxutil/enum.c | 61 + tools/vnet/libxutil/enum.h | 30 + tools/vnet/libxutil/fd_stream.c | 184 + tools/vnet/libxutil/fd_stream.h | 36 + tools/vnet/libxutil/file_stream.c | 220 + tools/vnet/libxutil/file_stream.h | 35 + tools/vnet/libxutil/gzip_stream.c | 174 + tools/vnet/libxutil/gzip_stream.h | 30 + tools/vnet/libxutil/hash_table.c | 658 + tools/vnet/libxutil/hash_table.h | 240 + tools/vnet/libxutil/iostream.c | 55 + tools/vnet/libxutil/iostream.h | 269 + tools/vnet/libxutil/kernel_stream.c | 178 + tools/vnet/libxutil/kernel_stream.h | 29 + tools/vnet/libxutil/lexis.c | 94 + tools/vnet/libxutil/lexis.h | 128 + tools/vnet/libxutil/mem_stream.c | 324 + tools/vnet/libxutil/mem_stream.h | 32 + tools/vnet/libxutil/socket_stream.c | 230 + tools/vnet/libxutil/socket_stream.h | 53 + tools/vnet/libxutil/string_stream.c | 162 + tools/vnet/libxutil/string_stream.h | 45 + tools/vnet/libxutil/sxpr.c | 1230 ++ tools/vnet/libxutil/sxpr.h | 440 + tools/vnet/libxutil/sxpr_parser.c | 1002 + tools/vnet/libxutil/sxpr_parser.h | 160 + tools/vnet/libxutil/sys_net.c | 319 + tools/vnet/libxutil/sys_net.h | 78 + tools/vnet/libxutil/sys_string.c | 218 + tools/vnet/libxutil/sys_string.h | 94 + tools/vnet/libxutil/util.c | 106 + tools/vnet/libxutil/util.h | 28 + tools/vnet/scripts/Makefile | 15 + tools/vnet/scripts/vn | 904 + tools/vnet/vnet-module/00README | 39 + tools/vnet/vnet-module/Makefile | 71 + tools/vnet/vnet-module/Makefile-2.4 | 98 + tools/vnet/vnet-module/Makefile-2.6 | 57 + tools/vnet/vnet-module/Makefile.ver | 51 + tools/vnet/vnet-module/Makefile.vnet | 60 + tools/vnet/vnet-module/esp.c | 903 + tools/vnet/vnet-module/esp.h | 120 + tools/vnet/vnet-module/etherip.c | 467 + tools/vnet/vnet-module/etherip.h | 38 + tools/vnet/vnet-module/if_etherip.h | 83 + tools/vnet/vnet-module/if_varp.h | 105 + tools/vnet/vnet-module/linux/pfkeyv2.h | 329 + tools/vnet/vnet-module/random.c | 87 + tools/vnet/vnet-module/random.h | 28 + tools/vnet/vnet-module/sa.c | 756 + tools/vnet/vnet-module/sa.h | 215 + tools/vnet/vnet-module/sa_algorithm.c | 367 + tools/vnet/vnet-module/sa_algorithm.h | 63 + tools/vnet/vnet-module/skb_context.c | 92 + tools/vnet/vnet-module/skb_context.h | 87 + tools/vnet/vnet-module/skb_util.c | 556 + tools/vnet/vnet-module/skb_util.h | 154 + tools/vnet/vnet-module/sxpr_util.c | 119 + tools/vnet/vnet-module/sxpr_util.h | 36 + tools/vnet/vnet-module/timer_util.c | 74 + tools/vnet/vnet-module/timer_util.h | 41 + tools/vnet/vnet-module/tunnel.c | 270 + tools/vnet/vnet-module/tunnel.h | 113 + tools/vnet/vnet-module/varp.c | 1536 ++ tools/vnet/vnet-module/varp.h | 161 + tools/vnet/vnet-module/varp_socket.c | 767 + tools/vnet/vnet-module/varp_util.c | 77 + tools/vnet/vnet-module/varp_util.h | 95 + tools/vnet/vnet-module/vif.c | 387 + tools/vnet/vnet-module/vif.h | 64 + tools/vnet/vnet-module/vnet.c | 699 + tools/vnet/vnet-module/vnet.h | 108 + tools/vnet/vnet-module/vnet_dev.c | 296 + tools/vnet/vnet-module/vnet_dev.h | 27 + tools/vnet/vnet-module/vnet_eval.c | 378 + tools/vnet/vnet-module/vnet_eval.h | 35 + tools/vnet/vnet-module/vnet_forward.c | 389 + tools/vnet/vnet-module/vnet_forward.h | 36 + tools/vnet/vnet-module/vnet_ioctl.c | 509 + tools/vnet/vnet-module/vnet_ioctl.h | 25 + tools/vnet/vnetd/Makefile | 121 + tools/vnet/vnetd/connection.c | 412 + tools/vnet/vnetd/connection.h | 76 + tools/vnet/vnetd/list.h | 284 + tools/vnet/vnetd/select.c | 109 + tools/vnet/vnetd/select.h | 60 + tools/vnet/vnetd/selector.c | 133 + tools/vnet/vnetd/selector.h | 62 + tools/vnet/vnetd/skbuff.c | 530 + tools/vnet/vnetd/skbuff.h | 538 + tools/vnet/vnetd/spinlock.c | 65 + tools/vnet/vnetd/spinlock.h | 47 + tools/vnet/vnetd/sys_kernel.h | 71 + tools/vnet/vnetd/timer.c | 157 + tools/vnet/vnetd/timer.h | 40 + tools/vnet/vnetd/vnetd.c | 1200 ++ tools/vtpm/Makefile | 94 + tools/vtpm/README | 45 + tools/vtpm/Rules.mk | 34 + tools/vtpm/tpm_emulator.patch | 1919 ++ tools/vtpm/vtpm.patch | 716 + tools/vtpm_manager/COPYING | 32 + tools/vtpm_manager/Makefile | 18 + tools/vtpm_manager/README | 94 + tools/vtpm_manager/Rules.mk | 67 + tools/vtpm_manager/crypto/Makefile | 24 + tools/vtpm_manager/crypto/crypto.c | 88 + tools/vtpm_manager/crypto/crypto.h | 175 + tools/vtpm_manager/crypto/hash.c | 153 + tools/vtpm_manager/crypto/rsa.c | 434 + tools/vtpm_manager/crypto/sym_crypto.c | 237 + tools/vtpm_manager/crypto/sym_crypto.h | 72 + tools/vtpm_manager/manager/Makefile | 36 + tools/vtpm_manager/manager/dmictl.c | 266 + tools/vtpm_manager/manager/migration.c | 307 + tools/vtpm_manager/manager/securestorage.c | 512 + tools/vtpm_manager/manager/tpmpassthrough.c | 110 + tools/vtpm_manager/manager/vtpm_ipc.c | 141 + tools/vtpm_manager/manager/vtpm_ipc.h | 71 + tools/vtpm_manager/manager/vtpm_lock.c | 63 + tools/vtpm_manager/manager/vtpm_lock.h | 48 + tools/vtpm_manager/manager/vtpm_manager.c | 285 + tools/vtpm_manager/manager/vtpm_manager.h | 150 + .../manager/vtpm_manager_handler.c | 488 + tools/vtpm_manager/manager/vtpmd.c | 371 + tools/vtpm_manager/manager/vtpmpriv.h | 186 + tools/vtpm_manager/manager/vtsp.c | 1042 + tools/vtpm_manager/manager/vtsp.h | 126 + tools/vtpm_manager/migration/Makefile | 42 + .../vtpm_manager/migration/vtpm_manager_if.c | 186 + tools/vtpm_manager/migration/vtpm_migrator.h | 104 + .../vtpm_manager/migration/vtpm_migrator_if.c | 219 + tools/vtpm_manager/migration/vtpm_migratorc.c | 211 + tools/vtpm_manager/migration/vtpm_migratord.c | 202 + .../migration/vtpm_migratord_handler.c | 177 + tools/vtpm_manager/tcs/Makefile | 24 + tools/vtpm_manager/tcs/contextmgr.c | 224 + tools/vtpm_manager/tcs/contextmgr.h | 82 + tools/vtpm_manager/tcs/tcs.c | 1192 ++ tools/vtpm_manager/tcs/tcs.h | 245 + tools/vtpm_manager/tcs/tpmddl.h | 69 + tools/vtpm_manager/tcs/transmit.c | 147 + tools/vtpm_manager/util/Makefile | 24 + tools/vtpm_manager/util/bsg.c | 829 + tools/vtpm_manager/util/bsg.h | 166 + tools/vtpm_manager/util/buffer.c | 226 + tools/vtpm_manager/util/buffer.h | 95 + tools/vtpm_manager/util/hashtable.c | 310 + tools/vtpm_manager/util/hashtable.h | 199 + tools/vtpm_manager/util/hashtable_itr.c | 231 + tools/vtpm_manager/util/hashtable_itr.h | 104 + tools/vtpm_manager/util/hashtable_private.h | 90 + tools/vtpm_manager/util/log.c | 142 + tools/vtpm_manager/util/log.h | 94 + tools/vtpm_manager/util/tcg.h | 503 + tools/xcutils/Makefile | 45 + tools/xcutils/lsevtchn.c | 65 + tools/xcutils/readnotes.c | 219 + tools/xcutils/xc_restore.c | 56 + tools/xcutils/xc_save.c | 328 + tools/xenballoon/xenballoon-monitor | 43 + tools/xenballoon/xenballoon.conf | 91 + tools/xenballoon/xenballoond | 205 + tools/xenballoon/xenballoond.README | 82 + tools/xenballoon/xenballoond.init | 91 + tools/xenmon/COPYING | 340 + tools/xenmon/Makefile | 47 + tools/xenmon/README | 114 + tools/xenmon/setmask.c | 89 + tools/xenmon/xenbaked.c | 1194 ++ tools/xenmon/xenbaked.h | 104 + tools/xenmon/xenmon.py | 712 + tools/xenstat/Makefile | 16 + tools/xenstat/libxenstat/COPYING | 510 + tools/xenstat/libxenstat/Makefile | 158 + .../libxenstat/bindings/swig/perl/.empty | 1 + .../libxenstat/bindings/swig/python/.empty | 1 + .../libxenstat/bindings/swig/xenstat.i | 8 + tools/xenstat/libxenstat/src/xenstat.c | 692 + tools/xenstat/libxenstat/src/xenstat.h | 187 + tools/xenstat/libxenstat/src/xenstat_linux.c | 282 + tools/xenstat/libxenstat/src/xenstat_netbsd.c | 97 + tools/xenstat/libxenstat/src/xenstat_priv.h | 102 + .../xenstat/libxenstat/src/xenstat_solaris.c | 405 + tools/xenstat/xentop/Makefile | 40 + tools/xenstat/xentop/TODO | 34 + tools/xenstat/xentop/xentop.1 | 100 + tools/xenstat/xentop/xentop.c | 1136 ++ tools/xenstore/.gdbinit | 4 + tools/xenstore/COPYING | 515 + tools/xenstore/Makefile | 119 + tools/xenstore/README | 5 + tools/xenstore/TODO | 10 + tools/xenstore/hashtable.c | 285 + tools/xenstore/hashtable.h | 199 + tools/xenstore/hashtable_private.h | 85 + tools/xenstore/list.h | 512 + tools/xenstore/talloc.c | 1311 ++ tools/xenstore/talloc.h | 144 + tools/xenstore/talloc_guide.txt | 569 + tools/xenstore/tdb.c | 2151 ++ tools/xenstore/tdb.h | 157 + tools/xenstore/utils.c | 61 + tools/xenstore/utils.h | 61 + tools/xenstore/xenstore_client.c | 615 + tools/xenstore/xenstore_control.c | 35 + tools/xenstore/xenstored_core.c | 1987 ++ tools/xenstore/xenstored_core.h | 191 + tools/xenstore/xenstored_domain.c | 710 + tools/xenstore/xenstored_domain.h | 67 + tools/xenstore/xenstored_linux.c | 73 + tools/xenstore/xenstored_netbsd.c | 73 + tools/xenstore/xenstored_probes.d | 28 + tools/xenstore/xenstored_solaris.c | 168 + tools/xenstore/xenstored_transaction.c | 291 + tools/xenstore/xenstored_transaction.h | 43 + tools/xenstore/xenstored_watch.c | 218 + tools/xenstore/xenstored_watch.h | 35 + tools/xenstore/xs.c | 900 + tools/xenstore/xs.h | 176 + tools/xenstore/xs_lib.c | 295 + tools/xenstore/xs_lib.h | 85 + tools/xenstore/xs_tdb_dump.c | 82 + tools/xentrace/Makefile | 54 + tools/xentrace/formats | 122 + tools/xentrace/setsize.c | 41 + tools/xentrace/xenctx.c | 886 + tools/xentrace/xentrace.8 | 134 + tools/xentrace/xentrace.c | 1045 + tools/xentrace/xentrace_format | 228 + tools/xentrace/xentrace_format.1 | 44 + tools/xm-test/COPYING | 340 + tools/xm-test/ChangeLog | 11 + tools/xm-test/Makefile.am | 18 + tools/xm-test/README | 317 + tools/xm-test/TODO | 3 + tools/xm-test/Writing_Tests_HOWTO | 136 + tools/xm-test/autogen | 9 + tools/xm-test/configure.ac | 170 + tools/xm-test/grouptest/create | 1 + tools/xm-test/grouptest/default | 31 + tools/xm-test/grouptest/medium | 25 + tools/xm-test/grouptest/quick | 4 + tools/xm-test/grouptest/security | 1 + tools/xm-test/grouptest/xapi | 2 + tools/xm-test/lib/XmTestLib/Console.py | 302 + tools/xm-test/lib/XmTestLib/DomainTracking.py | 61 + tools/xm-test/lib/XmTestLib/NetConfig.py | 261 + tools/xm-test/lib/XmTestLib/Test.py | 203 + tools/xm-test/lib/XmTestLib/XenAPIDomain.py | 188 + tools/xm-test/lib/XmTestLib/XenDevice.py | 272 + tools/xm-test/lib/XmTestLib/XenDomain.py | 390 + tools/xm-test/lib/XmTestLib/Xm.py | 245 + tools/xm-test/lib/XmTestLib/__init__.py | 26 + tools/xm-test/lib/XmTestLib/acm.py | 101 + tools/xm-test/lib/XmTestLib/arch.py | 127 + tools/xm-test/lib/XmTestLib/block_utils.py | 55 + tools/xm-test/lib/XmTestLib/config.py.in | 7 + tools/xm-test/lib/XmTestLib/network_utils.py | 60 + tools/xm-test/lib/XmTestLib/xapi.py | 65 + tools/xm-test/lib/XmTestReport/OSReport.py | 248 + tools/xm-test/lib/XmTestReport/ProgReport.py | 119 + tools/xm-test/lib/XmTestReport/Report.py | 156 + .../xm-test/lib/XmTestReport/ResultReport.py | 157 + tools/xm-test/lib/XmTestReport/arch.py | 46 + tools/xm-test/lib/XmTestReport/utils.py | 31 + tools/xm-test/lib/XmTestReport/xmtest.py.in | 15 + tools/xm-test/mergereport | 25 + tools/xm-test/mkreport | 71 + tools/xm-test/ramdisk/Makefile.am | 117 + .../ramdisk/README-XenSource-initrd-0.7-img | 42 + .../ramdisk/README-XenSource-initrd-0.8-img | 42 + .../ramdisk/README-XenSource-initrd-1.0-img | 46 + .../ramdisk/README-XenSource-initrd-1.1-img | 45 + tools/xm-test/ramdisk/bin/create_disk_image | 384 + tools/xm-test/ramdisk/configs/buildroot-i386 | 346 + tools/xm-test/ramdisk/configs/busybox | 465 + tools/xm-test/ramdisk/configs/uClibc | 172 + tools/xm-test/ramdisk/make-release.sh | 44 + .../patches/buildroot/add_xvd_devices.patch | 13 + .../ramdisk/patches/buildroot/hping.patch | 67 + tools/xm-test/ramdisk/skel/.profile | 3 + tools/xm-test/ramdisk/skel/etc/init.d/rcS | 19 + tools/xm-test/ramdisk/skel/etc/inittab | 5 + tools/xm-test/ramdisk/skel/root/.profile | 3 + tools/xm-test/runtest.sh | 341 + tools/xm-test/tests/Makefile.am | 44 + tools/xm-test/tests/Makefile.am.template | 21 + tools/xm-test/tests/_sanity/01_domu_proc.py | 32 + tools/xm-test/tests/_sanity/Makefile.am | 21 + .../01_block_attach_device_pos.py | 49 + .../02_block_attach_file_device_pos.py | 49 + .../04_block_attach_device_repeatedly_pos.py | 46 + ...ttach_and_dettach_device_repeatedly_pos.py | 49 + .../06_block_attach_baddomain_neg.py | 18 + .../07_block_attach_baddevice_neg.py | 53 + .../08_block_attach_bad_filedevice_neg.py | 52 + ...ttach_and_dettach_device_check_data_pos.py | 64 + ...0_block_attach_dettach_multiple_devices.py | 93 + .../11_block_attach_shared_dom0.py | 38 + .../12_block_attach_shared_domU.py | 30 + tools/xm-test/tests/block-create/Makefile.am | 28 + .../01_block-destroy_btblock_pos.py | 44 + .../02_block-destroy_rtblock_pos.py | 41 + .../03_block-destroy_nonexist_neg.py | 17 + .../04_block-destroy_nonattached_neg.py | 33 + .../05_block-destroy_byname_pos.py | 43 + .../06_block-destroy_check_list_pos.py | 42 + tools/xm-test/tests/block-destroy/Makefile.am | 26 + .../01_block_device_read_verify.py | 62 + .../02_block_device_write_verify.py | 63 + .../xm-test/tests/block-integrity/Makefile.am | 22 + .../tests/block-list/01_block-list_pos.py | 42 + .../block-list/02_block-list_attachbd_pos.py | 46 + .../block-list/03_block-list_anotherbd_pos.py | 54 + .../block-list/04_block-list_nodb_pos.py | 28 + .../block-list/05_block-list_nonexist_neg.py | 18 + .../06_block-list_checkremove_pos.py | 61 + tools/xm-test/tests/block-list/Makefile.am | 26 + .../tests/console/01_console_badopt_neg.py | 21 + .../tests/console/02_console_baddom_neg.py | 27 + tools/xm-test/tests/console/Makefile.am | 22 + .../tests/create/01_create_basic_pos.py | 49 + .../tests/create/02_create_noparm_neg.py | 17 + .../tests/create/03_create_badparm_neg.py | 19 + .../create/04_create_conflictname_neg.py | 41 + .../xm-test/tests/create/06_create_mem_neg.py | 53 + .../tests/create/07_create_mem64_pos.py | 49 + .../tests/create/08_create_mem128_pos.py | 49 + .../tests/create/09_create_mem256_pos.py | 49 + .../tests/create/10_create_fastdestroy.py | 43 + .../tests/create/11_create_concurrent_pos.py | 78 + .../create/12_create_concurrent_stress_pos.py | 59 + .../tests/create/13_create_multinic_pos.py | 27 + .../tests/create/14_create_blockroot_pos.py | 44 + .../tests/create/15_create_smallmem_pos.py | 27 + .../tests/create/16_create_smallmem_neg.py | 30 + tools/xm-test/tests/create/Makefile.am | 33 + .../tests/destroy/01_destroy_basic_pos.py | 40 + .../tests/destroy/02_destroy_noparm_neg.py | 16 + .../tests/destroy/03_destroy_nonexist_neg.py | 16 + .../tests/destroy/04_destroy_badparm_neg.py | 16 + .../tests/destroy/05_destroy_byid_pos.py | 33 + .../tests/destroy/06_destroy_dom0_neg.py | 14 + .../tests/destroy/07_destroy_stale_pos.py | 133 + tools/xm-test/tests/destroy/Makefile.am | 27 + .../xm-test/tests/dmesg/01_dmesg_basic_pos.py | 15 + .../xm-test/tests/dmesg/02_dmesg_basic_neg.py | 17 + tools/xm-test/tests/dmesg/Makefile.am | 21 + .../xm-test/tests/domid/01_domid_basic_pos.py | 16 + .../xm-test/tests/domid/02_domid_basic_neg.py | 15 + tools/xm-test/tests/domid/Makefile.am | 21 + .../tests/domname/01_domname_basic_pos.py | 17 + .../tests/domname/02_domname_basic_neg.py | 15 + tools/xm-test/tests/domname/Makefile.am | 21 + .../01_enforce_dom0_cpus_basic_pos.py | 126 + .../tests/enforce_dom0_cpus/Makefile.am | 21 + tools/xm-test/tests/help/01_help_basic_pos.py | 14 + tools/xm-test/tests/help/02_help_basic_neg.py | 14 + .../xm-test/tests/help/03_help_badparm_neg.py | 14 + tools/xm-test/tests/help/04_help_long_pos.py | 16 + .../xm-test/tests/help/05_help_nonroot_pos.py | 17 + tools/xm-test/tests/help/06_help_allcmds.py | 44 + tools/xm-test/tests/help/Makefile.am | 26 + tools/xm-test/tests/info/01_info_basic_pos.py | 11 + .../tests/info/02_info_compiledata_pos.py | 42 + tools/xm-test/tests/info/Makefile.am | 22 + tools/xm-test/tests/list/01_list_basic_pos.py | 14 + .../xm-test/tests/list/02_list_badparm_neg.py | 16 + .../tests/list/03_list_nonexist_neg.py | 17 + .../tests/list/04_list_goodparm_pos.py | 28 + tools/xm-test/tests/list/05_list_long_pos.py | 22 + tools/xm-test/tests/list/06_list_nonroot.py | 15 + tools/xm-test/tests/list/Makefile.am | 27 + .../tests/memmax/01_memmax_badparm_neg.py | 29 + tools/xm-test/tests/memmax/Makefile.am | 21 + .../tests/memset/01_memset_basic_pos.py | 99 + .../tests/memset/02_memset_badparm_neg.py | 56 + .../tests/memset/03_memset_random_pos.py | 80 + .../tests/memset/04_memset_smallmem_pos.py | 55 + tools/xm-test/tests/memset/Makefile.am | 24 + .../tests/migrate/01_migrate_localhost_pos.py | 86 + tools/xm-test/tests/migrate/Makefile.am | 20 + .../network-attach/01_network_attach_pos.py | 43 + .../02_network_attach_detach_pos.py | 49 + .../03_network_attach_detach_multiple_pos.py | 51 + .../04_network_attach_baddomain_neg.py | 15 + .../xm-test/tests/network-attach/Makefile.am | 24 + .../network/02_network_local_ping_pos.py | 71 + .../tests/network/03_network_local_tcp_pos.py | 75 + .../tests/network/04_network_local_udp_pos.py | 76 + .../tests/network/05_network_dom0_ping_pos.py | 54 + .../tests/network/06_network_dom0_tcp_pos.py | 57 + .../tests/network/07_network_dom0_udp_pos.py | 56 + .../tests/network/11_network_domU_ping_pos.py | 62 + .../tests/network/12_network_domU_tcp_pos.py | 64 + .../tests/network/13_network_domU_udp_pos.py | 64 + tools/xm-test/tests/network/Makefile.am | 34 + .../xm-test/tests/pause/01_pause_basic_pos.py | 63 + .../tests/pause/02_pause_badopt_neg.py | 32 + .../tests/pause/03_pause_badname_neg.py | 18 + .../xm-test/tests/pause/04_pause_badid_neg.py | 18 + tools/xm-test/tests/pause/Makefile.am | 22 + .../tests/reboot/01_reboot_basic_pos.py | 52 + .../tests/reboot/02_reboot_badopt_neg.py | 32 + .../tests/reboot/03_reboot_badname_neg.py | 18 + tools/xm-test/tests/reboot/Makefile.am | 23 + .../tests/restore/01_restore_basic_pos.py | 82 + .../tests/restore/02_restore_badparm_neg.py | 28 + .../restore/03_restore_badfilename_neg.py | 28 + .../restore/04_restore_withdevices_pos.py | 130 + tools/xm-test/tests/restore/Makefile.am | 24 + tools/xm-test/tests/save/01_save_basic_pos.py | 37 + .../xm-test/tests/save/02_save_badparm_neg.py | 28 + .../tests/save/03_save_bogusfile_neg.py | 39 + tools/xm-test/tests/save/Makefile.am | 24 + .../01_sched_credit_weight_cap_pos.py | 72 + tools/xm-test/tests/sched-credit/Makefile.am | 20 + .../security-acm/01_security-acm_basic.py | 121 + .../security-acm/02_security-acm_dom_start.py | 64 + .../03_security-acm_dom_conflict.py | 60 + .../security-acm/04_security-acm_dom_res.py | 69 + .../05_security-acm_dom_res_conf.py | 38 + .../06_security-acm_dom_block_attach.py | 82 + .../07_security-acm_pol_update.py | 313 + .../security-acm/08_security-acm_xapi.py | 358 + .../09_security-acm_pol_update.py | 437 + .../10_security-acm_pol_update.py | 354 + tools/xm-test/tests/security-acm/Makefile.am | 33 + tools/xm-test/tests/security-acm/acm_utils.py | 16 + .../xm-test-new-security_policy.xml | 97 + .../security-acm/xm-test-security_policy.xml | 111 + .../xm-test-update-security_policy.xml | 117 + .../tests/sedf/01_sedf_period_slice_pos.py | 62 + .../tests/sedf/02_sedf_period_lower_neg.py | 44 + .../tests/sedf/03_sedf_slice_lower_neg.py | 40 + .../tests/sedf/04_sedf_slice_upper_neg.py | 48 + .../tests/sedf/05_sedf_extratime_pos.py | 63 + .../sedf/06_sedf_extratime_disable_neg.py | 71 + tools/xm-test/tests/sedf/Makefile.am | 25 + .../tests/shutdown/01_shutdown_basic_pos.py | 54 + .../tests/shutdown/02_shutdown_badparm_neg.py | 39 + .../shutdown/03_shutdown_nonexist_neg.py | 22 + tools/xm-test/tests/shutdown/Makefile.am | 21 + .../xm-test/tests/sysrq/01_sysrq_basic_neg.py | 20 + .../xm-test/tests/sysrq/02_sysrq_sync_pos.py | 52 + .../tests/sysrq/03_sysrq_withreboot_pos.py | 40 + tools/xm-test/tests/sysrq/Makefile.am | 23 + .../tests/unpause/01_unpause_basic_pos.py | 76 + tools/xm-test/tests/unpause/Makefile.am | 20 + .../vcpu-disable/01_vcpu-disable_basic_pos.py | 82 + tools/xm-test/tests/vcpu-disable/Makefile.am | 21 + .../tests/vcpu-pin/01_vcpu-pin_basic_pos.py | 50 + tools/xm-test/tests/vcpu-pin/Makefile.am | 21 + tools/xm-test/tests/vtpm/01_vtpm-list_pos.py | 40 + tools/xm-test/tests/vtpm/02_vtpm-cat_pcrs.py | 49 + tools/xm-test/tests/vtpm/03_vtpm-susp_res.py | 99 + tools/xm-test/tests/vtpm/04_vtpm-loc_migr.py | 93 + tools/xm-test/tests/vtpm/05_vtpm-loc_migr.py | 93 + .../tests/vtpm/06_vtpm-susp_res_pcrs.py | 125 + tools/xm-test/tests/vtpm/07_vtpm-mig_pcrs.py | 119 + tools/xm-test/tests/vtpm/08_vtpm-mig_pcrs.py | 119 + tools/xm-test/tests/vtpm/09_vtpm-xapi.py | 158 + tools/xm-test/tests/vtpm/Makefile.am | 27 + tools/xm-test/tests/vtpm/vtpm_utils.py | 30 + tools/xm-test/tests/xapi/01_xapi-vm_basic.py | 61 + tools/xm-test/tests/xapi/02_xapi-vbd_basic.py | 129 + .../xm-test/tests/xapi/03_xapi-network_pos.py | 71 + tools/xm-test/tests/xapi/Makefile.am | 21 + unmodified_drivers/linux-2.6/Makefile | 6 + unmodified_drivers/linux-2.6/README | 15 + unmodified_drivers/linux-2.6/balloon/Kbuild | 9 + unmodified_drivers/linux-2.6/balloon/Makefile | 3 + unmodified_drivers/linux-2.6/blkfront/Kbuild | 5 + .../linux-2.6/blkfront/Makefile | 3 + .../asm-generic/pgtable-nopmd.h | 14 + .../asm-generic/pgtable-nopud.h | 15 + .../linux-2.6/compat-include/linux/io.h | 10 + .../linux-2.6/compat-include/linux/mutex.h | 31 + .../compat-include/xen/platform-compat.h | 158 + unmodified_drivers/linux-2.6/mkbuildtree | 88 + unmodified_drivers/linux-2.6/netfront/Kbuild | 5 + .../linux-2.6/netfront/Makefile | 3 + unmodified_drivers/linux-2.6/overrides.mk | 17 + .../linux-2.6/platform-pci/Kbuild | 22 + .../linux-2.6/platform-pci/Makefile | 3 + .../linux-2.6/platform-pci/evtchn.c | 356 + .../linux-2.6/platform-pci/machine_reboot.c | 122 + .../linux-2.6/platform-pci/panic-handler.c | 42 + .../linux-2.6/platform-pci/platform-compat.c | 143 + .../linux-2.6/platform-pci/platform-pci.c | 394 + .../linux-2.6/platform-pci/platform-pci.h | 32 + .../linux-2.6/platform-pci/xen_support.c | 74 + .../linux-2.6/xenbus/empty_directory | 0 xen/COPYING | 367 + xen/Makefile | 161 + xen/Rules.mk | 130 + xen/arch/ia64/Makefile | 74 + xen/arch/ia64/Rules.mk | 90 + xen/arch/ia64/asm-offsets.c | 273 + xen/arch/ia64/asm-xsi-offsets.c | 76 + xen/arch/ia64/linux-xen/Makefile | 25 + xen/arch/ia64/linux-xen/README.origin | 48 + xen/arch/ia64/linux-xen/acpi.c | 1100 + xen/arch/ia64/linux-xen/acpi_numa.c | 277 + xen/arch/ia64/linux-xen/cmdline.c | 131 + xen/arch/ia64/linux-xen/efi.c | 1334 ++ xen/arch/ia64/linux-xen/entry.S | 1851 ++ xen/arch/ia64/linux-xen/entry.h | 85 + xen/arch/ia64/linux-xen/head.S | 1264 ++ xen/arch/ia64/linux-xen/hpsim_ssc.h | 55 + xen/arch/ia64/linux-xen/iosapic.c | 1268 ++ xen/arch/ia64/linux-xen/irq_ia64.c | 336 + xen/arch/ia64/linux-xen/mca.c | 1950 ++ xen/arch/ia64/linux-xen/mca_asm.S | 1250 ++ xen/arch/ia64/linux-xen/minstate.h | 306 + xen/arch/ia64/linux-xen/mm_contig.c | 345 + xen/arch/ia64/linux-xen/numa.c | 67 + xen/arch/ia64/linux-xen/perfmon.c | 7879 +++++++ .../ia64/linux-xen/perfmon_default_smpl.c | 311 + xen/arch/ia64/linux-xen/perfmon_generic.h | 45 + xen/arch/ia64/linux-xen/perfmon_itanium.h | 115 + xen/arch/ia64/linux-xen/perfmon_mckinley.h | 187 + xen/arch/ia64/linux-xen/perfmon_montecito.h | 269 + xen/arch/ia64/linux-xen/process-linux-xen.c | 891 + xen/arch/ia64/linux-xen/sal.c | 386 + xen/arch/ia64/linux-xen/setup.c | 1052 + xen/arch/ia64/linux-xen/smp.c | 474 + xen/arch/ia64/linux-xen/smpboot.c | 963 + xen/arch/ia64/linux-xen/sn/Makefile | 1 + xen/arch/ia64/linux-xen/sn/kernel/Makefile | 5 + .../ia64/linux-xen/sn/kernel/README.origin | 12 + xen/arch/ia64/linux-xen/sn/kernel/io_init.c | 793 + xen/arch/ia64/linux-xen/sn/kernel/iomv.c | 82 + xen/arch/ia64/linux-xen/sn/kernel/irq.c | 550 + xen/arch/ia64/linux-xen/sn/kernel/setup.c | 803 + xen/arch/ia64/linux-xen/sn/kernel/sn2_smp.c | 621 + xen/arch/ia64/linux-xen/sort.c | 122 + xen/arch/ia64/linux-xen/time.c | 273 + xen/arch/ia64/linux-xen/tlb.c | 206 + xen/arch/ia64/linux-xen/unaligned.c | 1985 ++ xen/arch/ia64/linux-xen/unwind.c | 2390 +++ xen/arch/ia64/linux-xen/unwind_decoder.c | 459 + xen/arch/ia64/linux-xen/unwind_i.h | 164 + xen/arch/ia64/linux/Makefile | 57 + xen/arch/ia64/linux/README.origin | 34 + xen/arch/ia64/linux/bitop.c | 88 + xen/arch/ia64/linux/carta_random.S | 54 + xen/arch/ia64/linux/clear_page.S | 77 + xen/arch/ia64/linux/copy_page_mck.S | 185 + xen/arch/ia64/linux/dig/Makefile | 1 + xen/arch/ia64/linux/dig/README.origin | 7 + xen/arch/ia64/linux/dig/machvec.c | 3 + xen/arch/ia64/linux/efi_stub.S | 86 + xen/arch/ia64/linux/extable.c | 90 + xen/arch/ia64/linux/flush.S | 61 + xen/arch/ia64/linux/hp/Makefile | 1 + xen/arch/ia64/linux/hp/zx1/Makefile | 1 + xen/arch/ia64/linux/hp/zx1/README.origin | 7 + xen/arch/ia64/linux/hp/zx1/hpzx1_machvec.c | 3 + xen/arch/ia64/linux/hpsim.S | 10 + xen/arch/ia64/linux/idiv32.S | 83 + xen/arch/ia64/linux/idiv64.S | 80 + xen/arch/ia64/linux/io.c | 164 + xen/arch/ia64/linux/irq_lsapic.c | 37 + xen/arch/ia64/linux/linuxextable.c | 71 + xen/arch/ia64/linux/machvec.c | 70 + xen/arch/ia64/linux/memcpy_mck.S | 661 + xen/arch/ia64/linux/memset.S | 362 + xen/arch/ia64/linux/numa.c | 49 + xen/arch/ia64/linux/pal.S | 298 + xen/arch/ia64/linux/pcdp.h | 111 + xen/arch/ia64/linux/sn/Makefile | 2 + xen/arch/ia64/linux/sn/kernel/Makefile | 3 + xen/arch/ia64/linux/sn/kernel/README.origin | 9 + xen/arch/ia64/linux/sn/kernel/machvec.c | 11 + xen/arch/ia64/linux/sn/kernel/pio_phys.S | 71 + xen/arch/ia64/linux/sn/kernel/ptc_deadlock.S | 92 + xen/arch/ia64/linux/sn/pci/Makefile | 1 + xen/arch/ia64/linux/sn/pci/pcibr/Makefile | 1 + .../ia64/linux/sn/pci/pcibr/README.origin | 7 + xen/arch/ia64/linux/sn/pci/pcibr/pcibr_reg.c | 285 + xen/arch/ia64/linux/strlen.S | 192 + xen/arch/ia64/tools/README.RunVT | 46 + xen/arch/ia64/tools/README.xenia64 | 98 + xen/arch/ia64/tools/README.xenoprof | 154 + xen/arch/ia64/tools/linux-xen-diffs | 25 + xen/arch/ia64/tools/p2m_expose/Makefile | 31 + .../ia64/tools/p2m_expose/README.p2m_expose | 12 + xen/arch/ia64/tools/p2m_expose/expose_p2m.c | 185 + xen/arch/ia64/tools/p2m_foreign/Makefile | 52 + xen/arch/ia64/tools/p2m_foreign/p2m_foreign.c | 233 + xen/arch/ia64/tools/privify/Makefile | 9 + xen/arch/ia64/tools/privify/README.privify | 8 + xen/arch/ia64/tools/privify/privify.c | 360 + xen/arch/ia64/tools/privify/privify.h | 34 + xen/arch/ia64/tools/privify/privify_elf64.c | 120 + xen/arch/ia64/tools/privop/Makefile | 13 + xen/arch/ia64/tools/privop/pohcalls.S | 30 + xen/arch/ia64/tools/privop/postat.c | 27 + xen/arch/ia64/tools/sparse-merge | 152 + xen/arch/ia64/tools/xelilo/elilo.README | 20 + xen/arch/ia64/tools/xelilo/xlilo.efi | Bin 0 -> 373671 bytes xen/arch/ia64/vmx/Makefile | 24 + xen/arch/ia64/vmx/mmio.c | 577 + xen/arch/ia64/vmx/optvfault.S | 1186 ++ xen/arch/ia64/vmx/pal_emul.c | 62 + xen/arch/ia64/vmx/save.c | 69 + xen/arch/ia64/vmx/sioemu.c | 243 + xen/arch/ia64/vmx/vacpi.c | 268 + xen/arch/ia64/vmx/viosapic.c | 405 + xen/arch/ia64/vmx/vlsapic.c | 962 + xen/arch/ia64/vmx/vmmu.c | 626 + xen/arch/ia64/vmx/vmx_entry.S | 761 + xen/arch/ia64/vmx/vmx_fault.c | 605 + xen/arch/ia64/vmx/vmx_hypercall.c | 229 + xen/arch/ia64/vmx/vmx_init.c | 663 + xen/arch/ia64/vmx/vmx_interrupt.c | 114 + xen/arch/ia64/vmx/vmx_ivt.S | 1364 ++ xen/arch/ia64/vmx/vmx_minstate.h | 306 + xen/arch/ia64/vmx/vmx_phy_mode.c | 344 + xen/arch/ia64/vmx/vmx_support.c | 101 + xen/arch/ia64/vmx/vmx_utility.c | 674 + xen/arch/ia64/vmx/vmx_vcpu.c | 587 + xen/arch/ia64/vmx/vmx_vcpu_save.c | 367 + xen/arch/ia64/vmx/vmx_virt.c | 1636 ++ xen/arch/ia64/vmx/vmx_vsa.S | 84 + xen/arch/ia64/vmx/vtlb.c | 755 + xen/arch/ia64/xen/Makefile | 44 + xen/arch/ia64/xen/cpufreq/Makefile | 1 + xen/arch/ia64/xen/cpufreq/cpufreq.c | 314 + xen/arch/ia64/xen/crash.c | 48 + xen/arch/ia64/xen/dom0_ops.c | 623 + xen/arch/ia64/xen/dom_fw_asm.S | 41 + xen/arch/ia64/xen/dom_fw_common.c | 568 + xen/arch/ia64/xen/dom_fw_dom0.c | 561 + xen/arch/ia64/xen/dom_fw_domu.c | 238 + xen/arch/ia64/xen/dom_fw_sn2.c | 92 + xen/arch/ia64/xen/dom_fw_utils.c | 361 + xen/arch/ia64/xen/domain.c | 2370 +++ xen/arch/ia64/xen/faults.c | 825 + xen/arch/ia64/xen/flushd.S | 65 + xen/arch/ia64/xen/flushtlb.c | 91 + xen/arch/ia64/xen/fw_emul.c | 1591 ++ xen/arch/ia64/xen/gdbstub.c | 819 + xen/arch/ia64/xen/hpsimserial.c | 23 + xen/arch/ia64/xen/hypercall.c | 552 + xen/arch/ia64/xen/hyperprivop.S | 2225 ++ xen/arch/ia64/xen/idle0_task.c | 29 + xen/arch/ia64/xen/irq.c | 524 + xen/arch/ia64/xen/ivt.S | 1429 ++ xen/arch/ia64/xen/machine_kexec.c | 213 + xen/arch/ia64/xen/mm.c | 3426 ++++ xen/arch/ia64/xen/mm_init.c | 111 + xen/arch/ia64/xen/oprofile/Makefile | 1 + xen/arch/ia64/xen/oprofile/perfmon.c | 207 + xen/arch/ia64/xen/oprofile/xenoprof.c | 91 + xen/arch/ia64/xen/pcdp.c | 281 + xen/arch/ia64/xen/pci.c | 134 + xen/arch/ia64/xen/platform_hypercall.c | 87 + xen/arch/ia64/xen/privop.c | 889 + xen/arch/ia64/xen/privop_stat.c | 153 + xen/arch/ia64/xen/regionreg.c | 430 + xen/arch/ia64/xen/relocate_kernel.S | 206 + xen/arch/ia64/xen/sn_console.c | 147 + xen/arch/ia64/xen/tlb_track.c | 532 + xen/arch/ia64/xen/vcpu.c | 2315 +++ xen/arch/ia64/xen/vhpt.c | 586 + xen/arch/ia64/xen/xen.lds.S | 258 + xen/arch/ia64/xen/xenasm.S | 649 + xen/arch/ia64/xen/xenmem.c | 252 + xen/arch/ia64/xen/xenmisc.c | 142 + xen/arch/ia64/xen/xenpatch.c | 149 + xen/arch/ia64/xen/xensetup.c | 742 + xen/arch/ia64/xen/xentime.c | 263 + xen/arch/x86/Makefile | 93 + xen/arch/x86/Rules.mk | 60 + xen/arch/x86/acpi/Makefile | 4 + xen/arch/x86/acpi/boot.c | 1004 + xen/arch/x86/acpi/cpu_idle.c | 789 + xen/arch/x86/acpi/cpufreq/Makefile | 2 + xen/arch/x86/acpi/cpufreq/cpufreq.c | 604 + xen/arch/x86/acpi/cpufreq/powernow.c | 326 + xen/arch/x86/acpi/cpuidle_menu.c | 132 + xen/arch/x86/acpi/power.c | 348 + xen/arch/x86/acpi/suspend.c | 70 + xen/arch/x86/acpi/wakeup_prot.S | 267 + xen/arch/x86/apic.c | 1322 ++ xen/arch/x86/bitops.c | 101 + xen/arch/x86/boot/Makefile | 4 + xen/arch/x86/boot/cmdline.S | 364 + xen/arch/x86/boot/edd.S | 161 + xen/arch/x86/boot/head.S | 207 + xen/arch/x86/boot/mem.S | 78 + xen/arch/x86/boot/mkelf32.c | 407 + xen/arch/x86/boot/trampoline.S | 213 + xen/arch/x86/boot/video.S | 1006 + xen/arch/x86/boot/video.h | 31 + xen/arch/x86/boot/wakeup.S | 196 + xen/arch/x86/boot/x86_32.S | 107 + xen/arch/x86/boot/x86_64.S | 125 + xen/arch/x86/clear_page.S | 26 + xen/arch/x86/compat.c | 40 + xen/arch/x86/cpu/Makefile | 11 + xen/arch/x86/cpu/amd.c | 547 + xen/arch/x86/cpu/amd.h | 103 + xen/arch/x86/cpu/centaur.c | 102 + xen/arch/x86/cpu/common.c | 619 + xen/arch/x86/cpu/cpu.h | 30 + xen/arch/x86/cpu/cyrix.c | 399 + xen/arch/x86/cpu/intel.c | 302 + xen/arch/x86/cpu/intel_cacheinfo.c | 437 + xen/arch/x86/cpu/mcheck/Makefile | 10 + xen/arch/x86/cpu/mcheck/amd_f10.c | 131 + xen/arch/x86/cpu/mcheck/amd_k8.c | 324 + xen/arch/x86/cpu/mcheck/amd_nonfatal.c | 303 + xen/arch/x86/cpu/mcheck/k7.c | 94 + xen/arch/x86/cpu/mcheck/mce.c | 576 + xen/arch/x86/cpu/mcheck/mce.h | 30 + xen/arch/x86/cpu/mcheck/non-fatal.c | 96 + xen/arch/x86/cpu/mcheck/p4.c | 270 + xen/arch/x86/cpu/mcheck/p5.c | 52 + xen/arch/x86/cpu/mcheck/p6.c | 118 + xen/arch/x86/cpu/mcheck/winchip.c | 37 + xen/arch/x86/cpu/mcheck/x86_mca.h | 72 + xen/arch/x86/cpu/mtrr/Makefile | 5 + xen/arch/x86/cpu/mtrr/amd.c | 121 + xen/arch/x86/cpu/mtrr/cyrix.c | 379 + xen/arch/x86/cpu/mtrr/generic.c | 459 + xen/arch/x86/cpu/mtrr/main.c | 703 + xen/arch/x86/cpu/mtrr/mtrr.h | 87 + xen/arch/x86/cpu/mtrr/state.c | 78 + xen/arch/x86/cpu/transmeta.c | 108 + xen/arch/x86/crash.c | 118 + xen/arch/x86/delay.c | 30 + xen/arch/x86/dmi_scan.c | 499 + xen/arch/x86/domain.c | 1903 ++ xen/arch/x86/domain_build.c | 929 + xen/arch/x86/domctl.c | 1131 ++ xen/arch/x86/e820.c | 467 + xen/arch/x86/extable.c | 79 + xen/arch/x86/flushtlb.c | 170 + xen/arch/x86/gdbstub.c | 89 + xen/arch/x86/genapic/Makefile | 6 + xen/arch/x86/genapic/bigsmp.c | 54 + xen/arch/x86/genapic/default.c | 26 + xen/arch/x86/genapic/delivery.c | 68 + xen/arch/x86/genapic/probe.c | 109 + xen/arch/x86/genapic/summit.c | 26 + xen/arch/x86/genapic/x2apic.c | 84 + xen/arch/x86/hpet.c | 316 + xen/arch/x86/hvm/Makefile | 21 + xen/arch/x86/hvm/emulate.c | 1027 + xen/arch/x86/hvm/hpet.c | 598 + xen/arch/x86/hvm/hvm.c | 2672 +++ xen/arch/x86/hvm/i8254.c | 582 + xen/arch/x86/hvm/intercept.c | 232 + xen/arch/x86/hvm/io.c | 357 + xen/arch/x86/hvm/irq.c | 543 + xen/arch/x86/hvm/mtrr.c | 725 + xen/arch/x86/hvm/pmtimer.c | 284 + xen/arch/x86/hvm/rtc.c | 520 + xen/arch/x86/hvm/save.c | 76 + xen/arch/x86/hvm/stdvga.c | 633 + xen/arch/x86/hvm/svm/Makefile | 6 + xen/arch/x86/hvm/svm/asid.c | 232 + xen/arch/x86/hvm/svm/emulate.c | 214 + xen/arch/x86/hvm/svm/entry.S | 178 + xen/arch/x86/hvm/svm/intr.c | 222 + xen/arch/x86/hvm/svm/svm.c | 1426 ++ xen/arch/x86/hvm/svm/vmcb.c | 403 + xen/arch/x86/hvm/vioapic.c | 524 + xen/arch/x86/hvm/viridian.c | 349 + xen/arch/x86/hvm/vlapic.c | 1008 + xen/arch/x86/hvm/vmsi.c | 195 + xen/arch/x86/hvm/vmx/Makefile | 7 + xen/arch/x86/hvm/vmx/entry.S | 198 + xen/arch/x86/hvm/vmx/intr.c | 225 + xen/arch/x86/hvm/vmx/realmode.c | 245 + xen/arch/x86/hvm/vmx/vmcs.c | 1065 + xen/arch/x86/hvm/vmx/vmx.c | 2342 +++ xen/arch/x86/hvm/vmx/vpmu.c | 111 + xen/arch/x86/hvm/vmx/vpmu_core2.c | 481 + xen/arch/x86/hvm/vpic.c | 470 + xen/arch/x86/hvm/vpt.c | 423 + xen/arch/x86/i387.c | 139 + xen/arch/x86/i8259.c | 426 + xen/arch/x86/io_apic.c | 2289 +++ xen/arch/x86/ioport_emulate.c | 131 + xen/arch/x86/irq.c | 1021 + xen/arch/x86/machine_kexec.c | 169 + xen/arch/x86/microcode.c | 186 + xen/arch/x86/microcode_amd.c | 368 + xen/arch/x86/microcode_intel.c | 370 + xen/arch/x86/mm.c | 4558 +++++ xen/arch/x86/mm/Makefile | 5 + xen/arch/x86/mm/hap/Makefile | 11 + xen/arch/x86/mm/hap/guest_walk.c | 185 + xen/arch/x86/mm/hap/hap.c | 712 + xen/arch/x86/mm/hap/p2m-ept.c | 506 + xen/arch/x86/mm/hap/private.h | 68 + xen/arch/x86/mm/p2m.c | 1196 ++ xen/arch/x86/mm/page-guest32.h | 100 + xen/arch/x86/mm/paging.c | 663 + xen/arch/x86/mm/shadow/Makefile | 5 + xen/arch/x86/mm/shadow/common.c | 3894 ++++ xen/arch/x86/mm/shadow/multi.c | 5416 +++++ xen/arch/x86/mm/shadow/multi.h | 131 + xen/arch/x86/mm/shadow/private.h | 889 + xen/arch/x86/mm/shadow/types.h | 555 + xen/arch/x86/mpparse.c | 1153 ++ xen/arch/x86/msi.c | 768 + xen/arch/x86/nmi.c | 474 + xen/arch/x86/numa.c | 334 + xen/arch/x86/oprofile/Makefile | 6 + xen/arch/x86/oprofile/backtrace.c | 132 + xen/arch/x86/oprofile/nmi_int.c | 420 + xen/arch/x86/oprofile/op_counter.h | 29 + xen/arch/x86/oprofile/op_model_athlon.c | 186 + xen/arch/x86/oprofile/op_model_p4.c | 735 + xen/arch/x86/oprofile/op_model_ppro.c | 171 + xen/arch/x86/oprofile/op_x86_model.h | 51 + xen/arch/x86/oprofile/xenoprof.c | 82 + xen/arch/x86/pci.c | 117 + xen/arch/x86/physdev.c | 385 + xen/arch/x86/platform_hypercall.c | 425 + xen/arch/x86/rwlock.c | 28 + xen/arch/x86/setup.c | 1134 ++ xen/arch/x86/shutdown.c | 365 + xen/arch/x86/smp.c | 371 + xen/arch/x86/smpboot.c | 1519 ++ xen/arch/x86/srat.c | 315 + xen/arch/x86/string.c | 81 + xen/arch/x86/sysctl.c | 137 + xen/arch/x86/tboot.c | 119 + xen/arch/x86/time.c | 1272 ++ xen/arch/x86/trace.c | 213 + xen/arch/x86/traps.c | 3352 +++ xen/arch/x86/usercopy.c | 153 + xen/arch/x86/x86_32/Makefile | 11 + xen/arch/x86/x86_32/asm-offsets.c | 127 + xen/arch/x86/x86_32/domain_page.c | 261 + xen/arch/x86/x86_32/entry.S | 759 + xen/arch/x86/x86_32/gdbstub.c | 82 + xen/arch/x86/x86_32/gpr_switch.S | 44 + xen/arch/x86/x86_32/machine_kexec.c | 33 + xen/arch/x86/x86_32/mm.c | 389 + xen/arch/x86/x86_32/seg_fixup.c | 538 + xen/arch/x86/x86_32/supervisor_mode_kernel.S | 145 + xen/arch/x86/x86_32/traps.c | 601 + xen/arch/x86/x86_32/xen.lds.S | 105 + xen/arch/x86/x86_64/Makefile | 27 + xen/arch/x86/x86_64/asm-offsets.c | 153 + xen/arch/x86/x86_64/compat.c | 30 + xen/arch/x86/x86_64/compat/Makefile | 2 + xen/arch/x86/x86_64/compat/entry.S | 463 + xen/arch/x86/x86_64/compat/mm.c | 340 + xen/arch/x86/x86_64/compat/traps.c | 374 + xen/arch/x86/x86_64/compat_kexec.S | 184 + xen/arch/x86/x86_64/cpu_idle.c | 128 + xen/arch/x86/x86_64/domain.c | 83 + xen/arch/x86/x86_64/entry.S | 749 + xen/arch/x86/x86_64/gdbstub.c | 151 + xen/arch/x86/x86_64/gpr_switch.S | 66 + xen/arch/x86/x86_64/machine_kexec.c | 32 + xen/arch/x86/x86_64/mm.c | 524 + xen/arch/x86/x86_64/physdev.c | 57 + xen/arch/x86/x86_64/platform_hypercall.c | 42 + xen/arch/x86/x86_64/traps.c | 633 + xen/arch/x86/x86_64/xen.lds.S | 103 + xen/arch/x86/x86_emulate.c | 17 + xen/arch/x86/x86_emulate/x86_emulate.c | 3982 ++++ xen/arch/x86/x86_emulate/x86_emulate.h | 408 + xen/common/Makefile | 53 + xen/common/bitmap.c | 519 + xen/common/compat/Makefile | 9 + xen/common/compat/domain.c | 112 + xen/common/compat/grant_table.c | 236 + xen/common/compat/kernel.c | 55 + xen/common/compat/memory.c | 372 + xen/common/compat/multicall.c | 38 + xen/common/compat/schedule.c | 51 + xen/common/compat/xenoprof.c | 41 + xen/common/compat/xlat.c | 73 + xen/common/domain.c | 848 + xen/common/domctl.c | 875 + xen/common/event_channel.c | 1129 ++ xen/common/gdbstub.c | 728 + xen/common/grant_table.c | 1789 ++ xen/common/hvm/Makefile | 1 + xen/common/hvm/save.c | 224 + xen/common/kernel.c | 296 + xen/common/kexec.c | 529 + xen/common/keyhandler.c | 368 + xen/common/lib.c | 441 + xen/common/libelf/Makefile | 4 + xen/common/libelf/README | 1 + xen/common/libelf/libelf-dominfo.c | 517 + xen/common/libelf/libelf-loader.c | 267 + xen/common/libelf/libelf-private.h | 82 + xen/common/libelf/libelf-relocate.c | 358 + xen/common/libelf/libelf-tools.c | 249 + xen/common/memory.c | 642 + xen/common/multicall.c | 102 + xen/common/page_alloc.c | 1060 + xen/common/perfc.c | 277 + xen/common/rangeset.c | 414 + xen/common/rcupdate.c | 348 + xen/common/sched_credit.c | 1403 ++ xen/common/sched_sedf.c | 1483 ++ xen/common/schedule.c | 940 + xen/common/shutdown.c | 64 + xen/common/softirq.c | 166 + xen/common/stop_machine.c | 159 + xen/common/string.c | 446 + xen/common/symbols-dummy.c | 16 + xen/common/symbols.c | 164 + xen/common/sysctl.c | 246 + xen/common/time.c | 77 + xen/common/timer.c | 511 + xen/common/trace.c | 562 + xen/common/version.c | 57 + xen/common/vsprintf.c | 810 + xen/common/xencomm.c | 518 + xen/common/xenoprof.c | 891 + xen/common/xmalloc.c | 286 + xen/common/xmalloc_tlsf.c | 599 + xen/drivers/Makefile | 6 + xen/drivers/acpi/Makefile | 10 + xen/drivers/acpi/hwregs.c | 688 + xen/drivers/acpi/numa.c | 183 + xen/drivers/acpi/osl.c | 237 + xen/drivers/acpi/pmstat.c | 158 + xen/drivers/acpi/reboot.c | 38 + xen/drivers/acpi/tables.c | 313 + xen/drivers/acpi/tables/Makefile | 5 + xen/drivers/acpi/tables/tbfadt.c | 458 + xen/drivers/acpi/tables/tbinstal.c | 151 + xen/drivers/acpi/tables/tbutils.c | 560 + xen/drivers/acpi/tables/tbxface.c | 211 + xen/drivers/acpi/tables/tbxfroot.c | 275 + xen/drivers/acpi/utilities/Makefile | 2 + xen/drivers/acpi/utilities/utglobal.c | 740 + xen/drivers/acpi/utilities/utmisc.c | 229 + xen/drivers/char/Makefile | 3 + xen/drivers/char/console.c | 1002 + xen/drivers/char/ns16550.c | 428 + xen/drivers/char/serial.c | 506 + xen/drivers/cpufreq/Makefile | 3 + xen/drivers/cpufreq/cpufreq.c | 309 + xen/drivers/cpufreq/cpufreq_ondemand.c | 246 + xen/drivers/cpufreq/utility.c | 370 + xen/drivers/passthrough/Makefile | 6 + xen/drivers/passthrough/amd/Makefile | 6 + xen/drivers/passthrough/amd/iommu_acpi.c | 961 + xen/drivers/passthrough/amd/iommu_detect.c | 159 + xen/drivers/passthrough/amd/iommu_init.c | 669 + xen/drivers/passthrough/amd/iommu_intr.c | 220 + xen/drivers/passthrough/amd/iommu_map.c | 582 + xen/drivers/passthrough/amd/pci_amd_iommu.c | 470 + xen/drivers/passthrough/io.c | 326 + xen/drivers/passthrough/iommu.c | 309 + xen/drivers/passthrough/pci.c | 249 + xen/drivers/passthrough/vtd/Makefile | 7 + xen/drivers/passthrough/vtd/dmar.c | 518 + xen/drivers/passthrough/vtd/dmar.h | 96 + xen/drivers/passthrough/vtd/extern.h | 48 + xen/drivers/passthrough/vtd/intremap.c | 541 + xen/drivers/passthrough/vtd/iommu.c | 1949 ++ xen/drivers/passthrough/vtd/iommu.h | 458 + xen/drivers/passthrough/vtd/qinval.c | 459 + xen/drivers/passthrough/vtd/utils.c | 317 + xen/drivers/passthrough/vtd/vtd.h | 112 + xen/drivers/passthrough/vtd/x86/Makefile | 1 + xen/drivers/passthrough/vtd/x86/vtd.c | 141 + xen/drivers/pci/Makefile | 1 + xen/drivers/pci/pci.c | 64 + xen/drivers/video/Makefile | 5 + xen/drivers/video/font.h | 22 + xen/drivers/video/font_8x14.c | 4118 ++++ xen/drivers/video/font_8x16.c | 4630 +++++ xen/drivers/video/font_8x8.c | 2582 +++ xen/drivers/video/vesa.c | 306 + xen/drivers/video/vga.c | 144 + xen/include/Makefile | 70 + xen/include/acpi/acconfig.h | 206 + xen/include/acpi/acexcep.h | 303 + xen/include/acpi/acglobal.h | 377 + xen/include/acpi/achware.h | 131 + xen/include/acpi/aclocal.h | 965 + xen/include/acpi/acmacros.h | 707 + xen/include/acpi/acnames.h | 83 + xen/include/acpi/acnamesp.h | 305 + xen/include/acpi/acobject.h | 422 + xen/include/acpi/acoutput.h | 185 + xen/include/acpi/acpi.h | 69 + xen/include/acpi/acpi_bus.h | 389 + xen/include/acpi/acpi_drivers.h | 111 + xen/include/acpi/acpiosxf.h | 283 + xen/include/acpi/acpixf.h | 342 + xen/include/acpi/acstruct.h | 231 + xen/include/acpi/actables.h | 118 + xen/include/acpi/actbl.h | 296 + xen/include/acpi/actbl1.h | 727 + xen/include/acpi/actypes.h | 1237 ++ xen/include/acpi/acutils.h | 571 + xen/include/acpi/cpufreq/cpufreq.h | 207 + xen/include/acpi/cpufreq/processor_perf.h | 67 + xen/include/acpi/pdc_intel.h | 33 + xen/include/acpi/platform/acenv.h | 363 + xen/include/acpi/platform/acgcc.h | 65 + xen/include/acpi/platform/aclinux.h | 91 + xen/include/asm-ia64/bug.h | 10 + xen/include/asm-ia64/bundle.h | 233 + xen/include/asm-ia64/config.h | 296 + xen/include/asm-ia64/debugger.h | 116 + xen/include/asm-ia64/dom_fw.h | 197 + xen/include/asm-ia64/dom_fw_common.h | 115 + xen/include/asm-ia64/dom_fw_dom0.h | 41 + xen/include/asm-ia64/dom_fw_domu.h | 45 + xen/include/asm-ia64/dom_fw_utils.h | 45 + xen/include/asm-ia64/domain.h | 329 + xen/include/asm-ia64/elf.h | 68 + xen/include/asm-ia64/event.h | 85 + xen/include/asm-ia64/flushtlb.h | 90 + xen/include/asm-ia64/grant_table.h | 78 + xen/include/asm-ia64/guest_access.h | 30 + xen/include/asm-ia64/hardirq.h | 10 + xen/include/asm-ia64/hvm/support.h | 28 + xen/include/asm-ia64/hvm/vacpi.h | 46 + xen/include/asm-ia64/hvm/vlapic.h | 6 + xen/include/asm-ia64/hypercall.h | 28 + xen/include/asm-ia64/ia64_int.h | 56 + xen/include/asm-ia64/init.h | 4 + xen/include/asm-ia64/iocap.h | 18 + xen/include/asm-ia64/kexec.h | 17 + xen/include/asm-ia64/linux-null/README.origin | 3 + .../linux-null/asm-generic/pci-dma-compat.h | 1 + xen/include/asm-ia64/linux-null/asm/cyclone.h | 1 + xen/include/asm-ia64/linux-null/asm/desc.h | 1 + xen/include/asm-ia64/linux-null/asm/ia32.h | 1 + xen/include/asm-ia64/linux-null/asm/mman.h | 1 + xen/include/asm-ia64/linux-null/asm/mmzone.h | 1 + xen/include/asm-ia64/linux-null/asm/module.h | 1 + xen/include/asm-ia64/linux-null/asm/nmi.h | 7 + xen/include/asm-ia64/linux-null/asm/pdb.h | 1 + .../asm-ia64/linux-null/asm/ptrace_offsets.h | 1 + .../asm-ia64/linux-null/asm/scatterlist.h | 1 + .../asm-ia64/linux-null/asm/semaphore.h | 1 + xen/include/asm-ia64/linux-null/asm/serial.h | 1 + xen/include/asm-ia64/linux-null/asm/signal.h | 1 + xen/include/asm-ia64/linux-null/asm/sn/arch.h | 1 + xen/include/asm-ia64/linux-null/asm/sn/geo.h | 1 + .../asm-ia64/linux-null/asm/sn/nodepda.h | 1 + .../asm-ia64/linux-null/asm/sn/sn_cpuid.h | 1 + xen/include/asm-ia64/linux-null/asm/ustack.h | 1 + .../asm-ia64/linux-null/asm/xen/hypervisor.h | 1 + .../asm-ia64/linux-null/linux/bootmem.h | 1 + .../asm-ia64/linux-null/linux/capability.h | 1 + .../asm-ia64/linux-null/linux/completion.h | 1 + .../asm-ia64/linux-null/linux/device.h | 1 + .../asm-ia64/linux-null/linux/dmapool.h | 1 + xen/include/asm-ia64/linux-null/linux/file.h | 1 + .../asm-ia64/linux-null/linux/kallsyms.h | 1 + .../asm-ia64/linux-null/linux/kernel_stat.h | 1 + .../asm-ia64/linux-null/linux/mmzone.h | 1 + .../asm-ia64/linux-null/linux/module.h | 1 + xen/include/asm-ia64/linux-null/linux/mount.h | 1 + xen/include/asm-ia64/linux-null/linux/node.h | 1 + .../asm-ia64/linux-null/linux/page-flags.h | 1 + .../asm-ia64/linux-null/linux/pagemap.h | 1 + .../asm-ia64/linux-null/linux/platform.h | 1 + xen/include/asm-ia64/linux-null/linux/pm.h | 1 + xen/include/asm-ia64/linux-null/linux/poll.h | 1 + .../asm-ia64/linux-null/linux/proc_fs.h | 1 + .../asm-ia64/linux-null/linux/profile.h | 1 + .../asm-ia64/linux-null/linux/ptrace.h | 1 + .../asm-ia64/linux-null/linux/random.h | 1 + .../asm-ia64/linux-null/linux/rcupdate.h | 1 + xen/include/asm-ia64/linux-null/linux/rtc.h | 1 + xen/include/asm-ia64/linux-null/linux/rwsem.h | 1 + .../asm-ia64/linux-null/linux/seq_file.h | 1 + .../asm-ia64/linux-null/linux/serial.h | 1 + .../asm-ia64/linux-null/linux/serial_core.h | 1 + .../asm-ia64/linux-null/linux/signal.h | 1 + xen/include/asm-ia64/linux-null/linux/slab.h | 1 + .../asm-ia64/linux-null/linux/smp_lock.h | 1 + xen/include/asm-ia64/linux-null/linux/swap.h | 1 + .../asm-ia64/linux-null/linux/sysctl.h | 1 + .../asm-ia64/linux-null/linux/threads.h | 1 + xen/include/asm-ia64/linux-null/linux/tty.h | 1 + xen/include/asm-ia64/linux-null/linux/vfs.h | 1 + .../asm-ia64/linux-null/linux/vmalloc.h | 1 + .../asm-ia64/linux-null/linux/workqueue.h | 1 + .../linux-xen/asm-generic/README.origin | 8 + .../linux-xen/asm-generic/pgtable-nopud.h | 69 + .../asm-ia64/linux-xen/asm/README.origin | 48 + xen/include/asm-ia64/linux-xen/asm/acpi.h | 170 + xen/include/asm-ia64/linux-xen/asm/atomic.h | 188 + xen/include/asm-ia64/linux-xen/asm/cache.h | 37 + .../asm-ia64/linux-xen/asm/gcc_intrin.h | 605 + xen/include/asm-ia64/linux-xen/asm/ia64regs.h | 104 + xen/include/asm-ia64/linux-xen/asm/io.h | 490 + xen/include/asm-ia64/linux-xen/asm/iosapic.h | 179 + xen/include/asm-ia64/linux-xen/asm/kregs.h | 167 + xen/include/asm-ia64/linux-xen/asm/machvec.h | 573 + .../asm-ia64/linux-xen/asm/machvec_dig.h | 46 + .../asm-ia64/linux-xen/asm/machvec_hpzx1.h | 66 + .../asm-ia64/linux-xen/asm/machvec_sn2.h | 173 + xen/include/asm-ia64/linux-xen/asm/mca_asm.h | 410 + xen/include/asm-ia64/linux-xen/asm/meminit.h | 73 + xen/include/asm-ia64/linux-xen/asm/numa.h | 96 + xen/include/asm-ia64/linux-xen/asm/page.h | 225 + xen/include/asm-ia64/linux-xen/asm/pal.h | 1760 ++ xen/include/asm-ia64/linux-xen/asm/pci.h | 181 + xen/include/asm-ia64/linux-xen/asm/percpu.h | 87 + xen/include/asm-ia64/linux-xen/asm/perfmon.h | 287 + .../linux-xen/asm/perfmon_default_smpl.h | 83 + xen/include/asm-ia64/linux-xen/asm/pgalloc.h | 221 + xen/include/asm-ia64/linux-xen/asm/pgtable.h | 691 + .../asm-ia64/linux-xen/asm/processor.h | 777 + xen/include/asm-ia64/linux-xen/asm/ptrace.h | 384 + xen/include/asm-ia64/linux-xen/asm/sal.h | 891 + xen/include/asm-ia64/linux-xen/asm/smp.h | 143 + .../asm-ia64/linux-xen/asm/sn/README.origin | 17 + xen/include/asm-ia64/linux-xen/asm/sn/addrs.h | 299 + xen/include/asm-ia64/linux-xen/asm/sn/arch.h | 92 + .../asm-ia64/linux-xen/asm/sn/hubdev.h | 95 + xen/include/asm-ia64/linux-xen/asm/sn/intr.h | 73 + xen/include/asm-ia64/linux-xen/asm/sn/io.h | 281 + .../asm-ia64/linux-xen/asm/sn/nodepda.h | 87 + .../linux-xen/asm/sn/pcibr_provider.h | 153 + .../asm-ia64/linux-xen/asm/sn/pcidev.h | 87 + .../asm-ia64/linux-xen/asm/sn/rw_mmr.h | 32 + xen/include/asm-ia64/linux-xen/asm/sn/types.h | 28 + xen/include/asm-ia64/linux-xen/asm/spinlock.h | 236 + xen/include/asm-ia64/linux-xen/asm/system.h | 308 + xen/include/asm-ia64/linux-xen/asm/types.h | 87 + .../asm-ia64/linux-xen/linux/README.origin | 22 + xen/include/asm-ia64/linux-xen/linux/cpu.h | 26 + xen/include/asm-ia64/linux-xen/linux/device.h | 489 + xen/include/asm-ia64/linux-xen/linux/efi.h | 531 + xen/include/asm-ia64/linux-xen/linux/gfp.h | 148 + .../asm-ia64/linux-xen/linux/hardirq.h | 113 + .../asm-ia64/linux-xen/linux/interrupt.h | 307 + .../asm-ia64/linux-xen/linux/kobject.h | 286 + .../asm-ia64/linux-xen/linux/linux-pci.h | 820 + .../asm-ia64/linux-xen/linux/oprofile.h | 119 + xen/include/asm-ia64/linux/README.origin | 37 + .../asm-ia64/linux/asm-generic/README.origin | 15 + .../asm-ia64/linux/asm-generic/div64.h | 58 + .../asm-ia64/linux/asm-generic/ide_iops.h | 38 + .../asm-ia64/linux/asm-generic/iomap.h | 68 + xen/include/asm-ia64/linux/asm-generic/pci.h | 42 + .../asm-ia64/linux/asm-generic/pgtable.h | 214 + .../asm-ia64/linux/asm-generic/sections.h | 16 + .../asm-ia64/linux/asm-generic/topology.h | 55 + .../asm-ia64/linux/asm-generic/unaligned.h | 122 + .../asm-ia64/linux/asm-generic/vmlinux.lds.h | 90 + xen/include/asm-ia64/linux/asm/README.origin | 44 + xen/include/asm-ia64/linux/asm/asmmacro.h | 111 + xen/include/asm-ia64/linux/asm/bitops.h | 423 + xen/include/asm-ia64/linux/asm/break.h | 23 + xen/include/asm-ia64/linux/asm/byteorder.h | 42 + xen/include/asm-ia64/linux/asm/cacheflush.h | 50 + xen/include/asm-ia64/linux/asm/checksum.h | 76 + xen/include/asm-ia64/linux/asm/current.h | 17 + xen/include/asm-ia64/linux/asm/delay.h | 97 + xen/include/asm-ia64/linux/asm/div64.h | 1 + xen/include/asm-ia64/linux/asm/dma.h | 23 + xen/include/asm-ia64/linux/asm/fpswa.h | 73 + xen/include/asm-ia64/linux/asm/fpu.h | 66 + xen/include/asm-ia64/linux/asm/hdreg.h | 14 + xen/include/asm-ia64/linux/asm/hw_irq.h | 145 + xen/include/asm-ia64/linux/asm/intrinsics.h | 181 + xen/include/asm-ia64/linux/asm/ioctl.h | 77 + xen/include/asm-ia64/linux/asm/irq.h | 45 + xen/include/asm-ia64/linux/asm/linkage.h | 6 + .../asm-ia64/linux/asm/machvec_hpsim.h | 18 + xen/include/asm-ia64/linux/asm/machvec_init.h | 32 + xen/include/asm-ia64/linux/asm/mca.h | 132 + xen/include/asm-ia64/linux/asm/nodedata.h | 52 + xen/include/asm-ia64/linux/asm/numnodes.h | 15 + xen/include/asm-ia64/linux/asm/param.h | 42 + xen/include/asm-ia64/linux/asm/patch.h | 25 + xen/include/asm-ia64/linux/asm/rse.h | 66 + xen/include/asm-ia64/linux/asm/sections.h | 23 + xen/include/asm-ia64/linux/asm/setup.h | 6 + .../asm-ia64/linux/asm/sn/README.origin | 24 + xen/include/asm-ia64/linux/asm/sn/geo.h | 132 + xen/include/asm-ia64/linux/asm/sn/klconfig.h | 246 + xen/include/asm-ia64/linux/asm/sn/l1.h | 51 + xen/include/asm-ia64/linux/asm/sn/leds.h | 33 + xen/include/asm-ia64/linux/asm/sn/module.h | 127 + .../linux/asm/sn/pcibus_provider_defs.h | 68 + xen/include/asm-ia64/linux/asm/sn/pda.h | 69 + xen/include/asm-ia64/linux/asm/sn/pic.h | 261 + xen/include/asm-ia64/linux/asm/sn/shub_mmr.h | 502 + xen/include/asm-ia64/linux/asm/sn/shubio.h | 3358 +++ xen/include/asm-ia64/linux/asm/sn/simulator.h | 20 + xen/include/asm-ia64/linux/asm/sn/sn_cpuid.h | 132 + .../asm-ia64/linux/asm/sn/sn_feature_sets.h | 51 + xen/include/asm-ia64/linux/asm/sn/sn_sal.h | 1167 ++ xen/include/asm-ia64/linux/asm/sn/tiocp.h | 257 + xen/include/asm-ia64/linux/asm/sn/xbow.h | 301 + .../asm-ia64/linux/asm/sn/xwidgetdev.h | 70 + xen/include/asm-ia64/linux/asm/string.h | 22 + xen/include/asm-ia64/linux/asm/thread_info.h | 94 + xen/include/asm-ia64/linux/asm/timex.h | 40 + xen/include/asm-ia64/linux/asm/topology.h | 128 + xen/include/asm-ia64/linux/asm/unaligned.h | 6 + xen/include/asm-ia64/linux/asm/unistd.h | 405 + xen/include/asm-ia64/linux/asm/unwind.h | 240 + xen/include/asm-ia64/linux/bcd.h | 20 + xen/include/asm-ia64/linux/bitmap.h | 261 + xen/include/asm-ia64/linux/bitops.h | 159 + .../asm-ia64/linux/byteorder/README.origin | 9 + .../asm-ia64/linux/byteorder/generic.h | 172 + .../asm-ia64/linux/byteorder/little_endian.h | 106 + xen/include/asm-ia64/linux/byteorder/swab.h | 192 + xen/include/asm-ia64/linux/completion.h | 57 + xen/include/asm-ia64/linux/hash.h | 58 + xen/include/asm-ia64/linux/initrd.h | 20 + xen/include/asm-ia64/linux/ioport.h | 136 + xen/include/asm-ia64/linux/jiffies.h | 450 + xen/include/asm-ia64/linux/klist.h | 61 + xen/include/asm-ia64/linux/kmalloc_sizes.h | 33 + xen/include/asm-ia64/linux/kref.h | 32 + xen/include/asm-ia64/linux/linkage.h | 47 + xen/include/asm-ia64/linux/mod_devicetable.h | 323 + xen/include/asm-ia64/linux/notifier.h | 76 + xen/include/asm-ia64/linux/pci_ids.h | 2356 +++ xen/include/asm-ia64/linux/pci_regs.h | 488 + xen/include/asm-ia64/linux/percpu.h | 61 + xen/include/asm-ia64/linux/pm.h | 279 + xen/include/asm-ia64/linux/preempt.h | 62 + xen/include/asm-ia64/linux/seqlock.h | 175 + xen/include/asm-ia64/linux/sort.h | 10 + xen/include/asm-ia64/linux/stddef.h | 20 + xen/include/asm-ia64/linux/sysfs.h | 206 + xen/include/asm-ia64/linux/thread_info.h | 92 + xen/include/asm-ia64/linux/time.h | 181 + xen/include/asm-ia64/linux/timex.h | 320 + xen/include/asm-ia64/linux/topology.h | 144 + xen/include/asm-ia64/linux/wait.h | 458 + xen/include/asm-ia64/mm.h | 512 + xen/include/asm-ia64/mmu_context.h | 20 + xen/include/asm-ia64/multicall.h | 31 + xen/include/asm-ia64/offsets.h | 9 + xen/include/asm-ia64/p2m_entry.h | 76 + xen/include/asm-ia64/perfc.h | 22 + xen/include/asm-ia64/perfc_defn.h | 176 + xen/include/asm-ia64/privop.h | 13 + xen/include/asm-ia64/privop_stat.h | 41 + xen/include/asm-ia64/regionreg.h | 111 + xen/include/asm-ia64/regs.h | 1 + xen/include/asm-ia64/shadow.h | 77 + xen/include/asm-ia64/shared.h | 4 + xen/include/asm-ia64/sioemu.h | 29 + xen/include/asm-ia64/slab.h | 3 + xen/include/asm-ia64/softirq.h | 9 + xen/include/asm-ia64/time.h | 12 + xen/include/asm-ia64/tlb.h | 39 + xen/include/asm-ia64/tlb_track.h | 155 + xen/include/asm-ia64/tlbflush.h | 48 + xen/include/asm-ia64/trace.h | 4 + xen/include/asm-ia64/uaccess.h | 284 + xen/include/asm-ia64/vcpu.h | 428 + xen/include/asm-ia64/vcpumask.h | 60 + xen/include/asm-ia64/vhpt.h | 107 + xen/include/asm-ia64/viosapic.h | 74 + xen/include/asm-ia64/virt_event.h | 114 + xen/include/asm-ia64/vlsapic.h | 78 + xen/include/asm-ia64/vmmu.h | 223 + xen/include/asm-ia64/vmx.h | 57 + xen/include/asm-ia64/vmx_mm_def.h | 159 + xen/include/asm-ia64/vmx_pal.h | 122 + xen/include/asm-ia64/vmx_pal_vsa.h | 53 + xen/include/asm-ia64/vmx_phy_mode.h | 101 + xen/include/asm-ia64/vmx_platform.h | 54 + xen/include/asm-ia64/vmx_vcpu.h | 725 + xen/include/asm-ia64/vmx_vcpu_save.h | 40 + xen/include/asm-ia64/vmx_vpd.h | 126 + xen/include/asm-ia64/vtm.h | 67 + xen/include/asm-ia64/xengcc_intrin.h | 59 + xen/include/asm-ia64/xenia64regs.h | 31 + xen/include/asm-ia64/xenkregs.h | 98 + xen/include/asm-ia64/xenmca.h | 34 + xen/include/asm-ia64/xenoprof.h | 79 + xen/include/asm-ia64/xenpage.h | 103 + xen/include/asm-ia64/xenprocessor.h | 253 + xen/include/asm-ia64/xenspinlock.h | 30 + xen/include/asm-ia64/xensystem.h | 44 + xen/include/asm-ia64/xentypes.h | 15 + xen/include/asm-x86/acpi.h | 169 + xen/include/asm-x86/amd-iommu.h | 96 + xen/include/asm-x86/apic.h | 227 + xen/include/asm-x86/apicdef.h | 393 + xen/include/asm-x86/asm_defns.h | 15 + xen/include/asm-x86/atomic.h | 202 + xen/include/asm-x86/bitops.h | 446 + xen/include/asm-x86/bug.h | 21 + xen/include/asm-x86/byteorder.h | 36 + xen/include/asm-x86/cache.h | 15 + xen/include/asm-x86/compat.h | 8 + xen/include/asm-x86/config.h | 374 + xen/include/asm-x86/cpufeature.h | 194 + xen/include/asm-x86/current.h | 64 + xen/include/asm-x86/debugger.h | 78 + xen/include/asm-x86/debugreg.h | 67 + xen/include/asm-x86/delay.h | 13 + xen/include/asm-x86/desc.h | 226 + xen/include/asm-x86/div64.h | 49 + xen/include/asm-x86/domain.h | 402 + xen/include/asm-x86/e820.h | 38 + xen/include/asm-x86/edd.h | 54 + xen/include/asm-x86/elf.h | 24 + xen/include/asm-x86/event.h | 75 + xen/include/asm-x86/fixmap.h | 80 + xen/include/asm-x86/flushtlb.h | 118 + xen/include/asm-x86/genapic.h | 93 + xen/include/asm-x86/grant_table.h | 54 + xen/include/asm-x86/guest_access.h | 124 + xen/include/asm-x86/hap.h | 109 + xen/include/asm-x86/hardirq.h | 25 + xen/include/asm-x86/hpet.h | 73 + xen/include/asm-x86/hvm/cacheattr.h | 33 + xen/include/asm-x86/hvm/domain.h | 91 + xen/include/asm-x86/hvm/emulate.h | 49 + xen/include/asm-x86/hvm/guest_access.h | 10 + xen/include/asm-x86/hvm/hvm.h | 324 + xen/include/asm-x86/hvm/io.h | 125 + xen/include/asm-x86/hvm/iommu.h | 40 + xen/include/asm-x86/hvm/irq.h | 175 + xen/include/asm-x86/hvm/support.h | 139 + xen/include/asm-x86/hvm/svm/amd-iommu-acpi.h | 176 + xen/include/asm-x86/hvm/svm/amd-iommu-defs.h | 464 + xen/include/asm-x86/hvm/svm/amd-iommu-proto.h | 119 + xen/include/asm-x86/hvm/svm/asid.h | 59 + xen/include/asm-x86/hvm/svm/emulate.h | 56 + xen/include/asm-x86/hvm/svm/intr.h | 29 + xen/include/asm-x86/hvm/svm/svm.h | 76 + xen/include/asm-x86/hvm/svm/vmcb.h | 485 + xen/include/asm-x86/hvm/trace.h | 120 + xen/include/asm-x86/hvm/vcpu.h | 101 + xen/include/asm-x86/hvm/vioapic.h | 70 + xen/include/asm-x86/hvm/viridian.h | 65 + xen/include/asm-x86/hvm/vlapic.h | 105 + xen/include/asm-x86/hvm/vmx/vmcs.h | 351 + xen/include/asm-x86/hvm/vmx/vmx.h | 403 + xen/include/asm-x86/hvm/vmx/vpmu.h | 83 + xen/include/asm-x86/hvm/vmx/vpmu_core2.h | 68 + xen/include/asm-x86/hvm/vpic.h | 39 + xen/include/asm-x86/hvm/vpt.h | 193 + xen/include/asm-x86/hypercall.h | 148 + xen/include/asm-x86/i387.h | 46 + xen/include/asm-x86/init.h | 4 + xen/include/asm-x86/io.h | 55 + xen/include/asm-x86/io_apic.h | 195 + xen/include/asm-x86/iocap.h | 20 + xen/include/asm-x86/ipi.h | 8 + xen/include/asm-x86/irq.h | 68 + xen/include/asm-x86/ldt.h | 38 + xen/include/asm-x86/mach-default/bios_ebda.h | 15 + xen/include/asm-x86/mach-default/io_ports.h | 30 + .../asm-x86/mach-default/irq_vectors.h | 37 + .../asm-x86/mach-default/mach_mpparse.h | 17 + .../asm-x86/mach-default/mach_mpspec.h | 10 + .../asm-x86/mach-default/mach_wakecpu.h | 41 + .../asm-x86/mach-default/smpboot_hooks.h | 44 + xen/include/asm-x86/mach-generic/mach_apic.h | 94 + .../asm-x86/mach-generic/mach_mpparse.h | 18 + .../asm-x86/mach-summit/mach_mpparse.h | 108 + xen/include/asm-x86/mc146818rtc.h | 113 + xen/include/asm-x86/microcode.h | 93 + xen/include/asm-x86/mm.h | 401 + xen/include/asm-x86/mpspec.h | 84 + xen/include/asm-x86/mpspec_def.h | 188 + xen/include/asm-x86/msi.h | 218 + xen/include/asm-x86/msr-index.h | 423 + xen/include/asm-x86/msr.h | 121 + xen/include/asm-x86/mtrr.h | 72 + xen/include/asm-x86/multicall.h | 100 + xen/include/asm-x86/nmi.h | 41 + xen/include/asm-x86/numa.h | 77 + xen/include/asm-x86/p2m.h | 246 + xen/include/asm-x86/page.h | 379 + xen/include/asm-x86/paging.h | 413 + xen/include/asm-x86/percpu.h | 20 + xen/include/asm-x86/perfc.h | 17 + xen/include/asm-x86/perfc_defn.h | 130 + xen/include/asm-x86/pirq.h | 11 + xen/include/asm-x86/processor.h | 565 + xen/include/asm-x86/regs.h | 45 + xen/include/asm-x86/rwlock.h | 83 + xen/include/asm-x86/shadow.h | 127 + xen/include/asm-x86/shared.h | 78 + xen/include/asm-x86/smp.h | 115 + xen/include/asm-x86/softirq.h | 9 + xen/include/asm-x86/spinlock.h | 111 + xen/include/asm-x86/string.h | 232 + xen/include/asm-x86/system.h | 188 + xen/include/asm-x86/tboot.h | 103 + xen/include/asm-x86/time.h | 41 + xen/include/asm-x86/trace.h | 46 + xen/include/asm-x86/traps.h | 50 + xen/include/asm-x86/types.h | 68 + xen/include/asm-x86/uaccess.h | 281 + xen/include/asm-x86/x86_32/asm_defns.h | 146 + xen/include/asm-x86/x86_32/bug.h | 34 + xen/include/asm-x86/x86_32/elf.h | 70 + xen/include/asm-x86/x86_32/page.h | 131 + xen/include/asm-x86/x86_32/regs.h | 25 + xen/include/asm-x86/x86_32/system.h | 114 + xen/include/asm-x86/x86_32/uaccess.h | 88 + xen/include/asm-x86/x86_64/asm_defns.h | 125 + xen/include/asm-x86/x86_64/bug.h | 34 + xen/include/asm-x86/x86_64/elf.h | 90 + xen/include/asm-x86/x86_64/page.h | 155 + xen/include/asm-x86/x86_64/regs.h | 29 + xen/include/asm-x86/x86_64/system.h | 68 + xen/include/asm-x86/x86_64/uaccess.h | 64 + xen/include/asm-x86/x86_emulate.h | 22 + xen/include/asm-x86/xenoprof.h | 78 + xen/include/public/COPYING | 38 + xen/include/public/arch-ia64.h | 626 + xen/include/public/arch-ia64/debug_op.h | 96 + xen/include/public/arch-ia64/hvm/memmap.h | 88 + xen/include/public/arch-ia64/hvm/save.h | 201 + xen/include/public/arch-ia64/sioemu.h | 89 + xen/include/public/arch-x86/cpuid.h | 68 + xen/include/public/arch-x86/hvm/save.h | 440 + xen/include/public/arch-x86/xen-mca.h | 279 + xen/include/public/arch-x86/xen-x86_32.h | 180 + xen/include/public/arch-x86/xen-x86_64.h | 212 + xen/include/public/arch-x86/xen.h | 204 + xen/include/public/arch-x86_32.h | 27 + xen/include/public/arch-x86_64.h | 27 + xen/include/public/callback.h | 121 + xen/include/public/dom0_ops.h | 120 + xen/include/public/domctl.h | 680 + xen/include/public/elfnote.h | 233 + xen/include/public/elfstructs.h | 527 + xen/include/public/event_channel.h | 264 + xen/include/public/features.h | 74 + xen/include/public/grant_table.h | 431 + xen/include/public/hvm/e820.h | 34 + xen/include/public/hvm/hvm_info_table.h | 41 + xen/include/public/hvm/hvm_op.h | 131 + xen/include/public/hvm/ioreq.h | 127 + xen/include/public/hvm/params.h | 105 + xen/include/public/hvm/save.h | 88 + xen/include/public/io/blkif.h | 141 + xen/include/public/io/console.h | 51 + xen/include/public/io/fbif.h | 176 + xen/include/public/io/fsif.h | 191 + xen/include/public/io/kbdif.h | 132 + xen/include/public/io/netif.h | 205 + xen/include/public/io/pciif.h | 101 + xen/include/public/io/protocols.h | 40 + xen/include/public/io/ring.h | 307 + xen/include/public/io/tpmif.h | 77 + xen/include/public/io/xenbus.h | 80 + xen/include/public/io/xs_wire.h | 130 + xen/include/public/kexec.h | 189 + xen/include/public/libelf.h | 265 + xen/include/public/memory.h | 312 + xen/include/public/nmi.h | 78 + xen/include/public/physdev.h | 219 + xen/include/public/platform.h | 346 + xen/include/public/sched.h | 121 + xen/include/public/sysctl.h | 308 + xen/include/public/trace.h | 203 + xen/include/public/vcpu.h | 213 + xen/include/public/version.h | 91 + xen/include/public/xen-compat.h | 44 + xen/include/public/xen.h | 639 + xen/include/public/xencomm.h | 41 + xen/include/public/xenoprof.h | 138 + xen/include/public/xsm/acm.h | 235 + xen/include/public/xsm/acm_ops.h | 159 + xen/include/public/xsm/flask_op.h | 45 + xen/include/xen/acpi.h | 445 + xen/include/xen/bitmap.h | 259 + xen/include/xen/bitops.h | 178 + xen/include/xen/byteorder/big_endian.h | 102 + xen/include/xen/byteorder/generic.h | 68 + xen/include/xen/byteorder/little_endian.h | 102 + xen/include/xen/byteorder/swab.h | 185 + xen/include/xen/cache.h | 19 + xen/include/xen/compat.h | 192 + xen/include/xen/compile.h.in | 13 + xen/include/xen/compiler.h | 62 + xen/include/xen/config.h | 99 + xen/include/xen/console.h | 44 + xen/include/xen/cpuidle.h | 82 + xen/include/xen/cpumask.h | 398 + xen/include/xen/ctype.h | 54 + xen/include/xen/delay.h | 10 + xen/include/xen/dmi.h | 40 + xen/include/xen/domain.h | 59 + xen/include/xen/domain_page.h | 115 + xen/include/xen/elf.h | 58 + xen/include/xen/elfcore.h | 87 + xen/include/xen/errno.h | 132 + xen/include/xen/event.h | 82 + xen/include/xen/gdbstub.h | 114 + xen/include/xen/grant_table.h | 149 + xen/include/xen/guest_access.h | 24 + xen/include/xen/hvm/iommu.h | 50 + xen/include/xen/hvm/save.h | 162 + xen/include/xen/hypercall.h | 129 + xen/include/xen/init.h | 126 + xen/include/xen/inttypes.h | 251 + xen/include/xen/iocap.h | 34 + xen/include/xen/iommu.h | 116 + xen/include/xen/irq.h | 93 + xen/include/xen/irq_cpustat.h | 30 + xen/include/xen/kernel.h | 78 + xen/include/xen/kexec.h | 46 + xen/include/xen/keyhandler.h | 36 + xen/include/xen/lib.h | 103 + xen/include/xen/list.h | 917 + xen/include/xen/mm.h | 109 + xen/include/xen/multiboot.h | 112 + xen/include/xen/multiboot2.h | 97 + xen/include/xen/multicall.h | 30 + xen/include/xen/nmi.h | 14 + xen/include/xen/nodemask.h | 338 + xen/include/xen/numa.h | 20 + xen/include/xen/paging.h | 26 + xen/include/xen/pci.h | 81 + xen/include/xen/pci_regs.h | 530 + xen/include/xen/percpu.h | 14 + xen/include/xen/perfc.h | 115 + xen/include/xen/perfc_defn.h | 21 + xen/include/xen/prefetch.h | 60 + xen/include/xen/rangeset.h | 71 + xen/include/xen/rcupdate.h | 200 + xen/include/xen/sched-if.h | 82 + xen/include/xen/sched.h | 569 + xen/include/xen/serial.h | 153 + xen/include/xen/shared.h | 48 + xen/include/xen/shutdown.h | 13 + xen/include/xen/smp.h | 74 + xen/include/xen/softirq.h | 78 + xen/include/xen/spinlock.h | 95 + xen/include/xen/stdarg.h | 5 + xen/include/xen/stop_machine.h | 19 + xen/include/xen/string.h | 97 + xen/include/xen/symbols.h | 42 + xen/include/xen/time.h | 76 + xen/include/xen/timer.h | 134 + xen/include/xen/trace.h | 114 + xen/include/xen/types.h | 61 + xen/include/xen/version.h | 16 + xen/include/xen/vga.h | 25 + xen/include/xen/xencomm.h | 126 + xen/include/xen/xenoprof.h | 78 + xen/include/xen/xmalloc.h | 102 + xen/include/xlat.lst | 57 + xen/include/xsm/acm/acm_core.h | 197 + xen/include/xsm/acm/acm_endian.h | 69 + xen/include/xsm/acm/acm_hooks.h | 363 + xen/include/xsm/xsm.h | 629 + xen/tools/Makefile | 16 + xen/tools/compat-build-header.py | 21 + xen/tools/compat-build-source.py | 27 + xen/tools/figlet/LICENSE | 111 + xen/tools/figlet/Makefile | 10 + xen/tools/figlet/README | 9 + xen/tools/figlet/figlet.c | 2076 ++ xen/tools/figlet/xen.flf | 2230 ++ xen/tools/get-fields.sh | 442 + xen/tools/symbols.c | 506 + xen/xsm/Makefile | 8 + xen/xsm/acm/Makefile | 7 + xen/xsm/acm/acm_chinesewall_hooks.c | 721 + xen/xsm/acm/acm_core.c | 406 + xen/xsm/acm/acm_null_hooks.c | 95 + xen/xsm/acm/acm_ops.c | 212 + xen/xsm/acm/acm_policy.c | 893 + .../acm/acm_simple_type_enforcement_hooks.c | 924 + xen/xsm/acm/acm_xsm_hooks.c | 74 + xen/xsm/dummy.c | 580 + xen/xsm/flask/Makefile | 7 + xen/xsm/flask/avc.c | 817 + xen/xsm/flask/flask_op.c | 939 + xen/xsm/flask/hooks.c | 1366 ++ xen/xsm/flask/include/av_inherit.h | 1 + xen/xsm/flask/include/av_perm_to_string.h | 112 + xen/xsm/flask/include/av_permissions.h | 121 + xen/xsm/flask/include/avc.h | 106 + xen/xsm/flask/include/avc_ss.h | 14 + xen/xsm/flask/include/class_to_string.h | 14 + xen/xsm/flask/include/common_perm_to_string.h | 1 + xen/xsm/flask/include/conditional.h | 22 + xen/xsm/flask/include/flask.h | 35 + xen/xsm/flask/include/initial_sid_to_string.h | 17 + xen/xsm/flask/include/objsec.h | 33 + xen/xsm/flask/include/security.h | 81 + xen/xsm/flask/ss/Makefile | 11 + xen/xsm/flask/ss/avtab.c | 471 + xen/xsm/flask/ss/avtab.h | 85 + xen/xsm/flask/ss/conditional.c | 546 + xen/xsm/flask/ss/conditional.h | 77 + xen/xsm/flask/ss/constraint.h | 61 + xen/xsm/flask/ss/context.h | 110 + xen/xsm/flask/ss/ebitmap.c | 328 + xen/xsm/flask/ss/ebitmap.h | 79 + xen/xsm/flask/ss/hashtab.c | 181 + xen/xsm/flask/ss/hashtab.h | 85 + xen/xsm/flask/ss/mls.c | 612 + xen/xsm/flask/ss/mls.h | 37 + xen/xsm/flask/ss/mls_types.h | 58 + xen/xsm/flask/ss/policydb.c | 1798 ++ xen/xsm/flask/ss/policydb.h | 256 + xen/xsm/flask/ss/services.c | 1697 ++ xen/xsm/flask/ss/services.h | 15 + xen/xsm/flask/ss/sidtab.c | 327 + xen/xsm/flask/ss/sidtab.h | 53 + xen/xsm/flask/ss/symtab.c | 47 + xen/xsm/flask/ss/symtab.h | 23 + xen/xsm/xsm_core.c | 118 + xen/xsm/xsm_policy.c | 69 + 2799 files changed, 703776 insertions(+) create mode 100644 COPYING create mode 100644 Config.mk create mode 100644 Makefile create mode 100644 README create mode 100644 buildconfigs/Rules.mk create mode 100644 buildconfigs/enable-xen-config create mode 100644 buildconfigs/interface.exclude create mode 100644 buildconfigs/ketchup create mode 100644 buildconfigs/mk.linux-2.6 create mode 100644 buildconfigs/mk.linux-2.6-common create mode 100644 buildconfigs/mk.linux-2.6-git create mode 100644 buildconfigs/mk.linux-2.6-mm create mode 100644 buildconfigs/mk.linux-2.6-native create mode 100644 buildconfigs/mk.linux-2.6-rc create mode 100644 buildconfigs/mk.linux-2.6-tip create mode 100644 buildconfigs/mk.linux-2.6-tip-latest create mode 100644 buildconfigs/mk.linux-2.6-xen create mode 100644 buildconfigs/mk.linux-2.6-xen0 create mode 100644 buildconfigs/mk.linux-2.6-xenU create mode 100644 buildconfigs/mk.linux-2.6.5-SLES-xen create mode 100644 buildconfigs/mk.linux-2.6.9-RHEL-xen create mode 100755 buildconfigs/select-linux-arch create mode 100755 buildconfigs/select-linux-image create mode 100755 buildconfigs/select-repository create mode 100644 buildconfigs/src.git-clone create mode 100644 buildconfigs/src.hg-clone create mode 100644 buildconfigs/src.tarball create mode 100644 config/FreeBSD.mk create mode 100644 config/Linux.mk create mode 100644 config/MiniOS.mk create mode 100644 config/NetBSD.mk create mode 100644 config/OpenBSD.mk create mode 100644 config/StdGNU.mk create mode 100644 config/SunOS.mk create mode 100644 config/ia64.mk create mode 100644 config/x86_32.mk create mode 100644 config/x86_64.mk create mode 100644 docs/ChangeLog create mode 100644 docs/Docs.mk create mode 100644 docs/Doxyfile create mode 100644 docs/Doxyfilter create mode 100644 docs/Makefile create mode 100644 docs/README.xen-bugtool create mode 100644 docs/check_pkgs create mode 100644 docs/figs/acm_ezpolicy_gui.eps create mode 100644 docs/figs/acm_overview.eps create mode 100644 docs/figs/xenlogo.eps create mode 100644 docs/html.sty create mode 100644 docs/man/xend-config.sxp.pod.5 create mode 100644 docs/man/xm.pod.1 create mode 100644 docs/man/xmdomain.cfg.pod.5 create mode 100644 docs/misc/VMX_changes.txt create mode 100644 docs/misc/blkif-drivers-explained.txt create mode 100644 docs/misc/crashdb.txt create mode 100644 docs/misc/dump-core-format.txt create mode 100644 docs/misc/grant-tables.txt create mode 100644 docs/misc/hg-cheatsheet.txt create mode 100644 docs/misc/kexec_and_kdump.txt create mode 100644 docs/misc/sedf_scheduler_mini-HOWTO.txt create mode 100644 docs/misc/vtd.txt create mode 100644 docs/misc/vtpm.txt create mode 100644 docs/misc/xen_config.html create mode 100644 docs/misc/xend.tex create mode 100644 docs/misc/xenstore.txt create mode 100644 docs/pythfilter.py create mode 100644 docs/src/interface.tex create mode 100644 docs/src/user.tex create mode 100644 docs/xen-api/Makefile create mode 100644 docs/xen-api/coversheet.tex create mode 100644 docs/xen-api/fdl.tex create mode 100644 docs/xen-api/presentation.tex create mode 100644 docs/xen-api/revision-history.tex create mode 100644 docs/xen-api/todo.tex create mode 100644 docs/xen-api/vm-lifecycle.tex create mode 100644 docs/xen-api/vm_lifecycle.dot create mode 100644 docs/xen-api/wire-protocol.tex create mode 100644 docs/xen-api/xen.eps create mode 100644 docs/xen-api/xenapi-coversheet.tex create mode 100644 docs/xen-api/xenapi-datamodel-graph.dot create mode 100644 docs/xen-api/xenapi-datamodel.tex create mode 100644 docs/xen-api/xenapi.tex create mode 100644 extras/mini-os/Config.mk create mode 100644 extras/mini-os/Makefile create mode 100644 extras/mini-os/README create mode 100644 extras/mini-os/app.lds create mode 100644 extras/mini-os/arch/ia64/Makefile create mode 100644 extras/mini-os/arch/ia64/__divdi3.S create mode 100644 extras/mini-os/arch/ia64/__udivdi3.S create mode 100644 extras/mini-os/arch/ia64/__udivsi3.S create mode 100644 extras/mini-os/arch/ia64/__umoddi3.S create mode 100644 extras/mini-os/arch/ia64/arch.mk create mode 100644 extras/mini-os/arch/ia64/common.c create mode 100644 extras/mini-os/arch/ia64/debug.c create mode 100644 extras/mini-os/arch/ia64/efi.c create mode 100644 extras/mini-os/arch/ia64/fw.S create mode 100644 extras/mini-os/arch/ia64/gen_off.c create mode 100644 extras/mini-os/arch/ia64/ia64.S create mode 100644 extras/mini-os/arch/ia64/ivt.S create mode 100644 extras/mini-os/arch/ia64/minios-ia64.lds create mode 100644 extras/mini-os/arch/ia64/mm.c create mode 100644 extras/mini-os/arch/ia64/sal.c create mode 100644 extras/mini-os/arch/ia64/sched.c create mode 100644 extras/mini-os/arch/ia64/time.c create mode 100644 extras/mini-os/arch/ia64/xencomm.c create mode 100644 extras/mini-os/arch/x86/Makefile create mode 100644 extras/mini-os/arch/x86/arch.mk create mode 100644 extras/mini-os/arch/x86/minios-x86_32.lds create mode 100644 extras/mini-os/arch/x86/minios-x86_64.lds create mode 100644 extras/mini-os/arch/x86/mm.c create mode 100644 extras/mini-os/arch/x86/sched.c create mode 100644 extras/mini-os/arch/x86/setup.c create mode 100644 extras/mini-os/arch/x86/time.c create mode 100644 extras/mini-os/arch/x86/traps.c create mode 100644 extras/mini-os/arch/x86/x86_32.S create mode 100644 extras/mini-os/arch/x86/x86_64.S create mode 100644 extras/mini-os/blkfront.c create mode 100644 extras/mini-os/console/console.c create mode 100644 extras/mini-os/console/xencons_ring.c create mode 100644 extras/mini-os/daytime.c create mode 100644 extras/mini-os/domain_config create mode 100644 extras/mini-os/events.c create mode 100644 extras/mini-os/fbfront.c create mode 100644 extras/mini-os/fs-front.c create mode 100644 extras/mini-os/gntmap.c create mode 100644 extras/mini-os/gnttab.c create mode 100644 extras/mini-os/hypervisor.c create mode 100644 extras/mini-os/include/arch/cc.h create mode 100644 extras/mini-os/include/arch/perf.h create mode 100644 extras/mini-os/include/arch/sys_arch.h create mode 100644 extras/mini-os/include/blkfront.h create mode 100644 extras/mini-os/include/byteswap.h create mode 100644 extras/mini-os/include/console.h create mode 100644 extras/mini-os/include/ctype.h create mode 100644 extras/mini-os/include/err.h create mode 100644 extras/mini-os/include/errno-base.h create mode 100644 extras/mini-os/include/errno.h create mode 100644 extras/mini-os/include/events.h create mode 100644 extras/mini-os/include/fbfront.h create mode 100644 extras/mini-os/include/fcntl.h create mode 100644 extras/mini-os/include/fs.h create mode 100644 extras/mini-os/include/gntmap.h create mode 100644 extras/mini-os/include/gnttab.h create mode 100644 extras/mini-os/include/hypervisor.h create mode 100644 extras/mini-os/include/ia64/arch_limits.h create mode 100644 extras/mini-os/include/ia64/arch_mm.h create mode 100644 extras/mini-os/include/ia64/arch_sched.h create mode 100644 extras/mini-os/include/ia64/arch_spinlock.h create mode 100644 extras/mini-os/include/ia64/asm.h create mode 100644 extras/mini-os/include/ia64/atomic.h create mode 100644 extras/mini-os/include/ia64/efi.h create mode 100644 extras/mini-os/include/ia64/endian.h create mode 100644 extras/mini-os/include/ia64/hypercall-ia64.h create mode 100644 extras/mini-os/include/ia64/ia64_cpu.h create mode 100644 extras/mini-os/include/ia64/ia64_fpu.h create mode 100644 extras/mini-os/include/ia64/os.h create mode 100644 extras/mini-os/include/ia64/page.h create mode 100644 extras/mini-os/include/ia64/pal.h create mode 100644 extras/mini-os/include/ia64/privop.h create mode 100644 extras/mini-os/include/ia64/sal.h create mode 100644 extras/mini-os/include/ia64/traps.h create mode 100644 extras/mini-os/include/kernel.h create mode 100644 extras/mini-os/include/lib.h create mode 100644 extras/mini-os/include/linux/types.h create mode 100644 extras/mini-os/include/list.h create mode 100644 extras/mini-os/include/lwipopts.h create mode 100644 extras/mini-os/include/mm.h create mode 100644 extras/mini-os/include/netfront.h create mode 100644 extras/mini-os/include/pcifront.h create mode 100644 extras/mini-os/include/posix/arpa/inet.h create mode 100644 extras/mini-os/include/posix/dirent.h create mode 100644 extras/mini-os/include/posix/err.h create mode 100644 extras/mini-os/include/posix/fcntl.h create mode 100644 extras/mini-os/include/posix/limits.h create mode 100644 extras/mini-os/include/posix/netdb.h create mode 100644 extras/mini-os/include/posix/netinet/in.h create mode 100644 extras/mini-os/include/posix/netinet/tcp.h create mode 100644 extras/mini-os/include/posix/pthread.h create mode 100644 extras/mini-os/include/posix/signal.h create mode 100644 extras/mini-os/include/posix/stdlib.h create mode 100644 extras/mini-os/include/posix/strings.h create mode 100644 extras/mini-os/include/posix/sys/ioctl.h create mode 100644 extras/mini-os/include/posix/sys/mman.h create mode 100644 extras/mini-os/include/posix/sys/poll.h create mode 100644 extras/mini-os/include/posix/sys/select.h create mode 100644 extras/mini-os/include/posix/sys/socket.h create mode 100644 extras/mini-os/include/posix/sys/stat.h create mode 100644 extras/mini-os/include/posix/syslog.h create mode 100644 extras/mini-os/include/posix/termios.h create mode 100644 extras/mini-os/include/posix/time.h create mode 100644 extras/mini-os/include/posix/unistd.h create mode 100644 extras/mini-os/include/sched.h create mode 100644 extras/mini-os/include/semaphore.h create mode 100644 extras/mini-os/include/spinlock.h create mode 100644 extras/mini-os/include/sys/lock.h create mode 100644 extras/mini-os/include/sys/time.h create mode 100644 extras/mini-os/include/time.h create mode 100644 extras/mini-os/include/types.h create mode 100644 extras/mini-os/include/wait.h create mode 100644 extras/mini-os/include/waittypes.h create mode 100644 extras/mini-os/include/x86/arch_limits.h create mode 100644 extras/mini-os/include/x86/arch_mm.h create mode 100644 extras/mini-os/include/x86/arch_sched.h create mode 100644 extras/mini-os/include/x86/arch_spinlock.h create mode 100644 extras/mini-os/include/x86/os.h create mode 100644 extras/mini-os/include/x86/traps.h create mode 100644 extras/mini-os/include/x86/x86_32/hypercall-x86_32.h create mode 100644 extras/mini-os/include/x86/x86_64/hypercall-x86_64.h create mode 100644 extras/mini-os/include/xenbus.h create mode 100644 extras/mini-os/include/xmalloc.h create mode 100644 extras/mini-os/kernel.c create mode 100644 extras/mini-os/lib/ctype.c create mode 100644 extras/mini-os/lib/math.c create mode 100644 extras/mini-os/lib/printf.c create mode 100644 extras/mini-os/lib/string.c create mode 100644 extras/mini-os/lib/sys.c create mode 100644 extras/mini-os/lib/xmalloc.c create mode 100644 extras/mini-os/lib/xs.c create mode 100644 extras/mini-os/lock.c create mode 100644 extras/mini-os/lwip-arch.c create mode 100644 extras/mini-os/lwip-net.c create mode 100644 extras/mini-os/main.c create mode 100644 extras/mini-os/minios.mk create mode 100644 extras/mini-os/mm.c create mode 100644 extras/mini-os/netfront.c create mode 100644 extras/mini-os/pcifront.c create mode 100644 extras/mini-os/sched.c create mode 100644 extras/mini-os/xenbus/xenbus.c create mode 100755 install.sh create mode 100644 stubdom/Makefile create mode 100644 stubdom/README create mode 100644 stubdom/c/Makefile create mode 100644 stubdom/c/main.c create mode 100644 stubdom/caml/Makefile create mode 100644 stubdom/caml/hello.ml create mode 100644 stubdom/caml/main-caml.c create mode 100644 stubdom/grub.patches/00cvs create mode 100644 stubdom/grub.patches/10graphics.diff create mode 100644 stubdom/grub.patches/20print_func.diff create mode 100644 stubdom/grub.patches/30savedefault.diff create mode 100644 stubdom/grub.patches/40ext3_256byte_inode.diff create mode 100644 stubdom/grub.patches/50fs_fulldisk.diff create mode 100644 stubdom/grub.patches/99minios create mode 100644 stubdom/grub/Makefile create mode 100644 stubdom/grub/boot-x86_32.S create mode 100644 stubdom/grub/boot-x86_64.S create mode 100644 stubdom/grub/config.h create mode 100644 stubdom/grub/kexec.c create mode 100644 stubdom/grub/mini-os.c create mode 100644 stubdom/grub/mini-os.h create mode 100644 stubdom/grub/osdep.h create mode 100644 stubdom/libpci.config.h create mode 100644 stubdom/libpci.config.mak create mode 100644 stubdom/lwip.patch-cvs create mode 100644 stubdom/newlib.patch create mode 100644 stubdom/pciutils.patch create mode 100644 stubdom/stubdom-dm create mode 100644 tools/Makefile create mode 100644 tools/Rules.mk create mode 100644 tools/blktap/Makefile create mode 100644 tools/blktap/README create mode 100644 tools/blktap/drivers/Makefile create mode 100644 tools/blktap/drivers/aes.c create mode 100644 tools/blktap/drivers/aes.h create mode 100644 tools/blktap/drivers/blk.h create mode 100644 tools/blktap/drivers/blk_linux.c create mode 100644 tools/blktap/drivers/blktapctrl.c create mode 100644 tools/blktap/drivers/blktapctrl.h create mode 100644 tools/blktap/drivers/blktapctrl_linux.c create mode 100644 tools/blktap/drivers/block-aio.c create mode 100644 tools/blktap/drivers/block-qcow.c create mode 100644 tools/blktap/drivers/block-qcow2.c create mode 100644 tools/blktap/drivers/block-ram.c create mode 100644 tools/blktap/drivers/block-sync.c create mode 100644 tools/blktap/drivers/block-vmdk.c create mode 100644 tools/blktap/drivers/bswap.h create mode 100644 tools/blktap/drivers/check_gcrypt create mode 100644 tools/blktap/drivers/img2qcow.c create mode 100644 tools/blktap/drivers/qcow-create.c create mode 100644 tools/blktap/drivers/qcow2raw.c create mode 100644 tools/blktap/drivers/tapaio.c create mode 100644 tools/blktap/drivers/tapaio.h create mode 100644 tools/blktap/drivers/tapdisk.c create mode 100644 tools/blktap/drivers/tapdisk.h create mode 100644 tools/blktap/lib/Makefile create mode 100644 tools/blktap/lib/blkif.c create mode 100644 tools/blktap/lib/blktaplib.h create mode 100644 tools/blktap/lib/list.h create mode 100644 tools/blktap/lib/xenbus.c create mode 100644 tools/blktap/lib/xs_api.c create mode 100644 tools/blktap/lib/xs_api.h create mode 100644 tools/check/Makefile create mode 100644 tools/check/README create mode 100755 tools/check/check_brctl create mode 100755 tools/check/check_crypto_lib create mode 100755 tools/check/check_curl create mode 100755 tools/check/check_iproute create mode 100755 tools/check/check_logging create mode 100755 tools/check/check_openssl_devel create mode 100755 tools/check/check_python create mode 100755 tools/check/check_python_devel create mode 100755 tools/check/check_python_xml create mode 100755 tools/check/check_udev create mode 100755 tools/check/check_x11_devel create mode 100755 tools/check/check_xgettext create mode 100755 tools/check/check_xml2 create mode 100755 tools/check/check_zlib_devel create mode 100755 tools/check/check_zlib_lib create mode 100755 tools/check/chk create mode 100644 tools/check/funcs.sh create mode 100644 tools/console/Makefile create mode 100644 tools/console/client/main.c create mode 100644 tools/console/daemon/io.c create mode 100644 tools/console/daemon/io.h create mode 100644 tools/console/daemon/main.c create mode 100644 tools/console/daemon/utils.c create mode 100644 tools/console/daemon/utils.h create mode 100644 tools/console/testsuite/Makefile create mode 100644 tools/console/testsuite/README create mode 100644 tools/console/testsuite/console-dom0.c create mode 100644 tools/console/testsuite/console-domU.c create mode 100644 tools/console/testsuite/procpipe.c create mode 100755 tools/cross-install create mode 100644 tools/debugger/gdb/README create mode 100644 tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/Makefile.in create mode 100755 tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure create mode 100644 tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.in create mode 100644 tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.srv create mode 100644 tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/linux-xen-low.c create mode 100644 tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c create mode 100755 tools/debugger/gdb/gdb-6.2.1-xen-sparse/mkbuildtree create mode 100755 tools/debugger/gdb/gdbbuild create mode 100644 tools/debugger/xenitp/Makefile create mode 100644 tools/debugger/xenitp/README create mode 100644 tools/debugger/xenitp/cpu-ia64-opc.c create mode 100644 tools/debugger/xenitp/dis-asm.h create mode 100644 tools/debugger/xenitp/ia64-asmtab.c create mode 100644 tools/debugger/xenitp/ia64-asmtab.h create mode 100644 tools/debugger/xenitp/ia64-dis.c create mode 100644 tools/debugger/xenitp/ia64-gen.c create mode 100644 tools/debugger/xenitp/ia64-opc-a.c create mode 100644 tools/debugger/xenitp/ia64-opc-b.c create mode 100644 tools/debugger/xenitp/ia64-opc-d.c create mode 100644 tools/debugger/xenitp/ia64-opc-f.c create mode 100644 tools/debugger/xenitp/ia64-opc-i.c create mode 100644 tools/debugger/xenitp/ia64-opc-m.c create mode 100644 tools/debugger/xenitp/ia64-opc-x.c create mode 100644 tools/debugger/xenitp/ia64-opc.c create mode 100644 tools/debugger/xenitp/ia64-opc.h create mode 100644 tools/debugger/xenitp/ia64.h create mode 100644 tools/debugger/xenitp/xenitp.c create mode 100644 tools/examples/Makefile create mode 100644 tools/examples/README create mode 100644 tools/examples/README.incompatibilities create mode 100644 tools/examples/blktap create mode 100644 tools/examples/block create mode 100644 tools/examples/block-common.sh create mode 100755 tools/examples/block-enbd create mode 100644 tools/examples/block-nbd create mode 100644 tools/examples/bochsrc create mode 100644 tools/examples/external-device-migrate create mode 100644 tools/examples/init.d/sysconfig.xendomains create mode 100755 tools/examples/init.d/xend create mode 100644 tools/examples/init.d/xendomains create mode 100644 tools/examples/locking.sh create mode 100644 tools/examples/logging.sh create mode 100755 tools/examples/network-bridge create mode 100644 tools/examples/network-nat create mode 100755 tools/examples/network-route create mode 100755 tools/examples/vif-bridge create mode 100644 tools/examples/vif-common.sh create mode 100644 tools/examples/vif-nat create mode 100755 tools/examples/vif-route create mode 100644 tools/examples/vnc/Xservers create mode 100755 tools/examples/vnc/Xvnc-xen create mode 100644 tools/examples/vscsi create mode 100644 tools/examples/vtpm create mode 100644 tools/examples/vtpm-common.sh create mode 100644 tools/examples/vtpm-delete create mode 100644 tools/examples/vtpm-hotplug-common.sh create mode 100644 tools/examples/vtpm-impl create mode 100644 tools/examples/vtpm-migration.sh create mode 100755 tools/examples/xen-backend.agent create mode 100644 tools/examples/xen-backend.rules create mode 100644 tools/examples/xen-hotplug-cleanup create mode 100644 tools/examples/xen-hotplug-common.sh create mode 100644 tools/examples/xen-network-common.sh create mode 100644 tools/examples/xen-script-common.sh create mode 100644 tools/examples/xend-config.sxp create mode 100644 tools/examples/xend-pci-permissive.sxp create mode 100644 tools/examples/xend-pci-quirks.sxp create mode 100644 tools/examples/xeninfo.pl create mode 100644 tools/examples/xm-config.xml create mode 100644 tools/examples/xmexample.hvm create mode 100644 tools/examples/xmexample.hvm-dm create mode 100644 tools/examples/xmexample.hvm-stubdom create mode 100644 tools/examples/xmexample.nbd create mode 100644 tools/examples/xmexample.pv-grub create mode 100644 tools/examples/xmexample.vti create mode 100644 tools/examples/xmexample1 create mode 100644 tools/examples/xmexample2 create mode 100644 tools/examples/xmexample3 create mode 100644 tools/firmware/Makefile create mode 100644 tools/firmware/Rules.mk create mode 100644 tools/firmware/etherboot/Config create mode 100644 tools/firmware/etherboot/Makefile create mode 100644 tools/firmware/etherboot/README create mode 100644 tools/firmware/etherboot/eb-roms.h create mode 100644 tools/firmware/etherboot/gpxe-git-snapshot.tar.gz create mode 100644 tools/firmware/etherboot/patches/boot_prompt_option.patch create mode 100644 tools/firmware/etherboot/patches/series create mode 100644 tools/firmware/hvmloader/32bitbios_support.c create mode 100644 tools/firmware/hvmloader/Makefile create mode 100644 tools/firmware/hvmloader/acpi/Makefile create mode 100644 tools/firmware/hvmloader/acpi/README create mode 100644 tools/firmware/hvmloader/acpi/acpi2_0.h create mode 100644 tools/firmware/hvmloader/acpi/build.c create mode 100644 tools/firmware/hvmloader/acpi/dsdt.asl create mode 100644 tools/firmware/hvmloader/acpi/dsdt.c create mode 100644 tools/firmware/hvmloader/acpi/ssdt_pm.asl create mode 100644 tools/firmware/hvmloader/acpi/ssdt_pm.h create mode 100644 tools/firmware/hvmloader/acpi/ssdt_tpm.asl create mode 100644 tools/firmware/hvmloader/acpi/ssdt_tpm.h create mode 100644 tools/firmware/hvmloader/acpi/static_tables.c create mode 100644 tools/firmware/hvmloader/apic_regs.h create mode 100644 tools/firmware/hvmloader/cacheattr.c create mode 100644 tools/firmware/hvmloader/config.h create mode 100644 tools/firmware/hvmloader/e820.h create mode 100644 tools/firmware/hvmloader/hvmloader.c create mode 100644 tools/firmware/hvmloader/hypercall.h create mode 100755 tools/firmware/hvmloader/mkhex create mode 100644 tools/firmware/hvmloader/mp_tables.c create mode 100644 tools/firmware/hvmloader/option_rom.h create mode 100644 tools/firmware/hvmloader/pci_regs.h create mode 100644 tools/firmware/hvmloader/smbios.c create mode 100644 tools/firmware/hvmloader/smbios_types.h create mode 100644 tools/firmware/hvmloader/smp.c create mode 100644 tools/firmware/hvmloader/tests.c create mode 100644 tools/firmware/hvmloader/util.c create mode 100644 tools/firmware/hvmloader/util.h create mode 100644 tools/firmware/rombios/32bit/32bitbios.c create mode 100644 tools/firmware/rombios/32bit/Makefile create mode 100644 tools/firmware/rombios/32bit/mkhex create mode 100644 tools/firmware/rombios/32bit/rombios_compat.h create mode 100644 tools/firmware/rombios/32bit/tcgbios/Makefile create mode 100644 tools/firmware/rombios/32bit/tcgbios/tcgbios.c create mode 100644 tools/firmware/rombios/32bit/tcgbios/tcgbios.h create mode 100644 tools/firmware/rombios/32bit/tcgbios/tpm_drivers.c create mode 100644 tools/firmware/rombios/32bit/tcgbios/tpm_drivers.h create mode 100644 tools/firmware/rombios/32bit/util.c create mode 100644 tools/firmware/rombios/32bit/util.h create mode 100644 tools/firmware/rombios/32bitgateway.c create mode 100644 tools/firmware/rombios/32bitgateway.h create mode 100644 tools/firmware/rombios/32bitprotos.h create mode 100644 tools/firmware/rombios/Makefile create mode 100644 tools/firmware/rombios/apmbios.S create mode 100644 tools/firmware/rombios/biossums.c create mode 100755 tools/firmware/rombios/makesym.perl create mode 100644 tools/firmware/rombios/rombios.c create mode 100644 tools/firmware/rombios/tcgbios.c create mode 100644 tools/firmware/vgabios/BUGS create mode 100644 tools/firmware/vgabios/COPYING create mode 100644 tools/firmware/vgabios/ChangeLog create mode 100644 tools/firmware/vgabios/Makefile create mode 100644 tools/firmware/vgabios/Notes create mode 100644 tools/firmware/vgabios/README create mode 100644 tools/firmware/vgabios/TODO create mode 100644 tools/firmware/vgabios/biossums.c create mode 100644 tools/firmware/vgabios/clext.c create mode 100755 tools/firmware/vgabios/dataseghack create mode 100644 tools/firmware/vgabios/vbe.c create mode 100644 tools/firmware/vgabios/vbe.h create mode 100644 tools/firmware/vgabios/vbe_display_api.txt create mode 100644 tools/firmware/vgabios/vbetables-gen.c create mode 100644 tools/firmware/vgabios/vgabios.c create mode 100644 tools/firmware/vgabios/vgabios.h create mode 100644 tools/firmware/vgabios/vgafonts.h create mode 100644 tools/firmware/vgabios/vgatables.h create mode 100644 tools/flask/Makefile create mode 100644 tools/flask/libflask/Makefile create mode 100644 tools/flask/libflask/flask_op.c create mode 100644 tools/flask/libflask/include/flask.h create mode 100644 tools/flask/loadpolicy/Makefile create mode 100644 tools/flask/loadpolicy/loadpolicy.c create mode 100644 tools/flask/policy/Makefile create mode 100644 tools/flask/policy/Rules.modular create mode 100644 tools/flask/policy/Rules.monolithic create mode 100644 tools/flask/policy/policy/constraints create mode 100644 tools/flask/policy/policy/flask/Makefile create mode 100644 tools/flask/policy/policy/flask/access_vectors create mode 100644 tools/flask/policy/policy/flask/initial_sids create mode 100644 tools/flask/policy/policy/flask/mkaccess_vector.sh create mode 100644 tools/flask/policy/policy/flask/mkflask.sh create mode 100644 tools/flask/policy/policy/flask/security_classes create mode 100644 tools/flask/policy/policy/global_booleans create mode 100644 tools/flask/policy/policy/global_tunables create mode 100644 tools/flask/policy/policy/mcs create mode 100644 tools/flask/policy/policy/mls create mode 100644 tools/flask/policy/policy/modules.conf create mode 100644 tools/flask/policy/policy/modules/xen/xen.if create mode 100644 tools/flask/policy/policy/modules/xen/xen.te create mode 100644 tools/flask/policy/policy/support/loadable_module.spt create mode 100644 tools/flask/policy/policy/support/misc_macros.spt create mode 100644 tools/flask/policy/policy/systemuser create mode 100644 tools/flask/policy/policy/users create mode 100644 tools/fs-back/Makefile create mode 100644 tools/fs-back/fs-backend.c create mode 100644 tools/fs-back/fs-backend.h create mode 100644 tools/fs-back/fs-ops.c create mode 100644 tools/fs-back/fs-xenbus.c create mode 100644 tools/include/Makefile create mode 100644 tools/include/xen-foreign/Makefile create mode 100644 tools/include/xen-foreign/mkchecker.py create mode 100644 tools/include/xen-foreign/mkheader.py create mode 100644 tools/include/xen-foreign/reference.size create mode 100644 tools/include/xen-foreign/structs.py create mode 100644 tools/include/xen-sys/Linux/evtchn.h create mode 100644 tools/include/xen-sys/Linux/gntdev.h create mode 100644 tools/include/xen-sys/Linux/privcmd.h create mode 100644 tools/include/xen-sys/MiniOS/privcmd.h create mode 100644 tools/include/xen-sys/NetBSD/evtchn.h create mode 100644 tools/include/xen-sys/NetBSD/privcmd.h create mode 100644 tools/include/xen-sys/SunOS/evtchn.h create mode 100644 tools/include/xen-sys/SunOS/privcmd.h create mode 100644 tools/include/xen-sys/SunOS/xenbus.h create mode 100644 tools/libaio/COPYING create mode 100644 tools/libaio/ChangeLog create mode 100644 tools/libaio/INSTALL create mode 100644 tools/libaio/Makefile create mode 100644 tools/libaio/TODO create mode 100644 tools/libaio/harness/Makefile create mode 100644 tools/libaio/harness/README create mode 100644 tools/libaio/harness/attic/0.t create mode 100644 tools/libaio/harness/attic/1.t create mode 100644 tools/libaio/harness/cases/10.t create mode 100644 tools/libaio/harness/cases/11.t create mode 100644 tools/libaio/harness/cases/12.t create mode 100644 tools/libaio/harness/cases/13.t create mode 100644 tools/libaio/harness/cases/14.t create mode 100644 tools/libaio/harness/cases/2.t create mode 100644 tools/libaio/harness/cases/3.t create mode 100644 tools/libaio/harness/cases/4.t create mode 100644 tools/libaio/harness/cases/5.t create mode 100644 tools/libaio/harness/cases/6.t create mode 100644 tools/libaio/harness/cases/7.t create mode 100644 tools/libaio/harness/cases/8.t create mode 100644 tools/libaio/harness/cases/aio_setup.h create mode 100644 tools/libaio/harness/cases/common-7-8.h create mode 100644 tools/libaio/harness/main.c create mode 100644 tools/libaio/harness/runtests.sh create mode 100644 tools/libaio/libaio.spec create mode 100644 tools/libaio/man/aio.3 create mode 100644 tools/libaio/man/aio_cancel.3 create mode 100644 tools/libaio/man/aio_cancel64.3 create mode 100644 tools/libaio/man/aio_error.3 create mode 100644 tools/libaio/man/aio_error64.3 create mode 100644 tools/libaio/man/aio_fsync.3 create mode 100644 tools/libaio/man/aio_fsync64.3 create mode 100644 tools/libaio/man/aio_init.3 create mode 100644 tools/libaio/man/aio_read.3 create mode 100644 tools/libaio/man/aio_read64.3 create mode 100644 tools/libaio/man/aio_return.3 create mode 100644 tools/libaio/man/aio_return64.3 create mode 100644 tools/libaio/man/aio_suspend.3 create mode 100644 tools/libaio/man/aio_suspend64.3 create mode 100644 tools/libaio/man/aio_write.3 create mode 100644 tools/libaio/man/aio_write64.3 create mode 100644 tools/libaio/man/io.3 create mode 100644 tools/libaio/man/io_cancel.1 create mode 100644 tools/libaio/man/io_cancel.3 create mode 100644 tools/libaio/man/io_destroy.1 create mode 100644 tools/libaio/man/io_fsync.3 create mode 100644 tools/libaio/man/io_getevents.1 create mode 100644 tools/libaio/man/io_getevents.3 create mode 100644 tools/libaio/man/io_prep_fsync.3 create mode 100644 tools/libaio/man/io_prep_pread.3 create mode 100644 tools/libaio/man/io_prep_pwrite.3 create mode 100644 tools/libaio/man/io_queue_init.3 create mode 100644 tools/libaio/man/io_queue_release.3 create mode 100644 tools/libaio/man/io_queue_run.3 create mode 100644 tools/libaio/man/io_queue_wait.3 create mode 100644 tools/libaio/man/io_set_callback.3 create mode 100644 tools/libaio/man/io_setup.1 create mode 100644 tools/libaio/man/io_submit.1 create mode 100644 tools/libaio/man/io_submit.3 create mode 100644 tools/libaio/man/lio_listio.3 create mode 100644 tools/libaio/man/lio_listio64.3 create mode 100644 tools/libaio/src/Makefile create mode 100644 tools/libaio/src/compat-0_1.c create mode 100644 tools/libaio/src/io_cancel.c create mode 100644 tools/libaio/src/io_destroy.c create mode 100644 tools/libaio/src/io_getevents.c create mode 100644 tools/libaio/src/io_queue_init.c create mode 100644 tools/libaio/src/io_queue_release.c create mode 100644 tools/libaio/src/io_queue_run.c create mode 100644 tools/libaio/src/io_queue_wait.c create mode 100644 tools/libaio/src/io_setup.c create mode 100644 tools/libaio/src/io_submit.c create mode 100644 tools/libaio/src/libaio.h create mode 100644 tools/libaio/src/libaio.map create mode 100644 tools/libaio/src/raw_syscall.c create mode 100644 tools/libaio/src/syscall-alpha.h create mode 100644 tools/libaio/src/syscall-i386.h create mode 100644 tools/libaio/src/syscall-ia64.h create mode 100644 tools/libaio/src/syscall-ppc.h create mode 100644 tools/libaio/src/syscall-s390.h create mode 100644 tools/libaio/src/syscall-x86_64.h create mode 100644 tools/libaio/src/syscall.h create mode 100644 tools/libaio/src/vsys_def.h create mode 100644 tools/libfsimage/Makefile create mode 100644 tools/libfsimage/Rules.mk create mode 100755 tools/libfsimage/check-libext2fs create mode 100644 tools/libfsimage/common/Makefile create mode 100644 tools/libfsimage/common/fsimage.c create mode 100644 tools/libfsimage/common/fsimage.h create mode 100644 tools/libfsimage/common/fsimage_grub.c create mode 100644 tools/libfsimage/common/fsimage_grub.h create mode 100644 tools/libfsimage/common/fsimage_plugin.c create mode 100644 tools/libfsimage/common/fsimage_plugin.h create mode 100644 tools/libfsimage/common/fsimage_priv.h create mode 100644 tools/libfsimage/common/mapfile-GNU create mode 100644 tools/libfsimage/common/mapfile-SunOS create mode 100644 tools/libfsimage/ext2fs-lib/Makefile create mode 100644 tools/libfsimage/ext2fs-lib/ext2fs-lib.c create mode 100644 tools/libfsimage/ext2fs/Makefile create mode 100644 tools/libfsimage/ext2fs/fsys_ext2fs.c create mode 100644 tools/libfsimage/fat/Makefile create mode 100644 tools/libfsimage/fat/fat.h create mode 100644 tools/libfsimage/fat/fsys_fat.c create mode 100644 tools/libfsimage/iso9660/Makefile create mode 100644 tools/libfsimage/iso9660/fsys_iso9660.c create mode 100644 tools/libfsimage/iso9660/iso9660.h create mode 100644 tools/libfsimage/reiserfs/Makefile create mode 100644 tools/libfsimage/reiserfs/fsys_reiserfs.c create mode 100644 tools/libfsimage/ufs/Makefile create mode 100644 tools/libfsimage/ufs/fsys_ufs.c create mode 100644 tools/libfsimage/ufs/ufs.h create mode 100644 tools/libfsimage/zfs/Makefile create mode 100644 tools/libfsimage/zfs/fsys_zfs.c create mode 100644 tools/libfsimage/zfs/fsys_zfs.h create mode 100644 tools/libfsimage/zfs/mb_info.h create mode 100644 tools/libfsimage/zfs/zfs-include/dmu.h create mode 100644 tools/libfsimage/zfs/zfs-include/dmu_objset.h create mode 100644 tools/libfsimage/zfs/zfs-include/dnode.h create mode 100644 tools/libfsimage/zfs/zfs-include/dsl_dataset.h create mode 100644 tools/libfsimage/zfs/zfs-include/dsl_dir.h create mode 100644 tools/libfsimage/zfs/zfs-include/spa.h create mode 100644 tools/libfsimage/zfs/zfs-include/uberblock_impl.h create mode 100644 tools/libfsimage/zfs/zfs-include/vdev_impl.h create mode 100644 tools/libfsimage/zfs/zfs-include/zap_impl.h create mode 100644 tools/libfsimage/zfs/zfs-include/zap_leaf.h create mode 100644 tools/libfsimage/zfs/zfs-include/zfs.h create mode 100644 tools/libfsimage/zfs/zfs-include/zfs_acl.h create mode 100644 tools/libfsimage/zfs/zfs-include/zfs_znode.h create mode 100644 tools/libfsimage/zfs/zfs-include/zil.h create mode 100644 tools/libfsimage/zfs/zfs-include/zio.h create mode 100644 tools/libfsimage/zfs/zfs-include/zio_checksum.h create mode 100644 tools/libfsimage/zfs/zfs_fletcher.c create mode 100644 tools/libfsimage/zfs/zfs_lzjb.c create mode 100644 tools/libfsimage/zfs/zfs_sha256.c create mode 100644 tools/libxc/Makefile create mode 100644 tools/libxc/ia64/Makefile create mode 100644 tools/libxc/ia64/aclinux.h create mode 100644 tools/libxc/ia64/dom_fw_acpi.c create mode 100644 tools/libxc/ia64/sal.h create mode 100644 tools/libxc/ia64/xc_dom_ia64_util.c create mode 100644 tools/libxc/ia64/xc_dom_ia64_util.h create mode 100644 tools/libxc/ia64/xc_ia64.h create mode 100644 tools/libxc/ia64/xc_ia64_dom_fwloader.c create mode 100644 tools/libxc/ia64/xc_ia64_hvm_build.c create mode 100644 tools/libxc/ia64/xc_ia64_linux_restore.c create mode 100644 tools/libxc/ia64/xc_ia64_linux_save.c create mode 100644 tools/libxc/ia64/xc_ia64_save_restore.h create mode 100644 tools/libxc/ia64/xc_ia64_stubs.c create mode 100644 tools/libxc/rpm.spec create mode 100644 tools/libxc/xc_acm.c create mode 100644 tools/libxc/xc_core.c create mode 100644 tools/libxc/xc_core.h create mode 100644 tools/libxc/xc_core_ia64.c create mode 100644 tools/libxc/xc_core_ia64.h create mode 100644 tools/libxc/xc_core_x86.c create mode 100644 tools/libxc/xc_core_x86.h create mode 100644 tools/libxc/xc_cpu_hotplug.c create mode 100644 tools/libxc/xc_cpufeature.h create mode 100644 tools/libxc/xc_cpuid_x86.c create mode 100644 tools/libxc/xc_csched.c create mode 100644 tools/libxc/xc_dom.h create mode 100644 tools/libxc/xc_dom_binloader.c create mode 100644 tools/libxc/xc_dom_boot.c create mode 100644 tools/libxc/xc_dom_bzimageloader.c create mode 100644 tools/libxc/xc_dom_compat_linux.c create mode 100644 tools/libxc/xc_dom_core.c create mode 100644 tools/libxc/xc_dom_elfloader.c create mode 100644 tools/libxc/xc_dom_ia64.c create mode 100644 tools/libxc/xc_dom_x86.c create mode 100644 tools/libxc/xc_domain.c create mode 100644 tools/libxc/xc_domain_restore.c create mode 100644 tools/libxc/xc_domain_save.c create mode 100644 tools/libxc/xc_e820.h create mode 100644 tools/libxc/xc_efi.h create mode 100644 tools/libxc/xc_elf.h create mode 100644 tools/libxc/xc_evtchn.c create mode 100644 tools/libxc/xc_flask.c create mode 100644 tools/libxc/xc_hvm_build.c create mode 100644 tools/libxc/xc_linux.c create mode 100644 tools/libxc/xc_minios.c create mode 100644 tools/libxc/xc_misc.c create mode 100644 tools/libxc/xc_netbsd.c create mode 100644 tools/libxc/xc_pagetab.c create mode 100644 tools/libxc/xc_physdev.c create mode 100644 tools/libxc/xc_pm.c create mode 100644 tools/libxc/xc_private.c create mode 100644 tools/libxc/xc_private.h create mode 100644 tools/libxc/xc_ptrace.c create mode 100644 tools/libxc/xc_ptrace.h create mode 100644 tools/libxc/xc_ptrace_core.c create mode 100644 tools/libxc/xc_resume.c create mode 100644 tools/libxc/xc_sedf.c create mode 100644 tools/libxc/xc_solaris.c create mode 100644 tools/libxc/xc_tbuf.c create mode 100644 tools/libxc/xenctrl.h create mode 100644 tools/libxc/xenguest.h create mode 100644 tools/libxc/xg_private.c create mode 100644 tools/libxc/xg_private.h create mode 100644 tools/libxc/xg_save_restore.h create mode 100644 tools/libxen/COPYING create mode 100644 tools/libxen/Makefile create mode 100644 tools/libxen/Makefile.dist create mode 100644 tools/libxen/README create mode 100644 tools/libxen/include/xen/api/xen_acmpolicy.h create mode 100644 tools/libxen/include/xen/api/xen_all.h create mode 100644 tools/libxen/include/xen/api/xen_common.h create mode 100644 tools/libxen/include/xen/api/xen_console.h create mode 100644 tools/libxen/include/xen/api/xen_console_decl.h create mode 100644 tools/libxen/include/xen/api/xen_console_protocol.h create mode 100644 tools/libxen/include/xen/api/xen_crashdump.h create mode 100644 tools/libxen/include/xen/api/xen_crashdump_decl.h create mode 100644 tools/libxen/include/xen/api/xen_event.h create mode 100644 tools/libxen/include/xen/api/xen_event_decl.h create mode 100644 tools/libxen/include/xen/api/xen_event_operation.h create mode 100644 tools/libxen/include/xen/api/xen_host.h create mode 100644 tools/libxen/include/xen/api/xen_host_cpu.h create mode 100644 tools/libxen/include/xen/api/xen_host_cpu_decl.h create mode 100644 tools/libxen/include/xen/api/xen_host_decl.h create mode 100644 tools/libxen/include/xen/api/xen_host_metrics.h create mode 100644 tools/libxen/include/xen/api/xen_host_metrics_decl.h create mode 100644 tools/libxen/include/xen/api/xen_int_float_map.h create mode 100644 tools/libxen/include/xen/api/xen_int_int_map.h create mode 100644 tools/libxen/include/xen/api/xen_int_string_set_map.h create mode 100644 tools/libxen/include/xen/api/xen_network.h create mode 100644 tools/libxen/include/xen/api/xen_network_decl.h create mode 100644 tools/libxen/include/xen/api/xen_on_crash_behaviour.h create mode 100644 tools/libxen/include/xen/api/xen_on_normal_exit.h create mode 100644 tools/libxen/include/xen/api/xen_pbd.h create mode 100644 tools/libxen/include/xen/api/xen_pbd_decl.h create mode 100644 tools/libxen/include/xen/api/xen_pif.h create mode 100644 tools/libxen/include/xen/api/xen_pif_decl.h create mode 100644 tools/libxen/include/xen/api/xen_pif_metrics.h create mode 100644 tools/libxen/include/xen/api/xen_pif_metrics_decl.h create mode 100644 tools/libxen/include/xen/api/xen_sr.h create mode 100644 tools/libxen/include/xen/api/xen_sr_decl.h create mode 100644 tools/libxen/include/xen/api/xen_string_set.h create mode 100644 tools/libxen/include/xen/api/xen_string_string_map.h create mode 100644 tools/libxen/include/xen/api/xen_user.h create mode 100644 tools/libxen/include/xen/api/xen_user_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vbd.h create mode 100644 tools/libxen/include/xen/api/xen_vbd_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vbd_metrics.h create mode 100644 tools/libxen/include/xen/api/xen_vbd_metrics_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vbd_mode.h create mode 100644 tools/libxen/include/xen/api/xen_vbd_type.h create mode 100644 tools/libxen/include/xen/api/xen_vdi.h create mode 100644 tools/libxen/include/xen/api/xen_vdi_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vdi_type.h create mode 100644 tools/libxen/include/xen/api/xen_vif.h create mode 100644 tools/libxen/include/xen/api/xen_vif_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vif_metrics.h create mode 100644 tools/libxen/include/xen/api/xen_vif_metrics_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vm.h create mode 100644 tools/libxen/include/xen/api/xen_vm_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vm_guest_metrics.h create mode 100644 tools/libxen/include/xen/api/xen_vm_guest_metrics_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vm_metrics.h create mode 100644 tools/libxen/include/xen/api/xen_vm_metrics_decl.h create mode 100644 tools/libxen/include/xen/api/xen_vm_power_state.h create mode 100644 tools/libxen/include/xen/api/xen_vtpm.h create mode 100644 tools/libxen/include/xen/api/xen_vtpm_decl.h create mode 100644 tools/libxen/include/xen/api/xen_xspolicy.h create mode 100644 tools/libxen/include/xen/api/xen_xspolicy_decl.h create mode 100644 tools/libxen/include/xen_console_protocol_internal.h create mode 100644 tools/libxen/include/xen_event_operation_internal.h create mode 100644 tools/libxen/include/xen_internal.h create mode 100644 tools/libxen/include/xen_on_crash_behaviour_internal.h create mode 100644 tools/libxen/include/xen_on_normal_exit_internal.h create mode 100644 tools/libxen/include/xen_vbd_mode_internal.h create mode 100644 tools/libxen/include/xen_vbd_type_internal.h create mode 100644 tools/libxen/include/xen_vdi_type_internal.h create mode 100644 tools/libxen/include/xen_vm_power_state_internal.h create mode 100644 tools/libxen/src/xen_acmpolicy.c create mode 100644 tools/libxen/src/xen_common.c create mode 100644 tools/libxen/src/xen_console.c create mode 100644 tools/libxen/src/xen_console_protocol.c create mode 100644 tools/libxen/src/xen_crashdump.c create mode 100644 tools/libxen/src/xen_event.c create mode 100644 tools/libxen/src/xen_event_operation.c create mode 100644 tools/libxen/src/xen_host.c create mode 100644 tools/libxen/src/xen_host_cpu.c create mode 100644 tools/libxen/src/xen_host_metrics.c create mode 100644 tools/libxen/src/xen_int_float_map.c create mode 100644 tools/libxen/src/xen_int_int_map.c create mode 100644 tools/libxen/src/xen_int_string_set_map.c create mode 100644 tools/libxen/src/xen_network.c create mode 100644 tools/libxen/src/xen_on_crash_behaviour.c create mode 100644 tools/libxen/src/xen_on_normal_exit.c create mode 100644 tools/libxen/src/xen_pbd.c create mode 100644 tools/libxen/src/xen_pif.c create mode 100644 tools/libxen/src/xen_pif_metrics.c create mode 100644 tools/libxen/src/xen_sr.c create mode 100644 tools/libxen/src/xen_string_set.c create mode 100644 tools/libxen/src/xen_string_set.h create mode 100644 tools/libxen/src/xen_string_string_map.c create mode 100644 tools/libxen/src/xen_user.c create mode 100644 tools/libxen/src/xen_vbd.c create mode 100644 tools/libxen/src/xen_vbd_metrics.c create mode 100644 tools/libxen/src/xen_vbd_mode.c create mode 100644 tools/libxen/src/xen_vbd_type.c create mode 100644 tools/libxen/src/xen_vdi.c create mode 100644 tools/libxen/src/xen_vdi_type.c create mode 100644 tools/libxen/src/xen_vif.c create mode 100644 tools/libxen/src/xen_vif_metrics.c create mode 100644 tools/libxen/src/xen_vm.c create mode 100644 tools/libxen/src/xen_vm_guest_metrics.c create mode 100644 tools/libxen/src/xen_vm_metrics.c create mode 100644 tools/libxen/src/xen_vm_power_state.c create mode 100644 tools/libxen/src/xen_vtpm.c create mode 100644 tools/libxen/src/xen_xspolicy.c create mode 100644 tools/libxen/test/test_bindings.c create mode 100644 tools/libxen/test/test_event_handling.c create mode 100644 tools/misc/Makefile create mode 100755 tools/misc/fakei386xen create mode 100644 tools/misc/lomount/Makefile create mode 100644 tools/misc/lomount/lomount.c create mode 100644 tools/misc/miniterm/Makefile create mode 100644 tools/misc/miniterm/README create mode 100644 tools/misc/miniterm/miniterm.c create mode 100644 tools/misc/netfix create mode 100644 tools/misc/nsplitd/Makefile create mode 100644 tools/misc/nsplitd/nsplitd.c create mode 100644 tools/misc/sxp-pretty create mode 100644 tools/misc/xen-bugtool create mode 100644 tools/misc/xen-clone.README create mode 100644 tools/misc/xen-detect.c create mode 100644 tools/misc/xen-python-path create mode 100755 tools/misc/xencons create mode 100644 tools/misc/xend create mode 100644 tools/misc/xenperf.c create mode 100644 tools/misc/xenpm.c create mode 100755 tools/misc/xensymoops create mode 100755 tools/misc/xm create mode 100644 tools/misc/xsview create mode 100644 tools/pygrub/Makefile create mode 100644 tools/pygrub/README create mode 100644 tools/pygrub/setup.py create mode 100644 tools/pygrub/src/GrubConf.py create mode 100644 tools/pygrub/src/LiloConf.py create mode 100644 tools/pygrub/src/__init__.py create mode 100644 tools/pygrub/src/fsimage/fsimage.c create mode 100644 tools/pygrub/src/pygrub create mode 100644 tools/python/Makefile create mode 100644 tools/python/README create mode 100644 tools/python/README.XendConfig create mode 100644 tools/python/README.sxpcfg create mode 100644 tools/python/ZPL-2.0 create mode 100755 tools/python/get-path create mode 100755 tools/python/install-wrap create mode 100644 tools/python/logging/logging-0.4.9.2/PKG-INFO create mode 100644 tools/python/logging/logging-0.4.9.2/README.txt create mode 100644 tools/python/logging/logging-0.4.9.2/default.css create mode 100644 tools/python/logging/logging-0.4.9.2/liblogging.tex create mode 100644 tools/python/logging/logging-0.4.9.2/logging/__init__.py create mode 100644 tools/python/logging/logging-0.4.9.2/logging/config.py create mode 100644 tools/python/logging/logging-0.4.9.2/logging/handlers.py create mode 100644 tools/python/logging/logging-0.4.9.2/python_logging.html create mode 100644 tools/python/logging/logging-0.4.9.2/setup.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/app.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/critical.ini create mode 100644 tools/python/logging/logging-0.4.9.2/test/debug.ini create mode 100644 tools/python/logging/logging-0.4.9.2/test/error.ini create mode 100644 tools/python/logging/logging-0.4.9.2/test/events.xml create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test0.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test1.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test10.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test11.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test12.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test13.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test14.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test15.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test16.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test17.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test18.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test19.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test2.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test20.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test21.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test22.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/log_test3.ini create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test3.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test4.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test5.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test6.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test7.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test8.py create mode 100755 tools/python/logging/logging-0.4.9.2/test/log_test9.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/logconf.ini create mode 100755 tools/python/logging/logging-0.4.9.2/test/logconf.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/logging.dtd create mode 100644 tools/python/logging/logging-0.4.9.2/test/logging.xml create mode 100644 tools/python/logging/logging-0.4.9.2/test/logrecv.ini create mode 100755 tools/python/logging/logging-0.4.9.2/test/logrecv.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/myapp.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/mymodule.py create mode 100644 tools/python/logging/logging-0.4.9.2/test/stderr.exp create mode 100644 tools/python/logging/logging-0.4.9.2/test/stdout.exp create mode 100644 tools/python/logging/logging-0.4.9.2/test/warn.ini create mode 100644 tools/python/logging/setup.py create mode 100644 tools/python/ptsname/ptsname.c create mode 100644 tools/python/pylintrc create mode 100644 tools/python/remove-potcdate.sed create mode 100644 tools/python/scripts/README create mode 100644 tools/python/scripts/README.lifecycle create mode 100644 tools/python/scripts/test_hvm_create.py create mode 100644 tools/python/scripts/test_vm_create.py create mode 100644 tools/python/scripts/xapi.domcfg.py create mode 100644 tools/python/scripts/xapi.py create mode 100644 tools/python/scripts/xapi.vbdcfg.py create mode 100644 tools/python/scripts/xapi.vdicfg.py create mode 100644 tools/python/scripts/xapi.vifcfg.py create mode 100644 tools/python/scripts/xapi.vtpmcfg.py create mode 100644 tools/python/setup.py create mode 100644 tools/python/test.py create mode 100644 tools/python/xen/__init__.py create mode 100644 tools/python/xen/lowlevel/__init__.py create mode 100644 tools/python/xen/lowlevel/acm/acm.c create mode 100644 tools/python/xen/lowlevel/flask/flask.c create mode 100644 tools/python/xen/lowlevel/scf/scf.c create mode 100644 tools/python/xen/lowlevel/xc/xc.c create mode 100644 tools/python/xen/lowlevel/xs/xs.c create mode 100755 tools/python/xen/sv/CreateDomain.py create mode 100755 tools/python/xen/sv/DomInfo.py create mode 100755 tools/python/xen/sv/GenTabbed.py create mode 100755 tools/python/xen/sv/HTMLBase.py create mode 100755 tools/python/xen/sv/Main.py create mode 100755 tools/python/xen/sv/NodeInfo.py create mode 100755 tools/python/xen/sv/RestoreDomain.py create mode 100755 tools/python/xen/sv/Wizard.py create mode 100755 tools/python/xen/sv/__init__.py create mode 100755 tools/python/xen/sv/util.py create mode 100644 tools/python/xen/util/Brctl.py create mode 100644 tools/python/xen/util/SSHTransport.py create mode 100644 tools/python/xen/util/__init__.py create mode 100644 tools/python/xen/util/acmpolicy.py create mode 100644 tools/python/xen/util/asserts.py create mode 100644 tools/python/xen/util/auxbin.py create mode 100644 tools/python/xen/util/blkif.py create mode 100644 tools/python/xen/util/bootloader.py create mode 100644 tools/python/xen/util/bugtool.py create mode 100644 tools/python/xen/util/diagnose.py create mode 100644 tools/python/xen/util/dictio.py create mode 100644 tools/python/xen/util/ip.py create mode 100644 tools/python/xen/util/mac.py create mode 100644 tools/python/xen/util/mkdir.py create mode 100644 tools/python/xen/util/oshelp.py create mode 100644 tools/python/xen/util/pci.py create mode 100644 tools/python/xen/util/utils.py create mode 100644 tools/python/xen/util/vscsi_util.py create mode 100644 tools/python/xen/util/xmlrpcclient.py create mode 100644 tools/python/xen/util/xmlrpclib2.py create mode 100644 tools/python/xen/util/xpopen.py create mode 100644 tools/python/xen/util/xsconstants.py create mode 100644 tools/python/xen/util/xsm/__init__.py create mode 100644 tools/python/xen/util/xsm/acm/__init__.py create mode 100644 tools/python/xen/util/xsm/acm/acm.py create mode 100644 tools/python/xen/util/xsm/dummy/__init__.py create mode 100644 tools/python/xen/util/xsm/dummy/dummy.py create mode 100644 tools/python/xen/util/xsm/flask/__init__.py create mode 100644 tools/python/xen/util/xsm/flask/flask.py create mode 100644 tools/python/xen/util/xsm/xsm.py create mode 100644 tools/python/xen/util/xsm/xsm_core.py create mode 100644 tools/python/xen/util/xspolicy.py create mode 100644 tools/python/xen/web/SrvBase.py create mode 100644 tools/python/xen/web/SrvDir.py create mode 100644 tools/python/xen/web/__init__.py create mode 100644 tools/python/xen/web/connection.py create mode 100644 tools/python/xen/web/http.py create mode 100644 tools/python/xen/web/httpserver.py create mode 100644 tools/python/xen/web/protocol.py create mode 100644 tools/python/xen/web/resource.py create mode 100644 tools/python/xen/web/static.py create mode 100644 tools/python/xen/web/tcp.py create mode 100644 tools/python/xen/web/unix.py create mode 100644 tools/python/xen/xend/Args.py create mode 100644 tools/python/xen/xend/PrettyPrint.py create mode 100644 tools/python/xen/xend/Vifctl.py create mode 100644 tools/python/xen/xend/XendAPI.py create mode 100644 tools/python/xen/xend/XendAPIConstants.py create mode 100644 tools/python/xen/xend/XendAPIStore.py create mode 100644 tools/python/xen/xend/XendAPIVersion.py create mode 100644 tools/python/xen/xend/XendAuthSessions.py create mode 100644 tools/python/xen/xend/XendBase.py create mode 100644 tools/python/xen/xend/XendBootloader.py create mode 100644 tools/python/xen/xend/XendCheckpoint.py create mode 100644 tools/python/xen/xend/XendClient.py create mode 100644 tools/python/xen/xend/XendConfig.py create mode 100644 tools/python/xen/xend/XendConstants.py create mode 100644 tools/python/xen/xend/XendDPCI.py create mode 100644 tools/python/xen/xend/XendDSCSI.py create mode 100644 tools/python/xen/xend/XendDevices.py create mode 100644 tools/python/xen/xend/XendDmesg.py create mode 100644 tools/python/xen/xend/XendDomain.py create mode 100644 tools/python/xen/xend/XendDomainInfo.py create mode 100644 tools/python/xen/xend/XendError.py create mode 100644 tools/python/xen/xend/XendLocalStorageRepo.py create mode 100644 tools/python/xen/xend/XendLogging.py create mode 100644 tools/python/xen/xend/XendMonitor.py create mode 100644 tools/python/xen/xend/XendNetwork.py create mode 100644 tools/python/xen/xend/XendNode.py create mode 100644 tools/python/xen/xend/XendOptions.py create mode 100644 tools/python/xen/xend/XendPBD.py create mode 100644 tools/python/xen/xend/XendPIF.py create mode 100644 tools/python/xen/xend/XendPIFMetrics.py create mode 100644 tools/python/xen/xend/XendPPCI.py create mode 100644 tools/python/xen/xend/XendPSCSI.py create mode 100644 tools/python/xen/xend/XendProtocol.py create mode 100644 tools/python/xen/xend/XendQCoWStorageRepo.py create mode 100644 tools/python/xen/xend/XendStateStore.py create mode 100644 tools/python/xen/xend/XendStorageRepository.py create mode 100644 tools/python/xen/xend/XendTask.py create mode 100644 tools/python/xen/xend/XendTaskManager.py create mode 100644 tools/python/xen/xend/XendVDI.py create mode 100644 tools/python/xen/xend/XendVMMetrics.py create mode 100644 tools/python/xen/xend/XendVnet.py create mode 100644 tools/python/xen/xend/XendXSPolicy.py create mode 100644 tools/python/xen/xend/XendXSPolicyAdmin.py create mode 100644 tools/python/xen/xend/__init__.py create mode 100644 tools/python/xen/xend/arch.py create mode 100644 tools/python/xen/xend/balloon.py create mode 100644 tools/python/xen/xend/encode.py create mode 100644 tools/python/xen/xend/image.py create mode 100644 tools/python/xen/xend/osdep.py create mode 100644 tools/python/xen/xend/server/BlktapController.py create mode 100644 tools/python/xen/xend/server/ConsoleController.py create mode 100644 tools/python/xen/xend/server/DevController.py create mode 100644 tools/python/xen/xend/server/SSLXMLRPCServer.py create mode 100644 tools/python/xen/xend/server/SrvDaemon.py create mode 100644 tools/python/xen/xend/server/SrvDmesg.py create mode 100644 tools/python/xen/xend/server/SrvDomain.py create mode 100644 tools/python/xen/xend/server/SrvDomainDir.py create mode 100644 tools/python/xen/xend/server/SrvNode.py create mode 100644 tools/python/xen/xend/server/SrvRoot.py create mode 100644 tools/python/xen/xend/server/SrvServer.py create mode 100644 tools/python/xen/xend/server/SrvVnetDir.py create mode 100644 tools/python/xen/xend/server/SrvXendLog.py create mode 100644 tools/python/xen/xend/server/XMLRPCServer.py create mode 100644 tools/python/xen/xend/server/__init__.py create mode 100644 tools/python/xen/xend/server/blkif.py create mode 100644 tools/python/xen/xend/server/iopif.py create mode 100644 tools/python/xen/xend/server/irqif.py create mode 100644 tools/python/xen/xend/server/netif.py create mode 100644 tools/python/xen/xend/server/params.py create mode 100644 tools/python/xen/xend/server/pciif.py create mode 100644 tools/python/xen/xend/server/pciquirk.py create mode 100644 tools/python/xen/xend/server/relocate.py create mode 100644 tools/python/xen/xend/server/tests/__init__.py create mode 100644 tools/python/xen/xend/server/tests/test_controllers.py create mode 100644 tools/python/xen/xend/server/tpmif.py create mode 100644 tools/python/xen/xend/server/vfbif.py create mode 100644 tools/python/xen/xend/server/vscsiif.py create mode 100644 tools/python/xen/xend/sxp.py create mode 100644 tools/python/xen/xend/tests/__init__.py create mode 100644 tools/python/xen/xend/tests/test_XendConfig.py create mode 100644 tools/python/xen/xend/tests/test_sxp.py create mode 100644 tools/python/xen/xend/tests/test_uuid.py create mode 100644 tools/python/xen/xend/tests/xend-config.sxp create mode 100644 tools/python/xen/xend/uuid.py create mode 100644 tools/python/xen/xend/xenstore/__init__.py create mode 100644 tools/python/xen/xend/xenstore/tests/__init__.py create mode 100644 tools/python/xen/xend/xenstore/tests/stress_xs.py create mode 100644 tools/python/xen/xend/xenstore/xstransact.py create mode 100644 tools/python/xen/xend/xenstore/xsutil.py create mode 100644 tools/python/xen/xend/xenstore/xswatch.py create mode 100644 tools/python/xen/xm/XenAPI.py create mode 100644 tools/python/xen/xm/__init__.py create mode 100644 tools/python/xen/xm/addlabel.py create mode 100644 tools/python/xen/xm/console.py create mode 100644 tools/python/xen/xm/create.dtd create mode 100644 tools/python/xen/xm/create.py create mode 100644 tools/python/xen/xm/dry-run.py create mode 100644 tools/python/xen/xm/dumppolicy.py create mode 100644 tools/python/xen/xm/getlabel.py create mode 100644 tools/python/xen/xm/getpolicy.py create mode 100644 tools/python/xen/xm/help.py create mode 100644 tools/python/xen/xm/labels.py create mode 100644 tools/python/xen/xm/main.py create mode 100644 tools/python/xen/xm/messages/en/xen-xm.po create mode 100644 tools/python/xen/xm/messages/xen-xm.pot create mode 100644 tools/python/xen/xm/migrate.py create mode 100644 tools/python/xen/xm/new.py create mode 100644 tools/python/xen/xm/opts.py create mode 100644 tools/python/xen/xm/resetpolicy.py create mode 100644 tools/python/xen/xm/resources.py create mode 100644 tools/python/xen/xm/rmlabel.py create mode 100644 tools/python/xen/xm/setpolicy.py create mode 100644 tools/python/xen/xm/shutdown.py create mode 100644 tools/python/xen/xm/tests/__init__.py create mode 100644 tools/python/xen/xm/tests/test_create.py create mode 100644 tools/python/xen/xm/xenapi_create.py create mode 100644 tools/python/xen/xsview/__init__.py create mode 100644 tools/python/xen/xsview/main.py create mode 100644 tools/python/xen/xsview/xsviewer.py create mode 100644 tools/security/Makefile create mode 100644 tools/security/policies/example/client_v1-security_policy.xml create mode 100644 tools/security/policies/example/test-security_policy.xml create mode 100644 tools/security/policies/security_policy.xsd create mode 100644 tools/security/policy.txt create mode 100644 tools/security/policytools.txt create mode 100644 tools/security/python/setup.py create mode 100644 tools/security/python/xensec_gen/__init__.py create mode 100644 tools/security/python/xensec_gen/cgi-bin/policy.cgi create mode 100644 tools/security/python/xensec_gen/index.html create mode 100644 tools/security/python/xensec_gen/main.py create mode 100644 tools/security/python/xensec_tools/acm_getlabel create mode 100644 tools/security/readme.txt create mode 100644 tools/security/secpol_tool.c create mode 100644 tools/security/xensec_ezpolicy create mode 100644 tools/security/xensec_gen.py create mode 100644 tools/sv/Makefile create mode 100755 tools/sv/images/destroy.png create mode 100755 tools/sv/images/finish.png create mode 100755 tools/sv/images/next.png create mode 100755 tools/sv/images/pause.png create mode 100755 tools/sv/images/previous.png create mode 100755 tools/sv/images/reboot.png create mode 100755 tools/sv/images/shutdown.png create mode 100755 tools/sv/images/small-destroy.png create mode 100755 tools/sv/images/small-pause.png create mode 100755 tools/sv/images/small-unpause.png create mode 100755 tools/sv/images/unpause.png create mode 100755 tools/sv/images/xen.png create mode 100755 tools/sv/inc/script.js create mode 100755 tools/sv/inc/style.css create mode 100755 tools/sv/index.psp create mode 100644 tools/tests/Makefile create mode 100644 tools/tests/blowfish.c create mode 100644 tools/tests/blowfish.mk create mode 100644 tools/tests/test_x86_emulator.c create mode 100644 tools/tests/x86_emulate.c create mode 100644 tools/vnet/00INSTALL create mode 100644 tools/vnet/00README create mode 100644 tools/vnet/Make.env create mode 100644 tools/vnet/Makefile create mode 100644 tools/vnet/doc/Makefile create mode 100644 tools/vnet/doc/man/vn.pod.1 create mode 100644 tools/vnet/doc/vnet-module.txt create mode 100644 tools/vnet/doc/vnet-xend.txt create mode 100644 tools/vnet/examples/Makefile create mode 100755 tools/vnet/examples/network-vnet create mode 100644 tools/vnet/examples/vnet-insert create mode 100644 tools/vnet/examples/vnet97.sxp create mode 100644 tools/vnet/examples/vnet98.sxp create mode 100644 tools/vnet/examples/vnet99.sxp create mode 100644 tools/vnet/libxutil/Makefile create mode 100644 tools/vnet/libxutil/allocate.c create mode 100644 tools/vnet/libxutil/allocate.h create mode 100644 tools/vnet/libxutil/debug.h create mode 100644 tools/vnet/libxutil/enum.c create mode 100644 tools/vnet/libxutil/enum.h create mode 100644 tools/vnet/libxutil/fd_stream.c create mode 100644 tools/vnet/libxutil/fd_stream.h create mode 100644 tools/vnet/libxutil/file_stream.c create mode 100644 tools/vnet/libxutil/file_stream.h create mode 100644 tools/vnet/libxutil/gzip_stream.c create mode 100644 tools/vnet/libxutil/gzip_stream.h create mode 100644 tools/vnet/libxutil/hash_table.c create mode 100644 tools/vnet/libxutil/hash_table.h create mode 100644 tools/vnet/libxutil/iostream.c create mode 100644 tools/vnet/libxutil/iostream.h create mode 100644 tools/vnet/libxutil/kernel_stream.c create mode 100644 tools/vnet/libxutil/kernel_stream.h create mode 100644 tools/vnet/libxutil/lexis.c create mode 100644 tools/vnet/libxutil/lexis.h create mode 100644 tools/vnet/libxutil/mem_stream.c create mode 100644 tools/vnet/libxutil/mem_stream.h create mode 100644 tools/vnet/libxutil/socket_stream.c create mode 100644 tools/vnet/libxutil/socket_stream.h create mode 100644 tools/vnet/libxutil/string_stream.c create mode 100644 tools/vnet/libxutil/string_stream.h create mode 100644 tools/vnet/libxutil/sxpr.c create mode 100644 tools/vnet/libxutil/sxpr.h create mode 100644 tools/vnet/libxutil/sxpr_parser.c create mode 100644 tools/vnet/libxutil/sxpr_parser.h create mode 100644 tools/vnet/libxutil/sys_net.c create mode 100644 tools/vnet/libxutil/sys_net.h create mode 100644 tools/vnet/libxutil/sys_string.c create mode 100644 tools/vnet/libxutil/sys_string.h create mode 100644 tools/vnet/libxutil/util.c create mode 100644 tools/vnet/libxutil/util.h create mode 100644 tools/vnet/scripts/Makefile create mode 100644 tools/vnet/scripts/vn create mode 100644 tools/vnet/vnet-module/00README create mode 100644 tools/vnet/vnet-module/Makefile create mode 100644 tools/vnet/vnet-module/Makefile-2.4 create mode 100644 tools/vnet/vnet-module/Makefile-2.6 create mode 100644 tools/vnet/vnet-module/Makefile.ver create mode 100644 tools/vnet/vnet-module/Makefile.vnet create mode 100644 tools/vnet/vnet-module/esp.c create mode 100644 tools/vnet/vnet-module/esp.h create mode 100644 tools/vnet/vnet-module/etherip.c create mode 100644 tools/vnet/vnet-module/etherip.h create mode 100644 tools/vnet/vnet-module/if_etherip.h create mode 100644 tools/vnet/vnet-module/if_varp.h create mode 100644 tools/vnet/vnet-module/linux/pfkeyv2.h create mode 100644 tools/vnet/vnet-module/random.c create mode 100644 tools/vnet/vnet-module/random.h create mode 100644 tools/vnet/vnet-module/sa.c create mode 100644 tools/vnet/vnet-module/sa.h create mode 100644 tools/vnet/vnet-module/sa_algorithm.c create mode 100644 tools/vnet/vnet-module/sa_algorithm.h create mode 100644 tools/vnet/vnet-module/skb_context.c create mode 100644 tools/vnet/vnet-module/skb_context.h create mode 100644 tools/vnet/vnet-module/skb_util.c create mode 100644 tools/vnet/vnet-module/skb_util.h create mode 100644 tools/vnet/vnet-module/sxpr_util.c create mode 100644 tools/vnet/vnet-module/sxpr_util.h create mode 100644 tools/vnet/vnet-module/timer_util.c create mode 100644 tools/vnet/vnet-module/timer_util.h create mode 100644 tools/vnet/vnet-module/tunnel.c create mode 100644 tools/vnet/vnet-module/tunnel.h create mode 100644 tools/vnet/vnet-module/varp.c create mode 100644 tools/vnet/vnet-module/varp.h create mode 100644 tools/vnet/vnet-module/varp_socket.c create mode 100644 tools/vnet/vnet-module/varp_util.c create mode 100644 tools/vnet/vnet-module/varp_util.h create mode 100644 tools/vnet/vnet-module/vif.c create mode 100644 tools/vnet/vnet-module/vif.h create mode 100644 tools/vnet/vnet-module/vnet.c create mode 100644 tools/vnet/vnet-module/vnet.h create mode 100644 tools/vnet/vnet-module/vnet_dev.c create mode 100644 tools/vnet/vnet-module/vnet_dev.h create mode 100644 tools/vnet/vnet-module/vnet_eval.c create mode 100644 tools/vnet/vnet-module/vnet_eval.h create mode 100644 tools/vnet/vnet-module/vnet_forward.c create mode 100644 tools/vnet/vnet-module/vnet_forward.h create mode 100644 tools/vnet/vnet-module/vnet_ioctl.c create mode 100644 tools/vnet/vnet-module/vnet_ioctl.h create mode 100644 tools/vnet/vnetd/Makefile create mode 100644 tools/vnet/vnetd/connection.c create mode 100644 tools/vnet/vnetd/connection.h create mode 100644 tools/vnet/vnetd/list.h create mode 100644 tools/vnet/vnetd/select.c create mode 100644 tools/vnet/vnetd/select.h create mode 100644 tools/vnet/vnetd/selector.c create mode 100644 tools/vnet/vnetd/selector.h create mode 100644 tools/vnet/vnetd/skbuff.c create mode 100644 tools/vnet/vnetd/skbuff.h create mode 100644 tools/vnet/vnetd/spinlock.c create mode 100644 tools/vnet/vnetd/spinlock.h create mode 100644 tools/vnet/vnetd/sys_kernel.h create mode 100644 tools/vnet/vnetd/timer.c create mode 100644 tools/vnet/vnetd/timer.h create mode 100644 tools/vnet/vnetd/vnetd.c create mode 100644 tools/vtpm/Makefile create mode 100644 tools/vtpm/README create mode 100644 tools/vtpm/Rules.mk create mode 100644 tools/vtpm/tpm_emulator.patch create mode 100644 tools/vtpm/vtpm.patch create mode 100644 tools/vtpm_manager/COPYING create mode 100644 tools/vtpm_manager/Makefile create mode 100644 tools/vtpm_manager/README create mode 100644 tools/vtpm_manager/Rules.mk create mode 100644 tools/vtpm_manager/crypto/Makefile create mode 100644 tools/vtpm_manager/crypto/crypto.c create mode 100644 tools/vtpm_manager/crypto/crypto.h create mode 100644 tools/vtpm_manager/crypto/hash.c create mode 100644 tools/vtpm_manager/crypto/rsa.c create mode 100644 tools/vtpm_manager/crypto/sym_crypto.c create mode 100644 tools/vtpm_manager/crypto/sym_crypto.h create mode 100644 tools/vtpm_manager/manager/Makefile create mode 100644 tools/vtpm_manager/manager/dmictl.c create mode 100644 tools/vtpm_manager/manager/migration.c create mode 100644 tools/vtpm_manager/manager/securestorage.c create mode 100644 tools/vtpm_manager/manager/tpmpassthrough.c create mode 100644 tools/vtpm_manager/manager/vtpm_ipc.c create mode 100644 tools/vtpm_manager/manager/vtpm_ipc.h create mode 100644 tools/vtpm_manager/manager/vtpm_lock.c create mode 100644 tools/vtpm_manager/manager/vtpm_lock.h create mode 100644 tools/vtpm_manager/manager/vtpm_manager.c create mode 100644 tools/vtpm_manager/manager/vtpm_manager.h create mode 100644 tools/vtpm_manager/manager/vtpm_manager_handler.c create mode 100644 tools/vtpm_manager/manager/vtpmd.c create mode 100644 tools/vtpm_manager/manager/vtpmpriv.h create mode 100644 tools/vtpm_manager/manager/vtsp.c create mode 100644 tools/vtpm_manager/manager/vtsp.h create mode 100644 tools/vtpm_manager/migration/Makefile create mode 100644 tools/vtpm_manager/migration/vtpm_manager_if.c create mode 100644 tools/vtpm_manager/migration/vtpm_migrator.h create mode 100644 tools/vtpm_manager/migration/vtpm_migrator_if.c create mode 100644 tools/vtpm_manager/migration/vtpm_migratorc.c create mode 100644 tools/vtpm_manager/migration/vtpm_migratord.c create mode 100644 tools/vtpm_manager/migration/vtpm_migratord_handler.c create mode 100644 tools/vtpm_manager/tcs/Makefile create mode 100644 tools/vtpm_manager/tcs/contextmgr.c create mode 100644 tools/vtpm_manager/tcs/contextmgr.h create mode 100644 tools/vtpm_manager/tcs/tcs.c create mode 100644 tools/vtpm_manager/tcs/tcs.h create mode 100644 tools/vtpm_manager/tcs/tpmddl.h create mode 100644 tools/vtpm_manager/tcs/transmit.c create mode 100644 tools/vtpm_manager/util/Makefile create mode 100644 tools/vtpm_manager/util/bsg.c create mode 100644 tools/vtpm_manager/util/bsg.h create mode 100644 tools/vtpm_manager/util/buffer.c create mode 100644 tools/vtpm_manager/util/buffer.h create mode 100644 tools/vtpm_manager/util/hashtable.c create mode 100644 tools/vtpm_manager/util/hashtable.h create mode 100644 tools/vtpm_manager/util/hashtable_itr.c create mode 100644 tools/vtpm_manager/util/hashtable_itr.h create mode 100644 tools/vtpm_manager/util/hashtable_private.h create mode 100644 tools/vtpm_manager/util/log.c create mode 100644 tools/vtpm_manager/util/log.h create mode 100644 tools/vtpm_manager/util/tcg.h create mode 100644 tools/xcutils/Makefile create mode 100644 tools/xcutils/lsevtchn.c create mode 100644 tools/xcutils/readnotes.c create mode 100644 tools/xcutils/xc_restore.c create mode 100644 tools/xcutils/xc_save.c create mode 100644 tools/xenballoon/xenballoon-monitor create mode 100644 tools/xenballoon/xenballoon.conf create mode 100644 tools/xenballoon/xenballoond create mode 100644 tools/xenballoon/xenballoond.README create mode 100644 tools/xenballoon/xenballoond.init create mode 100644 tools/xenmon/COPYING create mode 100644 tools/xenmon/Makefile create mode 100644 tools/xenmon/README create mode 100644 tools/xenmon/setmask.c create mode 100644 tools/xenmon/xenbaked.c create mode 100644 tools/xenmon/xenbaked.h create mode 100644 tools/xenmon/xenmon.py create mode 100644 tools/xenstat/Makefile create mode 100644 tools/xenstat/libxenstat/COPYING create mode 100644 tools/xenstat/libxenstat/Makefile create mode 100644 tools/xenstat/libxenstat/bindings/swig/perl/.empty create mode 100644 tools/xenstat/libxenstat/bindings/swig/python/.empty create mode 100644 tools/xenstat/libxenstat/bindings/swig/xenstat.i create mode 100644 tools/xenstat/libxenstat/src/xenstat.c create mode 100644 tools/xenstat/libxenstat/src/xenstat.h create mode 100644 tools/xenstat/libxenstat/src/xenstat_linux.c create mode 100644 tools/xenstat/libxenstat/src/xenstat_netbsd.c create mode 100644 tools/xenstat/libxenstat/src/xenstat_priv.h create mode 100644 tools/xenstat/libxenstat/src/xenstat_solaris.c create mode 100644 tools/xenstat/xentop/Makefile create mode 100644 tools/xenstat/xentop/TODO create mode 100644 tools/xenstat/xentop/xentop.1 create mode 100644 tools/xenstat/xentop/xentop.c create mode 100644 tools/xenstore/.gdbinit create mode 100644 tools/xenstore/COPYING create mode 100644 tools/xenstore/Makefile create mode 100644 tools/xenstore/README create mode 100644 tools/xenstore/TODO create mode 100644 tools/xenstore/hashtable.c create mode 100644 tools/xenstore/hashtable.h create mode 100644 tools/xenstore/hashtable_private.h create mode 100644 tools/xenstore/list.h create mode 100644 tools/xenstore/talloc.c create mode 100644 tools/xenstore/talloc.h create mode 100644 tools/xenstore/talloc_guide.txt create mode 100644 tools/xenstore/tdb.c create mode 100644 tools/xenstore/tdb.h create mode 100644 tools/xenstore/utils.c create mode 100644 tools/xenstore/utils.h create mode 100644 tools/xenstore/xenstore_client.c create mode 100644 tools/xenstore/xenstore_control.c create mode 100644 tools/xenstore/xenstored_core.c create mode 100644 tools/xenstore/xenstored_core.h create mode 100644 tools/xenstore/xenstored_domain.c create mode 100644 tools/xenstore/xenstored_domain.h create mode 100644 tools/xenstore/xenstored_linux.c create mode 100644 tools/xenstore/xenstored_netbsd.c create mode 100644 tools/xenstore/xenstored_probes.d create mode 100644 tools/xenstore/xenstored_solaris.c create mode 100644 tools/xenstore/xenstored_transaction.c create mode 100644 tools/xenstore/xenstored_transaction.h create mode 100644 tools/xenstore/xenstored_watch.c create mode 100644 tools/xenstore/xenstored_watch.h create mode 100644 tools/xenstore/xs.c create mode 100644 tools/xenstore/xs.h create mode 100644 tools/xenstore/xs_lib.c create mode 100644 tools/xenstore/xs_lib.h create mode 100644 tools/xenstore/xs_tdb_dump.c create mode 100644 tools/xentrace/Makefile create mode 100644 tools/xentrace/formats create mode 100644 tools/xentrace/setsize.c create mode 100644 tools/xentrace/xenctx.c create mode 100644 tools/xentrace/xentrace.8 create mode 100644 tools/xentrace/xentrace.c create mode 100644 tools/xentrace/xentrace_format create mode 100644 tools/xentrace/xentrace_format.1 create mode 100644 tools/xm-test/COPYING create mode 100644 tools/xm-test/ChangeLog create mode 100644 tools/xm-test/Makefile.am create mode 100644 tools/xm-test/README create mode 100644 tools/xm-test/TODO create mode 100644 tools/xm-test/Writing_Tests_HOWTO create mode 100755 tools/xm-test/autogen create mode 100644 tools/xm-test/configure.ac create mode 100644 tools/xm-test/grouptest/create create mode 100644 tools/xm-test/grouptest/default create mode 100644 tools/xm-test/grouptest/medium create mode 100644 tools/xm-test/grouptest/quick create mode 100644 tools/xm-test/grouptest/security create mode 100644 tools/xm-test/grouptest/xapi create mode 100755 tools/xm-test/lib/XmTestLib/Console.py create mode 100644 tools/xm-test/lib/XmTestLib/DomainTracking.py create mode 100644 tools/xm-test/lib/XmTestLib/NetConfig.py create mode 100644 tools/xm-test/lib/XmTestLib/Test.py create mode 100644 tools/xm-test/lib/XmTestLib/XenAPIDomain.py create mode 100644 tools/xm-test/lib/XmTestLib/XenDevice.py create mode 100644 tools/xm-test/lib/XmTestLib/XenDomain.py create mode 100644 tools/xm-test/lib/XmTestLib/Xm.py create mode 100644 tools/xm-test/lib/XmTestLib/__init__.py create mode 100644 tools/xm-test/lib/XmTestLib/acm.py create mode 100644 tools/xm-test/lib/XmTestLib/arch.py create mode 100644 tools/xm-test/lib/XmTestLib/block_utils.py create mode 100644 tools/xm-test/lib/XmTestLib/config.py.in create mode 100644 tools/xm-test/lib/XmTestLib/network_utils.py create mode 100644 tools/xm-test/lib/XmTestLib/xapi.py create mode 100644 tools/xm-test/lib/XmTestReport/OSReport.py create mode 100644 tools/xm-test/lib/XmTestReport/ProgReport.py create mode 100644 tools/xm-test/lib/XmTestReport/Report.py create mode 100644 tools/xm-test/lib/XmTestReport/ResultReport.py create mode 100644 tools/xm-test/lib/XmTestReport/arch.py create mode 100644 tools/xm-test/lib/XmTestReport/utils.py create mode 100644 tools/xm-test/lib/XmTestReport/xmtest.py.in create mode 100644 tools/xm-test/mergereport create mode 100644 tools/xm-test/mkreport create mode 100644 tools/xm-test/ramdisk/Makefile.am create mode 100644 tools/xm-test/ramdisk/README-XenSource-initrd-0.7-img create mode 100644 tools/xm-test/ramdisk/README-XenSource-initrd-0.8-img create mode 100644 tools/xm-test/ramdisk/README-XenSource-initrd-1.0-img create mode 100644 tools/xm-test/ramdisk/README-XenSource-initrd-1.1-img create mode 100644 tools/xm-test/ramdisk/bin/create_disk_image create mode 100644 tools/xm-test/ramdisk/configs/buildroot-i386 create mode 100644 tools/xm-test/ramdisk/configs/busybox create mode 100644 tools/xm-test/ramdisk/configs/uClibc create mode 100644 tools/xm-test/ramdisk/make-release.sh create mode 100644 tools/xm-test/ramdisk/patches/buildroot/add_xvd_devices.patch create mode 100644 tools/xm-test/ramdisk/patches/buildroot/hping.patch create mode 100644 tools/xm-test/ramdisk/skel/.profile create mode 100644 tools/xm-test/ramdisk/skel/etc/init.d/rcS create mode 100644 tools/xm-test/ramdisk/skel/etc/inittab create mode 100644 tools/xm-test/ramdisk/skel/root/.profile create mode 100755 tools/xm-test/runtest.sh create mode 100644 tools/xm-test/tests/Makefile.am create mode 100644 tools/xm-test/tests/Makefile.am.template create mode 100644 tools/xm-test/tests/_sanity/01_domu_proc.py create mode 100644 tools/xm-test/tests/_sanity/Makefile.am create mode 100644 tools/xm-test/tests/block-create/01_block_attach_device_pos.py create mode 100644 tools/xm-test/tests/block-create/02_block_attach_file_device_pos.py create mode 100644 tools/xm-test/tests/block-create/04_block_attach_device_repeatedly_pos.py create mode 100644 tools/xm-test/tests/block-create/05_block_attach_and_dettach_device_repeatedly_pos.py create mode 100644 tools/xm-test/tests/block-create/06_block_attach_baddomain_neg.py create mode 100644 tools/xm-test/tests/block-create/07_block_attach_baddevice_neg.py create mode 100644 tools/xm-test/tests/block-create/08_block_attach_bad_filedevice_neg.py create mode 100644 tools/xm-test/tests/block-create/09_block_attach_and_dettach_device_check_data_pos.py create mode 100644 tools/xm-test/tests/block-create/10_block_attach_dettach_multiple_devices.py create mode 100644 tools/xm-test/tests/block-create/11_block_attach_shared_dom0.py create mode 100644 tools/xm-test/tests/block-create/12_block_attach_shared_domU.py create mode 100644 tools/xm-test/tests/block-create/Makefile.am create mode 100644 tools/xm-test/tests/block-destroy/01_block-destroy_btblock_pos.py create mode 100644 tools/xm-test/tests/block-destroy/02_block-destroy_rtblock_pos.py create mode 100644 tools/xm-test/tests/block-destroy/03_block-destroy_nonexist_neg.py create mode 100644 tools/xm-test/tests/block-destroy/04_block-destroy_nonattached_neg.py create mode 100644 tools/xm-test/tests/block-destroy/05_block-destroy_byname_pos.py create mode 100644 tools/xm-test/tests/block-destroy/06_block-destroy_check_list_pos.py create mode 100644 tools/xm-test/tests/block-destroy/Makefile.am create mode 100644 tools/xm-test/tests/block-integrity/01_block_device_read_verify.py create mode 100644 tools/xm-test/tests/block-integrity/02_block_device_write_verify.py create mode 100644 tools/xm-test/tests/block-integrity/Makefile.am create mode 100644 tools/xm-test/tests/block-list/01_block-list_pos.py create mode 100644 tools/xm-test/tests/block-list/02_block-list_attachbd_pos.py create mode 100644 tools/xm-test/tests/block-list/03_block-list_anotherbd_pos.py create mode 100644 tools/xm-test/tests/block-list/04_block-list_nodb_pos.py create mode 100644 tools/xm-test/tests/block-list/05_block-list_nonexist_neg.py create mode 100644 tools/xm-test/tests/block-list/06_block-list_checkremove_pos.py create mode 100644 tools/xm-test/tests/block-list/Makefile.am create mode 100644 tools/xm-test/tests/console/01_console_badopt_neg.py create mode 100644 tools/xm-test/tests/console/02_console_baddom_neg.py create mode 100644 tools/xm-test/tests/console/Makefile.am create mode 100644 tools/xm-test/tests/create/01_create_basic_pos.py create mode 100644 tools/xm-test/tests/create/02_create_noparm_neg.py create mode 100644 tools/xm-test/tests/create/03_create_badparm_neg.py create mode 100644 tools/xm-test/tests/create/04_create_conflictname_neg.py create mode 100644 tools/xm-test/tests/create/06_create_mem_neg.py create mode 100644 tools/xm-test/tests/create/07_create_mem64_pos.py create mode 100644 tools/xm-test/tests/create/08_create_mem128_pos.py create mode 100644 tools/xm-test/tests/create/09_create_mem256_pos.py create mode 100644 tools/xm-test/tests/create/10_create_fastdestroy.py create mode 100644 tools/xm-test/tests/create/11_create_concurrent_pos.py create mode 100644 tools/xm-test/tests/create/12_create_concurrent_stress_pos.py create mode 100644 tools/xm-test/tests/create/13_create_multinic_pos.py create mode 100644 tools/xm-test/tests/create/14_create_blockroot_pos.py create mode 100644 tools/xm-test/tests/create/15_create_smallmem_pos.py create mode 100644 tools/xm-test/tests/create/16_create_smallmem_neg.py create mode 100644 tools/xm-test/tests/create/Makefile.am create mode 100644 tools/xm-test/tests/destroy/01_destroy_basic_pos.py create mode 100644 tools/xm-test/tests/destroy/02_destroy_noparm_neg.py create mode 100644 tools/xm-test/tests/destroy/03_destroy_nonexist_neg.py create mode 100644 tools/xm-test/tests/destroy/04_destroy_badparm_neg.py create mode 100644 tools/xm-test/tests/destroy/05_destroy_byid_pos.py create mode 100644 tools/xm-test/tests/destroy/06_destroy_dom0_neg.py create mode 100644 tools/xm-test/tests/destroy/07_destroy_stale_pos.py create mode 100644 tools/xm-test/tests/destroy/Makefile.am create mode 100644 tools/xm-test/tests/dmesg/01_dmesg_basic_pos.py create mode 100644 tools/xm-test/tests/dmesg/02_dmesg_basic_neg.py create mode 100644 tools/xm-test/tests/dmesg/Makefile.am create mode 100644 tools/xm-test/tests/domid/01_domid_basic_pos.py create mode 100644 tools/xm-test/tests/domid/02_domid_basic_neg.py create mode 100644 tools/xm-test/tests/domid/Makefile.am create mode 100644 tools/xm-test/tests/domname/01_domname_basic_pos.py create mode 100644 tools/xm-test/tests/domname/02_domname_basic_neg.py create mode 100644 tools/xm-test/tests/domname/Makefile.am create mode 100644 tools/xm-test/tests/enforce_dom0_cpus/01_enforce_dom0_cpus_basic_pos.py create mode 100644 tools/xm-test/tests/enforce_dom0_cpus/Makefile.am create mode 100644 tools/xm-test/tests/help/01_help_basic_pos.py create mode 100644 tools/xm-test/tests/help/02_help_basic_neg.py create mode 100644 tools/xm-test/tests/help/03_help_badparm_neg.py create mode 100644 tools/xm-test/tests/help/04_help_long_pos.py create mode 100644 tools/xm-test/tests/help/05_help_nonroot_pos.py create mode 100644 tools/xm-test/tests/help/06_help_allcmds.py create mode 100644 tools/xm-test/tests/help/Makefile.am create mode 100644 tools/xm-test/tests/info/01_info_basic_pos.py create mode 100644 tools/xm-test/tests/info/02_info_compiledata_pos.py create mode 100644 tools/xm-test/tests/info/Makefile.am create mode 100644 tools/xm-test/tests/list/01_list_basic_pos.py create mode 100644 tools/xm-test/tests/list/02_list_badparm_neg.py create mode 100644 tools/xm-test/tests/list/03_list_nonexist_neg.py create mode 100644 tools/xm-test/tests/list/04_list_goodparm_pos.py create mode 100644 tools/xm-test/tests/list/05_list_long_pos.py create mode 100644 tools/xm-test/tests/list/06_list_nonroot.py create mode 100644 tools/xm-test/tests/list/Makefile.am create mode 100644 tools/xm-test/tests/memmax/01_memmax_badparm_neg.py create mode 100644 tools/xm-test/tests/memmax/Makefile.am create mode 100644 tools/xm-test/tests/memset/01_memset_basic_pos.py create mode 100644 tools/xm-test/tests/memset/02_memset_badparm_neg.py create mode 100644 tools/xm-test/tests/memset/03_memset_random_pos.py create mode 100644 tools/xm-test/tests/memset/04_memset_smallmem_pos.py create mode 100644 tools/xm-test/tests/memset/Makefile.am create mode 100644 tools/xm-test/tests/migrate/01_migrate_localhost_pos.py create mode 100644 tools/xm-test/tests/migrate/Makefile.am create mode 100644 tools/xm-test/tests/network-attach/01_network_attach_pos.py create mode 100644 tools/xm-test/tests/network-attach/02_network_attach_detach_pos.py create mode 100644 tools/xm-test/tests/network-attach/03_network_attach_detach_multiple_pos.py create mode 100644 tools/xm-test/tests/network-attach/04_network_attach_baddomain_neg.py create mode 100644 tools/xm-test/tests/network-attach/Makefile.am create mode 100644 tools/xm-test/tests/network/02_network_local_ping_pos.py create mode 100644 tools/xm-test/tests/network/03_network_local_tcp_pos.py create mode 100644 tools/xm-test/tests/network/04_network_local_udp_pos.py create mode 100644 tools/xm-test/tests/network/05_network_dom0_ping_pos.py create mode 100644 tools/xm-test/tests/network/06_network_dom0_tcp_pos.py create mode 100644 tools/xm-test/tests/network/07_network_dom0_udp_pos.py create mode 100644 tools/xm-test/tests/network/11_network_domU_ping_pos.py create mode 100644 tools/xm-test/tests/network/12_network_domU_tcp_pos.py create mode 100644 tools/xm-test/tests/network/13_network_domU_udp_pos.py create mode 100644 tools/xm-test/tests/network/Makefile.am create mode 100644 tools/xm-test/tests/pause/01_pause_basic_pos.py create mode 100644 tools/xm-test/tests/pause/02_pause_badopt_neg.py create mode 100644 tools/xm-test/tests/pause/03_pause_badname_neg.py create mode 100644 tools/xm-test/tests/pause/04_pause_badid_neg.py create mode 100644 tools/xm-test/tests/pause/Makefile.am create mode 100644 tools/xm-test/tests/reboot/01_reboot_basic_pos.py create mode 100644 tools/xm-test/tests/reboot/02_reboot_badopt_neg.py create mode 100644 tools/xm-test/tests/reboot/03_reboot_badname_neg.py create mode 100644 tools/xm-test/tests/reboot/Makefile.am create mode 100644 tools/xm-test/tests/restore/01_restore_basic_pos.py create mode 100644 tools/xm-test/tests/restore/02_restore_badparm_neg.py create mode 100644 tools/xm-test/tests/restore/03_restore_badfilename_neg.py create mode 100644 tools/xm-test/tests/restore/04_restore_withdevices_pos.py create mode 100644 tools/xm-test/tests/restore/Makefile.am create mode 100644 tools/xm-test/tests/save/01_save_basic_pos.py create mode 100644 tools/xm-test/tests/save/02_save_badparm_neg.py create mode 100644 tools/xm-test/tests/save/03_save_bogusfile_neg.py create mode 100644 tools/xm-test/tests/save/Makefile.am create mode 100644 tools/xm-test/tests/sched-credit/01_sched_credit_weight_cap_pos.py create mode 100644 tools/xm-test/tests/sched-credit/Makefile.am create mode 100644 tools/xm-test/tests/security-acm/01_security-acm_basic.py create mode 100644 tools/xm-test/tests/security-acm/02_security-acm_dom_start.py create mode 100644 tools/xm-test/tests/security-acm/03_security-acm_dom_conflict.py create mode 100644 tools/xm-test/tests/security-acm/04_security-acm_dom_res.py create mode 100644 tools/xm-test/tests/security-acm/05_security-acm_dom_res_conf.py create mode 100644 tools/xm-test/tests/security-acm/06_security-acm_dom_block_attach.py create mode 100644 tools/xm-test/tests/security-acm/07_security-acm_pol_update.py create mode 100644 tools/xm-test/tests/security-acm/08_security-acm_xapi.py create mode 100644 tools/xm-test/tests/security-acm/09_security-acm_pol_update.py create mode 100644 tools/xm-test/tests/security-acm/10_security-acm_pol_update.py create mode 100644 tools/xm-test/tests/security-acm/Makefile.am create mode 100644 tools/xm-test/tests/security-acm/acm_utils.py create mode 100644 tools/xm-test/tests/security-acm/xm-test-new-security_policy.xml create mode 100644 tools/xm-test/tests/security-acm/xm-test-security_policy.xml create mode 100644 tools/xm-test/tests/security-acm/xm-test-update-security_policy.xml create mode 100644 tools/xm-test/tests/sedf/01_sedf_period_slice_pos.py create mode 100644 tools/xm-test/tests/sedf/02_sedf_period_lower_neg.py create mode 100644 tools/xm-test/tests/sedf/03_sedf_slice_lower_neg.py create mode 100644 tools/xm-test/tests/sedf/04_sedf_slice_upper_neg.py create mode 100644 tools/xm-test/tests/sedf/05_sedf_extratime_pos.py create mode 100644 tools/xm-test/tests/sedf/06_sedf_extratime_disable_neg.py create mode 100644 tools/xm-test/tests/sedf/Makefile.am create mode 100644 tools/xm-test/tests/shutdown/01_shutdown_basic_pos.py create mode 100644 tools/xm-test/tests/shutdown/02_shutdown_badparm_neg.py create mode 100644 tools/xm-test/tests/shutdown/03_shutdown_nonexist_neg.py create mode 100644 tools/xm-test/tests/shutdown/Makefile.am create mode 100644 tools/xm-test/tests/sysrq/01_sysrq_basic_neg.py create mode 100644 tools/xm-test/tests/sysrq/02_sysrq_sync_pos.py create mode 100644 tools/xm-test/tests/sysrq/03_sysrq_withreboot_pos.py create mode 100644 tools/xm-test/tests/sysrq/Makefile.am create mode 100644 tools/xm-test/tests/unpause/01_unpause_basic_pos.py create mode 100644 tools/xm-test/tests/unpause/Makefile.am create mode 100644 tools/xm-test/tests/vcpu-disable/01_vcpu-disable_basic_pos.py create mode 100644 tools/xm-test/tests/vcpu-disable/Makefile.am create mode 100644 tools/xm-test/tests/vcpu-pin/01_vcpu-pin_basic_pos.py create mode 100644 tools/xm-test/tests/vcpu-pin/Makefile.am create mode 100644 tools/xm-test/tests/vtpm/01_vtpm-list_pos.py create mode 100644 tools/xm-test/tests/vtpm/02_vtpm-cat_pcrs.py create mode 100644 tools/xm-test/tests/vtpm/03_vtpm-susp_res.py create mode 100644 tools/xm-test/tests/vtpm/04_vtpm-loc_migr.py create mode 100644 tools/xm-test/tests/vtpm/05_vtpm-loc_migr.py create mode 100644 tools/xm-test/tests/vtpm/06_vtpm-susp_res_pcrs.py create mode 100644 tools/xm-test/tests/vtpm/07_vtpm-mig_pcrs.py create mode 100644 tools/xm-test/tests/vtpm/08_vtpm-mig_pcrs.py create mode 100644 tools/xm-test/tests/vtpm/09_vtpm-xapi.py create mode 100644 tools/xm-test/tests/vtpm/Makefile.am create mode 100644 tools/xm-test/tests/vtpm/vtpm_utils.py create mode 100644 tools/xm-test/tests/xapi/01_xapi-vm_basic.py create mode 100644 tools/xm-test/tests/xapi/02_xapi-vbd_basic.py create mode 100644 tools/xm-test/tests/xapi/03_xapi-network_pos.py create mode 100644 tools/xm-test/tests/xapi/Makefile.am create mode 100644 unmodified_drivers/linux-2.6/Makefile create mode 100644 unmodified_drivers/linux-2.6/README create mode 100644 unmodified_drivers/linux-2.6/balloon/Kbuild create mode 100644 unmodified_drivers/linux-2.6/balloon/Makefile create mode 100644 unmodified_drivers/linux-2.6/blkfront/Kbuild create mode 100644 unmodified_drivers/linux-2.6/blkfront/Makefile create mode 100644 unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopmd.h create mode 100644 unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopud.h create mode 100644 unmodified_drivers/linux-2.6/compat-include/linux/io.h create mode 100644 unmodified_drivers/linux-2.6/compat-include/linux/mutex.h create mode 100644 unmodified_drivers/linux-2.6/compat-include/xen/platform-compat.h create mode 100755 unmodified_drivers/linux-2.6/mkbuildtree create mode 100644 unmodified_drivers/linux-2.6/netfront/Kbuild create mode 100644 unmodified_drivers/linux-2.6/netfront/Makefile create mode 100644 unmodified_drivers/linux-2.6/overrides.mk create mode 100644 unmodified_drivers/linux-2.6/platform-pci/Kbuild create mode 100644 unmodified_drivers/linux-2.6/platform-pci/Makefile create mode 100644 unmodified_drivers/linux-2.6/platform-pci/evtchn.c create mode 100644 unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c create mode 100644 unmodified_drivers/linux-2.6/platform-pci/panic-handler.c create mode 100644 unmodified_drivers/linux-2.6/platform-pci/platform-compat.c create mode 100644 unmodified_drivers/linux-2.6/platform-pci/platform-pci.c create mode 100644 unmodified_drivers/linux-2.6/platform-pci/platform-pci.h create mode 100644 unmodified_drivers/linux-2.6/platform-pci/xen_support.c create mode 100644 unmodified_drivers/linux-2.6/xenbus/empty_directory create mode 100644 xen/COPYING create mode 100644 xen/Makefile create mode 100644 xen/Rules.mk create mode 100644 xen/arch/ia64/Makefile create mode 100644 xen/arch/ia64/Rules.mk create mode 100644 xen/arch/ia64/asm-offsets.c create mode 100755 xen/arch/ia64/asm-xsi-offsets.c create mode 100644 xen/arch/ia64/linux-xen/Makefile create mode 100644 xen/arch/ia64/linux-xen/README.origin create mode 100644 xen/arch/ia64/linux-xen/acpi.c create mode 100644 xen/arch/ia64/linux-xen/acpi_numa.c create mode 100644 xen/arch/ia64/linux-xen/cmdline.c create mode 100644 xen/arch/ia64/linux-xen/efi.c create mode 100644 xen/arch/ia64/linux-xen/entry.S create mode 100644 xen/arch/ia64/linux-xen/entry.h create mode 100644 xen/arch/ia64/linux-xen/head.S create mode 100644 xen/arch/ia64/linux-xen/hpsim_ssc.h create mode 100644 xen/arch/ia64/linux-xen/iosapic.c create mode 100644 xen/arch/ia64/linux-xen/irq_ia64.c create mode 100644 xen/arch/ia64/linux-xen/mca.c create mode 100644 xen/arch/ia64/linux-xen/mca_asm.S create mode 100644 xen/arch/ia64/linux-xen/minstate.h create mode 100644 xen/arch/ia64/linux-xen/mm_contig.c create mode 100644 xen/arch/ia64/linux-xen/numa.c create mode 100644 xen/arch/ia64/linux-xen/perfmon.c create mode 100644 xen/arch/ia64/linux-xen/perfmon_default_smpl.c create mode 100644 xen/arch/ia64/linux-xen/perfmon_generic.h create mode 100644 xen/arch/ia64/linux-xen/perfmon_itanium.h create mode 100644 xen/arch/ia64/linux-xen/perfmon_mckinley.h create mode 100644 xen/arch/ia64/linux-xen/perfmon_montecito.h create mode 100644 xen/arch/ia64/linux-xen/process-linux-xen.c create mode 100644 xen/arch/ia64/linux-xen/sal.c create mode 100644 xen/arch/ia64/linux-xen/setup.c create mode 100644 xen/arch/ia64/linux-xen/smp.c create mode 100644 xen/arch/ia64/linux-xen/smpboot.c create mode 100644 xen/arch/ia64/linux-xen/sn/Makefile create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/Makefile create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/README.origin create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/io_init.c create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/iomv.c create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/irq.c create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/setup.c create mode 100644 xen/arch/ia64/linux-xen/sn/kernel/sn2_smp.c create mode 100644 xen/arch/ia64/linux-xen/sort.c create mode 100644 xen/arch/ia64/linux-xen/time.c create mode 100644 xen/arch/ia64/linux-xen/tlb.c create mode 100644 xen/arch/ia64/linux-xen/unaligned.c create mode 100644 xen/arch/ia64/linux-xen/unwind.c create mode 100644 xen/arch/ia64/linux-xen/unwind_decoder.c create mode 100644 xen/arch/ia64/linux-xen/unwind_i.h create mode 100644 xen/arch/ia64/linux/Makefile create mode 100644 xen/arch/ia64/linux/README.origin create mode 100644 xen/arch/ia64/linux/bitop.c create mode 100644 xen/arch/ia64/linux/carta_random.S create mode 100644 xen/arch/ia64/linux/clear_page.S create mode 100644 xen/arch/ia64/linux/copy_page_mck.S create mode 100644 xen/arch/ia64/linux/dig/Makefile create mode 100644 xen/arch/ia64/linux/dig/README.origin create mode 100644 xen/arch/ia64/linux/dig/machvec.c create mode 100644 xen/arch/ia64/linux/efi_stub.S create mode 100644 xen/arch/ia64/linux/extable.c create mode 100644 xen/arch/ia64/linux/flush.S create mode 100644 xen/arch/ia64/linux/hp/Makefile create mode 100644 xen/arch/ia64/linux/hp/zx1/Makefile create mode 100644 xen/arch/ia64/linux/hp/zx1/README.origin create mode 100644 xen/arch/ia64/linux/hp/zx1/hpzx1_machvec.c create mode 100644 xen/arch/ia64/linux/hpsim.S create mode 100644 xen/arch/ia64/linux/idiv32.S create mode 100644 xen/arch/ia64/linux/idiv64.S create mode 100644 xen/arch/ia64/linux/io.c create mode 100644 xen/arch/ia64/linux/irq_lsapic.c create mode 100644 xen/arch/ia64/linux/linuxextable.c create mode 100644 xen/arch/ia64/linux/machvec.c create mode 100644 xen/arch/ia64/linux/memcpy_mck.S create mode 100644 xen/arch/ia64/linux/memset.S create mode 100644 xen/arch/ia64/linux/numa.c create mode 100644 xen/arch/ia64/linux/pal.S create mode 100644 xen/arch/ia64/linux/pcdp.h create mode 100644 xen/arch/ia64/linux/sn/Makefile create mode 100644 xen/arch/ia64/linux/sn/kernel/Makefile create mode 100644 xen/arch/ia64/linux/sn/kernel/README.origin create mode 100644 xen/arch/ia64/linux/sn/kernel/machvec.c create mode 100644 xen/arch/ia64/linux/sn/kernel/pio_phys.S create mode 100644 xen/arch/ia64/linux/sn/kernel/ptc_deadlock.S create mode 100644 xen/arch/ia64/linux/sn/pci/Makefile create mode 100644 xen/arch/ia64/linux/sn/pci/pcibr/Makefile create mode 100644 xen/arch/ia64/linux/sn/pci/pcibr/README.origin create mode 100644 xen/arch/ia64/linux/sn/pci/pcibr/pcibr_reg.c create mode 100644 xen/arch/ia64/linux/strlen.S create mode 100644 xen/arch/ia64/tools/README.RunVT create mode 100644 xen/arch/ia64/tools/README.xenia64 create mode 100644 xen/arch/ia64/tools/README.xenoprof create mode 100644 xen/arch/ia64/tools/linux-xen-diffs create mode 100644 xen/arch/ia64/tools/p2m_expose/Makefile create mode 100644 xen/arch/ia64/tools/p2m_expose/README.p2m_expose create mode 100644 xen/arch/ia64/tools/p2m_expose/expose_p2m.c create mode 100644 xen/arch/ia64/tools/p2m_foreign/Makefile create mode 100644 xen/arch/ia64/tools/p2m_foreign/p2m_foreign.c create mode 100644 xen/arch/ia64/tools/privify/Makefile create mode 100644 xen/arch/ia64/tools/privify/README.privify create mode 100644 xen/arch/ia64/tools/privify/privify.c create mode 100644 xen/arch/ia64/tools/privify/privify.h create mode 100644 xen/arch/ia64/tools/privify/privify_elf64.c create mode 100644 xen/arch/ia64/tools/privop/Makefile create mode 100644 xen/arch/ia64/tools/privop/pohcalls.S create mode 100644 xen/arch/ia64/tools/privop/postat.c create mode 100755 xen/arch/ia64/tools/sparse-merge create mode 100755 xen/arch/ia64/tools/xelilo/elilo.README create mode 100755 xen/arch/ia64/tools/xelilo/xlilo.efi create mode 100644 xen/arch/ia64/vmx/Makefile create mode 100644 xen/arch/ia64/vmx/mmio.c create mode 100644 xen/arch/ia64/vmx/optvfault.S create mode 100644 xen/arch/ia64/vmx/pal_emul.c create mode 100644 xen/arch/ia64/vmx/save.c create mode 100644 xen/arch/ia64/vmx/sioemu.c create mode 100644 xen/arch/ia64/vmx/vacpi.c create mode 100644 xen/arch/ia64/vmx/viosapic.c create mode 100644 xen/arch/ia64/vmx/vlsapic.c create mode 100644 xen/arch/ia64/vmx/vmmu.c create mode 100644 xen/arch/ia64/vmx/vmx_entry.S create mode 100644 xen/arch/ia64/vmx/vmx_fault.c create mode 100644 xen/arch/ia64/vmx/vmx_hypercall.c create mode 100644 xen/arch/ia64/vmx/vmx_init.c create mode 100644 xen/arch/ia64/vmx/vmx_interrupt.c create mode 100644 xen/arch/ia64/vmx/vmx_ivt.S create mode 100644 xen/arch/ia64/vmx/vmx_minstate.h create mode 100644 xen/arch/ia64/vmx/vmx_phy_mode.c create mode 100644 xen/arch/ia64/vmx/vmx_support.c create mode 100644 xen/arch/ia64/vmx/vmx_utility.c create mode 100644 xen/arch/ia64/vmx/vmx_vcpu.c create mode 100644 xen/arch/ia64/vmx/vmx_vcpu_save.c create mode 100644 xen/arch/ia64/vmx/vmx_virt.c create mode 100644 xen/arch/ia64/vmx/vmx_vsa.S create mode 100644 xen/arch/ia64/vmx/vtlb.c create mode 100644 xen/arch/ia64/xen/Makefile create mode 100644 xen/arch/ia64/xen/cpufreq/Makefile create mode 100644 xen/arch/ia64/xen/cpufreq/cpufreq.c create mode 100644 xen/arch/ia64/xen/crash.c create mode 100644 xen/arch/ia64/xen/dom0_ops.c create mode 100644 xen/arch/ia64/xen/dom_fw_asm.S create mode 100644 xen/arch/ia64/xen/dom_fw_common.c create mode 100644 xen/arch/ia64/xen/dom_fw_dom0.c create mode 100644 xen/arch/ia64/xen/dom_fw_domu.c create mode 100644 xen/arch/ia64/xen/dom_fw_sn2.c create mode 100644 xen/arch/ia64/xen/dom_fw_utils.c create mode 100644 xen/arch/ia64/xen/domain.c create mode 100644 xen/arch/ia64/xen/faults.c create mode 100644 xen/arch/ia64/xen/flushd.S create mode 100644 xen/arch/ia64/xen/flushtlb.c create mode 100644 xen/arch/ia64/xen/fw_emul.c create mode 100644 xen/arch/ia64/xen/gdbstub.c create mode 100644 xen/arch/ia64/xen/hpsimserial.c create mode 100644 xen/arch/ia64/xen/hypercall.c create mode 100644 xen/arch/ia64/xen/hyperprivop.S create mode 100644 xen/arch/ia64/xen/idle0_task.c create mode 100644 xen/arch/ia64/xen/irq.c create mode 100644 xen/arch/ia64/xen/ivt.S create mode 100644 xen/arch/ia64/xen/machine_kexec.c create mode 100644 xen/arch/ia64/xen/mm.c create mode 100644 xen/arch/ia64/xen/mm_init.c create mode 100644 xen/arch/ia64/xen/oprofile/Makefile create mode 100644 xen/arch/ia64/xen/oprofile/perfmon.c create mode 100644 xen/arch/ia64/xen/oprofile/xenoprof.c create mode 100644 xen/arch/ia64/xen/pcdp.c create mode 100644 xen/arch/ia64/xen/pci.c create mode 100644 xen/arch/ia64/xen/platform_hypercall.c create mode 100644 xen/arch/ia64/xen/privop.c create mode 100644 xen/arch/ia64/xen/privop_stat.c create mode 100644 xen/arch/ia64/xen/regionreg.c create mode 100644 xen/arch/ia64/xen/relocate_kernel.S create mode 100644 xen/arch/ia64/xen/sn_console.c create mode 100644 xen/arch/ia64/xen/tlb_track.c create mode 100644 xen/arch/ia64/xen/vcpu.c create mode 100644 xen/arch/ia64/xen/vhpt.c create mode 100644 xen/arch/ia64/xen/xen.lds.S create mode 100644 xen/arch/ia64/xen/xenasm.S create mode 100644 xen/arch/ia64/xen/xenmem.c create mode 100644 xen/arch/ia64/xen/xenmisc.c create mode 100644 xen/arch/ia64/xen/xenpatch.c create mode 100644 xen/arch/ia64/xen/xensetup.c create mode 100644 xen/arch/ia64/xen/xentime.c create mode 100644 xen/arch/x86/Makefile create mode 100644 xen/arch/x86/Rules.mk create mode 100644 xen/arch/x86/acpi/Makefile create mode 100644 xen/arch/x86/acpi/boot.c create mode 100644 xen/arch/x86/acpi/cpu_idle.c create mode 100644 xen/arch/x86/acpi/cpufreq/Makefile create mode 100644 xen/arch/x86/acpi/cpufreq/cpufreq.c create mode 100644 xen/arch/x86/acpi/cpufreq/powernow.c create mode 100644 xen/arch/x86/acpi/cpuidle_menu.c create mode 100644 xen/arch/x86/acpi/power.c create mode 100644 xen/arch/x86/acpi/suspend.c create mode 100644 xen/arch/x86/acpi/wakeup_prot.S create mode 100644 xen/arch/x86/apic.c create mode 100644 xen/arch/x86/bitops.c create mode 100644 xen/arch/x86/boot/Makefile create mode 100644 xen/arch/x86/boot/cmdline.S create mode 100644 xen/arch/x86/boot/edd.S create mode 100644 xen/arch/x86/boot/head.S create mode 100644 xen/arch/x86/boot/mem.S create mode 100644 xen/arch/x86/boot/mkelf32.c create mode 100644 xen/arch/x86/boot/trampoline.S create mode 100644 xen/arch/x86/boot/video.S create mode 100644 xen/arch/x86/boot/video.h create mode 100644 xen/arch/x86/boot/wakeup.S create mode 100644 xen/arch/x86/boot/x86_32.S create mode 100644 xen/arch/x86/boot/x86_64.S create mode 100644 xen/arch/x86/clear_page.S create mode 100644 xen/arch/x86/compat.c create mode 100644 xen/arch/x86/cpu/Makefile create mode 100644 xen/arch/x86/cpu/amd.c create mode 100644 xen/arch/x86/cpu/amd.h create mode 100644 xen/arch/x86/cpu/centaur.c create mode 100644 xen/arch/x86/cpu/common.c create mode 100644 xen/arch/x86/cpu/cpu.h create mode 100644 xen/arch/x86/cpu/cyrix.c create mode 100644 xen/arch/x86/cpu/intel.c create mode 100644 xen/arch/x86/cpu/intel_cacheinfo.c create mode 100644 xen/arch/x86/cpu/mcheck/Makefile create mode 100644 xen/arch/x86/cpu/mcheck/amd_f10.c create mode 100644 xen/arch/x86/cpu/mcheck/amd_k8.c create mode 100644 xen/arch/x86/cpu/mcheck/amd_nonfatal.c create mode 100644 xen/arch/x86/cpu/mcheck/k7.c create mode 100644 xen/arch/x86/cpu/mcheck/mce.c create mode 100644 xen/arch/x86/cpu/mcheck/mce.h create mode 100644 xen/arch/x86/cpu/mcheck/non-fatal.c create mode 100644 xen/arch/x86/cpu/mcheck/p4.c create mode 100644 xen/arch/x86/cpu/mcheck/p5.c create mode 100644 xen/arch/x86/cpu/mcheck/p6.c create mode 100644 xen/arch/x86/cpu/mcheck/winchip.c create mode 100644 xen/arch/x86/cpu/mcheck/x86_mca.h create mode 100644 xen/arch/x86/cpu/mtrr/Makefile create mode 100644 xen/arch/x86/cpu/mtrr/amd.c create mode 100644 xen/arch/x86/cpu/mtrr/cyrix.c create mode 100644 xen/arch/x86/cpu/mtrr/generic.c create mode 100644 xen/arch/x86/cpu/mtrr/main.c create mode 100644 xen/arch/x86/cpu/mtrr/mtrr.h create mode 100644 xen/arch/x86/cpu/mtrr/state.c create mode 100644 xen/arch/x86/cpu/transmeta.c create mode 100644 xen/arch/x86/crash.c create mode 100644 xen/arch/x86/delay.c create mode 100644 xen/arch/x86/dmi_scan.c create mode 100644 xen/arch/x86/domain.c create mode 100644 xen/arch/x86/domain_build.c create mode 100644 xen/arch/x86/domctl.c create mode 100644 xen/arch/x86/e820.c create mode 100644 xen/arch/x86/extable.c create mode 100644 xen/arch/x86/flushtlb.c create mode 100644 xen/arch/x86/gdbstub.c create mode 100644 xen/arch/x86/genapic/Makefile create mode 100644 xen/arch/x86/genapic/bigsmp.c create mode 100644 xen/arch/x86/genapic/default.c create mode 100644 xen/arch/x86/genapic/delivery.c create mode 100644 xen/arch/x86/genapic/probe.c create mode 100644 xen/arch/x86/genapic/summit.c create mode 100644 xen/arch/x86/genapic/x2apic.c create mode 100644 xen/arch/x86/hpet.c create mode 100644 xen/arch/x86/hvm/Makefile create mode 100644 xen/arch/x86/hvm/emulate.c create mode 100644 xen/arch/x86/hvm/hpet.c create mode 100644 xen/arch/x86/hvm/hvm.c create mode 100644 xen/arch/x86/hvm/i8254.c create mode 100644 xen/arch/x86/hvm/intercept.c create mode 100644 xen/arch/x86/hvm/io.c create mode 100644 xen/arch/x86/hvm/irq.c create mode 100644 xen/arch/x86/hvm/mtrr.c create mode 100644 xen/arch/x86/hvm/pmtimer.c create mode 100644 xen/arch/x86/hvm/rtc.c create mode 100644 xen/arch/x86/hvm/save.c create mode 100644 xen/arch/x86/hvm/stdvga.c create mode 100644 xen/arch/x86/hvm/svm/Makefile create mode 100644 xen/arch/x86/hvm/svm/asid.c create mode 100644 xen/arch/x86/hvm/svm/emulate.c create mode 100644 xen/arch/x86/hvm/svm/entry.S create mode 100644 xen/arch/x86/hvm/svm/intr.c create mode 100644 xen/arch/x86/hvm/svm/svm.c create mode 100644 xen/arch/x86/hvm/svm/vmcb.c create mode 100644 xen/arch/x86/hvm/vioapic.c create mode 100644 xen/arch/x86/hvm/viridian.c create mode 100644 xen/arch/x86/hvm/vlapic.c create mode 100644 xen/arch/x86/hvm/vmsi.c create mode 100644 xen/arch/x86/hvm/vmx/Makefile create mode 100644 xen/arch/x86/hvm/vmx/entry.S create mode 100644 xen/arch/x86/hvm/vmx/intr.c create mode 100644 xen/arch/x86/hvm/vmx/realmode.c create mode 100644 xen/arch/x86/hvm/vmx/vmcs.c create mode 100644 xen/arch/x86/hvm/vmx/vmx.c create mode 100644 xen/arch/x86/hvm/vmx/vpmu.c create mode 100644 xen/arch/x86/hvm/vmx/vpmu_core2.c create mode 100644 xen/arch/x86/hvm/vpic.c create mode 100644 xen/arch/x86/hvm/vpt.c create mode 100644 xen/arch/x86/i387.c create mode 100644 xen/arch/x86/i8259.c create mode 100644 xen/arch/x86/io_apic.c create mode 100644 xen/arch/x86/ioport_emulate.c create mode 100644 xen/arch/x86/irq.c create mode 100644 xen/arch/x86/machine_kexec.c create mode 100644 xen/arch/x86/microcode.c create mode 100644 xen/arch/x86/microcode_amd.c create mode 100644 xen/arch/x86/microcode_intel.c create mode 100644 xen/arch/x86/mm.c create mode 100644 xen/arch/x86/mm/Makefile create mode 100644 xen/arch/x86/mm/hap/Makefile create mode 100644 xen/arch/x86/mm/hap/guest_walk.c create mode 100644 xen/arch/x86/mm/hap/hap.c create mode 100644 xen/arch/x86/mm/hap/p2m-ept.c create mode 100644 xen/arch/x86/mm/hap/private.h create mode 100644 xen/arch/x86/mm/p2m.c create mode 100644 xen/arch/x86/mm/page-guest32.h create mode 100644 xen/arch/x86/mm/paging.c create mode 100644 xen/arch/x86/mm/shadow/Makefile create mode 100644 xen/arch/x86/mm/shadow/common.c create mode 100644 xen/arch/x86/mm/shadow/multi.c create mode 100644 xen/arch/x86/mm/shadow/multi.h create mode 100644 xen/arch/x86/mm/shadow/private.h create mode 100644 xen/arch/x86/mm/shadow/types.h create mode 100644 xen/arch/x86/mpparse.c create mode 100644 xen/arch/x86/msi.c create mode 100644 xen/arch/x86/nmi.c create mode 100644 xen/arch/x86/numa.c create mode 100644 xen/arch/x86/oprofile/Makefile create mode 100644 xen/arch/x86/oprofile/backtrace.c create mode 100644 xen/arch/x86/oprofile/nmi_int.c create mode 100644 xen/arch/x86/oprofile/op_counter.h create mode 100644 xen/arch/x86/oprofile/op_model_athlon.c create mode 100644 xen/arch/x86/oprofile/op_model_p4.c create mode 100644 xen/arch/x86/oprofile/op_model_ppro.c create mode 100644 xen/arch/x86/oprofile/op_x86_model.h create mode 100644 xen/arch/x86/oprofile/xenoprof.c create mode 100644 xen/arch/x86/pci.c create mode 100644 xen/arch/x86/physdev.c create mode 100644 xen/arch/x86/platform_hypercall.c create mode 100644 xen/arch/x86/rwlock.c create mode 100644 xen/arch/x86/setup.c create mode 100644 xen/arch/x86/shutdown.c create mode 100644 xen/arch/x86/smp.c create mode 100644 xen/arch/x86/smpboot.c create mode 100644 xen/arch/x86/srat.c create mode 100644 xen/arch/x86/string.c create mode 100644 xen/arch/x86/sysctl.c create mode 100644 xen/arch/x86/tboot.c create mode 100644 xen/arch/x86/time.c create mode 100644 xen/arch/x86/trace.c create mode 100644 xen/arch/x86/traps.c create mode 100644 xen/arch/x86/usercopy.c create mode 100644 xen/arch/x86/x86_32/Makefile create mode 100644 xen/arch/x86/x86_32/asm-offsets.c create mode 100644 xen/arch/x86/x86_32/domain_page.c create mode 100644 xen/arch/x86/x86_32/entry.S create mode 100644 xen/arch/x86/x86_32/gdbstub.c create mode 100644 xen/arch/x86/x86_32/gpr_switch.S create mode 100644 xen/arch/x86/x86_32/machine_kexec.c create mode 100644 xen/arch/x86/x86_32/mm.c create mode 100644 xen/arch/x86/x86_32/seg_fixup.c create mode 100644 xen/arch/x86/x86_32/supervisor_mode_kernel.S create mode 100644 xen/arch/x86/x86_32/traps.c create mode 100644 xen/arch/x86/x86_32/xen.lds.S create mode 100644 xen/arch/x86/x86_64/Makefile create mode 100644 xen/arch/x86/x86_64/asm-offsets.c create mode 100644 xen/arch/x86/x86_64/compat.c create mode 100644 xen/arch/x86/x86_64/compat/Makefile create mode 100644 xen/arch/x86/x86_64/compat/entry.S create mode 100644 xen/arch/x86/x86_64/compat/mm.c create mode 100644 xen/arch/x86/x86_64/compat/traps.c create mode 100644 xen/arch/x86/x86_64/compat_kexec.S create mode 100644 xen/arch/x86/x86_64/cpu_idle.c create mode 100644 xen/arch/x86/x86_64/domain.c create mode 100644 xen/arch/x86/x86_64/entry.S create mode 100644 xen/arch/x86/x86_64/gdbstub.c create mode 100644 xen/arch/x86/x86_64/gpr_switch.S create mode 100644 xen/arch/x86/x86_64/machine_kexec.c create mode 100644 xen/arch/x86/x86_64/mm.c create mode 100644 xen/arch/x86/x86_64/physdev.c create mode 100644 xen/arch/x86/x86_64/platform_hypercall.c create mode 100644 xen/arch/x86/x86_64/traps.c create mode 100644 xen/arch/x86/x86_64/xen.lds.S create mode 100644 xen/arch/x86/x86_emulate.c create mode 100644 xen/arch/x86/x86_emulate/x86_emulate.c create mode 100644 xen/arch/x86/x86_emulate/x86_emulate.h create mode 100644 xen/common/Makefile create mode 100644 xen/common/bitmap.c create mode 100644 xen/common/compat/Makefile create mode 100644 xen/common/compat/domain.c create mode 100644 xen/common/compat/grant_table.c create mode 100644 xen/common/compat/kernel.c create mode 100644 xen/common/compat/memory.c create mode 100644 xen/common/compat/multicall.c create mode 100644 xen/common/compat/schedule.c create mode 100644 xen/common/compat/xenoprof.c create mode 100644 xen/common/compat/xlat.c create mode 100644 xen/common/domain.c create mode 100644 xen/common/domctl.c create mode 100644 xen/common/event_channel.c create mode 100644 xen/common/gdbstub.c create mode 100644 xen/common/grant_table.c create mode 100644 xen/common/hvm/Makefile create mode 100644 xen/common/hvm/save.c create mode 100644 xen/common/kernel.c create mode 100644 xen/common/kexec.c create mode 100644 xen/common/keyhandler.c create mode 100644 xen/common/lib.c create mode 100644 xen/common/libelf/Makefile create mode 100644 xen/common/libelf/README create mode 100644 xen/common/libelf/libelf-dominfo.c create mode 100644 xen/common/libelf/libelf-loader.c create mode 100644 xen/common/libelf/libelf-private.h create mode 100644 xen/common/libelf/libelf-relocate.c create mode 100644 xen/common/libelf/libelf-tools.c create mode 100644 xen/common/memory.c create mode 100644 xen/common/multicall.c create mode 100644 xen/common/page_alloc.c create mode 100644 xen/common/perfc.c create mode 100644 xen/common/rangeset.c create mode 100644 xen/common/rcupdate.c create mode 100644 xen/common/sched_credit.c create mode 100644 xen/common/sched_sedf.c create mode 100644 xen/common/schedule.c create mode 100644 xen/common/shutdown.c create mode 100644 xen/common/softirq.c create mode 100644 xen/common/stop_machine.c create mode 100644 xen/common/string.c create mode 100644 xen/common/symbols-dummy.c create mode 100644 xen/common/symbols.c create mode 100644 xen/common/sysctl.c create mode 100644 xen/common/time.c create mode 100644 xen/common/timer.c create mode 100644 xen/common/trace.c create mode 100644 xen/common/version.c create mode 100644 xen/common/vsprintf.c create mode 100644 xen/common/xencomm.c create mode 100644 xen/common/xenoprof.c create mode 100644 xen/common/xmalloc.c create mode 100644 xen/common/xmalloc_tlsf.c create mode 100644 xen/drivers/Makefile create mode 100644 xen/drivers/acpi/Makefile create mode 100644 xen/drivers/acpi/hwregs.c create mode 100644 xen/drivers/acpi/numa.c create mode 100644 xen/drivers/acpi/osl.c create mode 100644 xen/drivers/acpi/pmstat.c create mode 100644 xen/drivers/acpi/reboot.c create mode 100644 xen/drivers/acpi/tables.c create mode 100644 xen/drivers/acpi/tables/Makefile create mode 100644 xen/drivers/acpi/tables/tbfadt.c create mode 100644 xen/drivers/acpi/tables/tbinstal.c create mode 100644 xen/drivers/acpi/tables/tbutils.c create mode 100644 xen/drivers/acpi/tables/tbxface.c create mode 100644 xen/drivers/acpi/tables/tbxfroot.c create mode 100644 xen/drivers/acpi/utilities/Makefile create mode 100644 xen/drivers/acpi/utilities/utglobal.c create mode 100644 xen/drivers/acpi/utilities/utmisc.c create mode 100644 xen/drivers/char/Makefile create mode 100644 xen/drivers/char/console.c create mode 100644 xen/drivers/char/ns16550.c create mode 100644 xen/drivers/char/serial.c create mode 100644 xen/drivers/cpufreq/Makefile create mode 100644 xen/drivers/cpufreq/cpufreq.c create mode 100644 xen/drivers/cpufreq/cpufreq_ondemand.c create mode 100644 xen/drivers/cpufreq/utility.c create mode 100644 xen/drivers/passthrough/Makefile create mode 100644 xen/drivers/passthrough/amd/Makefile create mode 100644 xen/drivers/passthrough/amd/iommu_acpi.c create mode 100644 xen/drivers/passthrough/amd/iommu_detect.c create mode 100644 xen/drivers/passthrough/amd/iommu_init.c create mode 100644 xen/drivers/passthrough/amd/iommu_intr.c create mode 100644 xen/drivers/passthrough/amd/iommu_map.c create mode 100644 xen/drivers/passthrough/amd/pci_amd_iommu.c create mode 100644 xen/drivers/passthrough/io.c create mode 100644 xen/drivers/passthrough/iommu.c create mode 100644 xen/drivers/passthrough/pci.c create mode 100644 xen/drivers/passthrough/vtd/Makefile create mode 100644 xen/drivers/passthrough/vtd/dmar.c create mode 100644 xen/drivers/passthrough/vtd/dmar.h create mode 100644 xen/drivers/passthrough/vtd/extern.h create mode 100644 xen/drivers/passthrough/vtd/intremap.c create mode 100644 xen/drivers/passthrough/vtd/iommu.c create mode 100644 xen/drivers/passthrough/vtd/iommu.h create mode 100644 xen/drivers/passthrough/vtd/qinval.c create mode 100644 xen/drivers/passthrough/vtd/utils.c create mode 100644 xen/drivers/passthrough/vtd/vtd.h create mode 100644 xen/drivers/passthrough/vtd/x86/Makefile create mode 100644 xen/drivers/passthrough/vtd/x86/vtd.c create mode 100644 xen/drivers/pci/Makefile create mode 100644 xen/drivers/pci/pci.c create mode 100644 xen/drivers/video/Makefile create mode 100644 xen/drivers/video/font.h create mode 100644 xen/drivers/video/font_8x14.c create mode 100644 xen/drivers/video/font_8x16.c create mode 100644 xen/drivers/video/font_8x8.c create mode 100644 xen/drivers/video/vesa.c create mode 100644 xen/drivers/video/vga.c create mode 100644 xen/include/Makefile create mode 100644 xen/include/acpi/acconfig.h create mode 100644 xen/include/acpi/acexcep.h create mode 100644 xen/include/acpi/acglobal.h create mode 100644 xen/include/acpi/achware.h create mode 100644 xen/include/acpi/aclocal.h create mode 100644 xen/include/acpi/acmacros.h create mode 100644 xen/include/acpi/acnames.h create mode 100644 xen/include/acpi/acnamesp.h create mode 100644 xen/include/acpi/acobject.h create mode 100644 xen/include/acpi/acoutput.h create mode 100644 xen/include/acpi/acpi.h create mode 100644 xen/include/acpi/acpi_bus.h create mode 100644 xen/include/acpi/acpi_drivers.h create mode 100644 xen/include/acpi/acpiosxf.h create mode 100644 xen/include/acpi/acpixf.h create mode 100644 xen/include/acpi/acstruct.h create mode 100644 xen/include/acpi/actables.h create mode 100644 xen/include/acpi/actbl.h create mode 100644 xen/include/acpi/actbl1.h create mode 100644 xen/include/acpi/actypes.h create mode 100644 xen/include/acpi/acutils.h create mode 100644 xen/include/acpi/cpufreq/cpufreq.h create mode 100644 xen/include/acpi/cpufreq/processor_perf.h create mode 100644 xen/include/acpi/pdc_intel.h create mode 100644 xen/include/acpi/platform/acenv.h create mode 100644 xen/include/acpi/platform/acgcc.h create mode 100644 xen/include/acpi/platform/aclinux.h create mode 100644 xen/include/asm-ia64/bug.h create mode 100644 xen/include/asm-ia64/bundle.h create mode 100644 xen/include/asm-ia64/config.h create mode 100644 xen/include/asm-ia64/debugger.h create mode 100644 xen/include/asm-ia64/dom_fw.h create mode 100644 xen/include/asm-ia64/dom_fw_common.h create mode 100644 xen/include/asm-ia64/dom_fw_dom0.h create mode 100644 xen/include/asm-ia64/dom_fw_domu.h create mode 100644 xen/include/asm-ia64/dom_fw_utils.h create mode 100644 xen/include/asm-ia64/domain.h create mode 100644 xen/include/asm-ia64/elf.h create mode 100644 xen/include/asm-ia64/event.h create mode 100644 xen/include/asm-ia64/flushtlb.h create mode 100644 xen/include/asm-ia64/grant_table.h create mode 100644 xen/include/asm-ia64/guest_access.h create mode 100644 xen/include/asm-ia64/hardirq.h create mode 100644 xen/include/asm-ia64/hvm/support.h create mode 100644 xen/include/asm-ia64/hvm/vacpi.h create mode 100644 xen/include/asm-ia64/hvm/vlapic.h create mode 100644 xen/include/asm-ia64/hypercall.h create mode 100644 xen/include/asm-ia64/ia64_int.h create mode 100644 xen/include/asm-ia64/init.h create mode 100644 xen/include/asm-ia64/iocap.h create mode 100644 xen/include/asm-ia64/kexec.h create mode 100644 xen/include/asm-ia64/linux-null/README.origin create mode 100644 xen/include/asm-ia64/linux-null/asm-generic/pci-dma-compat.h create mode 100644 xen/include/asm-ia64/linux-null/asm/cyclone.h create mode 100644 xen/include/asm-ia64/linux-null/asm/desc.h create mode 100644 xen/include/asm-ia64/linux-null/asm/ia32.h create mode 100644 xen/include/asm-ia64/linux-null/asm/mman.h create mode 100644 xen/include/asm-ia64/linux-null/asm/mmzone.h create mode 100644 xen/include/asm-ia64/linux-null/asm/module.h create mode 100644 xen/include/asm-ia64/linux-null/asm/nmi.h create mode 100644 xen/include/asm-ia64/linux-null/asm/pdb.h create mode 100644 xen/include/asm-ia64/linux-null/asm/ptrace_offsets.h create mode 100644 xen/include/asm-ia64/linux-null/asm/scatterlist.h create mode 100644 xen/include/asm-ia64/linux-null/asm/semaphore.h create mode 100644 xen/include/asm-ia64/linux-null/asm/serial.h create mode 100644 xen/include/asm-ia64/linux-null/asm/signal.h create mode 100644 xen/include/asm-ia64/linux-null/asm/sn/arch.h create mode 100644 xen/include/asm-ia64/linux-null/asm/sn/geo.h create mode 100644 xen/include/asm-ia64/linux-null/asm/sn/nodepda.h create mode 100644 xen/include/asm-ia64/linux-null/asm/sn/sn_cpuid.h create mode 100644 xen/include/asm-ia64/linux-null/asm/ustack.h create mode 100644 xen/include/asm-ia64/linux-null/asm/xen/hypervisor.h create mode 100644 xen/include/asm-ia64/linux-null/linux/bootmem.h create mode 100644 xen/include/asm-ia64/linux-null/linux/capability.h create mode 100644 xen/include/asm-ia64/linux-null/linux/completion.h create mode 100644 xen/include/asm-ia64/linux-null/linux/device.h create mode 100644 xen/include/asm-ia64/linux-null/linux/dmapool.h create mode 100644 xen/include/asm-ia64/linux-null/linux/file.h create mode 100644 xen/include/asm-ia64/linux-null/linux/kallsyms.h create mode 100644 xen/include/asm-ia64/linux-null/linux/kernel_stat.h create mode 100644 xen/include/asm-ia64/linux-null/linux/mmzone.h create mode 100644 xen/include/asm-ia64/linux-null/linux/module.h create mode 100644 xen/include/asm-ia64/linux-null/linux/mount.h create mode 100644 xen/include/asm-ia64/linux-null/linux/node.h create mode 100644 xen/include/asm-ia64/linux-null/linux/page-flags.h create mode 100644 xen/include/asm-ia64/linux-null/linux/pagemap.h create mode 100644 xen/include/asm-ia64/linux-null/linux/platform.h create mode 100644 xen/include/asm-ia64/linux-null/linux/pm.h create mode 100644 xen/include/asm-ia64/linux-null/linux/poll.h create mode 100644 xen/include/asm-ia64/linux-null/linux/proc_fs.h create mode 100644 xen/include/asm-ia64/linux-null/linux/profile.h create mode 100644 xen/include/asm-ia64/linux-null/linux/ptrace.h create mode 100644 xen/include/asm-ia64/linux-null/linux/random.h create mode 100644 xen/include/asm-ia64/linux-null/linux/rcupdate.h create mode 100644 xen/include/asm-ia64/linux-null/linux/rtc.h create mode 100644 xen/include/asm-ia64/linux-null/linux/rwsem.h create mode 100644 xen/include/asm-ia64/linux-null/linux/seq_file.h create mode 100644 xen/include/asm-ia64/linux-null/linux/serial.h create mode 100644 xen/include/asm-ia64/linux-null/linux/serial_core.h create mode 100644 xen/include/asm-ia64/linux-null/linux/signal.h create mode 100644 xen/include/asm-ia64/linux-null/linux/slab.h create mode 100644 xen/include/asm-ia64/linux-null/linux/smp_lock.h create mode 100644 xen/include/asm-ia64/linux-null/linux/swap.h create mode 100644 xen/include/asm-ia64/linux-null/linux/sysctl.h create mode 100644 xen/include/asm-ia64/linux-null/linux/threads.h create mode 100644 xen/include/asm-ia64/linux-null/linux/tty.h create mode 100644 xen/include/asm-ia64/linux-null/linux/vfs.h create mode 100644 xen/include/asm-ia64/linux-null/linux/vmalloc.h create mode 100644 xen/include/asm-ia64/linux-null/linux/workqueue.h create mode 100644 xen/include/asm-ia64/linux-xen/asm-generic/README.origin create mode 100644 xen/include/asm-ia64/linux-xen/asm-generic/pgtable-nopud.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/README.origin create mode 100644 xen/include/asm-ia64/linux-xen/asm/acpi.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/atomic.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/cache.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/gcc_intrin.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/ia64regs.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/io.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/iosapic.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/kregs.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/machvec.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/machvec_dig.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/machvec_hpzx1.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/machvec_sn2.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/mca_asm.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/meminit.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/numa.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/page.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/pal.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/pci.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/percpu.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/perfmon.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/perfmon_default_smpl.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/pgalloc.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/pgtable.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/processor.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/ptrace.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sal.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/smp.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/README.origin create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/addrs.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/arch.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/hubdev.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/intr.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/io.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/nodepda.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/pcibr_provider.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/pcidev.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/rw_mmr.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/sn/types.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/spinlock.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/system.h create mode 100644 xen/include/asm-ia64/linux-xen/asm/types.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/README.origin create mode 100644 xen/include/asm-ia64/linux-xen/linux/cpu.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/device.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/efi.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/gfp.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/hardirq.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/interrupt.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/kobject.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/linux-pci.h create mode 100644 xen/include/asm-ia64/linux-xen/linux/oprofile.h create mode 100644 xen/include/asm-ia64/linux/README.origin create mode 100644 xen/include/asm-ia64/linux/asm-generic/README.origin create mode 100644 xen/include/asm-ia64/linux/asm-generic/div64.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/ide_iops.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/iomap.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/pci.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/pgtable.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/sections.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/topology.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/unaligned.h create mode 100644 xen/include/asm-ia64/linux/asm-generic/vmlinux.lds.h create mode 100644 xen/include/asm-ia64/linux/asm/README.origin create mode 100644 xen/include/asm-ia64/linux/asm/asmmacro.h create mode 100644 xen/include/asm-ia64/linux/asm/bitops.h create mode 100644 xen/include/asm-ia64/linux/asm/break.h create mode 100644 xen/include/asm-ia64/linux/asm/byteorder.h create mode 100644 xen/include/asm-ia64/linux/asm/cacheflush.h create mode 100644 xen/include/asm-ia64/linux/asm/checksum.h create mode 100644 xen/include/asm-ia64/linux/asm/current.h create mode 100644 xen/include/asm-ia64/linux/asm/delay.h create mode 100644 xen/include/asm-ia64/linux/asm/div64.h create mode 100644 xen/include/asm-ia64/linux/asm/dma.h create mode 100644 xen/include/asm-ia64/linux/asm/fpswa.h create mode 100644 xen/include/asm-ia64/linux/asm/fpu.h create mode 100644 xen/include/asm-ia64/linux/asm/hdreg.h create mode 100644 xen/include/asm-ia64/linux/asm/hw_irq.h create mode 100644 xen/include/asm-ia64/linux/asm/intrinsics.h create mode 100644 xen/include/asm-ia64/linux/asm/ioctl.h create mode 100644 xen/include/asm-ia64/linux/asm/irq.h create mode 100644 xen/include/asm-ia64/linux/asm/linkage.h create mode 100644 xen/include/asm-ia64/linux/asm/machvec_hpsim.h create mode 100644 xen/include/asm-ia64/linux/asm/machvec_init.h create mode 100644 xen/include/asm-ia64/linux/asm/mca.h create mode 100644 xen/include/asm-ia64/linux/asm/nodedata.h create mode 100644 xen/include/asm-ia64/linux/asm/numnodes.h create mode 100644 xen/include/asm-ia64/linux/asm/param.h create mode 100644 xen/include/asm-ia64/linux/asm/patch.h create mode 100644 xen/include/asm-ia64/linux/asm/rse.h create mode 100644 xen/include/asm-ia64/linux/asm/sections.h create mode 100644 xen/include/asm-ia64/linux/asm/setup.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/README.origin create mode 100644 xen/include/asm-ia64/linux/asm/sn/geo.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/klconfig.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/l1.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/leds.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/module.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/pcibus_provider_defs.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/pda.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/pic.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/shub_mmr.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/shubio.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/simulator.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/sn_cpuid.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/sn_feature_sets.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/sn_sal.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/tiocp.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/xbow.h create mode 100644 xen/include/asm-ia64/linux/asm/sn/xwidgetdev.h create mode 100644 xen/include/asm-ia64/linux/asm/string.h create mode 100644 xen/include/asm-ia64/linux/asm/thread_info.h create mode 100644 xen/include/asm-ia64/linux/asm/timex.h create mode 100644 xen/include/asm-ia64/linux/asm/topology.h create mode 100644 xen/include/asm-ia64/linux/asm/unaligned.h create mode 100644 xen/include/asm-ia64/linux/asm/unistd.h create mode 100644 xen/include/asm-ia64/linux/asm/unwind.h create mode 100644 xen/include/asm-ia64/linux/bcd.h create mode 100644 xen/include/asm-ia64/linux/bitmap.h create mode 100644 xen/include/asm-ia64/linux/bitops.h create mode 100644 xen/include/asm-ia64/linux/byteorder/README.origin create mode 100644 xen/include/asm-ia64/linux/byteorder/generic.h create mode 100644 xen/include/asm-ia64/linux/byteorder/little_endian.h create mode 100644 xen/include/asm-ia64/linux/byteorder/swab.h create mode 100644 xen/include/asm-ia64/linux/completion.h create mode 100644 xen/include/asm-ia64/linux/hash.h create mode 100644 xen/include/asm-ia64/linux/initrd.h create mode 100644 xen/include/asm-ia64/linux/ioport.h create mode 100644 xen/include/asm-ia64/linux/jiffies.h create mode 100644 xen/include/asm-ia64/linux/klist.h create mode 100644 xen/include/asm-ia64/linux/kmalloc_sizes.h create mode 100644 xen/include/asm-ia64/linux/kref.h create mode 100644 xen/include/asm-ia64/linux/linkage.h create mode 100644 xen/include/asm-ia64/linux/mod_devicetable.h create mode 100644 xen/include/asm-ia64/linux/notifier.h create mode 100644 xen/include/asm-ia64/linux/pci_ids.h create mode 100644 xen/include/asm-ia64/linux/pci_regs.h create mode 100644 xen/include/asm-ia64/linux/percpu.h create mode 100644 xen/include/asm-ia64/linux/pm.h create mode 100644 xen/include/asm-ia64/linux/preempt.h create mode 100644 xen/include/asm-ia64/linux/seqlock.h create mode 100644 xen/include/asm-ia64/linux/sort.h create mode 100644 xen/include/asm-ia64/linux/stddef.h create mode 100644 xen/include/asm-ia64/linux/sysfs.h create mode 100644 xen/include/asm-ia64/linux/thread_info.h create mode 100644 xen/include/asm-ia64/linux/time.h create mode 100644 xen/include/asm-ia64/linux/timex.h create mode 100644 xen/include/asm-ia64/linux/topology.h create mode 100644 xen/include/asm-ia64/linux/wait.h create mode 100644 xen/include/asm-ia64/mm.h create mode 100644 xen/include/asm-ia64/mmu_context.h create mode 100644 xen/include/asm-ia64/multicall.h create mode 100644 xen/include/asm-ia64/offsets.h create mode 100644 xen/include/asm-ia64/p2m_entry.h create mode 100644 xen/include/asm-ia64/perfc.h create mode 100644 xen/include/asm-ia64/perfc_defn.h create mode 100644 xen/include/asm-ia64/privop.h create mode 100644 xen/include/asm-ia64/privop_stat.h create mode 100644 xen/include/asm-ia64/regionreg.h create mode 100644 xen/include/asm-ia64/regs.h create mode 100644 xen/include/asm-ia64/shadow.h create mode 100644 xen/include/asm-ia64/shared.h create mode 100644 xen/include/asm-ia64/sioemu.h create mode 100644 xen/include/asm-ia64/slab.h create mode 100644 xen/include/asm-ia64/softirq.h create mode 100644 xen/include/asm-ia64/time.h create mode 100644 xen/include/asm-ia64/tlb.h create mode 100644 xen/include/asm-ia64/tlb_track.h create mode 100644 xen/include/asm-ia64/tlbflush.h create mode 100644 xen/include/asm-ia64/trace.h create mode 100644 xen/include/asm-ia64/uaccess.h create mode 100644 xen/include/asm-ia64/vcpu.h create mode 100644 xen/include/asm-ia64/vcpumask.h create mode 100644 xen/include/asm-ia64/vhpt.h create mode 100644 xen/include/asm-ia64/viosapic.h create mode 100644 xen/include/asm-ia64/virt_event.h create mode 100644 xen/include/asm-ia64/vlsapic.h create mode 100644 xen/include/asm-ia64/vmmu.h create mode 100644 xen/include/asm-ia64/vmx.h create mode 100644 xen/include/asm-ia64/vmx_mm_def.h create mode 100644 xen/include/asm-ia64/vmx_pal.h create mode 100644 xen/include/asm-ia64/vmx_pal_vsa.h create mode 100644 xen/include/asm-ia64/vmx_phy_mode.h create mode 100644 xen/include/asm-ia64/vmx_platform.h create mode 100644 xen/include/asm-ia64/vmx_vcpu.h create mode 100644 xen/include/asm-ia64/vmx_vcpu_save.h create mode 100644 xen/include/asm-ia64/vmx_vpd.h create mode 100644 xen/include/asm-ia64/vtm.h create mode 100644 xen/include/asm-ia64/xengcc_intrin.h create mode 100644 xen/include/asm-ia64/xenia64regs.h create mode 100644 xen/include/asm-ia64/xenkregs.h create mode 100644 xen/include/asm-ia64/xenmca.h create mode 100644 xen/include/asm-ia64/xenoprof.h create mode 100644 xen/include/asm-ia64/xenpage.h create mode 100644 xen/include/asm-ia64/xenprocessor.h create mode 100644 xen/include/asm-ia64/xenspinlock.h create mode 100644 xen/include/asm-ia64/xensystem.h create mode 100644 xen/include/asm-ia64/xentypes.h create mode 100644 xen/include/asm-x86/acpi.h create mode 100644 xen/include/asm-x86/amd-iommu.h create mode 100644 xen/include/asm-x86/apic.h create mode 100644 xen/include/asm-x86/apicdef.h create mode 100644 xen/include/asm-x86/asm_defns.h create mode 100644 xen/include/asm-x86/atomic.h create mode 100644 xen/include/asm-x86/bitops.h create mode 100644 xen/include/asm-x86/bug.h create mode 100644 xen/include/asm-x86/byteorder.h create mode 100644 xen/include/asm-x86/cache.h create mode 100644 xen/include/asm-x86/compat.h create mode 100644 xen/include/asm-x86/config.h create mode 100644 xen/include/asm-x86/cpufeature.h create mode 100644 xen/include/asm-x86/current.h create mode 100644 xen/include/asm-x86/debugger.h create mode 100644 xen/include/asm-x86/debugreg.h create mode 100644 xen/include/asm-x86/delay.h create mode 100644 xen/include/asm-x86/desc.h create mode 100644 xen/include/asm-x86/div64.h create mode 100644 xen/include/asm-x86/domain.h create mode 100644 xen/include/asm-x86/e820.h create mode 100644 xen/include/asm-x86/edd.h create mode 100644 xen/include/asm-x86/elf.h create mode 100644 xen/include/asm-x86/event.h create mode 100644 xen/include/asm-x86/fixmap.h create mode 100644 xen/include/asm-x86/flushtlb.h create mode 100644 xen/include/asm-x86/genapic.h create mode 100644 xen/include/asm-x86/grant_table.h create mode 100644 xen/include/asm-x86/guest_access.h create mode 100644 xen/include/asm-x86/hap.h create mode 100644 xen/include/asm-x86/hardirq.h create mode 100644 xen/include/asm-x86/hpet.h create mode 100644 xen/include/asm-x86/hvm/cacheattr.h create mode 100644 xen/include/asm-x86/hvm/domain.h create mode 100644 xen/include/asm-x86/hvm/emulate.h create mode 100644 xen/include/asm-x86/hvm/guest_access.h create mode 100644 xen/include/asm-x86/hvm/hvm.h create mode 100644 xen/include/asm-x86/hvm/io.h create mode 100644 xen/include/asm-x86/hvm/iommu.h create mode 100644 xen/include/asm-x86/hvm/irq.h create mode 100644 xen/include/asm-x86/hvm/support.h create mode 100644 xen/include/asm-x86/hvm/svm/amd-iommu-acpi.h create mode 100644 xen/include/asm-x86/hvm/svm/amd-iommu-defs.h create mode 100644 xen/include/asm-x86/hvm/svm/amd-iommu-proto.h create mode 100644 xen/include/asm-x86/hvm/svm/asid.h create mode 100644 xen/include/asm-x86/hvm/svm/emulate.h create mode 100644 xen/include/asm-x86/hvm/svm/intr.h create mode 100644 xen/include/asm-x86/hvm/svm/svm.h create mode 100644 xen/include/asm-x86/hvm/svm/vmcb.h create mode 100644 xen/include/asm-x86/hvm/trace.h create mode 100644 xen/include/asm-x86/hvm/vcpu.h create mode 100644 xen/include/asm-x86/hvm/vioapic.h create mode 100644 xen/include/asm-x86/hvm/viridian.h create mode 100644 xen/include/asm-x86/hvm/vlapic.h create mode 100644 xen/include/asm-x86/hvm/vmx/vmcs.h create mode 100644 xen/include/asm-x86/hvm/vmx/vmx.h create mode 100644 xen/include/asm-x86/hvm/vmx/vpmu.h create mode 100644 xen/include/asm-x86/hvm/vmx/vpmu_core2.h create mode 100644 xen/include/asm-x86/hvm/vpic.h create mode 100644 xen/include/asm-x86/hvm/vpt.h create mode 100644 xen/include/asm-x86/hypercall.h create mode 100644 xen/include/asm-x86/i387.h create mode 100644 xen/include/asm-x86/init.h create mode 100644 xen/include/asm-x86/io.h create mode 100644 xen/include/asm-x86/io_apic.h create mode 100644 xen/include/asm-x86/iocap.h create mode 100644 xen/include/asm-x86/ipi.h create mode 100644 xen/include/asm-x86/irq.h create mode 100644 xen/include/asm-x86/ldt.h create mode 100644 xen/include/asm-x86/mach-default/bios_ebda.h create mode 100644 xen/include/asm-x86/mach-default/io_ports.h create mode 100644 xen/include/asm-x86/mach-default/irq_vectors.h create mode 100644 xen/include/asm-x86/mach-default/mach_mpparse.h create mode 100644 xen/include/asm-x86/mach-default/mach_mpspec.h create mode 100644 xen/include/asm-x86/mach-default/mach_wakecpu.h create mode 100644 xen/include/asm-x86/mach-default/smpboot_hooks.h create mode 100644 xen/include/asm-x86/mach-generic/mach_apic.h create mode 100644 xen/include/asm-x86/mach-generic/mach_mpparse.h create mode 100644 xen/include/asm-x86/mach-summit/mach_mpparse.h create mode 100644 xen/include/asm-x86/mc146818rtc.h create mode 100644 xen/include/asm-x86/microcode.h create mode 100644 xen/include/asm-x86/mm.h create mode 100644 xen/include/asm-x86/mpspec.h create mode 100644 xen/include/asm-x86/mpspec_def.h create mode 100644 xen/include/asm-x86/msi.h create mode 100644 xen/include/asm-x86/msr-index.h create mode 100644 xen/include/asm-x86/msr.h create mode 100644 xen/include/asm-x86/mtrr.h create mode 100644 xen/include/asm-x86/multicall.h create mode 100644 xen/include/asm-x86/nmi.h create mode 100644 xen/include/asm-x86/numa.h create mode 100644 xen/include/asm-x86/p2m.h create mode 100644 xen/include/asm-x86/page.h create mode 100644 xen/include/asm-x86/paging.h create mode 100644 xen/include/asm-x86/percpu.h create mode 100644 xen/include/asm-x86/perfc.h create mode 100644 xen/include/asm-x86/perfc_defn.h create mode 100644 xen/include/asm-x86/pirq.h create mode 100644 xen/include/asm-x86/processor.h create mode 100644 xen/include/asm-x86/regs.h create mode 100644 xen/include/asm-x86/rwlock.h create mode 100644 xen/include/asm-x86/shadow.h create mode 100644 xen/include/asm-x86/shared.h create mode 100644 xen/include/asm-x86/smp.h create mode 100644 xen/include/asm-x86/softirq.h create mode 100644 xen/include/asm-x86/spinlock.h create mode 100644 xen/include/asm-x86/string.h create mode 100644 xen/include/asm-x86/system.h create mode 100644 xen/include/asm-x86/tboot.h create mode 100644 xen/include/asm-x86/time.h create mode 100644 xen/include/asm-x86/trace.h create mode 100644 xen/include/asm-x86/traps.h create mode 100644 xen/include/asm-x86/types.h create mode 100644 xen/include/asm-x86/uaccess.h create mode 100644 xen/include/asm-x86/x86_32/asm_defns.h create mode 100644 xen/include/asm-x86/x86_32/bug.h create mode 100644 xen/include/asm-x86/x86_32/elf.h create mode 100644 xen/include/asm-x86/x86_32/page.h create mode 100644 xen/include/asm-x86/x86_32/regs.h create mode 100644 xen/include/asm-x86/x86_32/system.h create mode 100644 xen/include/asm-x86/x86_32/uaccess.h create mode 100644 xen/include/asm-x86/x86_64/asm_defns.h create mode 100644 xen/include/asm-x86/x86_64/bug.h create mode 100644 xen/include/asm-x86/x86_64/elf.h create mode 100644 xen/include/asm-x86/x86_64/page.h create mode 100644 xen/include/asm-x86/x86_64/regs.h create mode 100644 xen/include/asm-x86/x86_64/system.h create mode 100644 xen/include/asm-x86/x86_64/uaccess.h create mode 100644 xen/include/asm-x86/x86_emulate.h create mode 100644 xen/include/asm-x86/xenoprof.h create mode 100644 xen/include/public/COPYING create mode 100644 xen/include/public/arch-ia64.h create mode 100644 xen/include/public/arch-ia64/debug_op.h create mode 100644 xen/include/public/arch-ia64/hvm/memmap.h create mode 100644 xen/include/public/arch-ia64/hvm/save.h create mode 100644 xen/include/public/arch-ia64/sioemu.h create mode 100644 xen/include/public/arch-x86/cpuid.h create mode 100644 xen/include/public/arch-x86/hvm/save.h create mode 100644 xen/include/public/arch-x86/xen-mca.h create mode 100644 xen/include/public/arch-x86/xen-x86_32.h create mode 100644 xen/include/public/arch-x86/xen-x86_64.h create mode 100644 xen/include/public/arch-x86/xen.h create mode 100644 xen/include/public/arch-x86_32.h create mode 100644 xen/include/public/arch-x86_64.h create mode 100644 xen/include/public/callback.h create mode 100644 xen/include/public/dom0_ops.h create mode 100644 xen/include/public/domctl.h create mode 100644 xen/include/public/elfnote.h create mode 100644 xen/include/public/elfstructs.h create mode 100644 xen/include/public/event_channel.h create mode 100644 xen/include/public/features.h create mode 100644 xen/include/public/grant_table.h create mode 100644 xen/include/public/hvm/e820.h create mode 100644 xen/include/public/hvm/hvm_info_table.h create mode 100644 xen/include/public/hvm/hvm_op.h create mode 100644 xen/include/public/hvm/ioreq.h create mode 100644 xen/include/public/hvm/params.h create mode 100644 xen/include/public/hvm/save.h create mode 100644 xen/include/public/io/blkif.h create mode 100644 xen/include/public/io/console.h create mode 100644 xen/include/public/io/fbif.h create mode 100644 xen/include/public/io/fsif.h create mode 100644 xen/include/public/io/kbdif.h create mode 100644 xen/include/public/io/netif.h create mode 100644 xen/include/public/io/pciif.h create mode 100644 xen/include/public/io/protocols.h create mode 100644 xen/include/public/io/ring.h create mode 100644 xen/include/public/io/tpmif.h create mode 100644 xen/include/public/io/xenbus.h create mode 100644 xen/include/public/io/xs_wire.h create mode 100644 xen/include/public/kexec.h create mode 100644 xen/include/public/libelf.h create mode 100644 xen/include/public/memory.h create mode 100644 xen/include/public/nmi.h create mode 100644 xen/include/public/physdev.h create mode 100644 xen/include/public/platform.h create mode 100644 xen/include/public/sched.h create mode 100644 xen/include/public/sysctl.h create mode 100644 xen/include/public/trace.h create mode 100644 xen/include/public/vcpu.h create mode 100644 xen/include/public/version.h create mode 100644 xen/include/public/xen-compat.h create mode 100644 xen/include/public/xen.h create mode 100644 xen/include/public/xencomm.h create mode 100644 xen/include/public/xenoprof.h create mode 100644 xen/include/public/xsm/acm.h create mode 100644 xen/include/public/xsm/acm_ops.h create mode 100644 xen/include/public/xsm/flask_op.h create mode 100644 xen/include/xen/acpi.h create mode 100644 xen/include/xen/bitmap.h create mode 100644 xen/include/xen/bitops.h create mode 100644 xen/include/xen/byteorder/big_endian.h create mode 100644 xen/include/xen/byteorder/generic.h create mode 100644 xen/include/xen/byteorder/little_endian.h create mode 100644 xen/include/xen/byteorder/swab.h create mode 100644 xen/include/xen/cache.h create mode 100644 xen/include/xen/compat.h create mode 100644 xen/include/xen/compile.h.in create mode 100644 xen/include/xen/compiler.h create mode 100644 xen/include/xen/config.h create mode 100644 xen/include/xen/console.h create mode 100644 xen/include/xen/cpuidle.h create mode 100644 xen/include/xen/cpumask.h create mode 100644 xen/include/xen/ctype.h create mode 100644 xen/include/xen/delay.h create mode 100644 xen/include/xen/dmi.h create mode 100644 xen/include/xen/domain.h create mode 100644 xen/include/xen/domain_page.h create mode 100644 xen/include/xen/elf.h create mode 100644 xen/include/xen/elfcore.h create mode 100644 xen/include/xen/errno.h create mode 100644 xen/include/xen/event.h create mode 100644 xen/include/xen/gdbstub.h create mode 100644 xen/include/xen/grant_table.h create mode 100644 xen/include/xen/guest_access.h create mode 100644 xen/include/xen/hvm/iommu.h create mode 100644 xen/include/xen/hvm/save.h create mode 100644 xen/include/xen/hypercall.h create mode 100644 xen/include/xen/init.h create mode 100644 xen/include/xen/inttypes.h create mode 100644 xen/include/xen/iocap.h create mode 100644 xen/include/xen/iommu.h create mode 100644 xen/include/xen/irq.h create mode 100644 xen/include/xen/irq_cpustat.h create mode 100644 xen/include/xen/kernel.h create mode 100644 xen/include/xen/kexec.h create mode 100644 xen/include/xen/keyhandler.h create mode 100644 xen/include/xen/lib.h create mode 100644 xen/include/xen/list.h create mode 100644 xen/include/xen/mm.h create mode 100644 xen/include/xen/multiboot.h create mode 100644 xen/include/xen/multiboot2.h create mode 100644 xen/include/xen/multicall.h create mode 100644 xen/include/xen/nmi.h create mode 100644 xen/include/xen/nodemask.h create mode 100644 xen/include/xen/numa.h create mode 100644 xen/include/xen/paging.h create mode 100644 xen/include/xen/pci.h create mode 100644 xen/include/xen/pci_regs.h create mode 100644 xen/include/xen/percpu.h create mode 100644 xen/include/xen/perfc.h create mode 100644 xen/include/xen/perfc_defn.h create mode 100644 xen/include/xen/prefetch.h create mode 100644 xen/include/xen/rangeset.h create mode 100644 xen/include/xen/rcupdate.h create mode 100644 xen/include/xen/sched-if.h create mode 100644 xen/include/xen/sched.h create mode 100644 xen/include/xen/serial.h create mode 100644 xen/include/xen/shared.h create mode 100644 xen/include/xen/shutdown.h create mode 100644 xen/include/xen/smp.h create mode 100644 xen/include/xen/softirq.h create mode 100644 xen/include/xen/spinlock.h create mode 100644 xen/include/xen/stdarg.h create mode 100644 xen/include/xen/stop_machine.h create mode 100644 xen/include/xen/string.h create mode 100644 xen/include/xen/symbols.h create mode 100644 xen/include/xen/time.h create mode 100644 xen/include/xen/timer.h create mode 100644 xen/include/xen/trace.h create mode 100644 xen/include/xen/types.h create mode 100644 xen/include/xen/version.h create mode 100644 xen/include/xen/vga.h create mode 100644 xen/include/xen/xencomm.h create mode 100644 xen/include/xen/xenoprof.h create mode 100644 xen/include/xen/xmalloc.h create mode 100644 xen/include/xlat.lst create mode 100644 xen/include/xsm/acm/acm_core.h create mode 100644 xen/include/xsm/acm/acm_endian.h create mode 100644 xen/include/xsm/acm/acm_hooks.h create mode 100644 xen/include/xsm/xsm.h create mode 100644 xen/tools/Makefile create mode 100755 xen/tools/compat-build-header.py create mode 100755 xen/tools/compat-build-source.py create mode 100644 xen/tools/figlet/LICENSE create mode 100644 xen/tools/figlet/Makefile create mode 100644 xen/tools/figlet/README create mode 100644 xen/tools/figlet/figlet.c create mode 100644 xen/tools/figlet/xen.flf create mode 100644 xen/tools/get-fields.sh create mode 100644 xen/tools/symbols.c create mode 100644 xen/xsm/Makefile create mode 100644 xen/xsm/acm/Makefile create mode 100644 xen/xsm/acm/acm_chinesewall_hooks.c create mode 100644 xen/xsm/acm/acm_core.c create mode 100644 xen/xsm/acm/acm_null_hooks.c create mode 100644 xen/xsm/acm/acm_ops.c create mode 100644 xen/xsm/acm/acm_policy.c create mode 100644 xen/xsm/acm/acm_simple_type_enforcement_hooks.c create mode 100644 xen/xsm/acm/acm_xsm_hooks.c create mode 100644 xen/xsm/dummy.c create mode 100644 xen/xsm/flask/Makefile create mode 100644 xen/xsm/flask/avc.c create mode 100644 xen/xsm/flask/flask_op.c create mode 100644 xen/xsm/flask/hooks.c create mode 100644 xen/xsm/flask/include/av_inherit.h create mode 100644 xen/xsm/flask/include/av_perm_to_string.h create mode 100644 xen/xsm/flask/include/av_permissions.h create mode 100644 xen/xsm/flask/include/avc.h create mode 100644 xen/xsm/flask/include/avc_ss.h create mode 100644 xen/xsm/flask/include/class_to_string.h create mode 100644 xen/xsm/flask/include/common_perm_to_string.h create mode 100644 xen/xsm/flask/include/conditional.h create mode 100644 xen/xsm/flask/include/flask.h create mode 100644 xen/xsm/flask/include/initial_sid_to_string.h create mode 100644 xen/xsm/flask/include/objsec.h create mode 100644 xen/xsm/flask/include/security.h create mode 100644 xen/xsm/flask/ss/Makefile create mode 100644 xen/xsm/flask/ss/avtab.c create mode 100644 xen/xsm/flask/ss/avtab.h create mode 100644 xen/xsm/flask/ss/conditional.c create mode 100644 xen/xsm/flask/ss/conditional.h create mode 100644 xen/xsm/flask/ss/constraint.h create mode 100644 xen/xsm/flask/ss/context.h create mode 100644 xen/xsm/flask/ss/ebitmap.c create mode 100644 xen/xsm/flask/ss/ebitmap.h create mode 100644 xen/xsm/flask/ss/hashtab.c create mode 100644 xen/xsm/flask/ss/hashtab.h create mode 100644 xen/xsm/flask/ss/mls.c create mode 100644 xen/xsm/flask/ss/mls.h create mode 100644 xen/xsm/flask/ss/mls_types.h create mode 100644 xen/xsm/flask/ss/policydb.c create mode 100644 xen/xsm/flask/ss/policydb.h create mode 100644 xen/xsm/flask/ss/services.c create mode 100644 xen/xsm/flask/ss/services.h create mode 100644 xen/xsm/flask/ss/sidtab.c create mode 100644 xen/xsm/flask/ss/sidtab.h create mode 100644 xen/xsm/flask/ss/symtab.c create mode 100644 xen/xsm/flask/ss/symtab.h create mode 100644 xen/xsm/xsm_core.c create mode 100644 xen/xsm/xsm_policy.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..aba8ddf --- /dev/null +++ b/COPYING @@ -0,0 +1,370 @@ + +GNU General Public License +-------------------------- + +Most files in this repository are licensed under the terms of the GNU +General Public License (GPL), a copy of which is attached at the end +of this notice. Note that the only valid version of the GPL as far as +the files in this repository are concerned is _this_ particular +version of the license (i.e., *only* v2, not v2.2 or v3.x or +whatever), unless explicitly otherwise stated. + +Licensing Exceptions (the relaxed BSD-style license) +---------------------------------------------------- + +For the convenience of users and those who are porting OSes to run as +Xen guests, certain files in this repository are not subject to the +GPL when distributed separately or included in software packages +outside this repository. Instead we specify a much more relaxed +BSD-style license. Affected files include the Xen interface headers +(xen/include/public/COPYING), and various drivers, support functions +and header files within the Linux source trees on +http://xenbits.xensource.com/linux-2.6.X-xen.hg. In all such cases, +license terms are stated at the top of the file or in a COPYING file +in the same directory. Note that _any_ file that is modified and then +distributed within a Linux kernel is still subject to the GNU GPL. + + -- Keir Fraser (on behalf of the Xen team) + +===================================================================== + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Config.mk b/Config.mk new file mode 100644 index 0000000..9280228 --- /dev/null +++ b/Config.mk @@ -0,0 +1,114 @@ +# -*- mode: Makefile; -*- + +# A debug build of Xen and tools? +debug ?= n + +XEN_COMPILE_ARCH ?= $(shell uname -m | sed -e s/i.86/x86_32/ \ + -e s/i86pc/x86_32/ -e s/amd64/x86_64/) +XEN_TARGET_ARCH ?= $(XEN_COMPILE_ARCH) +XEN_OS ?= $(shell uname -s) + +CONFIG_$(XEN_OS) := y + +SHELL ?= /bin/sh + +# Tools to run on system hosting the build +HOSTCC = gcc +HOSTCFLAGS = -Wall -Werror -Wstrict-prototypes -O2 -fomit-frame-pointer +HOSTCFLAGS += -fno-strict-aliasing + +DISTDIR ?= $(XEN_ROOT)/dist +DESTDIR ?= / +DOCDIR ?= /usr/share/doc/xen +MANDIR ?= /usr/share/man + +# Allow phony attribute to be listed as dependency rather than fake target +.PHONY: .phony + +include $(XEN_ROOT)/config/$(XEN_OS).mk +include $(XEN_ROOT)/config/$(XEN_TARGET_ARCH).mk + +ifneq ($(EXTRA_PREFIX),) +EXTRA_INCLUDES += $(EXTRA_PREFIX)/include +EXTRA_LIB += $(EXTRA_PREFIX)/$(LIBLEAFDIR) +endif + +# cc-option: Check if compiler supports first option, else fall back to second. +# Usage: cflags-y += $(call cc-option,$(CC),-march=winchip-c6,-march=i586) +cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ + /dev/null 2>&1`"; then echo "$(2)"; else echo "$(3)"; fi ;) + +# cc-ver: Check compiler is at least specified version. Return boolean 'y'/'n'. +# Usage: ifeq ($(call cc-ver,$(CC),0x030400),y) +cc-ver = $(shell if [ $$((`$(1) -dumpversion | awk -F. \ + '{ printf "0x%02x%02x%02x", $$1, $$2, $$3}'`)) -ge $$(($(2))) ]; \ + then echo y; else echo n; fi ;) + +# cc-ver-check: Check compiler is at least specified version, else fail. +# Usage: $(call cc-ver-check,CC,0x030400,"Require at least gcc-3.4") +cc-ver-check = $(eval $(call cc-ver-check-closure,$(1),$(2),$(3))) +define cc-ver-check-closure + ifeq ($$(call cc-ver,$$($(1)),$(2)),n) + override $(1) = echo "*** FATAL BUILD ERROR: "$(3) >&2; exit 1; + cc-option := n + endif +endef + +define absolutify_xen_root + case "$(XEN_ROOT)" in \ + /*) XEN_ROOT=$(XEN_ROOT) ;; \ + *) xen_root_lhs=`pwd`; \ + xen_root_rhs=$(XEN_ROOT)/; \ + while [ "x$${xen_root_rhs#../}" != "x$$xen_root_rhs" ]; do \ + xen_root_rhs="$${xen_root_rhs#../}"; \ + xen_root_rhs="$${xen_root_rhs#/}"; \ + xen_root_rhs="$${xen_root_rhs#/}"; \ + xen_root_lhs="$${xen_root_lhs%/*}"; \ + done; \ + XEN_ROOT="$$xen_root_lhs/$$xen_root_rhs" ;; \ + esac; \ + export XEN_ROOT +endef + +ifeq ($(debug),y) +CFLAGS += -g +endif + +CFLAGS += -fno-strict-aliasing + +CFLAGS += -std=gnu99 + +CFLAGS += -Wall -Wstrict-prototypes + +# -Wunused-value makes GCC 4.x too aggressive for my taste: ignoring the +# result of any casted expression causes a warning. +CFLAGS += -Wno-unused-value + +HOSTCFLAGS += $(call cc-option,$(HOSTCC),-Wdeclaration-after-statement,) +CFLAGS += $(call cc-option,$(CC),-Wdeclaration-after-statement,) + +LDFLAGS += $(foreach i, $(EXTRA_LIB), -L$(i)) +CFLAGS += $(foreach i, $(EXTRA_INCLUDES), -I$(i)) + +# Enable XSM security module. Enabling XSM requires selection of an +# XSM security module (FLASK_ENABLE or ACM_SECURITY). +XSM_ENABLE ?= n +FLASK_ENABLE ?= n +ACM_SECURITY ?= n + +QEMU_REMOTE=http://xenbits.xensource.com/git-http/qemu-xen-unstable.git + +# Specify which qemu-dm to use. This may be `ioemu' to use the old +# Mercurial in-tree version, or a local directory, or a git URL. +# CONFIG_QEMU ?= ../qemu-xen.git +CONFIG_QEMU ?= $(QEMU_REMOTE) + +# Optional components +XENSTAT_XENTOP ?= y +VTPM_TOOLS ?= n +LIBXENAPI_BINDINGS ?= n +PYTHON_TOOLS ?= y +CONFIG_MINITERM ?= n +CONFIG_LOMOUNT ?= n + +-include $(XEN_ROOT)/.config diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2b724bb --- /dev/null +++ b/Makefile @@ -0,0 +1,273 @@ +# +# Grand Unified Makefile for Xen. +# + +# Default target must appear before any include lines +.PHONY: all +all: dist + +export XEN_ROOT=$(CURDIR) +include Config.mk + +SUBARCH := $(subst x86_32,i386,$(XEN_TARGET_ARCH)) +export XEN_TARGET_ARCH SUBARCH XEN_SYSTYPE +include buildconfigs/Rules.mk + +# build and install everything into the standard system directories +.PHONY: install +install: install-xen install-kernels install-tools install-stubdom install-docs + +.PHONY: build +build: kernels + $(MAKE) -C xen build + $(MAKE) -C tools build + $(MAKE) -C stubdom build +ifeq (x86_64,$(XEN_TARGET_ARCH)) + XEN_TARGET_ARCH=x86_32 $(MAKE) -C stubdom pv-grub +endif + $(MAKE) -C docs build + +# The test target is for unit tests that can run without an installation. Of +# course, many tests require a machine running Xen itself, and these are +# handled elsewhere. +.PHONY: test +test: + $(MAKE) -C tools/python test + +# build and install everything into local dist directory +.PHONY: dist +dist: DESTDIR=$(DISTDIR)/install +dist: dist-xen dist-kernels dist-tools dist-stubdom dist-docs + $(INSTALL_DIR) $(DISTDIR)/check + $(INSTALL_DATA) ./COPYING $(DISTDIR) + $(INSTALL_DATA) ./README $(DISTDIR) + $(INSTALL_PROG) ./install.sh $(DISTDIR) + $(INSTALL_PROG) tools/check/chk tools/check/check_* tools/check/funcs.sh $(DISTDIR)/check +dist-%: DESTDIR=$(DISTDIR)/install +dist-%: install-% + @: # do nothing + +# Legacy dist targets +.PHONY: xen tools stubdom kernels docs +xen: dist-xen +tools: dist-tools +kernels: dist-kernels +stubdom: dist-stubdom +docs: dist-docs + +.PHONY: prep-kernels +prep-kernels: + for i in $(XKERNELS) ; do $(MAKE) $$i-prep || exit 1; done + +.PHONY: install-xen +install-xen: + $(MAKE) -C xen install + +.PHONY: install-tools +install-tools: tools/ioemu-dir + $(MAKE) -C tools install + +.PHONY: install-kernels +install-kernels: + for i in $(XKERNELS) ; do $(MAKE) $$i-install || exit 1; done + +.PHONY: install-stubdom +install-stubdom: tools/ioemu-dir + $(MAKE) -C stubdom install +ifeq (x86_64,$(XEN_TARGET_ARCH)) + XEN_TARGET_ARCH=x86_32 $(MAKE) -C stubdom install-grub +endif + +tools/ioemu-dir: + $(MAKE) -C tools ioemu-dir-find + +.PHONY: install-docs +install-docs: + sh ./docs/check_pkgs && $(MAKE) -C docs install || true + +.PHONY: dev-docs +dev-docs: + $(MAKE) -C docs dev-docs + +# Build all the various kernels and modules +.PHONY: kbuild +kbuild: kernels + +# Delete the kernel build trees entirely +.PHONY: kdelete +kdelete: + for i in $(XKERNELS) ; do $(MAKE) $$i-delete ; done + +# Clean the kernel build trees +.PHONY: kclean +kclean: + for i in $(XKERNELS) ; do $(MAKE) $$i-clean ; done + +# build xen, the tools, and a domain 0 plus unprivileged linux-xen images, +# and place them in the install directory. 'make install' should then +# copy them to the normal system directories +.PHONY: world +world: + $(MAKE) clean + $(MAKE) kdelete + $(MAKE) dist + +# clean doesn't do a kclean +.PHONY: clean +clean:: + $(MAKE) -C xen clean + $(MAKE) -C tools clean + $(MAKE) -C stubdom crossclean +ifeq (x86_64,$(XEN_TARGET_ARCH)) + XEN_TARGET_ARCH=x86_32 $(MAKE) -C stubdom crossclean +endif + $(MAKE) -C docs clean + +# clean, but blow away kernel build tree plus tarballs +.PHONY: distclean +distclean: + $(MAKE) -C xen distclean + $(MAKE) -C tools distclean + $(MAKE) -C stubdom distclean +ifeq (x86_64,$(XEN_TARGET_ARCH)) + XEN_TARGET_ARCH=x86_32 $(MAKE) -C stubdom distclean +endif + $(MAKE) -C docs distclean + rm -rf dist patches/tmp + for i in $(ALLKERNELS) ; do $(MAKE) $$i-delete ; done + rm -rf patches/*/.makedep + +# Linux name for GNU distclean +.PHONY: mrproper +mrproper: distclean + +# Prepare for source tarball +.PHONY: src-tarball +src-tarball: distclean + $(MAKE) -C xen .banner + rm -rf xen/tools/figlet .[a-z]* + $(MAKE) -C xen distclean + +.PHONY: help +help: + @echo 'Installation targets:' + @echo ' install - build and install everything' + @echo ' install-xen - build and install the Xen hypervisor' + @echo ' install-tools - build and install the control tools' + @echo ' install-kernels - build and install guest kernels' + @echo ' install-stubdom - build and install the stubdomain images' + @echo ' install-docs - build and install user documentation' + @echo '' + @echo 'Building targets:' + @echo ' dist - build and install everything into local dist directory' + @echo ' world - clean everything, delete guest kernel build' + @echo ' trees then make dist' + @echo ' xen - build and install Xen hypervisor' + @echo ' tools - build and install tools' + @echo ' stubdom - build and install the stubdomain images' + @echo ' kernels - build and install guest kernels' + @echo ' kbuild - synonym for make kernels' + @echo ' docs - build and install user documentation' + @echo ' dev-docs - build developer-only documentation' + @echo '' + @echo 'Cleaning targets:' + @echo ' clean - clean the Xen, tools and docs (but not guest kernel trees)' + @echo ' distclean - clean plus delete kernel build trees and' + @echo ' local downloaded files' + @echo ' kdelete - delete guest kernel build trees' + @echo ' kclean - clean guest kernel build trees' + @echo '' + @echo 'Miscellaneous targets:' + @echo ' prep-kernels - prepares kernel directories, does not build' + @echo ' uninstall - attempt to remove installed Xen tools' + @echo ' (use with extreme care!)' + @echo + @echo 'Trusted Boot (tboot) targets:' + @echo ' build-tboot - download and build the tboot module' + @echo ' install-tboot - download, build, and install the tboot module' + @echo ' clean-tboot - clean the tboot module if it exists' + @echo + @echo 'Environment:' + @echo ' XEN_PYTHON_NATIVE_INSTALL=y' + @echo ' - native python install or dist' + @echo ' install into prefix/lib/python' + @echo ' instead of /lib/python' + @echo ' true if set to non-empty value, false otherwise' + +# Use this target with extreme care! +.PHONY: uninstall +uninstall: D=$(DESTDIR) +uninstall: + [ -d $(D)/etc/xen ] && mv -f $(D)/etc/xen $(D)/etc/xen.old-`date +%s` || true + rm -rf $(D)/etc/init.d/xend* + rm -rf $(D)/etc/hotplug/xen-backend.agent + rm -f $(D)/etc/udev/rules.d/xen-backend.rules + rm -f $(D)/etc/udev/xen-backend.rules + rm -f $(D)/etc/sysconfig/xendomains + rm -rf $(D)/var/run/xen* $(D)/var/lib/xen* + rm -rf $(D)/boot/*xen* + rm -rf $(D)/lib/modules/*xen* + rm -rf $(D)/usr/bin/xen* $(D)/usr/bin/lomount + rm -rf $(D)/usr/bin/cpuperf-perfcntr $(D)/usr/bin/cpuperf-xen + rm -rf $(D)/usr/bin/xc_shadow + rm -rf $(D)/usr/bin/pygrub + rm -rf $(D)/usr/bin/setsize $(D)/usr/bin/tbctl + rm -rf $(D)/usr/bin/xsls + rm -rf $(D)/usr/include/xenctrl.h $(D)/usr/include/xenguest.h + rm -rf $(D)/usr/include/xs_lib.h $(D)/usr/include/xs.h + rm -rf $(D)/usr/include/xen + rm -rf $(D)$(LIBDIR)/libxenctrl* $(D)$(LIBDIR)/libxenguest* + rm -rf $(D)$(LIBDIR)/libxenstore* + rm -rf $(D)$(LIBDIR)/python/xen $(D)$(LIBDIR)/python/grub + rm -rf $(D)$(LIBDIR)/xen/ + rm -rf $(D)/usr/lib/xen/ + rm -rf $(D)/usr/local/sbin/setmask $(D)/usr/local/sbin/xen* + rm -rf $(D)/usr/sbin/xen* $(D)/usr/sbin/netfix $(D)/usr/sbin/xm + rm -rf $(D)/usr/share/doc/xen + rm -rf $(D)/usr/share/xen + rm -rf $(D)/usr/share/man/man1/xen* + rm -rf $(D)/usr/share/man/man8/xen* + rm -rf $(D)/boot/tboot* + +# Legacy targets for compatibility +.PHONY: linux26 +linux26: + $(MAKE) 'KERNELS=linux-2.6*' kernels + + +# +# tboot targets +# + +TBOOT_TARFILE = tboot-20080613.tar.gz +TBOOT_BASE_URL = http://downloads.sourceforge.net/tboot + +.PHONY: build-tboot +build-tboot: download_tboot + $(MAKE) -C tboot build + +.PHONY: install-tboot +install-tboot: download_tboot + $(MAKE) -C tboot install + +.PHONY: dist-tboot +dist-tboot: download_tboot + $(MAKE) DESTDIR=$(DISTDIR)/install -C tboot dist + +.PHONY: clean-tboot +clean-tboot: + [ ! -d tboot ] || $(MAKE) -C tboot clean + +.PHONY: distclean-tboot +distclean-tboot: + [ ! -d tboot ] || $(MAKE) -C tboot distclean + +.PHONY: download_tboot +download_tboot: tboot/Makefile + +tboot/Makefile: tboot/$(TBOOT_TARFILE) + [ -e tboot/Makefile ] || tar -xzf tboot/$(TBOOT_TARFILE) -C tboot/ --strip-components 1 + +tboot/$(TBOOT_TARFILE): + mkdir -p tboot + wget -O tboot/$(TBOOT_TARFILE) $(TBOOT_BASE_URL)/$(TBOOT_TARFILE) diff --git a/README b/README new file mode 100644 index 0000000..1a46b32 --- /dev/null +++ b/README @@ -0,0 +1,229 @@ +################################# + __ __ _____ _____ + \ \/ /___ _ __ |___ / |___ / + \ // _ \ '_ \ |_ \ |_ \ + / \ __/ | | | ___) | ___) | + /_/\_\___|_| |_| |____(_)____/ + +################################# + +http://www.xen.org/ + +What is Xen? +============ + +Xen is a Virtual Machine Monitor (VMM) originally developed by the +Systems Research Group of the University of Cambridge Computer +Laboratory, as part of the UK-EPSRC funded XenoServers project. Xen +is freely-distributable Open Source software, released under the GNU +GPL. Since its initial public release, Xen has grown a large +development community, spearheaded by XenSource Inc, a company created +by the original Xen development team to build enterprise products +around Xen. + +The 3.3 release offers excellent performance, hardware support and +enterprise-grade features such as x86_32-PAE, x86_64, SMP guests and +live relocation of VMs. Ports to Linux 2.6, Linux 2.4, NetBSD, FreeBSD +and Solaris are available from the community. + +This file contains some quick-start instructions to install Xen on +your system. For full documentation, see the Xen User Manual. If this +is a pre-built release then you can find the manual at: + dist/install/usr/share/doc/xen/pdf/user.pdf +If you have a source release, then 'make -C docs' will build the +manual at docs/pdf/user.pdf. + +Quick-Start Guide - Pre-Built Binary Release +============================================ + +[NB. Unless noted otherwise, all the following steps should be +performed with root privileges.] + +1. Install the binary distribution onto your filesystem: + + # sh ./install.sh + + Among other things, this will install Xen and Xen-ready Linux + kernel files in /boot, kernel modules and Python packages in /lib, + and various control tools in standard 'bin' directories. + +2. Configure your bootloader to boot Xen and an initial Linux virtual + machine. Note that Xen currently only works with GRUB and pxelinux + derived boot loaders: less common alternatives such as LILO are + *not* supported. You can most likely find your GRUB menu file at + /boot/grub/menu.lst: edit this file to include an entry like the + following: + + title Xen 3.3 / XenLinux 2.6 + kernel /boot/xen-3.3.gz console=vga + module /boot/vmlinuz-2.6-xen root= ro console=tty0 + module /boot/initrd-2.6-xen.img + + NB: Not all kernel configs need an initial ram disk (initrd), but + if you do specify one you'll need to use the 'module' grub directive + rather than 'initrd'. + + The linux command line takes all the usual options, such as + root= to specify your usual root partition (e.g., + /dev/hda1). + + The Xen command line takes a number of optional arguments described + in the manual. The most common is 'dom0_mem=xxxM' which sets the + amount of memory to allocate for use by your initial virtual + machine (known as domain 0). Note that Xen itself reserves about + 32MB memory for internal use, which is not available for allocation + to virtual machines. + +3. Reboot your system and select the "Xen 3.3 / XenLinux 2.6" menu + option. After booting Xen, Linux will start and your initialisation + scripts should execute in the usual way. + +Quick-Start Guide - Source Release +================================== + +First, there are a number of prerequisites for building a Xen source +release. Make sure you have all the following installed, either by +visiting the project webpage or installing a pre-built package +provided by your Linux distributor: + * GCC v3.4 or later + * GNU Make + * GNU Binutils + * Development install of zlib (e.g., zlib-dev) + * Development install of Python v2.3 or later (e.g., python-dev) + * Development install of curses (e.g., libncurses-dev) + * Development install of openssl (e.g., openssl-dev) + * Development install of x11 (e.g. xorg-x11-dev) + * bridge-utils package (/sbin/brctl) + * iproute package (/sbin/ip) + * hotplug or udev + +[NB. Unless noted otherwise, all the following steps should be +performed with root privileges.] + +1. Download and untar the source tarball file. This will be a + file named xen-unstable-src.tgz, or xen-$version-src.tgz. + You can also pull the current version from the mercurial + repository at http://xenbits.xensource.com/ + + # tar xzf xen-unstable-src.tgz + + Assuming you are using the unstable tree, this will + untar into xen-unstable. The rest of the instructions + use the unstable tree as an example, substitute the + version for unstable. + +2. cd to xen-unstable (or whatever you sensibly rename it to). + +On Linux: + +3. For the very first build, or if you want to destroy existing + .configs and build trees, perform the following steps: + + # make world + # make install + + This will create and install onto the local machine. It will build + the xen binary (xen.gz), and a linux kernel and modules that can be + used in both dom0 and an unprivileged guest kernel (vmlinuz-2.6.x-xen), + the tools and the documentation. + + You can override the destination for make install by setting DESTDIR + to some value. + + The make command line defaults to building the kernel vmlinuz-2.6.x-xen. + You can override this default by specifying KERNELS=kernelname. For + example, you can make two kernels - linux-2.6-xen0 + and linux-2.6-xenU - which are smaller builds containing only selected + modules, intended primarily for developers that don't like to wait + for a full -xen kernel to build. The -xenU kernel is particularly small, + as it does not contain any physical device drivers, and hence is + only useful for guest domains. + + To make these two kernels, simply specify + + KERNELS="linux-2.6-xen0 linux-2.6-xenU" + + in the make command line. + +4. To rebuild an existing tree without modifying the config: + # make dist + + This will build and install xen, kernels, tools, and + docs into the local dist/ directory. + + You can override the destination for make install by setting DISTDIR + to some value. + + make install and make dist differ in that make install does the + right things for your local machine (installing the appropriate + version of hotplug or udev scripts, for example), but make dist + includes all versions of those scripts, so that you can copy the dist + directory to another machine and install from that distribution. + +5. To rebuild a kernel with a modified config: + + # make linux-2.6-xen-config CONFIGMODE=menuconfig (or xconfig) + # make linux-2.6-xen-build + # make linux-2.6-xen-install + + Depending on your config, you may need to use 'mkinitrd' to create + an initial ram disk, just like a native system e.g. + # depmod 2.6.18-xen + # mkinitrd -v -f --with=aacraid --with=sd_mod --with=scsi_mod initrd-2.6.18-xen.img 2.6.18-xen + + Other systems may requires the use of 'mkinitramfs' to create the + ram disk. + # depmod 2.6.18-xen + # mkinitramfs -o initrd-2.6.18-xen.img 2.6.18-xen + + +Python Runtime Libraries +======================== + +Xend (the Xen daemon) has the following runtime dependencies: + + * Python 2.3 or later. + In many distros, the XML-aspects to the standard library + (xml.dom.minidom etc) are broken out into a separate python-xml package. + This is also required. + + URL: http://www.python.org/ + Debian: python, python-xml + + * For optional SSL support, pyOpenSSL: + URL: http://pyopenssl.sourceforge.net/ + Debian: python-pyopenssl + + * For optional PAM support, PyPAM: + URL: http://www.pangalactic.org/PyPAM/ + Debian: python-pam + + * For optional XenAPI support in XM, PyXML: + URL: http://pyxml.sourceforge.net + YUM: PyXML + + +Intel(R) Trusted Execution Technology Support +============================================= + +Intel's technology for safer computing, Intel(R) Trusted Execution Technology +(Intel(R) TXT), defines platform-level enhancements that provide the building +blocks for creating trusted platforms. For more information, see +http://www.intel.com/technology/security/. + +Intel(R) TXT support is provided by the Trusted Boot (tboot) module in +conjunction with minimal logic in the Xen hypervisor. + +Tboot is an open source, pre- kernel/VMM module that uses Intel(R) TXT to +perform a measured and verified launch of an OS kernel/VMM. + +The Trusted Boot module is available from +http://sourceforge.net/projects/tboot. This project hosts the code in a +mercurial repo at http://tboot.sourceforge.net/hg/tboot.hg and contains +tarballs of the source. Instructions in the tboot README describe how +to modify grub.conf to use tboot to launch Xen. + +There are optional targets as part of Xen's top-level makefile that will +download and build tboot: install-tboot, build-tboot, dist-tboot, clean-tboot. +These will download the latest tar file from the SourceForge site using wget, +then build/install/dist according to Xen's settings. diff --git a/buildconfigs/Rules.mk b/buildconfigs/Rules.mk new file mode 100644 index 0000000..ee61cf6 --- /dev/null +++ b/buildconfigs/Rules.mk @@ -0,0 +1,42 @@ + +include Config.mk + +export DESTDIR + +ALLKERNELS = $(patsubst buildconfigs/mk.%,%,$(wildcard buildconfigs/mk.*)) + +%-install: + $(MAKE) -f buildconfigs/mk.$* build + +%-dist: DESTDIR=$(DISTDIR)/install +%-dist: %-install + @: # do nothing + +# Legacy dist target +%-build: %-dist + @: # do nothing + +%-prep: DESTDIR=$(DISTDIR)/install +%-prep: + $(MAKE) -f buildconfigs/mk.$* prep + +%-config: DESTDIR=$(DISTDIR)/install +%-config: + $(MAKE) -f buildconfigs/mk.$* config + +%-delete: + $(MAKE) -f buildconfigs/mk.$* delete + +%-clean: + $(MAKE) -f buildconfigs/mk.$* clean + +%.patch: + $(MAKE) -f buildconfigs/mk.$* $@ + +%-mrproper: + $(MAKE) -f buildconfigs/mk.$*-xen mrproper + rm -rf pristine-$(*)* ref-$(*)* + rm -rf $*-xen.patch + +# never delete any intermediate files. +.SECONDARY: diff --git a/buildconfigs/enable-xen-config b/buildconfigs/enable-xen-config new file mode 100644 index 0000000..75f648e --- /dev/null +++ b/buildconfigs/enable-xen-config @@ -0,0 +1,48 @@ +#!/bin/sh + +set -e + +if [ $# -ne 1 ] ; then + echo "Usage $(basename $0) " 1>&2 + exit 1 +fi + +CONFIG=$1 + +setopt() +{ + OPTION=$1 + VALUE=$2 + + # First remove any existing instances of this option + sed -e "s/^# ${OPTION} is not set$//g ; s/^^{OPTION}=.$//g" -i "${CONFIG}" + + # Then append the new value + case ${VALUE} in + y|m) echo "${OPTION}=${VALUE}" >> "${CONFIG}" ;; + n) echo "# ${OPTION} is not set" >> "${CONFIG}" ;; + *) echo "Invalid value ${VALUE} for ${OPTION}" 1>&2 ; exit 1 ;; + esac +} + +setopt CONFIG_PARAVIRT y +setopt CONFIG_PARAVIRT_GUEST y +setopt CONFIG_XEN y +setopt CONFIG_VMI y +setopt CONFIG_KVM y +setopt CONFIG_KVM_INTEL y +setopt CONFIG_KVM_AMD y +setopt CONFIG_LGUEST n +setopt CONFIG_XEN_BLKDEV_FRONTEND y +setopt CONFIG_XEN_NETDEV_FRONTEND y +setopt CONFIG_HVC_XEN y +setopt CONFIG_NUMA n +setopt CONFIG_LOCALVERSION_AUTO n + +case ${XEN_TARGET_ARCH} in + x86_32) setopt CONFIG_64BIT n ;; + x86_64) setopt CONFIG_64BIT y ;; + *) ;; +esac + +exit 0 diff --git a/buildconfigs/interface.exclude b/buildconfigs/interface.exclude new file mode 100644 index 0000000..1df89a5 --- /dev/null +++ b/buildconfigs/interface.exclude @@ -0,0 +1,7 @@ +*.size +*.pyc +checker +checker.c +ia64.h +x86_32.h +x86_64.h diff --git a/buildconfigs/ketchup b/buildconfigs/ketchup new file mode 100644 index 0000000..8725f7d --- /dev/null +++ b/buildconfigs/ketchup @@ -0,0 +1,742 @@ +#!/usr/bin/python +# +# ketchup 0.9.8 +# http://selenic.com/ketchup/wiki +# +# Copyright 2004 Matt Mackall +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +# Usage: +# +# in an existing kernel directory, run: +# +# ketchup +# +# where version is a complete kernel version, or a branch name to grab +# the latest version +# +# You can override some variables by creating a ~/.ketchuprc file. +# The ~/.ketchuprc is just a Python script, eg. it might look like this: +# +# kernel_url = 'http://kernel.localdomain/pub/linux/kernel' +# archive = os.environ["HOME"] + '/tmp/ketchup-archive' +# gpg = '/weird/path/to/gpg' +# + +import re, sys, urllib, os, getopt, glob, shutil + +def error(*args): + sys.stderr.write("ketchup: ") + for a in args: + sys.stderr.write(str(a)) + sys.stderr.write("\n") + +def qprint(*args): + if not options["quiet"]: + sys.stdout.write(" ".join(map(str, args))) + sys.stdout.write("\n") + +def lprint(*args): + sys.stdout.write(" ".join(map(str, args))) + sys.stdout.write("\n") + + +def fancyopts(args, options, state, syntax=''): + long = [] + short = '' + map = {} + dt = {} + + def help(state, opt, arg, options = options, syntax = syntax): + lprint("Usage: ", syntax) + + for s, l, d, c in options: + opt = ' ' + if s: opt = opt + '-' + s + ' ' + if l: opt = opt + '--' + l + ' ' + if d: opt = opt + '(' + str(d) + ')' + lprint(opt) + if c: lprint(' %s' % c) + sys.exit(0) + + options = [('h', 'help', help, 'Show usage info')] + options + + for s, l, d, c in options: + map['-'+s] = map['--'+l]=l + state[l] = d + dt[l] = type(d) + if not d is None and not type(d) is type(help): s, l = s + ':', l + '=' + if s: short = short + s + if l: long.append(l) + + if os.environ.has_key("KETCHUP_OPTS"): + args = os.environ["KETCHUP_OPTS"].split() + args + + try: + opts, args = getopt.getopt(args, short, long) + except getopt.GetoptError: + help(state, None, args) + sys.exit(-1) + + for opt, arg in opts: + if dt[map[opt]] is type(help): state[map[opt]](state,map[opt],arg) + elif dt[map[opt]] is type(1): state[map[opt]] = int(arg) + elif dt[map[opt]] is type(''): state[map[opt]] = arg + elif dt[map[opt]] is type([]): state[map[opt]].append(arg) + elif dt[map[opt]] is type(None): state[map[opt]] = 1 + + return args + +# Default values +kernel_url = 'http://www.kernel.org/pub/linux/kernel' +archive = os.environ["HOME"] + "/.ketchup" +rename_prefix = 'linux-' +rename_with_localversion = False +wget = "/usr/bin/wget" +gpg = "/usr/bin/gpg" +precommand = postcommand = None +default_tree = None +local_trees = {} + +# Functions to parse version strings + +def tree(ver): + return float(re.match(r'(\d+\.\d+)', ver).group(1)) + +def rev(ver): + p = pre(ver) + r = int(re.match(r'\d+\.\d+\.(\d+)', ver).group(1)) + if p: r = r - 1 + return r + +def pre(ver): + try: return re.match(r'\d+\.\d+\.\d+(\.\d+)?-((rc|pre)\d+)', ver).group(2) + except: return None + +def post(ver): + try: return re.match(r'\d+\.\d+\.\d+\.(\d+)', ver).group(1) + except: return None + +def pretype(ver): + try: return re.match(r'\d+\.\d+\.\d+(\.\d+)?-((rc|pre)\d+)', ver).group(3) + except: return None + +def prenum(ver): + try: return int(re.match(r'\d+\.\d+\.\d+-((rc|pre)(\d+))', ver).group(3)) + except: return None + +def prebase(ver): + return re.match(r'(\d+\.\d+\.\d+((-(rc|pre)|\.)\d+)?)', ver).group(1) + +def revbase(ver): + return "%s.%s" % (tree(ver), rev(ver)) + +def base(ver): + v = revbase(ver) + if post(ver): v += "." + post(ver) + return v + +def forkname(ver): + try: return re.match(r'\d+.\d+.\d+(\.\d+)?(-(rc|pre)\d+)?(-(\w+?)\d+)?', + ver).group(5) + except: return None + +def forknum(ver): + try: return int( + re.match(r'\d+.\d+.\d+(\.\d+)?(-(rc|pre)\d+)?(-(\w+?)(\d+))?', + ver).group(6)) + except: return None + +def fork(ver): + try: return re.match(r'\d+.\d+.\d+(\.\d+)?(-(rc|pre)\d+)?(-(\w+))?', ver).group(4) + except: return None + +def get_ver(makefile): + """ Read the version information from the specified makefile """ + part = {} + parts = "VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION".split(' ') + m = open(makefile) + for l in m.readlines(): + for p in parts: + try: part[p] = re.match(r'%s\s*=\s*(\S+)' % p, l).group(1) + except: pass + + version = "%s.%s.%s" % tuple([part[p] for p in parts[:3]]) + version += part.get("EXTRAVERSION","") + return version + +def get_localversion(): + v = '' + + for name in glob.glob('localversion*'): + try: v += open(name).readline().strip() + except: pass + + try: + c = open('.config').read() + v += re.search(r'^CONFIG_LOCALVERSION="(.+)"', c, re.M).group(1) + except: pass + + return v + +def compare_ver(a, b): + """ + Compare kernel versions a and b + + Note that -pre and -rc versions sort before the version they modify, + -pre sorts before -rc, -bk, -git, and -mm, etc. sort alphabetically. + """ + if a == b: return 0 + + c = cmp(float(tree(a)), float(tree(b))) + if c: return c + c = cmp(rev(a), rev(b)) + if c: return c + c = cmp(int(post(a) or 0), int(post(b) or 0)) + if c: return c + c = cmp(pretype(a), pretype(b)) # pre sorts before rc + if c: return c + c = cmp(prenum(a), prenum(b)) + if c: return c + c = cmp(forkname(a), forkname(b)) + if c: return c + return cmp(forknum(a), forknum(b)) + +def last(url, pat="(.*/)"): + for l in urllib.urlopen(url).readlines(): + m = re.search('(?i)' % pat, l) + if m: n = m.group(1) + return n + +def latest_mm(url, pat): + url = kernel_url + '/people/akpm/patches/2.6/' + url += last(url) + part = last(url) + return part[:-1] + +def latest_ck(url, pat): + url = "http://ck.kolivas.org/patches/2.6/pre-releases/" + url += last(url) + part = last(url) + pre = part[:-1] + + url = "http://ck.kolivas.org/patches/2.6/" + url += last(url,"(2.6.*/)") + part = last(url) + rel = part[:-1] + + l = [pre, rel] + l.sort(compare_ver) + return l[-1] + +def latest_dir(url, pat): + """Find the latest link matching pat at url after sorting""" + p = [] + for l in urllib.urlopen(url).readlines(): + m = re.search('"%s"' % pat, l) + if m: p.append(m.group(1)) + + if not p: return None + + p.sort(compare_ver) + return p[-1] + +# mbligh is lazy and has a bunch of empty directories +def latest_mjb(url, pat): + url = kernel_url + '/people/mbligh/' + + # find the last Linus release and search backwards + l = [find_ver('2.6'), find_ver("2.6-pre")] + l.sort(compare_ver) + linus = l[-1] + + p = [] + for l in urllib.urlopen(url).readlines(): + m = re.search('"(2\.6\..*/)"', l) + if m: + v = m.group(1) + if compare_ver(v, linus) <= 0: + p.append(v) + + p.sort(compare_ver) + p.reverse() + + for ver in p: + mjb = latest_dir(url + ver, pat) + if mjb: return mjb + + return None + +def latest_26_tip(url, pat): + l = [find_ver('2.6'), find_ver('2.6-git'), find_ver('2.6-pre')] + l.sort(compare_ver) + return l[-1] + +def find_info(ver): + b = "%.1f" % tree(ver) + f = forkname(ver) + p = pre(ver) + + s = b + if f: + s = "%s-%s" % (b, f) + elif p: + s = "%s-pre" % b + + return version_info[s] + +def version_urls(ver): + """ Return the URL for the patch associated with the specified version """ + i = find_info(ver)[1] + if type(i) != type([]): + i = [i] + + v = { + 'full': ver, + 'tree': tree(ver), + 'base': base(ver), + 'prebase': prebase(ver) + } + + l = [] + for e in i: + l.append(e % v) + + return l + +def patch_path(ver): + return os.path.join(archive, os.path.basename(version_urls(ver)[0])) + +def download(url, f): + qprint("Downloading %s" % os.path.basename(url)) + if options["dry-run"]: + return 1 + + if not options["wget"]: + p = urllib.urlopen(url).read() + if p.find("404") != -1: + return None + open(f, 'w').write(p) + else: + e = os.system("%s -c -O %s %s" % + (options["wget"], f + ".partial", url)) + if e: + return None + os.rename(f + ".partial", f) + + return 1 + +def verify(url, f, sign): + if options["no-gpg"] or options["dry-run"] or not options["gpg-path"]: + return 1 + + sf = f + sign + if not download(url + sign, sf): + error("signature download failed") + error("removing files...") + os.unlink(f) + return 0 + + qprint("Verifying signature...") + r = os.system("%s --verify %s %s" % (options["gpg-path"], sf, f)) + if r: + error("gpg returned %d" % r) + error("removing files...") + os.unlink(f) + os.unlink(sf) + return 0 + + return 1 + +def trydownload(urls, f, sign): + for url in urls: + if download(url, f): + if not sign or verify(url, f, sign): + return f + if url[-4:] == ".bz2": + f2 = f[:-4] + ".gz" + url2 = url[:-4] + ".gz" + if download(url2, f2): + if not sign or verify(url2, f2, sign): + return f2 + return None + +def get_patch(ver): + """Return the path to patch for given ver, downloading if necessary""" + f = patch_path(ver) + if os.path.exists(f): + return f + if f[-4:] == ".bz2": + f2 = f[:-4] + ".gz" + if os.path.exists(f2): + return f2 + + urls = version_urls(ver) + sign = find_info(ver)[3] + if sign == 1: sign = ".sign" + f = trydownload(urls, f, sign) + if not f: + error("patch download failed") + sys.exit(-1) + + return f + +def apply_patch(ver, reverse = 0): + """Find the patch to upgrade from the predecessor of ver to ver and + apply or reverse it.""" + p = get_patch(ver) + r = "" + if reverse: + r = " -R" + + qprint("Applying %s%s" % (os.path.basename(p), r)) + if options["dry-run"]: + return ver + + def cmd(patch, reverse, dry): + base = "patch -l -p1%s" % reverse + if dry: + base += " --dry-run" + + if p[-4:] == ".bz2": + pipe = "bzcat %s | %s" % (patch, base) + elif p[-3:] == ".gz": + pipe = "zcat %s | %s" % (patch, base) + else: + pipe = "%s < %s" % (base, patch) + + err = os.system(pipe + " > .patchdiag") + if err: + sys.stderr.write(open(".patchdiag").read()) + os.unlink(".patchdiag") + return err + + err = cmd(p, r, 1) + if err: + error("patch %s failed: %d" % (p, err)) + sys.exit(-1) + + err = cmd(p, r, 0) + if err: + error("patch %s failed while it was supposed to apply: %d" % (p, err)) + sys.exit(-1) + +def untar(tarfile): + old = os.getcwd() + os.mkdir("ketchup-tmp") + os.chdir("ketchup-tmp") + + err = os.system("bzcat %s | tar -xf -" % tarfile) + if err: + error("Unpacking failed: ", err) + sys.exit(-1) + + err = os.system("mv linux*/* linux*/.[^.]* ..; rmdir linux*") + if err: + error("Unpacking failed: ", err) + sys.exit(-1) + + os.chdir(old) + shutil.rmtree("ketchup-tmp") + +def install_nearest(ver): + t = tree(ver) + tarballs = glob.glob(archive + "/linux-%s.*.tar.bz2" % t) + list = [] + + for f in tarballs: + m = re.match(r'.*/linux-(.*).tar.bz2$', f) + v = m.group(1) + d = abs(rev(v) - rev(ver)) + list.append((d, f, v)) + list.sort() + + if not list or (options["full-tarball"] and list[0][0]): + f = "linux-%s.tar.bz2" % ver + url = "%s/v%s/%s" % (kernel_url, t, f) + f = archive + "/" + f + + sign = find_info(ver)[3] + if sign == 1: sign = ".sign" + + f = trydownload([url], f, sign) + if not f: + error("Tarball download failed") + sys.exit(-1) + + else: + f = list[0][1] + ver = list[0][2] + + qprint("Unpacking %s" % os.path.basename(f)) + if options["dry-run"]: return ver + untar(f) + + return ver + +def find_ver(ver): + if ver in version_info.keys(): + v = version_info[ver] + d = v[1] + if type(d) is type([]): + d = d[0] + for n in range(5): + return v[0](os.path.dirname(d), v[2]) + error('retrying version lookup for %s' % ver) + else: + return ver + +def transform(a, b): + if a == b: + qprint("Nothing to do!") + return + if not a: + a = install_nearest(base(b)) + t = tree(a) + if t != tree(b): + error("Can't patch %s to %s" % (tree(a), tree(b))) + sys.exit(-1) + if fork(a): + apply_patch(a, 1) + a = prebase(a) + if prebase(a) != prebase(b): + if pre(a): + apply_patch(a, 1) + a = base(a) + + if post(a) and post(a) != post(b): + apply_patch(prebase(a), 1) + + ra, rb = rev(a), rev(b) + if ra > rb: + for r in range(ra, rb, -1): + apply_patch("%s.%s" % (t, r), -1) + if ra < rb: + for r in range(ra + 1, rb + 1): + apply_patch("%s.%s" % (t, r)) + a = revbase(b) + + if post(b) and post(a) != post(b): + apply_patch(prebase(b), 0) + a = base(b) + + if pre(b): + apply_patch(prebase(b)) + a = prebase(b) + + if fork(b): + a = apply_patch(b) + +def rename_dir(v): + """Rename the current directory to linux-v, where v is the function arg""" + if rename_with_localversion: + v += get_localversion() + cwd = os.getcwd() + basedir = os.path.dirname(cwd) + newdir = os.path.join(basedir, rename_prefix + v) + if newdir == cwd: + return + if os.access(newdir, os.F_OK): + error("Cannot rename directory, destination exists: %s", newdir); + return + os.rename(cwd, newdir) + qprint('Current directory renamed to %s' % newdir) + + +# latest lookup function, canonical urls, pattern for lookup function, +# signature flag, description +version_info = { + '2.4': (latest_dir, + kernel_url + "/v2.4" + "/patch-%(base)s.bz2", + r'patch-(.*?).bz2', + 1, "old stable kernel series"), + '2.4-pre': (latest_dir, + kernel_url + "/v2.4" + "/testing/patch-%(prebase)s.bz2", + r'patch-(.*?).bz2', + 1, "old stable kernel series prereleases"), + '2.6': (latest_dir, + kernel_url + "/v2.6" + "/patch-%(prebase)s.bz2", + r'patch-(.*?).bz2', + 1, "current stable kernel series"), + '2.6-rc': (latest_dir, + kernel_url + "/v2.6" + "/testing/patch-%(prebase)s.bz2", + r'patch-(.*?).bz2', + 1, "current stable kernel series prereleases"), + '2.6-pre': (latest_dir, + kernel_url + "/v2.6" + "/testing/patch-%(prebase)s.bz2", + r'patch-(.*?).bz2', + 1, "current stable kernel series prereleases"), + '2.6-git': (latest_dir, + [kernel_url + "/v2.6" + "/snapshots/patch-%(full)s.bz2", + kernel_url + "/v2.6" + "/snapshots/old/patch-%(full)s.bz2"], + r'patch-(.*?).bz2', + 1, "current stable kernel series snapshots"), + '2.6-bk': (latest_dir, + [kernel_url + "/v2.6" + "/snapshots/patch-%(full)s.bz2", + kernel_url + "/v2.6" + "/snapshots/old/patch-%(full)s.bz2"], + r'patch-(.*?).bz2', + 1, "old stable kernel series snapshots"), + '2.6-tip': (latest_26_tip, "", "", 1, + "current stable kernel series tip"), + '2.6-mm': (latest_mm, + kernel_url + "/people/akpm/patches/" + + "%(tree)s/%(prebase)s/%(full)s/%(full)s.bz2", "", + 1, "Andrew Morton's -mm development tree"), + '2.6-tiny': (latest_dir, + "http://www.selenic.com/tiny/%(full)s.patch.bz2", + r'(2.6.*?).patch.bz2', + 1, "Matt Mackall's -tiny tree for small systems"), + '2.6-mjb': (latest_mjb, + kernel_url + "/people/mbligh/%(prebase)s/patch-%(full)s.bz2", + r'patch-(2.6.*?).bz2', + 1, "Martin Bligh's random collection 'o crap"), + '2.6-rt': (latest_dir, + ["http://people.redhat.com/mingo/" + + "realtime-preempt/patch-%(full)s", + "http://people.redhat.com/mingo/" + + "realtime-preempt/older/patch-%(full)s"], + r'patch-(2.6.*?)', + 0, "Ingo Molnar's realtime-preempt kernel"), + '2.6-ck': (latest_ck, + ["http://ck.kolivas.org/patches/2.6/" + + "%(prebase)s/%(full)s/patch-%(full)s.bz2", + "http://ck.kolivas.org/patches/2.6/pre-releases/" + + "%(prebase)s/%(full)s/patch-%(full)s.bz2"], + "", ".sig", + "Con Kolivas' patches for system responsiveness (desktop)"), + '2.6-cks': (latest_dir, + "http://ck.kolivas.org/patches/cks/patch-%(full)s.bz2", + r'patch-(2.6.*?).bz2', ".sig", + "Con Kolivas' patches for system responsiveness (server)") + } + +# Override defaults with ~/.ketchuprc which is just a Python script +rcpath = os.path.expanduser('~/.ketchuprc') +if os.path.isfile(rcpath): + try: + execfile(rcpath) + except Exception, e: + sys.exit('Failed parsing %s\nError was: %s' % (rcpath, e)) + +# Add local trees +for k,v in local_trees.items(): + version_info[k] = v + +# Environment variables override defaults and ketchuprc +kernel_url = os.environ.get("KETCHUP_URL", kernel_url) +archive = os.environ.get("KETCHUP_ARCH", archive) + +# And finally command line overrides everything +if not os.path.exists(wget): wget = "" +if not os.path.exists(gpg): gpg = "" + +options = {} +opts = [ + ('a', 'archive', archive, 'cache directory'), + ('d', 'directory', '.', 'directory to update'), + ('f', 'full-tarball', None, 'if unpacking a tarball, download the latest'), + ('g', 'gpg-path', gpg, 'path for GnuPG'), + ('G', 'no-gpg', None, 'disable GPG signature verification'), + ('k', 'kernel-url', kernel_url, 'base url for kernel.org mirror'), + ('l', 'list-trees', None, 'list supported trees'), + ('m', 'show-makefile', None, 'output version in makefile <arg>'), + ('n', 'dry-run', None, 'don\'t download or apply patches'), + ('p', 'show-previous', None, 'output version previous to <arg>'), + ('q', 'quiet', None, 'reduce output'), + ('r', 'rename-directory', None, 'rename updated directory to %s<v>' + % rename_prefix), + ('s', 'show-latest', None, 'output the latest version of <arg>'), + ('u', 'show-url', None, 'output URL for <arg>'), + ('w', 'wget', wget, 'command to use for wget'), + ] + +args = fancyopts(sys.argv[1:], opts, options, + 'ketchup [options] [ver]') + +archive = options["archive"] +kernel_url = options["kernel-url"] +if options["no-gpg"]: options["gpg-path"] = '' + +# Process args + +if not os.path.exists(options["directory"]): + qprint("Creating target directory", options["directory"]) + os.mkdir(options["directory"]) +os.chdir(options["directory"]) + +if os.path.isfile(".ketchuprc"): + try: + execfile(".ketchuprc") + except Exception, e: + sys.exit('Failed parsing .ketchuprc\nError was: %s' % (e)) + +if options["list-trees"]: + l = version_info.keys() + l.sort() + for tree in l: + if version_info[tree][3] == 0: + lprint(tree, "(unsigned)") + else: + lprint(tree, "(signed)") + lprint(" " + version_info[tree][4]) + sys.exit(0) + +if options["show-makefile"] and len(args) < 2: + if not args: + lprint(get_ver("Makefile")) + else: + lprint(get_ver(args[0])) + sys.exit(0) + +if len(args) == 0 and default_tree: + qprint("Using default tree \"%s\"" % (default_tree)) + args.append(default_tree) + +if len(args) != 1: + error("No version given on command line and no default in configuration") + sys.exit(-1) + +if options["show-latest"]: + lprint(find_ver(args[0])) + sys.exit(0) + +if options["show-url"]: + lprint(version_urls(find_ver(args[0]))[0]) + sys.exit(0) + +if options["show-previous"]: + v = find_ver(args[0]) + p = prebase(v) + if p == v: p = base(v) + if p == v: + if rev(v) > 0: p = "%.1f.%s" % (tree(v), rev(v) -1) + else: p = "unknown" + lprint(p) + sys.exit(0) + +if not os.path.exists(options["archive"]): + qprint("Creating cache directory", options["archive"]) + os.mkdir(options["archive"]) + +if precommand and os.system(precommand): + sys.exit('Precommand "%s" failed!' % precommand) + +try: + a = get_ver('Makefile') +except: + a = None + +if not a and os.listdir("."): + error("Can't find kernel version for non-empty directory") + sys.exit(-1) + +b = find_ver(args[0]) +qprint("%s -> %s" % (a, b)) +transform(a, b) +if options["rename-directory"] and not options["dry-run"]: + rename_dir(b) + +if postcommand and os.system(postcommand): + sys.exit('Postcommand "%s" failed!' % postcommand) diff --git a/buildconfigs/mk.linux-2.6 b/buildconfigs/mk.linux-2.6 new file mode 100644 index 0000000..6b8d989 --- /dev/null +++ b/buildconfigs/mk.linux-2.6 @@ -0,0 +1,10 @@ +XEN_LINUX_SOURCE ?= tarball +LINUX_VER ?= 2.6 + +IMAGE_TARGET ?= vmlinux bzImage + +XEN_LINUX_CONFIG_UPDATE := buildconfigs/enable-xen-config + +EXTRAVERSION ?= + +include buildconfigs/mk.linux-2.6-common diff --git a/buildconfigs/mk.linux-2.6-common b/buildconfigs/mk.linux-2.6-common new file mode 100644 index 0000000..6561e4e --- /dev/null +++ b/buildconfigs/mk.linux-2.6-common @@ -0,0 +1,154 @@ +LINUX_SERIES = 2.6 + +# Linux search path, will be searched for tarballs and mercurial +# repositories. +LINUX_SRC_PATH ?= .:.. + +# The source directory is not automatically updated to avoid blowing +# away developer's changes. If you want to automatically pull a new +# version of the Linux tree then add `XEN_LINUX_UPDATE=y' to your make +# command line. +ifeq ($(XEN_LINUX_UPDATE),y) +__XEN_LINUX_UPDATE = $(LINUX_SRCDIR)/.force-update +else +__XEN_LINUX_UPDATE = +endif + +ifeq ($(XEN_LINUX_NONINTERACTIVE_CONFIG),y) +__NONINT_CONFIG = yes $$'\n' | +else +__NONINT_CONFIG = +endif + +LINUX_DIR = build-linux-$(LINUX_VER)$(EXTRAVERSION)_$(XEN_TARGET_ARCH) + +IMAGE_TARGET ?= vmlinuz + +LINUX_VER3 := $(LINUX_SERIES).$(word 3, $(subst ., ,$(LINUX_VER))) + +.PHONY: _build +_build: build + +include buildconfigs/src.$(XEN_LINUX_SOURCE) + +LINUX_ARCH = $$(sh buildconfigs/select-linux-arch $(LINUX_SRCDIR)) +IMAGE_PATH = $$(sh buildconfigs/select-linux-image $(LINUX_DIR) $(LINUX_ARCH) $(IMAGE_TARGET)) + +INSTALL_BOOT_PATH := $(DESTDIR)/boot + +# Default to allowing interface mismatch +ifndef XEN_LINUX_ALLOW_INTERFACE_MISMATCH +XEN_LINUX_ALLOW_INTERFACE_MISMATCH := y +endif + +KERNELRELEASE = $(shell $(MAKE) -s --no-print-directory -C $(LINUX_DIR) kernelrelease) + +# The real action starts here! +.PHONY: build +build: $(LINUX_DIR)/include/linux/autoconf.h +ifneq ($(XEN_LINUX_ALLOW_INTERFACE_MISMATCH),y) + @if ! diff -urN -X buildconfigs/interface.exclude \ + $(LINUX_SRCDIR)/include/xen/interface xen/include/public ; then \ + echo "" 1>&2 ; \ + echo " *** $(LINUX_SRCDIR)/include/xen/interface is out of date " 1>&2 ; \ + echo " *** relative to $(XEN_ROOT)/xen/include/public." 1>&2 ; \ + echo "" 1>&2 ; \ + exit 1 ; \ + fi +endif + if grep "^CONFIG_MODULES=" $(LINUX_DIR)/.config ; then \ + $(MAKE) -C $(LINUX_DIR) ARCH=$(LINUX_ARCH) modules || exit 1 ; \ + $(MAKE) -C $(LINUX_DIR) ARCH=$(LINUX_ARCH) INSTALL_MOD_PATH=$(DESTDIR) modules_install ; \ + fi + $(MAKE) -C $(LINUX_DIR) ARCH=$(LINUX_ARCH) INSTALL_PATH=$(DESTDIR) $(IMAGE_TARGET) + mkdir -p $(INSTALL_BOOT_PATH) + @cp -v $(IMAGE_PATH) $(INSTALL_BOOT_PATH)/vmlinuz-$(KERNELRELEASE) + @cp -v $(LINUX_DIR)/.config $(INSTALL_BOOT_PATH)/config-$(KERNELRELEASE) + @cp -v $(LINUX_DIR)/System.map $(INSTALL_BOOT_PATH)/System.map-$(KERNELRELEASE) + +$(LINUX_DIR)/include/linux/autoconf.h: CONFIG_FILE=$(CURDIR)/$(LINUX_DIR)/.config +$(LINUX_DIR)/include/linux/autoconf.h: $(LINUX_SRCDIR)/.valid-src + rm -rf $(LINUX_DIR) + mkdir -p $(LINUX_DIR) + # Re-use config from install dir if one exists. Next try to use + # buildconfigs/create_config.sh is one is provided by the source + # tree. Finally attempt to use make defconfig. + set -e ; \ + CONFIG_VERSION=$$(sed -ne 's/$$(XENGUEST)//; s/^EXTRAVERSION = //p' $(LINUX_SRCDIR)/Makefile); \ + if [ ! -z "$(XEN_LINUX_CONFIG)" -a -r $(XEN_LINUX_CONFIG) ]; then \ + cp $(XEN_LINUX_CONFIG) $(CONFIG_FILE); \ + elif [ -r $(DESTDIR)/boot/config-$(LINUX_VER3)$$CONFIG_VERSION$(EXTRAVERSION) ] ; then \ + cp $(DESTDIR)/boot/config-$(LINUX_VER3)$$CONFIG_VERSION$(EXTRAVERSION) $(CONFIG_FILE) ; \ + elif [ -e $(LINUX_SRCDIR)/buildconfigs/create_config.sh ] ; then \ + cd $(LINUX_SRCDIR) && sh buildconfigs/create_config.sh \ + $(CONFIG_FILE) $(EXTRAVERSION) $(XEN_TARGET_ARCH) $(XEN_SYSTYPE) ; \ + echo "Configured $(LINUX_DIR) using create_config.sh" ; \ + elif $(MAKE) -C $(LINUX_SRCDIR) ARCH=$(LINUX_ARCH) defconfig O=$$(/bin/pwd)/$(LINUX_DIR) ; then \ + echo "Configured $(LINUX_DIR) using defconfig" ; \ + else \ + echo "No configuration method found for this kernel" ; \ + fi +ifneq ($(XEN_LINUX_CONFIG_UPDATE),) + echo "Updating $(CONFIG_FILE) using $(XEN_LINUX_CONFIG_UPDATE)" + sh $(XEN_LINUX_CONFIG_UPDATE) $(CONFIG_FILE) +endif +ifeq ($(XEN_TARGET_ARCH),x86_32) + sed -e 's!^CONFIG_HIGHMEM4G=y$$!\# CONFIG_HIGHMEM4G is not set!;s!^\# CONFIG_HIGHMEM64G is not set$$!CONFIG_HIGHMEM64G=y!' $(CONFIG_FILE) > $(CONFIG_FILE)- && mv $(CONFIG_FILE)- $(CONFIG_FILE) +endif +ifneq ($(EXTRAVERSION),) + echo "$(EXTRAVERSION)" >$(LINUX_DIR)/localversion-xen +endif + $(__NONINT_CONFIG) $(MAKE) -C $(LINUX_SRCDIR) ARCH=$(LINUX_ARCH) oldconfig O=$$(/bin/pwd)/$(LINUX_DIR) + @set -e ; if [ ! -f $(LINUX_DIR)/Makefile ] ; then \ + echo "***********************************"; \ + echo "oldconfig did not create a Makefile"; \ + echo "Generating $(LINUX_DIR)/Makefile "; \ + echo "***********************************"; \ + ( echo "# Automatically generated: don't edit"; \ + echo ""; \ + echo "VERSION = 2"; \ + echo "PATCHLEVEL = 6"; \ + echo ""; \ + echo "KERNELSRC := $(CURDIR)/$(LINUX_SRCDIR)"; \ + echo "KERNELOUTPUT := $(CURDIR)/$(LINUX_DIR)"; \ + echo ""; \ + echo "MAKEFLAGS += --no-print-directory"; \ + echo ""; \ + echo ".PHONY: all \$$(MAKECMDGOALS)"; \ + echo ""; \ + echo "all:"; \ + echo " \$$(MAKE) -C \$$(KERNELSRC) O=\$$(KERNELOUTPUT)"; \ + echo ""; \ + echo "Makefile:;"; \ + echo ""; \ + echo "\$$(filter-out all Makefile,\$$(MAKECMDGOALS)) %/:"; \ + echo " \$$(MAKE) -C \$$(KERNELSRC) O=\$$(KERNELOUTPUT) \$$@"; \ + ) > $(LINUX_DIR)/Makefile ; \ + fi + $(MAKE) -C $(LINUX_DIR) ARCH=$(LINUX_ARCH) prepare + +.PHONY: prep +prep: $(LINUX_DIR)/include/linux/autoconf.h + +.PHONY: config +config: CONFIGMODE = menuconfig +config: $(LINUX_DIR)/include/linux/autoconf.h + $(MAKE) -C $(LINUX_DIR) ARCH=$(LINUX_ARCH) $(CONFIGMODE) + +.PHONY: clean +clean:: + [ ! -d $(LINUX_DIR) ] || \ + $(MAKE) -C $(LINUX_DIR) ARCH=$(LINUX_ARCH) clean + +.PHONY: delete +delete: + rm -rf tmp-linux-$(LINUX_VER) $(LINUX_DIR) $(LINUX_SRCDIR) + +.PHONY: mrproper +mrproper: + rm -rf $(LINUX_SRCDIR) + rm -f linux-$(LINUX_VER).tar.bz2 + +.PHONY: $(LINUX_SRCDIR)/.force-update +$(LINUX_SRCDIR)/.force-update: + @ : diff --git a/buildconfigs/mk.linux-2.6-git b/buildconfigs/mk.linux-2.6-git new file mode 100644 index 0000000..1a142dd --- /dev/null +++ b/buildconfigs/mk.linux-2.6-git @@ -0,0 +1,2 @@ +LINUX_VER ?= 2.6-git +include buildconfigs/mk.linux-2.6 diff --git a/buildconfigs/mk.linux-2.6-mm b/buildconfigs/mk.linux-2.6-mm new file mode 100644 index 0000000..f02e864 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-mm @@ -0,0 +1,2 @@ +LINUX_VER ?= 2.6-mm +include buildconfigs/mk.linux-2.6 diff --git a/buildconfigs/mk.linux-2.6-native b/buildconfigs/mk.linux-2.6-native new file mode 100644 index 0000000..c7c0949 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-native @@ -0,0 +1,5 @@ +EXTRAVERSION = -native +IMAGE_TARGET = bzImage +INSTALL_BOOT_PATH = $(DESTDIR)/boot + +include buildconfigs/mk.linux-2.6-common diff --git a/buildconfigs/mk.linux-2.6-rc b/buildconfigs/mk.linux-2.6-rc new file mode 100644 index 0000000..34f4513 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-rc @@ -0,0 +1,2 @@ +LINUX_VER ?= 2.6-rc +include buildconfigs/mk.linux-2.6 diff --git a/buildconfigs/mk.linux-2.6-tip b/buildconfigs/mk.linux-2.6-tip new file mode 100644 index 0000000..0588ad2 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-tip @@ -0,0 +1,2 @@ +LINUX_VER ?= 2.6-tip +include buildconfigs/mk.linux-2.6 diff --git a/buildconfigs/mk.linux-2.6-tip-latest b/buildconfigs/mk.linux-2.6-tip-latest new file mode 100644 index 0000000..78a9ecb --- /dev/null +++ b/buildconfigs/mk.linux-2.6-tip-latest @@ -0,0 +1,14 @@ +XEN_LINUX_SOURCE ?= git-clone +LINUX_VER ?= 2.6-x86-latest + +IMAGE_TARGET ?= bzImage vmlinux + +XEN_LINUX_CONFIG_UPDATE := buildconfigs/enable-xen-config + +XEN_LINUX_GIT_URL ?= git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-tip.git +XEN_LINUX_GIT_REMOTENAME ?= x86 +XEN_LINUX_GIT_REMOTEBRANCH ?= auto-latest + +EXTRAVERSION ?= + +include buildconfigs/mk.linux-2.6-common diff --git a/buildconfigs/mk.linux-2.6-xen b/buildconfigs/mk.linux-2.6-xen new file mode 100644 index 0000000..8594b55 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-xen @@ -0,0 +1,6 @@ +EXTRAVERSION ?= -xen +LINUX_VER ?= 2.6.18 + +XEN_LINUX_SOURCE ?= hg-clone + +include buildconfigs/mk.linux-2.6-common diff --git a/buildconfigs/mk.linux-2.6-xen0 b/buildconfigs/mk.linux-2.6-xen0 new file mode 100644 index 0000000..96a5712 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-xen0 @@ -0,0 +1,2 @@ +EXTRAVERSION = -xen0 +include buildconfigs/mk.linux-2.6-xen diff --git a/buildconfigs/mk.linux-2.6-xenU b/buildconfigs/mk.linux-2.6-xenU new file mode 100644 index 0000000..02f3e07 --- /dev/null +++ b/buildconfigs/mk.linux-2.6-xenU @@ -0,0 +1,2 @@ +EXTRAVERSION = -xenU +include buildconfigs/mk.linux-2.6-xen diff --git a/buildconfigs/mk.linux-2.6.5-SLES-xen b/buildconfigs/mk.linux-2.6.5-SLES-xen new file mode 100644 index 0000000..b27e077 --- /dev/null +++ b/buildconfigs/mk.linux-2.6.5-SLES-xen @@ -0,0 +1,10 @@ +XEN_TARGET_ARCH = x86_32 + +EXTRAVERSION = -xen +LINUX_VER = 2.6.5-SLES + +XEN_LINUX_SOURCE = hg-clone +XEN_LINUX_HGREPO ?= http://xenbits.xensource.com/kernels/sles9x.hg +XEN_LINUX_HGREV ?= tip + +include buildconfigs/mk.linux-2.6-xen diff --git a/buildconfigs/mk.linux-2.6.9-RHEL-xen b/buildconfigs/mk.linux-2.6.9-RHEL-xen new file mode 100644 index 0000000..ac830e5 --- /dev/null +++ b/buildconfigs/mk.linux-2.6.9-RHEL-xen @@ -0,0 +1,10 @@ +XEN_TARGET_ARCH = x86_32 + +EXTRAVERSION = -xen +LINUX_VER = 2.6.9-RHEL + +XEN_LINUX_SOURCE = hg-clone +XEN_LINUX_HGREPO ?= http://xenbits.xensource.com/kernels/rhel4x.hg +XEN_LINUX_HGREV ?= tip + +include buildconfigs/mk.linux-2.6-xen diff --git a/buildconfigs/select-linux-arch b/buildconfigs/select-linux-arch new file mode 100755 index 0000000..a5d0856 --- /dev/null +++ b/buildconfigs/select-linux-arch @@ -0,0 +1,30 @@ +#!/bin/sh + +ME=$(basename $0) + +if [ $# -lt 1 ] || [ $# -gt 2 ] ; then + echo "usage: $ME <linux-build-directory>" 1>&2 + exit 1; +fi + +LINUX_DIR=$1 + +case ${XEN_TARGET_ARCH} in + x86_32|x86_64) + if [ -d ${LINUX_DIR}/arch/x86 ] ; then + ARCH=x86 + elif [ "${XEN_TARGET_ARCH}" = "x86_32" ] ; then + ARCH=i386 + else + ARCH=x86_64 + fi + ;; + *) + ARCH=${XEN_TARGET_ARCH} + ;; +esac + +echo "$ME: ${ARCH}" 1>&2 +echo ${ARCH} + +exit 0 diff --git a/buildconfigs/select-linux-image b/buildconfigs/select-linux-image new file mode 100755 index 0000000..07899f8 --- /dev/null +++ b/buildconfigs/select-linux-image @@ -0,0 +1,33 @@ +#!/bin/sh + +ME=$(basename $0) + +if [ $# -lt 3 ] ; then + echo "usage: $ME <linux-build-directory> <linux-arch> <linux-targets...>" 1>&2 + exit 1; +fi + +LINUX_DIR=$1 +LINUX_ARCH=$2 +LINUX_TARGET=$3 # We don't care about second and subsequent targets + +case ${XEN_TARGET_ARCH} in + ia64) + IMAGE=${LINUX_DIR}/arch/ia64/hp/sim/boot/vmlinux.gz + ;; + *) + if [ -f ${LINUX_DIR}/arch/${LINUX_ARCH}/boot/${LINUX_TARGET} ] ; then + IMAGE=${LINUX_DIR}/arch/${LINUX_ARCH}/boot/${LINUX_TARGET} + elif [ -f ${LINUX_DIR}/${LINUX_TARGET} ] ; then + IMAGE=${LINUX_DIR}/${LINUX_TARGET} + else + echo "$ME: cannot determine Linux image to use for ${LINUX_ARCH} in ${LINUX_DIR}" 1>&2 + exit 1 + fi + ;; +esac + +echo "$ME: ${IMAGE}" 1>&2 +echo ${IMAGE} + +exit 0 diff --git a/buildconfigs/select-repository b/buildconfigs/select-repository new file mode 100755 index 0000000..e5bf27e --- /dev/null +++ b/buildconfigs/select-repository @@ -0,0 +1,69 @@ +#!/bin/sh + +ME=$(basename $0) + +if [ $# -lt 1 ] || [ $# -gt 2 ] ; then + echo "usage: $ME <repository-name> [search-path]" 1>&2 + exit 1; +fi + +REPO=$1 +LINUX_SRC_PATH=$2 + +if [ X"${LINUX_SRC_PATH}" != X ] ; then + echo "$ME: Searching \`${LINUX_SRC_PATH}' for $REPO" 1>&2 + IFS_saved="$IFS" + IFS=: + for i in $LINUX_SRC_PATH ; do + # Ignore current directory since we will almost certainly find + # the target directory there which breaks updating (there's no + # point updating from yourself!). + if [ X"." = X"${i}" ] ; then + echo "$ME: Ignoring \`.'" 1>&2 + continue + fi + + if [ -d "$i/$REPO/.hg" ] ; then + echo "$ME: Found $i/$REPO" 1>&2 + echo "$i/$REPO" + exit 0 + fi + done + IFS="$IFS_saved" +fi + +if [ -d ${XEN_ROOT}/.hgxxx ] ; then + XEN=$(hg -R ${XEN_ROOT} path default) + if [ $? -ne 0 ] || [ X"$XEN" = "X" ] ; then + echo "$ME: Unable to determine Xen repository parent." 1>&2 + exit 1; + fi + + BASE=$(dirname ${XEN}) + if [ $? -ne 0 ] || [ X"$BASE" = "X" ] ; then + echo "$ME: Unable to determine Xen repository base." 1>&2 + exit 1; + fi + if [ -d "$XEN" ] && [ ! -d "$BASE/$REPO" ] ; then + echo "$ME: No such dir: $BASE/$REPO" 1>&2 + exit 1 + fi + + echo "$ME: Found ${BASE}/${REPO}" 1>&2 + + # If ${BASE}/${REPO} is a local directory then prepend file:// so that + # the test in src.hg-clone will fail and we will clone instead of + # linking this repository. We only want to link repositories which + # were found via LINUX_SRC_PATH. + if [ -d "${BASE}/${REPO}" ] ; then + echo "file://${BASE}/${REPO}" + else + echo ${BASE}/${REPO} + fi +else + echo "Unable to determine path to Linux source tree." 1>&2 + echo "Falling back to linux-2.6.18-xen Mercurial repository." 1>&2 + echo http://xenbits.xensource.com/linux-2.6.18-xen.hg +fi + +exit 0 diff --git a/buildconfigs/src.git-clone b/buildconfigs/src.git-clone new file mode 100644 index 0000000..722d45a --- /dev/null +++ b/buildconfigs/src.git-clone @@ -0,0 +1,60 @@ +# Mercurial +GIT ?= git + +LINUX_SRCDIR ?= linux-$(LINUX_VER).git + +# The URL of the remote GIT repository +ifeq ($(XEN_LINUX_GIT_URL),) +.ERROR: XEN_LINUX_GIT_URL not specified +endif + +# The name to use for the remote repository +XEN_LINUX_GIT_REMOTENAME ?= origin + +# The branch in the remote repository +ifeq ($(XEN_LINUX_GIT_REMOTEBRANCH),) +.ERROR: XEN_LINUX_GIT_REMOTEBRANCH not specified +endif + +XEN_LINUX_GIT_LOCALBRANCH ?= master + +# Set XEN_LINUX_GITREV to update to a particlar revision. +XEN_LINUX_GITREV ?= + +$(LINUX_SRCDIR)/.valid-src: $(__XEN_LINUX_UPDATE) + @set -e ; if [ -d $(LINUX_SRCDIR) ] && [ ! -d $(GIT_DIR) ] ; then \ + echo "$(LINUX_SRCDIR) exists but is not a git repository." 1>&2 ; \ + false ; \ + fi + + @set -e ; if [ ! -e $(LINUX_SRCDIR)/.git ] ; then \ + mkdir $(LINUX_SRCDIR) ; \ + cd $(LINUX_SRCDIR) ; \ + $(GIT) init-db ; \ + fi + + @set -e ; cd $(LINUX_SRCDIR) ; \ + if ! $(GIT) remote | grep -q $(XEN_LINUX_GIT_REMOTENAME) ; then \ + echo "Adding remote git repository \`$(XEN_LINUX_GIT_URL)' as \`$(XEN_LINUX_GIT_REMOTENAME)'" ; \ + $(GIT) remote add $(XEN_LINUX_GIT_REMOTENAME) $(XEN_LINUX_GIT_URL) ; \ + fi + + @echo "Updating remote \`$(XEN_LINUX_GIT_REMOTENAME)'" + @cd $(LINUX_SRCDIR) && $(GIT) fetch $(XEN_LINUX_GIT_REMOTENAME) + + @set -e ; cd $(LINUX_SRCDIR) ; \ + if ! $(GIT) branch -l | grep -q $(XEN_LINUX_GIT_LOCALBRANCH) ; then \ + $(GIT) branch --track $(XEN_LINUX_GIT_LOCALBRANCH) \ + $(XEN_LINUX_GIT_REMOTENAME)/$(XEN_LINUX_GIT_REMOTEBRANCH) ; \ + $(GIT) checkout ; \ + fi + + @ set -e ; cd $(LINUX_SRCDIR) ; \ + if [ -n "$(XEN_LINUX_GITREV)" ] ; then \ + echo "Updating $(LINUX_SRCDIR) to revision \'$(XEN_LINUX_GITREV)'." ; \ + $(GIT) reset --hard $(XEN_LINUX_GITREV) ; \ + else \ + $(GIT) reset --hard $(XEN_LINUX_GIT_REMOTENAME)/$(XEN_LINUX_GIT_REMOTEBRANCH) ; \ + fi + + touch $@ diff --git a/buildconfigs/src.hg-clone b/buildconfigs/src.hg-clone new file mode 100644 index 0000000..0586bc8 --- /dev/null +++ b/buildconfigs/src.hg-clone @@ -0,0 +1,32 @@ +# Mercurial +HG ?= hg + +LINUX_SRCDIR ?= linux-$(LINUX_VER)-xen.hg + +# Repository to clone. +XEN_LINUX_HGREPO ?= $$(sh buildconfigs/select-repository $(LINUX_SRCDIR) $(LINUX_SRC_PATH)) + +# Set XEN_LINUX_HGREV to update to a particlar revision. +XEN_LINUX_HGREV ?= tip + +$(LINUX_SRCDIR)/.valid-src: $(__XEN_LINUX_UPDATE) + set -e ; \ + if [ ! -e $(LINUX_SRCDIR)/.hg ] ; then \ + __repo=$(XEN_LINUX_HGREPO) ; \ + if [ -d $${__repo} ] ; then \ + echo "Linking $${__repo} to $(LINUX_SRCDIR)." ; \ + ln -s $${__repo} $(LINUX_SRCDIR) ; \ + else \ + echo "Cloning $${__repo} to $(LINUX_SRCDIR)." ; \ + $(HG) clone $${__repo#file://} $(LINUX_SRCDIR) ; \ + fi ; \ + else \ + __parent=$$($(HG) -R $(LINUX_SRCDIR) path default) ; \ + echo "Pulling changes from $${__parent} into $(LINUX_SRCDIR)." ; \ + $(HG) -R $(LINUX_SRCDIR) pull $${__parent} ; \ + fi + set -e ; if [ -n "$(XEN_LINUX_HGREV)" ] ; then \ + echo "Updating $(LINUX_SRCDIR) to revision $(XEN_LINUX_HGREV)." ; \ + ( cd $(LINUX_SRCDIR) && $(HG) update $(XEN_LINUX_HGREV) ); \ + fi + touch $@ diff --git a/buildconfigs/src.tarball b/buildconfigs/src.tarball new file mode 100644 index 0000000..c356e4c --- /dev/null +++ b/buildconfigs/src.tarball @@ -0,0 +1,19 @@ +XEN_LINUX_MIRROR ?= http://www.kernel.org/pub/linux/kernel/v2.6/ +XEN_LINUX_TARBALL ?= linux-$(LINUX_VER)-xen.tar.bz2 + +LINUX_SRCDIR ?= linux-$(LINUX_VER) + +KETCHUP ?= python buildconfigs/ketchup + +vpath linux-%.tar.bz2 $(LINUX_SRC_PATH) + +# download a pristine Linux kernel tarball if there isn't one in LINUX_SRC_PATH +linux-%.tar.bz2: + @echo "Cannot find $@ in path $(LINUX_SRC_PATH)" + wget $(XEN_LINUX_MIRROR)/$@ -O./$@ + +# XXX create a pristine tree for diff -Nurp convenience + +%/.valid-src: $(__XEN_LINUX_UPDATE) + $(KETCHUP) -d $(@D) $(LINUX_VER) + touch $@ # update timestamp to avoid rebuild diff --git a/config/FreeBSD.mk b/config/FreeBSD.mk new file mode 100644 index 0000000..b421a1c --- /dev/null +++ b/config/FreeBSD.mk @@ -0,0 +1 @@ +include $(XEN_ROOT)/config/StdGNU.mk diff --git a/config/Linux.mk b/config/Linux.mk new file mode 100644 index 0000000..52c2227 --- /dev/null +++ b/config/Linux.mk @@ -0,0 +1,8 @@ +include $(XEN_ROOT)/config/StdGNU.mk + +# You may use wildcards, e.g. KERNELS=*2.6* +KERNELS ?= linux-2.6-xen + +XKERNELS := $(foreach kernel, $(KERNELS), \ + $(patsubst buildconfigs/mk.%,%, \ + $(wildcard buildconfigs/mk.$(kernel))) ) diff --git a/config/MiniOS.mk b/config/MiniOS.mk new file mode 100644 index 0000000..fc02b70 --- /dev/null +++ b/config/MiniOS.mk @@ -0,0 +1,9 @@ +include $(XEN_ROOT)/config/StdGNU.mk +include $(XEN_ROOT)/extras/mini-os/Config.mk +CFLAGS += $(DEF_CFLAGS) $(ARCH_CFLAGS) +CPPFLAGS += $(DEF_CPPFLAGS) $(ARCH_CPPFLAGS) $(extra_incl) +ASFLAGS += $(DEF_ASFLAGS) $(ARCH_ASFLAGS) +LDFLAGS += $(DEF_LDFLAGS) $(ARCH_LDFLAGS) + +# Override settings for this OS +PTHREAD_LIBS = diff --git a/config/NetBSD.mk b/config/NetBSD.mk new file mode 100644 index 0000000..7e64541 --- /dev/null +++ b/config/NetBSD.mk @@ -0,0 +1,4 @@ +include $(XEN_ROOT)/config/StdGNU.mk + +# Override settings for this OS +CURSES_LIBS = -lcurses diff --git a/config/OpenBSD.mk b/config/OpenBSD.mk new file mode 100644 index 0000000..b421a1c --- /dev/null +++ b/config/OpenBSD.mk @@ -0,0 +1 @@ +include $(XEN_ROOT)/config/StdGNU.mk diff --git a/config/StdGNU.mk b/config/StdGNU.mk new file mode 100644 index 0000000..aaa89a2 --- /dev/null +++ b/config/StdGNU.mk @@ -0,0 +1,51 @@ +AS = $(CROSS_COMPILE)as +LD = $(CROSS_COMPILE)ld +CC = $(CROSS_COMPILE)gcc +CPP = $(CC) -E +AR = $(CROSS_COMPILE)ar +RANLIB = $(CROSS_COMPILE)ranlib +NM = $(CROSS_COMPILE)nm +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump +SIZEUTIL = $(CROSS_COMPILE)size + +MSGFMT = msgfmt +MSGMERGE = msgmerge + +# Allow git to be wrappered in the environment +GIT ?= git + +INSTALL = install +INSTALL_DIR = $(INSTALL) -d -m0755 -p +INSTALL_DATA = $(INSTALL) -m0644 -p +INSTALL_PROG = $(INSTALL) -m0755 -p + +PREFIX ?= /usr +BINDIR = $(PREFIX)/bin +INCLUDEDIR = $(PREFIX)/include +LIBLEAFDIR = lib +LIBLEAFDIR_x86_64 = lib64 +LIBDIR = $(PREFIX)/$(LIBLEAFDIR) +LIBDIR_x86_64 = $(PREFIX)/$(LIBLEAFDIR_x86_64) +MANDIR = $(PREFIX)/share/man +MAN1DIR = $(MANDIR)/man1 +MAN8DIR = $(MANDIR)/man8 +SBINDIR = $(PREFIX)/sbin + +PRIVATE_PREFIX = $(LIBDIR)/xen +PRIVATE_BINDIR = $(PRIVATE_PREFIX)/bin + +SOCKET_LIBS = +CURSES_LIBS = -lncurses +PTHREAD_LIBS = -lpthread +UTIL_LIBS = -lutil +SONAME_LDFLAG = -soname +SHLIB_CFLAGS = -shared + +ifneq ($(debug),y) +CFLAGS += -O2 -fomit-frame-pointer +else +# Less than -O1 produces bad code and large stack frames +CFLAGS += -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls +endif diff --git a/config/SunOS.mk b/config/SunOS.mk new file mode 100644 index 0000000..61fa489 --- /dev/null +++ b/config/SunOS.mk @@ -0,0 +1,55 @@ +AS = $(CROSS_COMPILE)gas +LD = $(CROSS_COMPILE)gld +CC = $(CROSS_COMPILE)gcc +CPP = $(CROSS_COMPILE)gcc -E +AR = $(CROSS_COMPILE)gar +RANLIB = $(CROSS_COMPILE)granlib +NM = $(CROSS_COMPILE)gnm +STRIP = $(CROSS_COMPILE)gstrip +OBJCOPY = $(CROSS_COMPILE)gobjcopy +OBJDUMP = $(CROSS_COMPILE)gobjdump +SIZEUTIL = $(CROSS_COMPILE)gsize + +MSGFMT = gmsgfmt + +SHELL = bash + +INSTALL = ginstall +INSTALL_DIR = $(INSTALL) -d -m0755 -p +INSTALL_DATA = $(INSTALL) -m0644 -p +INSTALL_PROG = $(INSTALL) -m0755 -p + +PREFIX ?= /usr +BINDIR = $(PREFIX)/bin +INCLUDEDIR = $(PREFIX)/include +LIBLEAFDIR = lib +LIBLEAFDIR_x86_64 = lib/amd64 +LIBDIR = $(PREFIX)/$(LIBLEAFDIR) +LIBDIR_x86_64 = $(PREFIX)/$(LIBLEAFDIR_x86_64) +MANDIR = $(PREFIX)/share/man +MAN1DIR = $(MANDIR)/man1 +MAN8DIR = $(MANDIR)/man8 +SBINDIR = $(PREFIX)/sbin + +PRIVATE_PREFIX = $(LIBDIR)/xen +PRIVATE_BINDIR = $(PRIVATE_PREFIX)/bin + +SunOS_LIBDIR = /usr/sfw/lib +SunOS_LIBDIR_x86_64 = /usr/sfw/lib/amd64 + +SOCKET_LIBS = -lsocket +CURSES_LIBS = -lcurses +PTHREAD_LIBS = -lpthread +UTIL_LIBS = +SONAME_LDFLAG = -h +SHLIB_CFLAGS = -R $(SunOS_LIBDIR) -shared + +ifneq ($(debug),y) +CFLAGS += -O2 -fno-omit-frame-pointer +else +# Less than -O1 produces bad code and large stack frames +CFLAGS += -O1 -fno-omit-frame-pointer +endif + +CFLAGS += -Wa,--divide -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ + diff --git a/config/ia64.mk b/config/ia64.mk new file mode 100644 index 0000000..50edf63 --- /dev/null +++ b/config/ia64.mk @@ -0,0 +1,6 @@ +CONFIG_IA64 := y +CONFIG_IA64_$(XEN_OS) := y + +CONFIG_IOEMU := y +CONFIG_XCUTILS := y +CONFIG_XENCOMM := y diff --git a/config/x86_32.mk b/config/x86_32.mk new file mode 100644 index 0000000..1f5cd60 --- /dev/null +++ b/config/x86_32.mk @@ -0,0 +1,15 @@ +CONFIG_X86 := y +CONFIG_X86_32 := y +CONFIG_X86_$(XEN_OS) := y + +CONFIG_HVM := y +CONFIG_MIGRATE := y +CONFIG_XCUTILS := y +CONFIG_IOEMU := y + +CFLAGS += -m32 -march=i686 + +# Use only if calling $(LD) directly. +LDFLAGS_DIRECT_OpenBSD = _obsd +LDFLAGS_DIRECT_FreeBSD = _fbsd +LDFLAGS_DIRECT += -melf_i386$(LDFLAGS_DIRECT_$(XEN_OS)) diff --git a/config/x86_64.mk b/config/x86_64.mk new file mode 100644 index 0000000..6f77053 --- /dev/null +++ b/config/x86_64.mk @@ -0,0 +1,23 @@ +CONFIG_X86 := y +CONFIG_X86_64 := y +CONFIG_X86_$(XEN_OS) := y + +CONFIG_COMPAT := y +CONFIG_HVM := y +CONFIG_MIGRATE := y +CONFIG_XCUTILS := y +CONFIG_IOEMU := y + +CFLAGS += -m64 + +LIBLEAFDIR = $(LIBLEAFDIR_x86_64) +LIBDIR = $(LIBDIR_x86_64) + +SunOS_LIBDIR = $(SunOS_LIBDIR_x86_64) + +# Use only if calling $(LD) directly. +ifeq ($(XEN_OS),OpenBSD) +LDFLAGS_DIRECT += -melf_x86_64_obsd +else +LDFLAGS_DIRECT += -melf_x86_64 +endif diff --git a/docs/ChangeLog b/docs/ChangeLog new file mode 100644 index 0000000..08f975e --- /dev/null +++ b/docs/ChangeLog @@ -0,0 +1,130 @@ +This file contains a list of changes and additions to the API/ABI that +might affect cross-OS compatibility or otherwise impact OS +implementations, in particular any changes to hypervisor interfaces and +the inter-domain protocols. When making such a change you are expected +to add it here (bonus points for a link to fuller documentation). New +entries should be part of the patch making the change (so the history of +this file will give the relevant changeset), added to the top of the +file, and in a format like: + +2008-01-08 Add ChangeLog file + +Add a ChangeLog file indicating changes to the API/ABI, as discussed +here: +http://lists.xensource.com/archives/html/xen-devel/2008-01/msg00010.html + +Xen 3.3 release +--------------- + +17974: PHYSDEVOP_manage_pci_add/PHYSDEVOP_manage_pci_remove +http://xenbits.xensource.com/xen-unstable.hg?rev/183ca809e1d7 + +17903: Add greater than 16 xvd device availability +http://xenbits.xensource.com/xen-unstable.hg?rev/0728459b3c8d + +The tools can now attach a disk of the form: +(1<<28) | (device<<8) | partition +to support many more xvd disks and up to 256 partitions. +The linux guest frontend has been expanded to support +this new construct, while legacy guests should just ignore it. + +17538: Add XENPF_set_processor_pminfo +http://xenbits.xensource.com/xen-unstable.hg?rev/5bb9093eb0e9 + +17537: Add MSI support +http://xenbits.xensource.com/xen-unstable.hg?rev/ad55c06c9bbc + +17524: Add DOMCTL_set_cpuid to configure guest CPUID on x86 systems. +http://xenbits.xensource.com/xen-unstable.hg?rev/18727843db60 + +17336: Add platform capabilities field to XEN_SYSCTL_physinfo +http://xenbits.xensource.com/xen-unstable.hg?rev/250606290439 + +17289: PV framebuffer dynamic resolution facility +http://xenbits.xensource.com/xen-unstable.hg?rev/d97e61001d81 + +Guest may send XENFB_TYPE_RESIZE if feature-resize=1 in +xenstore of the backend VNC server. VNC server code sets +feature-resize if it can handle the resize request. + +16857: XS_SET_TARGET +http://xenbits.xensource.com/xen-unstable.hg?rev/26fc953a89bb + +New xenstore command to allow an arbitrary domain to inherit the +privileges of another (in addition to its own). + +16856: XEN_DOMCTL_set_target +http://xenbits.xensource.com/xen-unstable.hg?rev/cff4c8a1aa28 + +New domctl command to give an arbitrary domain dom0-magnitude privileges +over an arbitrary other. + +16725: XENFEAT_mmu_pt_update_reserve_ad +http://xenbits.xensource.com/xen-unstable.hg?rev/847bc9b19c48 + +New feature flag available via the version hypercall indicates whether the +Xen host supports MMU_PT_UPDATE_PRESERVE_AD for this guest. + +16724: CPUID feature flag for MMU_PT_UPDATE_PRESERVE_AD hypercall +http://xenbits.xensource.com/xen-unstable.hg?rev/a66bdc82d8fa + +The x86 CPUID_4000_0002_ECX[0] flag indicates whether the Xen host +supports MMU_PT_UPDATE_PRESERVE_AD for this guest. + +16718: MMU_PT_UPDATE_PRESERVE_AD +http://xenbits.xensource.com/xen-unstable.hg?rev/fba4e7357744 + +A subcommand of the x86-only mmu_update() hypercall to allow batched +updates of pagetable entries, while atomically preserving the current +status of accessed and dirty bits in each entry. + +Xen 3.2 release +--------------- + +16592: XEN_DOMCTL_test_assign_device +http://xenbits.xensource.com/xen-unstable.hg?rev/ef83b50fc4a4 + +Test VT-d device assignability in xend. If VT-d is not enabled, or the +device is not exist, or the device has already been assigned to other +domain, it fails and quits the domain creation. + +16549: GNTST_address_too_big +http://xenbits.xensource.com/xen-unstable.hg?rev/baf90ee3c1da + +32-on-64 related additional error return. + +16512: XEN_DOMCTL_set_opt_feature +http://xenbits.xensource.com/xen-unstable.hg?rev/1de4e5056394 + +Allows the domain builder to set optimization features for a guest. This +is currently only used by the IA64 domain builder to identify identity +mapped regions based on the guest_os_type specified in the domain config +file. Other architectures may extend this domctl to enable features +specific to their architecture. + +16504: flush cache disk op +http://xenbits.xensource.com/xen-unstable.hg?rev/ebfb3b26010d + +Adds a BLKIF_OP_FLUSH_DISKCACHE request. The backend is expected to ask +underlying storage to flush its cache upon receiving this request. +Backend advertises availability via 'feature-flush-cache' xenstore node. +Needed for correct behaviour of disk-cache-aware filesystems such as +ZFS. + +16425: multicast notifications +http://xenbits.xensource.com/xen-unstable.hg?rev/d3041196ae69 + +Adds XEN_NETIF_EXTRA_TYPE_MCAST_ADD and XEN_NETIF_EXTRA_TYPE_MCAST_DEL +operations to the networking driver, which it uses to request multicast +addresses it's interested in. Available if the backend has +'feature-multicast-control', requested by the frontend with +'request-multicast-control'. Used by Solaris: this avoids having to +always put the backend's underlying networking device into promiscuous +mode. + +16402: gnttab page attributes +http://xenbits.xensource.com/xen-unstable.hg?rev/2e5d922b7ee3 + +Adds new grant tab flags for table entries. + +Older changes are not recorded further. diff --git a/docs/Docs.mk b/docs/Docs.mk new file mode 100644 index 0000000..3c95cfc --- /dev/null +++ b/docs/Docs.mk @@ -0,0 +1,9 @@ +PS2PDF := ps2pdf +DVIPS := dvips +LATEX := latex +FIG2DEV := fig2dev +LATEX2HTML := latex2html +DOXYGEN := doxygen +POD2MAN := pod2man +DOT := dot +NEATO := neato diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 0000000..21b06aa --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,1218 @@ +# Doxyfile 1.4.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Xen Python Tools + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = api/tools/python + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = YES + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../tools/python/xen/ + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.py *.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = "sh ./Doxyfilter ../tools/python" + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/docs/Doxyfilter b/docs/Doxyfilter new file mode 100644 index 0000000..6a6d50f --- /dev/null +++ b/docs/Doxyfilter @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Doxyfilter <source-root> <filename> +# + +dir=$(dirname "$0") + +PYFILTER="$dir/pythfilter.py" + +if [ "${2/.py/}" != "$2" ] +then + python "$PYFILTER" -r "$1" -f "$2" +else + cat "$2" +fi diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..dce7cd9 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,116 @@ +#!/usr/bin/make -f + +XEN_ROOT=.. +include $(XEN_ROOT)/Config.mk +include $(XEN_ROOT)/docs/Docs.mk + +VERSION = xen-unstable + +DOC_MAN5SRC := $(wildcard man/*.pod.5) +DOC_MAN1SRC := $(wildcard man/*.pod.1) +DOC_MAN1 := $(patsubst man/%.pod.1,man1/%.1,$(DOC_MAN1SRC)) +DOC_MAN5 := $(patsubst man/%.pod.5,man5/%.5,$(DOC_MAN5SRC)) +DOC_TEX := src/user.tex src/interface.tex +DOC_PS := $(patsubst src/%.tex,ps/%.ps,$(DOC_TEX)) +DOC_PDF := $(patsubst src/%.tex,pdf/%.pdf,$(DOC_TEX)) +DOC_HTML := $(patsubst src/%.tex,html/%/index.html,$(DOC_TEX)) + +GFX = $(patsubst %.fig, %.eps, $(wildcard figs/*.fig)) + +.PHONY: all +all: build + +.PHONY: build +build: ps pdf html man-pages + @if which $(DOT) 1>/dev/null 2>/dev/null ; then \ + $(MAKE) -C xen-api build ; else \ + echo "Graphviz (dot) not installed; skipping xen-api." ; fi + rm -f *.aux *.dvi *.bbl *.blg *.glo *.idx *.ilg *.log *.ind *.toc + +.PHONY: dev-docs +dev-docs: python-dev-docs + +.PHONY: ps +ps: $(DOC_PS) + +.PHONY: pdf +pdf: $(DOC_PDF) + +.PHONY: html +html: + @if which $(LATEX2HTML) 1>/dev/null 2>/dev/null; then \ + $(MAKE) $(DOC_HTML); fi + +.PHONY: python-dev-docs +python-dev-docs: + @mkdir -v -p api/tools/python + @set -e ; if which $(DOXYGEN) 1>/dev/null 2>/dev/null; then \ + echo "Running doxygen to generate Python tools APIs ... "; \ + $(DOXYGEN) Doxyfile; \ + $(MAKE) -C api/tools/python/latex ; else \ + echo "Doxygen not installed; skipping python-dev-docs."; fi + +.PHONY: man-pages +man-pages: + @if which $(POD2MAN) 1>/dev/null 2>/dev/null; then \ + $(MAKE) $(DOC_MAN1) $(DOC_MAN5); fi + +man1/%.1: man/%.pod.1 Makefile + $(INSTALL_DIR) $(@D) + $(POD2MAN) --release=$(VERSION) --name=`echo $@ | sed 's/^man1.//'| \ + sed 's/.1//'` -s 1 -c "Xen" $< $@ + +man5/%.5: man/%.pod.5 Makefile + $(INSTALL_DIR) $(@D) + $(POD2MAN) --release=$(VERSION) --name=`echo $@ | sed 's/^man5.//'| \ + sed 's/.5//'` -s 5 -c "Xen" $< $@ + +.PHONY: clean +clean: + $(MAKE) -C xen-api clean + rm -rf .word_count *.aux *.dvi *.bbl *.blg *.glo *.idx *~ + rm -rf *.ilg *.log *.ind *.toc *.bak core + rm -rf $(GFX) ps pdf html + rm -rf api + rm -rf man5 + rm -rf man1 + +.PHONY: distclean +distclean: clean + +.PHONY: install +install: all + rm -rf $(DESTDIR)$(DOCDIR) + $(INSTALL_DIR) $(DESTDIR)$(DOCDIR) + + $(MAKE) -C xen-api install + + cp -dR ps $(DESTDIR)$(DOCDIR) + cp -dR pdf $(DESTDIR)$(DOCDIR) + $(INSTALL_DIR) $(DESTDIR)$(MANDIR) + cp -dR man1 $(DESTDIR)$(MANDIR) + cp -dR man5 $(DESTDIR)$(MANDIR) + [ ! -d html ] || cp -dR html $(DESTDIR)$(DOCDIR) + +pdf/%.pdf: ps/%.ps + $(INSTALL_DIR) $(@D) + $(PS2PDF) $< $@.new + mv $@.new $@ + +ps/%.ps: %.dvi + $(INSTALL_DIR) $(@D) + $(DVIPS) -Ppdf -G0 -o $@.new $< + mv $@.new $@ + +%.dvi: src/%.tex $(GFX) + $(LATEX) $< >/dev/null + if [ -e $*.toc ] ; then $(LATEX) $< >/dev/null ; fi + +%.eps: %.fig + $(FIG2DEV) -L eps $< $@ + +html/%/index.html: src/%.tex + $(INSTALL_DIR) $(@D) + $(LATEX2HTML) -split 0 -show_section_numbers -toc_depth 3 -nonavigation \ + -numbered_footnotes -local_icons -noinfo -math -dir $(@D) \ + $< 1>/dev/null 2>/dev/null diff --git a/docs/README.xen-bugtool b/docs/README.xen-bugtool new file mode 100644 index 0000000..a7e95ef --- /dev/null +++ b/docs/README.xen-bugtool @@ -0,0 +1,16 @@ +xen-bugtool +=========== + +The xen-bugtool command line application will collate the Xen dmesg output, +details of the hardware configuration of your machine, information about the +build of Xen that you are using, plus, if you allow it, various logs. + +The information collated can either be posted to a Xen Bugzilla bug (this bug +must already exist in the system, and you must be a registered user there), or +it can be saved as a .tar.bz2 for sending or archiving. + +The collated logs may contain private information, and if you are at all +worried about that, you should not use this tool, or you should explicitly +exclude those logs from the archive. + +xen-bugtool is wholly interactive, so simply run it, and answer the questions. diff --git a/docs/check_pkgs b/docs/check_pkgs new file mode 100644 index 0000000..a4835a4 --- /dev/null +++ b/docs/check_pkgs @@ -0,0 +1,20 @@ + +silent_which () +{ + which $1 1>/dev/null 2>/dev/null || { + echo "*************************************************" + echo "*************************************************" + echo "* WARNING: Package '$1' is required" + echo "* to build Xen documentation" + echo "*************************************************" + echo "*************************************************" + } + which $1 1>/dev/null 2>/dev/null +} + +silent_which latex || exit 1 +silent_which dvips || exit 1 +silent_which ps2pdf || exit 1 +silent_which fig2dev || exit 1 + +exit 0 diff --git a/docs/figs/acm_ezpolicy_gui.eps b/docs/figs/acm_ezpolicy_gui.eps new file mode 100644 index 0000000..aac6d4f --- /dev/null +++ b/docs/figs/acm_ezpolicy_gui.eps @@ -0,0 +1,1756 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%BoundingBox: 0 0 635 339 +%%Creator: bmeps +%%Title: acm1.jpg +%%Pages: 1 +%%PageOrder: Ascend +%%DocumentData: Clean7Bit +%%EndComments +%%BeginProlog +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +{ +gsave +0 339 translate +635 339 scale +13 dict begin +/fa currentfile /ASCII85Decode filter def +/fb fa << >> /DCTDecode filter def +/DeviceGray setcolorspace +<< +/ImageType 1 +/Width 635 +/Height 339 +/ImageMatrix [635 0 0 -339 0 0] +/MultipleDataSources false +/DataSource fb +/BitsPerComponent 8 +/Decode [0 1] +>> +image +fb closefile +fa flushfile fa closefile +end +grestore +} exec +s4IA0!"_al8O`[\!<E@K"aC"Is4[N@!!**$!<E3%!<E3%!<E3%!<E3%!<E3%!<E3% +!<E3%!<E3%!<E3%!<E3%!<E3%!<E3%!<E3%!<E3%!<E3%!WTq8$O?c3!daqK&HMjL +!$;1@!<iK)!<E3%!!!!!!!!!!!<N?+"U52;#mq(?_uR1V!!30'!s/T-"U,#3!!!%J +!<N?'";(eM+Yc7e'2`0C,&n;PJWZW3,=8ZO'iNHK,VrnMJdDc"(Dn#.,pjuf.4R/3 +2E*TU3^Z;(7Rp!@8lJ\h<``C+>%;)SAnPdkC3+K>G'A1VH@pm)L51SAMNX0fQ'Rc( +R@9kFUnsrdW2Zf&Za@-K\%&u[_Sa=2`lH0Bb0nbge^i@)g"PEEj5f=akNM0qnac;D +p%J.Tq>1-F!!iT+!!#4`q&B%@rr@Y4r'^Lp^+OZB`Oap6Xm-u1/c8sq0>XiE\uE3f +r#Oag#j??qep\%ZHqE7#?$foIcDC&Go>CF\r4XoUPNj([TQNtE>L7aXYM/aQ*tNUC +8b,R\`a0UiM-KroQc!M_*-mbC2bK>*Wl0WAX?LGS!L,%hN.M-'o))\\oI(Hg)LO(T +$atQD#gukbI)P)VJ4K,@R61n:o7oQMcOMH*h[=lL0uc$L!!l>]#9AO1B9H';JW`<t +J)PTrC]90'_XICH[#b25O8*#;)ZKf3=n'''!!o\Orr@nNrYKd5^Z^u20)kpg.nK[? +ib8>lL#(^kqB_aA^[R-/5PU[3iHKN^reY:)n,+B:O8^Jepg)_prrAcrn@S]##d#?O +!/[KM(O(VKka$TKrrBnd8H/\LB`A'dh;PR6-(bR[n;>XYpmOG+GBdp&M.a]or$&M/ +r[*^$?/?M"!2<Wc]Dhj:XX!iGM51%dGD<lrQ@(=b!5^6WrrD%lrr@c/iNN)(T*rF' +MUF`aRQobTG\d;8B>X-8,.*F1-BIq-hAZCoU06utrr<>,,Q@b#U])'RL;2r<HnPF! +_CJ1W!9.\l!/97fYPKQ;$bu1gdQdMq5N-g2C]90'_XICH[#b25O8*#;)ZKf3=n''' +!!o\Orr@nNrYKd5^Z^u20)kpg.nK[?ib8>lL#(^kqB_aA^[R-/5PU[3iHKN^reY:) +n,+B:O8^Jepg)_prrAcrn@S]##d#?O!/[KM(O(VKka$TKrrBnd8H/\LBn,)dG\MF& +!/4SFZe(fPBZ0nK9crl;]J]]h7(;,^rXg&%rr="5rYLoU^Z_!]/,oUd,=VV4j)fdf +o'k90'&SCmpdAf1r,.Sj?="QM!2<]eYQ"S.N>MTpNR@b+$d6FpoLjWJrrD!iUAk5` +\j,/!G\d;8Do09e+uE[+2b3d!0+EEY/biEI!/*h"rrC(&rr@c7iNN)(^C'u@n;,JA +pt[%]]Y$-%!(=<T?iCWU0E2"kU])'rL;2r<r%%dI_BVML!980N_Xd3Jr=\"AJ+uEF +^\hu1pg.8FrrActn?;il#`TZ#!0,D+!"6uf?h@!?(&n9m&Yf:*n[^sDH$F-(M/U8G +r$!t)r\m@F0/!cad4P,prr@^AMuNdskl1X:jl*E>-,0fe!:YflJ,%hAn@ZCkrr>Hr +iEuQr)DD*j)l*BKQ\N9=QM`95!5]sLrrD)$J&:dGfDQ?>Kn&kcBYXI_O8*$f)ZKf3 +>O\ir!!nQ#rrA&+rr<GM_dE%/IMMk_i=Vga!:dWbiH]Z`reYR1^\f94?iDuSr%B]4 +J'fkC=8r8R7Z79H`p+f=^Ve!Q4rAYr!0`!6!!kKH_"?U$r.#i=FJSp:L#(_VqBd9k +J+/3@^\Hn4n@Q=rrr>I)iD9Fag/n:S*2EKLGDErsR!^@_!5][IrrD)DJ&:XCp\ba9 +Kg5?"Bj^dYO8*#;*rc57@IU2p!!mElrrA'Vrr<G=_r()[4r""si=2W5n]F)TGBeK6 +M2/s?r#rGNr]*LH0(0=#d4k>orr@^1Q2^j)./s:$h;Ri!-6ESEn:oB+puNUe]=^<, +!(=``:]:pZ=8r7?U])'RLVN&?Hn#'q_Ac,I!99;n_X?pfr=]]q5PRT[?i4r*pg)_t +rrAd%n>H9d#]2*l!00qV!"6EVht0T+'`S0l%AeREb!4j/G^KFHLVf\]+geHP3Cj!" +mgjoq0_eTH!/*7qrrC(frr@c/iU?Ui*t!MeL=3j3jWF!(]KQ8p7)RtZrXddbrr=%B +VS3[j/,6sN]>BbVlbIuNIKILHf_$bs+qG@b5hA1(r#X6CD,.niI7hi'g5j5A+*\^Y +`7&EelHj:`_pW:08pe0Pob20gbR5eJ-g]%Hc7Je+%0$<9kcb@WBC.q\$`3pL8_s81 +$iu!Q9HQW,:Q>>dr)iG!L%X=?C7bb2!+jIfkaiTtMc0'pr%Ii3q_EP4f).\+bB",Z +r$33<Kt\+G<q4Ln1p7>(HZNh2HfAi.)@ZdE]Y;k^Dm^O2NC(cJo_\aZ-f_,CT*=H9 +&@<O0T<kDSi@be\-/XK#08VkOrLEis>\j1X<tj8X@mn_Y/ppF`7_,R5;I/#4>2*iD +)K!KTGB7J@"g.FFgJ5$G;LZji!$#IoS+,&'*KM&`RJTg0_T@bs);Y0VNk$#"aRp;0 +^D+<mnJC.#K]_fkMZ+*7cJ#a]D7*#B(KBo8_VZ40cMmk`/:9IWl;>kSO_<[qrX=&G +iWoB)dd'PAGVg:#<?@8D]fh+D%[-RLf\T%%B;GOL5IF:D4?9-\Y1rYirL:/742`%S +T!P<KHqO0rq^[%EnMJs+UBFZpAbGMEIO0AFn:uq/QuD=D/amfUeT<;p[H[9d?eV<a +Zb[oOYCC()g52R42o.`2Xs1FPO6nh%#Xu;.8IVnRcs(Hk(;\)q&f>2dni,qVi@g-s +Zd#\/p!8FY$N;,l7nrLgcDd`:?fDm<0->QrnXbpS[prs2C7ZN*i_R?#*;I!4d<n_" +T6h=7rr@cA2=3V83[UU*mDbUk5,'@7jSUV0!!Q-e*f&_Q1Iq4qUA1V_`]A2NCTLn< +B>a]]I3/=*1nqk`,Ce^t%V<;ddf.4.X$mef@:KeN/bE4W"&2EXDS,lS"oC-$?hUf( +W."r"ce^M0Ffu>NBAs)UNXhhdpj1[d\^de=_LDi=D<$U8gI&/Yg:!1(>u/@]G.2JH +fZ[[AH]K?.\S.mrqXXXdLiYdmKi3j!^Z-6q+c>L`!"8A?83:iS)tJQ1!(*F\JdtM( +62pr$@_^iiT*^!Trr<+J#M4DApm]3*rU7ZX)u/_geqUqX@hXb01Ieb&Ca4f(G+`,( +Y!0gA[OC:$ehqgOMu2\/mVgB($\$<,$)7BX+T8U[0mm7$Vr=`c;uNb75@k97iV122 +JR/ZXYMrd4&]O6J^,8:;X6i^'hm@UC4VLW<gI0N`rA*Nd4.H-B0C8>OJT##J/d@NQ +n>#\LpaP`;p/V'c5A>fGdIEjE&:Vlr%D3X^Gc&C<+7/7]k=tF^N4q(Brn2D^$N(;, +Dt+@U^U8QK28+??eN@n)[tS>^#-ICD_oid[rgVmtN<U6m$V`B_2tgtaOQ-564q%AB +*u+*k+tnZChq:re?Bt%qhn["rM%+W(Ln5T%X_L'8\CWt(-IL_fZ$9$m9XDC#k\,Dk +9AukO0,C,>/@RiQr)E[rJjX:f#35Z\meh(p\6*I;ZUaAGnB^foQL0,<@Jjp#?]&iJ +P>C]2_L#C&4s=.NTtL@[n<U]F-p1oaeuJIG(<La6&rG[r(r%_O;=H7!=+U8]iK&i0 +2>B9'8&?>Wr,;9Npd74fpnQgBM`a5^Nu*:\C"$hnBD`tq/Ch+c9a>&)dF)m72+o%X +X8BZ>l-N.ODrt7lnupQX%0$=cT)Sd/[EBZG2c9A+i?*rLZYU]`rr?Rog&q&PZ&dmW +_1*9bpKnN7^&V8[5>g#p$_Y>qRCj;Pm]U$gY)=Uo]klELnGHA!CtC]oip)hEfO"c2 +@K-=`T+_&Tn^#4I&8_8_n6<d0!"&Z*@W.7@[_IIfY8$mdfABC!bMaOq<aH]YP=P+* +m]V&t>:\Mc`)6;ubf/UJ,3Qm*I[Yut#+;FgZ5"HCpnQbS`ifL>,JtT:Hu&4qifAaV +)u/=W5OaDT61II_mhU.t_&b6.eC1;Fo[!;%4B\_!(7G`JrM-1C>\]QhfiRotCX\rF +p>=*&M<.tA!#%S0Di`cZXRsb-iuc(ON'@WUrGHeddBENrHnt5iJF)rUP_l;bI5:-- +3[U*qr948FbmgGk,/fHa:l7g?#NCg2.IfLXL[95XIam6loUd6'/FaO65AH++fsa9B +1%>e55D]4PY3Hp:cbWs"r*b6;S3"uQmkh)uF:4f$NF0Qr<`]24\?rlt/?\DNnHaJ5 +[.3Sa!!e#3SR2_Q,'Y(#(Y&@uqbcXn%)*m6htJ36oUgd8*4Yb[BAu.2g*_`k#(07p +IM[LN2re8$bOM>qf;ro)+DW#Jn?VgY>pJ<_ZYjmnqqWVsHWRT'me*cAr'gK/rm=mY +pVnWJrXO<EnW/Gh"+3E0Q12iRLVushp_UpK/GNr<Nu!*fT(tKuBrLgi9@0K]X<R0$ +Q%[lRV#$Y,K[q1dG]fQD$VMp:Q-\7bc(D@+QAb8,`0[(rr\Rf'GN.J]iV`l;:;:l> +Zc<Vj^CtbI(.ID2=%2n2Wlq(^O,Z#8'1;ZS&*^eIT2N>Q@f>G[%Ee9KfC?3H$1[mn +HkZIQ]LD]Mcg[:@586:2G^I<](QK9Wi6QuR+aE`VnJ?h#bpuB!D\dRhiUD#_X*BQ> +qBiDG9Alq%`4CDZq?EMNXV\ns(`JY5g/Rn;rr<6%c<mpp`-m!O^(pEgn0?s@O*jg_ +$eNk?GN$:?r%ITLMKS@kC(Xb/fLoZ4h&f#2n$O5HhT(&@C6hY2XBX@QQ[3NI]1+-R +\4HS$e,B[Pqbpl$c1ZXom>o-!(>j?q;>Bm$q`ANK'=%%.`!bIZ(]H8(Q>IN\B%t4( +Se%:;]GTN;U6"6dCQ`Hfgg#5>]<q?GCSom3Sl96_r)*EM^+<sT*W@_i*;C!4oCdq; +TX=0?hscFdLce7Eq^d,-piGGQ.rQ2J01PJ:(]GoZ:Z=MIqo6>Oe(WgY<RWcth\8Ln +DrR6,&l0/(O$8JMBdrT`LHi+Hm1o/H6bDkQ_G>\H)La,[C;Wo_dI4_8nE8b;NdPc= +g$7:&QCFi<>$^!eiLL3PYd`,(rBFH4n*d?T\4`;a/QYZXSg`O2[?=ltrJEgMX7+d6 +p9aU=q^Um&._GIGWHdFka7^F4M(ecK2i2Ic/UcP1!-lQ(f6160&\-G?,B5m%pDsp1 +6FjOs>17*4B"NDh;KoH_)ih,]&+";pfmdTuJUb)2m+Lo9=nqOVe)Q5&BRU+fpd83Z +>!=!TC[f*Y4\N\5pqO`'!n=q*pi"3f(UVFs<nI70D(_SmSfI9\V_"lkUF!=]htFEQ +'Ygt%lj*&bj(Ib7n>GtDpp6fAoCc_bB>k<E`Ho`Eh\/0gG`pBYde$<Ep_1[(1kSkf +^tpBKn33\Kl2&W<#h1[m42fXMXTd+^R4^*1_J<6E9kX0&nY5q:rK99Ci6Mc.FRak! +Dq_\;hE17nNUX)Xfq79]L>(+ohB/(LeGL&[Hogf3USZ5.r-F.=ScS?jT9&CdpgXL* +*XgAkZqYjY>u+8.\DhZ2!83G,ORSt6ReWZOHhcFU[eOj:krpBfL:[Bb_LEV?kJ2Ne +fB9oWd'`<[NCQ7B4r;UhX.:b?\'fCnF6keGPko7p<ccM3S\iFmlX(#^;t/hd^(8T) +3d<mKB>Qef`4"MBnMJ\[?Oh2tIhp5sj':(Th"]dCrXeUSU#8Rn(gB^IBrXnd_#>Y, +1bTGU)LL_h`>VugC"+^uX8(ZN0n.Dt#JXlSfus)G[J8)F8U.C6g;h+GZqdUiDrLNj +QG*iB3kh5crm'@UdCp]<G$c"B`r3Di>oUXA7u7(Ci]RaXNF0LrnOHHcnEn3WX*rsV +eEiO>pHLN[a7aNOTY;H4*XhK3`".D@)12gqr(+Cl+,?:,N.)W=hnpAQO)JU@e#[#b +n6VNbka"0A.Ct]:=41p5ha<"l=1Zp=lZGQHmBTCWBC*b;!IRrCgU.K,=A/EpJ( +rL#cNnO$+B?F'h0`6X-d-*u3F]bA;4"S&s\+P#K@2i2GM&#j<TI^:\Z2%:&@nMMgI +6$-uk98eP/RaqB<d][MqNAF>[XO^aaC6fW]NN\eI-2Tjm]QZ&TL`(e9j\!/71BhTF +`u1rW=T7R`B_'4<:[:N:5Iu>J4L+k6qU+Dcm[r'k/:Vd@[!?0YVtHh.+5K/`*a&F+ +rFkcBpJ:*FQ9Vs1>!0(ge,%JRO7&XkO">d&rj6r\qks+^b9,P)XWCUDl2>;da7iBq +8!ZBNIp+t>rR:VlI(&DZrrBd&rr@m4CiB.35OYKL!9,EZht,'AhbF+N`)3eSkEu8L +^75-&LneI0cS>G"I5h0+&Ose?!1m^"qJZ?WNK*r<4NIEmhu#n^)ub,DqrtljMDdEk +o,m_UJ+44hDu&N[n#$*bi[EI:o?;^A:&b43;6dn!R/[0#S,WJEULSf"1&h6%2u`ji +7`PH.Sn%crDuLr?NW/u@a8PYfr'_PDrcsS%l+d"N2E1R*+8=66nJ#eXq;sp1VZ-YU +.+mGKb^\MErrBu=oKhssiWT5rrK.$ZAK;5-Q;($GGO^+(`@@sc*<+YV[KLV56&&4W +YXYeJ!JG,MD202kr'L1TX]nF_fq>b]QHj>E`X?G.(aH$+I.cadrr<=I\\(T*6XRL< +<:`oP_u"g+`eYj22L`KW[&q8^.KP;:gd;7`=UjK=$o9Nh,E.]:++RF!#4]0Y4pq3= +poCR_^)ZodDu1i0c(6QXL;0BO%,1E/iU6mK!5U\0(48K`]<Fj`57tQ-!RMm%m30jL +)p>:+\VA,RhG8%Tk^Fc'J)H7mA6i:1`VBCB#pImW_#>R@7KF>0Iq>[^A%!6F#@9^o +?hisFr@?jaMEg%!T+84iSOW?CphTAg`;Teb0"#CF-Im"@hZWrZ_+=S+A)@Z.3ej[] +KpJ9>2R=a"Cr5W.b>,:NBl<ACXf\Z<27e8.CO5He*ut:-rr<mb)uO"3c/6F@#4pjc +a.3`.n\)VjRsh/I4trJc5D^u)06-#1^R>1Ai:s2Z*ij?%p:a9R@_UC*2l&X6i_:B7 +F[+Oq`.&..ipVoBCsN^&_@M7u!"7u(nCEf&GX`](>k*Ti%j(#)45]ft7JJTGW['96 +3aL5K>3f@XX/juhbAV<;W;(^=:CYXDr'g,d_]S(1p:#$kq\+@%]-sj5rmcM'F/l8' +A))GLrr@Y&$%.EB.+\[[f?F\U.CQ_C\AKBFjm0o7;oG,fZBQKcAOmr&f\7H5!0$)# +`u3oprf05$OoGEg=+\1t5MB&oJ*e?J#=.gGMS(&#o$>>5g3OjYgFWY-nU@2p?gs]- +IKctTaKk>+M#8Qm8mp139kATsp>6JHq_ZBtA@FKVEEh)OIp3Nb!5Z^*Z36qCIa:6e +GmC8F;-s;a*e\2"PK=XajCDc6IR3u6_-?p>i2"*n^YkfW)>G$,DhPk5IQMcqItg-: +nY_4+6%/9A6hkMnC"IO>p49.+()Ha6p`ne%O705i<oVtKR4`<W@#c81QS/)o^1He` +`X`4*rrBkk/q7EdDq`N>rL(ADpp]tQnQ3[_L_b/;5OFFFLW!QQcW'k1Qf3+qi2q4A +^Tr@*YDQ4bkoM2Am\-=Z?halG''\+&]r(R*rqfQI@qDLjhq%SanItV1]`!J4Ama63 +i8<o:iVa6N$eT]Vp6klT4;G$s%/mObrZM,7rM09\iEJj#*T13^<q(>"c`[1!]s@EW +iS31,_%pbEmi1Nf1u9Wf%Vj^PUS&ui,hAm=rr@XXr*oFqq`92T(\cgo(q,L`&&l;9 +h\CSFHt\uC8_/8dC;'i@c\K6d27:JZ4?9,1LKO'M[]Q]G)CC5bVV5`CAZYPdMj\(" +XdpR!^Y::<$7YK%p6PZ32=CNK:T=,Ap5&[?Iq_V`?\iR&GW3Rp(9=0YahJ9K5(%8H +V13-;4&bYWpd3HI:At@IINnPK^tp+$?E:3^po;Kn?P$t])K_<lAufoo[JP,rgYVup +.XWrK\*Z?sHsuf0nL)<U!4Q$crr?JU;nu`AF\da!&i;f^rl8=XrLEM0#>O_Tb>[rO +MM*&X/uJ00kCrh$V_4>*DcbM4$2d*Kq`F]"2c7tW[Gi\hK>\l$;D3Qd?gpmQpaULT +2lg`*VoMS`r[e#0]R0.3p1a;;nJC0A`bfe458LnIWr*P/PI/#)i8plS4_MS)>.9:" +"n>gV8&KfnG^IGVp&>#FoR?o-2V,I#Lc'Tij%$VeeS]EA."9\gZ>[ZgMuNdc0?4H= +N*[aU]OC\WrrBk]r\XW"rZgo.MnC/%Zc0WrrLH,4poD71%e.eu^)#or-dfXGl7qR2 +$#VU@(L5NJbC>9#=,;lrX'YR^`#>e0Ud'cRXek?uBqO.tp-S<I?aRf=rr<W0/c9YY +kO#VGF8cKnibPaNMD'`l'Ds4rq_!71rL,,/H`2D?*YV>*?73MWrrBpWotQ?0=+pK_ +iU&hTi6R<'<_jJ*j%e+lQ1UI:T,$UVIPcN,^TkJ-JA)ZkGN%Gtrr<;iRW$"apa\"J +rr?i,#(.$X*W$iOfAMF<mJd/urr<ORCKf2[pfAN;Wd#K!rXnZJNpQ-dU%,&!%uMD` +nKa3@gYt*9UZ%1K;E>4M*jj;n:&VeT4+>M)555NU(ZA`=+5-s"q#0^nin+)D(ZL5; +[Vd&C0C9))7e!n'Ii<d[h[t5LiU7<PO2\jJ^)^ln&:9Im(KulP^(TWf4s'6K\RBjD +SL_TriM\="q`R;3n@^dZ]C__I^U)hq'0j?@!W4&/HcMbcL[=!YJ(m`m`L\gZ^[T"u +7uASjT4[l#n_:m/,4Itt35;ijVo-9$Rb*M2GG/2GCZeOElom#d^'t#AKKDdaqKIVI +<7R]4(Q3Nnp4p"^pa4OXm-,&!rr='8naGIer"Oe(IiWthBmP32Nie(rrXF+Up/CK_ +KtM-Z%u=OKd<'/2!++p(0DM`Ll&b1Z$J+n2^Xr-]'2aP&2`c)Fh:hhNqf[GChZ,hi +HnbN.m62D8@JGiP-cQ)c[o\Cc!+C:S^g@.c:PN3h1d;HKa'KDWSFh:9:%:Qn;t7nc +KHCLQVX^5?L&=<9/sl'G=4VL#r'B?>48[8];:1-+i[ZZZnJCSpq]^9?p8.chJu`\\ +58^bu(W2lRp4`AVpohKJ(:Bcf?aa+ar"CIEp#*"XpfGGL$N;5OHk4!jT"[;P]&p,9 +iQZjPdpr.nnL%W;Gs7^IV-/&p`;5J-Xl>Cs=.VqPDt^s@=o2!B5PXd-:DnW7P'dr5 +::L6'"U"N]qc2<sFnfhN/cO56%(mbRBAnohhq?o'T*F[>%0PP/KtLd_Zf?gp4A4UN +*V_D1j/7[>hh\kYfer5S\dsO<$X8Yp?=-4'p`ne%M#RGeILc.FrX$]Grm105<n4i[ +nN0/=!.pm#IahONq"MR2(,*=#nW2Z.Z1'9^*s_-pB\JK;iI;s5^(g+=hCI4ErZLun +fAbDA]IoXBQ"-mhHrI"hXt;"kepgm$C&\/:%K2@%+F_[%*'@G<qb)<*pc%,,#lan@ +;SQfcgG6gh=td\Kg)F"Pp6tbTn=TCrpa>Pm`P2QBr%m:B$/51B4!aU%i-e)SC]=AT +GZm=e\q4YI3nBQS]4aq3T?0;-rr?t5L,OK$pe,ZUN')1NX'5&.r"ns,rOKR\i*/te +Gj%l\0A,#]n:-9J,`gphZgdtp:"R#cGohZ^Hf:"pl@0<24AW(oqY%Coq>MJ_26.Ze +De&>;!8-3&RJ8<%p\kNUikO4ua5/AtZlafW?aR$(bOGg1nV$lApkL2rh[[@E>lN)N +?OLeGph?W#53(X-Dm!e<-h*(@IY?Pe9n(Y66JqZ19=/B8KIQd'rrBl$oe-FFA,V'6 +4u.LHph0(pp0@Qc2*VfMK2)+on-?bC40[020B,3R57"[cL6DI#nZVjB:Plh%^*`XT +!61T7LW%Ij22t8JPO#L;M60(ABQr1cf%/:J:%0\,g&7K@&Ur+,!:WU1&*k=E#ESjI +pb2=^n42koVYkoAX1V0rYN#;3V"+HbN.5O,D0"ON<r,=A4s\cPnWUo]GR&B6CYH;F +FZgN&cc$f=Se]e`RI,.!2bN\VfrOhPrL\NTIa*AIJ*bL[Htr1)K=lKfrrDgA\,6t: +p1!Q9a'SlF1\f?ETDL`u7fB350`2R84pV!jdI479-fBIglXp#epj(j.HUuSt$qJ7. +&$3?\r$8j$KC7obX\\^>-CgOfm=D'^cMe:JTCG/>`;Q;,14IF<Ia4Rm?1]0JpuC<D +%K&BaAT=DNB^;Mi(=V9+pj^8S*r(0Q=2e6I)RJpo`=5b^h?_Q&[IIS,A`8[ZBj-h; +&\@`Zp@A"A2skth?[Qtjhd^Y?ESt'i(uBKJi7_mk^720:M==U*I'WMliRY!$l5FN@ +(@p]R44A;@O,?fhM7!@mHfNlH#^%ZP>A,@%GJ=$O^(=t,(&n8c.;:^3CHaH(Iq!mD +rmQ?05!Ja2a239LL+OWAnUKqAn5J=rK6/%S'DR>TdJO++9ln<\&Fa=gY,ufJ(W?06 +l"b1!pm>a3lJ0Y\;;qNTpaFb\\aZ*K\&-$)`p\aK[*FT.J&+m0BqO-Ya'Ld>peC9S +_qIo%:P1kM^'K*apeC8Pi5V1)nT#X#;"94q!S75spg_6^_sii<?LZO<TR8cO+LJK( +25frHPHT5_^n9@kp3?@$qa:A`_(!#]XFAM;55=s3^Y!bH+1;*;nb`$_nMe=gMuFc] +a8RajU[<3YD[>*2hAl<S@jf8l'$&oNe&SZX"$tHo9<0]Zl-afX<RrP+f!O6\q=!hA +_tc,0"%:L*V5J)r6di@?)uF8B[+?jNm9JmVYui#F::'ISPMtC3>?GVEgtgXNYP<T7 +el((4QVu6glbPHS4!3sL9Bd8WrrC$crr<>#^6*l:r*:E6MG*1Ml2Lb&dJj1PUqVt? +TDVt:n;9m'([:r"`R4\c#c,1sI\6OD+7P.\kniUa!6*@c!!oC7Oa`r#>p&Qn9Bd8W +rrC$crr<>#^6*l:r*:E6MG*1Ml2Lb&dJj1PUqVt?TDVt:n;9m'([:r"`R4\c#c,1s +I\6OD+7P.\kniUa!6*@c!!oC7Oa`r#>p&Qn9Bd8WrrC$crr<>#^6*l:r*:E6MG*1M +l2Lb&dJj1PUqVt?TDVtrD[ai,NK.sKb/C-$4c\r#U$a?oi>S3bH--'dITQG\rr@^e +Du&QDrV9gjU](k)^ZV,ZpZ6nEO8)2ArrAX2J)?Ok!#e)\)Op_Oao;?i=8Zb3!/,** +q<'.(q-WZerXl-EkFA1=jaY:2pdV.\!1n4\hj+3A)F*1!S,R]Trr@LLr+Q*1KPGH$ +k9p6(?h'oj&ZNP.S=fQ;J&[QE7]-1cSoT><Q2^h&5N*NMrcuWc!.J#IB`A'cC2`q2 +-iNjJnY?'qWP@VdTD.Gs`fKmaVZ-XfEduu3rr<s%i>S3bH--'dITQG\rr@^eDu&QD +rV9gjYLd]J]'k3WD'/``h;KH53q`6%4rrqG!ZF9ElagnN-EEkc0)kqFq>IMaHrI-2 +[a8`WgsY*ERN#SifgQC1X2us30s#\4o9!fAfRL:NJj81Xg2FfW)XF$%n*MduCWF1- +XTjB))rX!qBY)HTL\58hnOA@JiZIOqS%jM<%Y>j4iG%u0:=m_[2OhGWcIiog40G3" +q)RpL<joR6o7*LgaH!_oZT%:H!0!shrrBq<rmPCdGff-H6gP)^d!l+E:[i:Srr?_> +&*P&snU?h_N=,FR%eq4*QLQ<2A`>:39%!<MLnF0/T81,enD2*bA)SF=`82bV5#2gd +P=i<tQV#PH>FS92Uq?LTOM`D-S&ISu^'j3;41FUBq_gi;j18S,rmmo8Ee!23m'e_- +%R*XUi:O3"pp'?"_Z'TP4u*';_-B*&i,)IOcNj6siFe4aDrFDrD6L#FX5VA;J&+g. +A%ceKe:iXTCLaAHS*`!)f&uCGQ&VX=Z'7P"CB_RJ')naUMgbY%qeU`1d=0Lb+kO*i +LHZ@Gp\RGG2]<3]1u"Qg=RkOWU3oi.1n%L`k\i$?XX3K/?6t\+"PVVVB,jJRp1nFb +(HR`S[mK^_)?/?^!C(=i&&nPK#lam>Igp)+$@X&\pA0NCq_S$EiJ3.d<7S`r>E'T> +[^p@?i!FLB2Vu<#)0ct9p3IdJCTCTA_5"hfiWhk0hAXarKJgMt++YLQ-]c1fZ)?t( +DN2=\rr?epfTQJ6IeU\Y!;Is-(rHH9Hi'Ce/,\)qo$/am(@*m:D@oYGL,i/Lm$?]F +2npUXP$#!Sa)O!6C&8QmaWIp?m60CNRD3AVKT#@Ef>2cGL$h&t1%FGE^B_3:+,Nb' +CS8f2pufU[*Ga3>\aZMR+PWfrbdN:!o5;ba?eCj/cmp45-,9?m!noM'5@lc_TF?mA +5I9>/#9s]J3"?6SocAd(2d?7fJ,]KP3Gr'p:q.m5#KHcDg"`OP>]0>-T22Z!!3/qH +_(C/pVf(0pfg\s]rU5agZeeJ<);4OS7XF.eSiUtlrrD$/J+-CoqDC8tnFuq.hu*.\ +rr@gWrrBl2J*>DCrrBsoq`fT%eYE*aBE%r84oYMW;j74]%tEsErrCAGO8*jir"So) +ND<:I4oYMgp\t4V>!LusJ+-CoqDC8tnFuq.hu*.\rr@gWrrBl2J*>DCrrBsoq`fT% +eYE*aBE%r84oYMW;j74]%tEsErrCAGO8*jir"So)ND<:I4oYMgp\t4V>!LusJ+-Co +qDC8tnFuq.hu*.\rr@gWrrBl2J*>DCrrBsoq`fT%eYE*aBE%r84oYMW;j74]%tEsE +rrCAGO8*jir"So)ND<:I4oYMgp\t4V>!LusJ+-CoqDC8tnFuq.hu*.\rr@gWrrBl2 +J*>DCrrBsoq`fT%eYE*aBE%r84oYMW;j74]%tEsErrCAGO8*jir"So)ND<:I4oYMg +p\t4V>!LusJ+-CoqDC8tnFuq.hu*.\rr@gWrrBl2J*>DCrrBsoq`fT%eYE*aBE%r8 +4oYMW;j74]%tEsErrCAGO8*jir"So)ND<:I4oYMgp\t4V>!LusJ+-CoqDC8tnFuq. +hu*._T[82HS3QLDmg6/*^(0oKj,X>XM0)l_2<$-I"FehW#eV%%YBXf17nqO:r&(Q_ +pg<&VPC';(F&CFDI59h^cn@!udPYC9gVp,Eh*R]9k+M_SJl[B;:$l>Cg=u<rnh'7g +NX-b_j(I`S*)ONc#i^^ie)I$"N\ja(70-CR:]LJn^\e_ZrX*nj-h^K*VEa.Mrm+*F +mf*8$am\PVesZO<%^#a2Jt;oAdA]e=!/IoSGQ.[+V+:GE\,H_Yrr>Nb0E*$=g#)f0 +Ss:DelJM@chu+IX^\E.?B7Ko,<.DfdJ)I5SoP.;(!9*;CSc8]cku%H\%^#a2Jt;oA +dA]e=!/IoSGQ.[+V+:GE\,H_Yrr>Nb0E*$=g#)f0Ss:DelJM@chu+IX^\E.?B7Ko, +<.DfdJ)I5SoP.;(!9*;CSc8]cku%H\%^#a2Jt;oAdA]e=!/IoSGQ.[+V+:GE\,H_Y +rr>Nb0E*$=g#)f0Ss:DelJM@chu+IX^\E.?B7Ko,<.DfdJ)I5SoP.;(!9*;CSc8]c +ku%H\%^#a2Jt;oAdA]e=!/IoSGQ.[+V+:GE\,H_Yrr>Nb0E*$=g#)f0Ss:DelJM@c +hu+IX^\E.?B7Ko,<.DfdJ)I5SoP.;(!9*;CSc8]cku%H\%^#a2Jt;oAdA]e=!/IoS +GQ.[+V+:GE\,H_Yrr>Nb0E*$=g#)f0Ss:DelJM@chu+IX^\E.?B7Ko,<.DfdJ)I5S +oP.;(!9*;CSc8]cku%H\%^#a2Jt;oAdA]e=!/IoSGQ.[+V+:GE\,H_Yrr>Nb0E*$= +g#)f0Ss:DelJM@chu+IX^\E.?B7Ko,<>kf:JldH<:bm%f-nuUtT8k#"a<YrMrr@bA +E.,$3l=g1tLKAuA:d=0mci,".g#rA8Sn0#5lL4Kt#Q5QdrrBssq`OlYo?=!/*F8[F +"[N(fUJF,LI",e$rcs`X_lLQ1W#tYGkPkMmO6lK<!7*E]piUfY'a+Gp4T>E?nbo&/ +q;tQS!"Q13!.pkndANW75!QC"rr>1(5N1/Mn5Kqp2"U_ILWB+6rrD5M8,PE[rX+5V +!(NQ0nG*"/5Q(+$rr@gUJ&+rGkgRlW+!92!rrAX%+7SRapaQOI)L;>__rLVVrrDZ7 +U])9>rr<3frrAfi5PTVUB:o0KP^gTO^Yl%4oP*Lg!"Y.WoD\g:16;3QGPi0Xrf'&? +!9*JHSq$Ru;?$V+Y5\KRTRY@eTDUl;Zd8XO4N]nIe_fjq&,J-Srr@hpp3HZ<kF"j< +3kP@uJc>^>Sq$8)rm*h,o-jV=LKAuA:d=0mci,".g#rA8Sn0#5lL4Kt#Q5QdrrBss +q`OlYo?=C_nDERirr<3O[.'E;HlMT'rX$:-S)^B6i1m9hi#CR0^U,VWiN7IC3_m/` +hWqc!LGYQGi+MFin8hLX4>VKH*Cius'NuT:BCRR.Mlu?6hP]>RZtl9Fpjr*4<rI]d +D-%Z`rWq-Q2mDItqd=eomW*UQ!/Ha#YN=KETCs*$!.p&@QC@\u-N='t?[NU`+6%b_ +phO1)?E<,T"+JYl4tZ;Tn:->fq6YVfG]u]hpuhNdr*[pb2fI]h$L7B'SflQG2kk"M +,Leh1WU!k.I;*d4G?WJ'$eE(t5Pu4:;UYJ)D=L!m$%q_Gj%kJY0B)j[_AZ424Djsc +h0;T3n)4cf=i%!1>JloB^B8t%mt<_,M!_i*Ut],;RKfnR*<T:224'*#1sZZ?]/G;9 +BR4ijks&SegJV$%-i=RWp2=Le"<ZA'j07R+e:5;+60NXX29YcQp'1NsJs"16?PN8s +iFi'%KAec)9de!TqG*[Q'(=f*m/a$XP@!e)M4n]C?7"$I26:Tn!$&ahDiB"VS)K94 +'Bsg-M/GF?iEOV=BmS@FT_NS/nI;q1Hf;X>>r0lH5@n3s.G'A.?X*;\rr<24rLX!: +GjA0sS&IT(r(HoXGb;jE.Ik-JMGddmpnpg\6i1s&2XhF[-.ET=Rb#$GpbPOVnID3> +f6fk:X5XRU;Uo\8c2/N2_*MZP?iC?.q_9i;^oMq8IiNK#+"7W5n5;-Zi,0KS7W@.( +o8i8bq\Oau9M`L>'oW(lfRr.4m.Kim^p31>)L`pp^C.b4G]1MYg9pC"r%"r=n\9s( ++,PKSB?";8IO;\rGD'nI=`<8Y!lJ<59[R;#r(K4/iOF#K'l-`_J''^g<a#?9YK2Y& +qc$*dJIJhOBl=]aE!hbk,G?U2NJsVfON76lrr@[]5NJ;_d!ta?rm.55I/Te?oQU9R +/*$W8qf@.!p^L`mh$_+"!";%(IaUFarXW`>#*Joc"O-hT^M'`:`L[t;a1r5=YN$^\ +n%;NpA,IPGZ_i2r?N`7@h.u%@M**e:F7BdVmAKk3nBR6Vhh[m91sK[N3g*49I,4:e +rX2fso>/-FfDL!m627Y_''ml%IQI6sXaC-<QeT8dn(CM'h[6cPpa5J-';tP^plBq' +U&.T6e"7/"'PP87T,%A+C=tIOpf=u8m6U9'it]'$/>):D#3oP.^)[&+nOJL")uH>u +^Z&=HIP^mXpil7`iNBTg!!t$jT8NUOn;kMk!-jG!4''c/!4Vk>j/iDZnOJ:UYO%6t +_b/O&YO,U'<Ns4:cITAI4'&WXrlqEE=l)S)]RMkhhgZ^FH2BGtbduuVIti_FO2b&p +=8eNc*,5/=,5>70(ZUgRV.or0*5?4VK>.N$8$:VE)>HY[?O2$TpoCR]*Z=a2XD<8$ +pjM'r&D25s`P*\'dPO*=Df0Jl:V-+X!rgf$'0J53T8*%\1%;B,^MA=Yrr<s%iG3]0 +iTJ+.JH#VTI_YacGhZ!g.I_O^pj:q+Ld!B[\&+d4."CVSiS2TbrXg6.h[k/OV_uc+ +nLMMK/Fh;TnOp]%f>HL!g@'X+5N;\CLf&P6q[*9&_+_R+r+=N0@ipFh+n-PbnbZ>] +Sbk?Z4sg!misuB>-fK!]IhR&?a,p;D+7MgnN>MTp'fE&L'E6@M&b'^s^(a"WqgJ86 +`'4fo__6aJ+2h9($h"8o$fR/^9iF[ZDm+ESid^E6HtiINIasm:m'#pqiHD^#DY$3a +O6jR>081LU-ElIeM)Q>#M7cF>D&;3p_-[6<CZFl94>3LDrXkJuet5:Z5A%*RJ$s:6 +D\66(_@N_6F/XI0n@s^brr<XEr[mXfKtJZ#(ZBN:!.qiH?aK3R/3H)mChWY]LAq6g +)guqHSd=l$_9WMaTD!0t%H3odq"%#SN^%#\O+4M@S`/Ib5IDm+S)I.pIhD<+!;o4_ +X#rJ;kms]i=7G[s?"Zt'4hcd2mtJLh]I!'&iXa*.J*QJ'J&+j/AWM\:c[dqA#C(l, +^='_AHroeZ_Z'T?GcOcJdC_3/C79Xjn(D4/GO82a08hbmDi/p.UA2o!peg$TDq].' +EW)MGFcCmf\mL;Xp8@]Wn9a)qf7^:!?\og9e9-8A^&raknB^J4n2K:Oa8Z,C=R3P* +K75h../8#VLVd5-MCl440,&'V)>KWY%3NU$%?H=HoUSc^d]GC]9e0GT\(0lQc"C,, +i7'!]MU,59[RfiJnrMO$p4Lo1A`&&Xr*e#1TC't0I!#=NHi^D7YkU/sh[oHp]G'hA +p9+0@n4VaLhA,N5_hd`.FlM:!=a1bB+,I,5!MF[E9^r=hWVG3%5de)jYD[eJnBS0* +^Q*g%Z6'66(jo$PaUaDRIiWcPiGXB3$i;7(Hns0b/GpseBC'@&1WM]ur]KNH!4M"h +!0$h?F00oKHDg,7n@(mV/rfA0p/n]l=+pK[isgLR*Y[+Ir@c3_!:^!gNdQ-B93:V$ +_c4MB:](rc6LjNb.pN#RKY@rkYPUk<0_jDlU\7s^n%aF+*s\G3DNi#mDo]Om`W#r- +<dDGnbDYhG?8qZ$nFtlhLpS`9_r%[epP(<5;;mRrr]U$\i]TZE3bH(p.p)85L4"B_ +1%@3fkJ9BA4rO.anEWgDNT"XnG=K$1?1HNur'TG8'O,;FhBq_^`*\4)4RkAIYDW>[ +%t$ce9i#*bc1\d=n)AhKr*A6/?Wur3M0rIa4b$;trPl>)0,XK!_7GAVib_cIkb<q_ +U5C@nMC>3V.&)SIU5C@nMC>3V.&)SIU5C@nMC>3V.&)SIU5C@nMC>Zp5Ai.M19]F7 +rloNXJH#W":ZC+.!%)%mrrC.!i]m;Ih1+X2rR:cH_6KX'9tt@+q\FZoci$%Vd8g&g +2hSk\mt9L8Iq_+\HiWpJq_rmln8I[pnR'/j(j/%Tr*fQ3^*ih3nM0PlPP[H>$MY'U +@q5#s_-c\-YDX<#K_-d#/)ZGHD69s).es#D:t>4FF[$O4WTZ7omAGO5.Dl!=rr@XP +HnG+4Tm0`OJH#Tjrr?aTKAJf]G(no4"mDQq]K:E.'?,D_O5\ZPq_ruFT<g_lnFkm2 +poj/li[;T9N=3dU*rUi.B0RJ_UKVSj"36^7&UZ%.al&3spcH8T/+F.#DuTf.]J&7" +5JeHZnF)%;RuMU[9(9QKUj.)W]CACK4AFjs'3seu-D,4R=kn7[`kF=\r'B?r([JD3 +>=U$Dif=l@g>@b"6)ZZ\XflL9QE,4RGrO"*[a2d>)gm)4SNKL5n):CpfR>&2j0+$+ +'"e7EJ&94RIM@9BGh_/+a,btI)gj0<J&6?3rm*g:DM=s;@dCc>Cc,Sdm;)EEplD6G +U[3L"MJ?014eB,AM6pWSIM71PT=+u`^<PYUg101+NkQEZDtnJT!"!7kn<`j,pau$: +p2TlrnM9:d=2jY'5kPYGrr<3b9Ar)QrrBk`5A$P@rr>@S/`2K#$%*W\erAZ7pa>,% +!/su?rg0YL[U0aH(Z;La:C29h](d*A!!L:948Uqkr%ut'%fB9#l/?Opr%n?QKHEOi +nSHpW_QA;gnR'/:$\$FZZ01K<ifA\_.b";an;gT=ZM56qf7gtJ:YFdI*^B02KmYu@ +Di@k4NO0.Dj5F(Y5DFV&nP6:H3PS+ciIBUgi4$YNO1kqi-\-RVn3c,@`_?f\a'(E^ +Hr9nD$^9W-^[OGM8H/[C;c_<FdGXs:q]kf%rN'i5@GkLRIh$TsMI-@5BTE\h2LYo2 +!/.-@Ie\uo&)gaKlZsAd57rBSn@B.s5Hjj[?h#6*&$L6X!68HPL[AmI,KF+InTTsT +9D^So8,-`ef0Ji6J`@8qJ$W4mD\;n"n6WHu9)cntrYF)_$fg+PI`=UY=2^.3l5h2K +TBq"RG['0?LGeH0_1*%KINN>$p6FX/lc#Lc:G:Du`IIBcDBs-@BmT6e?\*Y.pa6T3 +ipe>(M#J`K2:]D]I@9^<g+VF2`@i=sr\F8.D>RC>hKV!QI+@oi!87Op?7>?ANP%W^ +rl>$=Z7h$ACQ#<=i(&KD(&KZ$rYP(MJc%=_hZ\6T_n$Y;4<*5g$V9d%KY/ViI3VPW +rr@e$0B&N6l1XsKKAk\tkW2GOVNmlh=sfMKBr5S_7[[t-Vs1d@[Cr-%]Je-W)Yd\, +A+7+Kh\X!92hl\piVa/'?8D$=`IHRmit$gU3T?*Vg5!Mj%-nr]rL\RQO+j2IF;l;6 +HplD3%K(\*F?KtJ=5gicea9`iKj+JLi[m8tG\59#n7BnO+-&HBnY#iE^M"^L^U+K] +n^$7)KR=1)13i*9^qU.k4rJ`YrKf`a>G$tJ2Y_4NDh9o4[,-Amm7Y?h$bp?cpa:!t +rr@bZrrBoSn@/,DrM=lHj5"M12#dOj[$t>K;=Kga$M\Dqh]5g%Hqs1inSA+1XfqM` +iih_AnRpgIm5sje_I!nf&`^%ph>CLPe$&P-iLc)n.dB22pi#YGrX)/j=nmWb)HXkL +JNjfs'O1@ua$1==!8sMV+h$oBKC=0mU3m[E0>ZC6HmeeUp`&/iT7D;"iMh;mIL5on +nB\n2LK\W6T*o"A]$7TCmuG%piZAhYU?>=_nMcI,,5bI2kD?o&r)`ZIiZY%1RLb-h +Fl/"I'DuS(mi6*=07uAEiBIA,)Ma,$1f"-s42N$Je+>j5l!CP^'N%:5;+20r72/Tf +'N%:5;+20r72/Tf'N%:5;+20r72/Tf'N%:5;+20r7=;ZCEIZ)qIb5b9CS.s=ri1G` +fD./rhu6+V')qsaB)_l2!(n<I]0H'K!r@7.L;1)[Pa67MG\d##%JHnf_YXBtHhNF, +jo)XAj3$>@rr=Q^R>(5kCe_[p+84OZr:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td- +r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq +8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5n +d!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3 +dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_ +h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'W +q--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3" +n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td- +r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq +8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5n +d!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3 +dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_ +h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'W +q--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3" +n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td- +r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq +8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5n +d!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3 +dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_ +h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'W +q--A_h=(:3dQd5nd!tjq8+td-r:/3"n=S'Wq--A_h=(:3dQd5nd!tjq8+td-r:/3" +n=S'Wq-.2WrnQ5HgqU*Qm=uWJV1k-,=oG"UN-ZP'%"VdE08l'ZQ7Y6GM82>,Oh+)C +L.52DO7H*:^\j*5-3!tLc2ReCPWaM<rrDP)=8\7hg(42%^Y/Verr>jp!46$]:Mm81 +QWI5!I\!EPVT%+*]8I?#V5uUD1HhA&J#HJNf.\UR\W5H=/W>h41F0r7=8r88?[pG3 +Xaf7.!"T#/o)=^;9CM]morn8m!9)i7?d\hOIK':6hR3@`J,DD.!6b4'[4f;Y>LpRX +rr@h(q0tp.V>'orHIr,q!:]IW!&*R8e@tq_Tl4-7!5JLRrrD0Z+53(=oI]B9p\t62 +^\kjAqbm=jKtV:$rrDh<rrADXIqi>?d'p-,hu<ZdrrDWhO6oU/qA/b-qu6ZThu7!1 +rMb5F!(,PMrr@QH49#<(qEMA@^Z\nQ^[M1&LO],L8+reRr<mr'rVllen,EA@kl0)G +rrAa2>5nT;rZ2"=jo$:TQf%EelX0Dd!5lSL^SC[(qu2;_C]=>6pil`<Qh5cB?[pG3 +Xaf7.!"T#/o)=^;9CM]morn8m!9)i7?d\hOIK':6hR3@`J,DD.!6b4'[4f;Y>LpRX +rr@h(q0tp.V>'orHIr,q!:]IW!&*R8e@tq_Tl4-7!5JLRrrD0Z+53(=oI]B9p\t62 +^\kjAqbm=jKtV:$rrDh<rrADXIqi>?d'p-,hu<ZdrrDWhO6oU/qA/b-qu6ZThu7!1 +rMb5F!(,PMrr@QH49#<(qEMA@^Z\nQ^[M1&LO],L8+reRr<mr'rVllen,EA@kl0)G +rrAa2>5nT;rZ2"=jo$:TQf%EelX0Dd!5lSL^SC[(qu2;_C]=>6pil`<Qh5cB?[pG3 +Xaf7.!"T#/o)=^;9CM]morn8m!9)i7?d\hOIK':6hR3@`J,DD.!6b4'[4f;Y>LpRX +rr@h(q0u+EEFZSf[F@V9;?d+V9CFSE23kQ"N0c`knPQ5c7AZ*m;f#e;\&:p\ET +2VbCY93&IMIa!impK)RF#V<&/(-+$#S7/7-dTI`M+h!>tY-)E+)gchF1.n_>,Jj>X +IK':6hR3@`J,DD.!6b4'[4f;Y>LpRXrr@h(q0tp.V>'orHIr,q!:]IW!&*R8e@tq_ +Tl4-7!5JLRrrD0Z+53(=oI]B9p\t62^\kjAqbm=jKtV:$rrDh<rrADXIqi>?d'p-, +hu<ZdrrDWhO6oU/qA/b-qu6ZThu7!1rMb5F!(,PMrr@QH49#<(qEMA@^Z\nQ^[M1& +LO],L8+reRr<mr'rVllen,EA@kl0)GrrAa2>5nT;rZ2"=jo$:TQf%EelX0Dd!5lSL +^SC[(qu2;_C]=>6pil`<Qh5cB?[pG3Xaf7.!"T#/o)=^;9CM]morn8m!9)i7?d\hO +IK':6hR3@`J,DD.!6b4'[4f;Y>LpRXrr@h(q0tp.V>'orHIr,q!:]IW!&*R8e@tq_ +Tl4-7!5JLRrrD0Z+53(=oI]B9p\t62^\kjAqbm=jKtV:$rrDh<rrADXIqi>?d'p-, +hu<ZdrrDWhO6oU/qA/b-qu6ZThu7!1rMb5F!(,PMrr@QH49#<(qEMA@^Z\nQ^[M1& +LO],L8+reRr<mr'rVllen,EA@kl0)GrrAa2>5nT;rZ2"=jo$:TQf%EelX0Dd!5lSL +^SC[(qu2;_C]=>6pil`<Qh5cB?[pG3Xaf7.!"T#/o)=^;9CM]morn8m!9)i7?d\hO +IK':6hR3@`J,DD.!6b4'[4f;Y>LpRXrr@h(q0tp.V>'orHIr,q!:]IW!&*R8e@tq_ +Tl4-7!5JLRrrD0Z1/26O.,IG_f"+m=B42bC1ItL[YmD+*j3&s7%bTc2OkU5#bWnBP +IbND>\qfW<qcr"29U?OH+0s5[!@1R?E5VYi5DtbXX958S_5!;F'kE1jGi_>U)+XK\ +Gi/Kj%hemBfOk-`rr>Y4^Uepi2CnbuYO)Sp!+OjPrrE'!VuHbN0#+=O"9(DqrrB:T +M*LYWeSAoW.&)SIU5C@nMC>3V.&)SIU5CG&o.o!*16NMlDAbtj&Af5+JiNrF#U>J_ +^,5eQIq;E0+1+Q)bO>W-K)!UMrrCb7`*X0A48^&mC%7\3[iQ@aY.n_4KO,GBoj<Rt +%FKQAET,4PGQZme`a8`@)aj,p!/DY$3^/^m4roCOimQ7:NCN'ml:,@]#Q>b,q^d6C +rrDFdrr@r[rP&:3qBfnU!/Gk!b9-?U!;fcGik[it^]'(Y5N1IKZH)nC!<1MUpomQK +n,EB^Atf5)LNS'g+7o%";Ld\Urnegr'(g[k]"_eeq=sof5Ogu<!,mqtM0_DFh-[IC +r;;#nTD5&Y!4U#K!(=M3leh2-?eN$B6Ih^G?7PoS4=@;6NQZ"l,.IN]5A_JU^5&_U +QrrXK_gXK<rSfI9CSJu3b+81>&nj>7OaN:\Hg<->G"Ulr!,!C0)XkY;fg#1ApoWP. +DL>1K6f@.onaRR:B$M7\XZs;-F5c!ZGb\@VQLQ5?>M>',<Oin77#;m;m#;TW5T`X" +/H5_@0kk"g"kWbSoXi"`[7(d,72/Tf'N%:5;+20r72/Tf'N%:5;+5kJnB9b9K`;$e +Di=Vq[?]pp:DS=grl+XYn[l]R"o]S<8)]NLYDllj1g:tE?eO>p5K8[a`*1F+_!`@m +3n=..iXJi)*uQqA/2rE.%9CYHdBm13hbLV36S*lG&,[*Yp/NX`hL<uY^qb$k!!NH& +^5E$<!PdTj1']1WH_45C6h$;Wp>62V!,9d5&)[\H]M%;2&:?;'Vnufo_rC-<XaVPj +pVpPS]MILgBB&*3^M,n:Gdfqna57JOh\:>(>4C_t!dm57kr.BCnK+ejQ[Q#PE*:?n +S?YDg^MH^A1!eSrrls3kV#LGE-fPSbPOFM2!9A+h0E$WDrrD\blhdU[!/"aqjI6(p +J+bJnci4"AJ,Q,-qL6dUj2QTG%="*:!)bCL[]$CNDtktEa$5KSZ\SGkA+m\4]C7Jo +Rd\-9M>AQQn@-<H&!$Q@//Fg(nFiLE,NH8MmtRDc\FApc,SOGXIpKVSDi=NZVs,6N +d@/+ohA+NmmJKK59j^<Y2OC6*(-eEun5:?bIh;V[i0OBC[9)^Ci\/u/iciF;:I=i- +q^DIE_-T6RnO)9%m1]@lrmX0?2o@k9]!q%&q]4r$!43!ILilK(IaC:t4s8^,8,E\R +2hu6f06R9pKj)7/h]$=!"oc!iH]JmN$X2%2Vs^D]!.pL9!5V>+/,M.%`h+88.""5W +L&=18!"UFOl2L_dchItH/RA,UDi`ce^Yr&i!5hq6"'Y5M`p\e7+1r'B*D>@(r$Hk_ +p,8Nr_S:b2%fRi#C]4nF!;K/>1k-<hAb_Df!!k\.+S^ZHBclhDIubZZIPq-*_+B,h +Dq]-h2sd;Xih+n*a'\=3^OtFMkJViTr!!%nr'12]$VTJlmE+<MDoE?'(8%pOK75D$ +e))#A3;DgURWKcRn2J/?KK@tBYP_;u!/*;L(0]n>+,'V/f!TFQT*p*Ff>$7S5IMnm +nQ3XnZfd&A)gi%gRIM65^[P,Spa8;mRq(JcM-g=6C9Q9N8bdr-ch?`#47QClf/e)_ +n/p[$Ld!)3DuH<@Fl8l1r$o5)FRP+d,*1`dJ`8<cpn#CC>"?Af?I$_f4ta_NXM8Y6 +MI%_0XF;.<`a?+I_OoMnj/7jNA5DtPTDT;u_`e_.`Sl:UI!bWAiKf,i[J2EF;rVDA +j0&sj-.79W58S(G5K/,(]!$k]rBEj&]Pm=Yih+(P*,5C"\,QG]Qf+Am3WsM=?_h$8 +5A\*gqd06+r,M5h;u"9-)LPliNIPKp$fXjpn5(dlP=#5>^*ip(h?3O#nJB*W%(t-_ +07f`*IOY'Lp<`U5i89>t(&SM]p3o""VdR+*erS`Rg%41QT[>l;j3&O%hB]>pDNWn- +$pW[OIinY9Ib$`g\%lnlC7bhq:Z1.*-qD.8g1SAH$2eRYq[\/2hZLMIn/K='%R(87 +*uk1oG`,,fPP[prPMpDErr<33pecC7?"tE$nJ8=tMno>mZF6IdI6LF7r'YmmKU1II +d<lOGq18E!HuA`Lj"L0Rn,*aOim7!hiZA7$fDbj?[m/fVhm76SIN%Ld)'JmPkP3Zu +VdHh0Tmpk8%:B$1nP@/q2tj)/rYK4-]Kc@MV-A<le,3;*4WiXTJp\pJpeSpP%fS)/ +T1.=Q?dN/3rrDOihqItIhZ5t"S):[;MgRV`2bWW^rX'hK!!NPT5@[nng!@RN%K7a= +h\SHcG\?"H`@m;P!"4/hj#`=UB2eTmi]dYF8_8>/F7>efnILL.,Q3pW+)1^:K)YgP +d570aGpKhEc'B14hu<Z[L&JOZDu22;_(S#hO,Q7,rKMfj>lNS%[mu.%@G_ZE.JR]; +T8VE9NNGG(5PU$(hr4!cn_Ze1OlDFY]&&/!+8A73hhLu=Nhu4lqr-/Gn+]U6T+BiS +ZJ4EkJZh6`g\;TCrrD[+qb$d#rr@e%paP6!MADd%rr@`8`B&MH,G#SDA+8saBDrL8 +pf]m#r[7KM624aT0B"kf-.;$]et(`kpfcPlr+4f)LO\fbG_8eA$2>J=&o[P<]O>lZ +pfg5=2%*@VCA9C\!"Abo;o*qkmef`X!"$u/FT2@98GlW\45qX]2uFmbn?dX%r(lj# +^%"VQVo>;'6t4\hXZO?`n?otbprg]B&O54VnG]*['C[sY,iA*IpcdG.n@"=Ol1Vm5 +Do+_s^E,QJJ&/et!!`H'i3/RSm4nK>GdmCdpeBeHnTM<)`R!8Ep5A^bn5J@jJTL^, +Gc17Ee:&3]AG?E"/)kc0^)#uT"k5^OpmMmk6bj``pj)Bc6h+fCr%6qn$N*<=4@Nh2 +IB!1mj%oG$QgM2]lJK&M!/\I'msmXl[X[7trr?qt(4OF*')e#A!W*0%g`IO6D=Qf* +56lRQnAiCq'KfGUmG79(`%leeIg#[liXHu7pke%5"2Omdpa>'\>JM.POl9S)<RST* +IrFQIf81#ohsa`TgQ0.BZ`!^8VtS"-!/)<b;7cN_rr<2`rr@Y5Gjk?#rr@asrr<>N +`T]Q5IaDF1r&O<-i4m//!:91L9>BFtgA"n1=,spRiGXPpgRmuCrrD!pq!4T#7+_,# +m*GH(*t%Z/LAq8-*uB?4rX(%Q!!SSVTBr\XrmF"GIKXHd!"(nEp]pL]poj2-iA\]Y +KfV`jYPVi-9@NfEibQjRHm6#o^Ypg[J&+I!*@HQUA09KCikL2Srr@pXrY`3??a"]e +p41XT1#d*ZG]`_Q=M41u`4<SdHoUeKf`Urbrr@Y;rn3n^rr?fY/&D'(ln@l&p2g2" +Ir:>%X7e^-r-@:-1ZS4d^,=gJ!5bN#Ss=0i4):9d_JLsEqA4^*hs<!:CVCnAIPg>6 +Ii<\Vrr<Ec_Kr)ciNE?.A)Q4KiEIrNGgl6dp)X+GrrCuN5750`P999eO,<U\)a4s` +D"3kZ_uB]?"n423CS:IKp5Si[n[mf$`*\:+)>Kcmg\.f>[u5WHnLqqgIb"9!X7>(Q +d^<EbGUcLE<YfY%+Og63i&r:Q^cIs0ZKC=AnTKKVGCohpi30o'ooFsD*o6`]nF,`" +ih$G>^[PGjC]4U9nR"TO?c.AMrW;UKNBBp!U#R"Q4o86,4n3F#n_<%mQ12g\de'U/ +L\?&Gm.K@6"ajS)2=3cK4u(RRIQi!sgW*JEPOS#Kp.,,_K#rJr?aU)hq\sp%n?9VL +pfK`jIqdfo\mL-j!#!!MHoq:[Ir5WYph%k*qd&ubnOqi;LW99[rX(BZG_a4%ZF@ff +pf*FOHb^[FpnMVtHm/!=[^#e/ipR?$(W+qQ%dR;rL6($&rX0,2EVSGb?N1"W?@LQL +(jlei./s:a\c'0*DN>/(_nI>i;t/DV58U]Q)d7a.J+4'0^VekPj1^HA,K1`<FhJ0u +5OaMF^CBhY*D><bpVX1mhsd(K4s.Q0A[f^`"($@J\*u&errAjEi/'WXrNEpZGan[> +@<_8A!;;8r^+oCAiLg#^rrDc9par@V3fej+-`;g.*trlT;#!m6TDU1r(AQMTM>340 +HnkSh[(Rsd=gRTfm+616Y7YLj$e.%(eDg8PX4n#q-N!PGMQ)+*!$4)6p$$Pt!2<f4 +Qi7<OF;+?JrIu+5O4`dlA0"HaK"_O5d_D6EnU(6B_=/Q,=n-G./AE8*dD)n5mDo5, +"f`XPJ)M_<1%DB\`=M_kDhpRgrrBlK8&7Fb<o[B_dFU6-:\I<WUOESu+o_Or6@9gI +SIa\.Q/W;pJ&2<dSac&q9^+dL?bQH]^[P;Xpac[;>PgNtJ$q;RDqH;M4tHT'I!b?! +p@e%6_dT9RROl%j_nV'N""esKrrBqgGW*J:"89\'^W0.),Q8HCNW'j*_-`&U*RK:H +0A,a3,KCiPdsP*`mdj?GIB!*nKDs2Hn8TiCTCMO4cg6AD:C_aRn.)LlIb.pJ^Yk]4 +"ScWh/N<B5rN,Y9,N:r)rEJ4"]m9VX"blK5__2mCIP+D$lbDr-J$_)9O5ShU`)cW$ +[thU0i,c$qMoA_H'f6p];Xrn7qHNL)=7Le-#QFc-h!iANCZEa!?QQQbfrMtSn4>3I +p6sFD)uL9GTtWO9bbFRdQG<<+ks#OD]PleZ(k0`e&:P=<qo6Vd!9gk;%ZB;L)>L+: +n:ubrm9<QZmX_3M>5l.4l9C^Zn4"Mp.^/aaU\9[spa>7kU:5UQIPUp'_GC%5?%18[ +)h'erd<`RGh*9p]GU)16r"U=58*lf,7aa0?"'b(p2oS!&VsN$n9fKuh$aT<RLZ(C+ +`*X:&#lZLT=8:D,O6iuq_-[a;RJhtli3PYV8*r8*KDl0)Xn]fO^U#hNn-Aq"Vm$+# +_+<uGIMqp!iMXX_pqcG)_Z'V@<$"//"6fJD*-FK,RZE("/F39g\^>L*Hr]/s^Lsa) +)KJ+JZ#?F_<lRj7b3MIpRWZ6dkuXMirh9>lM#(398*ibG!%%sJ%tE98rlDkNY!*cU +g!u!Fr/=f\rIK+k!<"P>h/-"X'N%;K$i8KB(&L[LZc/QFLVl?3?P5S@N<AmopmM-^ +"jm&5q_*E7^U8!<5ASRekTB:ic[qeYXSt*T+7[96ZbOJOLRojkUqbkhrrC.mrr@b9 +^'".%i_S6/ib/0A]f0<,pgrrM_uB]M4@PoX4ppeOmr+-a!8sZ&*5"N+5Q2&8i2]'L +r$[n(rr@XVIa-aT^+fHb\@U&b?M]G8r(Z\dp-JJ7nc&SlZ1u3f?93hq`#9B"N4^=2 +BprVBJuq3YC(U/PHg<c,:A7bShc[D_?f<sA(&.\7'Cj]5\*`GM"2\@brJ`oSV1-@t +n5$7?]CssjeGMc)hrAp>2%LT"S)`Dm#2ch<NIDCBpb,>9mGSFgD%"9'15B2@n@uq6 +4s@b@HmA.,L3/IclJDtu4DMSp]^aUk5M9t*+1C(FrrA)Y"9"_g+R,N&O6n/6K7@Pf +KcdYa`@3tupik^.m";D;,Oe0pn]TnCJinI^4iI=OrM@4KI!F98083;K^@O\]7\a\p +4BQit?_>3H^&J(a]Fh@%?Ka-/^W[5-09"eqp!<OK]D`2_j'UN4`ubXQCp/R8#J]2p +rr<a^$V0\ninoI)BrrUUS&F/%rMoktA.'t:rXaQ"pe1>*e,3eHX7gH#S[Pl'l<b## +!+rtQh7fTA%hf=4btI(mMYo7W^#8nj&:T5Urr<4FrOCV%hC?:L[/H4upiC`\!!Ua> +rrDs/CV_&,S)CKhl25DWn*M,C?\dTYm4%%u+/eKhD4u3trXdn/kJI(VO8]W'*t<T* +rr<EW8^[50(W4!&4qq=JP>9a-.>8$)(C0Z*^A*WUg\,*gn8H1'"6//"r"KtH$:4P- +r'U8Vi4ms;p6trW4>X!^BD)W#++nr\V>/Bqn*E"&9B&C\a+i2p.S9/G_-/1n@;#-! +iYJ0Ua%u2+N-o]G[_(Me&K?4SrrB@^n4kXj;tc%GVS3Z?<S,!"Dh@iS_ESoc2#X5! +/c6^[LPRg;K&7GqB0AKQJbVaoC%P,4iKrp@iI?E.+$STP5N-d>j1c#tT%ciHn=PMa +>N;^L]M7?l&P2mG[CMml_O_YCC&\0b(g_[=+!,o>5DHq[%%:8bQfF.\b$TiE[0Ei( +bP%V-XM8Y&N5#pR@P<W=!#IKLM*D`()#Q,X^Q$!@^CPeI?8(i6c@t6SBsaEU`0VD: +m%**[/CWSPM#:Q;0+gk;Fmp3(4A!EDrrBmOJ$b8nIq8$&"897o-c/+$]34WAlpLY/ +HkH-dnW1RKO,gS!8&X-Er"Q'gYK!1DHn*`1:[kah*Vf6rX%Mo+r"JD6H<.;NGc/A! +0%M\g-fQe>3ei[o)Ye%:>>_iXr,q";!IZ3m!!Q]Xrr?bm)u^jHV1-fU0)-7Q)fPWj +6iID8W-CPIT*;qLq#:?P\"EB?pg*UM+RkGKIM6thifD.+ZMspA48keGCH=Dm9eYO$ +kPQZVn<`FS)SPoOi'7!<2P)JpVL0fl!.r)ediLRM!.oLr!5TlZ!,G;9^u#4\^*A*a +5O_Q(L;1"^`Z5jjnB[0Sn5"f<)>MD8J*dL1f2L9b)X5j<'5oba+3.2k?OAnWG@@KG +UVr`Zcm%#DFfFdk0m.,M@pu9[?])VVWh1=r-i>"`]`%q\gA_-_rWW3,ptl>CU]1;t +$h4D@r"GR[Do*%=0B#E\]M7loiP375N&P&Zn^#(5g@N4s)mt0N5ITa+Hr"D6=[!r& +^=2#lLOWlr(3l<?ZhWAHa5KgQ1k,[V&^hKTH2BLL_+MIWm:5inrLu.;frN"tZB)eN +LHk9$DtkV#lkApLrX^.kr$M4'n^maU^9-p,p'K86Bs7:uHlq@OphJ]?g@)mc!r+iD +n@jPE\&77GO8D4fg@O\*IQ-d=NHM;Fhs:V1Y6TQn@=N/Zg&o6bn54+\?O+4Qq^D@l +piH<KI_u*bZ7kUaiS..b?5`OOnUJR4a8@b)lm6fO>&+';!]?o=0C_!Z`eCVp]3UIr +d<+i;ei3R7rr@b$V"d^&::^--nRD+L7E:6K'RQbU'qb32pmL-Vpc$+0hmTH!m=3/` +Z_t`[MZ3YXrr?N3NBSAtphTC=?d\dgnCRXU?cCpUhceS.25:"@n@W':f5?&^Tm4aZ +n;a?Hf\(M!rXp4uHhd#NX#iI:j%`sN^(5\AIiQ0Dr*97%K!KcCp]kU38Gk7<&,QP1 +!5Uk3Dl7tJrK0>&U;+&gMuNcj?]/XqIXF>3ibui_LS#9fD[uQ+p6GTOrZV&`p29D% +_)iTQ)16A]%ud+i4r\luK2L+j?\V/B)G^NDGh^$Sr"Ms+$:jX0`#l5XlrDuEcO0Ru +Mb=!LJti8F_'-SOUAk31IQuUlBRVU/6c'lkpjN/B"N5rQrf(FWH<GW<27JA5/"[]( +^JiT=4)Z%kHpQ7/nB;Y+G++tfWtiX\F5_VSn,*njp9`3G!(ZoFrltEJd#P1(%tDDL +5N&$B^VfLHg]#%h!4I[Mal76mDrTs"+4t3qps#^]?WZd,m(q`NLV=5rPr("lOl4Q? +SfP?9V53mh%;Se(`?%.LnJ(h#OfS7]_d@!Q:&b3F^OF_j$beLr1P>CWpc78>pa<93 +rrDRZi*YiOj,\mX2#XGg%=E9mL[`XLfBi\1Vo!6/c\WaL^Vo$`hr4$^qurdC_L=Q8 +7_:"VnBnXNN1[E]`G;(NBHIT\-@X@7&H!uSpdT`7r&!q6oSH!N;+25ka6\"0MrJ[Q +'HgofIr,Vpocp[,/c-3AhbWEBO<:\cr"IPqrr<<hi2Ib[:B>^Tq\)9q!.oB=$XD/g +mu&:W\pap49`;Cai1kc95N&9XXaG*_MrP?C.?B9K)Gf>\M(.@9JlfBAAZtIjnDE8m +_EARN4u`"<Oe_hsnAiLT''MM"D[ok7#D_C3IrF5uM8/?*^L@f,5P%s+i1MIoejo+a +rr=B+qd]X+ft2^_TDF6=6Efs.n\>!^*[UgdYg*1JrffQqFT2@1CfgOu?eQ%i`P:++ +!<3$prrBE3Io"c'r-6Qf!+E&DJ*KJ&f$tMs*ut7aHmATd^(L/3i((h;!U*l(4q`6: +d&$7#NI2K#.&VJX<R]fL0.ee.m]>TYeT=N07+H-*2=CqJUHuZCr(Z-''5;b_M>T*/ +koM"r1sJeIjW![`-.hKjiXJ7Re\$=gT8NXK)"h0)iCCm0Jp)I4m/d)brmm^K5,$pG +LL7CnL2FR<nXE8JAU3gdj,_+dT7jfE`(pTk8)Z*4q!6mlSe:MRhAMXR6%d,&Bj-Oj +Bl\[NT<Un>4Ce7>Hl)aO^(br5peg'9#6)n/n%if.KO51;."#(jjm[n!n>;iI.n9NR +pg'ls-iL.G'?T8VNut^c`IEWJ>BkL!dJbi`SfINniboS,B1)3-_EDqk9Xa`,Wr,KY +GJa@[ZKM,tS&,qJ&V"Z$M1;p__f(5I?BtC`r)`iY4t$7bpdssNj%m]K*Vc,^Di[+n +%ftrVIZO@ml+aZEWSu1Vlh6"]Iq^!^?P:"[j0'Kulf)6=UZ`d3DtOQH`7h,K4tHP* +eUB#nn&tS]8cJdB8H-Fd=,j$NQ#Z;ca'L=lNBbHZg%4p&^%)+&`37m!p8.Pfj)%lX +?gOhhprbr$TDWE*GcThB`h+%4]&3E9I1+*P.+*/*%Vkb4^(]%=S')7&`**YUB:sKq +*@n_kVu.34p*oclnZQ9tU3pD=?i+?n+,R]mIa)6,Y.*F%pfQDhFT2?Lr+l7m4=B,Q +"+JPP2#dQ/InT=fQ\DQViEgb5`P*JCrrD!t[*^K'-fX%N&%;8r61Hk.Bk;L*L%%n; +$$H+]rm)W'ds`D:.e$6QI4sP+Q_AZi_X[O-nOMtNqc\A@rL:2:%/`fJr(X)6qnp,l +rL86`r,M7f^jd*_?7V)V4sn"ooT'Z54Os*"p"`s7`.Hf-6BGr%*t&#Ji0RfUJ&/=E +^B$MJpaa0F(%-H\q^lJf-1J\9jX7tgg:-d_pqbp]Jt<kMIt5k_%h\S\iQ$It/pq[/ +a+/OjTk[m7>>GYcOf!A8*Br4Rd^163Df9^Ur=XN@dD+V*T<QXsFT2=EkW1*-rHD!* +Do4fn8&=)j^V[1/[bi%<5Gu@_KKgOU"laHV?8:rqM`qZ!+?pR0iJ59U$K&eZ'g-K: +C4?[V*;E1E57;j15,i$6M0p,?gN[eW0^.\OrrCuCT<QYZrr<^[4n0&:^(9P<RTI?J +MYbrF5D3AMocK1N+8eS)2gcqH\ZP-<1"G1%T*p-[D\;nFe\>_"LH=jWXa'jG`S0L, +-1GQaN=lbWRdREVhghBhhQ(@gh:ccAF8cKJq\8fui2>qlq`B%Pi4nu8_9^d:j/.J+ +9)d,m&,uW`NU^WLrY=V9^C9nD`h#3<4DFfq#mKdXmX.smhm%F(iZF+::\[FBF055, +_KS6!+(Dk/DD^`F9=2#!:EOs"rr@b.nZUj]`=6b(]DO8M"9'(6M`a5nn,"&BgO;$T +kJDCl$bRSOBDrP09ifG7[f6>]H15mo^U-eS^\C(_FT#jhIP(R"L$em?$$N:bC#Yb> +*X:+G^!aYSIM)S[_'FM<T7R"Bn>>S8!Uu`%?gru&cTXC>ME_5-)#3:fQMme3/Gp4W +?h#rO.[b.&,'Z?1=2g5[4uW0rV"j7Ie9l,mY7Pr.`R+B!qgZM4/Cs7e@A%?RDi/qE +@Qs*Urr@XgI`M;GNP6]`h)kf"XUG+E:QDXX-%'A#F5YBu(\WmY+7/srO2foAPG78I +1=j/kI`u+)ppHcDc@6KliFZd"#+_Z>?dlZ+n@)]OIPKS8W1Od*4CA'!XrU$kJ(^=j +#g)!6.ouVWn,EB(X+0nLH5Grfrr<3Hpu2*YGU)0SL-K?ig0oJDm8r8=MnEob5DYp> +F?bh9ZXS0G^d#1*!5sp=BV$jGPA4DWdl_:4fA^\!WnUX#CIQ#.5Dlg"+&:9AYH,Ai +J&*C[@%WH$n@:,5J+0)N?gru&d,$fZME`'Z)#,'DQMme3'7:MhU%'r<.;<\6,'_#` +=2KHH4ph!EX*Er2e9hqg?P%I7`RB&NqgZPU/C?%3@B\r7Q\p1H@Sa&=#6)kS"[?VV +pfp&QV7[WE!!m680@,ZN&:\D$Ma-`/pp7nGn<Noepo"(Qrr<AgNBcF(f<sN&0C@Z` +/M$fLcOGA6GY_$10DnDK*C%l\o=f+i^YkEMYg8)Hrr<5d3[_cBG>7RF_`ujF_;DZ8 +_QS^&h]'9oVntZO59B_+ibsWZ1%<MI4qE#`589UGLVd=(ho+Ce4E7E:IgnR]pe/Lh +$[bN0WFI$,Hl&j<G]UbVkki-am;LXTreS>ehadT,VuB:I`LlrhDu)%-nid!QjuNGA +P"krV;C*CoJ)HfgZ2Xg?rED_'pi546*I)56hAPk7V=?(Qrr<3GrLn^Sm6gSRn?7hZ +%i\mR9XiTc/,LW-rr<hB&(ulY5OaX09@Op?:Ok:h$fNG)V#3+FLSs>W3;@o1iL^hZ +B>_<hQ?[KM$*F(=pe0PCm+(tpG^nYdp4'p)f`U93KDS:?cJB*3!/6F&B(,^6kJk7F +pp7u33RKs'rr<?/muHg`;rZ]>Gc0OCN&=E+7m(jppm]-E5MI8h]?kFPm6=7NZLr^$ +rrB>p^e]Qs(HsDJD"pld4a4\J1AKt6lQ.\RX.so`ia9YT08+d,n`P_dn<LbF^*[*4 +!$3t8JGO3'IhYE2Ih+Jq4tC<=%J8P6^*K:5?KYS`4oN]`]M1#`oSjSdr"ERb#:/#. +!'No-nR!_p_*1%/pcUiMJ(XWfrnl2MVr9;^Qf)5&Hq@eR7e\qcM``*N_nuBnnOJ@W +8<o+dCDm\lVr$[,V1le,EG9]8/5jKaCZo]@r!*0$KR<\10DIrL*.PgSrr<Agr%1_> +7XsA/nFui*hsduP!9%Sj!5e.'!/6L4MuB:HS,WHpkPQr^pq+<1Qi3EQ&g-mOrrDRr +Iq\i^_H6a;'5I)6%Xu]QY_RkF0AM$'Se:pi5@r^Zr-Z0/]N*sl^Q'?Tp_E;-6hnE= +idVt7XBTr,`-3:9oZmnKps+;7`\uZsfBrK9X@oZRU#HnHc!i82ipo6-ok31OQbhS3 +hnU?'W5%9op_Wfjpac=]rrE$3n+]`:n;2N:pj:u7pekJg[D(,i!"2<r'c6R<J8L +>CXN3qebNo-f=EJrr?O.!5`7>]:\jh^U/CaGFoh'%fXKJ!49fE,<t\Frr<-#!,'(# +"9&k8mB?:PJ)PSK,5?NT>Q,3fpdm]UJ)MUO!/,"a'>G&nhq10Jhq0t3p=K">fful/ +Z#lVIH2dltep^BgKmXF.06e;OrZ(ICJj'uu)Xqs0r#P9n8,*8H*uB0'iQm.(4lUqu +r!DldTl7!q*sI$PYNZrgSu\R_-.)<h4&&L@pi30:gA!a'e1GP+Cp[k.@#@tZ42=)M +9^gWO^q[?0+7N)cnHPnpr,C&+muIAsn6MTo_r'-siBN&Z/`M96+m-0%kD8ej:\Jr0 +Sj!P[;>B%P>BjXSnJ?j@3NR\YIhM`?C)U]VipRV,P31e$pku4Jj(807nXmf#!!%7f +_YWE(Y'?j:d^4EQ9tJ*hodf?qX_b'iD.73sDXOZ=FH.1\M-<IkfDqT@f?8ZK[[F(- +^TaX&>GQ#bG<0oHVLHc3Z=SccVqsc6pNALHT+?Ai_0'I7"G(*<ined%Ar5#"E[*(7 +5o'(s@aG&S,8QRE!+9)^rr>^)2d96Y72/U+)B\`%a6[e!_EZaui'mCUr<ld'8c*%G +=87dFch<Cg=,=9Q)>KOopk/*XiiMtkkDXe6Dfr8YP>]qY&$E@AiI;=&hA;O;jo5=6 +i:q`[D$NW%5K!7Y1#a3;f2IT]pkA_J"LS,^IOj\)1"?1Hr#aO<>BB<jiCAM!%/aIn +<VYD?rUg*iNh-gWD;h%$_I!apO4jh1LZ4OZ=3'cerr<N#X`Ad'LOU@o1>2H_j-P=E +9r7V``kE/^M55C>_*/@\Zn:/8rrBkm^LEgi*Y\>Ii,8^F(]M>A\*ZWR0B$:)ea_*] +rr@b4nRkf#!/0OnVnkTLQ_1Un_]K'\[oi1WTr@a!rXkcTRYBXOrrD"&n9s2Lr)p,s +hsc_PK3m*@;]JupI9l?WiNIi^%"'l+GOTt<Y)E;[#iEOlKcUEeO2G<)G^'$T<n9L> +4;6XY"&JF$Klg\hI=D1r<S)A#m&^XfYd+2Tf!RY2<ke,ge#?^2?;!/oT,N)Grr@XS +I`"AbFT2?p2aQu+_>SOq#6*2bmAGlWBO?M.*VeI\rrBjL#lZ%Co%V+?%6S,Z`643p +*X2>8iP1*g*Vch>GamY&J@0;0Hrf`tKY0P>(WH"1g:Vk`nNO3pir8ur6a,qX!5l4Z +@\<K@rrDY=+7Sinq'b,9J)p:;kd5i\p\t5\kPe%3rCE3-IK'9M[/G_soaO]'ZhQbf +pfGI=VsP`!L\A2!'##FbDYX53nB^+VM=L'.K(.$oK>'8=ZbbN8rZ6GbnAiKI!!6h@ +'RV]FVJ/4NphAZi(?chJFgs8f)G@gK^!;V6c"Zip_=)u*%_`'jB)BI&%WpNM)f94n +](&:I]L1^T*I.28YJacepmUAAdkjAM08NsMr[R]4`h*qD,&=VN):3agi6<3Bn]-R# +6)\H<"7Vq8]EP$7C&SS2LPBn6/)sE\J+-$jfRMjM_`^BFT7B$Kn^Bbu+fb;RifCu] +rr@WM!:\^W[aqMK;L\iIB@&bHiP14Q0Dm:?C\ES6IN6?,!!ssDNkAAkIqZaYR.Y;< +`L=".-X$]QQ/c'h2oHJY^*<RWJl_ldIP91;oUcoucC,Q,1ojF)e&)+bn5&@E1W3TQ +plhiEp><0"Jc&6<Gh9c=%W1S*58i826a$2O`a0b6Hs>QZMm:::)LOd/D%fIl[%E.t +R]`,mG7O#dr"F/F5Lk8B"1[l&J:)M0(L?fMn^HGUm2t9$)uNk;TmpbEMr,1)YDU'r +5N+/CK9oY('YYsm>4HOLD/[5)%IZE-j8E?%Ig:,r!#01?#B[(ta8S3k'KuWuh=7D" +[2i>&e%ah'J+-5s,?FSTX6JNJkLmW/8c3\7rM0>rBLCdo`q__1n/LAhQ]8DVM"BGM +#P`^#`2Pj,n5$j6+S"$mr[@<?_;Bs\*UoE3h]M.;++eAmDr<hkJ&8kf)Ye5oqu-n9 +q[DcdL8TI'r#aO<;f-UFUZ2m#cOBSa`ZH/65PW"/Ig<\"r-RqX[t4N.p^d(9piGG5 +MH[K>Iq?;$`*NJN[J5#Y([U2@^P0#h;rQ4TnG`L)e+eF/\^:m&[Jp40Oo9lSN@+Z* +5dgA_j0&XM>5nT<^P9M0=,o+EK`;%Z\*ZiX@nHI!n5J9F!.o>HL;+2q(\,78pdt"R +_,c%+5N*CQF/rYm!nYA?(h)sLM_@1+,[M58p$:uPda#]?=44kAT-(>YLZ&-0/GqNt +5=4kZQ2>-?..A%gce9I,n@lTr57MjM`W#oDrYKr,eNO+`4n'PKHnh=>P>C]B_Q=3_ +IgJ!'6buqo?O4$b!`';tp@iJfR]V9trO215:Y:<n?ML`$,\d8YYcj,e)#jSSrlWqr +JUSF526cj[rr<A'`'#Wln5$lYh[&L3%<PtP:%Vd.C#9N!rr@^io>&'TS,5Zn(Wi^n ++5't,^ON9&IAnOTn&0_/$heK5\aa`-q!%;biD,*dIu4"L_N0L;!Nu+]X3Gh::\!&P +hhj*EIPpmVnV=tbB!#Y&hi;%N0CEAC=joPTr+5Bt!06t#iGTB8hfu?ARQcSfC\u@[ +J!<n2#h/eR"QM1\,!A<O-G/7fT8)514t1j`n.r@j+-6F%[',J%:#Sm!^DQq^l9>@J +BCKP'nR%3g0^F07[.mL4nAAK#*eWtViS&%e_B0#j%i4rh`P2\cIfoJh`dTB,#-Zib +rrBkf^LI3C&*b'FXl(9(r)`YRWp/CJ4qE#S][Zr]BE%tc\+[3-Si%#pr&XeKr%iKR +YCelS`7rUhh].(eh#(@-INA2AmS=.6HcKJ,jRIGT+n3]V=q(BWT1[S>qb(e`LnF02 +DqLuITD!`+ipVKrHjaTe!"-L<6f=#YINs5CpiH<gps8pR\,7F(HnYL"KqnMsHoplU +Kn!`"MkBKokl1VnINA2R:]CEKU[Yn7rdTG^*;hnnGGoqr^qKp?O,()\\gX;1.&+F; +rL#cfiNEZ;E;dNK/$3S&>'GcUrr?e0#l*9&8%Fm0e8DoFW;afoZ=elmHpn4>0Uc8g +kJU[=nLf_7r+O[E;pdHA1E`.Bd![45=SrBmQ]:tfHqs17KYAFNBAu_+YP:Nc!9%^@ +61FmSKR>kVGiRe%r#f02rZCVgr%&p*C"e!D^#Mo`6SoaON>'0Yn<.N)Qi&)>7E!nt +O7kXaA;.a3pgN.k/&hMg[ib!KY5!J:*E,SYrr?]HfZV+"L%V4Bf%,:!d_6k'gR]Y> +ZjV_PZ:1eGk[#n2^9)NebU9m)fCZW878(-`g\nBAjMsK$J,;0Cg6;O::nNPDr_MB2 +rrA2%`8C8,e3ET!lhdRZ!/"aqjI6(pJ+bJnci4"AJ,Q,-qJNRPI3n?hrBeP$:qDCT +I;*n8Vs51sXlZ^bW*Wi/cXCHJ>?oo>m;6o]E2Z&nkCr&QAS'6fhm*1]Sf65['7M/* +!9IK.g\*l'_cm'?_JdA"r2ZUiI`MG4CL?j2pl#.PQ#qLof`(rt%;YtD$,7(%nue&@ +q]GXmXaf:gi\1:2dJ^girr>/=YP]aF7K3A1\j*[jJ*2Qorr@`0Lqiae8+unBr:&:P +n=/qarr=P4rnk!\!1k+\rY'`)rrD.d+5?KRo>=c3!5`Zm?i6t"qgQTbG\^[]:&'YH +!0qYUDqP'icOF[i!"$CPJ+Bb'mJA"$iN7Ug2rZLi<RLc[A,cNk26Zp)r'gVcb.9gH +m!n2Q[Jp5[hh]&C=%Du_O8SLu^[R`_(]OIbkl0JErrAW/fDZG4,6%Z)>p%hp5P)cr +rrBoS`*`GCU\fM\rci3cpeCOArrA=+IrF!>!7)*irr<A?&V'ASO6ufdq;JH*iA]aL +r`.AE^LR9D4>j>?-N$=4!6Vl;\*SV-L:.$]K\qU#q,^)[rK$mGf"^^DnJD3*.d6lh +Z2Xfq)V=s%INndlj]rt_p-8/d>5nT>Do?#\X/#Q@a8U=!?hd@jMuNdBF<,\CJdoH$ +5o:a>CW&UPH@eAe\H]cnnTX?=q],N"nIDDTmiVPN?aJpUr*-8Jf364PHtiI/p^b>m +,h9&"P4q#7]rufDe2:MuiQ3D.]=XM$,aMn229l.$_\`<g+7s'8ZCh+>^V[bbFgQY9 +(]M5moOFN!TDg"K_S?(m["#t0rkT]KA"U'!rZT%n^]&A_rrDh(0A%fSN,SDdrrBtn +peUnicDls#@kZIbretdh;#^O61\acG!7%U*pgYu0/,kKGp/(bh`&%0arrC?EXm,ic +Z5;j`UhUZk[ZgS'I`pGGWM`uiGGj_0DsVuC_SZ/hmbPgC?!6";HnU5$CRAW?,ZDT) +9,jn:3j8a8-)t^Dlh;0\f%-:KZ:h.\p\2/+g3*C[CM@X^hq?mF%J?"$T0:$"93Y%^ +8D#>&Q<&SC#sBSUqQ&CSl8iOWQBU0/5m@7YHFAYUpQiq#:Sp\ulumA<o%h1lQ3'.H +i6B2[c7U?!A;4(tIrF!>!7)*irr<A?&V'ASO6ufdq;JH*iA]aLr`.AE^LR9D4>j>? +-N$=4!6Vl;\*SV-L:.$]K\qU#q,^)[rK$mGf"^^DnJD3*.d6lhZ2Xfq)V=s%INndl +j]rt_p-8/d>5nT>Do?#\X/#Q@a8U=!?hd@jMuNdBFFS5]rrC@SC]=A@^CbtdY)huI ++8OltJ)N?G0E+u!rP)kB!'E-Br$M>1rrC3Q5I^!/kD$DE!/06c^\Lr$pAL'MnCGAD +S+.<p!%R43hm*1]Sf65['7M/*!9IK.g\*l'_cm'?_JdA"r2ZUiI`MG4CL?j2pl#.P +Q#qLof`(rt%;YtD$,7(%nue&@q]GXmXaf:gi\1:2dJ^girr>/=YP]aF7K3A1\j*[j +J*2Qorr@`0Lqiae8+unBr:&:Pn=/qarr=P4rnk!\!1k+\rY'`)rrD.d+5?KRo>=c3 +!5`Zm?i6t"qgQTbG\^[]:&'YH!0qYUDqP'icOF[i!"$CPJ+Bb'mJA"$iN7Ug2rZLi +<RLc[A,cNk26Zp)r'gVcb.9gHm!n2Q[Jp5[hh]&C=%Du_O8SLu^[R`_(]OIbkl0JE +rrAW/fDZG4,6%Z)>p%hp5P)crrrBoS`*`GCU\fM\rci3cpeCOArrA=+IrF!>!7)*i +rr<A?&V'ASO6ufdq;JH*iA]aLr`.R4r(6ZFq`4Re\C5?p140)s>Od%.7YXQQ,4FNh +^`<O:2:e%0/@&&'^703(0*LT>fYJi]b:CJG,Z1qS8Kh!W`;,bt0o,*/Jj84Krr?U] +*e37qIMHMbDtm>%LJEB[YGn^Mn3;]B)#PfOWd$>Y=2R65r&*tlXju"%DrBN-?O?,4 +"6%_.gr11:h8Q)_A&C>KN&;!MJf(;qQ5JIE*+EX4'.`',[QV=K^[*cR?EN9[Df\/0 +c21(>95]oL`Ofkl%Qna0$Ze)',d2*p9+8!@o%HKQ)a&IQ^WGZYrrDULGaJEI2u`mS +;"ae9rrBm??h-p@BKuA0rr?^3!<#.]d<5C];+20r72/Tf'N%:5;+20r72/Tf'N%:5 +;+20r72/Tf'N%:5;+20r72/Tf'N%:5;+20r72/Tf'N`JM[hnt1=7(S=!+re=5_&'s +nSNc>+-$:#jDaWBrJ#7j!;)uNqksGoK4;UVRf:Z`p+?9irrDnFrM&WSI/#MeB>K'/ +]GGqS^D5W'8&!RC^*EPgZ'oGMN;ikpZjS;KZk&+%47Ms'B4muaHcLG5Ml5:nqC\&2 +fN[+_C%foT2TX+%.nLu\f>^HJ!rOp-o2Ms`rrA,Cde#1-BY#Og%]B1hIaF6`lIL9@ +cE1l,qH_@L`J\DObM,TFR2lIeY,)Q-+p*\GO.n`An:TX``,?+;Gb;lTh\0mDY@<Kf +L]/2&j0RQUpC@m1elVB1nNKaTh+!Psju4EiD]GgBFXN-u4qr5Ob>Zo,U:9[Y7;70o +CE#k8j7\3_1j8U+7"re]=[arC!Fi"->9k\Y"X!M??3gK1T3iUgiV3;F5Pa]orr<DL +_#FE+lf52H55tVi8,iQP\j,.VGTZp5I!,GhLYqf]rrD5k8,Okj"9/AIYE$Bequ4tb +r:]@S!/5"BJ,/d-fDZkAg*?UtT%tLXXLo)D!'\+XrrBt*qa>r*gS=`_Q\#/:kL[a] +pjN/BBTN.>FFV05n3?jIq!7q[&&7KDrrCA_O8)a^#QFdr?QFXTp\ggNq<cT0!"-ob +rrDZVj0/AErrBDrrr>3n5N,ai+9$\9FoMGshtDm+!9^gtXl6J:j5Ga!U02546fM?l +h#4-s61OR,qSYLc\_pbO-cGgcAKk%*Og;r^^`c29r%g"CTC7/)rrBt8nalb\Sm<H- +beFL7^%"Vm!"\hEci/33J&=&>.K9(\+8e@\+$]S_dJj1TbODG,h\:S0rr@ForrD5K +8,P.r_Op:E;?$X6rUKLZ!1mI<nBAWSJ%bABrr<T(a3Xa1FFV1`r$hX>`?5"Bh]G)Y +jjF).%*S.<qqi*;nG`K9I/a30QUgs00>a@MHr9nD_u9,srrAWr+7RLh$@fbZ8,iQ" +pW(VZH$"PR1AiU#pVe6P5MmPIq;p$(!"@'?Vu,?cJ+3I<I!kqokEe^:1G?V;msJ"4 +!5nd*oD\f^>p&R[^C#J(nONTZ&,4,jrcrU8__V-=rqFARr'0'\5PaEgrr<JNn>H0@ +'S#WP!.91o!9]\=r%g"CTC7/)rrBt8nalb\Sm<H-beFL7^%"Vm!"\hEci/33J&=&> +.K9(\+8e@\+$]S_dJj1TbODG,h\:S0rr@ForrD5K8,P.r_Op:E;?$X6rUKLZ!1mJA +=2o024q%.4i4CWWpepkG4c[!]nK6].N&+ggBDBHLfh#bYr*o.CLUEU5bo=Sb44SFY +6J#&]F^7>Ninj\8:Uu1Q\TUqT<]Lh6MC>3V.&)SIU5C@nMC>3V.&)SIU5C@nMC>3V +/*5+3MuHDNp;$\.ZX!I%5P7tM[_KqF`r?&03-^eh_`.SqqG?k`e,KEaZ![&rIqV(D +(B4AIrrE!^rm>le6MMeF>+`^ifltEl1X[j!^;'1;!5q*^pQ8ZTE>(atZtI;bHb!DP +0l:P[pE1QBR"0+>Re_$a\&Wn)a.EDiP@o$6^Q#An7%1lLFMSmO>^1]LAq(lDUh[@^ +e21kqkuTfenk-Mo@rOV.F3B$Mh[oHV'ms_H4=fELI;?_=StE)hGdm$@0jWI=Iq<<B +g"FW+:R)+MHGB/m/eM6\Nd-@)meuP4&,?`OJ+a].^\"sLaKOP1p71YCL3/LO-Qi7+ +!W*gNhBL=R^Y8SFQi%WFa8RL^-i5B.3kh4tqagWnnB]&!g/mu'nOLKH&UZ2*)fp!8 +`;]gn3Z#^V%)jTM]Fh4>9>^P>TAmNk2q@O>Mu>dWi-cjM!"?L:>Po62O8f1h2t/K[ +YP]_sZ-Vfurr<5Lq\SY5q`]9$ilV3#iD'5Dpf[Us"kinUKK%iPm1\sn6$8rJ+7P]u +!5bi4A+595VMPl8Fo.6e><R%0den=JfK:[bIa'8<'=[rJ8aprt7K3@?B(%U,!W7!- +/H5^X:Va_rJ$c)LV0job2i><aGh:t__VZ;[=8f?%+FGp=/b(@2*sVX9Y/ts!i*?HE +2o^?#hC.mtrr@s>4*dXJiB:AminHol9Y/6'iVdukO5e`EnQ5E'rr@XrrrBl7^YkMe +(k)g:leil_-6KOSr"D$_htV'XhcfOd_;C&P9tu\#47U)Z8c=u?D4c[:r]^+"nPA/! +Yoils&)&hWH=r$t)Ybr^`"uTPMr:37l;uQN'DP)rTkWJFHb00Ko`"oGE-S;?=,-W7 +ipYPj<W/2Xrr@Z7J&*?/!4+jFHnkD?TP,G:8%[+7?eEQ]\pRrd!/+/AB<h6,i_P=j +GPiSlEG6*cg]%7drN.qapfdM7pO_cp;?$V'@P'(`pK7B>p]L&ap-JYF5N&ji#lIE# +IaXkX:Tjlu#JC':JgP`G_Yso;YODer'o2.bq]'jI`7gRU'YE?/\Td#iKROuTps\ah +_7EsaQT@Wp:Q"e(-fV=e2%=WHINA37id!-JR2])P1gbt9+)(ZS"(oopi8LeN?MKBW +!!N/IIQUe_dQd5,$\&>-2u&7!B>PSEr"MfB*Z8nL]Ad;UlMghb^,B;FH1:h6N4^>' +U)Rm?(<`e(G=hQ1j,H7c9@5pZ>KPl_8Gl>ah"X8'^\B+ir$t#,BYX<f#Q-ZIrX`H' +n+_(oS,WI!UAk4JIK%%^!/5(ZhtU%E#Q-@Or%rWk8,QRephK9mC#A\j?=3D5r+3Y: +#Q,rVn=P3LSGq*ClcP+]/&7eJ.VW8`rVlkq/'@1VU5C@nMC>3V.&)SIU5CJ:5A@=s +,qJuqAOkYhriaY&Q,?I(p\)<!bh&Uae#fFOZ'jui^XVo_[)f/WJ`Mj]/L*tFrT/XV +<:V?HQcP$O"OY"2=B\W6p<KH5!'InKrr@aHrrDuC;>mi"rrC@u%"I575Q:^>e:2<" +5P*(9+8Ag]rrC:9,AVMe$T`N8<-;gC<6>D?#"e=Z"!iHj32jUN,.[^IKYR#ZPQ(WI +'S!tgF8bP6J)OZ\rr=Gqrr@_0kPO*KrrBpI:]=0jW:`kgAdK3]L?3L(,OmY7Z0HR6 +nED<N:BRl'%he[;!)2lpfY?B!f)?_8;u95T5M=r`P5+n26Ml8br/\8aMu-FB[JO%Y +Iar6-T-MSN519b+\'C%Y/cPeYj5IcO'Yf6lpij(er^!=Y(WXFCqC-mt5Oe,T5Q$.( +r%F+crrD[hrX+/6hU$cJ#*8Od*tA5Mkb\1krrBsMT`5#_%3P)R4raM%d7a6`rr@h$ +62prG)F*2.HoM'*U6k@Jrr<Q&KDtqm1k3C;pi$0475*SsrZ1A2rrA-orr@cCnDF5& +?boP945(5F!$K\h!"7iG^\nk]JtMg^Zlf95U])(M_ghM3Ig&(+mq=r`K1GhmO,!Z7 +oMYYqJ)T82J,';0pg5*PrrD8Zr"T/2_nD`C!:gR@n@h(*rrCG>paQ4CLO2>e!9>%_ +iI$#3rrAd[n5K>e%ebPT!6@!H_YEnErr>J@i2?Ppm0EXkOD+XoL%4Zireb(!:]*<. +&,uVPdJj1Sj5IcO'Yf6lpij(er^!=Y(WXFCqC-mt5Oe,T5Q$.(r%F+crrD[hrX+/6 +hU$cJ#*8Od*tA5Mkb\1krrBsMT`5#_%3P)R4raM%d7a6`rr@h$62prG)X;]-j8CdR +Ir52cKKi]9H/cU*hm7fGrrBnnj6M,;llU'8rlOlkm(F>'m([<4/_A[grr=^uq_\:Q +mj:c>pO<p*0k#9I@=R:oKsk(6<tebD=l'!Ke*X=*0(#HJDCr#.k0fE+/g-Q'.A<"> +8>J^F<Fm:Srr@bhi8=AOrY#52iR-)gq--Ae/e80d!7:3(Qi@$qNg9VEb1a=5nOLKC +XnA!HV>.+ML8Cm7=*E.5G+E(3,/WpIrqc1r\$WHH[("JZ\@T;`\<8g'Xe:\GVj^#2 +,\L[egKA9811*\Ol8Bj+nR4A"blmK^@s_md)!6th[^EJRRaubZMWWW>GbdVV'1?Gi +;T\O6mD])*-<9qNJ(^uRcl`+`r**Oha5_[F*'?mo!+DAs!<"<lrosF_i7P7/-GQo0 +rJQ03rrE%jrr?`Drg`mJnB4c*a87:[]A^2Ziq^4lW-FZgGK*U7`=V9.XpYrl^M2RD +lZY7r3^e;&KdNLklSc=jg)d/-(9sc1-Cq,A#d0ncBs3^_EUZ-**8GpR^6dl@Ji):V +is13a&+Fe^3i7Q,3GK4P?bdI9Oa,&EK>c>E4l>W1D6(W6V/*8LE'1^\WMr(NGASYJ +P$"M^/`lq^5N%iWnAcUa+6*9^qbhZsii\L)%h@n`=.U1[^q+044>3h[YJ%@!3m<ak +pp7oAn#'EdnnH8Wa&j[Cg2!*FZTS6f9)Ni1KR=U55^;DgF5m3")16kZ[0WiQ7^KGr +7JgbIfcRQ8LKdE4=Om)&H1`fnrm0I>IN%u7;t0rcXPH<jidF<b^%!E1N&kR7GHBTa +=sf/A@H1_al5tZY!;'1h!,WhU>#5&1oD\fbT"`D;^VL<H#^C""^(Sk:3Ni"X$g:ba +LNL_S'R7O8?9%I4cC^hFpA/?ka,%BDj'.QUGg")&m+0b8afG33`ObcPiboBC%<8Fa +g:jD`h;-p^]Dhj<b?k2KT=lKL!!4g'q#ZWP<a,7qrTPF!&UXNXa:s-@ZIckSmn!HY +^l.+L5bn;nQG*:u5C,j!"CD*.nc^-)XL7NmqpI^u,5;*;O=:,/Kk7H;ceafa!.bs$ +r..sV./4s6ir8uepl!!m[\l![Q/[c2=7GXUGN"#gGg!o%-FVCr]ftIM]X[J"NI2I\ +iVrlqGXGbkg?(s`Ntch3?I:l0iVc&Zokaq8drekf9?;Q=rr@gNr#bq=U6kajrrD', +J)I5sr=nqirr<;=@"/FZ%Il=MMgQ9"5EB")X/P5`?6-P_pquuBrn1Yc5DE[sme%#F +;jQF`_-\;`1&O`7DO7N&cB2f][u*#e#1LNA>(!oQP@pr4V/(D%Sc8]FrYj6<1i3lK +9tGR[T%iWoh?J.Zq:B]5],F6"F+ffGAi7I1"!@e0Zr'7+;+20r72/Tf'N%:6i#f>X +r0!?F!4&Bh#QFd*kJKpNW3;?`!,JbTG`1PLlrX'dGl,>rPkm=s4:gpSp5&6gL,Fm$ +.s%P3LVm:#NA[gS;R^3eb<:l\ASlJr!Xd$^OEdsKdm)c^5O_I<%6DYSqt;Q=Qb6OL +CMabEIOF[S^i+q`3q9)^EejkP[u(#kih$QmSfmP=[='Cdn$PkjroWM.B4bc`'c-QA +47iFXhu<[G(&Jhti[t'>rrD1"O8*q^r"HjEN%=TUrrBuAp`]Y;bV^L3n'CbVJ,L3c +ls]noLpuk@rrAF$niqaKZfh5ug\-]t^&J(m5A-&1g\qMYn`R_l<kie99iK%2!!YV! +rla15HnVAlU;p$W[<pd?htVi>rZB`KRlu5H8_5dn;X6i_]8ogue$Y/bp02(9e1Du9 +"6'!jYP9@2`D;M$Zo@@+i'5m"p+Y*I2#bXin>X1]IX![J(u+$KQi@$k+8dcV#[[;t +iFi'f>0k'&42QFprr@_I_Kp@eX8`/6B"R%Ulq]"q4sL!Ie>.uHc[amW^Yk0f[,:d6 +Dtm3PZ4H;1j)=^*c]<Pf5@u!qrrDSDh[fWDrr<=ca"N,G!8.;On=TJ#r[RAig'@?O +!;6Zkrfd?^'eb;+[_*c`rNH2G])JJc>kp@O(VU!#cel($a3WDE<W/H%$%+<!$[qP$ +j+"uXKte4/pc77S$N)[-]Q<"@q^?pA?PVt8T,$Sh%+96'4'nCKn`Rc="RB5LTrHf% +hJ$h<Gm0_C_gQL+p)elNjPi9:rr?S:j$3O@+1(ooM-^5B5E;B*O8dZ/!!`H'Arl^U +^=WAb$ZG`grM@4hJ&6""\+Y=H8,iSJSc0i4akd$t!/+SU&^Tg#^U8RM0`M--T`3OA +g]$'YJ)MLL!/)H\+5%Rir$:09nG`L(*s048X+0WJ^MS)kPI9k0^[QdK92Y]hL%4+$ +!"OBkrr@bJrr<@,p?0J7Kcd_[r"LC6(P9@2rrBpGZM8R>C3jOj/MD]_n4c.$?hTfc +!/6,Vi\^.KJj83/rr@a3X'KI(/_<)cXSDDErrCuJ?ai8t2rDI-DrT+@.e!.3rrC^O +!:\;VnK@i3K\QJ>^U4"@_Ya:brr?SJgKXYD^[R'S2p)("KN%jgU])"+r.AS[h>[J. +'E/Xuplk^X_GC13#cEHl5bJ#`+ln*rrr=*iBMD.XQc&h'!ri8;&b,ts9(<G-BRVL] +dN@t0r%.Wdq[*8Y5I;em^&n;U?anA"^Pi_O(\&mN1Io@_!/PmpZ/a.JHlqmFJNQg" +Mr<3B4qR!PnLqfV+8dF6)S;Wfr($be"hXcSrr@a3VZ-X(I_[!@rr@bDrr<K[rr<ku +j!Xa=_u:BPlmLi.ph-f*AEj*oitK1C?he.r(4X4srr?te_c?^:_AhK!ppAocrrD!\ +pr3+nIa[*_5N8XA!/4q_!!t9_pAY,KIr>i4GT6?urr@Y4r+,?L_-[`,ptPcS_r:Uq +'O:FRiVrn2r[qqL,k/"U^D1uHp&b!oqe^S57e"aBD.DNXKCsX3_rk+reTpYR7e&C9 +nBu_T:t*'+D=JPA_KnYsLunDYiU6d9^\eeTj6soN&,[Zbpd+DirX$F<60eIM*^=B/ +q_.k-eph<cpaSLf4u7R)dem,(hq<MQL]+5:,sT@UMZ+Fl!.oTfT*+k@_d3ZE-fM*J +%JVSCn1Th[*tuuj3l=qKn%8h"BbhLK.fCIfT)Ld#_WoPHH)>h=_Vajdr\/^3pjh6, +IQr3=K3S?UKYL%H2oRbh$/eU9p@c34pjip;T\4]\pj_<WL0kVn*.1ZZFlNYP]J&8+ +cN8@g_n(?9mt\Xa4tp]C&AA_N$fC0BMC>3V.&)Uon&GA-no5^.rrB>Xn72Iu;ifW6 +^-;Ld4qE#TnYG'M[u5\Yr"/W`nMA+LrrBKGNBB4:XaEV5CZ.JqlB[ka?OD5rC2dm1 +\rO=u?!kEV"o[!=g_NcBXe]#7pX>o>CT4+FT<qA*rr<2crKLm?/3Z4E'`F%S(#g;N +/<Y6V_"N6Y4<*=!M-_X?M<'\Dh[TK;pj_7p'`:;L[/5,XOt3(n8at!\NO6i;T&Ma9 +;LZqQp\Y\sp8Y^UoY5r!"b1$C)0hXfOMc5#T)rAI"Rp>\r[%>Sb2K*gD+DMd%"9=D +B>VCpFBpNa8Y_0p<7JQ<D5UX_iYX:YQRQ7gTmS?b/`C&r*sQj@!rN?!*UrMu\(3Z7 +E?;UI+,'V"n><iDrr@_$D*McN\lOk%Y65;GWTk8Jg=kE"D0H`LXlNP)?+Ga*TDh;3 +/,m>qrrE%bG](6=rOBW'b.ha'q_c^R"9/@$5Ds=TR/Cn&!$d8UJ*\t@rrAl+?emsE +)\rAQ,hMaBrrB<Bp4*)Be,KF8(LP]/_B0\1?OoA6c"k=&fU9+['B3ZFBl8X9(R0i] +g*bkpKKE-8J`kk.]s'gn\]DXVq;:n!f6eIqT7NG<NborN\/mNtUmHuB2[8,A,h)40 +@m,OAkf8+a?@6h2=TYqiHBX5]q]PfRnM0S,O2>ViReO\Q]tNgQQOL>Z?,)'I9$ep! +ROE&k=f6[+k%b-N6H9\p?EC._/=HFUrr@\/rrCuVh\UcOl`]!^p2^+urr?dUL]7@[ +cc4k!T+Cr%COb&>`a9H<fKKW>6Imu#!e/@CZ]E%C)>klW,H,u5pqrb?>%PJH2NPl4 +4!0/`$[s@trf=-`q![20Vr>AokW]_<47-t'QN$rchm5QF'\hQ$m2,8-p-&2?@H!i; +''fJs[F`7p!/-sVo]ok:IfR5t?eT;Rf>DMAHq0a?2r]=A5!JqEILQ,`f00?442`3H +IO+0l-h%qk#5FJCO,"X7J1'j:m_/-0_u9hf,tF$l_>aLe97Oc_rr?q4*r)f8$$kR7 +gIq7-nHHD<^Y9jgrrDs2e&@ptr+#V$GVAbNpj_c@`K5PgRZ+htL#R%(#K/j4a+=.G +nQXs:LcuLI>JjY$WV]'K5IZp@JGp!mSdX"2,@>gO\^p[L4rqf':jDU2%Xb\ZAao$Z +L%.rprr?PIpdmS6n@/*Vp_!C@Hm*tCHqaL6]HQdNj0-9`N1[LB_gg?gir79DT=($P +&c4#):"$bpHgtmf?9doaN-kZq,NC/g08a'UD\;5WIhOq'iXZLSfZiIV!:WqUpak%8 +0*?IJiO?]8Ms943fVcaHn5%C*/GK&Tk>2&`Hq!opI!bXVnT3X?`4stIp5/PJnY>f1 +eF]Ho]as'\^jh7!&UZerKtIlS>@3;\"Z>1g2``:Z5DK(gg'-*H9:]@qmgjn@hlsTW +/*"D!&,(#4BC.M7c[nCNTfaXkUWrRbn.WZbT>[9Bi\unq8c(LBT%:qbpbD;'ig%!- +1`J(SHntJJnMe<d[b`ag4qRL%`ZGsJHip//\e^j<r(+QlCq_.`c\9*JU5C@nMC>3V +.&)SIU5C@nMC>3V/&4A`YPBIprr?V#i",gjc\@_&;-soah;@daYM4:#c*R,@rrCP) +a7]:6fB7/LLZX"KQ17S`K`;&1U<DMD@aa`=WVq^PnYH$UL@-l-Ic'i(pucGOI5Ab6 +\poU3nU?P#GX>d4.4s\_Y%ubRX*NW]h[T@DbJsN&e,1_opj9t`AuFTp(u4o,8CdVG +i8:pQKCo0D&U:)0oq%B:6fQG1TD0ggT>FA1f]dmn?gPJbT+laq^)?E`SStJ%Jh&EO +iXbEDj)%T357[,]!+p-[HuY`BM#Jq'K=kXP?e`?6Zg%5`+8CWN&(m6BT[<ToGCOah +qfd;Jp=K'crr<1Rq#,Eqr<pZ<ZtJd8-bsNDL70=cb`R.Mhr9#I3;C+ff_d2IINnP7 +;YXbkpINoK<TDteg(02$pmL,;*rJ1r6h!H6plj\ZLALoMNr19oS+^1Xc\ZcD!!<'Q +rbq]tq_8$M_-m9Ci10fI#J]2nT<^EBg[K,edl^@p=8iNbq\OWjpbhRtp+#SS`(ptm ++RkH2DqW9OKV,PWm;$;&hXAHK!WEXTFt_TKp:C#bU3oB-Uc_RonEr8cBFB+`3T,Y] +<ibahifsETnSa3WY<EkReUA,/W^#F7T+etMhhRin;Vqn3a57]JD_Ll0.JV*V$cF)" +rr@Y$B]$3f`:)?2^`O&8/pMm>[3#pl?1G6s\,QG[K6Y`A&C64M4q#b#=oSI/5>fqE +0)0MHbjakc+5$Q!97I%me:5:9rYtXEiD7?%paF58rZ1o]piYOkM#RJE<mTLkijZab +ph9[-#lJZ2Qc/n(*W-K=LPNSRJ&8elO/ei+2bqj$1\dX<UJ947qfdu_+WKkdl[C"^ +f0A4Fe8BY8D6@^Pqt@2'1OO6C!.oQs3QLMij5\aO5@EC'=FGPJd=)B$!0`6.=8e82 +N;inSS:8fW_LKI6IaMNpM;R(f!<3$/2uF<3rrAp!?@Sr!HhNj*V7nDR2thdsHgmrm +#ODuk\)9Kgrr@n*#CJU[qeLRArLX#hBto@o'RggHdN0Y.:#fT;[^l]o5@ZqQ-nIP6 +d_6iK5K1CCrrBmgFKZO+_rA.Z4?9<aF2YdCd;==Rr(#976PkRX'_!"_r">4j'>e+` +PS*h>h=3GarrBt:!/9/#NT9dt(O8&+_>aMi-fK::`VXBL!5V1<1]'dh8&2Su&T=B5 +(W67Q#QE=^[GID4mtY8VT9"@gi_(4D!dkjSn&)>eIhVf>fm(9NR^L2.r!<<&Jm3]R +rrBBL^gE$"!4+>q:YVr/rr@cP)15ijTDTMp^?<MiBC)bj(I\4s^)m2Npj`;D"PEJY +qZ-Cej$1E<MuFUm['VIspeUc+`#l=hJA+22rYLdWI])0g3pPa2n/e(h1<T;r)r`c3 +GglWtS+,a\\)%bIrr?PIN?8]rB92gU4s0dMh?pXZLW1tgIanAt[U6nr8,aA:p<!9! +Iqt\02Tk@$?PgImD(GJciVrnnDtm3P)L`Is5JS?O2;\N9!,$Otp8Rh@W-Eg1IN/:2 +\C*K>kS>90:LDhJ_I"=brr?hALVL7#m/I'_Ia_UTrr@e,rr@c;WHcV[1\cmfhgmsC +rr@c-i:#;/iMMb;!",@W5)K/Tp_Vop_LM>JINSRi_)jgj_5Vq$@I`O@Du:oq1%B2# +^Co9#!5^%Vl@'N<DhO/lJ&+9tC4D$\)#_-s)s]_rYO)8hD=GaJ$753A!6.'<`k)[9 +p+uK%&!$Q@nP@0^n5$fQ5Hu91j"H,#0tR=mmsI?oHp>+1#JpE?%/aG\rYg$fiVrn; +]LN`uIBNK%\bNFRHZQ+_9[c*'!/P!f^[:^a7dK!)MnE3i?iJ+YHqjR0^M"!NNh6V8 +Nt20)Ii:/i!]ThFm,Rt<rltF*n@->C"DV<5;@Rig*\@<DnG`LU'lDq`#jZpWpl"YA +!<*Q>A&2*[XQ^ceZLL>&pi"M9q[\LtZ.=u8l?W^%n,#A!g`I@tB^^`qn&@$7;rU<_ +>E@HIiJ*AA.I$g7pfm7*8\0]9rrDF2qc<V0ZLBfjq`fgA!q7SShh1n=n4(%[A&N\+ +JNaZLC*+G0+aEJ9#.<GdMr,9\phZ#F/,kYmqg\VGiPtltJm!Tqb:E&X.&)SIU5CA& +Nr3hq!%/B=?i)&I!!Y[\3;oL#kPkP8;uT$h[GUqT7udNKohBfXYKS*AB>M?icBIbp +_]SAOYCcp^rLs3BrmB<r44\msce4XdO&*I@]Mn;M:&(REpcJf?T@o-&]XBl)K"`IY +ce])Z1sH*BiOkVT^)d!N%Zbt6gdc5@q"/hbrk\QDIOfdOpso6PkeHUdn(F1;dWI4" +^U!-<^DHk\Hr&063_,+Z_aa/sQ)=I]rLLYFg\F]^O_I!?nE'D$4s'LViLbo-*BV3+ +elmj]U=FA?4,3X$0DR-5#X@Z#ia;XAgJs[A!9@V;^[)?3rrD;-L?n#@gPc&)X7j@/ +!4,r/TKi*JppRrZO2h2"ni1l\rkg\p!;]ObrR:cH_6IA<9n-h@q\K23BE%u15Q9&Q +r@:O8Gk].KZ'WB\#N;#)+RsB02.)0S)6C3k&'q1gH<J"iS+`NFP3^-=qchQlN?Sg( +cbmNO\,&noNi-B$/uNK!%+d58K\+<fnBV%6*pfuB35ZiR:PuaHLUE1t?4(%l4qlHF +,Q%q%;:.QSfKo$7hcH.ANXq'VT\8%'??hf4e7YB1=n8cSN[9EoDBkJ(%lXFfJmj,Q +J+-9aK"i5mF\g_$^fheMnCI?OSgR0Z@fHGf-\FN_if+IJ]Um^;8COZ7n?/spa`Rd* +/G=CBh)e,F/Zc-oFX=Y(H,$*)g-=p5H2Dok`g]aFr#30H!.o_[^ru3"/Tg;sNj-k" +3PGpdSh.JJq5Ja#?\.`ln[,':VsDnmhEAU)8&OrYHVG:$kZho.pg2fm7.t!@F7rl6 +'DiSn$1Kn?dJGbF)u^SU_(UPXn<8@=(#Z:j8\AQn+7*A09RL@0+!:9=r[L,SpkQgp +/_#9WHs>U4HWL.HM71*Ip-6%bK#?rdAZ_5_^W)lMrrCu;hsa=3ft74plYd#WcaJ&3 ++7L)arrA#X!"45oGRNPu`h/9`ZqnQbLE6TsTtI?'5NlG^rXs\-rr<ct."!fFHiF'R +QHAR%Di/k5JikESlQ/"Sk.Tbm%/?/ce3#tTmh4lr\!gY'!5T0F!8s%Z^P=pCiBIqP +*:Zg`r'02%g&D&g?a?g%TB#qOgFN!6pjrC+q`+L4po!$\NICm&0Arn7)ZJ,fp$]f5 +L].bXS++>3?7,1(J`$p.%9i!<fpB9-R#Al]+RpM<X8DnsNdouV+7N'5M-h:96i0OO +fASoUXaG!U=7$%c/q2;rpPj[]Du<Va^>JJ8p+GiYM7`ld1jJ3_&^;]_4bn]mJ)OT+ +9?'.?dXU3nZI!Id*TJ7/+1M%F!5mYCeElNTD17$6S,WJ$^Y,ga7.;P.n@/+3cub>' +*t5Q,L`SMMQYl6X9_$/[21G[P5K&$`22OFailsF@DhNL4idGa_rr<<'+5ZdEir9!a +e,C$Y.b";7n?^$D`ilQ1IN.coOaF9I1Z*QZ!;n)\&,c_)O8KkuJ+;egi?6KskVo;e +i6'7C!0:"W!!rj^rr>?u,Q@`J<aYcNO8)7&+8QH2+8@UsrrD*/J&4LErrCDa8H/\+ +Q'_LN*i&]I%0ulbH:RrCXM=Q'+7PA*5P,oC5O`5qrrC*<rr@_)62pqXhN@g<K.AVS +4\,Dq)@ucNoT/nf=b#u,\SJ-G?[88P^9bKC[e[[L\VB.fhn*NhjLad9Z1dV*mB)?7 +34D<j;+20r72/Tf'N%;D#OODK!$nCjm&9g_rrCUFnC"g]/&XV%Hn2Jsl0oYP+2M5] +<1cP,4t_(EinqXJ7p<=0>J$FQa2\SID[g";<n=B($Zg;tDlH<L$k5$D1m'>+/7FHt +-aqVki1lSL7qFo9Iq(,G959@epkhl$8)oqfT3<t/^Y2KB.">_;*CJfM/q=SVV+'DT +&OgC"dDr7l+BGKA!9#@+!.p./!7<4nMuNeI/j;@HoE%ni!!u0n'OUir8*jU_!/Y^c +g]%8H&sRX5rX(2O&&Osk\9jk%j5[iYjnf(Bq$M?D,erhG?hu(@Z"O%^a2bC_J/UZ2 +-3!ssMZ3\(/H(JknLd!gJ+N`grr<?Yr"N63M.c0Xrr@mqna$2TTE_`E?\Y2[n&55+ +MuNb^rZH[:am^Oj^(pDRi\)'p61DXDrXaHJpl4f^?1Eu9r($7Oi\/-K)"jFq`h*>3 +#&4&PnEu:pO,CY&eE0ssIhQ1(]!OLl5\]r]bjt^fi/d[nNs1$d(\l>V!3+$*rr?Db +^Th_T'Hb77/H)_/+,e5_mD&0Q_&r/CY7LW`]$J5hL0cn8'DkUR55h&l;>l_:]<-GZ +$-V9lfIOo_[&8Sk')p1J"n;iboD\dno^L2HDq^!,Q-Y<d0K4]5Hgu$j)A]l^pm^1^ +Zq]I1;ZlaKpnR^bpnQk^m:GhN8FV]-!W+m%J+ctD!.nU+%K3*1D[^m&p?24/nC,F- +ZCcsFc\oR-;_-+&j(F-sA$=QM`nq[6!+CrCrrD$_^#=HkSe1Ff*s98kmuIASpfZYU ++2D4n^(>Zan]-Et5NAr'QMq'\rrCderX-R?:]A\OhoB)[h[@.5G^n^Cm($ojrrCfG +iNL&SM;BfY:#Z*P"o).mrMTYq@IMkkp8Ri;nHVVT2iTA<DEe_\EVR<-r?pWdls'"T +i5W+^$3(!6hq;Y[qOX6@c\st"qaYml6h#SW[9qVqTDboWC*20%i@""]q^MFipoE@< +L]7@^B>tJ8kOsGq#ONhr4\PKepf[,'i'rderr<OXd6I+qhtSf/_b4nNCEEkjT8DI6 +(W>#,!9$g^rlcE.pm:#j)#P`1^+=Y1nb7Ss@4-\V#l_t2&:Q<X-c8+WptbiGn=RXK +n<j,)phAaVpeUl2r)`muKWF)3!/#Yhrr<F"iB<h,GJ_[_?e_.ldrdnM:];8HrrDPW +r(?hk_uB]RrN#o10*^,0!5c_FpXspSW9LO9_JA/5TA-jSJOL8M72/Tf'N%:5=3#bK +rrA7=nZVnRItJj>S:8g"oD\gWWW)r@0A>moc_JM[!;`S,&Qb'-qB,)^qZHW6i[p5X +Xmjr[^U:Q;Q16NJIOY&ciVrn.kMd8Fn8I>?m-E](L`4gaM1-R\h[Xed+';bF1%E#p +c%!]6qT"K#T*jl;pO[PV#&9V#ULg@ZlWS<VTCW!^K)Yg\%uENXTC#M8IuQZ-4rdAq +#_'r]&2jIf"FNT8Tg*'N)rfG&nS[gh\+nA?cc$Z+0^qTN%j1$8!"X0%^P6)'XG4'Y +BTi9[IgC;B`-U%SW4Y?/CZ;Bf>Dq7,EBdVNrrA4on:U_W4sg4/dp9@aTDh;3/,m>q +rrE%bG](6=rOBW'b.ha'q_c^R"9/@$5Ds=TR/Cn&!$d8UJ*\t@rrAl+?emsE)\rAQ +,hMaBrrB<Bp4*)Be,KF8(LPaSm*#D/,c/9BW;M,E!7go80"q7Arr=t7$q[I5J+-[7 +gKVY2*:Xi%Hm@XS"THf`[na]sY7^P">efdr\#/F/O2m:mGV8fceF_F%]1?i;3TAk= +:Ul6-7@#I@*0g\3qTJi%5AC,tM;C;lMuF@FBg3]AFe=OT*q7LrD\d_'^:q5+INj/5 +L>tYrVrE&ALY=\E%uBZHWI-S#l96+�?*K%(`=8@A[_dBY&nhS#0p9g:,S(*Ihc$ +CZ03u;=N32O7=QR+-$1drr<Ej([J[0:O`n,HlHLrHq42:Kls*S^CboBppSpJf5:L$ +HjBFjIH%7*-2`,bTl8nN'>s<di0T1GHgq_%#l'uU$*T,l+.7R!rndVcQf0<E^*`b` +](f.*h#(%$Ht^8$FJ&=Uf8RF(J&+d"rr@j="7QHi2M:$CLcSM-"Fk!d$i'P)0*D"H +;L\^p7b;b^!/<PD_lnjP-Ik:%pj9r/&ad5BnJagaP[2mu5N&2!Z?M!FnJ8)O`,0_; +Sdk15_Mqp_B8h9iHn"hZnYbDC^u#BE3mkYRgjFF;;XntOYCmu.4E9k#_YW\sn6pF2 +M1-<20"p;Y5KB?Mn:uue)=&=VnBSFYnQ4\MGcu>O8+"(g07M5R2R`C#nU'8>r$&j9 +e3Dns_k[!5*sMR0nM[N/4^6qNPP[^^rr<B'/)a]^$$]6Prr@_Ua'TL0iKaC%BO1]. +#DUu4n[FH>D;h#fiV/-"%7A0C'##1WGK,+kBclhDYCfk?'YZ\S'B2cJY5!4W^B-S5 +Hq=%MiZF,/&%iAK7!r;8Ff"kGi9/.qgV;\8`Hr\_qW<Y346X7W_G,6ig/mW*XD82\ +ipBQVrr<_FH/ag-iMXIC:ZDVfK/>e5Hq=*(ia;(ei1C#Kd=26_Am5X=pdmXoHroji +L@buk=S_g%O8f1ehn_K;p^d7<r*f("O8dZgL@6rP(k9D$Y5Z>2'"e>H!!o`sYNacL +M<Fm=_H!^4B4i!2VKfSm2/i7<f>4Wor+5ZHn42I[LH[6CGg#'d`I2[`pOdR+KAQdG +BTLks`8;.h1Z8kthtB)*l/St'pYHV_T"YU15h1<(k["qQIr*Gdrr<Q^Qc+Vq55S/^ +1K:X2A@_45hB1*`^M#:q.*'3."P\;]dJa>chse.';t17UVsHY+"S$,VrY9fippp8d +Bg)k%W]t/=JA)ZGB(7hI(\"oOf=q>K!TU:Xi$ZM=4t(e4cocUD@meeSCMt&;*:[iO +(%G@Z^*NIu:[to:GQ.XH.K,-GSHHeO0_%*AO8)"g$_W!d+*eC8?\puWIOtCQph/W* +$2EiGB7[c.kT:O'&H#,irr@U42rJ&'*s1Kmf>S8T(7lfHrU=mH*]j'Fi10u0B>^nm +ps[:;#D%8FiEm44hQPmfg>Cg9MtN\PphraoB"Md:D\RS#dQRkLSf$uf_bZ.r#Os[\ +`Q`>p=L\7:D#=Ot0+Rd/_1!KW]HOGqq__X!nH2jN`O7fQ*ZrlIj5K.65+D(+ddP#6 +pg0$]!!P+UPJ/2Sp2YNspna[35h0(ErK;$jM`j_NIi<\fVtaoB5IO(=^Lh7WK%dtr +[6%0:Sgh5>[_)'&_8!B*JMlnJIN@s5PMs2>idZflh]6r6pVe0Yd_61id2k1?.HMWY +^Yl5d!rR!+;tbA/kMr`Vq!6m.L%3(=?&ur&8&<p*_=-*i>POV#j)&;FT<YT;h&!nW +"hYX[q_241Ia]>AeG^\Ag#!<f#GlfXphr85:Q77/mgVqZVo(_2oIJ7E>0k%b!2OKs +Dnjtk*t7BT+bO%pYH2KM^:WAX%\JFR)?#s&!,:m:rmEtPe3"tRpg7+m291`lAT&"T +*S%o=?\^ZXKqeAB!9&[CWHb2Yh[2J)iL=Zlit&shDG=AM?Q-Wn?222&hq;r-=+YlW +r%mi_`ZKrHfDHnd%Xor'(ZG,,Y5ig*q`@j+rrBP+'B?1ppnP^;fCh\K@ARbN=8&eN +Wn:[.B5:[_SZ>lgrr?Z\X=WfaRbg9*W1o.tU5C@nMC>3V.'EXLVSD2VYPj_.J3WnF +4rRK37<q+W^*<?%i=Ee4M#RGi]NI)"rY>3FU\m[Qf84AfL[>c0pj^/IRf9k!gd,gg +KNq4USRljn:jLF`dNZZma1HTTFN"1ZJr&a(FdB@Dq`'&Hq@/aiD&)`@NT8pMpYs2J +j%VGcjTMgApn(%F\*RD\^CTR4e8_dPm6=55LPW'L-J\agG79F!n*Fuf.,Xt7J(TPV +!#&#c^]+:!'d))cn56ss+TDGH&,moBa++)=.Rqh#i1m(gT)q5cokqa`D%D[9#<_*< +ljAF9OnXS@4Q?W!lsKN:+8lbsfPgci'//Ctrr>;i+5"?t;Et"$T<R5P-==fsrd_RE +Z%(]-J+Q)m@DD_6^Q2b#!Hjq<`.-JgiVrnrT8"BhrM2Uir#tQ@LW4aL`*O,PGN-Ah +rr=!c?h&bCS&ZQgn5"mi,i*]H`E']2p_WEQ;"4,2O+BD,j%Xkje9&l@'A;2#L9Bkq +rrBCfT&&;frr<1Oai&ESrlDjoJ,N,,BDUROARF.qp(RD&["!],*W;RW3VG*:r&+8! +TR9l?Q8C69rlO%7^Lk/#e)=lQJHs'J0B-@*NM_)Kpc%]>bJc[Vr%7E]ijP9ElW<VC +m.gjpiQhQ"'B;$)4<ri+91&.J!6,NVC&TRQrr@n)55tV7@!kHBZ?4nIitoNRL]0C6 +X*t+t5kn.ET1JpR^*WcrSj2a0LHZ+D;q<LI)tBia?c,Ssp>tg?^mjeE?OhN-C:^%W +O,(=(_LDdDr,g?a7t'o'il?\!/`?Rc?]':od53FVl44YZ`*]d!O+&F#S)[S1^LU-n +K>sN$4DEdQB>T*PppK,-J6rY28+ACSSe^c;_qUf:INe4,M7e,8GY[kL3qnF&hh="U +T+#k5)cY@*[-^fNmFBO7n#`MWh"'^2Gct$?"UFF7n;"fn+o"XfrEo8Nn^#&O!/dBe +r!W%0j1nt/e28<XGZXTBf)-ETNq6&9a-Y3b)eg?9e*Xp.TY<//rZ9eWp31h*YD%EO +r-ko;<UfX01=\5=)LMJaL3tB-?No]G3^8801ZA?6$0`7t+Rs*&0/UEPkh:34Vd4PJ +a1a%SI!+JQ'3s2SG1O+Npk-SX2XqlFHt20\AltO"nK3Xrh.OYe`QdPKWaB(.cr!4G +;+20r72/Tf(%L1@rrD->4DXrsA7+98rhgjF"aT!X4ph!g_GU*qT_OB64pUk@nO(6E +(3SpP+Z)><8[S"0LpWcbG^/!-kmZHf\?rNf4=QuWUq3\ijH<lQXf5IBV*V/rQ#"tN +!5cA;ci"PBMnf$@e,KFD/s#d/IMr.cU5/8_rr<6AJ,]KpeusjV)rVe>`7c;j+2o<6 +27q*7k$*a*I;Q@7gL9f3>2J3<2iB4nCeQV38>!PT-?YG\fP/\3!$`8Err?G4J,]LA +]Jj1ZIb\<$ARDkNrL2p:!WN/"T>e5;9R\r<nV;rkVR:k;nmUk]VuHaL@_^umrm6/g +/cO)r!;_3DU?h['rL&/rPdgUV+-5=U^HNsq^X6ENmpg-SUjp3iIrES5<O\SodcU>n +qeF([V5Qa-Q%ZI!rmQ@1[>m>lQ&=Ei/oBR9HZWBu9ajqC(hAVnI]7h5.ssqI:CQK[ +[=ikW^Z"VlNCVrNKl<kZWbT+_PV;3[%ikk+a_p:Nqc'QX6julsFn9)H8!pjdNqG%9 +r*(8K2o&c1Ho(9tKROd9>."&^nB4VV[?$5q%"(e>/&OOh^XKh[i'm/VO,?+=kT:Ie +$2Bug]R;9e=OF8<@JGi@H1X7CimUoBO&`[;m^t]_ITChirr@XGJ&:!mfAeg.mhC5n +58lb"c#^p4'7=6KPP[`H`..9g5@fWN!9%Pf1]<Sf&Dc1?2uT.,62O+'GjF>g!04E. +!rg/G!pm9Y*:"]Ra2AK4pV\/\"3][!1k1A]&((:fqRg2):2N?hn7Td+g%3*G)>#L> +p.bLb4qpf^j5]<SV>)"gSL<H]l4*V1GRsKr[b#s4HqX6GifAgYZerS^$JXOC_@M?, +,D4tErJnO1iLMX@p3o,V_^j^'"F-]Y*Y%mVG\?^bpuSXWeij?;Y!,)LdGG9nFlP+V +CVL--1sZU.*rVN)?gq^G2guY>8+9a^$8(bIcUbc62>sKQ5&.4&lWULi'I-cN:"P3' +n"A.s2#dQ1)J=f!n@[@:.D,+.M;<X,^TfGK^LnXJ4sg!)i6R?C(9aQ^=P&!WgjF)1 +[u()!l5*WY\`'.qrmj<BGO9D>Db0Y%ia"(oQB,nPdp;`mj"K.sa1`GdRGok9G\?%H +TCIEK4qmFBn@+,N#jS0f^=<'6p3Zf>Jpd?_^q[2dm-uHVDDX$"ch<Y!H1>67rMT9` +%f"LCe36+p3quS3CTlEX`':N9Jc>^_5DWK5L;#87)Z/PIL&3WS`1A4C\*UT!HqWrF +`P7.rLW+`aHkGjlm4%'Ur'@H0X5Z'lr,qG""@$@&'7;n?Uh'G)rZV2Jd(FSmDoK:( +m0.Fu?H9m3$M=pah[['_'7=E=^L2PHrZ9Vh*r'YYpndLR`81L@]G9qRrX&T)nG`J^ +f"=*(:E=gYe3%I*cQ%2M`g/e:n5"h5d68URr8$l*rX!</1Z9Fn5D9%:!rN<#:3[KN +*72?@rr<2npp9Lfe&C4NNk_ap9_a>31\Lms5@b90kuU(Wr,\W^dJamM\&&(E!rPg_ +\*Qo[ROpnDIOt8(n],#+Y.q.;7+Ns&H*6Dcc\B(si;>6\^[O^"!W3h_]JJN-#jVEi +ZZh$YYD/M7m'R(KHs?*,p*TQWnB9jYNr0.Z1k*sS!4/lr,91'uLW:f*ET.KHI!>@h +j(iSdX2<^2eGOUM+o=KO3h++f2>Dmq=2o^l4s9-inAE0D!";&Rps8s?"QJoH`D;>_ +ZnKE>+3#DlKmZAbX5I=h?Ml2@(]5NS[tt%aiC8:G4qH9E%eubOg11"(lc2&0L%.+h +Rd]Tarr<2m^Ae1e1ZD+b:AB*Qrr<IWp1?su5M?5"ID:Wb%Y);S]G'3FL42;\4n.(C ++7P#43S/aqn(:/(M!tY)J)ML"]LDi#$K&ZaYdT,["o)HA^BX]<[IsP"*8YJF0`M-+ +VaC?;/1c;0@I%*e3jsnF!"0&!3gPW^ld#m^ET5k([f6>Z??hmFDqR4h3k+JcTj@FP +p+"G%"n9.^SZ\OQ:JfMf9rWLLpe1K_rrBkp5A9M?rZCp3YMB%ge[2LpHrl(qrr@Y" +al"fkr(caJKt\UQ6cBt!`ROFLKCEfpp>6D8_;7.85Q8kk*rZOpAb^iRAcBii?21Dh +j5P!rIi6'EJ?Ae^r%7\riL^@&,P\\D49!15%(q!(YMnr,rrBM=loApclaD6trr@Xr +r$U+o46+R1j*eT:/t_Ye`?#E!p_i*og(0%s$iNKn!.o3<*q;2UnbXJYc<gQ6COMmk +Shg'[q_ruM?O+5JWVBf_:qcADUZ-3HIggFK"`)`H*PBq0bnF*hd'!%I'(4dN\^^7g +,>b".P29Ef1;`/6[#=A+.&)SIU5C@nMC>3V/&4A`YPBIprr?V#i",gjc\@_&;-soa +h;@daYM4:#c*R,@rrCP)a7]:nFX@6P27WB&iEnBgB)Sho1[q$Tp)a^U2rH?Ih]MhW +lM:snMR:-M?NN>=VX*(e\+%)nUT.GLGa$2)54Q[L4s0\'p_0=dZ1qhLPP>MfrLs3G +HmI2?05;R<^*%YRi75ld08SI&]C9TDl(>Cs_`n0:rr<IV&H:7Q)#jSH^Y/)LZ1sg^ +&*tCkgJ%RG4?_]k?c:jkQ$D=on6a)Jpf90J9CQ<(08Vn+5N+N5d\>1V50*/28+V+I +]'"[P5PRe2d+;CL]AJ3)[GU>kqb2LV:]CD'iVQ4E#lUEGi<oS'rr@_qrY1F5%u7n7 +^Yoh*!/-R5>Q,5lpdtJci<8Z\^&<T^^&<Sdrr?O.!,DIrqc!A.<r3E"rLJ5prr>7_ +!3r0o(5MV.nSddo!<3$%`r31B!,B1Lp1NnC^b>I3+71ebrl&a45M>YeVo:lTh\^eC +r-.Za`G^S4!"HXHGh?fH&cOtA>OZ">ItW7p\bJ,D-2=gESN#KiN4p?!ia3+0ILg@S +P%_G1L,FF8i]m>\Qhr--Ab[qU8U+b=;Xn,4%gm\ue:2"ipa?rsn(B5NNk&rp'?(kS +M`c(X,5`eXrrA!$f.[+9k^O;\-[]V&2uTeTGY_02Z1,e-rN^ochh%:]`8:[l?W%Zn +I\E`Gfm$_$rMfaGIP?$8j'V-rq\/m=pn?[OnMeD$Mgr$&m,.=pi[9RIZZLU3rXp:m +q"N_0K03/nh/iH8_qKR'TP6XEgA\.O?iL+<BR4!SUY(!im(-f$B4VbmdI=oYh;_af +DZ9]gHQdQ?5AL2u*juBraSg_j!,[f-3b3.nRBLo*8,SlIK"pZ1r+H$0eDp7PYK1)G +-Kj_?qb25N^n7s*KQUW0rn%/MYcijH%fX6^5O@Y)PN%qIrrA,[!41SMA)k6uj0@0" +A=ZK<qt:`Qr[<.mrr@Y+r%e$Lq`hu0n0?t#LOLt*414j+q`B$a`k"Bprr?\Wo`"oE +?8:sT_9W,I^U'7m2saQA6ehUm?Q(Rn_S=fSa*PLZ5AgE#fLt7kKYM<[Lqg;e_F+2A +pp]25m.'R$rX&DZmuA=d!63@;_nl?Q*]uOBST"4onTE1EiUPqopg)pn-iOFu4T5?U +kH+RhpmLX+`r?#BFH;2`"SeDSCZGl<pa3cCC%3#":8]b+5@n15%h8GQnHZl`(4X.d +1egW\?gp`R"9-:>!44u0^C>\/<'T<2?Q]2mibsLA&GQPjLOX#Vg(3-@OmlSjJ%$MU +Hj?bQ!5V8!IiNpan9c\T/=m?^\K;\Q2LJQpkDoP6nJD*:(ZVk$eil%!pf$\"iVrnq +YLq%6^[Tcsrr<9&!RIil42G:0GJqg]T+q8DJ&)fr9mcaapq,M*_Y:e7f>8W_rrDbR +q"=MIrnF"ipkrkUpV7m?N_f0m9^rpYIqUKg5I>@Ehi/[@rLJFQYNU>MHHr;F5770u +`&bqlr'B6m?XE7$NNDE:"n?0MKl9(Ri4VW[e#fgW]I2]ScN<m2`h!LJnHXh%B9u-> +DL<Jb*u9*%I7DNE?OqCN?.'Y;<4_9AU5C@nMC>3V.&)Uon&GA-no5^.rrB>Xn72Iu +;ifW6Y#.V&Ma.,u>$(ZRFoMI?0(@\EmJCmQad%>-+80KBZj6T1IhpZ313i!]7irCo +fP==`!7HY+m[O1bMZ3[>C\pEt!8+dX5Q:`JrrDbNYNn^PAcDan;W%1IIQr>Ir:lUb +rr>K;!h'Yep\T8N+7NEog-\W^^M1H=3qn.g#l`5$[R4a.!!PP3ps7q@^$msXn<\oX +F5q1kSeLUarrBrd%J\,`H?I5si<q:RB_q1PmCq$Y[>r#\D')uN42"86-'S2&rLElf +DZ9]n*tpkL?\CWa]N=UG_j^T*Nr4.:*VDW0f_@g6\F;/&F>"/P*Vd2)YD;Z(>;(eT +%j-%aokdW#dr]B4dpX/sXj,@%+hF"=d?-8&-W'9HC!YNYi/b['fe^M&[V+t8i]018 +Hl.LP*X2)+MfkGp?PN:t?O<XL>Q4[(BO2\-A^^JYiSB"%]$u`!NuD\X$f'tF*k4UR +PGqj,AYqh;F)B0O_b0h@rr@e@rr@d:rr@`$r\?;/'OUbpYP]ocpd[GLrr=<CrY,;3 +pf7=on="jD?2]EU!!k7UO'hE%rr@^nrXs_U90_PdrIb'0rr<]>pIb";JDV2k1]'Q; +%JVaLrrBl93pr?mhi;%N4rsQ0n<TL%MetZ$`7fEiIO9;,8+Ea7A)]"%-WAUgX%dcd +9%NA]om:O3=k30>2$iUoZdeI2Oup3uFT2>t?4#/2-@!/\onKm;r'p0/JUSX;9(>u< +J$ZOr^TtKcnR#YuUZ$\&CL>fe=@]&lIrFYf#!O>8rr?\G\t.ucV16?@p75s9::&M. +$]S5Ap`'*&r"T+*^Ys/3!5b&lVu.lkn=]bAHpR_"^+B9a`Aur(%fZOG.IjV#BD+jn +-_:_G!/e*Kd(d&>YeRYhYP>'o2>sl?4\tHPnKukq%jL2YpIb";LOU.g1\!9?rr=*f +'qj4F[[ODdnaTsVpc[YANF(@8K2:#[9s+=&pbVI>degoPg$3eU2nB]P%J]&D/Mc>K +<j^73"S"C@i[["\a`ORX1Ki9*Y7u5>:[j-oHq!&N>2NVtd_%6,CAoF,m6!&\IhR(e +`7I&!M5T-DGF&4Kn5k4KNt?QZ`VSrPf$['aG[J<k%_(G9&8DYmV!'j/V5iA*=j=Qb +bOG*rdk>fIWGcnE)nB$LSi)\rdb`tPN*9@<5k;q]&2Up%#A@^Y#$C'$rr@_%62pqX +-B\<.K%hsX1Iq?fq@EN*1lqPMfmiO\\SJ+q=S\RtZ*Ue.G58ma[YE\`ft1mbjJ$SG +^%Um>h9Cmr3O_Ek;+20r72/Tf'N%;D#OODK!$nCjm&9g_rrCUFnC"g]/)pR/gN--) +ec,VWYM295dd8YE%8Tq$Sc8\(h*4]>[#*tW]tV/KQ]E`cD(VH?1X0oZ4H[Sk8.3Na +Wi.1o?Xr;l)#bQNUI`kH6fIJLkh=UCXBIo>V<ZcGgc)fnTk/1oiOQ>,J_hiMO\S7g +*#u!Z^k(Nq)h&NNbeLMLr$+.m'f7F86cFKWrn@APKlQWQ_HnSZDo1Ckrr<E+!9#=) +ac".<!:Zr$4T$_]r"nmqiA^Z^3c9ePG^%3cn?+ltiK*b04<g+U%InrAf7c'k7n1)Y +4rJ+d^'!joM;^Z6Hu8D7`k?^krmL[n`8?E[*t1!0*-dt?g:dK^C]1_Nn8%"/_I&&k +rrC^&GF&4Mdsp<rpjM-$!&mHsX<uL_[<:^k9lEqu5N-*s;uVDMTD-_4!#kc[rKdH` +i0.NR5O?@eIa"AqS)`u)(]-Mr+oRA-mi6TDp0dY+q`oC]i3o]UKAfVFrr=)7T2>&p +a2\kPmtJLYIM;"]&j@ml4&te]N90h3idZnjrrBu>4qLNMT+bu>!"AMG$[@O#NFtf= +pjN/B"OHibn>Gu)n`T7WnE]ebrMk5sq\]+g^ji_p_^g5iFrA&+NF0LC$Lg:I4_sRM +GcO7%T>Zd&rl\Ksm3=jn+^342D/[AM&%+&.r,V=VY6M2+G^e8a\,J>UjBBQSXT""Q +!mj+Hrm#upIL?)#IL,I5VlFtpi((XkLE?`5,H:I<mnUUe5,7(tL)piP]FgfDpl>)? +dkj3;o3T&WZo^p?@d?_8FH&KE:m0gB/MBo.Gc`.YO4khu=pKq7B`A(b)oJ*kVf/PM +>&Wd7nMeI3\o"dTQ^6JDdm%<6g<[Z<qY^'N`^mJ;:Cqc[_\tB+cDq"]3qetT?d?6> +)sa[1J'#WR/,mW)`ddq^5A0ii&rJ*3MC>3V.&)SIU5C@n^,tl"r1$9:J*`*k)#jTc +j7\#(PYjkYci3ti(7"n"?sS9^!)'s`?TW_l?h.c8[Klc"kNr<M!32[1[Jijk3WB)A +a2Hm/)Uq"DU$MTQrr@[\^Z:j_d!ta@rm1TEqLAI<lh]$-R=F:^\,DR6O5KfK4segq +d9l%pT`5#7Aj>4H5PA@^!,^WlC)F#F>Q+q!iia-_4pV%`]G[bpZc(SDK_ueYc?nFR +phNs_2%;o<n<M=YY5E@CGd[H$UKgRJB89F(]$*aICN*t<[T<6bX1qNBMG)'&RSVQ8 +!887JYDp7Hrk\U7Z$1p"!M]SoDsOu9O2'V@58(0dJ$a<g/'.3U_2m<Bp.+nqnLIf? +::5SL=j-gF#iEOl!r/h7?eNf]8ZQVKfjEDN"bs(\heBZ5kN8\"rrCuH57r>H:Sd2] +ZX8?l08U_Z^&=G8D*N`(&EMFo]E`pLa26_kIht?-RQh3E(&S4(Dh*9uGd#*D%_Q6? +?bjGG9eY0a;#QtL%!\-!-&/,_'B3*4c[ie:pkATS)"#$gr'd6rr%ICirr<2spn-Nk +g./qB-MYLJ5N9G3rr?[2!.qfNm;q8?J=[#@gTQ(I4r^-0>)/+p0DHgMM#J1El<aS, +@':2FI`jWQ'V?Ir#6*01)S?I:IgUE$]JJN=Mk1OMcg;h6r?Fi2Ubse1HplJsU;Qs: +_u!GI5I',4TC=5Krr<N+Fn%d--c!L&a,.QFH_6)i\*S>!DiK(pK`25Ub.]7u0;j98 +^*<>nqd$[fIq"Iq^'49@qa13Nn?9ksJ5]1;a7*!e_L;gdj7,'8e$Pb%^VbL1rLpuD +:NZA;q],NUrL('/c]-ccrK9D(I/J?>Ipmg=Hp.Fer(6F^?i?4:D<#Qf:ZARqNuNSb +5^kA2/)a:C57i'Oj7[-T\&'3gr*Jm%m4n)hp3"`r)ud_.GDUbYr%$3C'B1Zs(EPWc +IKFmPpiZ,Ea1O%&rr<2<rN5u2VYi.C$0$A/i*Yl,q`3q+!ri8YR/[/m\&:gS^'7_f +*<+(TBE%r7fDbi&Jc<uE5Oa]"j37\cL[@V%Mb!QCrr<F.p2g#JqdF`9im!dWi=.S. +e\C'<GYc3Q*f6e1*\Gk=/)tO5j1jq0nKM0jMZ3YZoRGP8rXp=*Eng;je:&]k(AK!K +4a]-e!.o9:bJg%V[VshDp.b&6Kf-&d^Bf=-br]&+Oa\!l$bZ8^5OI.'HtUp8D[cD> +p4:_B2>GA2SiZ?eL!P95-%'Fs#DkR<iG\)Iqd]O6pj_e>`SN6;DS,D9rX&u%]AgTA +n(H1*hE16sU#=7X*[^F&TCIF1#m:&;r&"AS]NaAI;crR&Mgc^SlbBd``kU/m`BUfV +YGc^>nN9P9o2,C=mQL_t1W>I>J$iRrZ/Z;Ng5#Y6^#S^`IaK5Irr?Upfl-j&D[Ser +0C`uSF808*YM*V_rL*S)rr<D`_Kp_0'D'-aZ]Do*Jc>`>:(Q%c?]=Q)f"V@f!9#'q +K<AnAJ&b=frma1Ee344a_u"*,kJ[r=HqF*Aih$m"&q85XFFMB)Z_*<X*WFiD[#`1o +rWiH'AG?t\hse-nF`5tUp5SQ;m*D_]f)@c$B8sVYG^!E^Dh.fmrm2ankJWu,q!5m` +YePn)$fPm5D[Ce<rN,o%i1H!Y#ctD*JY!PEhhsU6i*@ZoLAq7[n*Y\C0DPV(rrC\X +j8T*MX=M\3U5C@nMC>3V.&)SIU5CJ:5A@=s,qJuqAOkYhriaY&Q,?I$\d/.XcbMNk +r'ipo&Wu'9:kCt9*"WW)^4+Ts@UIcg>h!\7NGnMKrK=RZ?W+G#o24%\J(^aXSu8RQ +(NZj,Y-5%tHjr6ph9=/;90L5A9-Y(pjIFa2pgF]c>Q0SBp,W-Q`*<!MrrC7P+7R,h +YdaQK+7+(K!/S,R-iX16)H$A;rkdUSI`-`@a5d%KHjCtf4olf*,+\`6$n;&^J'jC- +`o$QKrr<(Lq;9U"!":=R%"HZeI`C:?MZ3YWJ,/!Lrr<HVq[@u%Oab=-pko(O!Isq& +f;JO$p]^-ti0DlqJ+-\#5J[k_\q[n/1u3(Ipf$[;iSVPD7p"0*B^Q%YV17F\%Ht9f +[R,4JS*[U?8I(_'5F8oH*gtrIq)FE=J(YY5ci4!Ea6`g+?Xcl$'N%:5;+20r72/Tf +'N`>(2u`lAM".\IOai!*qOE#]'lo0[XBGQ'jC6QPljL#s!9n]^lfW3@MuHDNp;$\. +ZX!I%5P7tM[_KqJa$9,Spm_9#>)3*A4b%S2n8)2(P@*#Srl)Y*rrE'!:]CF%?%6$B +ZS26>QN$rjU[e6]!*A@%]&>h.IaQm4_cSQL9+MPIZfucPMR6Y&;hn:eM(\I:4\"U8 +[Cgo?k197[*A[?:9dW@rGAF0F.`d.7o&Jqp!Vl-$^Ce+;@H)bG?:T'JHPC<J5&;lp +VqaC+1Ke)('U!m9l84Y_0t@I_Gi`2*WB[!+TC>>=pa7Z^`W#po59A_?(9_TFDrRCF +r+Y0:*dGRrq!n5OB_1Q$pV]905E'u$Fu5a;ioQJWV*hA0Oj9j#G)N22g1?I!G1pf( +SWluU-`28Frr@Xarl/:L/\F_jnB9dOML6i`N%F#<r(J\k3r[K-Z>13ea)`5$GB<&a +'^t3CZ)-R=a<Qj-eZNP(U#gG^?<h^__iLVW)@_\!d.CZRC!c)_(6tUaBjmq(\m;GG +V4EpEBkT)/L>L4qd`UG#cJIA$M?kF=WpE^nS,WHqb<c..T=nV3!!4^dq#\5/!5qrg +"2#f;^P?/(Hs,uD_VQ+(S+2.1'(>TpT+8M&j0&`%Sic_Brr<E+!(V[[?9S;gKlu>p +8ZfWkj)=]ZHp;[hY5#X.&Y2-brr@^j8%=1MepbSL/9A:nJ&8M<J*69%rrBim0E+53 +rY:`Orr>90^]"@=J+<abn>ru=q>8_B!:V2UrrA-/rr@aaT`5#<hQQqZJJMVjJOfVg +/I29(omclFZMspc$:!KPBKu+_X8`1X7@4#S+Ar.=$Hka8!2$r3rr<*O^\IrB]aI06 +nGT/K<`32G'Xo`;c#iJ<Jk(\8L^#89:k<%A!CGP!'#Y=ceJ(kL'N%:5;+20r72/Tf +GR*Unrr==@J*a-3!WN/Mq=)hBC+C)crrA)trrDCCi_TMD[9D:YMC>3V/!#*s=R]\: +?O6Ghe%kr1n[=WonGD3@K_5_&$/TnZhbgW-HqE<^Kc7sp1sl$H`VpCVR`]Da#O<fa +Q</9O_)g=%QHL9-<V8$?B\W"P+Fb:on3>i,rr>7Z5N/AU^Z^7HB`A(Jrr@U7r;5^X +!/?KIrrAbunY?*a"9&H'!;#ZSrYd]lreMZ9O8KO`'E8'Y^5r&&g[Ft]Ii:Q@5N#?; +!'^6DiM1>+k^iYFrrBk7!.dB4qrn%[L5iqI!29_gU]1;rrWN6$oC&IR)E.KlL`aWt +r"&Q9rrCBZO8*DCn+n/V)F*^Ui(s@Q55IM&UMmp2Jc'3]!5SU7JNs)0kl1X;NP>Dm +U'L4`rr<0#"TJJnn"]k#2Z*K0+3'B>&,6h<!7/B?ph8FNqB18+5O^nq:]CDYqENr6 +DnkLer+Q*1_#FCc5Q(EBrr@e5^Ae2-#P"Sh!!E3'rrDZZU])/iqu2Bn`fL$.p_3Z- +cb>J8B\W"P+Fb:on3>i,rr>7Z5N/AU^Z^7HB`A(Jrr@U7r;5^X!/?KIrrAbunY?*a +"9&H'!;#ZSrYd]lreMZ9O8KO`'E8'Y^5r&&g[Ft]Ii:Q@5N#?;!'^8MeCO;R)LPQT +GE-S=&)r'Akr54F^)M;m:Ufk*kCW+Z)uW:EIO"WkXD63O%ta^disTO:TsjKGN025/ +GA022[E?O+dD0=2_MTbC=oSK_@ab8VljL"Hl$%mJkAT+5r_0Bd_#FD@>lC<[oi(Xo +^WD\[5Opf/S,U</L]3N$IrsT;CfgOu?eQ%i`P:++!<3$prrBE3Io*ibnWp*Bp\%n_ +GDu0P8?]5p5Of9E^#W5CS4(a`m2>EqhX4H-N*BH04idli-6O8h-GEa[Xmt:8n`R]I +,h_!f0B6ht&LO^A*ABsl<urHA=eEa?Bc=+pFU]6_qGker]TTGJ=POFO*:i!kpV]3n +`EsNk:C?lkhcfNhVl/hs*\I7(bZAshT@mC)p3(Ld'BMN,*j>O4$\VPoURiY8qnLG. +r#@eqr%-?Ii2OJuDu;1?)1KXJ%f6M/)h[[-;YYD7!"F5Ce&.dj^Yp[;Vec/uHu/f_ +KL`knZNpCR?PYg>J&2<b?gF3S&,9&;'(djP=^E?BYM":rrL*/I`]nC6q`FLup4`$L +m@eh*!!HBuiMCebi"q1H3UgqPrWpF30'C5<4T1t'CVPb`2#Y)Z0,7:_!!M`@[4`*T +h]+fmlQ<AJJ@mJnc\rcL`FcdP?c9\mC7cEW(T?Z'#.<t^qnde%nKl]"#jhQkSfd_T +J*eDSrktBJpsJt.r!LjO!9&1tJ)T;_)h\#;ptOenchnF!_&`aSGMqqDrrBEKrrC#$ +:W#5BRJUcE:K*>JW;'rahqS/2iMDlsHqX!2a$6sj`OcWjh0_B\'CbJaM=DsL5@Ydk +Sfa;)dBqf(1r&f9rrDs242i9X[?'!V_`tUX_uB]QG^^1u/,oVI&i]Mhd_BOHg#HH< +P.tb6@oN.9qg!Q&rLbgX2>A--h]+KQ!8+X9O6kugn+]:5BCM*Sf)-uVCN%kS_O`LW +J)e$.Vo.s=G_b9kL\+ePQfnAKg&q<]$3'u/"P!GOD*Q\m`ngjnIK'9Vh>:7Q!5Uhu +!,ql;pc/`U7fAo8D,3Oga+O;6n[H<+"b1,;JX8P:nUJM2%/_?t4s0PmiWmC4L].q] +JZnC.LOd)Cp`&0eZ15:npiiEZ2Xj1uV>O9^Gk#CDrP&9HIh;7%Ma+JUm.^#t6N+#N +iOUSQBCPoH?(^hlrr@^6^n2)9!9%h](O+Ko`*.ro.kcZa)=WE:rYL($2rY"jr"K)1 +$F`jYe&QqIJ)M=G!/)<NQD`^QM7NqM]KrQGImk&K72/Tf'N%:5;+20r72/Tf'N]g6 +dpMZ<=8p@'^`WM^T+*<*U9L+R8FEX`[/EKkdZ*k"f66j4:O?6IJ7X)R,6%X=^0#8c +;QFjb\V+4Tj-!u!K1O>fbJ*uVTp&[[!5BQqrrD-a+5(kqoJ12hpZ'0+r1Kh^0)PX& +9l'b+G^'/fPSAUYhga"pXaf5h%fZP"Jrf91n+mnZYP[kKhu6GLrKhs4!(/*@rr@L1 +49#<'$9tib^Z]4Z^[K3uJ,)B#p0IFk%%>fHq!dbP!%98pbJ*uVTp&[[!5BQqrrD-a ++5(kqoJ12hpZ'0+r1Kh^0)PX&9l'b+G^'/fPSAUYhga"pXaf5h%fZP"Jrf91n+mnZ +YP[kKhu6GLrKhs4!(/*@rr@L149#<'$9tib^Z]4Z^[K3uJ,)B#p0IFk%%>fHq!dbP +!%98pbJ*uVTp&[[!5BQqrrD-a+5(kqoJ12hpZ'0+r1Kh^0)PX&9l'b+G^'/fPSAUY +hga"pXaf5h%fZP"Jrf91n+mnZYP[kKhu6GLrKhs4!(/*@rr@L149#<'$9tib^Z]4Z +^[K3uJ,)B#p0IFk%%>fHq!dbP!%98pbJ*uVTp&[[!5BQqrrD-a+5(kqoJ12hpZ'0+ +r1Kh^0)PX&9l'b+G^'/fPSAUYhga"pXaf5h%fZP"Jrf91n+mnZYP[kKhu6GLrKhs4 +!(/*@rr@L149#<'$9tib^Z]4Z^[K3uJ,)B#p0IFk%%>fHq!dbP!%98pbJ*uVTp&[[ +!5BQqrrD-a+5(kqoJ12hpZ'0+r1Kh^0)PX&9l'b+G^'/fPSGL)OU[l+`UoK5>)&_k +M$r3J)g7Z3=R]5:h(<L2D69qQ^h8cP[uAWp)/^?n=NZ$Qqf.,-Q%%@-p1p;-#QC`Q +(>&@;Gj#&r*ts.VCJb$2dYG$oS,NkSrrA3tqa(5^fXL`BrrD<`!;;>Sl-I8^%(/<Y +J$P,n^)Lbb0:Tb'LP^I-Ht>i2;%AiEi3?!,p7:`V[rZ>+UdqJ!;JI,!ZYQG_U$?g+ +CDq<#C"ej!MWuWKRe\b[*s:9Vc,[h>l$bVe,>H\%0R,Olrr<:O;lXLqZ^.:DL&:sm +Bbu`8kWDRNUYYpr<nb2bC0%q>VZY,?lC*q$[s$2)R3d_ZK&0ke'E/;Nrr<IInM1"r +ko[;\LqYA'4s9@_ZXnF@BKQ["GZ2KUgq*C;?,Nbc-,5@#9()Oub"j@SYj?LaBPjWW +PZoriq\M[8;+20r72/Tf'N%:5;+20r72/Tf'N%;D#OODK!$nCjm&9g_rrCUFnC"g] +.&)SIU5C@nMC>3V.&)SIU5C@nMC>3V.&)SIYEqIk0DRB,gB7H"F7t.a!7am)gA_." +:E9B7aQ)8uq[I9^qG?k`dso3=?h-p@BKuA0rr?^3!<#.]d<5CbHm/$WQ14D=I+GrA +XbiN?d68n)G*_>rTm92?rM"`a=@9J]3$*M'>MB9+H^p$DYFY[nBME]eSgjFPV>03$ +)lnPM?Q)L\YX<AHjcW>%pVo#A>#($#gVr?r4=-(>4u]H#W^!d"`nqFO+'AFfSf+_Z +/Z[X_ghC`NF#D_I9sh#(C[%RJk2i%Udr&d%=9L0p*TcS/UGBr`b]!89YPnm"nTX[Z +])A.KB.qqfp7_82q^hY^pmLXO!;<o#V0lkE;dG%6T>W<k7IV\ka87jo7e*+fItB_: +_B4^t#.:=4rrBrMr,&dFpf6gmi_!SAqSf(erZ:?R)t70dnM=+Xf)-t8li%e%!.t/o +=2p\OcEZ<rJ6j>fAs$a+!!FJ8BD`O&o2P5*m1#t/g3'TV]ER;sB>D5BrJg5`[(h<] +<mZ)D_1"<3kD4L+im3L&b.<Xh4B8(j4s&9=%9_um?1j^/!W*U]_.<QGC&\0k?QO^T +VS=2O-?qViqR>f5q_eAk+7N)+!8DhkiI.nhHp;Q>QhG:cYK%a[-Jl*+?[1nPrr<3R +oY5pJ+1D-Ir'BAH)qeJ0HEO%5^Z0ORm5r-\BtAbsi<>[f<ke'XS)aMK[Vu'`62O/s +'D=!PgT:0_p3HF_q`Ol,?82BZJma&M?gpsCfmh"**Vcs`qbVMN8,+J/=8p[`%0$<> +CZY>LVg-\KTBu7N!!p@,!V"//(8d)NcNs;PgVZ5o^(G-!`&$3DZWbn$?OoARr-/#; +!;fK>d!lXWi0U#<pe.Q1S)Xb@!W.BE:&VB4C]=CfS+\<0T=cEG6gB0,a2'tAGRrf+ +`dM@9O?p)FVrBAr^&&iJ5Oeu4rZB>BrA.U+r)]oRr%7^<i]/lW%!XEln=PCs3G#C= +]>RSE5B$Q%#6arunINRaL#95/__:d4?\gm"rmZBOrMTYqi2<3PQ\rKkp`&q&pj8_0 +=&8Vgrr<2^pj:q[Lgk:Wh\A=-p^?%uOlNWtrr<cjO,4no\CWicj4t7(Vi<kYC"dKb +VU0c+HsYD]NsbZ#a$,(5TD1%RMQXZKgus2ArK03,Tmk=Grr<4onRqb30$iO,L4<nh +8&_)<BDD@:#.!(hpoec.-WL&8r"n:\?\A>&\V%fHpp*H*BZn^?GV0G(=h)lW/`YM@ +.iVh-rr>F9;+20r72/Tf'N%:5;+20r72/U*!W,T@aXLTo.R*o_omV9FXr<2["ob]M +pX_=OC"d0cBlcFr11+0hC,]4Xfj)d9HZA0$l?4UlDD%\Ber_n`F$Gu^]#(bl'n!-` +rn_"ep4]I%kka3(YPTU/qS&G=/Y8`6C;1KG#H-iF(N?WNX`&#nrZjjPCN1!hZGAY# +b8UMaT^buGiSa_9Ytt<?:[E6(!/H3cfDbh5g-=QSrrB<"r?"i%FoMGsKAkG^!'Jt) +rr@Y(rl$>p+5<^e4!9#siqL@24IgAinL^.$k5aF\GQcN'nF5o!S,SJDp-AWX_m/,Q +rco3-_o'5uDu)YYf`V6S%`a+7q;bEQ!"TS<M8/9N5@b<&4!"J'!9]JNZgbHtPkZ]t +a&1>CT7[(CZtI0mMgD7aCq%!YTCCINg(02s1qP-(4*8*aD>Z9OSP!**=1FPE'&r.b +Kr1.aK:*uRDb/Km=*<KX7nh=umdG9M>?J2][4OU9n=RRiLgE'jACInGoS[!YrbF.j +bHK^S=]o"qqDGpP=0YuFe*8j<a!8[E7Xt=`)Q3N6^*eEi07M9?r6E^i!"$ZErr>;; +M;S)dhm=d2!&M!3_JuT*cjSiWrZ_+($3("Lm2thfC7k,"pmqEbLqW[PJ+G`lr$ND\ +r;aPZJ)W,1d%C6]dJj1Q`i8t<5VIuOpl"%:-iX0&["#r4WdiA'ickAGp[&:e!6jgP +n=46coE+fYrr@kgqa,f(4%K2r)Q3N6^*eEi07M9?r6E^i!"$ZErr>;;M;S)dhm=d2 +!&M!3_JuT*cjSiWrZ_+($3("Lm2thfC7k,"pmqEbLqW[PJ+G`lr$ND\r;aPZJ)W,1 +d%C6]dJj1Q`i8t<5VIuOpl"%:-iX0&["#r4WdiA'ickAGp[&:e!6jgPn=46coE+fY +rr@kgqa,f(4%K2r)Q3N6^*eEi07M9?r6E^i!"$ZErr>;;M;S)dhm=d2!&M!3_JuT* +cjSiWrZ_+($3("Lm2thfC7k,"pmqEbLqW[PJ+G`lr$ND\r;aPZJ)W,1d%C6]eqSh: +]<;f]3i7sPGC-4Pg-(acWG3>YM`qjQ6L[16*hBH0X4lesnC`'7>ls0^b4b!`ensHY +ZARWFT2)qO8J4H!Hoo;3[P,E<"SdmXn<]&^4a]9Zrr@n'4fDV">1S\4kWU0!^!lH; +>bo_B)et$G&0<iAB81Y73T-&kA<'-prr@f@rKmNakiBGmrrCuL`#noaU])4Ap0[h" +UQYCRrr@Y#&:a9!]&*R<eis:d-htGBn.3FoNP5JaLQ&lAH8PR._n$Y>4@st#C)8'C +DItW@*iFgrXR..?^XR1>)CmBu::7^`FUO+C)dA5V^5K>1e=tHb9@X[+<gM<0lO3S, +hV&j@[sM41/ab1Q]"=C=RsD0pGiVHYK'!Aj"rUiohrg'TMuHDNp;$\.ZX!I%5P7tM +[_KqJa$9,Spm_9#>)3*A4b%S2n8)2(P@*#Srl)Y*rrE'!:]CF%?%5Jpkajs--gYF7 +$UOQ!iQ+,cj%WV70?.cZ1W/hL)IA_Jm3_e14Cl%n[D(5b*@"+-;R>l]p9"=XrM-@E +g.SF!g-[aACEYS+8,P0Xa1EiTN\JNC'B61*iU(.6Q\(R^c\1sUM#RGVi7)jEM7*tC +6FYM,e74-k!pDOcQIh]Se+FUA\(5r-p`kW(RTkU0[tDr?i0[lX?i1T[i31/4h,F$7 +n.peWHo8V,kD@XE=MOWfi6N/5UI3Qb-agM52!ZR&/$%s@>sWH<4eA>@:=7h>CFe/q +8T*,9DsQ<Y1?ZHFphG<RD/]28^Z-ZUiLg,MiKldfAcDbI:#d>k+[>J$J)P3n_@?MF +#]KVd5OblV!9Bf&`;]i:&o[P65N+?s!16jc,X9qrnL^r"j8T+2+TDGX1$nlWp^?$r +JbqQSK>Ck#piZ(Uins+qU;uc`p5JsL4rO-r?2+Tne)D.>nR(:J_!d-;&O%:aC@Kin +:Q5;R#Q.Bf'3j(9<5/l+0DMTg4q#_[*Il!0_ce[RD6E0TpR]4ZYP9EalmM;c`--SS +n41:?pd[R/+8c0:/pP"F<Z`!(0n7'ApsJRYiFg$R&c<79Fl72l?f9H6!"`c;9D_\< +_Ai!2`HoRT1`mB[dN/bjVrJ^<%Y&$Ba5S39V`[$:=.f83rWpdK!!LsDrr@U6,5a@1 +rL<feiF$n'ieQn#!9#$Q='rE2g[TpVfBiU?G^oF,pj]A@ibnmG(\gkec]2?HIN`sF +i+Kt-!![\Uo`"n1GZR0*.B\%_[IF1rNr1]kD]J".e&B'4r"6%*J'^(1pa.rdM;nO8 +!bree]RQaVclWu"5B62qiGZ\r&*b5R5Abo)?fMBOrYKr\pb/^`HLJb4M15PT.==nh +TmU#52thChJY?E93i!R@1ADI@mtY3onKs<D2hucZWG?r)/#FcbbPhffq[*0Bn__.> +MI$;X)?#ij2thChJbr%:=SQ\%(K:*unVbAX41P'`qd4%1^)d0I^*W\TnTToj+R@l, +q"O:\icg1E1qa8nnX%:E6fLmtoM$Dsd'V+,C0D@<LSR4Io*=Wmrr<0trLQ1YHtE*9 +n_<('!/Q"gqal%eiZJ(4J)d1!'PlYCLZ3/linoJS%=AKWL%/mgZhRPf^Loq(UW%_h +kJSI$*sn^l4A+HDrM=YoL)P!Kb?=L4)XBXa$JbD,_R!t6Zd_i@'N%:5;+20r72/Tf +'N%:5;-j;#9n186?i)<;!.XZjHo.uEMYP6cg!qr3&,$M3ZF.@ep?,-"oRGQcp6jHQ +X[^T*L@2rVe%@-iX32I:=dS9.in!6/VhS1pUQH;@L&fiikCr9p%g;ZO7IZ@kFA4j7 +Sf.%[SSP$7H5;W[%@3K[l*aG?2V+1[d/]<L)CWp5G:b)!niJS_rnWe9Q2^h&i4n5r +Ho(d&Kq\MH$:Fe.7tAA5FT2?X"oeQ1=8ipFr$FTIrr@bHrr<D@rXp=J7"=0+J&26b +V>gN%?i2$Rn<O"R`A.Am$&A4Mr$1+gOT,=!Qhs;M9Dod@hsK)]a^=SAOoGDk+8@6[ +m'H]l"UFg0YC(!]OFAX._VFp%iL_1VT+!U'&q@b1nJAu*@e%a%iU7/!Y>WaI!3uMN +pd`/(rQj0(%iT\)rrDpkcUt^pk5Ng+/\[o1m@L"gBRCj!]"T'[D>gKLCld6S_O:^j +[/U,_?a0k61XGtNI<3=fB88'$%H8K)BjZW6Vo8T8!ri8;][ZT2'l)J%Xo4HVBf@r! +/cPg(PJ]QtqZa:o>Nl(egX](GTc*_RIbe?>rr?l=ph?Aq<Z24(Zlf'S:YOjh2sb&E +&,m6u1W,Ygp<U\gJ\U]6rrBo?YBVl&&apHZU\dQs3fj1b)LqZ0\u*%<!!r%LDZ$!M +m4Si]*aBe#o0:t>HYIpmJ&+=VBWUU[euh5Nrr@m^&\6HPN>GI%n=HKR(&lcJ/CZd@ +4j*cp>orIQrX&(C^FbY8M!CQPdk8EKgSmBii/d[nNs1$d(\l>V!3+$*rr?D8=3M^: +pt+N-$hhsB"oC=5l2Dcg_*8Z!Ae3Vaj'RUb9:K3Ur)IChYCJhP^LAhtpcD:!YG0QU +dOkA]WG*H]ViIoB9l#0Nl8jpeUSRZ[D(eo:XfQZ'4aZc&i",`#"68j0^MWpRi?-$W +#^d`A^LbS1&:@CN.<X\.$YUS<e%B\Z&!+<2r"OY[$>KAdI`koMO8Mr9:P]g:%eL9a +rr<YF*WH,U4>$OFlBbC_WT-bjrnCd%^'&ZkiSKhHL[e5(3T-O-rrCuA^UCVj5MMsk +08R=kHs=r]+8C&u)Fs]\\(Rk^*GJh)dPQ1J!/9J5!!LsQL-KK](4#WQ-LkH$Y?Ulc +ic"3Cj4t*\^0[KLffTS:GXkHW^U<u*@BB6r?hf61rX-:79&>$>^DT3gh<7'.oH,#s +Qc(!8:&b4I+8/XHnE7UUNW/tt2!k\M$0hHi"],)tZsS?5Zaoken3=]1'_MD*MVf2! +D>sTkhA5UY`gq>5cb_p"qo#rB!S=t`IMHN<#WtuEMk+Wie#`BD:]:]`qc_eMn)8K- +9fp:#&,\M9J,]KXBE%r<?cAR#rWu*p!!Nl-pZ'"-J&78orrCuLSgNr>p7M6rPMolh +I5CT.rJ:C(Ir+QjphO18!/?p3#Dtb)2UVL@r%7^0i;WfD+,4.12o6LaT==`4qe#bS +ph0IUrrBl?rZcZB++sPDpoXPr"QT"O!0/YD$``FI"nALDINJDUnQ17Ui0aNEZTmi0 +H*$eI^t7HYr+"_\2oGLT`_VNL0sJ+UcB%KE2'!dtS/rR;]Y5rJ4B&*ASj1j)K#@;Z +rOb6YA,A@Oh@]P9q`im2KD(T/YO4Ver?'".(r,!krrBO;m$I_P$K"AC8*k*i<rUVJ +2qBUq"+JZ?[\&/#-N%$H!/.ZPi9/"]$2?a)rZ1MfJp[m`Y<V"][.8T3nHH:g?\:R5 +rr?XaNIEAPV$G#$LYi`Orr<SGnWWZQpq[3N5E#C$JZX&85N/5p]N'+DCZ,3ma1i(2 +6MPDnC\k3Aih$VRJ&Yh&IQDj:f!WhZhC-pe8#Z>9pk<_JDhXK!&H4J1Do$`9`p\a+ +:XMTF;+20r72>@MNW0">i]M#V!+]Hpa8VtYZit]0m";?R)E*lX?X7BT+(h*?gPa*+ +48D]IR%*%&_<DgqcQ&l=nO@^+$o[#t"bF;Uj7_ccIL1Bl56Nf)08[HNB?03mHQNl8 +hZJ@?DhbCa5KCJOnCFN;!$L/1q#9u6D[uOMiO5)P#)VuMpk.U6$s`)ecYmm$>@-&X +P8ATTrOMQmN8G6*pa+o?45u=fj72./!(alp%Y=+Rp6tbgrr<ITBB)buBi0_0?#S]Y +WGcY55DS,/D)@'eP3Yqh#KH`ge9"?./hXPi)]M=YS9mEnM=p7GihsWY/Or^1HSsA: +7<S;m=KUt^jt.StL>l;eUP:5\Jh)bHmH'`W96flBOqs$pB+ecOrr=8ZQ2^hl?%;kS +9>!2j4+>slnmu1:#Q:+Sm/d4eWh78<r_/nDrrA2%`8C8,e3ET!lhdRZ!/"aqjI6(p +J+bJnci4"AJ,Q,-qU,80J)lj'rKSIchtk)O-.foZ72/Tf'N%:5;+20r72/Tf'N%:5 +;+20r72=`&[+G5=Q2^iiU[SNg!*A@$9&Z_PLqdirq+''(]DhkCg&"]tl5AXYr?uIA +\GXD<pA3C[2ZEc`2oeHO0<Ah"rrBl-5AaWL=&[_`2lp/,hW)D$n4pC]7u+JM%o;^q +QD`\I*n&O2#IX&+q@AO&Hpa]ErZo#*r#r4fU=W62083m)LP\,DV-\E(9_"/Kqb[2I +plY)r_Z'TH^LisbJ3"5.#BSkk#JtpC[f*O^C*+)`#K!&LpegQ/8^k=<i#$o%Hp,mW +!,&Xs?]U!9eF5#XRf$1TS\Mu/"b+I_!BP\;iX9+JT=.P^n`)]J(J2+,?e`3NQgK9@ +rr<1Rq#1(Crm&ubg=,binZT`Dc[`TSrr<ZhM_>Qi_I)9jMn7`*_r$?7l2Dh#O7>]_ +;RZT*Klulp`Vq`5KmM(bDiHspr[6TQ_B%SCmu<qRIqZ$Bf3U0$B>e`)^PLe:7Xt1_ ++2Q8*J&+XiAq/Lo`uYBY]AP?rfDZ)Oq\"9?dXFAnQDsa8CDs+iJ(Q,Lpi60qpi#k+ +Yg>!eB[;Qb,D^_hrJWirnJXWKhCe>0QBgrPiYRUR+DJP!e\"KCUVKO^XZ#hADu:6[ +p9*p%`t-o0ia9P%!;Iq_rLa)iA1rc=!4S<m&]`qFhs;[P;>V8V6%<SJ!83;(9CS"] +D$0L0pb[F4Hm\XOid]X";&YM6mhC"JitqVD^Ps<Ir"nksiW6=a!/-d5H_6+_FhWHW +C&\1kM#RI\n&:@@1Z=,Vc_Gk\rL\SuQa@9_TC>bIg2$D>p_!"/C4?GkLP1.iT8=U1 +pebhr^I\@Hp^?,cLjolH*W:P#iOf*i$NC+C(SZ/7CL7U%_E[a22E<T1dI6OQ`;Q_h +%,W')!8sIWS<<\Kq"_Pb+7R+][]o#\alhts7sH9X:*[S^p-ns:rrD>(q`i[=rr<EG +rX,.p!(Lo(!.X"O5A78"PjNPd5667JrrCb'`EG4=YDoi4J+a`Sambq?r[.+n_G?*i +!3m(%+6!5"g4R@JrrBr$Mu&Kfp&>#Oh[R5<iNLuHrX%E>O+fb9nG]oVT7\3d4Ai"l +RRb8f)>lgn;=J'tBRVl7FlBlYA"e9G1r&f3rrBhd-c(9RHms!nrr@ueX8^LR#Q-%? +n&JcIgL'GT*tYetM4PCUqtj=keb4L#T>QX\a5QuoL[a3pT-(H_m<S4Xp4Ml7!/g[t +iS?0Gn%uhrRKp_N8)T0?%u(umJ@*!-qu]Fg!.n$p%/;jP^VuhNc&_7uqbQu?i(r`I +n:'-c:Y%aCrF]I8O(-SP/o!Qirr@XnG\?_,McT>>M*+0&/5>Z3j5U?5q_\;sH=+Z" +SKGY^&ZpF1^+/nfibofCHol8$d!3UIpHANV`)$.Q#OF%GG_8NVO20Y-Sfdoer,&/& +InA%hr*B1:r$VH4^(BQgRZE0;C(Ju^p:0TFM`aY0C7bSjBAmbIpk-@('u+8ILXC?= +gW$rKnIL=ir%$eLMOH^&L35#=("het%mYX&R'nmo44$/JpcnK!rNH,$qaSR)!"5S$ +(/O<.iS+mqgJdY"`uk(^g-=r)iD"Cm(,(:;F8I'S_r*.urr<>NJU_-ifsAKn&cVib +/b!i?-^CJ)5G(sG5PufK1\#sS]Mmf7`8C&X>9=^7rmZB9nK7.lqd8VQ&,uWH+,Bh= +n8I\InE9-[Z*g3Z4sbH7`F#mZJ\PZ2HfE*>`J<f0C[hMCh[hI?+8e"c=-*@]Z[;f6 +?OH]^[J6YorNaW*V8'qH7ooD/IOFJ^WdK$[GJUJ=Y^aJb^MEiLMZ3\GMYoa/QgHI' +2uJ!kk+2MP"QS!]YWoT\"T3045:3Mj$blgn_H*98I"1Yc!92>A?eR`_;r6F,]>?`q +o09Ro(#gm%>5*_oCD1rRfIhUlr-eO5]ITaj?a=%UYCliTZnI7uCZ5j<Gj"dVpo"!i +IafDqp_WHVi]"g!h)iK"9s;L-&8PV[^LNni%nQ6RLce,%B`l7?6a4p$&:?Y9.Id)I +h[I41HkGjCL%'#trX8LLnDBl<!/6F4!!Q*s!2DLOJ3WR8T<q]"!(qFKrrB3O7e(,F +Hq1.1ZUaNZM7c'UlM^kT"h00_Ff"<UcCLtM4q$m\kCn:>j/YBQpp.oZk'N^1G_57t +4uDbQN,>q=@OjT2IgYtW_`f6$fsH`c9>%k".&)SIU5CJ]^Dm1Qai&GUrlDjjO8Vg< +BF=S8!;9Cl!0A#aGQ.YUdGo`T;dTg2o2kWCrL3^Bpl"iNpgO1&"Z?7A]L;;hh`a-Q +r\A`Ap7D&)j)5Od_sm!c]8gfhT+G5Frr<)6,XZQ6RWj<FC%08L3P6=#9?4VdV1A%] +`f14Y4@*m=L%PhQ[%F%AD'.Jqi<SaYrr<2Ipm(_QHo8@^r#bJ+p.FkqJc'HFcNX*T +_!%C0IqLFgbJ522Ho#2Fn?^(p,IDc_PHO\U;80R8Dgc5L9cqEi'_,bO=FY#:C2fF9 +%ZC<Yn%oV4#l'bd!)7XeAq0Un['0?.li-t]?/`Bjg\0Y,!0\o2YWqL+5A1T+<;nJQ +nmh4)r/MgErrB:JrrE%_4b%SR!:Xf+r0(LA&,sP_g&D'P!)NUpZsA/b5I3$@P*Z+J +ZD6serr?-``4G^>.&)SIU5C@nMC>3V.&)SIU5C@nMC>3V.&+?Vp6u!]P#B'g<.4iI +!:'LWBj<M1jW6;SET+Zk?O;0,r$ClrhEQ[B-YU7-quPfAe)sA>p[IX0mX.BpCIr)Y +,Z@3OG5?@[b1f\\b`L%H7Z'I-%tn/7,5o!XfH[WLrr?m(K:7LRU+O'Tmu0J@0,X"] +.D!!ED2)nW<q,#6Xl,0D;UlWbqd&WTi?&S6>fmJ[l'C(]eGfO<X?UU+qE-Faj8T)j +J)Xi8!)*HTpf8KG=8a0Vm.()ULd1D_rrAns8j4)@h)XZl_Yp&kmRQV,nLU<RrrBl# +8+RG`pf7=o[l=7,-g^U-!/NT(/,oUH#tOnCrrCfCrf7`dHiF)a)u,]u!7M(GrY9qM +c2Rc6M>KI9!9+Ic9)enUJlSEKIa9)O[3(8J&,mr*d%;EX1\"<>,/*LoM#J=f8)^qm +X]+/lLM,Y.'DNNc?OZ@M<T:XJ'7>*#l)JuZ[*JjLn&BZSOelc4G\5ASST4,eT)J%n +j%l`R&cVhY&`Bki,6%W\rrD^OrL`EFS+[`pg[W#8p7V2/qdX>IZ*2.X"l0+X_-jo? +qbg\<nS@.q`1P_`6MP3j8pIDSSfdJ]m6CUN"CKQhASLX\j&bUQnNZ_pnG`L*+8@14 +_N0[`#C\4KS&'T\Q*-l@`%Ma:R_Qf0$2f2@Zka]CdsQZE$9.tb'CXZT!9<O)rr?a$ +pg=Vd5IK('rr<2;rL1,IJ\M<e!9E'&C]=BjT+pYl!,)o<.Jrlb_8#J1O4p5HJ+1]) +'7LAhfDZr,e,C9?pc%2>KKitMO$4fXDs[Sbhh'IK8&B`r:%7a>Z1ssT+1I%''gMDZ +pbVH8p6potfBib2rrBoB?c2mDpa?Atr&OX,r(Hh$q`X_8p6bUei]l*hp'(Dr^,C3d +p6sH[@f%h2gV[A6]FFB1!9-5^YM!j2?P@l%Hr0Uoqdt<*?OK%HIa<Kar#=WCp^*UC +_X6grO2/M;rN&,&qdFk#rNe_#ce5eC58&?0qdb(BpeUF4i7I?4n8lPjZpoi?1W*Q2 +VX[aT$i^20rr2tMp3\af^Of18^i'ATa^58MF5d-!"o*$0?-mt;n;lPcMC/*IW;JsV +RGqQfHp@Cqqcs%Hpik`b"6)8TrrA]VDdOL`bTF93llg*?.;D'[HpRX!koM&D_>aK< +J+3I<a8:?(k^FZLBCQAKIgl<7rrBl84qrIkINr3WOSD2Z56`^R)15i^#E?G!rm6)q +pq?1;_O_bB>O`*ZDoBs*pohQk,5?0IMc[431cTqLnU?Jp6iP6c[<@W.<IWE2/F`t' +IqoQ2J&+*/DNEJI4u$^dh[)Ib_;Lr<?NYdmYP9EAm/?Edp6P6_Tr3-JitoM'KV&MB +08Vn,r]5PF;nb8:qo)nFr[,tg,lASZU:b^>]AbJoIQ)L5p/:D#Kf+>Z!;6Ek[/U,B +0A<>tT_Na#(?>,,pcmeGL#3;d62P6Kr'U7]n0>jn"$^Pq5A9Kmr!W#o!;mGdir9"0 +oPH8TP43,NoD:9?.>,PWDi0$]Iu=7+"oNl0DhihJ[t1pP%tdO7T7sQF:1)^F6L^?/ +O5XH54puhm+170G:\Z97r"IDlC%5iprrDE9iX[,[ci4!L[Xk1JO5`B`IN*YHp_3.d +p)WX`lp\#NShJfu_`tR/!6&m0VtR.[A02@H-@X@An431jrM#(e?eNoHh@fT.iua2/ +!<3%2%gdV"n:uf`3I]<S!:Wggg<8[jK"n]PM#In^D"7N*mC2t%rmT^l3[ah="2eG& +nE5D;"]+WnpV<Ej;Jueha1iF?i3%MnnNH?X:D/&%rL?)b^D[$urr<To&H9sVhZl>+ +pk-We!3ug"q_3C(n_UP@K>V%7oZn"OrlDhirX;q2ci3tKr+5b2rl2NUVu&^RLZ(Jb +!W*6%D>h#[iSFM6*.B8_?FnY$@ZQG^n?@C[c\UKJ;;^&.p.bPeK#R`;?Os:2Iu/Z_ +?\H'2rr<G%_I'`)m(!qKhZNYCiD56C%esQ5&UtX5?3^0PnAeJVoa=P4I=M,[rn2e. +#Js+UN$#B,Vu'hN_R8n=_*?ZR!)rZ2iSWCfp6,3iq]L@-h@B(*JMb-og\*`-UF5h( +n[%7WMne2BJ$fgRjY-!%j"D;Lg"Pf-"25V"/cCb0'5JXQp_3N\puB=SLVsR-&:>\s +MfnR%a?O2WgO&XdZhSV3=8)XB]FX!$!/[$!Zk(.efui(/kNpgZ4\*Ei!"8]TYC=t# +!4G]$&TdpWoAU;6.p;Af`/bk@O*kHr57d%O[=\0`p+4V(1@j`P+4,,ugKrY.K)!UM +a?1.52/?BTiVRNle).Nn_;-X8]D\=M.XotJ+8r%KD=G1:$,Q0*)#OX.%m^)I]QrI? +h/D8]r"DT>T+Sd]I")(qK!>6nT,%>8Gf/R&^i'b]@JJl,2Q-%Rj5?QJ?Q9!cr\N$9 +A,ALg$M_[2pV8^O.CYN7UAZ24OT,;pYDBq,5B!aZJ)KdU5!AoG_WCdtKHL10<S!f# +rrC`9a.KZX`P:'[i"q$Y,OqPH?OqT$r&siHFeO19nB^L0q`Ff7qNlr=2-[7$r#PRQ +nI+>H^n2]UBV-BZ@IRL@'E8'L'rh*OrLElf?`9]V.&+F;n\>!l7u`#bMu4Fl!7^'& +J?PUF!+et%!IiXMp3du@rrCS,n;=m-?2-)M7K1TI'RhBV9E,!DIe_s^FC#7Jh;[fl +i]"gEfjj`BdX4u3n@$6oVhcB3HsQ8,i=EkfL=[?YN'83G?8oEGF\`kL55tW"4u;\o +XfpMp)WmT/Tj:uhMoIjsq[7YXj'V2!Ujh$)[ibMsbnJ%p)RT!kg\_BVPV%92(W/Io +r[.D]WuZECLVs_W2oXYc2,+;Hiua(@(M_0\T+A"BiQ$Zqd-&mMFhZAuXYW^H,%&a- +^Z;Qq+TDGGO,8AYf54Q$rrA1>9)emq0#.DdV;</pS\K%qq(f/."9-Pdp43/CePG2Y +rr=B1)ufoZiSn5A%,_'bksO(s:]CD!`IGuerLj/jls'F\rrD]k!;f4:oqVC>rrC.i +Du2"K+2@JtHqUYmU5C@nMC>3V.&)SIU5C@nMC>3V.&)SIU5CJ:5A@=s,qJuqAOkYh +riaY&Q,?I*+7W^tNK:mdJDV3X1MW4W)O?`ZT>FqN4sXlhFebaVGPZN8O,5thIqNOB +Oj+ki2ccAhd+cYf1<1B[Nq#Z?;!7)8!e5.U=&-'N[6NAdp/1<[Tmpnoe_c$]p-8>C +nGmfSH-f+Y4*]I$gIo=9Gb.P@hF+rn*Y%W"(Qp#b%[dGg2dZLaL3ig25NqW%AO$)X +rr@q+r@f-SiL[f&&,JCafh_VJ&3o0Hrr>^sOhYP$gVn^Gp;[&r&>.rhfK\AP,Fn;= +pAY,(kFp%i%i@8M0DK0egX#k[1\OuO!5oBBQi@'5ogep9YO1NQ!(WIS/H5_0#CK2E +!9j9SrYa>]oD\fcbl*iWnH6KOrrD\><un8GIaXhi^+4RAn<_/@M8'5n0mH@OkDQuO +nD?R^rYU"unTVeiGJW,qnFsf7Ld)b.jOQ0V'7=oslYkdD%dRF4pfHKK-Fg@onAX7J +Xeq+!FUNQ,'gA2X8,SlIK"pZ1r+H$0eDp7PWI>BWg><9%21Es1*ku<j%-ScE1li2q +%f7XI=2\GZl!>@6pdP'IIq2hPiSTWEnB@IVe,3:IhY^RW!/=DHRsUoX&cJcEc(O;C +n=on?G`0Js%D5j*G_9)*Jc,NJp4`MHJqSA1p2g#,nK[#]Lctq?clP.8Qb\[8rrBia +[3+cZn^G8[n0UqbT<n"%T,r6<j1^?lNdPW<Gi.PbJc7S&e,BrM1\P\\\)Ru2ph/Md +_;C1-0lIP8(3[D1nFqb:>HVZ=(;0JG;uNeIAW7ADIh]U4lT_bcm/I']HqjAVnG`Jb +pp'@miRXpsPMt&INrBRe/CHA'$cH5P`4q19&\@KP?6K#mnG`J^nG`KG4>X/**]!7t +T;6^Pb=D(<KrNkm;-@Z1I.n[H#OV'N$cb<cm(F5%l1$"VrrDFJi1J^Vp7Ll!m(]QJ +8,+P'S&'P<n?9`*'mN7B8,]*1CA[2D2(o$GN&6N,_uiR*/"u`albRc`a+j7-5>M!; +V0L!qkq/tfoPY<<#K4C2?f>Y8T7K%Am13j1J&9S-J)]A\<;(eKBD>8PiQi;>!5dsi ++70*h*sVX(rLa!JnC,<Rle5FXqsX;c*tUU\GQYh>%IpWXr+Y,^KpFsmQKc+6nZS#O +-1Lo_>4Hd$kPkPOg)gd<+8d\i!/7oZ@d/C3mb\Q1r':8^Kls(nIbNZs^LZd9rrBo5 +XA.["pdqj7oFD^b/cDFC0RPXcrnF`anN;,!LMo;%A)k+Li<f$\V/bH5D\(Q9qTc#& +*ta!<+5(/^rrA);pfm9,p6PZLrYKrV\bL[608gV'%_a.bAGC<*T,l^7/F\CTipRJ( +Zf542&b.f9oZn(AnG`L*rZCobj5"pt?8@R;D[pHL5D"8gWTmZP[?lX@RI%E\Hi$/- +!r,iql<6EjrrA'D#Q>]XqDX2inRf&&q"NcTK)?^hkroiK_S6laFVfnB.j#>g>'8)N +LZIr#^`STn0ooJKMCgjd/+?X"hhY=okoUBt@sOhs_5)Rs9n-[Ypt=]bJ3H0l[IF1r +i4m2@!dm6&J&N^pikNJtg&D%BdJRY8:PV.NIqt[0T+eWi");-apq>[:m>q$A?gu'B +'L![fD::.ToW;W]B7Vq$$H)Ct:[faM$Qk3(!r.-7#K/:L?f>Y8IuD&ErWVSu\+[ST +hi9o4/Na'/G[&(prZ&](4;n+$n5&bKq!$b0)Z3%O/p6J%&9rEXrrA!?5PlXPC[efd +Hq.tJCB";'OEg63Zfmj+PdXacM`#tnGlIc$J)I)oZ%'YU)LPfZ0?8"b)rU_rm+8<9 +,P]''$;5C97f6Hb;t+j'pl".4It1n4a6abGC**e[nW/\]/pps`rr<N'-ha,+@AWa^ +l)aO57n<)Lc2EXQPe'9>*nN:L!'gFlIqjJd+5j?@0A0^Mh\5ohn:/DA[2t\3:]ATt +?aHB?/SF+hM=C,s.F`i^^PS;LYWfVfYJPbu+k7t1:W-TNp_2U^m18aA=Llk_]H+/" +=':0m^*\\Z%h*6B"i$%E+8#koIqp.ZA&\;b`*X#L:\L+QTDVRG%tjl^&3M3,TAn^] +iSTTd%^iu[iHN7*L`SXMiGX7\)LP!&1uAo#hY8JX#E?GEgKaY;n;i3L9a7$F58&5/ +hL>gpg26;7,*Pf.rr<B)d(]RnHO#:C]F48Mi;We:/\GfW%3*05%_2##`4lKNNXlnM +X2KJlXhSPq%sVa)1MHX8jEgQV2;d*Xo$?@)NMC3)Ff=St#-P)B-eR0Li0iTV)h>^% +ZG1"Ke%@Vfi89N$#Q?++pp\0`$FFXP8+7V?hguX_NiAq(>Obe]jl,S7`L:VW-2<\_ +SKh8+rJm$;>(4cR3g/5deMSupGdG9%)1E3$r%IQcO8NkbMr=nn]R08M!5\[8paZmA +/b,=gf>@"1%n6Gi4qIEii8;I;KDLABNt_<Sa8Z,]5A'?mC#8sa*A2ip^;j+G4n7CR +r\NBqrXJY=Uh]o0[4fDC%euqidWFpkpfG7;.b-Aj$@G/0Q\BfYnIOR'Qgm0$%;5Cj +rJUS6n8#mjZtG2ECVSPT6N*N=Hf>F*$2>=s[^5MuiD,a/CW;qqZ_,1e\"4K,T>^Wq +(.$lN&bpt/kJ;U%\%0nnI<+pVJq"+%RMft.9a=X%?[qR;M``NMh[9-ka'Jr>2i%<F +i1Cp4In&s@K=k^Q4_IO[+,,)FiEm2]f%S0"]M7@W&ErjIRRY!LrHCfFZdgmLB1onK +e$Vn2lIu*Sk'Lh%Q+@;NYE&O!rr=DYj'V[-rr<6fof2oC.u_ih%X]DGrrD"'R!;\> +^:j&lN:l(a*[J;,n[>I%?[/oq<n^<@WG4?&A2`\MBi4m1AEY'Vd@l1g[Y2c#k^%=8 +'5AGQ]@="X5N,jp,Q@b;,>nI@_S]@Q!9@lZiEUaTrrA`;;?$X5_c6X9jD*B;pP&Sc +pfgqNr'@(P55s/5dj?m):Au`W\3tM"iGe;A=ih>X`?^SMA0_C2KcDVGI/a3;0kk"g +"kWbSoXi"`o=+,+rr=BGqd=p,Y("_>qPjBs?f]8fJ,('H9E,!#S,WKe.!kmWrrD!0 +YPBN[Zlf7)J$].Trh'2jBY+2MIqE'`,q],sAOkeTriaY)Fmns"72/Tf'N%:5;+20r +72/Tf'N%:5;+20r7<f()2?*Y%V17j1quQftebS^_\8!PR0AF,<;>'g(rrC`\Iam9O +L3W^4:g6`rOgtDkhadT[iih&Tp;tg5(OTTb?i)YW/bjO`Hp[e+IPp=Q,4R%uD(FVT +`X`LWcG'VQ[/U+26%,j0Rbgg^CRSmqr[E4af;q(hTQlr(e9ieD/,mlP[*OUn)U?&# +eN[`L+5g5<08b2^pn+STBqMbTpVK(#iK(f.g)j2^n&4[T^,5]lg3t2ocG-TEm=T3O +45>>&>N:]a_u:DMfC<4'/\c#GL>N2bZ0\T/D7jT_!"2<jN??g(f$\q;nJD*HIL#Ba +Ns2/&n432Epj;m'rrBo3rr<<OJar%lrr@^Fq\T-2rYKeFZhmPtbl7ZYrndY*q[`TL ++7R\X!!n#frrBoOjF:XqIr0\P)u[JM!!qJQV#LG37K&SO]qKphrr=_P!3m(4'Y!f/ +d/O)VJ+t87mFCaUp71XXm5_"m_*\/%D+hXr0B8tsrrD)OL#95g_p8_c6W!QQn[mft +_iKKn*e"E?n)08ChhN71n@l[,ZJ4;^T>nSIpl@YYcO9YSe%B\(=D:hY$8lZZ_u:q1 +]C-A^_0J3qIb8#4a6bbD^OuOdZ'Xi6n<;fA`?3a5rX)ffT7Obbrr@V*m0*/J"T23q +4+&USYAs'X`1Iu2GB<X@HnP0tnAAW.M'%=,bKg?-+1#>AC\pkA57bI^Bj)%b58%47 +rZ]'nCJ"Kerr@Xdq!5m?3WB)2FFM#s2=\_D]KtR2)Yh5D.fGY1;r\B)podlN]LCh@ +(&4]F*s4V3X*B98Sg<h1YB+uiCRY75X'OQHR`;V4?/96VStgFLH[B`qbjrp/hmL/L +TCKhu$1,2M+16<R:JuLf_4S0OmXNroJ+,u+S4A1Gm#1dSrrCR/p4'9k?eOcE#snGU +r$g-FA5=7o62e6@0RQO`^6dgY!8>3*5ITI%`kCl*iTJ,5rB'`Pn?7a63.BMh5I-oA +rW<)B*s2HBBYOcHK>er%:q[\'DoL%W?7`hc[G[)\Hnb>.iEm.1^'`G=0lnT]'%OPm +!8:ZV#r1H^)Z4FAY-7>YiLfK:!9:g)pqQMfpeCWFHli6g5AIse)rnEH^Y=\?#5G+T +08mTR8&RQ/p72)Or+#8,i:$%@nMfG<`"*12rmMninaF@4jSc62BE%rDf_Dr6O+dES +VYi^K%u9e,#5F\)?@ViirrBkd^MSJSX2Vgd/Q]ra&,\q/XoAC=GY@"6N5#sSD9pr( +b=A2BT<uZ*48W[ibPqPD4qtj5!!MZ:rXjCIp-na<rr<BUDq\.KO2KieIaB/H4CP7; +;tud=:P8X!pr25%(?=Cma'Tq`/cMFAZi:$`Qi8K5Yl=]>/I1:MLjWRgr=@g[rZD*0 +Hp?X1%4bf8D13g'#JqR6TC>u*LHZkc?OQ0A&,]`&VoN*ceU1<q!!NW&)^_9Ja5Ypi +N.)4`@JG3.c]*Dar]GQfnYa9A8md=f(&ku!oM=4/`4l`[/9jD=O2?D!KB(Lj^[Q`h +^&c]s)=)`,poX&OiLe,>'l(Jp^+f=Lp4L'RZ#SbVKAX$uIK]F1nJCnHTA[JBHlMQm +%g%-!e9j"@$X!2An3=qM'L!jg-HEuq)tgti$a9=O5K>oC;=HfhIg:3Ci;We.mX-hM +&)&s<n;lohq`=`55IMBp-i6#Tc<h*3BFbIL`B%V/V0j0ZWP;tnJmJfZM;fTfc1)J@ +0_#[oIi&"sQhFGS=n'bW?P`YAC]=BjhCmG4Vt%74XY\"I)h!?(rr@XeHu/9W^gGfr +p(d'QIa@`&ZS1^2q`g-q^*`X(VKelqmtNrKq`&@,+7N!cK)Yi?:TYR&pfmVJGdBj6 +e,KCUPMuM3rr<Jli1GgOI])]`p?_9mNpSZQLcY!Vp!#kD)V;l2DhpUn9@)7#p8@k` +5Anf#hmLet:[kje5k'8V^))/^rKFla#K#@%Hu/S9qb)@@rY^4Dn,*p$9^qM3m'R@[ +^Tp)>%5U]QJY;pXrOmXbpqOT[Br"K'!!ppK.\@>up;b1aGDgRH#j>HYTRP/CYW'Nc +K&7EZ3,3n,nG`LTrrDSqi0Wh\rr<KMi31i:_B(/r%uC5cifNq<i\0&mrX**2>l6%V +dQ@]8HlqlSKYM1Bpfj=&rr@Y%=o9lHl/3'jlb<[P3UjZ[&.NPSnE5W-!,)ft`K=jQ +:QPM$hm&Ngg&;2)b9+,mQT'^a+F_m*&GX*"ce9=*n:0iCj0'AG-fTo;rW.7np-&3< +q\/tST<m_J$h9,3iCCjOM*D3Y0R+\t:[J12qb_gjhB'#uBCUVjG@0i:?Q4IGOn]_d +n:uY15L&4a"oJ<"m-FNdI!tM)mBs"ogKtcZpcm`>?fIu=rr?fGT8;A2^)M=_`h*l# +mt[Japp&b,pa#%=^P9<YJpnqhUZ"_[CS-J*+1Uf9r"FRu^CWMKib`nun%sKC_S.Mp +V4mfIco`]9XWt_brr<]):QRaHJ&*s+D%D0s!!'_CI`n0BJ$TB>r"H&W@/g4Y7=9LX +mI=CYpsnn=_;6[:FlKV"-a/.%!8sB^%mDB=4nqV<Y1NA[nV31rVo/KjdQcZ^EHm\_ +?P%TpnRLJuUFr>3H;>#qLZ.BND=I@K8`QF-n.5F/'?:kq$@Wlj&GV+@hh1nen@-3j +&,sY"""_)ZnSe-3rO0IUh)k=ap]^/*p<`bo^,p^]o06jUO-/7Ic/5d%L]/fO0)^d) +\(&0(c\3DOq#0-c'R6[t^&n(BDt204QboAIRZ6dTrrBPm3<&up3rUT'X5g\V-2TuU +,5A/;!!qbGF\hcurr?b_flKL34E9@D+2QZTmId,s45caJh[o)1)DobKp`e`sZi-Nh +YC^,dGO4S`!c.f9LO.IB)DrUf5IHjU'a*Dpp:Uui^,u*/:[kQ[r%S-NJk:I/M"k-C +CZ03KS"GK,rr?qcfDbj?87*J6"6(tm-K">h?i?9Ti0`@@rr@XeG]q9H?PpF]iQm84 +5PD"n#_1fsIqT:8iV/*a>s<1^;>?43]rn7JmsooH3q99m*:s&Wpa/Mu:O1nPpa<8_ +X]JR%TBn[)rr<2rpj_dWp3P-!;lBO^pku0<*u_j/Hj'DX^Yk-eZp0:o/a@d!\bIPI +fmG@GJ`Hb1L0sFG^Q!2J^BSP&?1C"oSc\H>M#J1bG>J"kj8T)ileX2_Vh)DiO.QdY +3W@#?[9(>NpaC'5]HuP:!01,F'L"3u<U`M3IL>;q)Z?nQ9rPu!pZ'llU]1;s^$o@+ +j1c@E^Tp)jA,Bs;7.A6Q4;75urX";<*A6)3Qf+/'_`n_-=5sgGFe*%&Q\%>:V.lP1 +Hu&RCrr<2qpfldnJ`![bloji:ig,;u!!p?Xhh1q/Z3TaFn@.C1L4?T&I;f)sh];VZ +pb1R/$2D,!rn6f/Tr[s<=8p;2#*GiurrCc!(9=Fup\t5+qc9,`?J.sTbeIN47=9=> +J,)'b)ZKfj(\f`b!3+$+!EU\DMT)4e7P-.i^q0OGrg.kI^39Ib^Z<PPC_-D#ce8L$ +!*)7AC]'RtDuTe^LP^NI1OoE)V%6qh5PuH8rr==rp4NAFZ#B>crrCOJ!::S0erT1@ +rr==@^[.osIfBDuq=-)Nre`+uMC>3V.&)SIU5C@nMC>3V.&)SIU5CA&!<&e0jHQkH +PokN@qS,34<t.TGNVd#V6c>T.Iq#&(e%gDBdVeaX!#Kn3i-4Q+iD0jFH@/LOrj1T+ +&GUIhg.mX(=[7P0CV<)4]=,)UM%m=\8r0.'"Z?nd.M(konOD6!m7Zq5_`-Znm@GEi +O2WaSpcl_Y28C&u:=oCsia$V!f<d,1e\R.3:"=@_>J#/[XH<u!i]\^gh-]YEIuSm^ +f)?P7Abbrq(]7M4D;oL#htTc,*rJb-NUSUA^'F05p,9*_iXI",Do-rQf!Scg<W:.m +[&kucdsg1_EV:D[9sE-LI&l!`H)!P@Ibt))n4mnT^'N+M+*^l\deHV<kWU*fdea4J +!6!dL-81@-34St<eaN1CYO0)]^DVJOq"*K8M7fn69&^jMrXqF2C;8ET7!9UndJh%V +kuqD_BAS-UcbEk+qE,kQYQ"S5\&-JjC9-sHp2/3,i.E,W=,Hnnpq,I&`SZ4b0!"J/ +626T$c[jZ=2uU;b@I^,1*s8nM!/rsmVPirfj5?heIPq%QpkReRKNe,gCR[[-NBC51 +lTjskM>TlAl5K)QWp:H^=,?PH/P+6qchJZ5KmYQ6:PYMUrLuC^iD5+j5L=j:pfGC@ +!'De3!!OeGcf)(\TmSTl6g=S0ds`lnF2OWV5)9#2fR*ej:4oajnJfIag.%.]^P=up +e,0TWC3KS&n--R\r'd@D>,I_]Qbq)'a8QC;n5J;$_iKFW%)6Oqfrr:X%DEI"&&KCs +^)qT<rYd#sHI)L6rWhp/LU?LWpeO<WfC9*+F^9>orlWT0Ws]I*/T4V<[<qAKrMri2 +]Y'PGM)i<^rmIDC5AKZ@48(b`IqO7eh@B>+rr<Bi0mlCaZ1/o4pgrH%(WUhdJc$(O +Z>]+\nA='`nQ)pRHehUpcq^FtIu4.6d.E5j:?pR%<4LGRCFEeTf&>Zf$LgR`S4A1G +m#1dSrrCR/p4'9N.&)SIU5C@nMC>3V.&)SLj1cQ5!6N0(fD`k+_2nOi5A1D772/cR +rrDUEBj^Q;fDbjC]>Jd_\*_07!%(_C@"6k4IaB25WVft-jN$;1q&DLirr?JsrrE!G +HN*1/!8uM5q'Nk`+8q+IZi:%)TDnn)])K'dAmb?[/cPf^8)s@CYKr&Jn+9IG'N%:5 +;+20r72/Tf'N%:5;+20r72/TfGR*Unrr==@J*a-3!WN/Mq=)hBC+C5g,[!l.+H:<X +KFu$'G`,P;#I,#m%80Pp!oMk5*<<[@!V?Fe;Z?`2l<j/q^Ae2K]8uqO8+rONrr>>( +n,*R'rrD)DUK,g[aOG.E*=0<f#P/R,^U?"<q?gt%p`";S!68dPf[[b#.B*7Hi,-g? +!$.j3@oiU0TVeLa!.hUDr-"csrr-GAV7jL,2TFtA\XCjL5oYrVWPJRnK?+;*A9]?u +:*[S^p-ns:rrD>(q`i]bPYjj`;+20r72/Tf'N%:6n_]?+!9E-%C]=BF?f9!(V]W8L +>b[*+(4Z,s['0?.li-t]?/`Bjg\0Y,!0\o2YWqL+5A1T+<;nJQnmq::r/NrfrrB:S +U&P+h%q"-Or#6CN!$p1iKDtqVDt\\\!7UrJm2'-\O6k'i!$nDUm&9i2rrCUFnLhNS +MC>3V.&)SIU5C@nMC>3V.&)SIU5C@nMC>Y.rMI?jPct%N++QTP^HNQ;%7O]MH:Zo< +m8;9b*I#GAb,WV/'Sg#EAdKR_P]IPD8elkd*0I`M>tNkHMC>3V/!7B3`kEGfM;aL; +GX"L`[u%l*r"nD>#\lCrM0rU0GWTjA-(=#7NG?p>ZG4ic&`\q[I@'pEH`LqO8aPfM +p@otljnu)%4jX$YYDlo]8A5miiUcSPrr>FZ+7QkU21O!tpg*n"LX3r3r=@D-_WppH +rr>HFiHP8C58Jb@5>hF\$`i;hr=Uc;htVTs=oSKKrC?c<YDlo]8A5miiUcSPrr>FZ ++7QkU21O!tpg*n"LX3r3r=@D-_WppHrr>HFiHP8C58Jb@5>hF\$`i;hr=Uc;htVTs +=oSKKrC?c<YDlo]8A5miiUcSPrr>FZ+7QkU21O!tpg*n"LX3r3r=@D-_WppHrr>HF +iHP8C58Jb@5>hF\$`i;hr=Uc;htVTs=oSKKrC?c<YDlo]8A5miiUcSPrr>FZ+7QkU +21O!tpg*n"LX3r3r=@D-_WppHrr>HFiHP8C58Jb@5>hF\$`i;hr=Uc;htVTs=oSKK +rC?c<YDlo]8A5miiUcSPrr>FZ+7QkU21O!tpg*n"LX3r3r=@D-_WppHrr>HFiHP8C +58Jb@5>hF\$`i;hr=Uc;htVTs=oSKKrC?c<YDlo]8A5miiUcSPrr>FZE7WK_f>N.N +4AY#+(hc)KnB^g+K)>l4%6I/Nf8I%T)".D(KMPkJO0)^Q2n/X]JijS;?aYC#EGKE. +Ba('q5/BA0go$V]6a!/@.gfu84sn`f%Yds0[CI>4b%+')e3#q"4!o/$8C5SZ`!(=A +5bsWP5'd+:^Z<PPC_-D#ce8L$!*)7AC]'S_F8l4bO,:X<25l#h6eD',rr<8BJ&sSH +T`5#Y>Q(2o!;-9kqaK-/Ynt;`$J"STS$%-6d3-0R22u@!5Ju@s4tubQ7nlcdeSd=i +B'.E-d(FM!ko,'FL9IO_C,Z^gp/]c,lKWXq,r.>Prr<<.O8TPQ)F*a7^,pi9`hWME +$Qo3bIPcQ66%!kU!9]>3r#cb>oJlb^rrBuhq!J(]cPhl>5V.EKEVRr]!/UmLg]%8F +2';=<=T8BJj7/oE!"o\"\,QFjC-UYWZ2Xfta5]\h,T"L>rclqB_?"0H!(2geO8MO% +No0d)W;cjN!lt:q+,qB;OC'$Cq;Y?P!!iahr=2%15Ofl14+HkAU](f4+8Qt+1k4LN +I!5MikD`"0+;R363;dIi!5sKamJd14)NXYY/:Zl'psK*AM$<G(rrAWJ+7Oe_rrCF+ +nY?*a&e!a/rr>/r5N)UIrrAb5j%'(o(WUInGgq5[!Fu-06]]6Jr(&K$hu*#Crr@]R +a8Ui9N?@qW?QHoWj&,gV#R-:f%R1jrnK>P-J+L[TrX]GZqAar@J)WbAG5hR*NCWmh +(-hFNr+Q*1`.IdOrrC@`O8(skJ*:nIdJj3'"nC@I!1l%in:4X@!79crU]1<,$fe_r +!'G!\i;g._!27Hn8,abMa)Cs%e:5=9A,cNrO2d7Y82$"\r:-`c!/(EoreDST+8Cq) +*P_F1;?$V%"crTl58lcV+MKpeo>[R)&.9TV*.B_oi^%s8rrD5A8,OJF5P-r5B`A(N +K)"a5!7)REpcpB[k]-CFrr@lZn_='DSi%VZJ6;gOd,XYkrr<Z>j1#$g4DI+>!3uP( +TuZ1`rZi<#DuP"2J&24rrr>EI`fL$g094rfnYlHfKEB3nLEDKtplJi'^\Qnerr<<. +O8TPQ)F*a7^,pi9`hWME$Qo3bIPcQ66%!kU!9]>3r#cb>oJlb^rrBuhq!J(]cPhl> +5V.EKEVRr]!/UmLg]%8F2+'%7>mfi_cD@[k:=e@.eMM\h%6qcq)EokMO%4Uo5h0]D +bo5a97+Zg/W+d0*p3s3Cf]kBal5!GZip,)$=sbu+H3G%IEM;XK(K0+q>sWUPON2>u +Nhr^hX`>NoYa^cQh:e^DOX%5I/B[N7HX[NIU+44^JRe][4nZ#V.6L'!!6[u!YUk:e +^:!t@KdV\kq.W@m"crTUf"^^MnJD3*/L^V6Z2XfqqUb]\Sg+17jeX'O(-hEJ>5nT> +PJgi+[%mL"a8UGO?hdNDMuNdCP^d(CrrCAnC]=AA:D&*IdZ<`&+8PB-J)NuY0E,-P +rIJJY!'U"Yr$a`srrC575Hl\gkJ"A(!/2eV^\MS6p'$NTpX[+KV"#9$!%fVu^p3n> +T).(<'V6NK!9L%!fG6^Chcg$[_NVoFr3W6r!lt:elX0EbG`2S%QBZl,=T8BII;Ai_ +$;V7`o$WTc$Qo35Xaf;$i\1:7;>l&Urr>4TYP]h37K3A28jBO2J*4PRrr@a#Lqib" +8+m+Ir:edWn>,Rjrr=W!rkPf=!1s&=rY1qJrrD/W+4q>noA<aO!5ar<?i7:+qZ=h; +Hg>&6;L"-"!1&k!?s*F/c[BU/!")L6J+D$KljFp2n#_)u8`DE&>#G6LA,cNlorn9B +4@T8Mb=Y"'/:Zis[Jp5\-]#P5Bh.k#O8Sb'^[S&h(]OIe./q#errAYefDZJU,6%Z) +V'">*5P*c9rrBp"`*`GLU\b,5rd3s<pel?prrA@LIpcCY!7-(/rr<C%&V'B"O6d5r +q<tG8iCW#^r`W1tJRe][4nZ#V.6L'!!6[u!YUk:e^:!t@KdV\kq.W@m"crTUf"^^M +nJD3*/Lo;2QY!'P_qN'PQL3^ZRu<2oS(jpd;2CnYh;1kGEq.$agC-e*N\/Od75?lA +Rm1dDq(f/."9-Pdp43/CePG2Yrr=BA*<-#[j5P"S%;I!];"ae9J&/BsnkFUI5Pl5r +!,)<3rBL;/Fo-UWPP3o'Qbm)FGalQN?O],;nJ:0$1;dbb?"ZOpGJJ]`?5W1OXY5:6 +T8A`p(Hs2ETme_D(XR<LIn]WMTjR^fnK*hNh+,E8Se:HB=a6^\Z06L6rLLm`?[MeQ +"Wt,#Ic(+!q_EOsYl;fEfDa:sDs[l2!!MSprltHCKR`t:&,,bPr;QcOE^-i/!+LGp +V"h"hYP+#1q`"KhQc'uSGsCeQG`V_Y^&J(aO4n<_DuS/_R6E'%Iq=+Frr?e^\+YLT +!.o>srr?[2!5^u#!"0&/HgUf^mC==krrB;giDP&'rn%$;Qi3ER!"-p/&)04=pd7/6 +rL.'m!5V1m1uGeB!,2AUT+n@kZG3gI!4>#Fqa,eK3j!n<J)MLL!/6((^%VI+5PEln +m!mZ[n>ST$rr?j5UW`Y]rr@aanH\HQ:])B-,Q>q@J)I*qRW$s\!475f:Pr0pfjEJ5 +!8=&^rLlI6SQ<MY^[R<a!5cSOhYVepqeept5O;2@<1anT[JnS8rr?[2!5c&:KD*V= +rXdBfrr?Yt^[P+t9fMJ>!89ZD-cISHm'#f+m8(1id%Bf&X'b5Npdb>aiF)ZcDgfCH +rO4$K+8.)[.Skr:gA_.do)A]rJ)OOX62P;Yrr<=;M#RI]9AfLmrMH(XrrDF.fotE\ +L\=gC^\4S[IqVXQ-WRADDu:j[!:[4B\%^b_rnES6O8BUi'dpt-m=2KgJ(_U+XM,hU +ZcW4IKJUPrfqn08'RnM*qg\=#m)eci_ttt]?i*dh^U,Bd'<9[]2uXEh!;KY\gYZr@ +rr?oWrl=torr<Ciiii'jg-!.:htU5Z&:W<nrrBnRi]leXp\kLaBr:jDqa^?jpn_Et +Q2RobWV56''E/<gC]0b&!!rT)r%&rWn$i,m!4Ls<A,aFH!/4#7=8Q@l*aeVZr$sFV +5A]n?i=,5up1\mR!!OIOdsg3QU\cfo;R$/Crn%/!qetj.rr<2e[JnGN!/,k$((^-5 +g\h'OgN#N`rr?MX_PHt-rNGpU0Dd^-'pli\[Wt+,L3SdV1qinhJreW"^Mj,!rL#hu +=FXn3C]=BLICJp'rr@Xn>5nS7rrBo#rr<E3qg7`0J*jcgf`(rV5N,Lf<W:VI8!j1D +rr<DLiXad+TD1c98,\l_rrBl*m53_0rr?a3bb#TN5I/&3rrBGjg\X<e_)e]JZ[^pU +rr@b*rOqq)!'g24O6hAKTCAgD.fTKarM.R8c2RctAap#mSko8-Sg46Z07VoTp6opS +rrBEUAl":b+5(#TrrCcFD>g.mi13o`ft[$;rrBpPrndO%TDNLrp-7n>pf*k#J$o:' +jo5<mrrBpApk/:d-iO\'&cViCQbW#eqb9$:rrCb;ZR<^B&+$LeJ*g%?rm7;,:9.ag +htT_A!9%>c!,m;#ci(<tq]GMZr$kL"^Y8\fB_)0'Z[^q:HpRXBPQ(UgB8HQfZV03N +./g$4pAY-[+5)k;Q&#'7p:p=!gOEm4pa9(Fp2BjRrrDPmJ%*/LkPj4urK$ghrY6g" +?gW?MH%4M^n>s>HrNjWG!/*/HJ(^[er"OV/:&BG6O6k!Y9#LNVqd95!mD$"Ur"H*^ +q`"KdrrDgr5MP(6oD\f-aSs6YM>mP_PN&e>^Y1fbm,.S=?P%\>jSo4s)<*mIdeE_J +5A@"n^\^OpAGE!0rK[>brMfMJg(XGleSG,Qrr<?)!;nAimI.O[rrBLgIq/Jrrr@aE +nQ5Tpm2fX.Du;+=Ld,_HJ)Lh++80Dqrr<3E[*sK]rM06FHr@3Jb5U#leGF1O$3'u. +lF$W9rr@_%rY?%<pY5WG!8DNYZ2Xf5J&8#VX8T6qO"^AhrY:d<+1?GZn:l1Kq_Z"d +!.p9cBtnTcdJ]Is.9M(2Ir#&aC43SbK`;%(F^"eFB)_kJ5Q:]k#Q&l8rdX=G+5$Sn +Ys72FHr^0tqa["OS,WHqbMj&e4J2C94@f@>%Ia3/Kf%\rrr?a3bPqPU/3gGT[Jp67 +525s(J&+3`J$XX`+9)=pKDiLWrr>:We;rsGfUqZ44t?R4C]1$OrltHCKk9cZm@I,O ++eBqXbqFS)Ua`2>^\^Op?hg$trK[>brMfMJg,&^7gM?bWrr<?)!;nAimI.O[rrBL' +Iq/Jrrr@aEnQ5Tpm2fX.Du;+=Ld,_HJ)Lh++80Dqrr<3G9fMJ>!89ZCrrC!\&+$Le +J*g%?rm@A-:>9.BhtT_A!9%>c!,m;#ci(6rq]GMZr$kL"^Y8\fB_)0'Z[^q:HpRXB +PQ(UgB8HQfZV03N.10e(DuS/_Qi8=6Iq=+Frr?e^\+Yd\!.oCJrr?[2!5^u#!"0&/ +HgUf^mAV2[rrB;giDP&'rn%$;Qi3ER!"-p/&)04=pd7/6rL.'m!5V3):&BG6O6jXO +1;iu>qd95!mD$#@r"HNjq`"KdrrDgr5MP(6oD\f-8H-[.M>mP_PN&e>^Y1fbm,.S= +?P%\>jSo4s)<*mIdeE_J6CMiIrrBEUAcDaeQ64degA_0,T5FP%5N&*@^Y-BkO8f3s +_>`<gJ"QUQ8+o16:\[n]'n<XjdH1B.B)_kJZM9(GcR8]'cOp0WAs^:%;A@T/hu0>I +0DnMJrlY5lrm^g`m2>p("RWVrc2RcsJ,U2op@m>>rrCeO5I(4g@Xl7jpoF@sp5^m( +2uXPY`#lF55OaDPO8CcIrr@Y4VOR;Z!::l]J)Y$pp/gt&p8?YpB[?H$D6NYOr$24A +n?@DO^>J,Qg6)>pq\/rD-cKH[J$aKNft[$X^**B\ao;?o1W4drV=4<rKf%\rrr?a3 +bPqPU/3gGT[Jp67525s(J&+3`J$XX`+9)=pKDiLWrr>:We;rsGfUqZ44t?R4C]1$O +rltHCKk9cZm@I,O+eBqXbqFS)Ua`2>^\^Op?hg$trK[>brMfMJg,&^7gM?bWrr<?) +!;nAimI.O[rrBL'Iq/Jrrr@aEnQ5Tpm2fX.Du;+=Ld,_HJ)Lh++80Dqrr<3G9fMJ> +!89ZCrrC!\&+$LeJ*g%?rm@A-:>9.BhtT_A!9%>c!,m;#ci(6rq]GMZr$kL"^Y8\f +B_)0'Z[^q:HpRXBPQ(UgB8HQfZV03N.10e(DuS/_Qi8=6Iq=+Frr?e^\+Yd\!.oCJ +rr?[2!5^u#!"0&/HgUf^mAV2[rrB;giDP&'rn%$;Qi3ER!"-p/&)04=pd7/6rL.'m +!5V3):&BG6O6jXO1;iu>qd95!mD$#@r"HNjq`"KdrrDgr5MP(6oD\f-8H-[.M>mP_ +PN&e>^Y1fbm,.S=?P%\>jSo4s)<*mIdeE_J6CMiIrrBEUAcDaeQ64degA_0,T5FP% +5N&*@^Y-BkO8f3s_>`<gJ"QUQ8+o16:\[n]'n<XjdH1B.B)_kJZM9(GcR8]'cOp0W +As^:%;A@T/hu0>I0DnMJrlY5lrm^g`m2>p("RWVrc2RcsJ,U2op@m>>rrCeO5I(4g +@Xl7jpoF@sp5^m(2uXPY`#lF55OaDPO8CcIrr@Y4VOR;Z!::l]J)Y$pp/gt&p8?Yp +B[?H$D6NYOr$24An?@DO^>J,Qg6)>pq\/rD-cKH[J$aKNft[$X^**B\ao;?o1W4dr +V=4<rKf%\rrr?a3bPqPU/3gGT[Jp67525s(J&+3`J$XX`+9)=pKDiLWrr>:We;rsG +fUqZ44t?R4C]1$OrltHCKk9cZm@I,O+eBqXbqFS)Ua`2>^\^Op?hg$trK[>brMfMJ +g,&^7gM?bWrr<?)!;nAimI.O[rrBL'Iq/Jrrr@aEnQ5Tpm2fX.Du;+=Ld,_HJ)Lh+ ++80WRHmJ["]IiY4ibk%NT3M)!j1g:s/+EW0$sjuN'_oH$+--!l='"gmkDH?`h[WG\ +HiWrbnWqqHrZek]pj_f\4"Vi#;Ku5jEc6[3!#+_+&+*(]iTH-#[%Fe*U5C@nMC>3V +.&)SIU5C@nMC>3V.&)SIU5C@n^0LWH^Z<PPC_-D#ce8L$!*)7AC]'S_EW6"`O,:X< +21PW+V%6qh2uFU0rr==rp4NAFZ#B>crrCOJ!::S0Y+N!>m19+/VtSQ$/H^Ef`;VV] +W;cj.M2eG,QgEi6!!L2JAc9+<!"/U#D*.V_ktQ,=U%)@'K3C<<]PiS_'2F.n,kst( +2uF?tp@FdI+n.G+4[V^6C]=ABF5g9g7Jg<e#Q9V\%/<IonYc:8DZ`N"5NBJ_W^3KI +h\5u4gA"V+0#-?5!9b=+LAXWBl5HRS4rAZ<nI=?[%Ypb4GZcT*C]=AGkWB[#0DI"E +_U$l<8+96fr"T/2`a8arA02lj<W<&QdB3<pd68TK:&*-57<fck4pLcoi,9"ue&PXF +rr<Y=0A1.!BD*.&A,cO^LhQc0pgJC*99!<!qRtL6r&=,W;t9%3HrusHd_"uCH?\^G +rr<GIrYb&$(Qn^tK_PH^fDID5pAY-j.pn4ln>!bWVuHbU&H;``hu4M2rYfkW&23fj +chLjSf\bn"n>H9diGAMo_SZ<QKYRL<p\Z8Nn=KX[`ALm2m2c3,"_dk)rr?^1+8195 +/mlZ6n@a8\r!!&"^,Pfti<R?JKDtonrr?Zu+81RVZ6+Ydr+kgY_]Jro1AkPT2ktaX +pp]rY4*qA4X_Zk?V=@LNrWg3-ic^Zapk-BYA`fr$56sL3Z(J?=NrK)l&(sJ5O2NH\ +/po;AYO,tqBQ*D7p4'LKSUYC(Lu0%3;r].u/)gL.NZSl"ILQ-<nB\m04t6KXT*YE6 +pg[Wi[85LhiFg%k;rBj;!3j%e``AEe`J0c:p#8dB.9GkKiEt6Ohu3s%WVh(hDSK\> +XTQGS,`C\aLAX9rD]*9TVg-D)\j+)L,2V@s?4cVW\a]mM?aKN[Y$$!"`Ej:kn<=tB +n0a)9ehg/Akl)XQ4?]n&l'rNCrr<4Irr@Y!po8f^`SjVH>=%dj\&84Gfq7;;m>^*g +CVmdppmpDO$b,KKq`F^`n]/F`MglMT2r`X(_,pUQn\0XKHrB`ZiuJ4lZLCnuiZ3Bd +H?P]nir9"6qF?Ej(Th@W+k694rr@Z/J&+%8;8'>$ZnB*'Q(dD+ZT!cSBB/<]g#Mds +ZhQcS^V^!N[Prp<)riU)X2LVJ^,Fep)Md8V_+b0oT8.fBiHsB6=ST5_Ig0W`4SP3= +?]$Hm4^12A%??_>J+-C/gWja4-R\7Bn[nM=?\Z>b\PL&Vpf=p"D>sSBVu/'kpdkDb +iF2Xjn.3C>q&DWX'_0NC5P@>>-Qh+#ngeIIg)o!tfmi3mD>qqin6_%0n61=p9AlG\ +I;_pVc/To@<.>1d1]&G3^U8!:28>"*$9rA,!!.Pr4s0:nZhm_drrB?*O#FKE0R0Yl +4jn7*T)i]a&p8%E:"+J]J+5_[ie@'VV5NX&^B18,7sT%CG#%o*rX1=T!GNYFOak5H +nV>0A!<3%P'?@fqpi#\<rYC:U_>@;]/H5^("6(DZ5Oa7(!5c+mIqbPM=MG!O1&h4^ +a5AV-^[SNk1&h4\fAa"$"9/?.gYmY#/Y4Hl!/*GR)W1H8*^0N'KPS:$!/IJ$`..9h +J)R!6iHZO?:Oh>OJ&<ddDuTgS[J7H8!"(ge)Lq_u&,8m!!9'G4iMSp!!ri7TD#XKh +p&0mLL&V,[J,U50r%'Mr9E,"Ohg`L)rr<Aor)6V/qb7"i_+?k%YLs<&$/gl)IhT6g +1WGRT,k75Am;'Q\%fQt9DYWb]Ns0n:pj]6pkoMGOfC:LZp^?TWia/bV\)A[!X'Z;j +IO';1[.(h_[2hb5f;u?]B):].?6P5"i4NS"!!RWI!.oXo'>jbL[(hUnhqD:#\aWtm +K:r4g?7PaKir8ucpc%2VO*iV>?OQkc3dupdrlq#[nD:q<?Nk\88bf?Zr)D*l8"o3o +5-aU?n&JU\RA]=NpV%l^dB/>L]FXBcrr<41pc%,,KcdcYrLS/FJ)]A+0A-Th+71qb +C0,_*m.^BJGk^oDn?9lnJ02,k^%&]*iCCk:g-<Cp#.+(.U$LDN=m2;UNk>a>n^#*: +1=J,J8Nn-;-B7O6o$^$6&)]DE(=VtMeph1Trr?_>g,J%5KA[gir!31g^L47(hmVs> +iD5/W)t1Emn7T^)[thiH!VkR2AVZ#AXSa[19j>TJ#E(HX)8gBfgA!=*plVNM7o#6( +0-9DIIEfIeg%V\+TC>hkfo*aEIad"XJc&fWr'KpDm&-M(C2]!D,La@7rm^eRm*4H! ++8cQB5C;pia$1(5/,n+l$3(!<IuHSt49_p5=2V7G>'T@!ddK;:nL)3-p$-cGbMf@U +IOWC9r&`F'/LUN*8c&hHpg<"RVst_dY(P^#>JqThrr?V[MZ3Yi26R+,+81HaOmjIN +5+h^\C"c;%_"[WWqa3<XNsPM6i$O$F%d-m#]C8jtqbgPqcTWc,4<&dDGi-JH"n5!F +Y1l;cHf;a7/!oTKiie:W*s8mrNEZ:aN@fWoNtm,`EpJ0"p_EZnGW4RXIb"E,K35Y6 +rr?\iJ(b^Xrr<D55Cb\b(@NKpmoRB?$FKSr5.0F]kL&[E&SpAFT+L2uC45Qj&,7ST +lt>eqp5e\jm#U[lj48k@5M@XIL4Sj?`1IN')=&=cn[%L%3Rb\rr+`S:T0=<#pfCV& +nC?]Up!%tkpn`63A^?'PrGmB%G^mU,=,B*epm4LurY]rZ`832rIRs8*rr<50rK?ek +f\R.m(\VnEa.W5)`kNQZAoHGUj#?<nV>.7Q?cMsb$iFW,:[B1mZcAXrpnqQ[>:\#- +GV<+eQ@\!X>JZM-lP@2kf)?WR4tb<Z\;S.+VP/'NB6Ff;TC?&\fe2=C5O@dsO4m[q +*eWBXN#jbQRD33\N8M!`GT5UK`r?#Mr!31jHrKoTpc$)/LVp_7NLq&p*t*>n!9%k_ +ae3n-`>[OP_,e=X8&_)-l7huklU(,2Z)UUYh,dDB/u?2[!!Ni1^OA+jc_%U@r"FGN +489V-i#N3EJ%)nJIQltaLW3,GIg8.hIaS!V_`]+"Do8+R`&='3*s+V7Gk^/EMY5<j +9mg+(T*rQr%uL.T`D7!=)rR'6Zk!rIj2\`BmtE":4A4VHK>=,K>]9Ba+2nbFf:Nel +*.HI"%uu#lB<Q\)n9Set(&2qAQX'<LWqcSoj6r'.-@@]G4n-5(Ig3:[!/GXn?h%LB +^E.WXi:k/Q7n<>IiL^3eh\&l')BH<+MuNbb=o9i@#DTm&XDc;^!!S\eJA:*pg$-6g +I!t"H(&VS3n&MVcIL#XPi'5nIp9+1En]1VEJ*arKNA&?@LE88H$fGrT?6T*knA!#Y +$oHiQkaj!g(PD]7GRqXi+o_Oq6WCD^StDoepek,CCVBKbrr<3trJ:IRB\seW./8/Y +KB&*a*l%72^fpKNO+A4(X74a#pOW5Qn\`9$%amI"Y'`L`^((!8nb7RH0C[TQQ]HS2 +fm'7:+,g.!$G6])c%"`._AuuSnZR`I-?qblRf<B*Y93#C)XfQ0g["QRd=5hfrrC_I +f>W2%n[HPG&,uXHZ2XfWn%sP[rW<"Ipt;G?,Q@atbB9dMnB_(MYJ`)Hrr<ITEU_T< +0Dm,>>"/GkM/@b$F*PI8^LQaqE;VuO5/H0(b<HPCHr]^?rX3H;-/8>&rM"[:q_`b2 +d!U!+K>G=/2smsAS+^i?*;h>^hr4$SrrBEMMLT+H#5Ffepk`GK:ZE5(iGS^D/cDR= +%gDa'7"k?dfCjF`Zf8)VD[+q]hC-eE83Au1C*"\M:P,5.>>Np)j5</gmGa2\=5t?S +i*VEa1B!WU@Xj?oZu^I2iEsV)!"f&5rLA76qe,[__]"<J]+9mn(pk@mGlId&DsYT= +GgjVMgrI63^U-MMT*jj;pbqG)nOBd`:Z:,2:[p'6l[Kkgi2n*-T>Q]qIb7_o$m3O) +++/M6)u'[3K%@XYl$`a05OnaAi,tC*&)$.(G-1;\`Ld8qBDZ'2J)K4Brr?ZWLE8Ue +dB%Hi!"Sd^8_3fbH/]nOGe[Cfn7JNUMiSpd4q>-o5D6f@>!IXh&p,5j-iX1(4Afsg +HiqfHD'+F\(]GiXU=8gk5I?3^bt$aR56LocAo@d#d_?E5IMV^6a+(C<L[]rVLVcgi +pj:"kplG-(nOLR%J=d)>IgbKWhC84mi\-2c[@Y"g]a)9$paugL'5GkP[uS0RlbBeZ +^YmEAr&sgHn>G=?ZR<[eg#Mdsphf#MF5TR.rr=/*h[;UbHu&LknGC8`#.utd#NHoC +:D3Rkf!IXL\&=>\Sepq+K=!d/?I2K(opm;]>gfO_qVV!lrO'??rm\N7`>o'=pPeeB +Y8Vd$fDI_h^PgtX^*A"Ypoj0?is3J-8&ejOZ14`DmQ>.OF5s0MIN3`5cnl7PpgX1Z +Ig#PI;"0,Z(\*.o>23F&ehp*m%r9O+^J',m9E+uTJ'[:b1ONg5Xun`A5Brp>hr;D$ +^+OYWi<@e`CT6TK[.^!J4ZgYj!W3J1c`$,cHf=u)MYB:?nJ@#\.Jt45Ho9lB(K's[ +j0+NY'ttH.2tg&BHf3O$PK,np#D]r\F_TgVrU7V<&3Mu5Y7(,D`r686:4qR["o-8, +dJR#'%i>"B=O[18iI;Se?c5!R^9=2SPJ__GO`brFl^A<oUu#9iH9SU2?/95kStC.D +HY%1[[.-cGP2EZR\T)in!!OnOMtDiZ?<A?%ZqX"/"0&r<gIjKl[Jp416+3BP1h6%X +;NA`b<Z>^_eJI1WK3qV_#d#2hrr<\`7?>@$$,8lAU5C@nMC>3V.&)SIU5C@nMC>3V +.&)SIU5C@n^0LWH^Z<PPC_-D#ce8L$!*)7AC]'RtDuTe^LP^NI1OoE)V%6qh5PuH8 +rr==rp4NAFZ#B>crrCOJ!::S0UL++P,kr&3Du:<=p=\%]-$Zc5*WaV?HpOpifm'./ +qQ?%@SM@;-rKr(B?P$Q.QC]>PoH^`&=7H4iKR?)F)pL*"FeMBJV"dS`ethf=Mep$\ +J$+"ueUQMMrY,6urr?mH"FLZ_Zi.9q#CINdOo$R+f"VCg!,Silr[cX58`4NC4p\Ul +TDL]br]QW^?PWP^7I_V]cFr4u!T*$$:W8k`?IM"onMfF1Lif1p_RJ7OnNaGM:PjH` +WHg.R4rnlhic"4(q[!2QrrBEMr"Nr?1tR+hqZL]l[QVPM(\gMik25+kL-0b?O4lPN +P^a#&dIi.P^Y9RbQEB*dHf9FSn>G9$MRAKWV>d`tpm4e([9j>,KmXn^i%GZ:T2kB! +L7b]:m!\@Np9+28qa>[Mp5K"OJmJ0G9DGF^C&"Isdag9?g5GZ.phTDdp9+0dif=[e +;#%BV:P-:jIatkK[3rjrDhcsVL&>ZSUZ/J84qQuq?NbMr8)WgCCZ:mX?69aO?O1n; +;Ug.fd/O+(N'FT@RIMN<HJcX_paI$GiNN#4^cE#('UQY-47URV_uB^k^)6gZIqTo4 +Ic"H3rLlCrprcn?X?UNLDt\_OI[0Gein(uj-X?G!08$t_lT_`<O/Kko"nD9"X5L$_ +A$)V`mli`2!.oUr%IsGu_QW_^YCsVErnfs0:QLCi1]IH,@d"jnpf6gdcf58siSe\q +Ia&rHIK\rnZi6H`931QVm1]@WGc18X^"%L9nS>u;YO)8hJ+4aRpn?ZmN?/)hGDGk[ +!ViN"T7[b]pg.fM7f$?_U]$[sAoHBZrX!*)"9&V3GPb]q_V_\unON:KgO*H,2Z,/r +2o7e2*X;WEKHKoF]D2&urrBn^q_?4i7I,TD5A`Ul4Dgqiccu7.EV?"@BcdsO+P_C/ +ll*?eFR1+(ch:Z>)uEG/G5.X*6@:Qg!.p%+c6!-NC%6]P$/b9frX#:g[nQR`UNg#' +B8.A`8&R%tpp\9>O,H1Dfm&#?L\M%Qr'0tai9aU`T,2A;*I#K6f>Oit?0V>-]8D(= +kk@7BnNGa6nRep'!!J5YR?-^RqfdM7(OtpRDh6Iap;6S6rr<22pa(:I.ut'_)F+!s +J+2c"LY<i$2(oVKG^<`N-85-dJ)P8E!5aN][]=nPc)u:erY8f6-]2_X2/aK7!5]n[ +?6@Po!8E"s1]IH,;#]$1!'b6@K_f&6^LKL]X5]K54.tjsrr<WqBn(gt5O@X.Hs`Ma +qB*[lIu`,!a2Cac>GdD>08MXun&=aGQQ@RM[GfAO`;3f6iu!.Gj&#_)n@shP8+D(` +r[n)]5N&(S%6ncl')Yu/M=JL_$D72EGXLId`:)>?_Z'V@-]B^m*IK<`Zk!:Q_r5h, +m2,/`(W=Q[_EKUNZ2A+8C&\0Gd(Tg2Mr?mVHl8r/o)A^3ebh#ZT8?5#]fsb0^A)WB +pRek^r"J,aB)STViU<CSrr@Y4Hk,psrr<Qsrr@chqfgaF4:U9\ph0'j@';Nd9C6&( +a$1&`_T2ZVK_PH\09*^'kC:@[rr@b&rr<>VMLNtQ!!u0n,5Sd6^$sME')dm*"THs/ +)8BjG%Xuftn4UM;l$jEI!,Xq7IbIuFVu'>?b?k^-1B,h]$2dZ[/SZ@CHqEg/_9^iD +_tKo$CMiRq61DXK4qrBEnRo]Z6L3Qi\Z#EHeua<uWGBE2*in61&aEP\H2DH-aM5"S +m6CJWpf$n65IAK/'B<3bVhG'MArV02?h%L?^Y<P^YJX`%-f@Sc^5;AQ`?,ie:P_1K +n3?h-(VhV(iVrnsY6SgV*;J:Ic$REUQ\rF]Vqq,/ho-L&Vhb1#F5^IAl8lu1+1*^! +'-$XWq![`;=8ek@8cJbo'`F91*.H=C#!<.f&9p/-"S'OM:PQRfn1V[$pa:Rh0kZ;b +mi;6qZg\L@3.GXFHjB1K^jd#FH/RS<R_OWj$1Z.8=8r7-"2jt1Zf'"K&GX^DpiS<H +hH0DNphSep#_0Z`^'FR!rr@Y8rlSOfrL:R[UOGB)[(\0j:[luS56:!)0_jPp^Z'b1 +Aai(*rr<N+IrjRuN&i'JA+8BkF*J52*^'4CidVs4SeCQ(^_sdO^(Kj9NIDo6?9W4= +8^opMkhd/':&b3F62V2A4p:l3T<mF*J)IBBZ\O*Z(].,0bocFM^O\VVcDU17iLbh: +IH#bO8cJeKR!oN/#k2bn^(4Q6^U9EFh(.GbKB"O6i-qA>nI*-=5LFp:l5E-ci9uKM +l!?TY[^)LR_]H5UBDr3[q]br*rMfesCTlHP&)5;$;Z351rrCbKi&C&6n4Ub^-g]lg +lG!aEHuK#b%6<qD`P2hVnMfiBVYIdsd.i[kgZ(Nr.N:SUpugX\M6pWDrr<GAAcDc* +>2Q\GJi/UKT+H-YFmfOl=BY-B,l[jr7K1NZr%Ro^n/;i4.Q>01)rSd(2t.dc$*!De +p;aMi]Ii&Whhg(Mr&XGed!.KCnMfh/j0\cZFFOXF!WN.RMr@H_]%gUBd=*Nl0)_Z^ +i=,5eKfjdb>O'q.S"s?bHUIOsO8(*Cm*08<YI4B1_+6`p>5aBY$cRVq!!TdDqaH9k +U&-"]rrD",rmUh6m_$l+C\E8En+]2]D>*u0N7.&NTC@V\VsPQ5A,cO^IocFohDZJh +rL*%;i2:d+n58MdgJd$QWNuJTYQ"T39AbAG*[L535@b[>m02LANnU,DbHH_BHq7VN +rr?\]_@>OD$X`Za?N[4Z5N*+B::YB(\GlQf+4[`V:WI`Fi/iEjFKY[l$2>4Ln5\B! +_TUjZm!\AerX,FiCZ%[AWI-MkZ[?-/HnYL""WdAVnb9h*0#.2;nMl!K<aH4)p:]rb +k3Ks3e2L\DpbD8R:[p7`$[cril?ZflU_im2K_s(%J&*R`A+,)B0td%jQhsp8_(P^F +lFNn[dJFs^$2B9oqgEq757N*Wh*8gV-iJl#"RA:Yh8bSj#t8e0>l5GLpa`$88FU!' +98ua,=+I^%^PSD5[u()"pkJJ>IjUH>a3f8pqO.4KGYe$Gps]+YrkO#l<T!2Cn2nIa +p&tBqL;*Ot_jc]"GT0Z_!/6L6!"Bb6O&5UQWVrm:o))&+^Yo1m!/5@k!"-K\g?nn? +S)Ji4Ir0\P0B/NTj8KY:pdTCC7Lt-a,Q@`Hq#-Vp)ufoS3e(T\fUC"_$:4/@Hq=8; +Jp2[p^*!<Z^(Ym7euUtB4;"]C:\\kucf<p%TqQj8p`10k3urZFn/\Zai\-3R&D+;8 +%=E<f:P`H0qu_B,n`#HG57;_&_=O9#J)Hq@ZT#\Gg7S(frN?&err<3Fp`8@DM*D;2 +CL?icVsTc>a$d>>n2Gt^&b-[sJ,Bu_iRE>c]MYAM4O\5_3V]X]p`\Q[K,P99]h7X* +ShKUea$1.1P?77/`(BVD'R465:UmXug&!4J_*1(qDqRAS*A-ugpaHH`mhgPlW5%<L +he`0YLOm)arr<G-rYmZm(2roEg:YalHM@=eTD2A-f\SO`J"-H<$3(#',sUmMpo!L$ +n;%#D@qsoS^$!H)fVA./ps]6C"YKL@rr<3<]D]'#AM8Xa/GL=IcNSR-pK.,6g:+tL +g-ajiLV=Jt&)n+S2*ZeE)nn5Pqd!sO#(0k;583<G*V$RpnOHHCp.!JZ-gP+>^)cKG +Zb=DtI65<;&_4:V5%s^]rr<1urK"^+mt!\:^Lk4ermkHG9=OD&\+#UTc1j0Tn(J`# +?a:3[>Mo3(2hQK2qaG`kp5nclV>gORIqt$&p:9\TQ/\?]iBEI/pohR6-G$JW,N)il +`h*\='mRp\lrmc6b@K,1NshNlrr@XKrl<n&4C`ZsI4t<"rr@XVr,'u,:YDN:489KZ +%c@!VrK)IHrmeT0L\ssF&c;sdIqQBS+++]YhtFVU0luhc626T#^P%plT>[o6CR9Ma +2%+=9r%7FJlhLHR/&RFBpiGb.$3&,_"+D7Vrr<31pbc^a!!Nu5S-SD'rr<37pp^-+ +HtE*MnF'Q.]IZ9]DZtB\p8n$TYP^NX]j^EZp5b.*LU$V[iQVVE?75Mjiu]%]<nG?` +Z<rU!%0o2gLPpO#idFbD>)%U.Gh0pOr(6ng"Ru1*nMe9kY`H\?g,o9@Du2M0^'jmk +5A#u2+8.jKIM)?6_VYU2:ZKcP5I>@q@it&Y4PIfaj1j;lj-Gth+,aJ6oUgh%'KgN> +GhkJ<XEZhF\qZ3Ek\ot"_:IHEnID=opc$/Vp`prshi*$gr+5B\iue16GP5u_)<i74 +:B(=IPPtl^n?^,`"ScWgDZk;p^n1ZmAO"m-8,]0i?P\#fn/mO;CG1KIYJcK[?7".: +B8cci%:8A7*;G6WF2?]Ce,1`d*r>'8HL9"K1d2lWM"hM>5M;[e#OT;+!!Nr4ZECrZ +:P%6qrO>)NQbM5Dk2XCO#4V*Z.NeFrc\-cZrY=^JC@Q\D&iF%k9L%J_rr@^r`nKh[ +!1i(u#CjT2rr<<`MKHIkmD&!^>>`lDJ)NeF_%cO&$i#Xr+7Q%Ti0SasK(Vib9(>u@ +4<a_)$H)R:ZLGT'ifAjuJ&OPq6h)O[Zk)LO[&^le^m\-qgSS[g`hdArU#Ctnr)9o. +\?WJ+/W.%:+o$iNp9Zq6n@ue([su3n!83k8;",9OoZmeLr#Yg.2(s[`!"6Lg:M\n' +pe?-!WVeufpus/dFaH?*T)msHD;a:cpg!T4i"*\CK:fM0_g`5F1;345_Z'ViVr?-3 +&CnEN&%m9NpqutFr"8E^Sabs\^Df?AG[J=Z'E8&cL3YB>^,5V/=OlgsH`^Yq4s][e +M`bk9Y7u5j<UjFT4F,q9(&3"4LQ)3\Hp[AC[B=c7`ni](IO+^Ca5.ri]N2a!ef3Qi +NF*<G,OllT$=WfI8`2fNG^m`e2Nt?0[eb^0KmZmuGah=SFcAKVDuTfhNp+'^V8O9J +MtLk602o4,/EG7P8LEqfLTVu:Z<]*G`-s/Fd@Wnt8*_00;+20r72/Tf'N%:5;+20r +72/Tf'N%:5;+21#bFc6[r0^XRJc>^^\beXAlDO/hJ!1t6rrD,!nLsiqDuTh0U$MTR +rr@[\^Z:j_d!ta@rm1TEqLAI<lh@%30C1Rn.f=ed=Rk:f084K4IPcBjD)1k)4q[RT +nUKLU\#e67R=;tji]I/?KHCbQ"ZM:):._7::4EhG:0S*8[apU/D]EaEP(1BL^L9q7 +-%?OlnL_Lln,A,X5N1."Yc%F4^PO'-QJ],prr>GE+7SR!fMhd+?c8$'95iQHrrAc3 +O8*j!lm_r77'GT?r[#&@Sc8];%7g=WX7d8-!2<Qb,Q@`Vg\/qm!9f04rZ,#9$@gGR +plYRVM/E-&J+Q**rr<P/rImK"Q2F2Q!5u>EZbQ>[W;cjQ<IVfWoLf*.J)X[]g#)`> +e:5B9^L9q7-%?OlnL_Lln,A,X5N1."Yc%F4^PO'-QJ],prr>GE+7SR!fMhd+?c8$' +95iQHrrAc3O8*j!lm_r77'GT?r[#&@Sc8];%7g=WX7d8-!2<Qb,Q@`Vg\/qm!9f04 +rZ,#9$@gGRplYRVM/E-&J+Q**rr<P/rImK"Q2F2Q!5u>EZbQ>[W;cjQ<IVfWoLf*. +J)X[]g#)`>e:5B9^L9q7-%?OlnL_Lln,A,X5N1."Yc%F4^PO'-QJ],prr>GE5Q2[+ +q[%c"-hrW"!/YXkBn,bD;?$V+XSmgek`bp5rrC!HrNs?9,,kMALK8l@?h?qh,6%Z& +>Q3>`r=N"Y_lH"10DZso!"j_N/cPff0E-d-5N1.bYa>;$Iu(l7QN$rnQi@%R<?L_q +ce(niM*Jpcr(DOo9E,!d.Za@IBC$rE!(<I=0E*94mA9g_d1o1L3j\MmrrAchnJD3* +'&WE2rrD8?U])9:q[%c"-hrW"!/YXkBn,bD;?$V+XSmgek`bp5rrC!HrNs?9,,kMA +LK8l@?h?qh,6%Z&>Q3>`r=N"Y_lH"10DZso!"j_N/cPff0E-d-5N1.bYa>;$Iu(l7 +QN$rnQi@%R<?L_qce(niM*Jpcr(DOo9E,!d.Za@IBC$rE!(<I=0E*94mA9g_d1o1L +3j\MmrrAchnJD3*'&WE2rrD8?U])9:q[%c"-hrW"!/YXkBn,bD;?$V+XSmgek`bp5 +rrC!HrNs?9,-0?[$%BgBZIeI3ilQRnn>"rZ`gPjcV!9u]Q\p0!nN5ba`P#bKdrg@r +4^np1:[s(s099W4[$nWCd+W6$INFD@][SKtXKJ]P[<IJAWM57uU5C@nMC>3V.&)SI +U5C@nMC>3V.&)SIU5Jbfp>Z)PP:HO85O?udBFk&@r[k>EAF[_<N]nfi!3k#R!</3G +Ir@R@D7@BS"+3u@?1I(7`juADXsT:imspIs*C@d"o\-_@^&(/)Y9_mJR"XJEgWeAP +J!1r?ls,$3o)?\%!9>HPJFihHPEDX)_kH]6IoB0&luVY%%X\SVrYe7t5PC(+hm)(S +/0FY\1en9m.o.0Kr/B0LrJuC.Ndp!kd6R)Y.`4K-0]O4eAtXY,"5k:dnfqi*L6nfh +Ndp;K?c<faH^d3<&u3+%I;ode*P>kMu_=rr@d?p^@*%6:R+;!.rB)r\I(B1)I`5 +q@YWGr"`CanX;]$OM1er!76k(J)IWMrrA#*rr<I]n/)(WN?eH'"dU8.Arl^t07^h0 +,\\@q/?o-Sj"u8'+aaIn!21W.rrBlHL]7>lU]),=i%P$8)Fsc7/3ipS+7R?Y!Is<( +dQd5(@K->IB`A'e6MLrg!('/;rr@Yo&,n@Tr%])(?htBlO8KbqJ+8sliK1bTrrCE. +Ujq=._uB^qdJj1TL%#oXKT.5V!!SZ1r+;#bA1rJJoH\,mpaiZL!95nc_]aC2rrA`; +8H/\+AV^9c)5I0K$fE]:#oZ>5$S4O;bocGr?NGZ?8CB`l=^h=1`b>APL-kYCrr>AT +OoGE5b7FS.8,P+Y^gHpN1lqPM=F]k05N.^<!ri8IOedKbZGZ\BN7%Y*%#+0ere-7U +rr<4g+8f`3pgc%.^[rdc+8@CkrrD(9J&<FW"oeRr,=qh7_.AE%!#_R2)%5@Ur<XB4 +rX18AprsoMaBnstkXa,dn6'3"!6/^P!/<i8$i^2n7Zm]NK$+](1Iq?u(VjDS&i>[I +(0H(VRQobm^&n<7d#k,Ri3L9#!0#>*!"=SN(]K)I,Q@`IQ66$:O8*6=JA:_%Bcm+$ +Yl=^`1k3CdHj0NMTd*,.!5W6%rr<q3rYZ+%0Du1q8,`lI5PWJFnAnr;J*9GO5O_fa +rrC&Prr@d?p^@*%6:R+;!.rB)r\I(B1)I`5q@YWGr"`CanX;]$OM1er!76k(J)IWM +rrA#*rr<I]n/)(WN?eH'"dU8.Arl^t07^h0,\\@q/?o-Sj"u8'+aaIn!21W.rrBlH +L]7>lU]),=i%P$8)Fsc7/3ipS+7R?Y!Is<(dQd5(@K->IB`A'e6MLrg!('/;rr@Yo +&,n@Tr%])(?htBlO8KbqJ+8sliK1bTrrCE.Ujq=._uB^qdJj1TL%#oXKT.5V!!SZ1 +r+;#bA1rJJoH\,mpaiZL!95nc_]aC2rrA`;8H/\+AV^9c)5I0K$fE]:#oZ>5$S4O; +bocGr?NGZ?8CB`l=^h=1`b>APL-kYCrr>ATOoGE5b7FS.8,P+Y^gHpN1lqPM=F]k0 +5N.^<!ri8IOedKbZGZ\BN7%Y*%#+0ere-7Urr<4g+8f`3pgc%.^[rdc+8@CkrrD(9 +J&<FW"oeRr,=qh7_.AE%!#_R2)%5@Ur<XB4rX18AprsoMaBnstkXa,dn6'3"!6/^P +!/<i8$i^2n7Zm]NK$+](1Iq?u(VjDS&i>[I(0H(VRQobm^&n<7d#k,Ri3L9#!0#>* +!"=SN(]K)I,Q@`IQ66$:O8*6=JA:_%Bcm+$Yl=^`1k3CdHj0NMTd*,.!5W6%rr<q3 +rYZ+%0Du1q8,`lI5PWJFnAnr;J*9GO5O_farrC&Prr@d?p^@*%6:R+;!.rB)r\I(B +1)I`5q@YWGr"`CanX;]$OM1er!76k(J)IWMrrA#*rr<I]n/)(WN?eH'"dU8.Arl^t +07^h0,\\@q/?o-Sj"qk2$fE[V"DJbJdd03neMmIF#N.iF#oYc%$S6e'6G@gFY^^9" +KDlVaOFM#YCIdlCNGGKW+1"JXHi6.j4YFi&\%B$B?/hoCH)$L3glb)_Bd,UsOd%V_ +^`*4Q/cPejF5mcDT3Z(/!<3$k*l#>Epbg`iIN&45H]Bo@^Y.on:]/95!,2B49)bha +M7%eMREl,KqG^67&bh%chpD7BpZYM]Ol6/TSd/T!X#m1BnE]Q3<Ln\$cN&%cIh>]: +L3&pdp[pBbp%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY09X?dSfQ +KcAXqoKV;(p%A7opee_QIma2LDdQkurr@`lf@TXZiC<M5r=A[#]mYAtrY5U]je\6U +`T?4*2O9Yj96GB"F!<ngD/B>2ZeSe].9N9u(m3mF=W(*o<d%lN8GMZIe^aquq^?pG +&4,;/#K`[_q\FR$f(?X2hWF7p`S.`ekC>ZnD]YlmQL+HMn9EC;R<:H,2Jl73\$n-3 +.l:Vp)rlGD*'?mo!+DAs!<"<lrm<sghER(9$2t0W1\f,m!#,>Zr&=CiSgMfJ$U^XQ +`#g%F>JVbi9qc?0*@B<FdU0r([[+Lh\n#"<P:iUNT:Ya2lC)ME#ZgrSbP`?Og[k,7 +mJB]Yc(-nF_YEn-qB,K*#O>2XqgX(:aoR"GMtUsUc9?0-574W!T'bP*U]])MaJaUI +^]+:EU]1<Q9D^OfYK+tC"7jcr^Q>DKpAOrQZ&eZp?eM+8e)T?&F6ii+;fKe?RY:[t +J$NG[*A?d10QY:Tc\2VnrrA1o+2a69e8>)2rlKg)5AEF9Zg%Ji$h*%>-_L8T^Wnmi +J+2?&Z>][^([TiT!/L=-5Q:^@e:5AaI`]@sFC2@erZC$NrrDuK;?$V*p%p*9!7B2C +c(Fc\IM;_]o3Fla2qRDcrr>PXq_ir_rV01g!6oX.nD@TYDuC[mm+MC=L\Kalrr=q+ +J&?\`ahRIi?aFDE+5_bO!;tGGrr<N$q^2=CdF!EfTD3nb'E8((>t=fOfBf",!(TKT +2Lj]apeUnic#k,thd<[-r>c^@g&D&-pYe@:!&`8U_gR!U^[uVTZY07Zhh;"0r7=gG +!"JVu-N=(nd=0?4pj[`+rrDXr8,P<tm$n"T7n*;5J)UA$:]CF>FW^,1?a=VK*>e(q +rr@iKiBR>B2:R,nI=B;QoQ4HHBDs"1K`;&Mf2;PJ%JBTFrrCHoIa+M[Isr(2!:sJd +r&<6oS,WIY7JeumiViOIrrC;d+7S/pZ>][^([TiT!/L=-5Q:^@e:5AaI`]@sFC2@e +rZC$NrrDuK;?$V*p%p*9!7B2Cc(Fc\IM;_]o3Fla2qRDcrr>PXq_ir_rV01g!6oX. +nD@TYDuC[mm+MC=L\Kalrr=q+J&?\`ahRIi?aFDE+5_bO!;tGGrr<N$q^2=CdF!Ef +TD3nb'E8((>t=fOfBf",!(TKT2Lj]apeUnic#k,thd<[-r>c^@g&D&-pYe@:!&`8U +_gR!U^[uVTZY07Zhh;"0r7=gG!"JVu-N=(nd=0?4pj[`+rrDXr8,P<tm$n"T7n*;5 +J)UA$:]CF>FW^,1?a=VK*>e(qrr@iKiBR>B2:R,nI=B;QoQ4HHBDs"1K`;&Mf2;PJ +%JBTFrrCHoIa+M[Isr(2!:sJdr&<6oS,WIY7JeumiViOIrrC;d+7S/pZ>][^([TiT +!/L=-5Q:^@e:5AaI`]@sFC2@erZC$NrrDuK;?$V*p%p*9!7B2Cc(Fc\IM;_]o3Fla +2qRDcrr>PXq_ir_rV01g!6oX.nD@TYDuC[mm+MC=L\Kalrr=q+J&?\`ahRIi?aFDE ++5_bO!;tGGrr<N$q^2=CdF!EfTD3nb'E8((>t=fOfBf",!(TKT2Lj]apeUnic#k,t +hd<[-r>c^@g&D&-pYe@:!&`8U_gR!U^[uVTZY07Zhh;"0r7=gG!"JVu-N=(nd=0?4 +pj[`+rrDXr8,P<tm$n"T7n*;5J)UA$:]CF>FW^,1?a=VK*>e(qrr@iKiBR>B2:R,n +I=B;QoQ4HHBDs"1K`;&Mf2;PJ%JBTFrrCHoIa+M[Isr(2!:sJdr&<6oS,WIY7Jeum +iViOIrrC;d+7S/pZ>][^([TiT!/L=-5Q:^@e:5AaI`]@sFC2@erZC$NrrDuK;?$V* +p%p*9!7B2Cc(Fc\IM;_]o3Fla2qRDcrr>PXq_ir_rV01g!6oX.nD@TYDuC[mm+MC= +L\Kalrr=q+J&?\`ahRIi?aFDE+5_bO!;tGGrr<N$q^2=CdF!EfTD3nb'E8((>t=fO +fBf",!(TKT2Lj]apeUnic#k,thd<[-r>c^@g&D&-pYe@:!&`8U_gR!U^[uVTZY07Z +hh;"0r7=gG!"JVu-N=(nd=0?4pj[`+rrDXr8,P<tm$n"T7n*;5J)UA$:]CF>FW^,1 +?a=VK*>e(qrr@iKiBR>B2:R,nI=B;QoQ4HHBDs"1K`;&Mf2;PJ%JBTFrrCHoIa+M[ +Isr(2!:sJdr&<6oS,WIY7JeumiViOIrrC;d+7S/pZ>][^([TiT!/L=-5Q:^@e:5Aa +I`]@sFC2@erZC$NrrDuK;?$V*p%p*9!7B2Cc(Fc\IM;_]o3Fla2qRDcrr>PXq_ir_ +rV01g!6oX.nD@TYDuC[mm+MC=L\Kalrr=q+J&?\`ahRIi?aFDE+5_bor7;k0^\t^X +AdqAi`5mFD&(pqsJ+/?;Dl%-9cc492hqi/*-,.#MX_WB&r).Wmr%/,9o$+/9b`Uf< +8/BY%.tkEpXh61X[f*I\ApnmsAc8\%9cNf%rJn;Q='PW`4F>uq)c,r^Nr0+\/+Hu( +0)csMg-OYh"'Spo.<f@]rAs-93_kX<f]kN;V.HIACS]b-F;EL+bC"'Ir[mXZUFW,) +pWSYn1:":R<L&+YQ%9&L80Ht5<6=1()-bk9@g@k`aD@u8(A<mU=%R6/qnK^rA.clQ +J-f3YJ29ds!"%PArrD7rf0Ab7F8l6\.1_HOA:"$C><cC_jSo3?.6lcOnW3VWg=Q<4 +/jK-B5N+TlJa;<$B`A(erQ"p<(J4W,$,;E`k[i=pn32@$/s#d%r*fU*6S>_F!.jbA +!:b/\_JeHFqAFFJr"#G"!/mWO'7UjKd*&Nj!8r8)!;p+En=03jre=]krr</arrD'B +rr@`4`WN,qFcl\(Dtb@O>qc+'4u*';Tpo6c!5XB0rr<j*r$NO>4a]o)+8^PtrrD'C +5N+QkL]%Xmci+0qrrC$crr<A?N'HN;epm0QJ,V']peCZprr>D=Zi:#VoD\g\FFV/G +it(u(FeARBTDnnLU](pV(k9oUXT&:YI/a3E>p&R*pr!)c?"a0;"6][aiopCBK\sck +oJ5_sp`Da#!#J0'-QXr^4A2c:qgZ-U!936miApFhr=%E3rX:DDppr]K3Ur1/%,0>4 +$nad>\j,.6nW3VWg=Q<4/jK-B5N+TlJa;<$B`A(erQ"p<(J4W,$,;E`k[i=pn32@$ +/s#d%r*fU*6S>_F!.jbA!:b/\_JeHFqAFFJr"#G"!/mWO'9<t?SgDrXrP.-;nT;P8 +SG:/(L2C`+!!X5]r*:E6S9VjBnLh1umJc/G!/mZQ!/07L+7q>S5PQ<brr<j*r$NO> +*BZurO8FF:rrD'CJ)NEXO8TL@fDZ125PVfSiAg@Ylf)2`rrCgPa8Z,U8cJbs^PkD@ +A_)A#5(EPaO8)HI@K-<p0!kQXJb/mAnT98tDrVB))0MSL&"ik%Mkg7bIi*[^bH1^D +i-bP)r*:E6S+so#L0\Hl!!DEa!935B!/07L+7q>S5PQ<brr<j*r$MCsrrAaZ=oSK; +q>UHi\j,.4nV@&O]$L?\ci4!adJj1Q^PkD@A_)A#5(EPaO8)HI@K-<p0!kQXJb/mA +nT98tDrVB))0MSL&"ik%Mkg7bIi*[^bH1^Di-bP)r*:E6S+so#L0\Hl!!DEa!935B +!/0CP+7q>S5PQ=?rrC$drrBoWiue+8[BKKF>^u9cJ&63c&,I0OYP\p@rrD'C5N+Qk +L]%Xmci+0qrrC$crr<A?N'HN;epm0QJ,V']peCZprr>D=Zi:#VoD\g\FFV/Git(u( +FeARBTDnnLU](pV(k9oUXT&:YI/a3E>p&R*pr!)c?"a0;"6][aiopCBK\sckoJ5_s +p`Da#!#J0'-N5_!!25`^rrBk1rrDi*5N+QkL]%Xmci+0qrrC$crr<A?N'HN;epm0Q +J,V']peCZprr>D=Zi:#VoD\g\FFV/Git(u(FeARBTDnnLU](pV(k9oUXT&:YI/a3E +>p&R+Gf0N8?"a0;"7ADK\mP5-Ht30V6T2@P!.tZ?r[s9,:-;tb!(/ZPrr@WE+8fCl +r$MCsrrAaZ=oSK;q>UHi\j,.4nV@&O]$L?\ci4!adJj1Q^PkD@A_)A#5(EPaO8)HI +@K-<p0!kQXJb/mAnT98tDrVB))0MSL&"ik%Mkg7bIi*[^bH1^Di-bP)r*:E6S+so# +L0\Hl!!DEa!935B!/07L+7q>S5PQ<brr<j*r$MCsrrAaZ=oSK;q>UHi\j,.4nV@&O +]$L?\ci4!adJj1Q^PkD@A_)A#5(EPaOSI_Pj:H]1X:Ri,2q*jSl3K%p%rcbNc;-RS +1J'aJN@`Mdkp1^\gAl,$`_"2u^JiC,s4I~> +%%Trailer +%%EOF diff --git a/docs/figs/acm_overview.eps b/docs/figs/acm_overview.eps new file mode 100644 index 0000000..e52bcf3 --- /dev/null +++ b/docs/figs/acm_overview.eps @@ -0,0 +1,1463 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%BoundingBox: 0 0 1106 631 +% +% created by bmeps 1.2.6a (SCCS=1.78) +% +/pstr + 1106 string +def +/inputf + currentfile + /ASCII85Decode filter + /RunLengthDecode filter +def +gsave +0 631 translate +1106 631 scale +1106 631 8 [1106 0 0 -631 0 0] +{ inputf pstr readstring pop } +image +K)^H&K)^H&K)^H&VuHbSm(WPPK)^H&K)^H&K)^H&K)^H&_Z']3LFD?d!FYAJs-s#g +mt/<E^B!_Ss+:9&s+:99rrL.gjT#<(hh(m!rrLFWJcM;@!=6Gls+:9&s+:9<rrMk= +rW!!JGdm%S"J83b!'#T1Sc8\>JcM5>!.TV#K)^H&K)_5<"F'nH#`%UC")%Z7^OlKW +rr[`N!2kF`T>(F-!.TV#K)^H&K)_8="NLKB.+dV]"7H3iO+RD'rr[`N!8iD.TDnrm +!.TV#K)^H&K)_;>"SX;E#g_T3![%JFK)_GB"+L:Nhh(m#rrN0#ItI]Ps+:9&s-iri +NrT08j8T3X!#YH^s.B;nIfKK+K)_JC!WW4MK)^H&K)^H&Rf<FK!#X_q"HNN_8FM01 +T`5+D!!(o.s.B;m!!%M#s+:9&s+:9>rrN0#B]8sk5lL``3/'U!BSY35rr[`N!8iD. +TDnrm!.TV#K)^H&K)_>?"6TXaTAfeNmmr$1rVut,n"]mbIfKK+K)_JC!WW4MK)^H& +K)^H&S,WTJ!!'LcrrK<QKKoMSrVus9\"s$+IfKK+K)_JC!WW4MK)^H&K)^H&S,WTJ +!!'ccs5*bVcWL/H!-k@?"+L:Nhh(m#rrN0#ItI]Ps.TGnkD+Y=\UOX@n&YM2hZ*Yk +K)aX+"Lf3J!5GE2"+L:Nhh(m#rrN0#ItI]Ps.TGm0S0;6!!$-Brr_-Y!5F-cg]%E> +&-*jIVuHjK!!(o.s.B;m!!%Mbrrqk`Im?71JcM_L"UI@YBRe@Ks+16Z!!#:*rr_-Y +!5F-cgA_8I!!$u9rr[`N!8iD.TDnrm!.Vod!N/bG!1X#j+E6MdrrN0#ItI^4rrPFc +5fis-hZ*YkK)aO("#p8lpSe)oIfKK+K)_JC!WW4M`W#tJ0`:tR#XCY"Jq'\J!a%]1 +r;Zm)E:;$N!WW4MK)`I_!^H`NaSuA"!!'ccs4RDSQN.$+W;csL!!(oprrg)j@q1b, +rrR9gA,H9+g&B4PrrRm:Z1@nr^P/rgrrN0#=SK'#ldGe6ha%/@!si#,pO`F=rrUf% +./j2I&<G*9!WW4MK)`I_!^H`NaSuA"!!'ccs4RDShZ*YSW;csL!!(pFrrV>:PhZ3= +bWPe'!0mE]!6hqP!3uM'"jI#HYrj?2!!4H/UZh^Yk1T_5!0mH_!87#E"Qh!1!5JC1 +!WW3npQbg@e,KWm(]XOIL\HE#S,WN85lL`aB_TjQ!WW4MK)`I_!^H`NaSuA"!!'cc +s4RDSpAb2cW;csL!!(pKrr@cN,m"&HUYYqNfd6Lq!+Z$."MZ5_-/#j/!'L5\!nmV, +pAb4kk3N$LKd?^]rr2tPrVurBo`"p8rVuqPqYpTs!)*'P!9M`1"fDV+!-%CYs-N`h +^E<LV5k!)"!WW4MK)`I_!^H`NaSuA"!!'ccs4I>Q!!&Xirr[`N!8mMM!O3sH!!#mP +rr>pp!!=Mn4PB`6!'L5\!+WV?!5JL5"&]*ubk;#;--Y`U!%%UE"/GnrL&M&Pbk1o8 +Pl:X_L&1fN!!#[dQ2p$srrfM*!!o3Ks-3Neml1:I3;:i#!WW4MK)`I_!^H`NaSuA" +!!'ccs4I>Q!!&Xirr[`N!8mPN!2K8g!'K-<!87;N!JT\5rr>pq!!%,Prr?R.!!C"9 +s31HB"Asl,;'l/@!'KWJ!%%UE"2Fm9L&M&Pbk1o8L&M&PU\aul!!#[dQ2p$trrgXR +!"cnss-!BbpK.Cq=OI-M!!%M#s1A:45QF'jrr_-Y!5HSS!d$Q0p\t<"4JVfR!d$Q" +qu6bn,s9lYrr[?h4QaEY!WW4mW;csL!!(pOrrAhl!!4H/-2mlE4Pp)<Pl:X_A,6-, +fhj%m\c2aX!!#.\rrC:B!!%`Orr@cO!!&e]rr]Mg-$9.d!/:CP!6k*8!/:CP!6k<> +!WW3npQbg@fDc!:+92DNK)^r4"SY.]!/K#&!WW4MK)`I_!^H`NaSuA"!!'dTrrBh5 +!!%`KrrAhn!!(^Nrr[rT!%%OB"!mpI;>gFr,ldq!^]+?8!2$4i"+L:Nhtd9P^\n-< +-*dLMo-FA:;;V<QA,ZH.bc^sGL&M&PPlC[_bl.SBL&CrNL&M&P;<IlYL&M&Pbk1o8 +L&M&Pbkh>@!!#[dQ2p$urr[3?!0;a3NrK7N+92]1e,KIL!.TV#]Dhpt!'nX*"5a(Y +^W?ETg&1mNU\k&kZ2O_'-2dcC-2mlEU](2mbl.SB-2dcF,ldq!^]+?8!2$4i"+L:N +htm?Ro-OA9!@>tgrr]#B4PB6(!p533rVlo\g%bRL49,@-XoAJL!!">DrrC:B!!%`N +rrC:B!!#.Drr@cP!!(7<rrhKHs8Psq!!(7>rrN0#9D=_Pjk0S8cN!rAK)^f0"4$rI +:t,FG!!%M#s1A:45QEA1YlO+<rr_-Y!5HSS!2KMn!@?FtrrM7.rVur5rVllNr;Zh- +rVlkOr;ZiArVlsG!!%_frrN0#T;_blIfKK+qYpP*rVusFbk(i<k*0@<A#&r$$7,ZP +U]6#o,lf6FrVm8fb`jCR!$u_L4AiB^rr^"u4O!X$"jE3bKqmf/rr_CG-"HlQ"6M]l +;?$S&Yu-bcs1`Y<,pe9BrrM8Hr]C6ZPih]>!2KMn!+Z!-!6kEB!/:@N!5JL5!'L8\ +"6M]lA,Q?/juaqerVlqQ4Al(W!/:CP!6k??!M_dU,m@..!!">-qYpTs!)*'P!9Ml5 +"&Jstc[u1TrrZ't!700p!WW4MK)`I_!^H`4pm(pAdf0F,!!'dRrrAhn!!#.[rr>1\ +!!&8]rrA;^!!#.[rr>1[!!&8^rrXPI!/82f!WW4mW;csL!!(pRrrBh4!!&ecrrI3f +qu?a[bl7VBbl.SC4AktU"GQmUUF#U6!87AO"!mpI4T#-ZKdHWs!@<I!rr@0?!!(7A +rr@cP!!gaJjs:!-PYq;X!2KMm!JMis!!+Bfp\t@Y!!">-rVllArVuqPrVlo\4T5<\ +PlC[_FT)7?bl.PAL&M&Sbl@^rrVuq?rr2tPrVurBr;QaZpAb1>q>UKr!)*'P!9Mo6 +"4mMQBS-8Arr_-a!*IbN!WW4MK)`I_!^H`4pm(pAdf0F,!!'dQrr?R.!!>@`s)e5? +!+Yp+!+Ys-!%%UD!%%RD!/:CO"!mpIKtmWh!!&Xirr[`N!8m\R!)`^q!+YX#!'L&W +"=<[>s310:"""!I4S8[Sg&D!R,ldokrVloB-27HB-/&;\rVurBrVljprW!#Ds310: +!'L8\!+Ya'!/:7K!2KMn!+Ys,!6kEB"_5d#P[c$=!!">Drr>1\!!(7Arr>pq!!CIF +s31HB!'L8\!/:CP!6kEA!'KrT!%%I@!WW3npQbg@g&D/(!!(>ss,$aX:]Ldqec,[N +!.TV#]Dhpt!%.K,!9MZ/"5a(Y^Vp-P4T5<]A$Q"5!'L,X!'L/Z"53_S^\e'34T59^ +,ldq!^]+?8!2$4i"+L:Nhu*KSg&:sP-0Fk"!+Yp,!%%RD"(VB2bk_;?;*=gX!mL[u +q>gL@!Bc)7rrXPI!'L8\!SJdt!!4H/4T,6]L&WIu!!(^Nrr>1\!!:CEbkhA@,s;/, +"$HV`Z2FY),s4:9rVupEq>URD!!">-r;Qc@o`+usrVljDrVurOrVlj[rW!&Es8U=B +!!%-?rr@cP!!(7Brr?R.!!FTXKnWD&!!">@rrN0#9D=_Pjk9Y8-ibBAs+p[WT)\kh +ec,[N!.TV#]Dhpt!%.K,!9MZ/"5a(Y^Vp-Qo-OA9"=4$J--Z5c!%%OC"/GnrA,H<, +-2miG,ldq!^]+?8!2$4i"+L:Nhu*KSFT)7?A+]d$^\n-7A(gmHrW!$Hs8U=?!!8qq +bl.SKU]:A/PftER!'L&V"!mpI4T>?\;>pOr4MUmq!0mH_"blt&!!">Crr>1\!!:CE +bkqGIFQWTL!!#.]s#g8\!0mK_!0mH_!%%LA!/:CP!+Yp+!6k-:!@>MZrrXPI!%%RC +!'L5\"3gfFbl.SBL&V)PL&M&Xbl@_*,ldokbl.PAPl:X_-2IQB!!#[dQ2p%#rr^jQ +!-j+qM#RSG#QRuWrrN0#ItI^4rrPFc-MWl,jjO/2hZ*Yk_uBb]-2[`CPktC[bkhAH +A,lQT!!";F!%%UD"!mpIbeO/Z!!&Xirr[`N!8m_S!'L5\!5J1+!+Z!.!)`^p!+Z!. +!l+cZqu?hQs8U=B!!%`NrrXPI!'L)W!6kEB"$HV`^]"354T,3ZL&M&UL&WG!!%%RC +!'L5\!mL\gr;Zhmrr3-J!!#mr^]"39A,lS(4T,6[A,H90o-FA:-1h$6!6k'8!2KPn +"!mpI-2dcC4T5<_bl@]srVuqPrr2tPrW!&Es8S>_!!&ekrrXPI!'L)W!WW3npQbg@ +gA_8a!!(&ks+gUU:]NK*rrN0#ItI^4rrPFc-MWl,jjO/2hZ*Yk_Z'V#r;Zh-q>UbT +!!"<-!!#.]^]"35L&M&Pbl7VE,ldqh^]+?8!2$4i"+L:Nhu*KV,ldokoD\e:rVurO +rVll4rW!"Rs31EA!'L8\!5JL5!/:@N"!mpI4Sf!Wbl.SE4TGFkrVuqnr;Qb=rW!,: +s!7XF-2dcI49,@-k5W[)!!$O-rro/D4PBaVrW!#)KdHWs!'L,X!+Z!.!/::L!6kEB +!)``L!FmGS!!ah's8OAF!%%RC#<Vtd-0G7-L&M&PL&V)PL&M&Vbl@]?!!">ArrXPI +!'L)W!WW3npQbg@gA_8I!!)K;rrJ@Ko`#!-L$"Qu!p53$qYpWgKp;E5"5.0]k0O&/ +BE01*rrN0#IuaO0ft[N?!L2:&rr^"u-)8lf!^H`4pm(pAdf0F,!!'dNrr>1[!!$O) +rt.?n!'Gr8!%%YT!!">:,ldq0rr3'H!!(6XrrN0#T;_blIfKK+r;QjF!!">:rrXPI +!%%RC!6kEB!epZur;Zh^rr2tPrVuqPr;QjF!!#.WrrC:B!!YRcs#^8]-2[]B4T5<a +g&E>u!%%RC#!;kc-3+"hrVuqPq#:=VrVupEqu?a[U\aunjs:!--2IQ@bl.SBL&CrO +^HDJq##YF#,ldokr;Qj]!!">Err@cP!!%`Prr@cP!!^[Is#^8]4So'[,ldp-q>UKr +!)*'P!9Mr7!e11UeGfYK!!#.Dp\t>j!!"=ErrSrAFRoG3L&<4u!!&ekrrXPI!+Ym* +"$?P`4O=$/IfKK!rrN0#J+EU>g&B1sKfjIRrr@cL!!,3Wm/I/M!!">BrrJ@Zg&D+= +!%.K,!9MZ/"5a(Y^Vg'O;>U=nU\aun,ldpebl.SNUEom?A,eXk!+Z'/bl.SBbeO/Z +!!&Xirr[`N!8m_S"!mpI-2@KCg!'Krbl.SB4T,3Zbl.SDL&[D:!!+D.rr2tPrVuqP +r;QjF!!#.WrrC:B!!YRcs!7XF-2[]K,ldoks8OAF!%%RC#!;kc-3+"hrVuqPq#:=V +q>^OYU\OihA,ZH.U\Xoibl.SBL&1fU,ldokk5Q_-!%%RC"$?P`-3!oEL&M&PL&V)P +L&M&Vbl@]*!!#.XrrXPI!'L)W!WW3npQbg@gA_4f!$Kek!%%UE!@@@5rrAhm!!(6D +rrgOl!!&eerrKANq#CClr;QbNrVupEqu6Ykr;Zi4d/O37!!)35rrN0#J+WaAUHJAQ +!847L!%%C?!)`.`"$?P`-2dcF49,@DgA_4>!%.K,!9MZ/"5a(Y^Vp-PA,ZH34='t- +-0G.*!2KMn"i('`!!#.\!!p@>@fQKTs8U=B!!(6XrrN0#T;_blIfKK+r;QaCrVuqn +qu7#p@fQL+s8OAF!'L2Z!5JL5!gN_=rVupErVlkOrVuqPr;QjF!!#.WrrC:B!!YRc +s!7XF-2dcC^]"3;-3+!-!!">CrrtRc!%%[Fbl.SBL%tZJ4T,6],uMGOrrBh5!!#.V +rrC:B!!%`Lrr>1\!!UUH49,@-r;Qj]!!">Err@cP!!%`Prr@cP!!^[Is#^8]-2RWD +,ldp-q>UKr!)*'P!9Mr7!al!neGfMjr;Zg[qYpQ1r;Zg[WrE&]rVuq?pAY,fpAb1U +r;QaCr;Zhmr;Qb,r;Zh>d/O3?!!(p-rrN0#J+`gA;>:+kL!K]#ffUR&h>[I$pAb1U +mf*AO!!">DrrC:A!!(^,rrPFc-MWl,jjO/2hZ*Yk`W#pJrW!!Gk-=mc!'L2Z##P@# +-3+!Bqu@0Ps8S;`!%$e-bQ%Vhk.got!!&Xirr[`N!8m_S!-J2?!@>tgrrJl@rW!0L +s8OAF!%$e+rr>pq!!:jRL&M&P4T59[^]"35A,Q?/,ldp-q>UH=rW!-bs8OAF!%%RC +!)`^q"$HV`4T5<\bl.PG49,@-s8U=B!!%`Jrr>pq!!,sZqu6]Zbl.PA;>pOqU\Oih +bl.SBL&CrOUJ_!j"""!I4T5<\bl.PD49,@-rr2tPrVuqPrr2tPrW!&Es8R3?!!$O, +rrgOl!!">@rrN0#9D=_PjkB_9?iWI@rr?R-!!%-=rrM^;rVusFo;hlkA,QB-4SSjU +U\k)n,s3LQ!!#.[rrM7.r;Zh>r;QaCr;Zg[d/O3?!!(p-rrN0#J+imB4SJgUL!K]$ +;#gSBk2-+C,ldp-bfmh!r;Zh>n,EJP!!">DrrhI1!!">!rrPFc-MWl,jjO/2hZ*Yk +`r?%'rW!'Ibl@]brVuq?rVm'J!!#.]s-3E]"(VB2g&:sTPlJr-!%#AZ!WW4mW;csL +!!(pSrrM7.rW!'I@teD=qu?g]s8QU.!!GFHs8QU.!!>@`s+UFP!'L5[!6kEB!'L2Z +"!mpI4Sf!Wbl.SE4TGFkrVuqPrr2t.r;Zp^s8RfP!"5%ks5qNO!!">Fs31HB!/:4J +!2KMn!@=N>rs.4e,liZ!s4RAO!'L#U!6kEB"a%tQF?ClI!!B"rs+UFP#aGAhk0,+O +!%%XE!/:CP!/:FP!/:CP"O-oGjsC!,"_6]ps-+i/!!">@rrN0#9D=_PjkB_9?iWI? +rr?R-!!&emrr>pq!!+CiX8`1)qu?^Cq#:?/r;[*LZ2ajq,ldpBrVll4qu?^ZrVlo\ +-2dfD-.)YoT)\lKf)GdO!.XbC!'L2["!r&C4T5<\L!K\u4T5<\bhi@%,ldp-rVlkm +rVupEn,EJP!!">Crr^#5;;'\'!^H`4pm(pAdf0F,!!'dSrrAhn!!+CirVlj[rW!>$ +s8V4-!!%-@s5kX+!!'e5rs0nN!)`c\!!">Crr^#G,uM__!WW4mW;csL!!(pRrrAhe +!!#mqrrM7.rW)pDr;Zr)s8RfP!!#.ZrrXPI!%%RC"!mpI4Sf!Wbl.SE4TGGrr;Zm] +@jV$Q"$HV`U\t2l-2dfG-0G6OrVuqPp\t4Ur;ZpG4?Oqg!!?a2s'u$.!2K;g!6k'8 +!2KPn!2KJmr[%LC"=<41s+UFP!/:FP!/:CP!6kHB!-J/>r[%IB!%%I@!WW3npQbg@ +gA_4^!'ns3!'L5\"=;:ls)e2>!2Jo\!p533rVlo\fua6po-O86!875K!V7W:!!+Ci +rVltp,s9lZrr@cN!!">DrrAhl!!"=lrr\kn!8lB-!WW4Mq#:>(r;Zh-rr3,`,ldq? +cMmlSrVuqPhu<]0rVup\r;Qb=rVurBnG`SQ!!"=mrrPFc-MWl,jjO/2hZ*YkaSu78 +r;Zhmr;Qc3rW!$Hbl>od!!&8_rr>1\!!">Drs_g*!%%Z!!!"=!Ki'-Br;ZhOaSu;A +!2$4i"+L:Nhtm?QU\Ffi-'\?-!0m6Y!+Z$.!0mH_!/:@N"!mpI-2miHjs:!-4Sf!W +bl.SB4T>?\FS5\7L&V)P-2%<?bl>od!!%`IrrL=ipAbFEg&L1\!!">=rrC::!!,3s +r;QaCp&G3=s8RfP!!%`Prr@cP!!(7Arr>1S!!(^KrrN0#9D=_PjkB_9?iWI>rrLe! +rW!!Gk-=jb!+Yg(!SMSo498requ7!L!!&eoKdA#F-&(Xa!d$PYjo5<jrVupErVuqn +qYpP*rVusFbk1o84So*Yg&D!O;>U=nbgHFmT)\lKf)GdO!.XhE!0mE^!+Ys,!jPUe +p\t9VbhW4#UP6I)rW!%BKnZ;]rrC:B!!%-=rr@cP!!(74rrY@`!%%.7!mJmAjo5BI +!%.K,!9MZ/"5a(Y^W?ET-2mlEU\aujL&M&R4TEY)!!(7Brr@0?!!%`Orr@cP!!1;t +o`+usaSu;A!2$4i"+L:Nhtd9R^JQ<V!!4GmL&(`LUF#g<!@;jdrrC:B!!%`NrrY@` +!%%UD!6kEB!)`Ok"$?P`L&M#PKdHWs"Ci_X!/:FP!R)ka!!:CEbl.SBU\=]ho4'*D +!!OZGk5YJ*rVuqPp&>"<q>^RC;5=!f!R)ka!!gaJs5kU-!5JO5"-`cc-2[]CF=$ea +"!mpI-2IQB!!#[dQ2p%#rrQR.5g]N2Z2FY'4=0t,!'L&V!+Yj*!@=N?rrC:B!!,2Z +qZ$VMm/I'\rVurOkPkOerVusFA,ZH.FSl(<^\n-4U\"Kc-2RZB^]+65-2RZB^X<&` +T)\lKf)GdO!.XkF!Tk^-!!$O&rsRLB-):LT;'dLg-):D<!TnM&,m$2Nk4nrWUJ_": +!BcVFrr?R&!!#.ZrrJllr?VJAU[\9`bl.SBL&CrNA,ZH.bl%JEPWU6<4GE\4"$?P` +-2miGo/n<]qYpSM-2mlG-"H*:rrR9g;>L4ojuar3qYpU^!%.K,!9MZ/"5a(Y^W?EW +,ldq?q#:V0!!">Ff`2!urVlu^,lg(*rrqO2!!(6Xq#CLC-"F^1rrN0#T;_blIfKK+ +p\t6UrlbB)pAY0UFT+B'FQWK#!b3NRqu6c',pf>brrC:B!!%`IrrJA!qu6l_PYjPQ +^Y/Sd!/:@N$):um4ER@',pcFfrrQ[V;=jemk+dW-4GBQmrVlu^,lf7`rs.[r4?S=O +UWiE2$):um4ER@',pcFerrV>:g&1jOP_HmJrs>HB4Ca)[s--Bqq>UKr!)*'P!9Mr7 +!al!nci4!?q>^OBbk_8=4SJgX^]4>Xp&G(=m/I'>rVurBkPkNErVutQbl.SB4T#-Y +;>pOqA+T^$jsBm)"*FSC^\Rp1L!fo&T)\lKf)GdO!.XkF!+Z!.!'KrS"Ao.!,uOO> +!%%UD!M^t;!!+C@qu6]@4Sf$\-):M?-1q6<k5PA\;>C1l4RrFObl.SBL&M#OZ2O_' +-2miEYpK8I!@>tgrrY@`!%%UD!/:CP!2KJl!+Yj*"!s`BU](5nFSc";;>pOqg&(dN +5QE/+Q2p$prr_-Y!5HVT!ni:4pAYAO4L+qdZ%^m/rrM9+qYq#04GEh8Yrj<p4AggI +k/@9$!!&Xirr[`N!8klt"!mpnk3;mGU](5n^\%O+k2-+<bh)jpk1BV7!!#[dQ2p%# +rrQR.5gKB0A,?6+U\XoiFSu4=49-],!!?a2s31<>!DtuY!!(71rr@cP!!(7,rr@0? +!!YRcs!7XF-2dcCg&:sP-0Fh!!2KAj"$HV`A,ZH.4T5<\;:5CGT)\lKf)GdO!.XnG +!Tk^-!!'e,rr>1T!!CIFs0;:u!@?n,rrJl@p](CBs8R37!!#.\rr>1U!!$O"rrC:B +!!J#Us5o%6!!%`PrrB>!!!#.[rrY@`!%%UD!/:CP!/:CO!-Iu9![[l]r;Zi4r;Qc3 +rVusFk55/[5QE/+Q2p$prr_-Y!5F-cf`)!Q!2$4i"+L:NhoG`tbi\-h!)`^q!82u( +q>UKr!)*'P!9Mr7!al!nc2Rbir;Zh-q>UW\,ldok^]+65A,ZH1;?-[?qZ$\ms31HB +!2K)a"L2H!4=0t,rB((bmJd4?-2mlH^]4<[rVurBrVlk>rVuq.o)A]"rW!15,ldok +s8ODE!!KPQ!!"=mrr\kn!8lB-!WW4Mqu6Y<rVupqo`"nRqu?dE;,R;m"3gfF-2mlE +4T:$7-2mlEPlC[`fd6Ut#:3lTKd?^!-3!oHftW5<rVuq.rlkBA!/:CP"=7Q3Kk()^ +!%%49!/:CP"!p&U-2dfD4T>?]jsBs+!@9l,!!%`PrrY@`!%%UD!/:CP!6kHB!5JI4 +$&-U<@fQKTs8P4\!!#.Zrr>pq!!$O*rrPFc-MWl,jjO/2hZ*YkK)aL'!WW4mW;csL +!!(oYrrAhn!!"<ss7lTq!!#[dQ2p%#rrQR.5gKB0A,H<,4Sf!WFT)7CPlKm"-2dfG +U]:A<qu?hQs8U=B!!%`Crr=A<!!(^?rr>pq!!H1!bfi3K!!%`Orr>1\!!'e)rr=AE +!!0>NrVuuC^]"3:-3#7k!%$%m".oPnhq%f/!!%NGrr=AE!!'e+rr>1[!!?a&s+UFP +!mL[urVuq.rr3"o-2mlEU]18n;>pOu--ZDhbl.SBA,H9+L&M&PL&:lU,ldokbl@_* +A,ZH.-1_'9L%bQJ4PB`6!+Z!."=:h_s.fPn!%%XE"$?P`-2miDFT)7?bl7VB;>pOq +L&M#TZ%_??s1eL4!/:FP!5JI4!875K!^H`4pm(pAdf0F,!!'ccs4I>Q!!&Xirr[`N +!8k3a"/@.g4T:$7;>gIpKnB@"rrN0#9D=_PjkB_9?iWI<rr@0;!!%-;rrtRc!%#j! +-2[`CA,cK.bl%MA4T>?\^]"35L%50C-2%<>-0F[r!0mE^!@9&h!!#mprrXPI!%%49 +!87>O![[kVrVutQA,ZH0FT4K&!!'d^rr\kn!8lB-!WW4Mr;Qo^,ldokoD\eQrVuq. +rr3G&!!">-s!7XF-1h/$4T,6[-3!oEg&:sP--ZAg!6kEB!/:=M!2KMn!/:@N!2KMn +"GQlj@jV'R!/:"D!/:4K!@;jcrr=AE!!';&rsCjg!%$e-s#^8]-2miD4T5<_bl@^X +rVup\pAY,&rVupErr2sqrVupqq>UL]!%.K,!9MZ/"5a(Y^OlL<rrN0#T;_blIfKK+ +[Jp4QpAb1UK)bTF!WW3npQbg@gA_4^!'np2!2KJm!%%RD!3uG$!%%C?!E$W1rrC:A +!!&8_rr@cP!!%`CrrM89r]L/["E]?O;6fTi!)`@g!%%UD"!mpI-1_'9L&M&R4TD2U +!!,2.rW!#Qs+UFP!-HZh".oPnhq%f/!!%NHrrC:B!!#.QrrbFa!%%78rsFHBZ2`r4 +!!#l<-2[`D4O!g)!/:CP!-J2>!6kEB!/:=M!6kEB!/:@N"_.N4-o!!+Bfnc&TC +rW)p[!E#*YrrC:B!!">Crr@cP!!^[Is#^8]-2miD4T5<_bl@]QrVur5pAY0U-2mlG +L&\pd!!'e0rrPFc-MWl,jjO/2hZ*YkK)aL'!WW4mW;csL!!(oarr>1V!!#lJs7ZHo +!!#[dQ2p%#rrQR.5gfT3U\t/o4TA:X!!+D;r;QaCqZ$[D;8<#.!0mH_!@?n-rr@cP +!!%`?rr@cP!!(7/rr>1W!!+Alr;ZiNrr3'H!!">?rrLfsrVlj[rW!%Ss5kX)!!>@` +s1eO5!'J^0".oPnhq%f/!!%NHrrC:B!!#.QrrY@`!%%F?!6k3<!Bd.Trr>1\!!(7A +rrC:B!!%`MrrC:B!!%-=rr>1W!!4HVk4&BNFT)7?g%YLHU](5nA,Q?,L&M&Vbl@]* +!!">Drr>1\!!^[Is!7XF-1q3;L&M&R-0A_:!!#mkrrPFc-MWl,jjO/2hZ*YkK)aL' +!WW4mW;csL!!(o`rrJ?Hr?VM-A&!Wqp&>'n!)*'P!9Mr7!al!ndf0<^r;ZsHbl?fO +rVupqr;QaCr;ZmFA*3Ue!/:CP!%%UD!/:CP!/9h?!/:CP!6jg0!Tk^-!!dV"^Wapa +s&&aq!0mK_!%%UE!2KGk"S6+'!)`aq!%%UE"3gfF-2RZBPlC[b,ldokdf0EA!!(p- +rrN0#J,K<Hbl.SB4Sf!Yk(T'!rrY@`!%%F?!6k9>!Bd.Rrr>1\!!(7ArrC:B!!%`M +rrC:B!!#.Zrr>1Y!!4HVk3i6O49,@-pAY,HrVuqPr;QbNrW!/Hs8P1]!%%UD!'L5\ +#0d,I,ldp-oD\e:rVusr-2mlE^\Ig15QE/+Q2p$prr_-Y!5F-cf`)!Q!2$4i"+L:N +hh(m#rrN0#9D=_PjkB_9?iWI@rrL=irVusFbl7VBZ2FY&L&M#O-2mlEL&1fPg"HE* +L&M&P4T59[^]"35A*s9uPQ1\0li-rprVupEqYpPjrVupqrr2t?rVusFbl.PBUF#m> +"""!I^]"35-3!oEFSl+>-0G4,!'L5\!2J$C".oPnhq%f/!!%NGrr=AE!!'e2rsFu: +!!$O/s#^8]-2@K?bl%MC,uMGPrr>pq!!(7@rrXPI!/:=M!6kEB!'L2Z!'L2[!@<Hc +rrY@`!%%@=!6kEB!+Ys,$=a&9-0G7-49,@-rVlj[rW!/Hs8OAF!'KlQ!2KAj!)`Lj +!^H`4pm(pAdf0F,!!'ccs4I>Q!!&Xirr[`N!8iD.TDnrm!)*'P!9Mr7!al!neGfQn +-2dfD^\n*3A,QB-Z2Xb'A,ZH/4MUjp#/<8#!5JPfrVup\rVllArVup\m/I-@4L+5P +!%%UE!/:7K#UKHN-0G7-jsC!,"=7Q3@jV$Q"$HV`L&M&P4T>?\bkqG@4T59[A,ZH. +A(1G[T)\lKf)GdO!.XnG!)`^q!@>M[rrKl3r;[$as8P1]!%%F?"m.*2!%$=qrrq)0 +Z2aiXrVuq?r;QjF!!#.YrrC:B!!#.Zrr?R.!!%-;rrJmKo)AeS!!">=rrqO2!!">: +rr3,F,ldokrr3'_!!">Drr>1\!!gaJs#^8]-0G+)!R0^'rr>1Y!!'e.rrPFc-MWl, +jjO/2hZ*YkK)aL'!WW4mW;csL!!(o.s.B;m!!#[dQ2p%5rrDf_rrQR.5h5l8fd6Rs +!2KDj!'L5\"XVCms5kX+!!=N04=0q+!i,dLrVup\r;QjF!!"=prrA;_!!">?rr?R. +!!&8_rrAhe!!#mqrr>pq!!#mprr=AD!!&8^rrAhn!!"=orr\kn!8m&@!;Gs^!WW4M +qu6]M-2dfF4?Oqe!!]4us#^8]-27E>;>pP(4JV'=s1`%D!%$e-g&:sT-,9K[k&gS& +!@?n+rrC:B!!#.ZrrBh4!"!/Zs8Uc:49,@koD\nT!!"><rr>pq!!GF!bbHK`!!%`P +rrY@`!%%UD!'L5\"3gfFL&M&Q4L+nc"GJ-%-0G.*!2KGl!)`Ii!^H`4pm(pAir9#Q +o)AgL!!'dtrrqQ/UP4EGmJd:N;*<(Mrs7ces3/63KqllarrRm:FP6Zq!!&Xirr[`N +!8iD.TDnrm!)*'P!9NSI!a(NXo)Ac!!'o*7!)`[p!2KAi!5JI4!'L8\!M^t8!!>@` +s-3K_!/:@N"!mpI-.Mqp;>pOqL%tZJ^]"354T59[U\Ffi-'\B.!'L5\!/:CO!+Z!. +!@?n,rrM7.rVurOeGfWC!!(p@rrVpSYOVVo!!%NFrrAhf!!+CNrr3'_!!">>rrBh3 +!<+;C!!">Err>1[!<+;B!!%`NrrC:B!!#.Yrr>po!!+Alqu?_=oD\nT!!"><rrBh. +!!#.[rrY@`!%%UD!'L5\!6kHB!%%RDr[%LC!6k??!)`[p!2K8f!^H`4pm(pAir9(P +@,LVV"5a(Y^Zkb!jsBp*![V@JnG`TE!!#.[rrTrh^Y/VeL&NCq!!4HDZ2O\(UP7k0 +rrXPI!6j0s!WW4mW;csL!!(o0rrQ%DKqSGI!!#[dQ2p%5rreqo(m"F`rrQR.5h5l7 +4T5<\U\FcgU](5n-2miEfhqSG!@;jerrC:B!!%`NrrY@`!%$.p"0j-P4SJdWF8u<* +r;Qi5;'l2A![T.Hqu6Y+rVur5rVll4rVuqPqu6XYrVurBeGfWC!!(p?rrZX/0X(*J +!WW4Mq>UJj-2ITC-"HQFrrY@`!%%@=!M^t9!!+C@rr3#C-2%<=-2dcCbl.SB;>U:m +;>1%j4S&LS@fQKTo`"oFq#CFAbl.PD49,@-rVljprVurBrr3#C-27H?4So'XFT)7? +4SA^U5QE/+Q2p%+rri'%!+=^Xrr_-Y!5Idu!0m9Z!@>t[rr[rT!'L8\!87>O!5I7f +!'KoS#*f/fPQ1\0rr3'H!!(6srrN0#T;_blIfKK+e,KJH-"HWJ!i%&=`W$$^!!">$ +rrTrhZ24J%fnH^,rrTH&FPQlt!!#[dQ2p%5rs%ot!!!jsp\+Ug?iWIBrr^J--&)$l +"-b)XFSl(Bfp%1Q;2)d^rrQ[V;>^@qYpC]ke,KI2bk1o9UVHX)!9X:)!9X+W"6NH, +L&:lOZ"'$prr^Ik!)_5F".oPnhrt(?\,H@.0X(0L!WW4Mq#:BWA,R\T4ET`_rrZ*u +!'KrS!p3u=qu?dEA(ge[!SL?J!!O[&@fQKkr;QcMrVuqnq>UMk4=0q+![Tsnnc&^E +,pd[)rrJl@qu?apg&1jPF8u;NrVlu7,lg(*rrV=[-2[`D4JV`P!%%UE!2K5e!^H`4 +pm(pAiVrm[rVut,ht[3RhZ*Ykl2LsA!!"<-4=0t,!@?n!rr[rT!'L8\!2KMn!0l4; +!'L5\q^)4A"/GnrA,ZH4g&M'u!!(6srrN0#T;_blIfKK+eGfN5rVurBp\tBY,ldq! +a8Z;),ldoko`"u7-):)3!Tmnj,m+,I-0G.*"MZ5_!2KGk"PG($!-I&s!WW3npQbg@ +li-rer;Zm9O8&GL!al!n`;]l#k(<X+rrCa#rr\kn!8lu>!#YY7!AL_OrrN0#J*Ht8 +k$qoSo)Aj:^P/H,p\tBLP_Fh+rr3&)L$&:4!jPUepAY<Yb`mh*k1fn9U],rIg%kXK +bfoq`!872J"m2&';2*]urrAhn!!#.RrrPFc-MWl,jl-4>\,64,@,Lh\"5a(Y^Zkb# +K`D*8rr2tPrVuq.o)AfG!!#.[rrQ%D4PB`;49,A'k5>5\fp&92rr^q:-"HrS"!mpI +bhE'u!!&Xirr[`N!8l<+!/:CP!6k3;!0mH_!0kP(!/:CP!/:+G"JYqs-0Fn#!JMiq +!!&8]rr>pq!!#.Yrr?R.!!">$rrN0#9D=_Pjm2pHYPS;$(m"FfrrQR.5_B#jrr\kn +!8lu>!-%f8!AL_QrrN0#ItI^Frr>1\!!&8UrrPFc-MWl,jl$.=&GlG.@,Ln^"5a(Y +^Zkb#K`D*8rVlta!!">9rr[rT!'J^0"$?P`L$nsC,ldqhgA_3S!2$4i"+L:NhphZ+ +L&M&PbkM,;4T5<\bfB_c,ldokp&>':-2mlE^\7[-A+os'L&M#PjsC!,!@?n+rr=AD +!!'djrrN0#9D=_Pjm2pHfD,CJ(m"FhrrQR.5_B#jrr\kn!8lu>!13K]!AL_SrrN0# +ItI^GrrBh5!!">:rrPFc-MWl,jl$.=3;EOT@,Lt`"5a(Y^Zkb#F8u;'rVluD!!">9 +rr[rT!'KlQ!SP]VrrY@`!/9k@"!mpIbhE'u!!&Xirr[`N!8l<+!/:CP!6k3;!'L5\ +!6iOa!5JL5!+Y^%!'L2[!/:1I!+Ys-"!p&l-2mlEL&M#OU\k)l^]"05jsBs+!/9;0 +!WW3npQbg@li-tZpAb73O8&YR!al!nK)^T*".oPnhrk">\+]k'0X(HT!WW4MK)a-r +!+Z!.!+YX#!^H`4pm(pAi;WdZpAbD*huDR6!5Idu"$?P`FT)4AUAt9?rVm)a@lu&" +A+T[!",-^T4T>?_fd.r>r;Qu+49,@-4JVoU!`:8=qYpUo-&)$l#Wr(eL&_1sbi\d% +"/@.gg&D!R,ldqhr;QeO4T6W-;5<:R!WW4mW;csL!!(p+rr@cP!!(7;rraVJ!%$dK +rr>pq!!(^Grr@0=!!#.Vrr@cO!!GF;s8Tk5!!&8^rr@0=!!%`OrrAhl!!#mQrrN0# +'DEgR3U/j0"SMg"(m"Fk?iWHDs+^OUT)\jNk<K#(o`,1MYQ+V&!.TV#cMmkjrVurO +oD\kW!"/KJ!'/t&!$(Y3"CT+I!5Idu"$?P`L&V)TbU!5hA,cK/KdHWs!@?Furr[rT +!'L8\",-^T4T59[U\Xrp4TCWG!%$=rrr\Jc!%%=<!^$G_rZqRF!<+8EFT)4A49,A8 +rr3'H!!(7BrrLe8q>^M*kPkS`!2$4i"+L:NhphZ+L&M&PbkM,>,ldokmJd3OL#2h4 +js:!-4SJdT^\\!2-2IQ@^\n-44T,3\F<sf^rr>1Z!!#.[rr?R,!!#.<s3:TI(lqr& +5_B#jrrA\A!!G!Z!!%M#s3L]F^]"354S&LP5f3R%^Zkb.49,A'beIX6!!"=us8S>Z +!!#.\rr[rT!'L8\",-^T4T>?bfd-Uu,s3LQ!!1<srVup\r;QjF!!#.Srr>1T!!(^O +rrY@`!/:FP"!mpIbl7VB4T5?[-2mlE^Zb\!!!&Xirr[`N!8lc8!p4Ser$;A+g&D!O +L&M&PbkM,C,ldoks8UbLA,-'6Kfk(hs'l$/;?+Cb4Cb/]rrM8Hr]C6ZPih]>!+Z!. +!2K8f!'L,Y!@?n)rr>1\!!#.Qrr=AC!!">Drr=AC!!">%s3(HE#lm5Bs+^ORT@!W= +ItI^HrrAhn!!&8Srr>=%!!'durr>1V!",M$s5kU-!%!?CA,ZH4U]:@J!!#.\rrZa2 +!'L8\$p4Li--ZDhPU03,s'u$.!2KPn!/:CP!2K5e!'KuU!%%UD"!mpIL&V)V,ldqh +s8RfP!!ebgs03jM!/9Y:!WW4mW;csL!!(p9rrJl@q#CClrr2t?rVurBp\tOG!!">F +s!7XFA,?3*Z2O_)U]2Y>!!">C!!+Ciqu6\N-2RZC-$8q^"Qh!1!'KuT!-J2?!B`LD +!!'e2rr@cP!!+D!o`"pEqZ$UBrr2uOqZ$UBhZ(h#"(5(.5_B#jrrA\A!!Ej_!!%M# +s3CWGF<sfSrr>=%!!'durr=AA!!+CNrr2t?rVur5rr39e!!$O/s+LFQ4T>?b49,@D +s8Skn!!'e0rrhp>!!">ErrXPI!'KoR"?ZYa-&)6?r;QjF!!%`Prs^7S!6kKC,ldq! +s5mf;rVurOkl1\a!2$4i"+L:NhrF_:^\7^.4T>?`49,@-k55/cbeJjc,ldoks5kX, +!!';$rr>1\!!8Db-2.B?-0G1+!+Ya'!/:7K!+Z!.!2K;g!SJdu!!0igrVuq?qu6XB +rVuq_oD\fcq>^W4s8R3;!!(7#rrN0#'DEgR3U/j0#5%s"@,Lul!'l/9LAq@J!!#"A +5QE_8!!X!an,E@fItI^4rrPFc$MYqJ3T*.&*q]L95c4S^^Zkb",ldrE,lpl<r;Qj] +!!#mprs=AZ!'L;]K`D*8rr39e!!%-@s%rar;>:(jL&M&RL&[D:!!&edrrY@`!/:+G +"!mpI^]+6@,ldqhs3(HC-*^;nrVusFUZ_XY!!&Xirr[`N!8li:!/:CP#<[^)b]Egb +A,cK849,@-s4O0$4=)<M!!=PIs&&aq!%%RC!2KMn"""!I-2%<=A,cK.Z2FY),s4:9 +rVupEqYpQ1rVup\pAY+TrW!"0s1eO5!'L2Z!0mH_!'KiP!+Yj*",6dT-2mlE4T5<\ +Pi)KB!!#[dQ2p%3s7QEn@,Lt`!al!nK)^T*".oPnhrk">^\7^0&:a0JrrN0#ItI^4 +rrPFc-MWl,jl$.=@.sX*5em?V!!'durrXPI!6k6<"!mpIL&M#XK`D*8s8RcQ!'L8\ +#s81fL&_0!!!'e-rs1^e!%%5!!!#.QrrY@`!/:+G"!mpIbl7VG,ldqhs.fDj![V@= +k5PJ_!2$4i"+L:NhrF_:;>pOqZ2O\(P[ikTrr>1\!!">;!!FVJs5kX,!!%->rr>1\ +!!@?Cs!@UD#>r7[YpBAM-3!oE4T5<\PlC[_Pl:X_-2RWA;>pOqU\FcgPl:Xf-1h0! +,ldokr;QaZrVuqno)A\9rVuq.rW!!^s4RAO!@>#M!!$NdrrN0#9D=_Pjm2pHhtR0P +@,Ln^!al!nK)^T*".oPnhrk">TD8Hg&:a0HrrN0#ItI^4rrPFc-MWl,jl$.=5ktB\ +5em<u"5a(Y^Zkb#,ldqhq#:FB!!%`Ors=AZ!'L;]K`D*8rr39e!!%`Qs!7Xkk4\fT +U](5n4T5<\U\"Kf49,A8p&>+?!!(7Brrj\K!6kIsqu?dEFQVZa!WW4mW;csL!!(p: +rrA;_!!4Hgk5##W4R`=N4T>?\L&M&T-0G7-U](5r-0G7--2mlEPl:U^A,ZH0^]2(J +!!Hg3s._^T!!$O+rrBh5!!#mjrrM^;rW!(Cs3/\5rVurBrVlsG!!">9rrCaO!!ah' +,ldoks)e5?!Fsg^!!#.=rrN0#9D=_Pjm2pH\,$(*@,Lh\!al!nK)bWG!i%l3r;Qhn +4GDbo!L/;Nrr\kn!8lu>!-nA@!YBkMqYpTs!.TV#]Dhpt!%.K,!9N/=!#YS5!AM:d +rr_-Y!5Idu"!mpIbkV2?,ldperVm1$!!$O/s+LFQ4T>?e49,A8s8OAF!6k-9!'L/Z +!%%49"$?P`L%YHJ,ldqhrr3-J!!(7CU](5o4I"h)!WW4mW;csL!!(p9rr>1[!!4HV +k55/Y4Sf$b,pcEBUWgq.!!#.[rr=AE!!?a2s#g8\!+Z$."$?P`-2dcCPl:XaL&Z8o +!!9EZ-2[`C4So'X4T5<\^\@a.A,QB.,piEg!0mK_!6kEB!'KiP!0mH_!^-K/rVuuC +-2mlJ^],S[!%$P&!WW3npQbg@li-ruqu?b*ht[3Q?iWI(rrC9ZrrCaO!!&8_rrCaO +!!&8DrrZa2!)^H0".oPnhrk">./a,I&:a0DrrN0#ItI^4rrPFc-MWl,jl-4>a8>o< +0X(?Q"5a(Y^Zkb#,ldqhq#:S/!!"=hs8Skn!!^4<s+LFQ4T>?f49,A's8P1]!'KlP +rrTr4A,Q?,^\e'3L%>6G49,A8p&>+?!!'e5rs9tO!6kK*,ldper;QhP,uNn+!WW4m +W;csL!!(p8rr>1Z!!+C"r;QaZrW!$_FHk#GrrXPI!'L5[!2KJm!PcDK!!+D.rr2s\ +rVurOrVllArW!"Rs#g8\!%%OC!Bd.RrrAhn!!#mjrr=A;!!#mqrrC:B!!#.Prr>1\ +!!7lSA,ZH.;>pOt-3+!-rVur5i;WiY!)*'P!9NSI!V[0)!!-KbpAY2%!'n6t"S4_U +-)8BX!6kEB!/:FP!6kEB!/9J5"$?P`-,'<\T)\lKkl1Y'rW!!2J+*+2!WW4MK)`I_ +!^H`4pm(pAiVrmkrVusQYPA,"hZ*Ykl2Li3!!(7<rrBh5!!4H/4T5<\4T>?_K`D*8 +rr30b!!#.]s.fPn"!qH24T5<\Pl1O];>pOq-1V!;49,A8p&>+?!!%`PrrsbL!6kKC +A,ZH.-2p"/,ldokkl1\a!2$4i"+L:Nhr+M8UHJGS!'L5["$?P`-2.?@,ldp-r;QaZ +rVusr;>pOqA,ZE-4T5<\bl.PAbl.SDL&X:3!!,3Wq#:IZ!!">-p\t4>q#CFA-2mlE +-3!oEbl.SB4S\pWbh;sp!%%UE!mL\>q>^V-s8R3?!!%-!rrN0#9D=_Pjm<!MY5eQ1 +ht6pM?iWI*rr@cP!!">$rrQ[mZ0M>hbl.SBL&V)Pbl.SBL#`18,ldok^&J2,!!(p? +rrd9@&:a0@rrN0#ItI^4rrPFc-MWl,jl6:CpD<laYP.tuhZ*Ykl2Li3!!(7;rr@cL +!!+D!rr3(S!!#.\rrY@`!)`aq!+Yg)!@?FsrrAhn!!%`CrrY@`!/:+G"!mpIPlC[b +,ldqhrr2s\p](;9kl1\a!2$4i"+L:NhqnA6^JXq0!+Z$."!mpI-2.?@,ldp-r;Qc3 +q>^OBk5G;[4T5<\bl.PAU](5pL&X:6!!4HD^\@a.L&M&PA,$!'U](5p-&%'PbQ@hE +-2mlMg&M*7,ldokk55/`ffT67L&_1frW!'Is8V4-qZ$`Os8Tk5!!#.>rrN0#9D=_P +jm<!K:gi2OrrQR.5e[0tPl:X_4Pp)?@fQKTlMgk.rVuqPrr2uBrVuqPj8T3-!!"=Y +rr\kn!8m&@"8=3nn+-J[!!%M#s1A:45QE/+Q2p%+rrTAXYOqhshZ*Ykl2Lfp,uO@8 +!L+o/!!,48rVlu)!!&8_rr]MP-*dFK!JMiu!!4HVk55/\;#gSBnG`SQ!!%`Grr[?h +-0G4,!^$H/r;QhP,piNj![Tt(kPkS`!2$4i"+L:Nhq\53U](5n-3!oH,ldp-pAY4@ +!!">Brr>pn!!$O,rr>1\!!(7Arr>pq!!8qq;>pOr;<IcU!TqW)rraVJ!%$e%rr>1\ +!!$O*rr?R.!!&8_rr>1\!!,4RrVlnP-2mlH4TGG'rVup\rr2s\qu?^CrVm!H!!">- +iVrrZ!)*'P!9NSI!9`kO!al!n]`/(D,s9E.rr>1\!!(7/rrC:B!!%`PrrC:B!!%`5 +rrXPI!'Isp".oPnhs(.Ap[@VO!WW4MK)`I_!^H`4pm(pAir9#Ao)AgL!!'ddrr^#i +L"Z>&!SQ/trr^#iKqnJD"Qh!1!/9qB",/$`k3`0Kbk_8=k5NTcrrN0#T;_blIfKK+ +j8T1cKtmTd"S3o>!%%XE"!mpI4SJdW,ldokqu6Z2qu?aDk5>5Z4T5<\U]18nL&M&S +-0G6&rVusFU](2tfjc<.L&_1;rVuq.p\t6.r;Zi4qYpQ1rVup\rr2u'r;ZsHFG3R< +qu?h@s8P4\!!%-?rr@cN!!%->rr=AE!!&eQrrN0#9D=_PjkB_9?iWHWrr>pq!!&8L +rrC:B!!%`PrrC:B!!%`5rrXPI!'Isp".oPnhq%f/!!%M#s1A:45QE/+Q2p$prr_-Y +!5Gf="ChE3-0DB1!WW4mW;csL!!(p;rrCaO!!">E49-],!!#.\rrXPI!'KuT"!mpI +-2RWAA,QB-;>^@n4T,9Z-2dfDA,cK.4T,6^,s4:9r;[(4s8P1]!%$e%rr>pq!!#mk +rr=AE!!(^Orr?R%!!%-?rr=AE!!&8_rrCaM!!(^Nrr?R.!!#mTrrN0#9D=_PjkB_9 +?iWI"rrTrh^]"05g#h]%"6Ri-L&M&SUWgsEmf*:2rVuqPrr2uBrVuqPkPkY.s8UdO +!!#-prr\kn!8lB-!WW4MhZ!bS@lukNPi2QBP_Iom!JU.Arr\L>Ktm9[!5JMe!87&F +!87@*!5I@i!^H`4pm(pAdf0F,!!'d=rrXPI!+W/2!WW4mW;csL!!(p;rrCaG!!BM+ +s31HB!'KuT"!mpI-2RWAbl%MA^\e$24SJgU4T59\bU)u`"!uY#U](5nA+op&-2mlE +^\Ig/L&M&PL&M#OA,$$)4MUjp!%%UE!6kEA!%%UE!'L2Z!2KMn!%$V(!WW3npQbg@ +gA_4^!'n6t"6NHCU]19$^An6[k5Sp;!$sb4qu6\l;>VXEFT;Ap,lf5;rW!'I!!"<B +qu6]ZPl<cGFP6Tl!6kEB!/:FP!6kEB!/::L!M`Nk49:/hq>U]^@jNE$,pd[4!!$NB +rr\kn!8lB-!WW4Mhu<^>!<"2F!%#DCrrI4qrr3!]-/JS&@fXaNrs$[n!$rokA,$!) +ffT96,ln!Up\t7k!<+8F!'K-<!^H`4pm(pAdf0F,!!'d>rrAhn!!'d9rrN0#T;_bl +IfKK+j8T.6-2ITB-):G="!mpIL%bNK;#gSBqYpP*rVupqqYpOXp](:Vqu6`h@jV!P +"XR%.s8RfP!!'e-rr[?C!+Ya&"$?Q0g&1jNUHJJT![Tt(qu6a\!!">Crr@cP!!'e3 +rrhI1!!">(rrN0#9D=_PjkB_9?iWI*rrZ*u!%%XE!/:CP!Fn7h!!'e4rr>1V!!:jR +-2%<>-0G1+!FmGQ!!,sMrr2uBrVuqPrr2uBrVuqPr;Qf&-2ITA;>^@oUF#X7!/8,d +".oPnhq%f/!!%N,rsDU'A*3gkk(Nd]k3;mIbQ*@rrrJ@<hZ![f!!(7@rs'hr^]4=D +!/:4J#Wr*@s8V4D!/:7K#]p&Vk5YHk!)_YR!^H`4pm(pAdf0F,!!'d>rrZ*u!%"`H +!WW4mW;csL!!(p9rrM8HrB(*jU\k&mfnG[_rrRn.g%t^K-2mlE^\[s14T5<\;>rZY +A&%g=#Nhe8;*9Q-k5G;^YpBBIo`"sFbk1o9PihfA!87@qq#:H24?TnGrrSEpZ24J& +UEq4.iVrrZ!)*'P!9Mr7!al!n^&J'prW!&Es8RfH!!#mqrr>pj!!:CE;>'ti-3!oF +bWPY#!%%XE!6kEB!/:FP!6kEB!/:CO!2K8g!)`^p!2K2e!/8,d".oPnhq%f/!!%N- +rrV=/-2[]D@fV5IrrUCEL"lV2ffT6\!6kEA!gE\=rVlmE-2IQB^AqdBrrVd<-2IQA +,piKh!^$J9i;WjD!%.K,!9MZ/"5a(Y^U!kA49,A'XT&>%!2$4i"+L:Nhn/mgL&M&P +4Sf!W4T5<\bi\p+k01@8!TrP&rrN0#9D=_PjkB_9?iWI*rrY@`!%%XE!/::M"XRY) +!!">Err=AE!"EFl^S<mk-0G7-P]T#6rVup\re1@*rr2s\r;ZmF@o<4*!%%XE!6kEB +!/:FP!6kEB!/:FP!Tk^-!!">E4T5<a-0G7-jsC!,"?_BlF=$kc!/8,d".oPnhq%f/ +!!%N-rrUCE4T#-[ffWdCrrhJ<@s"LBrsuku;2)dbs-.U*;2)dbP_K&8"J^'9L"ZG) +#JYumk(QZdg%YLNfd.quK`K?qrrP;/k5G;]F9')PrrSEIL&CrO4=0k(!nf\5r;Qh? +!6jC$!^H`4pm(pAdf0F,!!'d>rrUD,;60]u!!&Xirr[`N!8kEg!%%UE!2KAi!'L5\ +!6g&pp\t9p!)*'P!9Mr7!al!n^&J0s!!">Err@cN!!ssqs+LFQ-0G6\rVup\rVlrP +A!Hlj!/:CP!/:=M!+Ys-"-iicbl.SB;?$Rqbl.SBL&V)Pbl.SBL&V)PA,ZH.A,cK/ +^ErjZ"53_SA,ZH.A,ZE.o0!!P!2I7-".oPnhq%f/!!%N,rrOJm^\7[-U\uJ?!'JL( +rrHU0rZqpPA,fCP!$rqFs#_V,rrUD,!<+8F!-J5?!ehqbrZqUG!2K>h#0^T>s+LHs +rVlmE-2dcEUB$#=rrFDlo)AbR!86<1!^H`4pm(pAdf0F,!!'ccs4I>Q!!&Xirr[`N +!8kHh!5JL5!%%F?!'L5\!6g&pp\t9p!)*'P!9Mr7!al!n^&J0\!!">Err@cN!!C"9 +s+UFP"3gfF-2mlF4L+V[!0mH_!/:@N!Tk^-!!&enrrC:B!!%`PrrC:B!!%`PrrC:B +!!%`Prs'hM!'L;]^JXq0!'L8\"!mpI4T#-YL&M&Pbe=#YT)\lKf)GdO!.Wr,"bcpV +-$6ours5k04PBc7PQ3iFrsWuLA+T`l;#lj\bQ*@rrrG5.rr3Wo!/:IQju`Wts8RcQ +!0mN`PQ3$%rrRlSbl.PB,s;,*!mCXSoD\lG!+YX#!l"^thu<aC!%.K,!9MZ/"5a(Y +^OlL<rrN0#T;_blIfKK+]`.t/rVuq?q#:=VrVurBK)bTF!WW3npQbg@gA_4^!'n6t +"!mpI-3!oEL&CuOA,cK.FT)7Bbl@^<qu?a[U\Oihbl.SBL&CrNPl:X_A,ZE-bl.SB +L&V)Pbl.SBL&V)Pbl.SEL&_1frW!"RUHJGS"=;:ls4RAO!/:=M!/:CP!6i.V".oPn +hq%f/!!%N+rrJlWrW!!G4I#gE!^%d^rVm(U!2KSo,piNi#3I4/s3(Isrr30b-3+"? +!+Z!-#P05fs8RcQFT)4@493UurrRlSbl.PB,s;,*!mCX,o`##g!%%7/rr^Ik!5IFk +!^H`4pm(pAdf0F,!!'ccs4I>Q!!&Xirr[`N!8kHh!%%UE!87/I!'L5\!6g&pp\t9p +!)*'P!9Mr7!al!n^&J0\!!">Err@cO!!(^Orr>1\!!(7BrrJl@qu?a[g%t^Kbl.SB +L&CrN;>pOq^]"04bl.SBL&V)Pbl.SBL&V)Pbl.SEL&_1,rVupEqu?aDL&M#Obl.SB +^\e$2L&M&Pbe=#YT)\lKf)GdO!.Wi)$e^4r,ldp-k5YH-4T,3`bQ(N?bQ)/KrrUCE +L&V)V4=1%-;#ni<rru=#bl<@sbl.PCF9'PXrrRlSbl.PB,s;,*!mCX,p&>,h!%#k] +rr^Ik!2JEN!^H`4pm(pAdf0F,!!'dns8L5os8)`s!!&Xirr[`N!8kKi!87>O!'L#U +!'L5\!82u(p\t9p!)*'P!9Mr7!al!n^&J0\!!">Err>pq!!">Drr>1\!!(7ArrV=m +-2dfE-):A;!6kEB!'L2Z!'L5\!6kEA!6kEB!/:FP!6kEB!/:FP!6kEB",6dTL&(cM +-&)6r!6kEB!6k??!/:CP!6i.V".oPnhq%f/!!%N%rs-;$!)`d?!!)mB"TU[bK`Hi& +rrUCEL&V)U4=1%-4=0n)#)*&As+LHsrVlqQ!6k-9!egWurVlmE4T,3\bQ)/Hrr[rT +--Z#]"0j-PUYYqO5QE/+Q2p$prr_-Y!5IUp!FmGT!!4HDg#i;7U](2n,uNLu!Du_k +rrLeMrZqPup&>#%rZqSBg#W/7!!&Xirr[`N!8kHh"(M<WZ1n8#YpBBIK)bQE!WW3n +pQbg@gA_4^!'n6t"!mpI-3!oE4T5<\;>pLp4T5<\bkqDAk(P,\!!+D.r;Qc@rVup\ +r;QaZrVurBrVm#_,ldq!rr2uBrVuqPrr2uBrW!%Ss8RfN!!,3Wq>UH=rVuq?qu6YM +rVurB^&J2,!!(p-rrN0#J)UD.^P2Oa#4j,es3(HhqLo':s+LH,q>UN?!/:FP"["+2 +s#_V)rs"/WPlHF;bl.PCK`K?irrRlSbl.PB,piKh!l"_hp\t>*!'Js,rr]"r-*c8* +!^H`4pm(pAdf0F,!!'dqrrP:_4T:$:49-\Okl1Zn-2miE,uNP!!b23mr;R#T,pdYe +;#k,#rs(Xd@tf"N-/%D[!WW4mW;csL!!(ogrrLg8p&>'Tb_#lfrrN0#9D=_PjkB_9 +?iWI*rrXPI!%%XE!'L5\!/:CO!-J2?!2KAi!V8GQ!!%`NrrC:B!!#.Zrr@cP!!&8] +rrXPI!)`aq!6kEB!/:FP!6kEB",6dTPl:X`4MUam!6kHB"=4$J--Z;e!+Z!.!2I7- +".oPnhq%f/!!%N-rrP:_^\[s6494's,s:u&!mCXdr;Qqqbl>leL&V)V4=1%-;#ni< +rrus5bl<@sbl.PCK`K?irrRlSbl.PB4=0q*!egWLq#:CX!'KlQ"*=MhbhN.!5QE/+ +Q2p$prr_-Y!5I[r!gE[pr;Qh_!'KKF!B_[^rrhq'!!#.ZrrQ$t^]+67@fX:>rrRlS +L&V)R^Aq-mrrN0#T;_blIfKK+K)_JC!WW3npQbg@gA_4^!'n6t"!mpI-3!oE4T5<\ +L&M#OL&M&UL&_1,PhH$8!-J2?!/:@N!6kEB!'L2Z!5JL5!BfuQrrfSQ!!">ErrC:B +!!%`PrrC:B!!n;Ys5kU-!%!?trs.\?,lggBs'u$."XTrDs5m2W!!%_drr\kn!8lB- +!WW4MiVrtD!'L/Y#Nd<Ys8OAkk5G;aUEt$^js;>Orrj\ps3(Isrr30b-3+"0!-J/= +"skSqs+LHsrVlqQ!6k-9!egWurVlq@!2KMm!^%dkq>UM+!+YX#"/>iYo@j3G5QE/+ +Q2p$prr_-Y!5I[r!^$J,qu6^D-0G+)!87,H!TrPBrrG5.p&>$Fp&>$Fp&>3#,lhF$ +4T59]js<.grrUjR;>L4m;'l/?!@9&RrrN0#T;_blIfKK+K)_JC!WW3npQbg@gA_4^ +!'n6t"!mpI-3!oE4T5<\L&M#O^]"374TA:X!!">E4T,6[U\t,lbl.SB4T#-Y-2mlH +-"CF2qu?hos8U=B!!%`PrrC:B!!%`Prr?R-!!FT14=)<Q!!FVJs5kX+!<+;B!!%_d +rr\kn!8lB-!WW4Mi;X`Y!'I%=s8SiV!/:IQPQ3i.s8U:h!5JR6@fU$<rsN<:L&]?s +;?-7f4=1",![RiArr345!-J8@K`K?qrrRlSbk:u;K`K?qrs7a5--ZDh@fU$<rs/.L +PlLcg!%%Kcrr34WL&_28,pfhnbhrF%5QE/+Q2p$prr_-Y!5I[r!`8rmqu6]Mbl.PG +Yrj<4,uNh&rr@cO,n#G,bU!5h,piTk,uO[A#3KDA!$rqUrVm+";;"em!$ua]rrkMI +Z2Z+4rVlqo!/:@N!@9&_rrFDljo5A^!2$4i"+L:Nhh(m#rrN0#9D=_PjkB_9?iWI* +rrXPI!%%XE!)`^q!6kEA!6kEB!^-K[pAb1Uqu6Z?rVup\qu6Yko`,(Vs8U=B!!%`P +rrC:B!!%`Orr>1T!!&enrrJl@p&G(i^&J2,!!(p-rrN0#J)C8-49,B\,m"&mFT)4E +@fRf;4=(!&rVm'a!$s`R!+Z$."sj6qL&X7]rr35I,lf5R,lhHSrrRlSbl.PCK`K?i +rrRlSbl%JFUEq3K,lg(+rrpUH4TGFYq#CC@rr3*I-3+"0p](<<i;WjD!%.K,!9MZ/ +"5a(Y^ZPP!YlH(lg%bRQUAuToKjt*/k5PB#;#i_4Kff?Fo7\J:Kn]R,,s;5-js:"; +KnVVhA,cK64=):NKleU@L%tZJ^]+66,s;/+!egWhr;Qd[-1h-<@fV5ErrN0#T;_bl +IfKK+dJj70L&O18FMHc6!JQcorrKBhqYpQXre1@7p&>0WP_Fgdnc&WDo?[F<!!#[d +Q2p%#rrQR.5e[1"@fQL+rr2tPrVurBr;QsI!!#.]juiG>!)`Um!6kEB!0m?[!M^t< +!!c@`!!">Fs4RAO!5JO5!87>O!5JI3!JMir!!,3sr;QfA4So*],p`NkKt[KgT)\lK +f)GdO!.Wl*!P`aU49:/uqu6i7F?D[-g&(dPUJX-NU](2sflU)ds-/68rreQK4Cc/) +rrTrhoDS[j^P2:PrrTrhoDAOjfjd-,Pl1Obo7`G/s-3;4rr3+TL&_1fpk8_>i;WjD +!%.K,!9MZ/"5a(Y^ZGJ#F<pne,uKohrVlu^,pi0^rs,;!;?-Zk!)`^p"["*ns#_V+ +rrsc3s8PprPl:Ue493.rs#^9krr3&D,pi3`!@9l,rrRlSbl%JB493V"rrh";,li&J +rrN0#T;_blIfKK+df0BD,lmoj!WW41kPkVG;9]%>!@9kbrrUCj4T#-YU\lD<L%kTP +F<po5,lf78o`"u&!6idh!WW3npQbg@gA_4^!'n3s!McFgrr_CG-$9%a"*>hObl7VC +^LR4)!JT5%rrR9g;>:(tPYjP*FP6]oF?Hi-rrTHZU\t,nZ%\tKrs$5l@m"jtg%bRR +fnE9cKp:`Q@thSq".oPnhq%f/!!%M#s1A:45QE/+Q2p$prr_-Y!5IUp%H_aY,ldok +;=jhfPQ6sGrrtS3s8Pprbl.PF^P2^f4=0t+"XQ;2s!8uhrs-:=;?-YY-0G1+!@9&a +rrFE.rVlqQ!6kB@!^$J,p\tDl4?Oo9UZMLW!!&Xirr[`N!8l9*"2=g^U]18q^H;L< +kl1^<!6kHB!JQcqrr^Ik!'L2Z#ep@%k5XR+!5J@0#dF@l^]4=u!%%=<"(M<2bg6:j +!!#[dQ2p%#rrQR.5_B#jrr\kn!8lB-!WW4MK)`I_!^H`4pm(pAdf0F,!!'dmrsIn` +@jM+;s8PprFSpgr,ll0h,pi?d!B_\,rrj]2s3(I?qu6gG4TGFD-2dcD4=0Y"!@9l, +rrRlSbl%JB493Utrr^Ik!5I[r!WW4mW;csL!!(p*rrQ[1U\t,njs;>;rrRlSbhN.# +KdA#F4T,3\;#nB1rrV=/;>L4n4=0.grrR9BPkb7^ffT6\!6idh!WW3npQbg@gA_4^ +!%<I!LAq@J!!(p-rrN0#ItI^4rrPFc-MWl,jjO/2hZ*YkhZ!fO491WG4951'"3gck +-2@K@4=0t+"XQ;2bQ*@orrj\ps8P2-r;Qd[-1q3<,s;/+!egWur;Qd[-1_';UB"fd +rrN0#T;_blIfKK+e,KJ3!6k??!b4@GrVm#8Ki*Q=qYpY^@q1c&rs4<U!-Eqds+Q^8 +rreQrA!H-Srs+dQs1`YQPih`?"]57^bU#CLrrFDlr;Qd[-2IQA;'l/?!mCXuq>UZP +,pe8!!6idh!WW3npQbg@gA_4n!"ab^LAq=9!!(RL!WW4MK)`I_!^H`4pm(pAdf0F, +!!'dsrrUk"A,?30^Aq.2493V#rrFDlq#:@W-2miI,s;4O!)`Xn"XQ;2s#_V*rrG5. +o`"q<4T59]PQ6F8rrFDlq#:?Ir;QiB!'KED!WW4mW;csL!!(p*rrR9BA+fj&@fZKS +!<?!qrt2L^!$rok--ZB8,lf5;bl8tOrr34u,lf5;!%"E>rs1_L@fRf$!%$=nrrlo- +s8OB-rVlrC!)`[o!b25SoD\l6!0m<Z#0^T>s+LHsci4%H!)*'P!9Mr7"-3E^pO`F# +rrREF(tJWf!!%M#s1A:45QE/+Q2p$prr_-Y!5I^s"6LmUoDAOlYlJnGK`IA8rs"_& +s8P1]g&:pSUEu]84=0t+"XQ;2s!8uhrs$4<;?-YY-2dcD4=0Y"!@9l,rrUjR;>pLr +js<.crrFDlr;Qhn!+Y0k!WW4mW;csL!!(p*rs&'@!$tL,g%t^cF9#hBs8QR/A,lSk +,pfhrs03jrs8RcQbl7VZ,s;5-fd.rLs8Th[!5JR649-\Bs8QR/;=skh,s;/+!mCXu +r;QhP!2K2d"7mfbk4S`UK`K?CrrN0#9D=_PjkB_:VZ6_KK)^W+!al!^ec,[N!.TV# +]Dhpt!%.K,!9MZ/"5a(Y^ZPOu;#i`@rVm,b493.rf`4/5rs-ao-3+"?!+Z!-"]59Q +s#_V+rrsc3s8PprU](2s;#mBks#_V*rrG5.o`"q<4T,3\4=/5NrrQ[1U\Xok;#ljZ +rrP:_^ZPOt!!&Xirr[`N!8l6)!p3u=rW!!GA&&!B!@9&irru=Hk5TN'Pl:Ud@jTh. +K`K?rrrsc3s8QR/U](2t^Aq.2s#^:drVlmE-1q3<,s;/+!mCXur;QhP!/:(F!^$I: +o`"uH!6idh!WW3npQbg@gA_8q!!&@;s+gUU(][+WrrN0#ItI^4rrPFc-MWl,jjO/2 +hZ*Ykjo5WL!$sa]Ki',rA,cK6F9"Fub`jCRU]19549/m+^LI7Rs8Ppr;0;j<,s;5- +fd-W?beI!T;?$Rr4=0q*!B_\#rrFE.r;R#,!%"Da@fSXirs_gObl@^r,liYCUHANd +jo5A^!2$4i"+L:Nhp;<1ULPSr!!%`Qs1\PUr;QtT!0mLG-0G%'!egWurr3-J4TGF- +-2[]G,piTk4=0q*!B_\#rrFE.rVlrC!/:@N!egW.p&>(U!+YX#!egWuci4%H!)*'P +!9Mr7"8<?,5_B#lrr^"9!/K,)!WW4MK)`I_!^H`4pm(pAdf0F,!!'dprrHUUrW!!G +4JViS!JMj!!!+C@r;Qq1,ldokFT2:Ffd-VEk5Q`)rr32U;'c2B-&)<t!Dt0@rrGtC +o`"q<;>^@sYpBAM,uOU?!`:8frr32H4='t--&(F[!WW4mW;csL!!(p#rs.\?!!'e6 +K`Lrq"9=/U,pi?d!egWurr3-J4TGF-4T#-^494(74=0q*!B_\#rrFE.rVlrC!/:@N +!egWLp\t?K49/m^rrRlSbg6:j!!#[dQ2p%"rrZX/!9\t6M>mZk!!(p,rrN0#ItI^4 +rrPFc-MWl,jjO/2hZ*Ykir9,6bfo5ErrUltg%bRJbfoq`!87,H!p7_Ng]%?!bi\Ns +!p7_Nir9&[!2$4i"+L:Nhp_T+Pe[(p#)*&!s+LGMqLo$ds!8udrrRlSbl7VG,s;5- +,s;))"Zue<s#_V*rrG5.o`"q<4T59]bQ)/NrrR9BbkV2?bU!7Cnc&ZE!6idh!WW3n +pQbg@g&D/`!!&(3s,-gYpD<lieGfRM!.TV#]Dhpt!%.K,!9MZ/"5a(Y^OlL<rrN0# +T;_blIfKK+eGfTo!'L,X#0['Es.]R9q#:@W-2dcIk+hPJK`K?rrrj]2s8OAkqu6gG +-3+!--2dcD4=0Y"!@9l+rrFE.r;Qg\-0G%'"0j-uU[\9bK`K?CrrN0#9D=_Pjk9Y: +pFlRapO`F'rr\;^!2n?H!WW4MK)`I_!^H`4pm(pAdf0F,!!'ccs4I>Q!!&Xirr[`N +!8l9*![RiAqu6kB!0mNG,piKh#%Jsas'l&(rVm(D!2KRJ!6kHB"slD3s%rd$rVm): +!'L;]4=0q*!B_\#rrFE.r;Qg\-0G4,!qR^#q>UQM,pge(rrRlSbg6:j!!#[dQ2p%! +rr]G)!0;a3N;j"S(]Z8=rrN0#ItI^4rrPFc-MWl,jjO/2hZ*YkK)aL'!WW4mW;csL +!!(p*rsGM$!+X7Qs4Ll^-3!oG@fU$<rt;(,-3+#-,pge8s3)c8s8RcQFT2:P,s;5- +^AoS(s8V4D!3uS(4=0q*!B_\#rrFE.r;R%t!'KlQo/m#Jrr34fL%G?E490L#bQ[V< +s7:r/qYpVN!6idh!WW3npQbg@fDbq,!"db6s,I$\BE/%9e,KIL!.TV#]Dhpt!%.K, +!9MZ/"5a(Y^OlL<rrN0#T;_blIfKK+df0WK,ldok,ldp-bl.PH49-Zi,lf78rr3S* +,pbZ9,pge8s1\O6--ZB84T>?cUEq3K4=);-rr3!]-2dcD4=0Y"!@9l*rrus5,s3IR +A,ZE1493V*g%YONL&_1s!'L,X!egWuci4%H!)*'P!9Mi4"Le@2+Qn@VO8f<;!!#:4 +rrN0#ItI^4rrPFc-MWl,jjO/2hZ*YkK)aL'!WW4mW;csL!!(p(rs%VM;*6sNU\auo +ULQDKPihoD"m0nh4ET`ars#`<A&&%tPl:Uck&`^JFQWQ%!L/<9rrJ@<o`"rGPktC_ +Yu*V\U\t,pP_J`/oD""C^]4?*Kp;H6!l'H\ci4%H!)*'P!9Mf3"HNN_3;8%)OoGQf +!!"/1df0@K!.TV#]Dhpt!%.K,!9MZ/"5a(Y^OlL<rrN0#T;_blIfKK+K)_JC!WW3n +pQbg@ec,bc!!"_1K)_&7"HNN_&D,>0!WW4MK)`I_!^H`4pm(pAdf0F,!!'ccs4I>Q +!!&Xirr[`N!8iD.TDnrm!)*'P!9Mc2"ntOf!#X%6s-<TeBE/#<\'Y-V!!%M#s1A:4 +5QE/+Q2p$prr_-Y!5F-cf`)!Q!2$4i"+L:Nhh(m#rrN0#9D=_PjjX50E;fh<@,HS9 +RK!G_+92Bacd2Um!!%M#s1A:45QE/+Q2p$prr_-Y!5F-cf`)!Q!2$4i"+L:Nhh(m# +rrN0#9D=_PjjO//Qi6sd#\^Dns.95lhiBJj!!,(BcMmqG!$HkY]0HE/!%.K,!9MZ/ +"5a(Y^OlL<rrN0#T;_blIfKK+K)_JC!WW3npQbg@dJj7);#L@q&7C9F^SJUGQ[^^f +!!*q'bl7YKJcN7[!%.K,!9MZ/"5a(Y^OlL<rrN0#T;_blIfKK+K)_JC!WW3npQbg@ +ci4$l:kA\9!!4!upWNR:QhE[r-Gh)0BD@bmjjO/2hZ*YkK)aL'!WW4mW;csL!!(o. +s.B;m!!#[dQ2p$krrVAM5_9!/!!4!N^V'SuQFN4\jjO/2hZ*YkK)aL'!WW4mW;csL +!!(o.s.B;m!!#[dQ2p$frrCZ&^SS[FpUL6TQFN4\jjO/2k5YLSK)aO("5a(YT;_bl +IfKK+K)_JC!WW3npQbg@K)^H&T`9V0\<[-VdJj7J!.TV#g&D0+!!'L,rr[`N!8iD. +TDnrm!)*'P!9Jh4K)_MDJsNp4!9MW.![%IkK)aO("+L:Nc`$jGIfKK+K)_JC!WW3n +pQbg@K)^H&T`9V0\<[-VdJj;n!!'ccs4dPUpD<lQVuHjK!!(o.s.B;m!!#[dQ2p#u +s+:9Ds+6QHQ2p$orrgpZ!$L`^s4mVVBE/%)VuHjK!!(o.s.B;m!!#[dQ2p#us+:9D +s+6QHQ2p$nrr\Sf!*FjQh>[Sd!!#Qerr[`N!2kF`T>(F-!)*'P!9Jh4K)_MDJsNp4 +!9MQ,"@)qe37ic^iVs)UGQ7^TkGJ7ZIt@Zh!!#[dQ2p#us+:9Ds+6QHQ2p$mrs%@/ +!!!:<f7O%ars%YI+92BQ\#'*)^OcHS!!$L&Q2p#us+:9&s+:9FrrMk=JcOU,!D)CK +rrC[E^AtZnE2[^UHM3X)jb!Mas+:9&s.KAma%1d^i;`lqT9]EVnq*0tQ2p#us+:9& +s+:9BrrM;t]70fVr;Zh9Z@;nYa-6N'nq*0tQ2p#us+:9&s+:9&s4.,LTD\`ihh(mE +rrDVAQCO6@jb!Mas+:9&s+::$rrA\i!!(o.s2"^8nq*0tQ2p#us+:9&s+:9&s4.,L +TD\`ihh(mErrDVAQCO6@jb!Mas+:9&s+::$rrA\i!!(o.s2"^8nq*0tQ2p#us+:9& +s+:9&s4.,LTD\`ihh(mErrDVAQCO6@jb!Mas+:9&s+::$rrA\i!!(o.s2"^8nq*0t +Q2p#us+:9&s+:9&s4.,LTD\`ihh(mErrDVAQCO6@jb!Mas+:9&s+::$rrA\i!!(o. +s2"^8nq*0tQ2p#us+:9&s+:9&s4.,LTD\`ihh(mErrDVAQCO6@jb!Mas+:9&s+::$ +rrA\i!!(o.s2"^8nq*0tQ2p#us+:9&s+:9&s4.,LTD\`ihh(mErrDtKjdbE4pk&Nt +s+:9&s+::$rrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9& +s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9Q +rrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i +!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o. +s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9& +s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9& +s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9& +s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9Q +rrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i +!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o.s+:9&s+:9&s+:9&s+:9QrrA\i!!(o. +s+:9&s+:9&s+:9&s+:9XrrC+:rrA\i!!(pSrrMT?K)^H&K)^H&K)^H&K)^H&]Dhto +(hhP#rrA\i!!(pUrr]H,#g\,&K)^H&K)^H&K)^H&K)`I_#PT&8!%;N1?Msj.G(3U2 +!!#iIs+:9&s+:9&s+:9&s+:9]rrA,M!!(>ss+:9&s+:9&s+:9&s+:9]rrMj2o)Jbe +K)^H&K)^H&K)^H&K)^H&\,QGho)Jd:K)^H&K)^H&K)^H&K)^H&\,QL')"dk/:kJ_! +s+:9&s+:9&s+:9&s0_k,O7`JQc[u1Ks+:9&s+:9&s+:9&s0_k-pDEW)!)S:IK)^H& +K)^H&K)^H&K)`1W!0?jS!7-8sK)^H&K)^H&K)^H&K)`1W!V[H,!!#iIs+:9&s+:9& +s+:9&s+:9UrrA,U!!(>ss+:9&s+:9&s+:9&s+:9UrrMj2qZ$UmK)^H&K)^H&K)^H& +K)^H&YQ"T`qZ$WBK)^H&K)^H&K)^H&K)^H&YQ"Xt)#XF7:kJ_!s+:9&s+:9&s+:9& +s/l;$O8T%Yc[u1Ks+:9&s+:9&s+:9&s/l;(pD<l1:kJ_!s+:9&s+:9&s+:9&s/Z/% +NrT1+K)^H&K)^H&K)^H&K)^H&X8`7q(f5haK)^H&K)^H&K)^H&K)_hM!KYQYs+:9& +s+:9&s+:9&s+:9&s+:9&s+:9&s+:9&s+:9ds+8"U^AuT3s+:9&s+:9&s+:93rr>$1 +!1NrgBS-89s+:9&s+:9&s,m<]hgtis!!"-ns+:9&s+:9&s+:95rr_-Y!-j+1T>(Fu +!$HmnK)^H&K)^H&K)^u5"5a(YT7[*8rrQR.+G0WFs+:9&s+:9&s,m<`hZ*YKK)_JC +!al!NK)^H&K)^H&K)^H&OoGO@!!&XCs.B;m?iV=$s+:9&s+:9&s+:95rr_-Y!2"lC +TDnt#!$HmnK)^H&K)^H&K)^u5"5a(YT7[*8rrQR.+G0WFs+:9&s+:9&s,m<`hZ*YK +K)_JC!al!NK)^H&K)^H&K)^H&OoGO@!!&XCs.B;m?iV=$s+:9&s+:9&s+:95rr_-Y +!2"lCTDnt#!$HmnK)^H&K)^H&K)^u5"5a(YT7[*8rrQR.+G0WFs+:9&s+:9&s,m<` +hZ*YKK)_JC!al!NK)^H&K)^H&K)^H&OoGO@!!&XCs.B;m?iUu:YlN%#s+:9&s+:9& +s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm +^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&XC +s.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H& +K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfn +K)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lC +TDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9&s+:9& +s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXN +s+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8 +rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YK[Jp8lUTjb!UQiUp!al!ApQbfnK)^H& +K)^H&K)^H&SGr]K!!&YVrr[s;L$$e_"$?P`FOC*hYrqq1!Frn1rrZa2!'J-u!al!A +pQbfnK)^H&K)^H&K)^H&SGr]K!!&YWrr@cP!!4HVk0s>4,ldp-dJj1mq#CFmk3`0N +49,@-r;QkQ,pf>0rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKli-rIqu?dE;5;\A +"!mpI4OX60bkD)<--YfW"$?P`-2miDg&:sO--Q;i?iUl7Q2nXNs+:9&s+:9&s+:9@ +rr_-Y!2&TW!'L&W!@<HOrrXPI!'J^0!6kEB"!p&l-2dfE-1gU*"$?P`-2miDbl.SC +-0EGO!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&YWrr>1U!!,3sg&D.#!!#.0rrC:B +!!%`PrrJl@rVuq.n,EJP!!">CrrZ*u!+X4P!al!ApQbfnK)^H&K)^H&K)^H&SGr]K +!!&YWrrtRc!%"mI-2RZBL"cP/,ldp-df0<^rVuqPrVm#E,ldokn,EJP!!">BrrLg+ +b5VNN!#,*m!5='bK)^H&K)^H&K)_A@"5a(YTBlL_49,@-s8V4k-2[`CA*X'pbh;Xg +"!mpI4OX60L&M&PPl1O`49,@-n,EJP!!"=^rrQR.'DIdm^4QB:s+:9&s+:9&s.')k +hZ*YKli.&L!!">CrrI3fr;Zh>q#:?/re1BJk55/a;#gU$s5q(M^\[s4,ldp-q>UQ@ +P_HmLrrV>:^\Ig1UP7k%rr@cP!!(7@rrY@`!%%LA"3cIQbkh>A49,@-r;QhnKsCLS +"m0nh4Cb/arrTrhk4nrWPa(J5!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&YWrrY@` +!%%OB!R*\)!!';$rrJ?1qu?dE;;(sK!87>O!^'=+rVusFFSu.@,ldp-qu6\N-2dfE +-&)?u"Qh!1!5JC1"-`cc-1h-:L&M&Pbl.PEfd-Uu-2miFo4'*F!!,3er;Qj]!!">D +rrB>'!!(^MrrJl@qu@!KL&_1X!!">:qYpY-!!#m^rrQR.'DIdm^4QB:s+:9&s+:9& +s.')khZ*YKli.&L!!">@rr>1\!!#.Zrr?R(!!FV1s8U=:!!$O-rrXPI!'L2Z!'L&W +"*FSCPl:X_4So'X-2mlEbk1o8L&M&Pbl7VCbU*5g!/:FP!R)kc!!';&rrY@`!%%UD +!/:CP!/:CO!2K;h!d+H>rVuq.qu6Z2rVupEli.#o!#,*m!5='bK)^H&K)^H&K)_A@ +"5a(YTBlLZ49,@-q>UKX-2mlEU]18nPl1U]-2dfGFT;Bbo`,!,rr3'H!!#.[rr?R' +!!C"9jsC!,!-J/=!/:CP!'KlQ!+Z!."Ja2*;'l2A!%%UD!'L#V!%%UD"$?P`-2miD +L&M&PU]18ofd6Ut![TrTrW!!^s.fMm!3uJ%!)`^q!+Y?p!al!ApQbfnK)^H&K)^H& +K)^H&SGr]K!!&YWrrY@`!%%F?!/:CP!'L8\!%%UE!/:FP!-J2?"$HV`bkqGC4GAK! +rVupqrr3'H!!#.\rrAhm!!?)n^HDJq"&]*uL&M&Q-/&7s!%%UE!2K/c!'KuU!@>M[ +rr?R-!!?`GUF#m>!0mK_"$?P`-2miDL&M&Pbl7VB;>pOqA,cK4YpC]ks8Psq!!#.[ +rrBh5!!+D.li.#o!#,*m!5='bK)^H&K)^H&K)_A@"5a(YTBlLZ49,@-q#:T],ldok +s8S>_!!IEDs1_G0!!AJcs31EA!/:CO"!mpI-3!oH,ldp-rr2s\rVuq.rr3,m,ldok +rr2s\rVupqrr2tPrVupEo)A\Pp](=Wbl.PA-2mlEPlC[_^]"354T>?_49,@-rVljp +rW!&Es8Tk5!!#.UrrLe!rVuqPrr2sqrVupqlMgon!#,*m!5='bK)^H&K)^H&K)_A@ +"5a(YTBlLZ49,@-p\tXJ!!">Fs#^8]-1dloqu?^ZrVlsG!!#.Zrr>1\!"R6Qs!7XF +4TGHD,ldokoDS[hA,ZH1bl@^<r;Zr7s8ODE!!%`Drr>1Y!!4H/Pl1O]U](5n4T,3] +,ldokrr3'_!!">Drr>1\!!CIFs&&aq!5J7-!+Z!."""!I^\n-4^Zth#?iUl7Q2nXN +s+:9&s+:9&s+:9@rr_-Y!2&TW"$?P`-27EH,ldoks8OAF!$rrh!!+C"r;QjF!!">C +rr@cP!"$mLs!7XF4TGH*rVup\r;QbNrVurBrr2sqrW!!^s-3K_!%%18"$?P`-2u*g +k5,)XL&M&PL&CrQ49,@-rr3'_!!">Drr>1\!!^[Is!7XF-2%9=o-OA9!d+H>rVupq +l2Lfm!#,*m!5='bK)^H&K)^H&K)_A@"5a(YTBlLZ49,@-q#:T],ldoks8ODA!!+C" +qYpXD!!">Crr@cP!"$mLs!7XF4TGG8rVuqPr;QbNrVurBrr2u5r;Zkn4T5<\L%50F +,ldp-pAY+TrVurBr;Qj]!!">ErrY@`!%%UD!'L5\!mL\grVuq?o`"oFrW!!G^ErjZ +!5Idu!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&YWrrY@`!%%F?!/:CP"&]*u-2[`D +4JV]O"!mpI-2dcCFT)7Hbl@\h!!#.]s.fPn!/:@N!/:CP!6kEA!+Z!.!%%UE!%%.7 +"!mpI4SJdTA,ZH.bl%JC49,@-rr3'_!!">Drr>1\!!:CEbl.SBL%G<E4T5<\-2mlE +;<\#]?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2&TW"!mpI-2IQAbU*5g"/Gnr-2mlF +;8;i)"!mpI-2dcC-2mlNg&M'u!!#.]s31HB!'L2Z"$?P`-2dcDjsBm)!/9qB"!mpI +4SJdTL&M&PFT)4Bjs:!-4T>?_49,@-rVlj[rW!#Ds4RAO!+YX#!2KAj!5Iat!al!A +pQbfnK)^H&K)^H&K)^H&SGr]K!!&YWrrXPI!%%LA!R)kh!!#.\rr>pq!!%-<rs<cn +-0G7-,ldokrVlk^rVupErr3'H!!#.\rr=AE!!&enrr?R.!!#mnrr?R,!!">6rrXPI +!'KuT!87>O"XVCms78AP!!&8_rrY@`!%%UD!'L5\#L*5J,ldok^]"07k$pNKqu6XY +qu?^okPkTk!#,*m!5='bK)^H&K)^H&K)_A@"5a(YTBlLW-2mlFA&&#e!i'6Or;Zi4 +rr2u5r;[!I;2']d-2mlHPlLb0rW!$H4?Oqg!!%`PrrXPI!'L8\!/:@O!^&Rkr;Zi4 +qu6]Z-2dfDFRT53,ldp-p&>"hrW!$H4?Oqh!!">DrrY@`!%%UD!'L5\"3gfFL&CuV +;2'^6,ldokqu6Z$qu?`3kPkTk!#,*m!5='bK)^H&K)^H&K)_A@"5a(YTBlLW-1_*: +U\t,lA+fm*-0G7--2.B>4T59^,ldp-rVlj[p](;9q>UG:rVusFk3r<P,ldp-p&>'G +-27H?Z2O\)49,@-rVlj[rVurBrr2s\pAb1>qYpOXrVup\k5PKj!#,*m!5='bK)^H& +K)^H&K)_A@"5a(YTBlLWFS,V7-):>:!L+o+!!,48rr2sEp](:Vr;QjF!!$O-rrM7E +q>^M*q#:=krVuq.mf*Ad!!$O$rrKk\qZ$XCU\t,o49,@DrVm"S!!">-rVljpq#CFm +g%t^K-2mlEUZVRX?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2&QV!l&4kq>^RC;;(^D +#F&(J,pbZq^\n*3-2moC,lp,mqu6bn4Ak8<rr@cO,lplXq#:N[,ldokk3`0LKn]*t +!Tmnj,lqN<qu6cB4?UCVrrRmOZ2=P%UJ^t9!Frn?rrAhn!!#.CrrQR.'DIdm^4QB: +s+:9&s+:9&s.')khZ*YKkPkb$Ki'sdKtlgN"!mpI-/8G"A,ZH.A%2I<k4\fT4T5<\ +UZMLW?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%:2"!mpI-/8G"-2mlE^T@G8^]"35 +-05(-?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%:2"!mpI-/AM#Pl:X_4KJJ^;>pOq +FQEH'?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%:2"!mpI-/AM#4T5<\UTFJ!js:!- +-0F:g!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&Y2rrXPI!%$J$!87>O!%"]G!0mH_ +!'K<A!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&Y2rrXPI!%$J$!0mH_!+W)0!)`^q +!2JTS!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&Y2rrZ*u!+Xjb"Qh!1!5GT7"0hh+ +-0"q+?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%71!L/i'rrV>:^T%57^P1h0rrQR. +'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K +!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H& +K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!A +pQbfnK)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y +!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9& +s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&Y;rr^rubiXNW +ec,\Y!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT@*Z>KdH]u![Tt6k5PJE^]+66;'k&u +!l$&*qu6YMrZqSBg%PFHUHJN%!E%PErrLeMrZqPSci4&S!#,*m!5='bK)^H&K)^H& +K)_A@"5a(YT@3`G@fRfsKnWAM!0l^I!nelRrr3!r-/SY(bU!85r;R"3!)\Gl,ph7> +rs4;Y-$4iN!%$=ors.4'-$4iN!'JX.!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&Y? +rr^pS4S/OO"3`&;k3;mIbQ*@NrrfSQ!!(7ArrTq8;?$RsbU$-^rr_C04S/RP!i#`m +q>ULn!5JO5!egW=dJj8U!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT@<fAK`Hi(rrQ[1 +U\k&kk4\fTbl%JBbQ*@irrCaGrrC:8rrlm4-"?r!rVlq/!5JL4!`8sNqYpV]!/:@N +!@9&grrVd<4T59]bQ'cQrrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKe,KK6!+Yp+ +!qXY-rVlk-r?VH.r;R>];'c2g;;(tH,lf5;L&Y!rrVm(s4='tR;<IlX#DFJq@jM+$ +;>:(pbU$cjK`K?qrrFDlr;QhP!0m?[!qX1Ar;QdD-1h-<^Aq-YrrQR.'DIdm^4QB: +s+:9&s+:9&s.')khZ*YKe,KR5,lhGip\tI\!+U_:490L&rt3!l-$4i_,ph6/F9#0l +^]-DBrr3XR!%!>Q@fRh.s8Rd!4?S=O49/7Rrs&)Ws8RcQbl.PB,s;,*!mCXuoD\m, +!'KoR"6M]G^X<&_?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%^>#GWRF!$sa]g&1jO +@fV5]rs(Xdg&M(7!5JO5#,;0.s3(Isrr33c-3+"u!'KlPrs,;!;?-ZM!'L5[![T/T +p&>)I!6kEA!@9l+rrUCE4S/RS492Y[rreQK,pf>5rrQR.'DIdm^4QB:s+:9&s+:9& +s.')khZ*YKd/O034=0t,!BeU*rrFDlr;Qu9!-J7o!'L2Z"bj5]bQ*@rrrtS3s8R0@ +U\t,r494(7K`InHrrQ$tbk:u;K`K?qrrFE.r;QiB!'KoR!^$Hmp&>2*4?NU+bgHFl +?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%O9$,:R.!%$=uf`3>rKa)W!4TD/VL&(`M +bQ*@rrrtS3s8P1]g&1jSF9')UK`K?qrrRlSbk:u;K`K?qrrFE.r;QiB!'KuT"7nVT +A+BR#UAuUfrrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKb5VZU!)`d?!!)jA"B#2Q +!/:7K!mCXurr3-a-3+!--2[]HK`Hi,K`K?qrrRlSbk:u;K`K?qrrFE.r;QiB!/:1I +"3`%kA+0F!@fW:PrrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKe,KG24Sf!\,piT8 +!'L&V!i#aLq>UN?!/:FP#!=43s#^;7r;QtT!5JPf!6kEA!egWup&>)I!6kEA!@9&i +rrSDbL%tZMYpBCFo`"p_r;QhP!/8l$!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&Y? +rrOJH^\e$8o-HO!s!8uhrs%VMg&K7r;>gFt4Al.&!/:FP#!=43s)\79r;Qs`!87C+ +!6kEA!egWup&>)I!6kEA!`8sNrVlq/!6k9="3`&;Z1\+ubU$-arrQ[1UXK/D?iUl7 +Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%a?",-_$^\n*:@fV5_s%rc\rVm+4!/:IQ4=/5N +rs$4<;?+i?FT2:E4=1%-^ApCprs-:=4TGG8!6kEA!egWup&>)I!6kEA!i#a*rr3&D +,s;#'"7mfbbk(i9,pge7rrUCj-.2_o?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2%^> +$"Er84EQ9e,lg(,rs7a5!+X6f@fSXjrt2L9-'ZW<,ph7Ef`3=?Z2Zp4rr3F;!'I%# +KdA%[s8RcQbl.PCK`K?irrRlSbl%JG49/m8UEooFrr35I,uO^BF9!Wa49/7XrrFDl +rr38!!%"DaPU-=)dJj8U!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT@*Z?KdA&E!!+C" +r;QuS;'c2B,uOU?!O4cd!!,4+rr314,lhHT;'l2@#)+?'!$tLqrr3%R--Z>f!ehrE +p&>)I--Z8d"_/hY!%"E=rrpUm;?-Ynpa,q>!@9&jrrJl@rVus]Z-`LP?iUl7Q2nXN +s+:9&s+:9&s+:9@rr_-Y!2%U;"6RhOg%YLIbfok^!nkfNqYpQKp\t<=bi[^\!p7_N +iVru4bh:;A!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXNs+:9& +s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR. +'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K +!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H& +K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!A +pQbfnK)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y +!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9& +s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7 +Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(Y +T7[*8rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H& +K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m +!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YK +K)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&XCs.B;m?iUl7Q2nXNs+:9&s+:9& +s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H&K)_A@"5a(YT7[*8rrQR.'DIdm +^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfnK)^H&K)^H&K)^H&SGr]K!!&XC +s.B;m?iUl7Q2nXNs+:9&s+:9&s+:9@rr_-Y!2"lCTDnt#!#,*m!5='bK)^H&K)^H& +K)_A@"5a(YT7[*8rrQR.'DIdm^4QB:s+:9&s+:9&s.')khZ*YKK)_JC!al!ApQbfn +K)^H&K)^H&K)^H&SGr]K!!%7q^Rr7B8,s=tQ2nXNs+:9&s+:9&s+:9@rrCr.!1Elf +'DIdm^4QB:s+:9&s+:9&s-s#g."VGg!!"\HQ2nXNs+:9&s+:9&s+:9?rrDfd^Aru% +E!0#@!"rnaDua@LDub%SQ2nXNs+:9&s+:9&s+:92rrB=<Q2hp(!!#g:Q32(9!%O_/ +!5='bK)^H&K)^H&K)^l2!3s8(r;Zgjad)oHrVuq2UQtnoK)^H&K)^H&K)^H&NrK*a +^6\]t!)(G"r;ZgjUQtnoK)^H&K)^H&K)^H&NrK*a^6\]t!)(G"r;ZgjUQtnoK)^H& +K)^H&K)^H&NrK*a^6\]t!)(G"r;ZgjUQtnoK)^H&K)^H&K)^H&NrK*a^6\]t!)(G" +r;ZgjUQtnoK)^H&K)^H&K)^H&NrK*a^6\]t!)(G"r;ZgjUQtnoK)^H&K)^H&K)^H& +NrK*a^6\]t!)(G"r;ZgjUQtnoK)^H&K)^H&K)^H&NrK*a^6\]t!)(G"r;ZgjUQtno +K)^H&K)^H&K)^H&NrK*a^6\]t!)(G"r;ZgjUQtnoK)^H&K)^H&K)^H&NrK+F^?,:n +!-63Cr;Zh<UZDLDK)^H&K)^H&K)^H&K)`smr;ZhIaoD;>!.TV#K)^H&K)^H&K)^H& +K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H& +K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H& +K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV# +K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;> +!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhI +aoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8 +r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H& +K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H& +K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H&K)^H&K)^H&K)b*8r;ZhIaoD;>!.TV#K)^H& +K)^H&K)^H&W;cn9Xi8'R!!%Mjs8;otItI]Ps+:9&s+:9&s+:9Mrr=dg!!%5bs8;ot +?cBb[Y(H\*s+:9&s+:9&s+::6rrBsN!!#C.s1\O7"Ik#CK)^H&K)^H&K)^H&l2LaF +^&S.0aoB'T!#^CgK)^H&K)^H&K)^H&l2LaU^An9Kn&bS0$,6H?$A/;0s+:9&s+:9& +s+::8rrA\i!!&q9^AuEsrrI?F^]4?NK)^H&K)^H&K)^H&K)b*8!2'2i!8iY5!Ul`$ +^Aph(!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9& +s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-n +s+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`i +hh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9& +s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+ +!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O` +TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9& +s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mN +rr?I+!!"-ns+:9&s+:9&s+:9&s69O`TD\`ihh(mNrr?I+!!"-ns+:9&s+:9&s+:9Z +rrC[9^B"#RrrA\i!!(oAs6@?!a3Xbc@/^-++G0WFs+:9&s+:9&s1/.1Va0DF!D)D# +rrA\i!!(oErrVqM.-LX4#^H*lrr?I+!!"-ns+:9&s+:9&s+:9_rrM:Rj8]3'n'V.8 +TD\`ihjXQGa!g!K!*I\L!+>d+!$HmnK)^H&K)^H&K)`L`#,`"4!"`PAlG!F/!!$uc +rrA\i!!(oGrr@<C!!+L/lG!J*-ia7Ve,KDUrVup@K)^H&NW0&i;<EK1K)^H&[JpEV +#QOj4n)s]P5QDqSrrA\i!!(oHrreYg!!nACrr_Ei!-$Ee!+>d+!$HmnK)aX+!b4@- +p&>)g4L)m*"(M<2;?$Rqbk(i8g#dndK)^H&b5VV=0E;Bfjo5F<!!)K9rrA\i!!(oI +rrh3b!%?I\rrP.[8CRS=@/^-++G0WFs5!\W@fQKTpAY9K,ldqua8Z.SrW!-bs5kU- +--Z)_"(M<WZ%E"-s+:9orr[`N!/KY8"1J71T@3`>TD\`ihjscL5QCf)jSo<q!!qK3 +rr?I+!!"-ns+::,rrY@`!%%@=!+Z!.!6iL`!+Z!.!i,dLrVusFZ2">!A,ZH.4G*Tb +s+:9prr_]q!''$="SW`5!-$D:!)NOn!,oDt!f+P2l2Ljp!!%9(rr]_1!#X48!&4BP +!"cSg!gg[RK)_qP"$?P`-2.?@,ldok`r?5h!!">Fs1eL4!@?Fqrr@0>!!%_)s+:9& +s31KFT)\khir9"fJcPcM!Z5nFm/I/q!!)3Arr?_a!<)s!.%c+kY5\Sd!!">=rrXPI +!%#\c!0mH_!/:FP!2KJm!'L/Y!3uJ&!+UW\K)^H&bPqZh!!r>XrrL/2JcPcM!>rlE +rrP.[5iDYCT.kisrVusikCW`<rrY@`!%%C>!6kEB!%#\c"$?P`-2dcCU\t/mA,ZE. +jsC!,!'Gl5K)^H&b5VNF!&0$)hu<bmO!su-!-mr3!Z1oVK)ad/#OfEW(]XOI[t=Xb +rrY@`!%%C>!6kEB!'KZK!R06RrrBh5!!$O+rr?R-!!%`Prr>1\!!+D.m/I+>fn06U +s+::0rrPFc5_B$krre)_!!&ASrrN0#ItI^Vrrqk!!!!;^K)`Rb!Mac7491*6rrY@` +!%%C>!6kEB"[)hbk(USFrsb`Wk5YHY!!';(k.cVbpAY--re1BJk4S`S;>pOq^\[s1 +4T,6]U]4j&!!';!rrCaOK`RFkqu7"F!!%`Qk&`^34JRN.K)^H&l2LfM!'l/9g]%E. ++92])nG`Oi!.TV#g]%AZ!!#99s1nX8UF#^9!2KPn"$?P`-27E>bl.SH4TFOi!!'e1 +rrnVe!%%ZurW!!^;'l2A!@;jarrJ?1qu?dE;;(aE!5JL5!)`Rl!V7W:!!+C"r;Zh- +q>UK14So*Z4L+kb"XO-K-)2da!!%+ms+:9&s6BUc5QF'$s4[JT^An7!nG`Oi!.TV# +gA_8"!!&@;s2+d9Z1e4uL&V)S,ldokq>UNYbbP.;!^-LIrVup\qYpOArVuuCbk;#: +A,Q?,A,$$)-0G%'!)`^q!5J=/!R)kd!!#.WrrAhg!!';'rr=A>!!+D.K)^H&K)b0: +!^H`NK)aO(")%Z7fCAkB!!%M#s4[JTpF$"aK)`^f!'L5\#:2KBF<pnePlC[h,ldp- +s8UceKfo>\qu?g]s5kX,!!%-=rr@cP!!5:_bk1r9^]+65Pl1U]-2dfDFSc";Z2O_' +;>1"iZ24M%-/&.p!SJdu!<"5C!!#.\rr=AB!!+AlrVuq_K)^H&K)b0:!^H`NK)aO( +"5a(YJ+<O?!!%M#s4RDSLB%=pK)`ag"=4$J-1h-9!p2U-rVm$I!!"<--1q6?;?-ZM +rVusFg&:pN-2mlGU]8R9!!>?;Kk()^!)`aq!%%UE!/:FP!-J2?!'L,X!'L5\!5J4, +!/:=N!3uA"!)`^q#\3#ss03jM!%%XE!%%OC#!DMK,ldpeK)^H&K)b0:!^H`NK)aL' +!\aU>nc&Xj!.TV#g&D0C!!&@;s24j:4T5<];;(aE!%%+7!/:FP!'L5\!)`aq!/:CP +"""!Ibl%MAL&M#U,ldoks8S>_!!IEDs1_G0!!&8\rrAhn!!$O$rr>1[!!+CiqYpQK +rW!*as8V4k-2mlE;?$Rq-2dfD4T>?_,ldp-K)^H&K)b0:!^H`NK)aL'!al!Vnc&Xj +!.TV#f`)!a!.TV#_uB_Ar;ZmF;8;u-!%%F@"XPHWFG4i9!!%`PrrAhm!!C"9s!@XE +!/:FP"!mpI4T,3Z4T5<dbl9d*!%%634T#0Z4So'\,ldokk4S`S4So*Y4So'XL&M&S +Z+j-^r;ZjEg&D!O-2dfD^]+68,ldp-K)^H&K)b0:!^H`NK)aL'!al!Nnc&Xj!.TV# +f`)!q!.TV#_Z'V2qZ$Xok5>5Z-2mlI,s5\%bl%J@bl.SBL&M#O;>pOs4TCZF!!">D +rrXPI!%%RC!/:CP#0d)n!!";kqu?aDL&1fLL&M&PA+fj%A,-*)A,H9+4T5<\-2[`E +-"H*9rr=AE!!#.\rrC:B!!#-5s+:9&s6K[d5QF'$s4I>Q?iV>>rrN0#J'7iqg!#/( +UY1q4![%JmK)`[e!P_M0!!+C@rVlsG!!#.UrrC:B!!%`OrrBh4!!/iWrVuqPrVlsG +!!">Crr@cP!!:CE-2ITB-&)0p"nO#?!%%71rr?R-!!-Sor;Zi&r;QaZq>^RCA*3^h +!Tk^-!!&8_rrC:B!!#-5s+:9&s6K[d5QF'crrR:)U\k&nUEr%UVZ-^*!$Lh3!WW4M +c2Rgt4So*Z4L+b_!l'H(U&P0:!.TV#^]+AM@jV'R#J^<=js:!-4SSjUbl.SBL&CrN +A,ZH.-2mlE-2dcF,ldokr;Qb=rW!#Ds!@RC!Bd.Orr?R.!!%`Hrr@0>!!>@`s&&aq +!@?n,rr>1Z!!4I#k5,)Xbl%MAbl7VBbl.SB4G*Tbs+:::rrPFc5f3O'K`D*!qu6aE +!!#lnrrQR.+S#I5!!%Mnrr@cJ!!$O,rrCaO!!'d.rrO;CItI^6rrM7ErW!$ts8U=B +!!$O'rrC:B!!#.ZrrM7.qZ$VMr;QjF!!">Crr=AE!!:jR-2mlF;8;i)"Qh!1!%%@= +!2KJm!'L8\!V7W:!!#.[rr>1\!!,3Wp\t6;rVupErVm#_,ldp-K)^H&K)b0:!^H`N +_Z'T^rVuqnrVllArVupEVZ-^*!$Lh3!WW4Mc2RctrW)pDr;Zh>rVlkmrVuq_UAk9; +!.TV#_uBbjbl%J@bl.SE4TGH*rVuqPp\t6;rVup\qu6Y+qu?^Cqu6aE!!">DrrA;_ +!!=PIs&&aq!-J,<#';.;k5YHkrVuqPp\t5gr;Zg[r;Qc%r;Zh-rr2t_rVusrk5>5_ +PU.W\s31HB!%%RC"!mpI-%c/Ks+:::rrPFc5f<U%g&1mNA,ZE-L&CuObbtIB?iV>> +rrN0#J'IuqK`D*8rr3"o-2mlEg&:pP;'e>urrO;CItI^=rrfS,!$tMAK`F77!!?*u +s31HB!/:1I!6kEB!'L/Y!Tk^,!!%-<rr=AE!!=N04=0q+",6dT^\n-9,uKAM4=0t, +#*f/f,ldokp\t5gr;ZjEbkqD?A,QB-U]18n-2mlJ,s6m8;'l2A!`B!ArVupEr;QaC +rVurOK)^H&K)b3;!^H`N_uB_$r;Zg[rVlj[r;Zh^VuHg+!$Lh3!WW4Mc2Rm"!!#.[ +rrgOl!!%_ArrO;CItI^=rr@cH!!C"9s+UFP!/:1I!6kEB!'L,X!-J2?!@?n*rr=A> +!!#.[rr?R&!!=P0s+UFP!/:4J!Tk^-!!+Ciq>UFWr;Zr7s8Ske!!9G*bl.SB4T,3Z +4T5<\b_#kHs+::;rrPFc5f<U%A,QB--2miD-2dfDL%#$Cfho#errQR.+S#I5!!%Mn +rr[rT!'L2Z"$?P`L%kTPUP7D$s5qPLXT&>E!.TV#`;]l04S\sX-):J>!2KMn!2K8f +"!mpI4So'X;>pOqA,?3*-27H?4T#-ZPU6,*#X$f?s#^8]-0G"&!6kEB!@>tbrrL=i +rVuqPrr3#64S\sX-*dIL"!mpI4T,3ZFT)7?b_#kHs+::;rrPFc5f<U%4T#0]g&M)e +qu?^ZqYpT2A,[bT;5=*i#UKHs^N;Ra4GCZP!al!Nnc&Xj!.W8n",-^T4T59_o-FA: +L&V)`fd.r>s8Th6!'Kj_,ldokbkqD@^LR6h!E#Wjrrc1]^]0#d,m;Hl;'i"6rrQ%D +L"H>++96nCs2+d:PW\mj!^(Ser;Qh?4Ce'a"/@t@bkqDDjs:!--0G((!%%UEr?VJ, +Pkb7`PYjOh,s6:arVlss!!$O&rrY@`-,90R"MZ5_-,9BX#GYck,pbZ`U\t,o;#ho; +r;Ql6,lg&Zs+:9&s6K[d5QF'err=AC!!B"rs'ts,!%%OB!+Yj*"$HV`bl.SB-2[`C +4N%1#?iV>>rrN0#J'IuqK`D*8rr3#64T5<\g&D!UK`D*8s8RfP!!">C!!#.Zrr?R* +!!bXds-*K`,piHh"[&"L!%$=rrr\Jc!%$7s![%JmK)_>?!+Z!.!+Yj)"!mpI-0>.- +Pa(h?!6k'7!p54!mJd42k5,)Yk0/GWK)^H&lMgoN!'nL&!6k<?$"O#8,ldok!!">C +rr>1[!!+AlrW!#Ds313;!-H0Z!al!Nnc&Xj!.W8n!/:CP![TrTr;Zh>rVm(U!!#.] +s31??!%%UE!6kHB!'L2[!@9&k!!:CEL&1iQ,ldokg&:sO4T,3],ldp-fDbmp!.TV# +S,WI7rVur5q>UOC!!"<ss+:9&s,R*\5QF'frs=AZ!$s`-!'L:rrVuqPrVurBrr2tP +rW!6e^]1P=!%%[Fbl%MG-$4i'!!"=`rrQR.+S#I5!!%Mnrr@cJ!!+D.rVm(3!!#.] +s31B@#aG?O!!%`Qs+UFP#s?!&YlFbMs8RfO!!bX$bQ%Vhs'u$.!2KPn!/:CP!2J0G +![%JmK)_A@!0mH_!'L&V"!mpI-%c/Ks+:92rrPFc5fE[749,@DK`D*!s'l$/-1`D_ +!0mK_"!mpI4T>?_K`D*!rr3'H!!$O.rrhp>!!(6^rrQR.+S#I5!!%Mnrr@cI!!+Ci +rr30b!!#.]s1eL4%!;OL49,A8s8OAF!'L8\#)*$W-3+"!rW!@:s8U:C!%%[:,ldok +rr3'H!!#.4rrO;CItI]jrr>1\!!&ehrrXPI!%!6sK)^H&NrK/G!'nL&"=4$JFNj^a +&>LNO!+Z$k!!$O/s.]Po-0G4,",-^T4T>?_,ldqhrVlsG!!%_lrrQR.+S#I5!!%Mn +rr[rT!%%Vu!H]Xe!"(%Ds8P1]!-J8@L&M&P-3!oN49,A8s.]Po-0G4,$&&?Z4TGG8 +!!">DrrlmpU]:@JrW!"Rs-3K_!2J-F![%JmK)_DA!87>O!%%C>"!mpI-%c/Ks+:92 +rrPFc5fNa'U](5s^],S[!'L5\$bu^O!!">Fs+LFQ-2miGK`D*8rr3'H!!%`OrrY@` +!/8Dl!al!Nnc&Xj!.W8n",-^T4T,3ZPl:XhFT;A'!!%`Qs+UFP!-J5?#Wr(eL&Zi, +!%%UD$&&?Z4TGG8!!#.Urs1^e!%%5!!!#.3rrO;CItI]krrA;_!!$O'rrXPI!%!6s +K)^H&NrK/G!'nO'##P@#-3+!Bqu@-Os8S;`!%$e-K`D*8rVlu7!!#.\rrXPI!/:CO +"$?P`KuO&n?iV>>rrN0#J'IuqK`D*8qu7(7!!#.]s#^8]L&_1,rVurBrr36d!!%`Q +K`D*8rVm2=!!#.]s+LFQ4SSjUU](5n4T5<\UXfAG+96nCs.0/mjs:!-^\@a1;#gT+ +K)^H&K)^l2!^H`N`W$,g!!#.]s-3E]"(VB2g&:sTPlHF;!%%UD"3^`F-3!oH,ldq! +rVlsG!!&8&rrQR.+S#I5!!%Mnrr[rT!'L2Z&\gF.!)`dr49,A8s8QR/!%$e,rs1^e +!+Z%_!!">Drs?mL!%%[FK`D*8pAY+Tqu?^CeGfRm!.TV#SGrZQKtm<\!L/h#s+:9& +s,I$[5QF'hrs7a5!!%-@s5kX+!!'e5rrj\K!)`d2rVur5rr3>1!!">-s8OAF!/:FP +!+Z!.!85?k!al!Nnc&Xj!.W8n#_`6Y4TEX#@jV'R%);iB49,A's8P1]!%%UD"]57" +4TE1q!!'e5rs>q1!%$e-K`D*8pAY--qu?_NeGfRm!.TV#K)^H&K)^H&QN%"O!'nR( +!6kEB!0mK_!'L5\!%%UD%8d**-3*uk!!"<eKi'/q!"!08s!7XF,pbZ"rVupq`;]mH +!$Lh3!WW4Mc2RctrVupEq>^Mjrr39e!!#.]s'l$/-2miQK`D*!s8OAF!$u`@;'l2A +"_7Rd!!#.Srr>pq!!"=orrO;CItI]Ps+:9&s+:9:rrPFc5fWg(bl.SBbl7VBFT)7? +L&M#OL&M&Rbl=I4!!=PIs!@I@!@?mHrrQR.+S#I5!!%Mnrr@cJ!!,3srVm0d!!#mr +s+LFQ-2miJPQ1\0s8Skg!!O\KK`D*8p&>#erVuqPe,KIl!.TV#K)^H&K)^H&QN%"O +!'nR("5*YS-2miGjs:!ir;R&b,ldqhs8T>Mr;['bF?BOLs8ODA!!,sM_uBdG!$Lh3 +!WW4Mc2Rgt-2dfG,pd'9qu6u-,pfhrs-*K`;>pLsf`2!urr3#(-2dfM4EN[c-3'_? +!)`Fh"&T$u-.;ep+96nCs8;ltPb[jM!L0A,rrL@+K)^H&K)^H&hu<aC!'nO'!jRI4 +r;Qf[bkh>@Yrma<rr^K0PhH'9$^BfRs!7XFFHhKDftm[i?iV>>rrN0#J'@omk5ENn +rrSF=k5>5\P_Ifgrr^K0PhH'9"dJ0Ls-/61rrhI1!!%`$rrO;CJ%GX\g!&a]!R/dG +rrY@`!-Io6"$?P`FOU6l,lf7Ro)A`q4T$K+A&!WqK)^H&K)bWG!^H`NU&P3@!!%_d +rrQR.+S#I5!!%M.rrcg3!%$dUrrO;CJ%P^_K`D*8q#:DL,uOI;"(M<2;;qNW,ldp- +p&>+?!!#.2rrC:B!!%`Drr=A@!!,sZU]1F+,s9DmrrIgqg]%B<;'f(ks+:9grrPFc +5b\2[,ldq!^&J.A!$Lh3!WW4MNW0),!!$NVrrO;CJ%P^\4T5<\PktC[Z2O_'A,?3* +^]"35-/nk+,ldp-p&>+?!!#.2rrAhn!!%`ErrC:;!!+Cih>[Q=Kqm9"!p533q>UGZ +rVup\ao;I-!!#mZrrTrh^\Rm0;>pOqFFsOEs2=p=5QF'CrrXPI!/8,d!al!Nnc&Xj +!.U%/!2KMn!5Hq]![%Jm]DhkQrVup\qYpOXrVupEqYpP*rVup\iVs!+!!"><rrXPI +!'Jd2!/:CP!0m*T!6kEB"!p&l-2dfE-1g!n"!mpIPh?!<,ldq0qYpPLrVupEb5VIV +rVupEkl1XtrVurOqYpOXrVuq.K)^H&`;]m(!'m7X"!mpIKt[Kf?iV>>rrN0#IuaO2 +;#gSBd/O.i!.VW\!87;N!87;M!2KGl!878L!%%UE!2JNQ"!mpI-2%9?,ldp-eGfN& +rVurBoD\g7rVuqPrr3"o-2mlEA)I:dbl.SB4PB`7bl.SB4So'\ju`ViUW`Z<o-O>8 +!6j^-!/:CP!/::L"L06Q-/!nMK)`dh!^H`NU&P3@!!&e-rrQR.+S#I5!!%M/rrY@` +!-HTf![%Jm])MaVrVuq_r;QaZqu?`3r;Qo^,ldoki;Wm*!!"><rrXPI!'Jd2!/:CP +!6k'7!5JL5!/:CO"O&.l!%$P&!6kEB!%$=u!6kEB!%%F?!R0]Krr?R,!!&8Jrr@cP +!!$O(rrC8ps+:9frrPFc5b\2ZUHE&!rrQR.+S#I5!!%M/rrUD,;:#7D+96o&rr@cP +!!#.[rrM7.qu?_Nr;Qb]rVupqi;Wm*!!">BrrQ\-g&D!R,ldp-eGfMYrVurBoD\fE +rVuq_r;Qj]!!">%rrXPI!%$:t"!mpI-0"q*g"G?a!3uD$!+Y9n!6kEB!+UW\K)`@\ +!^H`NK)aL'!al!Nnc&Xj!.TV#f`)!q!.VT["Qh!1!%%UD!0m?\!-J/=!'L5\!2KAi +"3cIQbkh>H^Qdm\s5p(u4ET`brrXPI!%%RC"$?P`-3!oH,ldp-q>UQ@P_HmFrrBh5 +K`Kg$rs.\.4=):9PW\pF!86uD!/:CP!6kB@"$?P`-3!oFUSIhg"PK#L4I#[A"3d!` +^\e$5@jNE`rW)mC!E&"VrrAhnK`Jm^rrqP,4?R,Kr;Qk/,pb\Q!<"2E;<IlX!gJDl +q>UTNUP5K*qu7"F!!%`Qk&`^34JV3A!V7W7!!#.Urrp/I4?R`#rVm#_;'dMNrW)mC +!+Z$.!p52jrVlq/;;(gG!el?jqu6Ykre1?eK)^H&hZ!XB!'l/9f`)"\!$Lh3!WW4M +K)aL'![%Jm\c2XlrVur5rr2s\qZ$UYrVllNrVupEqu6`h@jV'R!BdXcrs$4<!%$<. +-2dfD-3!oH,ldokrVlj[rVupErr3'H!!#.YrrIg"r;ZjEL&(`LKdHWs!Bd.SrrI3f +p](:?o)A]DrVurBrVm;Z,ldoks8RcQ!)^L.qu?^or;QeO-2dfE-&)?u!5J1,!-J2> +!H]Xc!!+BUqYpSk-2[`G-&)C!^\%R2FT;A_!!">BrrKAer;ZjEA,Q?1,ldokUF#g< +!-IQ,!+Z!."ApHF!%%F?!+Ym+"?`shs)do6%);iB49,@Ys8Ppr!'L/Y"&T$u4T59\ +F=$hb!@;6-s+::/rrPFc5_B$grrQR.+S#I5!!%M#s4I>Q+96o%rr@cP!!IEDs5kX) +!!">Drr?R.!!$O,rrL=iq#CE"rr2tPrVupEq>^kes8OAF!'L;]ff]35!/:FP"!mpI +4T,3Z4S\sWFSu.>ff]$0!-J2>!+Y^&!%%49!/:CP!6kHB!R)kh!!@rTs#fuT!87AO +!'L&W"*FSC^\%R,FT2:?4SSmV4T,3ZU\FfjFT9,K!!@?Cs#g8\!6kEA!+Yg)!'L5[ +!%%@>!@?mqrrBh5!!4J/4T5<\bkh>>A,$$*Z2\q=!"$F?s!7XF-3*ukrVur5rVm#_ +,ldokrr2s\p](:VK)^H&iVrsE!'l/9f`)"\!$Lh3!WW4MK)aL'![%Jm\c2Z_rW!$_ +s8S>_!!FT1!!">DrraVJ!%$e*rr>1V!!">Err@cL!!">E!""AZs!7XF4TEVOr;Zh- +rVlsG!!#.[rr?R'!!'e5rrM^;r;ZmF4=0t,!'L8\!/:@O"!p&l-2dfD-1_'9A,ZH2 +UWes"-2mlE-3!oE4Sf$X-2mlHbl@]QpAbE4s8Rd8,piKir]C0Xrr2t.r;cgCr;Zi4 +rr3#P-2mlG,s3LQ!!YRcs+NQ]-2diC49/7XrrbFa!%$e,rrA;X!!%`Prr=AB!!+Al +rVuq_mJd/KrW!"Rs'u$.!/:=M!0mE^!^&RkrW!.Vs8QRk,piKir]C1&rr30K!!">F +s+UFP!)`^p!-J2?"(VB2A,QE,-2dfD^OlK;s5O%[5QF'$s4I>Q?iV>>rrN0#ItI^Q +rrO;CJ%5L_,ldoks8P4\!!-S>rW!&Es8Skn!!#morr?R-!!?`GUF#m>"-iicL&:oP +A&$7e!!n;Ys!7XF4MN3@!!$O,rrXPI!'L8\!2KJm"&Yi.4T5<\;?$Rq;>gIsL&_1, +rVuqPrr2sErVuq_rVlk-rVupEo)A\PpAb4?^]+654T,6^-$7gorW!#Ds.fMm"&Yi. +4T5<\;>^@q,ldokqu6Z2rVusFU]18n4T5<\L&V)P;>pOqA,cK1YpC]kqu6aE!!">A +rrY@`!%%XE!Tk^-!!>?Jb[^VP!%%XE!%%OC#!DMK,ldpemf*9@rW!$_s8S>_!!#mn +rr=AE!!&enrr[s$4PBZ4!6kEB!'L,X$RGcQ4TGHD,ldokk5PA\-2mlGg&Jhd!!+C@ +rr2s\rVuqPK)^H&ir9'F!'l/9f`)"\!$Lh3!WW4MK)aL'![%Jm\GlPNrW!#7jsC!, +!Palu!!@rTs&&aq!5JI3!%%UE!0mK_!5JL5"$HV`L&CuRU]:A<rW!.Vs8OAF!%%OC +!-J,<"!mpI4T>?\4T5<\A,cK5o-FA:-3+"[r;Zhmrr2tPrW!&Es8Skn!!%`MrrXPI +!'KiP!'L#V!BeU)rr>1\!!FUls8RfP!!:CE4T5<\A,cK2o-FA:-2[]E,ldokqu6Xn +rW!)+s8SiVr;ZrQs8Tk5!!#.QrrXPI!%%LA"!mpI-3!oEFT)7?4T59[;>pOtbl@\h +r;Zg[rr3'H!!#.MrrM7.rW!%qs8U=B!!">Crr@cP!!$O$rrC:B!!#.YrrCaO!!#.\ +rr>pq!!@rTs.fPn"&]*u;>pOuZ2aj!4T,6[fn06Us5O%[5QF'$s4I>Q?iV>>rrN0# +ItI^QrrO;CJ%5LY^]"36FG9\o!^-M,rW!."s5kU-!'L2Z!2KMn!'L2Z"sj6L-3+"! +rVuq?rVm'a!%$e-s!@LA!)`Um$RGcQ4TGHD,ldokoDS[hA,ZH0bl;2P!!%->rr@cP +!!^[Is'l$/-2RWD,ldp-o)A\PqZ$[D4I#aC!'L5\!3uP'$K`W74TFOi!!">:rVlk- +rVurBr;Qj]!!">BrrsbL!'L:8-2[`CA,cK.;>pOq^[qI-49,@-qYpXD!!">Err=AE +!!';&rrAhn!!@rTs!@UD!5JO5"!mpI4R`:M;>gIp4T6Z+!!+D.rVm!H!!">:oD\n= +!!#.YrrC:B!!#.\rrBh5!!=PIs&&aq$)@P#,ldp-s+Mcs!!$M\s+::0rrPFc5_B$g +rrQR.+S#I5!!%M#s4I>Q+96o$rrqO2!!#,-rW!4gs8OAF!%%YerVuq_r;QbNrVuqP +r;Qs`!!">Fs+UFP!6kB@!6kEA!%%F@!0mB\"sj6L4TGH*rVup\r;QbNrW!/Hs!7XF +-0G1+!/:CP#0d,I49,@-qYpXD!!#.PrrY@`!%%Wg!9X+W"$?P`-2[]Ebl@^erVup\ +r;QbNrVurBr;Qj]!!">CrrC:B!!+Alqu?a[U](2p,ldoko)AeS!!">ArrXPI!%%XE +"!mpI-2dcCbl.SEL&_0!rVup\rr2uBrVup\nc&XP4S/URU]18nbl.SBA+KX%,ldp- +qu6Z?rVup\rVlj[rW!5=js:!--3+"hrVusF-2[`D4JRN.K)ad/!^H`NK)aL'!al!N +nc&Xj!.TV#f`)!q!.VKX!%%LB!%%XE#UKHN-.sRE!%%OB!'L5\!6kB@#!;kc-3+"! +rVurBq#:?<qu?^CrVusFbl%JF,ldp-s8RfP!!%`Nrr@cP!!UUH,ldokr;QbNrW!/H +s8P1]!'L,X"!mpI4S&LS,ldp-p&>+V!!">?rr@cP!!%`Nrr@cP!!(7@rr>1\!!(^N +rrC:=!!,3Wr;Qc@rVuq?o)A\PrVurOqu6jH!!">Fs31HB!'L2Z!6kEB",6d;-2mlE +PlC[_bl.SB4RrFObjtf7A,cK.L&M&P^[hC,,ldokqu6Z?rVup\rVlk^rVut/A,ZH1 +L&_1sq#CFXUOrMts5*bW5QF'$s4I>Q?iV>>rrN0#ItI^QrrO;CJ%,FXA,?6+FT2:? +;>pOq;>pOqFSl(<A,ZH.bl%JF49,@-s8RfP!!(7<rrC:A!!7964T5<]-1h*8"sj6L +4TGGVrVuqPr;QbNrW!,Gs!7XF-2dcCPl:Xebl@]*!!">ArrXPI!'KiP"!mpI4SA^V +49,@-q#:>hrVuqPr;QbNrVurBr;QaZrVurBrVllAqZ$Xo^\[s1bl.SBL%>6D4T5<\ +bkqDI,ldoks8V4-!!#.ZrrC:B!!7lSbl%MAbl7VBbl.SB4RrFOL&M&R,s5'(K`TDm +rVupErr2tPrVurBo)Ae<!!">BrrC:B!!#.ZrrXPI!%%UE!%%XE!6k<?!E$U`s+::+ +rrPFc5j&(Jg!&.9rrV>:Pg'./k02'?rrSsLg"HB,?iV>>rrN0#J*-b4bfo59rrTrh +Z-<4Lg!%\2rrJ@<f`)!q!.VKX!2KGl!@?n-rr@cM!!+D.qu6YMrVuq?rVm,b,ldp- +s8RfP!!(7<rrC:B!!H1!s4J^t!!#mprrsbL!'L;]bl.SB4T,3d49,@-s8P1]!%$e+ +rrC:B!!B"rs'u$.!0mE]"S3o>!'KiP"!mpI4SA^V49,@-q#:?<rVup\r;Qj]!!">B +rr>1\!!(7ArrC:A!!-T_q#:?IrVuq.o)A\PrVurBqu6aE!!">ErrXPI!%%RC!+Z!. +!mL\grVupErVm#_,ldp-nc&S8rVuqPqYpdH!!">-s8S>_!!&ebrrXPI!%%OB!6kEB +!'L2Z!+Ym+!/:FP!6kBA!Fsf7s+::)rrPFc5jA:Mfd.sD!!4HD^[M1);#gT\rVlo\ +bh2psKdAk\,m=8K,pcE`r;QfNg&D!Rf`2!ug&D+]!$Lh3!WW4Ml2M$a,p`Nk,pd'` +n,EJ9!!(^NrrLg+f`)(a,s3J"rW!$H-"F^frrL@+rr3(b!!#mLrrO;CJ%,FXg&(gM +;>pLpbkhA?;>U:mg&:sT--ZDho0!!P"-iicL&M&PbkV2<^]"35L&V)PU\t/mPlC[b +,ldp-rr2sErVuqnrr2t.rW!$ts8QU.!!%`OrrAhn!!?a2s1eL4!0mK_!SKU7!!">9 +rrXPI!'KrS"$?P`-27E>-2mlEU]18nA,ZH.;>^@n4T5<\bl%JD,ldokPl1Ogk#!Ee +s8OAF!%#DYrr_Cn4JVfR!'L5\!6k??"!mpI-3!oE;>pOqA,cK.U](5q-3+"hrVupE +r;QjF!!">9rrA;_!!+D.qYpP*rW!%Ss8UdO!!,4ErVlu7;*<)"rrXPI!%%OB!6kEB +!'L2Z!878M!@?n,rraVJ!%!m-rr_C\,s6eZK)aj1!^H`Nl2L`nq#CD:nG`SQ!!%`P +rr]MP!'Js7!%%==%6WeQs)\5@FT;Bb!!"=urrQR.+S#I5!!%N5rr=AA!!+C@nG`S: +!!(7Brr[?C!-Hrp!6k*9!)`aq#s826Z2aiX!!#.7rrO;CJ%#@W-2dfDU\t,l-2[`C +^\Rm0;>pOt,s4:9rVupErr2tPrVurBq#:>JrVuqPrVlk>rW!0Lk5YH-!!#.\rr@cO +!!58F-2dfG^]4>Kr;Zq0^Q_Uo!!">Err>pp!!=N04=0q+!%%49"!mpI4SA^V49,@- +p\t5Ir;Zm]4=0q+!5JF2!'L5\!6kB@!/:@O"XRZ4F?ClK!!:jRL&CuV;2'^6,ldok +qu6XYrVurBqu6aE!!">ErrBh4!!4H/4T,6^L&_1srVupEr;QaCrVurOoD\eQrVuq. +q>UH0rVupErr2sqrW!'I@tfV6rVup\qu6aE!!">BrrC:B!!#.Yrr>1[!!$O,rr@cO +!!OZYKlfF'rVurOK)^H&j8T0G!'ofK!^$G_r[%LC!+YR!"$?P`L&V)PA,ZH.bhE's +-1q6Hg&M'u!!">Fs3(HC-/&;"?iV>>rrN0#J*Ht7,ldrE-2dfDU[e?d,ldqhrr3'H +!!">!rrC:9!!=PIs5s:\##YF#K`D*8g&D*r!.VHW!)`^q!@@@8rr>1[!!">?rrLe! +p](<!rr2tPrVurBq#:>JrVuqPr;QaZrW!.Es8OAF!'L5[!'L#V!-J2>!'KoS"GQmU +jsB^$!@?n"rrXPI!'KrS"$?P`-2.?=4SSmVFSc";4T5<\bkqD?4SAaT4T>?\4SJgU +-2[]B4T5<\bkqDB,ldokrVlk-p](:Vrr2uBrVup\r;QaZrVurBo`"pErVusFk4nrV +4T5<`g&M**-2.B>;>^@q,ldokqu6Z?rVup\qu6YkrVusFk55/Y4SAaT4G*Tbs5O%[ +5QF(6rs(Xd!/:IQUHJMU!5J+)"$?P`L&V)SYpBB4g&D1$!!%,Ur6,4r4GEe7$$6.I +FT;Bb!!"=urrQR.+S#I5!!%N5rsC%P!6kKCKd?^!-1V!;,ldqhrr3(B!!%,prrgOG +!%!?sbQGV%4L+nc#s81fZ2aiX!!#.7rrO;CJ%#@WPl1R^g&1jMPl1R^L%kTJ^EraW +!@=N>rr@cP!!(^IrrCaO!!&8]rrBh5!!\/Ws!7XFA,ZE.juiJ?!+Yp+!'L)X!%%UE +!'L8\!TlN@!!FSJ!%$e!rrZ*u!+Y^%"$?P`-2.?>juiJ?!+Yj)"$?P`-2IQAF=$b` +!@;7Rrr>pl!!,sMqu6a\!!">ArrZ*u!'L2Z!+Yj*!'L2Z"!mpI4T,3ZFT)7?bk(i: +,ldpep\tB2!!">-rr3#C4Sf$Y-"HfO"!mpI4So'[,ldpTqYpXD!!#mlrrI3fq>^OB +@tO`4s5EtZ5QF(6rrY@`!/:CO"$?P`A+KX%49,A8rVlo\bh2pt,ldqho`"sFbl7VE +bQ%Vhg&D+]!$Lh3!WW4Ml2Li3!!(7BrrgOl!!&ebrrXPI!6kEA!SP]Mrr^IF!%%7: +!R06rrr[rT!'Js7![%Jm[Jp>+,pd[1rrhI1!!#.SrrM7lr?VJSk5>5]^EikBpAY2J +4GEY3!i%kfrr3(q4Ak8<rr@cO,lplXq>UJj4T6W2;5=/r,pfhorsHMN4=):9FP0M; +L%50DKn]1!",/$IFS,S5L&E:u;8;o+"/@.gFSPkAk(Po[,pb[-k5,)YUJ^t9!Frn? +rr]#B-$9"`"6O*'g&(dMUHJN%!Bd.SrrZ*u-):D<"2?,^4S&LRk(T&mrrTH&^\e$3 +PW\mj!HdK"rrZaW-*d=H!l%TSq>UQ@4Ajf,rs7b@4?Oo94GDpLs+::/rrPFc5jA:N +49,A8rVltR!!#.PrrY@`!/8l$"!mpIbjYQ6bQ%Vhg&D+]!$Lh3!WW4Ml2Li3!!(7A +rrY@`!/:"D"!mpIbgZRobQ%Vhmf*BC!!#.7rrO;CJ#`MNfnG.Bk5PG7OT,@Dk2H=? +k2ZIAk4JZRk2H=@bi\d%!TqVWrrD3RrrD25s+:9ZrrPFc5jA:N,ldq?rVltA!!#.Z +rreR.4GCQJrrY@`!/:FP!mEc(qu6i7;'dMNPl:U_FCY.H!b5a&p&>+?!!(79rrSrX +FT2:BbQ%Vhqu6l8F?DZ_Pih-.!al!Nnc&Xj!.X;6"O$iG-0G1+"$?P`L&M#Tk(R;B +L$&=5"!mpIbl7VDUHEYqrro/[,pcE`rr3&Q@tjdZ!ngG)p&>-<!!">;rrR:)U]18q +K`D*8qu6hp@luk`UZMLW+96nCs+:9&s+:9&s-EZd5QF(6rrXPI!6kHB"O&.l!0mK_ +!L+o.!!%->rrY@`!/:FP"$?P`L&M#OU\b#q-):KM!!#mnrr[rT!)`Fh"=4$J@m'`: +"$A\Cg&:pW,ldqhs8U:C!%%RC!+Ym+!@=N(rrQR.+S#I5!!%N6rr^IF!%%UD!2KMn +!87AO!H]Xd!!+C@rVlsG!!(7BrrXPI!6kEA!+Ym+#!CT=49,A8qu6aq!!&8WrrpUH +!$u_LrZqV.4Ce?i"3^`F-3!oHK`D*8rVloO4So*Z--YQP![%JmK)^H&K)^H&K)_/: +!^H`Nl2M5>!!(7Ck(P)]!%$e-s+U7K!0mK_"$?P`L&V)S49,A8rr2u5r;ZgDrW!!G +s)e5?!0mH^"Qh!1!)`Fh!%%@>!'L5[#pfQObl@^e!!">Drr?R(!!(^9rrQR.+S#I5 +!!%N6rs-aJ!%%[Ffjk!]!'L8\!)`Rm!@?FurrXPI!6kHB"!mpIbl7VBL&M&Q,piNj +##YC_!!"=urVlkmrVuq_pAY-:p&G)&rr3)E!!">Err[rT!'L5[!'L&W!%$h.![%Jm +K)^H&K)^H&K)_/:!^H`Nl2Lf2!!*!Er;[;%s8Uau!!"<TF<pne-3!oH49,A8rr3'_ +!!%`Prsq3l!%#D[^H;Kns77N:!%%UD!+Z!.!5J4,!%%@>!'L5[#pfQObl@^e!!">E +rrAhn!!d#Xb`jCR!/9Y:!al!Nnc&Xj!.X;6!6kEB!@9&j!!FV$s8S>_!!4HgA,ZH. +;?$Rt,ldqhrr3TW!!(7Cs4J[u!'KE+UEonos1eO5!)`^p"$?P`-2%9<bk;#:FT2:B +^An6[rr3(S!!#.\rr?R.!!e5%b]G-2!6j[,![%JmK)^H&K)^H&K)_/:!^H`NlMgnI +-2ITB-):J>&Q&N.--ZDhbU!5h^]4<r!!%`PrsC%P!/:IQK`D*!bl.PAbl7VBPl:Xf +FT;C',ldpBo`#%?!!"<Bqh5%4rr2uBrW!/Hs8U:C!%%XE#<VtdA,lS(4T5<\^Zb\! +?iV>>rrN0#J*R%6U\Olj4O!g)$7,ZP4S/UQPQ1\0rr30K!!(7Cs31HB#0d,I;#gSY +r;QcMrr2t.rW!&*s8Skn!!%`GrrC:B!!+BUr.P-8rVltR!!">Ert0qb!'L;]js:!- +PlLc;,ldokkPkT+!.TV#K)^H&K)^H&QN%"O!'oiL!6k?@"!o78k5G;^,ldpTrVm0d +!!%`Qs#^8]L&V)Y,ldq?s8OAF!+Yd'#!;kc-0G5;rVur5o`#">!!(7:rtETV!%$e- +s3(HC-3+"u!!"=hKdHZt!+Y3l!al!Nnc&Xj!.X;6!/:=N![TsnrVllArVuqnrVmWZ +!!(7Cs!7XFbl@^e!!">-s5kU-!2K>h$K`W7!'L;]49,@-o`#$;!!"><rr[rT!%%XE +%#"Z]4TGGG!!#.D@jV'R!2J`W![%JmK)^H&K)^H&K)_/:!^H`NlMh%3!!"=hbkh>> +bl.SBbl.PJ49,A8s8P1]!/:FP#pfQObl?>r!%$e%rrB>'!!Qm4,ldpBoD\n=!!(7: +rr^IF!%%XE$HrJM-3+"[!!";kr;Zj\UZVRX?iV>>rrN0#J*R%;K`D*8bfoq`",-^T +-2dcX,ldqhs8OAF!6kKCbQ%Vhs8S;`!%%@=!-J2?!MdF/!!%`Frr^IF!%%=<",-^T +4T>?fK`D*8s8R0@!$rri!!,s3k5PK*!.TV#K)^H&K)^H&QN%"O!'oiL"3^`F-2@K? +bl.SBbl.PJ49,A8s8P1]!/:FP#UKHNbl>le!%%=<"Ao.!-"HoS!5J.*"!mpIbkD&= +bQ%Vhrr32H!!">Fs+U=M!Bd.<rrQR.+S#I5!!%N6rr[rT!'L&V",-^T-2dcX,ldqh +s8OAF!6kKCbQ%Vhs8RcQ!)`Fh#:0?M4Ac(Y-1h-=bQ%Vhp&>,J!!#.\rs"/W!'L;] +4T#0\,uMG<rrO;CItI]Ps+:9&s+:9:rrPFc5jJ@ObQ%Vhq#:?IrVurBrVm0M!!&8` +s#^8]L&V)X,ldqhs3(HC-2%9<^\\!24S&LS,ldqhpAY6=!!">Ers$[I!%%[FPl:Xa +,uMG:rrQR.+S#I5!!%N6rr[rT!'L&V"-`cc-2miZjs:!-g&M'u!!(7Cs3(HC-3+"! +!!#mhrr@cM!!$O#rr^IF!%%=<",-^T4T>?bK`D*8s8Psq!!4HVk2QCB+96nCs+:9& +s+:9&s-EZd5QF(7rr^IF!%%C>"!mpIA,cK8UAt9?k5YHD!!%`Prs9tO!6kK*,ldq? +rVlr]KtmQc!+Yp,!2K,b"!mpIbkD&=bQ%Vhrr3>L!!">Fs4IAP-'\?-!l&6!kl1]l +!$Lh3!WW4MlMgs?!!#.VrrCaO!!&8_rrZa2!%%XE&1%;Vbl@^e!!">Fs.]Po--Z>f +!l'HOr;QaZr;ZjEk4ATTbQ%Vhp&>,J!!#.\rsFG[!'L;]PQ1\0^\n*5UJ\;[rrO;C +ItI]Ps+:9&s+:9:rrPFc5jJ@ObQ%Vhp\t5'rW!!sKk()^!)`aq"$?P`L&V)V,ldq! +s8Psq!!d#K^LJPi-0G1+!Tk^-!!#.OrrXPI!6k0:!6kEB#0d,IbQ%Vhrr2s\rVupq +re1G:!!">/rrQR.+S#I5!!%N6rr[rT!'L#U!'L5\!b6p]rVuqPrr30K!!(7Cs31HB +%F"kP,ldokFNgLW!!#.ZrrAhm!!$O"rr^IF!%%=<",-^T-3!oWK`D*8s8V4-!!"<T +KnXUp!!#.FrrO;CItI]Ps+:9&s+:9:rrPFc5jJ@ObQ%VhpAY+Tq>^OBk5PA_49,A8 +rr33L!!%`Qs4J^o!!+D.r;Qb,rVuqnnc&\;!!(7:rrC:B!!^[Is3(HC-3!oFfd6Cn +!)_t[!al!Nnc&Xj!.X;6",-^T4SSjVbU*)c!'L5["sj6Lbl@^erW!&Es8Skh!!#.Y +rraVJ!%$durr^IF!%%=<",-^T-3!oHK`D*8rr2tnpAb2Ikl1],!.TV#K)^H&K)^H& +QN%"O!'oiL"6Lm0;>1"jff]04!BeU)rrZ*u!5JO5"&T$ug&D!PbWPb&!E%PIrrY@` +!'KcN"!mpIbk:u;,lf7jrr^pS!'L5[!p3?+qu?aDA*<jn?iV>>rrN0#J*R%9YlFcX +pAY/u-2[`C;>gFq,lf7irrOJH-2miEUF#g<!@;7QrrhI1!!$O!rr^IF!%%=<"2=g9 +;?$RtPQ1]*rVlo54So*Z-&(O^![%JmK)^H&K)^H&K)_/:!^H`Nl2LeHbk(i;ULQD` +U\aukKqnSG!R06orro0-4?R`=qYpQ1rVuqnnG`PP!%%:;!p7_hrVlr(L$&:4!Tp0V +K`JmLrrQR.+S#I5!!%N5rrLg+oD\rX;*8@'qu6`NKtmTd!p7_hqu6ha;*6t*k5,)\ +K`D*!k4/HRjs:")o`"sFbl%JAPa)%E!SN_:K`K?YrrO;CItI]Ps+:9&s+:9:rrPFc +5dC=k;#gSBmf*:2_>aRE!$Lh3!WW4MZ2XnP!!#mbrrM9+_>aQZ!.TV#K)^H&K)^H& +QN%"O!'mji"Qh!1!/7QT!al!Nnc&Xj!.V<S!2KMn!5GZ9![%JmK)^H&K)^H&K)_/: +!^H`NZMt&L!!">-XoAH1!$Lh3!WW4MZMt"h!!"=HrrO;CItI]Ps+:9&s+:9:rrPFc +5dLCl,ldpBXT&?0!$Lh3!WW4MZi:0j,ldq!XT&>E!.TV#K)^H&K)^H&QN%"O!'mji +"!mpIUTFIs?iV>>rrN0#J$].XbQ%Vhk,eRa+96nCs+:9&s+:9&s-EZd5QF'TrrQ[V +4KJJ`?iV>>rrN0#J$T(U4=+L<rrO;CItI]Ps+:9&s+:9:rrPFc5_B$grrQR.+S#I5 +!!%M#s4I>Q+96nCs+:9&s+:9&s-EZd5QF'$s4I>Q?iV>>rrN0#ItI^QrrO;CItI]P +s+:9&s+:9:rrPFc5_B$grrQR.+S#I5!!%M#s4I>Q+96nCs+:9&s+:9&s-EZd5QF'$ +s4I>Q?iV>>rrN0#ItI^QrrO;CItI^crr>R2!!"*ms+:9&s+::6rrPFc5_B$grrQR. ++S#I5!!%M#s4I>Q+96nCs6BUb*>6OK!>kfgs+:9&s+::6rrPFc5_B$grrQR.+S#I5 +!!%M#s4I>Q+96nCs6BUb*@/g8!A=G)s+:9&s+::6rrQ!s5_B$grrQR.+S#I5&-.33 +s4I>Q#QT@+s6BUb*@/g8!A=G)s+:9&s+::6rrQj6+G0XGrrP^k5k4jU-idY,s4I>Q +!!%e+s6BUb*@/g8!A=G)s+:9&s+::6rr[`N!9\t6gA_9L&--,.rrPFc."_KPrr]_1 +!2kGKlMgl*,g0Nq0*$V(K)^H&K)b$6"2=g9LP#Q[rr]G)!2oAe"+L:Na+F?FrrZ@' +#k*BFlMgl*,g0Nq0*$V(K)^H&K)b!5""4-Tf7O%Xrrhd-!#YJ#rrh3b!%@Sns4mVV +^An71K)b*8!?EH/?NDe[K)^H&K)^H&k5PP0#QQi9s4mVV?iU2!n,EKV!!%7qs5!\X +pE0GIkCW`urrF,cb?k9'!.t6&s+:9&s5j7`IfKI>kCW`lrrhdu!!#RWrrbRe!-n6i +s5<n[a!^ofT7[+,rrF,cb?k9'!0[?^LWMd]T7[)ps,d6bpG`-Q&=;R3s5a1apSSi. +!$LIlrrq9k!!"FNK)am2!T//$!!#iIs6'C_*@/g8!A=G9rrE*,b7am]*J4<Cs,[0\ +mpQ(o!'#QqjZieOrVusikNi-Kf/Wa0!"a`IjZif"rVus)LP#QgrrF,cb?k9'!0[?_ +!%,l`!?E24s+:91rrKSGJcO^/!>qHlrrIl\JcO^/!BA\_s5j7]*@/g8!A=G9rrE*H +b=r!X*J4<Cs,6mXYAgO-r;Zg?^d%s7TC:nA!r&TpY<W(!rVup0]KcLBc[u2WrrF,c +b?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_ +!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24 +s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\' +!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB +5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+ +rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9' +!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l` +!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9& +s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M# +s3(EB5lL``5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL`` +5_B$+rrF,cb?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,c +b?k9'!0[?_!%,l`!?E24s+:9&s0D\'!!%M#s3(EB5lL``5_B$+rrF,cb?k9'!0[?_ +!%,l`!?E24s+:9&s0D\'!!$BF^Aum*rrC*Y^Aote!!#99s.')i*@/g8!A=GrrrM:) +aT);fb=r!X*J4<Cs+:9Vs1\O7%&h>K!J:Nj!!#'3s.')i*@/g8!A=GrrrTq8;p0%Z +!%,l`!?E24s+:9&s0D[=!!!nZrr<A?!!"-ns.')i*@/g8!A=GrrrTq8VT[cr!%,l` +!?E24s+:9&s0DY)$,6H?#L!,IfY.=c.Y@\jrrF,cb?k9'!6bBD^At.Sh#RL&b=r!X +*J4<Cs+:9Vrr@\d!!!bWrrCrk!!*,!K)_A@!?EH/?NDe[bPqXY!2drq!<=Ii9`Z7T +K)^H&K)`+U!RZtI^Aph(!!".brrCsT!!%5_^B1s,mt1S/rrF,cb?k9'!6bBD^At.S +h#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscbhu*NTT7[)ps4mVT*@/g8!A=GrrrTq8 +VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".brrCsT!!&XCs+::+rrF,cb?k9'!6bBD +^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscbhu*NTT7[)ps4mVT*@/g8!A=Gr +rrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".brrCsT!!&XCs+::+rrF,cb?k9' +!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscbhu*NTT7[)ps4mVT*@/g8 +!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".brrCsT!!&XCs+::+rrF,c +b?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscbhu*NTT7[)ps4mVT +*@/g8!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".brrCsT!!&XCs+::+ +rrF,cb?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscbhu*NTT7[)p +s4mVT*@/g8!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".brrCsT!!&XC +s+::+rrF,cb?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscbhu*NT +T7[)ps4mVT*@/g8!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".brrCsT +!!&XCs+::+rrF,cb?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-++Nscb +hu*NTT7[)ps4mVT*@/g8!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+!!".b +rrCsT!!&XCs+::+rrF,cb?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i@/^-+ ++Nscbhu*NTT7[)ps4mVT*@/g8!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::Arr?I+ +!!".brrCsT!!&XCs+::+rrF,cb?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s760i +@/^-++Nscbhu*NTT7[)ps4mVT*@/g8!A=GrrrTq8VT[cr!%,l`!?E24s+:9&s+::H +rrDNbrr?I+!!"/<rrDNArrD6ZrrCsT!!&YgrrDM>s+::2rrF,cb?k9'!6bBD^At.S +h#RL&b=r!X*J4<Cs+:9&s7uZr:fsq"rr?I+!!"/?rr^kl+QqJY"0X,.hu<WUhu*NT +TDnimpRauqK)^H&j8T-#,g0Nq0*'2q!l"`4bM<1!-Hf*a*?Bb3K)^H&K)bZH#L<AM +(hf8N;>pP#*P\M%0E;)lf`).0!!#96\#90)#CO:A?jH`<kCW_cs5X+[*@/g8!A=Gr +rrTq8VT[cr!%,l`!?E24s+:9&s+::Grr>md!!*Agf`(rTnGiQ.K)^H&ir9$",g0Nq +0*'2q!l"`4bM<1!-Hf*a*?Bb3K)^H&K)bWG!7139!-$Nh!&4!E!=7k?s+::1rrF,c +b?k9'!6bBD^At.Sh#RL&b=r!X*J4<Cs+:9&s7cNn;"O_g#j9e=!3bto!-!PiK)ag0 +!?EH/?NDGQb<Q+_!1_6g!<=Ii9`Z7TK)^H&K)^H&p\t6>oDel1eGfM&o`,!ukCW_c +s5EtY*@/g8!>baZ0ENL"I*:=H!%,l`!?E2KrrBfcJ"1ug[t=Y#rr>mh!!*AgeGfNP +o`+u2K)^H&i;Wfu,g0Nq(BDXo!g!D0bM<1!-Hf*a*?CUK!3^tSRK*>kK)`pl!71?= +!-$Bd!&4-I!=7k?s+::/rrF,cb?k8d!6`.ZO8s[Oh#RL&b=r!X*Ld!.It@Zh!!%M# +s2Y->;"t"k#j9Y9!3c+s!-!PiK)aa.!?EH/?NCrCbJ41@!.;uG!<=Ii9`Z7TRf<G= +!!%WNT)Sil!.TV#a8Z.cp](;5d/O)"q#CF$kCW_cs53hW*@/g8!>ba\^]r$0X?`?= +j3?B\!%,l`!?E2LrrRZM!.t6BrrN0#ItI^?rr>ml!!*Agd/O*Lq#CD6K)^H&hZ!Ts +,g0Nq(BDt#"O>ren+l\W"T7uamc;mR!<=Ii9`Z7TRf<G=!!%WNT)Sil!.TV#`r?%b +q>^M7cMmkuqZ$X&kCW_cs5*bV*@/g8!>bag^]VO-l20iI"9%iVhq6`c!%,l`!?E2L +rrRZM!.t6BrrN0#ItI^>rr>mn!!*AgcMmmJqZ$V8K)^H&h>[Kr,g0Nq(BE4*!lEOQ +jo5D]oB=oa!<=Ii9`Z7TRf<G=!!%WNT)Sil!.TV#`W#qaqu?_9bl7Ysr;Zj(kCW_c +s5!\U*@/g8!>bal^]MC0qr%JTr9E(n!<=Ii9`Z7TRf<G=!!%WNT)Sil!.TV#`;]g6 +rVus)kKNr+YPeG$E.\+As4mVT*@/g8!>bam^]E$\pAY9I[srm8r'1BmIt+rZpAY0h +k2+np!%,l`!?E2LrrRZM!.t6BrrN0#ItI^=rrCCE!!$u\rrb"U!!qb>s+::+rrF,c +b?k8d!8tWn`pNR$!lodMn,NMTLX5bq!V5.+h#RL&b=r!X*Ld!0IfKJ#s.95l!!%M# +s24j=:]Ldab5VRI!!$tis+::*rrF,cb?k8d!9(]of_bOF!nV)kl2Ul&Gh;fl!W;-> +h#RL&b=r!X*Ld!0IfKJ#s.95l!!%M#s24j<cN%q*rrOk[kCW_cs4dPS*@/g8!>baq +^]<QorrKSgj8]3?\,-+)mc*%'!%,l`!?E2LrrRZM!.t6BrrN0#ItI^;rrH6baSu:E +E.\+As4[JR*@/g8!>bar^]<QprrL_BhuEdKhu!ESo%rH`!<=Ii9`Z7TRf<G=!!%WN +T)Sil!.Tq,\\A-T`kMM[Y.ju"K)`%S!?EH/?NCrCjhLo6lhg\^GkV1B)eEf*TqT-u +f*8m]TlOpM'_hY/#`&<W!VP=2h#RL&b=r!X*Ld!0IfKJ#s.95l!!%M2rr^l&5VRci +U]:N'5Y.7!K)`7Y!?EH/?NCrCk.h#9mJQtbh`1E3!ZPIKnCI`8<ttH1!BA^/rrMfL +k1p%<-Hf*a*?CUK!e11Mdf0B`L"YMd"6NH,FRK/0biX`]!WW4MPlCgZLF@`SRfEIC +YCce`rrF,cb?k8d!9_,u_Xm^*!RD>S!!3@2Y3OU>Ti(Xd!>*TTrrD`Sh#RL&b=r!X +*Ld!0IfKK?rr^q#-"H':"2=g94QHGAL&M&PbjbW849-[)^UjFHUQjojrrN0#J!L$7 +T-4(4"!D9,LP!:>^BD#qIom9\!!+dgK)`C]!?EH/?NCrCkJ.)\r;Qf4&GlG.0=Bro +!N74>!!*YWr;QcbkM6.=-Hf*a*?CUK!e11MoD\efrVurOk5PO;!!">*rr@cP!!(75 +rrA;]!!,s3^&J4F!!">-ec,[N!.U@8!T,=)!!49^c[u1hrrTZC)#aL98FM01]DhmP +,g0Nq(BE[7!8.5L!P\p<!!*:Ej4==YAbuH.+Q*+s!9WM$!<=Ii9`Z7TRf<G=!!)Wj +!'L5\!6jU*"$?P`4QHGAL&M&Pbjk]5L&(cN-$8:brr=AE!!&86rrN0#J!g6>^CUAF +(nZE[Rf<TdIh2S[.+a(N]`/!Q,g0Nq(BE^8!QtB>rrL^_qu?aT[G]X;W$;->!?gk0 +rrN&Ul.l@?-Hf*a*?CUK!e11MoD\eQrVurBk5PNG!!#.Arr@cP!!(75rr@cJ!!+Bf +_>aKtrVurOec,[N!.UF:!13Zb!Isiqs-N`hmoTPi&@[8k^&J*R,g0Nq(BE^8!:Tmd +!Uh0/!!,4/p"'8fTn`J\!f`o#p"'5e./X&G3;<CO!:oC1!<=Ii9`Z7TRf<G=!!)Wj +!'L5\!6jU*"$?P`4QHGAL&M&Pbjk]5L&M&Q4=0k)!BeTDrr_Cn4L*E9!WW4MRf<Mo +!!!;&K)_&7"GZsW#g\,&^Ae3S,g0Nq(BEa9!8.5L!+>^)!Bea(f)s0?6Oi_h!uk*l +d.l2nasd/f!.XqH!9WS&!<=Ii9`Z7TRf<G=!!)Wj!'L5\!6jU*"$?P`4QHGAL&M&P +bjk]5L&M&SbiXU*qu?aDUQb]Z!!%M<rrg@J!"dJ.s,m<aY5ePFkCW`KrrF,cb?k8d +!:.E$_YO01!2'/h!Bea)f)j*+%Ia?!%;!]Xf)a#Br;Zj(ci*kDq<Hc:!%,l`!?E2L +rrRZM!;$3j4T5<\bi\p349,@Ds8TifL!8f]!/:CP!6k!5!/:CP!6kHB!M_dT!!&e] +rr^q:4O!g)!R/dBrr^qO;;(sK!l'H5p](08!9WhO!WW4MSGr`T(]XiVK)^o3"HNN_ +3;8%)_#FEU,g0Nq(BEd:!8RPQ!V[H0!!+%cq:>Y:'^Pf#)lWSa!Q,BF!!#RfrrD9K +h#RL&b=r!X*Ld!0IfKK?rr>1\!!(76rrKlEr?VJdk5G;`49,@D@jV'R!@=!*rrJ?H +r?VJS^\n*3L&M&Pbjk]5L&M&Pbl%J@L&CuP--Z5c!M`Nk,lqN<r;R1&!!#.]YpBAM +-):Kor?VGtq#:AZ4T6W-;5=$g"&U?jg&D!U49,@Ys)]Rd!!,4+r;Qhn4=0n*!@>#A +rrN0#J"6N@Du]m)K)^i1")%Z7O+RDIrrF,cb?k8d!:7K%_YO01!0@$X!0m<2!++jh +!-n=k!-nDA!3cA$!;Ys:!<=Ii9`Z7TRf<G=!!)Wj!'L5\!6k*8!V91b!!+Bfrr2s\ +p&G)&qu6]@4S\sWA,ZE-L&M&Pbjk]5L&M&PbkqD@^ErjZ!'L2Z!SKU4!!+C@rVm!H +!!"=0qu?aD4So*YL&:lNUF#a:!BeU*rrC:B!!$O.rraVJ!$sc)!!#.[rr?R(!!#.P +rrN0#J"?TAhZs3YK)^f0"SW`5)#&X^_>aNV,g0Nq(BEg;!8.8M!V[H0!!#C_f)a5c +q>^U/:k,,8^B:*X:^Hmt!*B!J!%@dG!&4?N!9WY(!<=Ii9`Z7TRf<G=!!)Wj"$?P` +-1q3<fd6@m"3gfF4S8[SL&M#PjsBa%!2KPn!/:CP!6k!5!/:CP!6k<>!R)kh!!&8_ +rrM7.q#CFAg&D!R,ldoko)Jb:r;Qb,pAb1Urr2uBrVup\rr2sEp&G30s8Skk!!">E +!!#.PrrN0#J"?TA:]LLAK)^c/"0V\)O+RDJrrF,cb?k8d!:7K$n,<7dO8T%Z#/'ib +!RNt+!!,'Wp&>*fT-4"2!)`UE!3#eq!5JL4!:oL4!<=Ii9`Z7TRf<G=!!)Wj"!mpI +-1q3;4T,6e;3[h%!!">-s8P4[!!GEZbfjSr!!+D.rr2sqrW!'IFG5EGrVupqrr2tP +rVurBnc&TCrVurBq>UGKrVup\rr2s\r;ZmF4=0t,!)`aq!%%LB!'L/Z![V>&rVurB +rr2tnr;ZpGFKo?T!!$O.rrC:B!!%`Prr=AB!!.2+rW!%Ss8R3?!!d#Xg&J<'!/:"D +!WW4MT)SoM!!#iIs,$aW0E;rAs2"^9*@/g8!>bb'^];IRrr=bO!!$@%f)T.T!!+4W +nG`N]&GuM.Er+Af6i?ub;#UCprnd%u!<=Ii9`Z7TRf<G=!!)Wj"!mpI-2%9<L&CuO +U]18q,ldokrr3*`!!"=0r;Qb,rW!%Bs8Tk5!!PLVs8ThrrVuq.rr2tPrVurBnc&TC +rVurBq#:OE!!">-s.fMm$^C\kbU!5h-0G7--2[`DL!9Jq"&\4\FT)7?PlC[_-2mlI +4S/UQL&M&PU]18n^]"35L&V)P-2[`LU]:??!!#mrs'u$.!0mH^!SO7<rrN0#J"HZB +LB%=hK)^]-",?jV^OlL&rrF,cb?k8d!:@Q%hYmHS^\e'3[JSPUY5A8#+Q)Ve!RD>U +!!*;,qpth;r;Zj(kPbD\l0[:-!%,l`!?E2LrrRZM!;$3m,ldokp&>"<rVuq_rVlsG +!!#.\rrY@`!'L,X"sj6L4TGFkrW!&8s.`Hh!!'e5rr@cP!!(75rr@cP!!(7<rr>1\ +!!:CE;>pOqFT)4>FT)7BU]:??r;Zn`s31HB!'L8\!6kEB",6dTPl:X_4T59[L&M&P +bl7VBL&M&PL&V)P-2dfDFT2:E,ldp-s8Skm!!,3WmJd4f!.UU?!^H_sK)^Z,"5a(Y +BS-9$rrF,cb?k8d!:@Q%n,<7d@/U'*0Da9#0DtkO&D-dY#5R`J^Y\\_qYpT2#lO`' +./MNq'`A"3L].5Qo'P66!%,l`!?E2LrrRZM!;$3m,ldokpAY-:rVup\r;QjF!!#.\ +rrY@`!'L,X$TnCh-3*uk!!"=!4T#0[-):G=!/:CP!6k!5!/:CP!6k6<!'L5\#0d)n +!!">-rVllArW!%Ss8ODE!!?a2s4RAO!'L8\!87>O"$HV`4T5<\Z2O\&L&M&Pbl7VB +L&M&PL&V)P-2mlE-3!oIjs:!-4T>?\A,H<-4JVBF!WW4MTDo#^!!$DYs+gUU2uk@Y +s2+d:*@/g8!>bb(^];=Nrr<W/!!%cNf)VuO!!'5#rs\_YaM>TQ!.<VYipZjDrrA,X +!!'2!f)TUb!!"_OrrE#bh#RL&b=r!X*Ld!0IfKK?rrXPI!%%@=!/:CP!/:@N"!mpI +4T>?_49,@DqYpa^!!">Fs!@I@!@<Hsrr@cP!!(75rr@cP!!(7<rr>1\!!UU/,ldok +r;Qc@rW!.Vs8OAF!%%UD"!mpI4T59d,ldp-s8OAF!%%RC!/:CP!6kHB!/:CP!/:FP +!%%UE!+Z$.!6kEB!'L5[!H]Xc!!,sZnc&Xj!.UX@"4$rIYCce0rrRZM!.t6frrF,c +b?k8d!:IW&eG]CI^\n-5#138!!(6\b!*K1!!U%>u^]KStI/Vk%i:[$J!&4?O!(6Y8 +!5/40!8mbT!93G&!<=Ii9`Z7TRf<G=!!)Wj"!mpI4SJdTL&M&PL&CrQ,ldp-rr3'_ +!!#.XrrtRc!%%[F-2RZC-&)0p!/:CP!6k!5!/:CP!6k6<"XO-K-3)3g!!#.ZrrC:B +!!\/Ws!7XF-2miG,ldp-rVm0M!!">Fs!7XF-2dcCL&M&Pbl7VBL&M&SL&_1sr;Zi4 +rr2uBrVup\r;Qi\@jV'R!@?FirrN0#J"Q`C[f?ESeGfS[4L+SZ!jOkAW;ck[n,NC2 +"2@\qg&(dOk#"7UqYpZN4=,^-rr\kn!9\t6_uB`X,g0Nq(BEm=!9!hU!/LLQ!&+6$ +!Q,-?!!(@DrrMZ,r4iAq!.=_#!T*\OrrB8#!!'G(f)QN`!!&qqrrD<Oh#RL&b=r!X +*Ld!0IfKK@rrC:B!!#.TrrA;_!!%`NrrY@`!'L8\"$?P`4T#-`fd-Uu4TGF-r;Zj\ +U\FcgL&M&Pbjk]5A,ZH.bk_8=L&M&V4TGF-!!">CrrAhn!!]4us!7XF-2miG,ldok +rVm0d!!">-s!7XF-2dcC^]"35bl7VBL&M&SL&_1srVusFk5PA\bl.SB4Sf!XKdH]u +!)`=e!WW4MTDo"c!!)34rr?R.!!%`IrrAhn!!&dorrR9B4S/RSk$o_7!!#mnrrY@` +!%%LA!'L5\!5Hn\"1J71c[u27rrF,cb?k8d!:IW&li$h`=T&4"?Mi=SGl7UB;#L=n +bko0WO8s\*h#Q[:rr=bO!!%9@f)S2:!!%9BrrDQVh#RL&b=r!X*Ld!0IfKK@rrC:B +!!#.Xrrh#/U]8R;!!#.ZrrY@`!%%XE"$?P`4T,3[o0!!P#F,8g,ldokU\k&mk*2,0 +rr@cP!!(75rr>1\!!(7>rrAhm!!B"rs!@XE!5JL4$9S:g-0G7-,ldokrVls^!!">D +rr>1\!!:CE4T5<\^]"04bl.SBL&V)PL&M&SL&_1srVupEr;QjF!!">?rrgOl!!">9 +rrN0#J"Q`BIfKJurr>pp!!&8ZrrAhm!!&7arrAhn!!&8VrrJl@q#CC@r;Qblr;Zhm +r;QcMr;Zh-d/O3g!!'ccs24j;*@/g8!>bb(^]=!)rr=bO!!&#Uf)Rr3!!'5$rrDB] +^]KStI/;Y!q>L<nO8T%Y;>\rFEr>t<;#UCoo^:N9!%,l`!?E2LrrRZM!;-9kbl.SB +FT2:Bb`ksNrW!"Rs!@XE!0mK_!L+o0!!B"rs#g8\"XS9*b]G01!!#.\rr@cP!"C6. +g&M)rF<pneU]:@JrVurBnc&SOrVuqnqu6YMr;Zg[rr2tPrW!'IUWgJ8rVupqrr3'H +!!">Drr>1\!!(7Brr@0?!!8qqPl:Xd4Qc\Dfhq_K!'L8\!/:CP",6dTbl.SB-2dcL +,ldoks8R1'FMIhT"MZ5_!%%49!WW4MTDntB!$Kek!SJdt!!'e2rrL=irVup\X8`4R +-2mlEA+op&U\=`g-2dcC;>gIp;>gFoPl1R^-.)YohZ*YkK)`ag!?EH/?NCrCm_Ai" +rVlj'r;Zi*r7:tr#5nN%hu3QTde^`\O8s\)h#Qd>rrBh4!!"5?f)V<=!!"GGrrDl_ +h#RL&b=r!X*Ld!0IfKK@rrBh5!!=N0,piBf!^-Kmr;ZmF4=0n*"&]*u4S8[S^]"04 +-2[`D,piHh"2Fm9L&M&Pbjk]54T,6[-2fq+-2dfE-/&7s!%%C?!@?FurrXPI!%%UD +!-J2?!6kHB!/:CP",6d;-2mlG,s3LN!!C"9s+UFP",6dTbl.SB-2dcC4T5<]bfp"c +r[%LC!)`=e!WW4MTDntB!$Kbj!R)kh!!+D;r;QaZrVusFk,\L^4T,6[4SSjUU\k)n +;2',k!!">Crr=AD!!">Crr>pp!!"=lrr_-Y!5F-c_uB`X,g0Nq(BEp>!65!;!8%,K +!7:Yq!3#hr!#YY6!6"`L!g!D0qV;/1rVlllqu?`Dr7:qVqu?`krr2usmbImD-Hf*a +*?CUK!e11Mo`"odo)Jlts8P4S!!:jR4SJgV-*dCJ!R*\#!!$O.rr@cP!!(75rr?R$ +!!+Cir;Qf4-2@N@U](2p49,@-rVlkOrVuqnrr2u5rW!%1s8Ske!!?*us-3K_#DN3X +js:!-4T,3Z;>pOsbiU5H!!+D!o)Aak!.UX@!e11mdf0<^r;Zg[rVlk-rVusFbcCaD +L&:oN-2@K@bU*5g!@>M[rrZa2!'L5[!Tk^,!!">Crr=AC!!(6jrr_-Y!5F-c_uB`X +,g0Nq(BEp>!6G-=!5JI4!#5=^!0-pW!(d(g!;60'!g!D0q:u&JrVljGr;Zi#r7:qq +qu?`SrVu<A!<=Ii9`Z7TRf<G=!!)Zk!Tk^'!!=MnA(gh\!FmGS!":/`PU-;Uk5V\4 +,s3LP!!4HVk5##XKdHWs![U^KrVlu)!!#.MrrGtCpAb4Vg%t^LbWPe'!BdXbrr[rT +!)`^p!/:CP!/:FP!87>O!)`aq!O4cb!!PK[49,@krr3'_!%$e,rrXPI!)`[o!0mH_ +"53^h4So*[,uN@crrN0#J"Q`BIfLV=rrA;^!!?a2s+UCO!0ljM"5-OKg&D!Q^P0nS +rrLe!qZ$W?q>UFWrVusFbl%JBFCX#&rrBh2!!(7BrrCaL!!'d]rr_-Y!5F-c_uB`X +,g0Nq(BEp>!7(QC!5JI4!%@`r!-8#<!-nJB!9a0n!g!D0q:u&ErVlj_r;ZhkqptfW +r;ZiTrVu<A!<=Ii9`Z7TRf<G=!!)Wj!O4cc,m6>-KtldGrrhJZKnZ<$rrUl-^]"06 +k5YJ[bk1o<g!#/7bk_8>g!&g_"I)"p,lmoj!Frn<rrBh5K`Jm`rrLfsr;Qc3rVurO +rVltA,s9lYrrC:BK`Kg*rrJ@Kr;Qc@r;Qi54Ce6f!b4?trVllNrIk9Ik3r<O!!%M@ +rrRZM+OL,gA,QB.KtmQd!)`Ok!L,_F,lqMjr;Qs`!!#mrF=$nd!BeTlrrP;/;<@fX +4T5<\-2mlEPktC[FT)7@--Z#]!/::M!/:FP!/::M!/8i#"5a(Y^OlL'rrF,cb?k8d +!:R]'d/EtE^\n-42>bu*C]+55J,TBIi;(.iO8s\(h#R0Irr>=_!!&_if)Qcg!!(pT +s6e\D!%,l`!?E2LrrRZM!1*Wck02W\!l'H([/U1-!.UX@!e11mci3u-r;ZgDrVusF +oD8IfbWP\$!+Z!-"=4$J,s;&)!'KWJ!0mH_!6jX+!/:CP!Bc)8!!$O+rrM7.rVuq_ +oD\eQqZ$V+rr2s\qZ$V+dJj=+!!'ccs24j;*@/g8!>bb)^];a[rrBh4!!"nRf)TUb +!!%NIrrD!Q^]KStI/2RupAP!k5lCZ_U&3FA)uTa:hu3TCh#RL&b=r!X*Ld!0IfKJ# +s.95l!!%M@rrRZM+O9ue4Sf$X^\[s2jsBa%!2KPn!%%==!5Iq$!/:CP!6j[,!Tk^- +!!0@KrVupEqu6Y+rVupqo)A\9qZ$aFs8V4-qZ$UYdJj=+!!'ccs24j;*@/g8!>bb) +^];a[rrBh4!!"nRf)TUb!!%NIrrD!Q^]KStI/2RupAP!k5lCZ_U&3FA)uTa:hu3TC +h#RL&b=r!X*Ld!0IfKJ#s.95l!!%M@rrRZM+O9ufbU*/e!0m<Z!)`^q"=8/DKi.gL +!)`aq!%%LB!HaS*!!%`?rr@cP!!(7,rr>pq!!Z=#s!7XF-2[]B-2mlE^[qI*g%k[N +-3+"0rW!'`,ldokdJj=+!!'ccs24j;*@/g8!>bb,^]W$Cn+6MX!5JI4!&aZ*!,MN5 +!.XtI!;H<X!oq&Pq=XgerVlj_r;Zhkqptfer;ZiTrVlunoBYE,h#RL&b=r!X*Ld!0 +IfKJ#s.95l!!%M@rrRZM+O0od;>gIp4Sf!W^]"3:4S/UQ^HDJq!+Z$.!%%OC#,D4u +,ldpBnc&T2r?_FCr?VGcmJd0krW!'Ik5YHDrVur5rVlkmrVup\o)A]SrVus]4T5<^ +^]-Fq!!/<HrVurOdf0F,!!'ccs24j;*@/g8!>bb.^]DaRq>UH0r;ZgTqptg`r;ZhI +jSo3Fr;Zhkqptfer;ZiTq>UKfj7M..!%,l`!?E2LrrRZM!.t6BrrN0#J"Q`BIfLV: +rr?R+!!';#rr?R.!!C"9UJ^ph!5JO5!%%RD!-J5?"!mpI4S&LQjsB[#!6jm2!'L5\ +"B!BsKdH]u!/:CO!+Z!.!0m'S!'L5\!L.^*!!dW.,ldoks'u$.!2J!B"5a(Y^OlL' +rrF,cb?k8d!;=2/_WU[n!5JI4!&aZ*!,MN5!.X)0!'pJ_!29;A!#tk:!8mPN!:BI8 +!<=Ii9`Z7TRf<G=!!%WNT)Sil!.UX@!e11mci4!!q>^OBoDAOk,ldokKfo85!@=N> +rr=AE!!">ErrhI1!!#.Orr>1T!!">6rrM7lo`+tSrVlj[rVurBo)A\9rVuuP^]"36 +4I#gF!`Au^rVuq.df0F,!!'ccs24j;*@/g8!>bb0^]<]orrBh4!!"nRf)TUb!!%N0 +rr>=_!!&_if)Qcg!!(pMrrM`JpY>iM-Hf*a*?CUK!e11MK)_GB!WW4MTDntB!$KYg +!2KJm!B_\-!!#mnrr=A@!!+C"qu6XBrVuq.rr2uBrVup\nG`U7KnX%9!!K(@b_>3J +rr>1R!!">Drr>1\!!(77rrBh5!!ahMs!7XF,piNj!i,e>rVupEdf0F,!!'ccs24j; +*@/g8!>bb1^]<*]rrBh4!!"nRf)TUb!!%N0rr>=_!!&_if)Qcg!!(pLrrDZbh#RL& +b=r!X*Ld!0IfKJ#s.95l!!%M@rrRZM+OU2hU\t/o-1eD?!!&8]rr=AB!!+C"qYpQ> +r;Zi4rr2uBrVup\mJd0?rVurBli-rIquH[A!B_\-!!(7Brr>1\!!'e0rrSsL^]+65 +A,ZH1;?-YYqZ$UBrr3*I!!">-e,KO-!!'ccs24j;*@/g8!>bb2^]DIRr;QeQ+LV7O +5aV6CrrD-Th#RL&b=r!X*Ld!0IfKJ#s.95l!!%M@rrRZM+O^8jbU*5g"=;:ls'u$. +!@>tgrr=AD!!,3Wq#:?<rVusFk5PA\bl.SB4RN.KL&M&Pg$J_=^\n-5UWiZ9!+Z!. +!/:FP!-J2?!)`Xn#j+ta!%$e-s!@XE"-iicFSc%<A,cK.4T5<\UXT5FhZ*YkK)`ag +!?EH/?NCrCpqQmirVlom33iMb0^nu?!;6-C!<=Ii9`Z7TRf<G=!!%WNT)Sil!.UX@ +!e11me,KHm-2mlF--Z>f!'L5\!'L5["=4$J-):A;"m2&6s8U=B!!">CrrXPI!%%%4 +"0hh+-0tR2;>pOq4So'X^]"354T>?\g&1mNA,ZE.KdHZt"3gf--2mlHbl@^rqZ$W2 +rr2tPrVupqe,KO-!!'ccs24j;*@/g8!>bb2^]=91rr?H2!!#:_rrN,Vq:u&O-Hf*a +*?FVK!8"@Q!e11MK)_GB!WW4MnG`L?Z2XlT!$Kek!R)kg!!';%rrBh4!!#mqrr@cP +!":0-g&M)rF<pneU]8R;!!">CrrXPI!%%"3!l'HOm/I+K-2mlEU\Xoi-2mlEg&D!O +A,QB0,s4:9qu?g]s8U=B!!">Drr=AD!!">DrrBh5!!"=orr_-Y!5F-c_uB`X,g0Nq +(BF9H!6kEA!8"@R!8mbT!9a16!<=Ii9`Z7Tnc&Z_+LeKQ!e11MK)_GB!WW4Mo)Acp +0UK'E!e11mec,[4-2dfDU\aujL&CuOA,cK.-2[`D,piHh!l+cZrVupEr;QaZrVurB +ec,VkrVup\q#:>9rVuq?rVlj[p&G)&rr2tPrVupErVlk-r;Zh-r;QaCrVurBeGfX. +!!'ccs24j;*@/g8!>bb3^]<$crr@<B!!#!gIfL>a!!'5$rrD?[h#RL&b=r!X*V9:5 +T-++NXT&?O!!("<!-J2d"$A\f^OlLFrrN0#J+WaDY:oq^n$2loIfLVArr@cO!!&eh +rr>1[!!M!Ts3*V"!!d$6s5kU-!'L2Z!)`^q!6j!n!)`^q!2K>h"Qh!1!'L2Z!'L&W +!BdXcrr@cP!!#.[rrBh4!!'e3rr>1\!!%`&rr_-Y!5F-c_uB`X,g0Nq(BF9H!8.8M +!+>a*!2$e$!+>a*!2'5i!:'C9!<=Ii9`Z7Tp&>&b+T;?@E30'@IfKJgrrAhi!!+C1 +K)ap3!WW4MpAY/s0`D%P:pBs$IfLVArrA;_!!&efrr>1\!!(^NrrIg"qu?dE;6g*" +"!mpI;>gFoPl:X_g"$*)ffUQjpAY5i,pg>'rrJlWr?VJAU\k&k^]"35U\t,o49,A' +qu6YMrVuq_eGfX.!!'ccs24j;*@/g8!>bb3^]<$crr?I*!!&YjrrMB.]u9tDr;Zhi +rVll_q:u&O-Hf*a*?FnS!M:M4!!(?HrrRZM!65$=L%bQJ4P>>dk5PJ_!.XeD!O"3T +!!'4,rrRZM+OpDmKfl.'rr_ji-&)3q"PM"QPg'"(!l$j-qYpV,4L)j)!9WSH!i&Vf +qYpSkbk_8?F<tGGrr_-Y!5F-c_uB`X,g0Nq(BF9H!8.8M!+>a*"/#Vn`Oh'/2#RCS +TDecilM96:!%,l`!?E3VrrJ`7qZ$U5X8`6N!!(%=!/:CP!@9&h!!+C@K)b$6!WW4M +qYpT"0_tbLpTXZ!IfLUCs+^OUhZ*YkK)`ag!?EH/?NCrCq7m!_rVlk*r;Zqls7_Sd +MuY^5!!&YirrD?[h#RL&b=r!X*W,j;T-3q0!'mUb!e11M`r?$mrW!)Fs3-]iqu?`3 +K)b'7!WW4Mr;Qf$0_k\K+KteHIfLUCs+^OUhZ*YkK)`ag!?EH/?NCrCq7m!_rVlk* +r;Znks7!UY!&XWS!2'5i!:'C9!<=Ii9`Z7TrVlnj+SPj9@&s;/IfKJgrr@cP!!(7A +rrJ?1rVupEK)b'7!WW4Mrr3#&0_YPI5d11hIfLUCs+^OUhZ*YkK)`ag!?EH/?NCrC +q7m!_rVlk*r;Znks7!UY!&XWS!2'5i!:'C9!<=Ii9a)OXs8S]6RfLJ.!/:CP!6kB@ +!R)kh!!&8Js8:(@k3`0Lbi\d%!R0^"rrLg+q#:EKbfo2Kr6,0'ir9/^!.Y$P0Ve[O +0W0C#+G0WJrr_-Y!5F-c_uB`X,g0Nq(BF9H!8.8M!+>a*!h]M^\r6VGr;ZhirVll_ +q:u&O-Hf*c*?CapR/l+D!^%c+m/I'>rVurBqu6Y\rVup\qu6]3A,R\S4I#^B!i%k( +qu?aDZ2=P%UJ_":!Ft9irrXPI-/&4r&L@E'Z2aj!,s9l\UHBh&!'Js4rrKAerW!!G +;8;u-!P`.C,lp,mqYpVl4=0n*!@>#2rrW6$="p9I!cn>aK)^T*"5a(Y^OlL'rrF,c +b?k8d!;XD1f_tgM@/U',TE"DlMuY^5!!&YirrD?[h#RL&b=r$Y&eLE1hu<\@rVurB +mJd/`rVurBqYpXD!!#.Zrr@0:!!+CirVlk-p](:VrVloO4So*Z-):J>!2KMn!/:CO +!6kEB#DN3X,ldpB4So*Y;>pLpU\OliA,Q?,FSPn;--Z>f!+Yd(!'K<A!WW3/T)\pk +!$HmnLAqA5!!'ccs24j;*@/g8!>bb3^]<$crr?I*!!8emn@FPY2#RCSTDecilM96: +!%,l`"<ANOIh8%Khu<\1rVurBmJd/KrVurBqu6Y+rVup\rVlk>pAb1Urr2tnqZ$UB +rVup\rr3#]-2@ND-/&=uL&M&PL&M#OL&M&SL&_0!pAb1>rr2u'pAb3;rr2t?pAb1U +rr2tnqZ$UBrVup\j8T5^!.0bDU&Y9,HN51?s+^OUhZ*YkK)`ag!?EH/?NCrCq7m!_ +rVlk*r;Znks7!UY!&XWS!2'5i!:'C9!<=Ii9a2UYs8VP;&G?)(33.N156(]@rr@cP +!!(71rr>1\!!(7ArrM7lr;Zh>rr2u5r;ZstKnXUprW!&Rs8R3?!!d#Xg&J<'!/:FP +!'L2[![TrTrW!$ts8Psq!!&emrr@cP!!@rTs!@RC"XRY)!!">Err=AE!"*5[b]G-2 +!87DP^\n-8;2'^G-2mlHg&M(orW!15bh<$$,liYZrrrH'J,f8()"mq0.'\7)Qfihu ++G0WJrr_-Y!5F-c_uB`X,g0Nq(BF9H!8.8M!+>a*!h]M^\r6VGr;ZhirVll_q:u&O +-Hf*a*?G+Y!q1UMp](:rX8`6N!!)'Z!-J/crW)mC!-Ic2!'L5\"a#HP@lu(9!!+D; +rr2sqrVuq_rr2tPrW!'Ik5YHkrVuq_rVloOU]18nU\t0"PlLd-,ldokk5RRC!!(7A +rr@cP!!@rTs!@UD$&/EZ49,@-s8RfP!!#mprrUl-^]+65;>pOqPlC[_L&M&T-0G7- +A,ZH.Pl:U_fq[R,!WW4Mrr3&oNu7Wl!'%1^!e11mK)^T*"5a(Y^OlL'rrF,cb?k8d +!;XD1f_tgM@/U',TE"DlMuY^5!!&YirrD?[h#RL&b=r!X*W,j<msboF!!#!ZrrRZM +!9F.\jsB[#!6k$6!'KoS!@>thrs7a5!!#mrk&_pJ!!#mqrrAhm!!,3WqYpOmrVuq? +rVlk>rW!"ps#g8\!6kEA!/:CP",6dT-2mlEPlC[i@fQKks8OAF!%$e&rs7a5!!#mr +k&_pJ!!#mqrrAhm!!,3WhZ!WW!.XqH!r%`mq>^L4YQ"ZR!$HmnLAqA5!!'ccs24j; +*@/g8!>bb3^]<$crr?I*!!8emn@FPY2#RCSTDecilM96:!%,l`!?E3VrrVY=&GuM/ +&GN:+!e11Mj8T*Ap&G(=nc&SOpAb7@A*3ai!2KMn!d%ouqu?^or;Qb,qu?a[U\t,p +,ldokk5G;[bl.SDL&X:7!!(7Arr@cP!!^4<s!7XF4T,3^@o:qZU](5nFSGe8U](5p +FCQWp!!#morr?R,!!,3Wi;WiY!.XkF!r%`mqZ$WZYl=cS!$HmnLAqA5!!'ccs24j; +*@/g8!>bb3^]<$crr?I*!!8emn@FPY2#RCSTDecilM96:!%,l`!?E3TrrVY=&H)S/ +YH7a*IfKK.rr^K!Kk()^"Ja2bL$%q*!'L#V!@>#Jrr@cJ!!,s3qYpS<-2[`K;<IoY +js:!--2dcCbl.SDL&X:7!!(7Arr@cP!!^[Is!7XF4S\pVA,ZH.g%YLHL%kWK;8;u- +!H]Xc!!,sZir9&[!.XeD!r%`mr;ZhYYl=cS!$K\h!R.><K`Rt2lMgs?,s9D's4I>R +hZ*YkK)`ag!?EH/?NCrCq7m!_rVlk*r;Znks7!UY!&XWS!2'5i!:'C9!<=Ii9`Z7T +p&>3aIh2S[:osZuIfKK+rr@cP!!(71rr>1\!!">B!!+Bfr;QbNqZ$[D;8;l*!p3u= +r;ZpGg&K:q!!#.ZrrC:B!!7lSFT)7?bl.PAL&M&Vbl@\h!!#.VrrY@`!%%@=!/::M +![U^Yp\t<W@jV'R!@?FZrrN0#J+imGpQ$-k!&1YW!e11mdf0;EqZ$[D;8;Jt"!mpI +4T,3\k(UR7rrTrhg!Tg%hZ*YkK)`ag!?EH/?NCrCq7m!_rVlkBr;Zmq^U1Rc!$D.> +!2oeq!:'C9!<=Ii9`Z7ToD\s^Ih2nSXoAHP!!(pV!/:CP!86c>!'L5\"52@;-2[`D +4PB`6!/:@O![V@=o)A`E-2mlK;?-YB!!">CrrAhn!!8qqL&M&P^]"04L&M&Vbl@\h +!!#.VrrY@`!%%@=!/:@O![V@=o)A`E-2mlE;<.ZX!!%N@rri(W(]`0mrrRZM+Og>k +jsBd&!-IW."!mpI4T59_fd-UuFI)q,Pl:X_Z-rXShZ*YkK)`ag!?EH/?NCrCq7m!Y +rVllDXoJIJrVllYq:u&O-Hf*a*?F\M!q1W:XoAHP!!(pV"0hh+-1(X34T5<abl@_* +@jV'R"=:>Qs.fPn!'L,X!l'HBq#:TC,ldoks8ODE!!'e4rrtRc!%$e-Pl:X_L&M#O +L&M&Vbl@\h!!#.Vrr@0?!!&ekrrgR!s8Skn!!#.XrrTrhg%bRMbU!5h-0,",!!%N> +rrVqUO0S]dIfLV?rrC::!!$NsrrXPI!'L5[!/:CP!%!s2!'L5\!)_2E"5a(Y^OlL' +rrF,cb?k8d!;XD2_YsH5!'mag!$M4>!9!\/!<=Ii9`Z7TRf<G=!!(mU!l'HOm/I&J +rVurBrVloO4T#0]^]4<[rW!I+^]4>rUJV!k4TGG'4ES@;rs-:b!!">Fs+UFP"=9he +^HDJq"&]*ubl.SG4MT+TKfo>7#0d,I,ldp-q#:?/rVusFPlC[fUHAMVZ2ahMrW!I+ +^]4>rUJV!k4TGG'4ES@;rrg(_!!">*rrN0#J"Q`BIfLV?rrC:B!!IDfbbI<!!!':m +rrXPI!'L5[!2KMn!)]'^!+Z!.!0kq3"5a(Y^OlL'rrF,cb?k8d!;O>0j8JuYh[$Lf +!>+/errD]dh#RL&b=r!X*Ld!0IfKJgrr>1\!!(7?rr?R-!!>@`s+U@Nr[%LC!^-M, +r;cgCr;Zgprr2sEp](=@g&D!O-2%<CPlLb0!!#.Urr>pp!<+;C!!@rTs+U@Nr[%LC +!^-M,r;cgCr;Zgpj8T/\!.UX@!e11me,KElrVuqPrVlk-rVup\n,EJ9!!#.ZrrSrX +A!HupKfk(:rr_-Y!5F-c_uB`X,g0Nq(BF6G!6kB@!S9<=!!3F.fDPXKj7qF2!%,l` +!?E2LrrRZM!65$=4T5<\bkh>>U](5n4T>?\FS>b<--ZDO-27H@-/&:t!P]rV!!&em +rr@07!!\/Ws!7XF4SJdT4SSmW-/&:t!-Ir8"=;:ljsBd&!@?FZrrN0#J"Q`BIfLV? +rrA;_!!%`NrrY@`!%%+6"!mpI4G*Ucrr_-Y!5F-c_uB`X,g0Nq(BF3F!9O+X!T-HH +!!@G[^UM1U!+c$.!I+#0rrDQ_h#RL&b=r!X*Ld!0IfKJgrr]MP!'L&V"0jsNbl.PC +^JQ<T!!4HgoD\aj^HDAn![U^sr;QfA4T#0[4L+e`!H]Xc!!">E!!\\fs!7XFFS5Y7 +F=$hb!@<HsrrTr4-2[`E-$8bXrrKksqZ$[D;;'t/!WW4MTDntB!$Kbj!/:CP!5JI3 +"$?P`-1Cj9,ldp-nG`R6bi[LV!TqW(rrLfsqYpTLbl.PCKfk(grr^r=UZVLS!jOjt +rr3)_UQjI'rrQ[mZ2Xb)b_<ggrr_-Y!5F-c_uB`X,g0Nq(BF3F!Q+O,rr?I*!!Akn +s7F:^!/CFP!2'/g!;-!@!<=Ii9`Z7TRf<G=!!("<!TqVmrrgQ@Ktl=:rrCaNK`Rt2 +q#:?/re1?ep\tN6KnZ;Ts)]Psrr3&7;0;C/"J^ZJKtm?]"O*Wp^Y/G_!87=)!Mef8 +rrN0#J"Q`BIfLVWrrZaW4Qc&2!/:CP!6kB@"!mpI-2dcIk&`^3,s7t"rrXPI!'L5[ +"2?-.A,?30@jM*T,pe9DrrQ%DFSYq=YpC]koD]EU4=-d&^JQ:'!%"E?s+LFQA,Q?2 +K`D*8s8Skn!!9pZ4T5<]-$9.d#Nd<4!/:#A-2mlE;>gFrKdA%?rr314!!">FUF#m> +!@=N:rrJm:r]C48k3r<PhZ*YkK)`ag!?EH/?NCrCp:p^Op&+gi@/U'-TE"r``Rb*E +r;Zhir;Qcdp"]WK-Hf*a*?CUK!e11MK)_GB!WW4MTDntB!$LY.!87>O!/9h?!/:CP +!6kEA!-J2?!)`^p!JMis!!#.ZrrXPI!'L5["?ZYa-0G.*!'L)X"!u1kA,ZH.Z24J& +,ldokoD\qj!!"<BqZ$gHs8OAF!%%RC#!;kc-3+"!rVus]-2RZEU]:A<rVusF-2RZB +A,ZE0,ldokrr2s\rVup\qZ$XCk5>5[KdHTr!@<Hhrr_-Y!5F-c_uB`X,g0Nq(BF*C +!:0U`!+>a*"/#VnoYoD^LA_)PTDecimI]38!%,l`!?E2LrrRZM!.t6BrrN0#J"Q`B +IfLVXrrC:B!!%`?rr@cP!!LOGs4Lo\!!'e5rr@09!!$O-rrXPI!'L5[!'L5\!6kEA +!'L#V!^-L)rVusFoDJUgL&M&P4S/RQ4SAaYbl8sh!%%RC"sj6L-3+"!p&G1Ws8U=: +!!">DrrXPI!%%XE!'KrT!-J2>!'KuU!'KfO"5a(Y^OlL'rrF,cb?k8d!;+&,kPbD\ +@/U'-TE"r``Rb*Er;ZhirVll_o\BNJ-Hf*a*?CUK!e11MK)_GB!WW4MTDntB!$LY. +!6kEB!/9h?!'L5\!^&Rkqu?_,rr2u'r;ZmF4=0q+!5JO5"!mpI4T59^49,@-rVlkO +rW!9OUWfJq!!#mrjsC!,!+Ys,!%%UE!2K/c!'L/Z![U]@rVuuCg&:sO-2dcI,ldok +s8RfM!!k]^49,@-s8U=?!!4I2A,ZH.bl7VE,ldp-rr2s\qZ$e0UEom?4T>?\4T,6^ +4GAJdrVupEnc&^K!!'ccs24j;*@/g8!>bb.^]<Qrrr?I*!!@G[^UM1U!+l*/!2'5i +!:'44!<=Ii9`Z7TRf<G=!!%WNT)Sil!.UX@!e11mnG`Na4T6Z+!<"2D;=XYd4SJgU +A,ZE-4T5<`4Qc\DA,ZH.A,cK1,ldp-rVls^!!">Drr=AE!!'e4rrJ@Krr2tPr;Zi4 +rr2tPrVupEo)A\Pr;Zq0oDaOD!!1<frVup\r;QsI!!">Fs+U@N"$HV`L&M&Rg&K:o +!!=Oks31HB!0mK_"!mpI4T>?\4T#0cA,lQk!!">Fs-3K_!@>MZrrXPI!%%18"5a(Y +^OlL'rrF,cb?k8d!;+&,kPbD\@(61:TDecilL`m5!%,l`!?E2LrrRZM!.t6BrrN0# +J"Q`BIfLV\rr=A<!!%`Drr>1W!!+C1rVllNrVusFk5G;^,ldokrr3'H!!#.[rrXPI +!%%XE!/:CP!-Ir7!'L5\!)`aq!%%UE!/:"D!'L5\!+Z$."eu%t-0EGN!!#.ZrrsbL +!%%[FL&CuS-0G7-L&M&Rbl>ob!!BM+s31HB",6dTbl.SB4T>?\4T,6[4T>?b49,@- +s8ODE!!'e3rrXPI!%%18"5a(Y^OlL'rrF,cb?k8d!;+&,kPbD\@(61:TDecilL`m5 +!%,l`!?E2LrrRZM!.t6BrrN0#J"Q`BIfLV\rr@06!!'e)rr>1\!<,(]A#&r$!/:CP +!+Ys,"$?P`-3!oH,ldp-rVlsG!!">ErrXPI!%%=<!3uJ&!l+bhrVupEnc&_S!!">: +rVm"S4L+q1rVup\r;QsI!!#.]s+UCO!/:FP!+Z!.!mL\gr;Zgprr2tnrW!%Ss8U=B +!!#.\rr>1[!!'e5rrj\K!%%ZurVup\r;Qo^,ldp-nc&^K!!'ccs24j;*@/g8!>bb. +^]<Qrrr?I*!!Akns7F:^!/CFP!2'5i!:'44!<=Ii9`Z7TRf<G=!!%WNT)Sil!.UX@ +!e11mn,ELCP_Fhj!!IDfb`om4rrY@`!%%@=!+Z!.!2KJl"$?P`-3!oH,ldp-rVm3N +!!">Fs5kU-!)`Cg!)`^q!^-K/rVuqPnc&\R!!">?rrC:B!!#.ZrrsbL!'L;]L&M&Q +-0G4,!'L5\!mL\gr;Zhmrr2tPrW!%Ss8U=B!!#.\rr>1\!!">Drrj\K!%%Z?rVuqP +r;Qc@rVuq.nc&^K!!'ccs24j;*@/g8!>bb.^]<Qrrr?I*!!Akns7F:^!/CFP!2'5i +!:'44!<=Ii9`Z7TRf<G=!!%WNT)Sil!.UX@!e11mm/I(0rVuqPmJd87!!#.Trr>1\ +!!(7@rrY@`!%%XE"!mpI4T59a,ldoks8U=B!!%`FrrBh4!!#mq!!">7rrY@`!%%C> +"!mpI4T,3`,ldp-s8P4\!!#.[rr>1\!!:CEL&M&P-2miDL&M&SL&_1srVup\rr2sE +rVuq?rVm$I!!">FL&M&PL&CrNL&M&PL%50FhZ*YkK)`ag!?EH/?NCrCoY:IirVlk* +r;Zqls8V`1f)UR(!!&YirrD?Vh#RL&b=r!X*Ld!0IfKJ#s.95l!!%M@rrRZM+RK+. +bl.SBPjSJQ,ldp-pAY,HrVuqnr;QjF!!">ErrXPI!'L5["sj6L-3+"hrVuqPoD\f# +q>^MLnG`SQ!!">>rrXPI!'L2Z"sj6L4TGFDrVuq.rVlj[rW!#Ds+UFP!%%UD!/:CP +",6dTbl.SB4T>?\-2mlEU](2r,ldoks+UFP!/:@N!'L5\!2K)a"5a(Y^OlL'rrF,c +b?k8d!;+&,kPbD\@/U'-TE"r``Rb*Er;ZhirVll_o\BNJ-Hf*a*?CUK!e11MK)_GB +!WW4MTDntB!$LY."Qh!1!86c>"!mpI4SJdTU](5n;>pLpPl:X_A,cK1,ldp-rVlsG +!!">ErrXPI!'L/Y!TqW'rrM7.qu?^Cn,EJP!!">>rrXPI!%%RC"sj6L4TGFDrVuqP +rVlkOrW!"as+UFP!'L5[!6kEB"&]*ubl.SB4T>?\-2mlEbl.PA4T5<]ft[Ld!+Z!- +!0mE^!6k!5"5a(Y^OlL'rrF,cb?k8d!;+&,kPbD\@/U'-TE"r``Rb*Er;ZhirVll_ +o\BNJ-Hf*a*?CUK!e11MK)_GB!WW4MTDntB!$LV-!p53Om/I/6!!#.Srr=AE!!?`T +b_>uq!5JO5"!mpI4T59^,ldokrr2sqrW!4$g&M*7@jM+;qu6Y+qu?_Nn,EJP!!">> +rr>1\!!J#"b`m5K!!>@`s#g8\!/:CO!/:CP!d+HrrVup\rVllArW!$_s8U=B!!#.\ +rr=AE!!(7Arr>1\!"$F?,ldok^]4=uqu?`@nc&^K!!'ccs24j;*@/g8!>bb.^]<Qr +rr?I*!!Akns7F:^!/CFP!2'5i!:'44!<=Ii9`Z7TRf<G=!!(^P!P_M1,lqN<jo5>P +rr3"/;;M6Rjuh>orrg)64?S>jrrM8(r]C1Fo)Acr4I#aC"MZ89s7?)@!5I7f!WW4M +TDntB!$Kbj"!mpI4SA^SL%bQIA,ZE0,ldp-rVlsG!!">ErrBh3!<+;D!!">BrrM^; +rVusFk3r<P49,@-p\t58p&G1@s8P4\!!&8^rrC:B!!5:_L&M&P;>gFu,ldoks8U=B +!!#.\rr=AE!!(^Nrr@cP!!7lSFSu1?,piEg!86oB"5a(Y^OlL'rrF,cb?k8d!;+&, +kPbD\@/U'-:Odk?`G5H@r;ZhirVll_o\BNJ-Hf*a*?CUK!e11MgA_5L!!*!\"!mpn +^Zb\!o/qa+rrG5.h>[O=!6kB@#GV8F4?NU+oD&=jYpBB44='tio`#$b,liYrrrdEi +s8U=>!!%`,rrN0#J"Q`BIfLV?rrXPI!'KoR!)`Ol!'L2Z"!mpI4T59^,ldokrVlkm +pAb2'qYpOXrVuq.mf*AO!!">>rrM7.pAb:As8QU.!!(7ArrCaO!!5:_U](5nL&CrT +,ldoks8UdO!!#.\rrXPI!%%RC!0mH_",6dT4Sf$[,ldoknG`UJ!!'ccs24j;*@/g8 +!>bb.^]<Qrrr?H:!!&YirrD?Vh#RL&b=r!X*Ld!0IfKK'rr_C0-,9EY!gE[2kl1_. +!/:FP!SP]SrrhIH!!(7Ars7a54S/UQUB"g!rrOJm^]+67@fU$3rrZ*u!/:=M"smdZ +s+LHJr6,04gA_3S!.UX@!e11me,KNF,pfhfrrI3fr;ZjEA,H9.49,AVrVltR,pd[2 +rrKksqu?dE;8;u-!87>O!@?mrrrZ*u!'KuT!R*\(!!aer!!">Fs-3K_!87;M"XO-K +;?,>K!!&8]rrZ*u!%%XE"&T%EZ2Xb*;#gSBr;QcMrVuq_rr3"@-2dfH;02d<-1Lp: +hZ*YkK)`ag!?EH/?NCrCoY:IirVlk*\,ZN$rVll_o\BNJ-Hf*a*?CUK!e11Mg]%>> +!-J,<!`8s4l2Lh/!/92-"dC;5493V(rrR9BU](2o4=0.drrSqqFSu.>,pi9b"I&mK +!/:@N#Nd>-s8PqBk1'D4!!%M@rrRZM+Me!\k*1RRPihfA!R06orrM9Eq>UWOKlggh +k4nrVA,ZH.A*s9uk$qoSo`#6NP_Fh8s3*Sf^]+67F?Hi*rrTGfA,cK0PWXZsrr^qO +;;(pJ!87;M"6NHXg&(dNKfk(drs6AnL"ZJg!!">7rr_-Y!5F-c_uB`X,g0Nq(BF*C +!9a=\!+>a*"/#VnoYoD^LA_)PTDecilL`m5!%,l`!?E2LrrRZM!8IMTK`H5lrrTrh +^]"09UHBhbA(ge[&E[Ib,s7t&s'n.k,s7Fl@o<4)"m0;W,s7Fjrs+ccs._\&4I#U? +#0['0^P)[3rVlm\-2dcEK`InErrTrWPl1O^4=0b%"kb23K`Hi)rrJ?1rr3!F-.Dkq +!!%M@rrRZM+Hla*jsC!,!83M7"!mpI4Ri@QhZ*YkK)`ag!?EH/?NCrCoY:IirVlk* +r;Zqls8V`1f)UR(!!&YirrD?Vh#RL&b=r!X*Ld!0IfKK'rr^pS-):26#@d`[4?Oo9 +U]19*YpBB44='u;s'n.k,s7Fl4=1",#GWRF4?Oni4T>?dKdC4T4?OniFSPk?fq\TI +K`K?qrrFDlr;Qi5!/:%E!p1dTp\tCZ4TC*8L&CrO;*=jX"slCH@m"89f`)!Q!.UX@ +!dF\NP5bN9rVupqOoGEVrVuq.nG`U:!!'ccs24j;*@/g8!>bb/^]DaTrVlk*r;Zql +s8V`1f)UR(!!&YirrMiOp"]WK-Hf*a*?CUK!e11MgA_A!!!"<-L!9Go!egVarr38J +,ph^Rs!8u+rr31"!5JQX!/:FP#<X=4s5kUR^]+6B@fU$=s+LG!bl@^e,pi3`!egWu +rVlmE4T,3\bQ(N3rrQ[1L%tZPF9')UK`Hi)rsU3-s8U:C,lf5;!!%,orrN0#J"Q`C +QN.$KPQ(V/rVuqnR/[01r;Qf4-2mlE^[V7*[f?ESK)`ag!?EH/?NCrCp:p[\qu6Y( +r;Zqls8V`1f)UR(!!&YhrrN&[p>#`L-Hf*a*?CUK!e11Mg&D,g;'l2A!@;7SrrOJm +oDS[nUB$PYUB#E7rs$5#Z2`#JL&V)V4=1%-PQ6F8rrsbqk5U,8Pl:U`493UurrRlS +bl.PB,s;,*!mCX,o`"u&!'L&V#JU7ms8RcQL&M#]bQ->rs-*LG^]4>K,lj^orrN0# +J"Q`C[f?ESPlC`urVupERK!EC,lf5;r]L,Z!'K`M".oPnkCW`OrrF,cb?k8d!;F80 +b5M5;!+>a*"/#VnoYoD^LA_)PTDSWhrSdM,!<=Ii9`Z7TRf<G=!!(UM$GU[F,lg(! +s5kUir6,A?!'L:'!5J@0!mCXurr30b-3+!-!6kB@#%IYWs+LHsrVlqQ!6k-9!egWu +rVlmE4T,3\bQ'currQ[14SSjV4?WWC!egW.rVln?-3!oFo?@.4!l"^tgA_3S!.UX@ +"4$rIYE]%eFT)7?A"!>sL%bQJ--YiX!dF\FK)`^f!?EH/?NCrCpV6e%r;QiULEZTr +""6E"4iK8Y,5hKC18!b-rrMuQptYrN-Hf*a*?CUK!e11Me,KU649/mkbkM/@4T@MD +bk_8?bQ*@rrrkM2s8P2-qu6kS!/:H,!6kEA!egWup&>)I!6kEA!@9l+rrUCEFS>_9 +;#i`QrrQ[Vbl7VDK`Hi*rrG5ZpAY.>-//A#!!%M@rr_]i!+;&k!+Z!.!83e?!SJdp +!!,48mf*>:!%<I!_Z'WW,g0Nq(BF6G!7^rH!M9q1!!*@\r;Qc`ptYrN-Hf*a*?CUK +!e11Mg]%:aA,?30js;>RbQ'd%bQQW!;#ni9rrUCEL&V)V4=1%-4=0.frs"/WU]6#J +bl.PCK`K?irrRlSbl.PB,piKh!i#aLq#:HY49/mbrrFDGr$;RJ,pe9Fjs?errrFDl +gA_3S!.UU?"$chtpQY[Z;'g=MrrJm:r]C6ZL"Ykn"5a(YG_5t4rrF,cb?k8d!;O>0 +oDS[hVm$.$TDeciqY8kI!%,l`!?E2LrrRZM!8IMT,ph^Mrrj\ps8OAkqu6iQs8RcQ +U\t,q;3_+)!/:FP#!=43s'l&Dr;Qsu!6kIs!6kEA!egWup&>)I!6kEA!^%dkrVlq@ +!5J@0"7mf=FS5Y6;>`N^,lf5RPlIL)k5PA^@m&oOrrFDlgA_3S!.UU?".&uf[t=X: +rr\#V!5F-c_Z'WW,g0Nq(BF9H!6G-=!V[G8!!*AorVllWq:u&O-Hf*a*?CUK!e11M +g]%@c!'KlNrs+5XA,lQk!5JL4#)*%es1\P2rVm(s!-J7b!/:FP#!=43s.]R(rVm,b +,piTkK`K?qrrRlSbk:u;K`K?qrrSDbL&M#P,pi?d!^$IXmf*?B!/:FP!Fn7jrrQ[1 +L&M#QPQ54IrrN0#J"HZBk5YKYK)^`."8=JL(kVe(rrF,cb?k8d!;XD1f)>UKVlg"" +\,H=,kP<p7!%,l`!?E2LrrRZM!8@H&49-[L^W`HM!%$e-s1\O[U]:@Y!%$e-s%rc+ +k5V\4-0G6O!%#D5s#_V,rs_'jA*3g+491WGs+LHsrVlqQ!6k-9!egWur;R!J!0mNG +491*7rs+cNPlLcJ!%%Jqrr3!r;>L4nK`Hi+rrFEfrVm2d,lj20s-*L0k1]h:!!%M> +rr[3?!7-8sMuNm?!!&(3s2"^9*@/g8!>bb3^]<$crr?I*!!8Men@FPY2#RCSTDeci +lM96:!%,l`!?E2LrrRZM!87AQ;'l,?!E%PKrs,;F!$rok--Z>f##P@H,lf78rr30b +!!%-@4=0t+##P@H,lf6Urr3%R!6kEA!egWup&>)I!6kB@#0]10,p`P$rVm%T!/:IQ +4S\sW-3!oF,piBe#DE/3s8UaPbl%JG^Eik+,lg'Og&D*R!.UR>"6Tpi:kJ_+rri'5 +!#YH^s2"^9*@/g8!>bb3^]<$crr?I*!!8emn@FPY2#RCSTDecilM96:!%,l`!?E2L +rrRZM!8.;Tk+dWaPhGm4"2BPD^\[s4fnG-Tqu6i7PhH)ibl%JCfnG-Tr;Qfhbl%JA +o??k,!V=P2rr]$ML"ZD("nTt0s8UdIbl7VCbfon_#4p(1s8Tifq>UQ3Kn[:nrrN0# +J"6N@Du]m!K)^i1"(2*/YCceirrF,cb?k8d!;XD1f_tgM@/U',TE"DlMuY^5!!&Yi +rrD?[h#RL&b=r!X*Ld!0IfKJ#s.95l!!%M=rrhL-!!'K[s,[0^NrT/FK)`Uc!?EH/ +?NCrCq7m!_rVlk*r;Znks7!UY!&XWS!2'5i!:'C9!<=Ii9`Z7TRf<G=!!%WNT)Sil +!.UL<"O@>R&C5t.OoGQf!!"/1K)`Uc!?EH/?NCrCq7m!_rVlk*r;Znks7!UY!&XWS +!2'5i!:'C9!<=Ii9`Z7TRf<G=!!%WNT)Sil!.UI;"Kqe*#cE:SPQ(c@!!!;VK)`Rb +!?EH/?NCrCq7m!_rVlk*r;Znks7!UY!&XWS!2'5i!:'C9!<=Ii9`Z7TRf<G=!!%WN +T)Sil!.UF:!13Zb!Isiqs-N`hmoTPi&@[8k^&J*R,g0Nq(BF9H!8.8M!+>a*!h]M^ +\r6VGr;ZhirVll_q:u&O-Hf*a*?CUK!e11MK)_GB!WW4MR/[?)&-)\IT7[*3rs&4I +&-)\Yf7O%8rrF,cb?k8d!;XD1f_tgM@/U',TE"DlMuY^5!!&YirrD?[h#RL&b=r!X +*Ld!0IfKJ#s.95l!!%M8rrM"*rW!!BE6j.9TDnu%BGg^K!D)[2s1A:3*@/g8!>bb3 +^]<$crr?I*!!8emn@FPY2#RCSTDecilM96:!%,l`!?E2LrrRZM!.t6BrrN0#J!L$7 +T-4(4"!D!$ItGG6^BBUIIo$^T!!+dgK)`C]!?EH/?NCrCq7m!_rVlk*r;Znks7!UY +!&XWS!2'5i!:'C9!<=Ii9`Z7TRf<G=!!%WNT)Sil!.U75!r%HuJcM8?!AL^/s0r"/ +*@/g8!>bb3^]<$crr?I*!!8emn@FPY2#RCSTDecilM96:!%,l`!?E2LrrRZM!.t6B +rrN0#J!'a5f7-%FJcMSH":.7`Qf!Dp[Jp7J,g0Nq(BF9H!8.8M!+>a*!h]M^\r6VG +r;ZhirVll_q:u&O-Hf*a*?CUK!e11MK)_GB!WW4MK)^H&K)^H&h>[Kr,g0Nq(BF9H +!8.8M!+>a*"/#VnV7VZd2#RCSTDecilM96:!%,l`!?E2LrrRZM!.t6BrrN0#ItI]P +s+:9&s5!\U*@/g8!>bb3^]<$crr?I*!!Jqos81$QW;o0]!!&YirrD?[h#RL&b=r!X +*Ld!0IfKJ#s.95l!!%M#s+:9&s+::,rrF,cb?k8d!;XD1f_tgM@/U'*T=Fn$@/U'* +TDecilM96:!%,l`!?E2LrrRZM!.t6BrrN0#ItI]Ps+:9&s5!\U*@/g8!>bb3^]<$c +rr?I*!!&(irr?I*!!&YirrD?[h#RL&b=r!X*Ld!0IfKJ#s.95l!!%M#s+:9&s+::, +rrF,cb?k8d!;XD1f)>UKVlg""\,H=,l1s-9!%,l`!?E2LrrRZM!.o]lIf]TMItI]P +s+:9&s5!\U*@/g8!>bb3^];ISrrMj2YQ+\0n,<7djS@U4!%,l`!?E2Lrr@h+!1Elf +ItI]Ps+:9&s5!\U*@/g8!>bb2^]=!)rrA[q!!&YirrDulh#RL&b=r!X*Ld!/f,0)> +S,`R,c[u1Ks+:9&s5!\U*@/g8!>bb2^];m^rrJ/dZiC+4L]%/PnG(f?!%,l`!?E24 +s+:9&s+:9&s+:90rrF,cb?k8d!;F8/p&+gkhg\2,5QXKKkPY>\r87;*!<=Ii9`Z7T +K)^H&K)^H&K)^H&N;io!,g0Nq(BF3F!QtA@rrN,^pY>iM-Hf*a*?Bb3K)^H&K)^H& +K)^f0!?EH/?NCrCp:p[\X8`5"kP!^4!%,l`!?E24s+:9&s+:9&s+:90rrF,cb?k8d +!;4,.deBpE!Vk[Ih#RL&b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8!>bb.^]MC0pTjf" +p?;,(!<=Ii9`Z7TK)^H&K)^H&K)^H&N;io!,g0Nq(BF!@!QrpJhu^uM^>A8Zl/pjm +h#RL&b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8!>baZ^]KStI*:=H!%,l`!?E24s+:9& +s+:9&s+:90rrF,cb?k8d!6`.ZO8s[Oh#RL&b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8 +!>baZ^]KStI*:=H!%,l`!?E24s+:9&s+:9&s+:90rrF,cb?k8d!6`.ZO8s[Oh#RL& +b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8!>baZ^]KStI*:=H!%,l`!?E24s+:9&s+:9& +s+:90rrF,cb?k8d!6`.ZO8s[Oh#RL&b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8!>baZ +^]KStI*:=H!%,l`!?E24s+:9&s+:9&s+:90rrF,cb?k8d!6`.ZO8s[Oh#RL&b=r!X +*J4<Cs+:9&s+:9&s,?sY*@/g8!>baZ^]KStI*:=H!%,l`!?E24s+:9&s+:9&s+:90 +rrF,cb?k8d!6`.ZO8s[Oh#RL&b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8!>baZ^]KSt +I*:=H!%,l`!?E24s+:9&s+:9&s+:90rrF,cb?k8d!6`.ZO8s[Oh#RL&b=r!X*J4<C +s+:9&s+:9&s,?sY*@/g8!>baZ^]KStI*:=H!%,l`!?E24s+:9&s+:9&s+:90rrF,c +b?k8d!6`.ZO8s[Oh#RL&b=r!X*J4<Cs+:9&s+:9&s,?sY*@/g8!>baZ^]KStI*:=H +!%,l`!?E24s+:9&s+:9&s+:90rr=Bt!1s5k*eOEDs+:9&s+:9&s,?sXYKD=qDbnQ+ +!ddFabC9OkB?G](YCce+s+:9&s+:9Grr +~> +grestore +currentdict /inputf undef +currentdict /pstr undef diff --git a/docs/figs/xenlogo.eps b/docs/figs/xenlogo.eps new file mode 100644 index 0000000..aa5f2f9 --- /dev/null +++ b/docs/figs/xenlogo.eps @@ -0,0 +1,1479 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: (ImageMagick) +%%Title: (/homes/kaf24/xenlogo.eps) +%%CreationDate: (Tue Oct 28 13:52:11 2003) +%%BoundingBox: 155 324 445 461 +%%DocumentData: Clean7Bit +%%LanguageLevel: 1 +%%Pages: 1 +%%EndComments + +%%BeginDefaults +%%EndDefaults + +%%BeginProlog +% +% Display a color image. The image is displayed in color on +% Postscript viewers or printers that support color, otherwise +% it is displayed as grayscale. +% +/DirectClassPacket +{ + % + % Get a DirectClass packet. + % + % Parameters: + % red. + % green. + % blue. + % length: number of pixels minus one of this color (optional). + % + currentfile color_packet readhexstring pop pop + compression 0 eq + { + /number_pixels 3 def + } + { + currentfile byte readhexstring pop 0 get + /number_pixels exch 1 add 3 mul def + } ifelse + 0 3 number_pixels 1 sub + { + pixels exch color_packet putinterval + } for + pixels 0 number_pixels getinterval +} bind def + +/DirectClassImage +{ + % + % Display a DirectClass image. + % + systemdict /colorimage known + { + columns rows 8 + [ + columns 0 0 + rows neg 0 rows + ] + { DirectClassPacket } false 3 colorimage + } + { + % + % No colorimage operator; convert to grayscale. + % + columns rows 8 + [ + columns 0 0 + rows neg 0 rows + ] + { GrayDirectClassPacket } image + } ifelse +} bind def + +/GrayDirectClassPacket +{ + % + % Get a DirectClass packet; convert to grayscale. + % + % Parameters: + % red + % green + % blue + % length: number of pixels minus one of this color (optional). + % + currentfile color_packet readhexstring pop pop + color_packet 0 get 0.299 mul + color_packet 1 get 0.587 mul add + color_packet 2 get 0.114 mul add + cvi + /gray_packet exch def + compression 0 eq + { + /number_pixels 1 def + } + { + currentfile byte readhexstring pop 0 get + /number_pixels exch 1 add def + } ifelse + 0 1 number_pixels 1 sub + { + pixels exch gray_packet put + } for + pixels 0 number_pixels getinterval +} bind def + +/GrayPseudoClassPacket +{ + % + % Get a PseudoClass packet; convert to grayscale. + % + % Parameters: + % index: index into the colormap. + % length: number of pixels minus one of this color (optional). + % + currentfile byte readhexstring pop 0 get + /offset exch 3 mul def + /color_packet colormap offset 3 getinterval def + color_packet 0 get 0.299 mul + color_packet 1 get 0.587 mul add + color_packet 2 get 0.114 mul add + cvi + /gray_packet exch def + compression 0 eq + { + /number_pixels 1 def + } + { + currentfile byte readhexstring pop 0 get + /number_pixels exch 1 add def + } ifelse + 0 1 number_pixels 1 sub + { + pixels exch gray_packet put + } for + pixels 0 number_pixels getinterval +} bind def + +/PseudoClassPacket +{ + % + % Get a PseudoClass packet. + % + % Parameters: + % index: index into the colormap. + % length: number of pixels minus one of this color (optional). + % + currentfile byte readhexstring pop 0 get + /offset exch 3 mul def + /color_packet colormap offset 3 getinterval def + compression 0 eq + { + /number_pixels 3 def + } + { + currentfile byte readhexstring pop 0 get + /number_pixels exch 1 add 3 mul def + } ifelse + 0 3 number_pixels 1 sub + { + pixels exch color_packet putinterval + } for + pixels 0 number_pixels getinterval +} bind def + +/PseudoClassImage +{ + % + % Display a PseudoClass image. + % + % Parameters: + % class: 0-PseudoClass or 1-Grayscale. + % + currentfile buffer readline pop + token pop /class exch def pop + class 0 gt + { + currentfile buffer readline pop + token pop /depth exch def pop + /grays columns 8 add depth sub depth mul 8 idiv string def + columns rows depth + [ + columns 0 0 + rows neg 0 rows + ] + { currentfile grays readhexstring pop } image + } + { + % + % Parameters: + % colors: number of colors in the colormap. + % colormap: red, green, blue color packets. + % + currentfile buffer readline pop + token pop /colors exch def pop + /colors colors 3 mul def + /colormap colors string def + currentfile colormap readhexstring pop pop + systemdict /colorimage known + { + columns rows 8 + [ + columns 0 0 + rows neg 0 rows + ] + { PseudoClassPacket } false 3 colorimage + } + { + % + % No colorimage operator; convert to grayscale. + % + columns rows 8 + [ + columns 0 0 + rows neg 0 rows + ] + { GrayPseudoClassPacket } image + } ifelse + } ifelse +} bind def + +/DisplayImage +{ + % + % Display a DirectClass or PseudoClass image. + % + % Parameters: + % x & y translation. + % x & y scale. + % label pointsize. + % image label. + % image columns & rows. + % class: 0-DirectClass or 1-PseudoClass. + % compression: 0-none or 1-RunlengthEncoded. + % hex color packets. + % + gsave + /buffer 512 string def + /byte 1 string def + /color_packet 3 string def + /pixels 768 string def + + currentfile buffer readline pop + token pop /x exch def + token pop /y exch def pop + x y translate + currentfile buffer readline pop + token pop /x exch def + token pop /y exch def pop + currentfile buffer readline pop + token pop /pointsize exch def pop + /Times-Roman findfont pointsize scalefont setfont + x y scale + currentfile buffer readline pop + token pop /columns exch def + token pop /rows exch def pop + currentfile buffer readline pop + token pop /class exch def pop + currentfile buffer readline pop + token pop /compression exch def pop + class 0 gt { PseudoClassImage } { DirectClassImage } ifelse + grestore +} bind def +%%EndProlog +%%Page: 1 1 +%%PageBoundingBox: 155 324 445 461 +userdict begin +DisplayImage +155 324 +289.992 137.145 +12.000000 +302 143 +1 +1 +1 +8 +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8e6 +d4cec2b6b6aa9e9e9e9e9e9e9e9eb6b6b6c8cee0e6ffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffff2e0c8b6a49e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +aabccee6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffff8e0c2aa9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb6ceecffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffff8dabc9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +aac8ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffe0bc9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaac8f2ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffff8ceaa9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb6e0 +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffff2c29e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9eaadaffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffff2c29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaadaffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffff8c29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9eaadaffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffcea49e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb6e6ffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffe6aa9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9ec2f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffff8c29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4e0ffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6aa9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1acb6bac4c8c8d6d6d6d6d6d6 +d6cfc8c8bdbaafa89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9ebcf8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffc89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea8b6c4 +cfd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8bdafa19e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaaecffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffff8b09e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9ea1afc1d2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6cbbaa89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ed4ffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeca49e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eafc1d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cfbaa59e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9ec2f8ffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffda9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1bacfd6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6c8af9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb0f8ffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffc89e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9ea5c1d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cfb39e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9ea4ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc29e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea5c1d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cf +b39e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4ecffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1bdd6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cfaf9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4 +daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9eb3d2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8a59e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9edaffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea5c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d2b69e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9edaffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb3d2d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8a59e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9ea4ecffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9ea1c4d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d2af9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4ecffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffda9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea8cbd6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6bd9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9ea4f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffe09e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eafd2 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +c8a19e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb0f8ffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeca49e9e9e9e +9e9e9e9e9e9e9e9e9e9e9eb3d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cba89e9e9e9e9e9e9e9e9e9e9e9e9e9e9ec2ffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffff8b09e9e9e9e9e9e9e9e9e9e9e9e9e9e9ec1d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d2a89e9e9e9e +9e9e9e9e9e9e9e9e9e9e9ed4ffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e +9ec1d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d2a89e9e9e9e9e9e9e9e9e9e9e9e9e9e9eecffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffda9e +9e9e8027272727272727272727272b353535353535353535353535353535353535353535 +353535353535add6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c85035353535353535322727272727272727272727 +272727272d3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f5fefffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffff2a49e9e9e9e45000000000000000000000000000000000000 +00000000000000000000000000000000000000001ac8d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad28000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00003fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffb69e9e9e9e9e9e27000000 +000000000000000000000000000000000000000000000000000000000000000000000050 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6930d0000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffd49e9e9e9e9e9e9e9413000000000000000000000000000000000000000000000000 +000000000000000000000000000085d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d66b0000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000fbfffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffff8a49e9e9e9e9e9e9e9e8009000000000000000000 +000000000000000000000000000000000000000000000000000000000dadd6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c83500000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000002fdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e +9e9e9e9e6200000000000000000000000000000000000000000000000000000000000000 +0000000000000028c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6ad1a000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000006fffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffe69e9e9e9e9e9e9e9e9e9e9e9e4500000000000000000000000000000000 +0000000000000000000000000000000000000000000050d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6850d0000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000fafffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9e9e9e2b00 +000000000000000000000000000000000000000000000000000000000000000000000000 +0085d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d65d0000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000002fcfffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09e9e +9e9e9e9e9e9e9e9e9e9e9ea8c81a00000000000000000000000000000000000000000000 +0000000000000000000000000000000dadd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6c83500000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000005fefffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffaa9e9e9e9e9e9e9e9e9e9e9e9e9ecbd6ad0d00000000000000 +00000000000000000000000000000000000000000000000000000000000028d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad1a000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000009fffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffda9e9e9e9e9e9e9e9e9e9e9e +9e9ebad6d6d6850000000000000000000000000000000000000000000000000000000000 +0000000000000000005dd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d685 +0d0000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000001fcfffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffaa9e9e9e9e9e9e9e9e9e9e9e9ea5d6d6d6d6d65d0000000000000000000000000000 +00000000000000000000000000000000000000000000000093d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d65d0000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000049efffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffe69e9e9e9e9e9e9e9e9e9e9e9e9ec8d6d6d6d6d6d6 +350000000000000000000000000000000000000000000000000000000000000000000000 +00000dbbd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c83500000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000004faa +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb69e9e9e9e9e9e +9e9e9e9e9e9eacd6d6d6d6d6d6d6c81a0000000000000000000000000000000000000000 +000000000000000000000000000000000028d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6ad1a000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000009769e9ee0ffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffff29e9e9e9e9e9e9e9e9e9e9e9e9ecbd6d6d6d6d6d6d6d6ad0d0000000000 +00000000000000000000000000000000000000000000000000000000000000005dd6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6850d0000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000001d949e9e9eb6ffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9eb3d6d6 +d6d6d6d6d6d6d6d685000000000000000000000000000000000000000000000000000000 +000000000000000000000093d6d6d6d6d6d6d6d6d6d6d6d6d6d6d65d0000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +459e9e9e9e9e9ef2ffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa49e +9e9e9e9e9e9e9e9e9e9e9ecfd6d6d6d6d6d6d6d6d6d6d65d000000000000000000000000 +000000000000000000000000000000000000000000000000000dbbd6d6d6d6d6d6d6d6d6 +d6d6d6c83500000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000096c9e9e9e9e9e9e9ec8ffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffda9e9e9e9e9e9e9e9e9e9e9e9eacd6d6d6d6d6d6d6d6d6d6d6 +d6d635000000000000000000000000000000000000000000000000000000000000000000 +0000000035d6d6d6d6d6d6d6d6d6d6d6ad1a000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000001d8a9e9e9e9e9e9e9e9eaa +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffbc9e9e9e9e9e9e9e9e9e9e +9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6c81a000000000000000000000000000000000000 +000000000000000000000000000000000000006bd6d6d6d6d6d6d6d6d6850d0000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00003b949e9e9e9e9e9e9e9e9e9ee0ffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffff89e9e9e9e9e9e9e9e9e9e9e9ea5d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad0d000000 +00000000000000000000000000000000000000000000000000000000000000000000a0d6 +d6d6d6d6d6d65d0000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000d7b9e9e9e9e9e9e9e9e9e9e9e9ec2ffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffda9e9e9e9e9e9e9e9e9e9e9e9ebdd6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d68500000000000000000000000000000000000000000000000000 +0000000000000000000000000dc8d6d6d6d6c83500000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000001aadd6a59e9e9e9e9e +9e9e9e9e9e9ea4ffffffffffffffdfbf8f7f5f3f3f1f0000000000000000000000000000 +001f3f3f5f7f8fbfcfffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc9e9e9e9e9e +9e9e9e9e9e9e9ed2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d65000000000000000000000 +00000000000000000000000000000000000000000000000000000035d6d6d6ad0d000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000042c8d6d6bd9e9e9e9e9e9e9e9e9e9e9e9ee6ffcf9f5f3f000000000000000000 +0000000000000000000000000000000000000000000000002f5f8fcfffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf8f7f +3f3f0f000000000000000000003f3f7f9fdfffffffffffffffffffffffffffffffffffff +ffffffffffffffffa49e9e9e9e9e9e9e9e9e9e9eafd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d62800000000000000000000000000000000000000000000000000000000000000 +0000000000006bd685000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000078d6d6d6d6cf9e9e9e9e9e9e9e9e9e9e9e6c47 +1f0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000f4f9fdfffffffffffffffffffffffffffffffffffffffffffffffffffff +ffbf3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3fefffffffffff +ffffffffffffffaf6f2f000000000000000000000000000000000000000000001f6fcfff +ffffffffffffffffffffffffffffffffffffffffffe69e9e9e9e9e9e9e9e9e9e9e9ec1d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c80d00000000000000000000000000000000 +000000000000000000000000000000000000000000350000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000da0d6d6d6d6d6 +d6a89e9e9e9e9e9e9e763b09000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000004f9fffffffffffffffffffff +ffffffffffffffffffffffffffffff6f0000000000000000000000000000000000000000 +000000000000001fffffffffffffffffffff9f4f00000000000000000000000000000000 +000000000000000000000000003fafffffffffffffffffffffffffffffffffffffffffce +9e9e9e9e9e9e9e9e9e9e9e9ed2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad0000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000035c8d6d6d6d6d6d6d6bd9e9e9e9e94621300000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000f7fefffffffffffffffffffffffffffffffffffffffffffff3f000000000000 +0000000000000000000000000000000000000000004fffffffffffffffdf6f0f00000000 +00000000000000000000000000000000000000000000000000000000003fdfffffffffff +ffffffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9eafd6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6850000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000005dd6d6d6d6d6d6d6d6d6cf9e9e944f0900 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000f8fffffffffffffffffffffffffffff +ffffffffffffff000000000000000000000000000000000000000000000000000000008f +ffffffffffdf5f0000000000000000000000000000000000000000000000000000000000 +00000000000000000f9fffffffffffffffffffffffffffffffffff9e9e9e9e9e9e9e9e9e +9e9e9ebdd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6500000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000d93d6d6d6 +d6d6d6d6d6d6d6d69b4f0900000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +2fcfffffffffffffffffffffffffffffffffffffbf000000000000000000000000000000 +00000000000000000000000000bfffffffef5f0000000000000000000000000000000000 +0000000000000000000000000000000000000000000000009fffffffffffffffffffffff +ffffffffe69e9e9e9e9e9e9e9e9e9e9e9ecbd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6280000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000028bbd6d6d6d6d6d6d6d6d6d6d6850d00000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000f9fffffffffffffffffffffffffffffffffff8f00 +000000000000000000000000000000000000000000000000000000ffffff8f0f00000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000009fffffffffffffffffffffffffffffd49e9e9e9e9e9e9e9e9e9e9ea1d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c80d0000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000050c8d6d6d6d6d6d6d6d6d6d6ad3500 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000006fffffffff +ffffffffffffffffffffffff5f0000000000000000000000000000000000000000000000 +000000003fffef3f00000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000bfffffffffffffffffffffffffffc89e9e9e +9e9e9e9e9e9e9e9eafd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000d85d6 +d6d6d6d6d6d6d6d6d6d66b00000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000005fffffffffffffffffffffffffffffff1f000000000000000000 +0000000000000000000000000000000000006fbf0f000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000001fffffff +ffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9ebad6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d685000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000028add6d6d6d6d6d6d6d6d6d6c835000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000005fffffffffffffffffffff +ffffffef000000000000000000000000000000000000000000000000000000003f000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000009fffffffffffffffffffffffffa49e9e9e9e9e9e9e9e9e9e9e +c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d650000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000042c8d6d6d6d6d6d6d6d6d6d6ad +0d0000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000007fffffffffffffffffffffffffaf000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000002fffffffffffffffffffff +ffff9e9e9e9e9e9e9e9e9e9e9e9ed2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d628000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +78d6d6d6d6d6d6d6d6d6d6d6850000000000000000000000000000000000000000000000 +000000000000000000000000001f3f3f3f3f3f3f00000000000000000000000000000000 +00000000000000000000000000000000000000bfffffffffffffffffffffff7f00000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000dfffffffffffffffffffffe69e9e9e9e9e9e9e9e9e9e9e9ed6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c80d000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000dadd6d6d6d6d6d6d6d6d6d6d68500000000000000000000 +00000000000000000000000000000000000000000000000f5fafefffffffffffffffffcf +8f2f000000000000000000000000000000000000000000000000000000000000001fefff +ffffffffffffffffff3f0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000bfffffffffffffffffffffe69e9e9e9e9e9e +9e9e9e9e9eacd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6ad00000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000035c8d6d6d6d6d6d6d6d6d6 +d6d68500000000000000000000000000000000000000000000000000000000000000001f +9fffffffffffffffffffffffffffffffbf3f000000000000000000000000000000000000 +0000000000000000000000007fffffffffffffffffffff0f000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000009fffffff +ffffffffffffffce9e9e9e9e9e9e9e9e9e9e9eacd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d68500000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00006bd6d6d6d6d6d6d6d6d6d6d6d6930000000000000000000000000000000000000000 +00000000000000000000000f8fffffffffffffffffffffffffffffffffffffff8f000000 +00000000000000000000000000000000000000000000000000000fefffffffffffffffff +cf0000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000008fffffffffffffffffffffce9e9e9e9e9e9e9e9e9e9e9ebad6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d65000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000d93d6d6d6d6d6d6d6d6d6d6d6d6ad0d000000000000 +0000000000000000000000000000000000000000000000002fdfffffffffffffffffffff +ffffffffffffffffffffff9f000000000000000000000000000000000000000000000000 +00000000008fffffffffffffffff9f000000000000000000000000000000000000000000 +00000000000000000000000000000000000000003f3f3f3f3f0f00000000000000000000 +000000000000000000000000000000000000000000000000bfffffffffffffffffffffce +9e9e9e9e9e9e9e9e9e9e9ebad6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d62800000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000028bbd6d6d6d6d6d6d6 +d6d6d6d6d6c81a0000000000000000000000000000000000000000000000000000000000 +005fefffffffffffffffffffffffffffffffffffffffffffffff3f000000000000000000 +000000000000000000000000000000000000002fffffffffffffffff5f00000000000000 +0000000000000000000000000000000000000000000000000000000000001f6fcfffffff +ffffffffaf2f000000000000000000000000000000000000000000000000000000000000 +0000bfffffffffffffffffffffbc9e9e9e9e9e9e9e9e9e9e9ebad6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c80d00000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000050d6d6d6d6d6d6d6d6d6d6d6d6d6d64200000000000000000000000000000000 +00000000000000000000000000005fffffffffffffffffffffffffffffffffffffffffff +ffffffffaf0000000000000000000000000000000000000000000000000000000000efff +ffffffffffff2f0000000000000000000000000000000000000000000000000000000000 +0000000000000f9fffffffffffffffffffffffef2f000000000000000000000000000000 +00000000000000000000000000000000cfffffffffffffffffffffb69e9e9e9e9e9e9e9e +9e9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6ad0000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000d85d6d6d6d6d6d6d6d6d6d6d6d6d6d693000000 +0000000000000000000000000000000000000000000000000000002fefffffffffffffff +ffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000 +000000000000000000000000bfffffffffffffff00000000000000000000000000000000 +000000000000000000000000000000000000004fefffffffffffffffffffffffffffaf00 +000000000000000000000000000000000000000000000000000000000000ffffffffffff +ffffffffffb69e9e9e9e9e9e9e9e9e9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6780000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000028add6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d61a00000000000000000000000000000000000000000000000000 +000000000fcfffffffffffffffffffffffffffffffffffffffffffffffffffffff1f0000 +00000000000000000000000000000000000000000000000000007fffffffffffffbf0000 +00000000000000000000000000000000000000000000000000000000000000008fffffff +ffffffffffffffffffffffffff0000000000000000000000000000000000000000000000 +000000000000001fffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9ec8d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad1a000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000042c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d66b000000000000000000000000 +0000000000000000000000000000000000007fffffffffffffffffffffffffffffffffff +ffffffffffffffffffffff1f000000000000000000000000000000000000000000000000 +000000007fffffffffffff7f000000000000000000000000000000000000000000000000 +0000000000000000009fffffffffffffffffffffffffffffffffff000000000000000000 +0000000000000000000000000000000000000000003fffffffffffffffffffffffb69e9e +9e9e9e9e9e9e9e9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d685000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000078d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d60d00000000000000000000000000000000000000000000000000000000001fffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000 +0000000000000000000000000000000000004fffffffffffff4f00000000000000000000 +000000000000000000000000000000000000000000006fffffffffffffffffffffffffff +ffffffffff0000000000000000000000000000000000000000000000000000000000007f +ffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8500000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000001aadd6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d685000000000000000000000000000000000000000000 +0000000000000000009fffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffbf00000000000000000000000000000000000000000000000000000000003fffffff +ffffff0f000000000000000000000000000000000000000000000000000000000000003f +ffffffffffffffffffffffffffffffffffffcf0000000000000000000000000000000000 +00000000000000000000000000afffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e +9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad2800 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000001ac8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d62800000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000007fffffffffffdf00000000000000000000000000000000000000 +0000000000000000000000000fdfffffffffffffffffffffffffffffffffffffaf000000 +000000000000000000000000000000000000000000000000000000cfffffffffffffffff +ffffffb69e9e9e9e9e9e9e9e9e9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6930d000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000a0d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6bb000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000007fffffffffffaf0000000000 +00000000000000000000000000000000000000000000000000007fffffffffffffffffff +ffffffffffffffffffff7f00000000000000000000000000000000000000000000000000 +0000000000ffffffffffffffffffffffffbc9e9e9e9e9e9e9e9e9e9e9ebad6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d65d000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000dc8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d68500000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000afffffffffff6f000000000000000000000000000000000000000000000000000000 +0000000fefffffffffffffffffffffffffffffffffffffff3f0000000000000000000000 +0000000000000000000000000000000000003fffffffffffffffffffffffffce9e9e9e9e +9e9e9e9e9e9e9ebad6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8 +280000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000028d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6500000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000cfffffffffff3f00000000000000000000000000 +00000000000000000000000000000000007fffffffffffffffffffffffffffffffffffff +ffff0f00000000000000000000000000000000000000000000000000000000007fffffff +ffffffffffffffffffce9e9e9e9e9e9e9e9e9e9e9ebad6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6a00d00000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000005dd6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d635000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000ffffffffffff +00000000000000000000000000000000000000000000000000000000000000dfffffffff +ffffffffffffffffffffffffffffffdf0000000000000000000000000000000000000000 +00000000000000000000afffffffffffffffffffffffffce9e9e9e9e9e9e9e9e9e9e9eac +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d66b00000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000093d6d6d6d6d6d6d6d6d6d6d6d6d6d60000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000003fffffffffffbf00000000000000000000000000000000000000000000 +00000000000000004fffffffffffffffffffffffffffffffffffffffffaf000000000000 +000000000000000000000000000000000000000000000000dfffffffffffffffffffffff +ffe69e9e9e9e9e9e9e9e9e9e9eacd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6c835000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000dbbd6d6d6d6d6d6d6d6d6d6 +d6d6d6000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000006fffffffffff8f0000000000000000 +000000000000000000000000000000000000000000009fffffffffffffffffffffffffff +ffffffffffffff7f00000000000000000000000000000000000000000000000000000000 +000fffffffffffffffffffffffffffe69e9e9e9e9e9e9e9e9e9e9e9ed6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad1a0000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000028c8d6d6d6d6d6d6d6d6d6d6d6d600000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000af +ffffffffff5f000000000000000000000000000000000000000000000000000000000000 +efffffffffffffffffffffffffffffffffffffffff3f0000000000000000000000000000 +0000000000000000000000000000004fffffffffffffffffffffffffffff9e9e9e9e9e9e +9e9e9e9e9e9ed2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6780000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000050d6d6d6d6d6d6d6d6d6d6d6d60000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000dfffffffffff1f00000000000000000000000000000000 +000000000000000000000000002fffffffffffffffffffffffffffffffffffffffffff0f +00000000000000000000000000000000000000000000000000000000007fffffffffffff +ffffffffffffffffa49e9e9e9e9e9e9e9e9e9e9ec8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6c85000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000085d6d6 +d6d6d6d6d6d6d6d6d6000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000001fffffffffffef000000 +0000000000000000000000000000000000000000000000000000006fffffffffffffffff +ffffffffffffffffffffffffdf0000000000000000000000000000000000000000000000 +00000000000000bfffffffffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9ebad6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad28000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000dadd6d6d6d6d6d6d6d6d6d600000000000000000000000000 +0000000000000000000000000000000027737f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f +7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f +7f7f7f7f9fffffffffffaf00000000000000000000000000000000000000000000000000 +00000000009fffffffffffffffffffffffffffffffffffffffff9f000000000000000000 +000000000000000000000000000000000000000000efffffffffffffffffffffffffffff +c89e9e9e9e9e9e9e9e9e9e9eafd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6850d0000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000028c8d6d6d6d6d6d6d6d6 +d60d000000000000000000000000000000000000000000000000000000003becffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000 +00000000000000000000000000000000000000cfffffffffffffffffffffffffffffffff +ffffffff6f00000000000000000000000000000000000000000000000000000000002fff +ffffffffffffffffffffffffffffd49e9e9e9e9e9e9e9e9e9e9ea1d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d65d0000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000050d6d6d6d6d6d6d6d6d6350000000000000000000000000000000000000000 +00000000000000001dffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffff3f00000000000000000000000000000000000000000000000000000000000fffffff +ffffffffffffffffffffffffffffffffffff3f0000000000000000000000000000000000 +0000000000000000000000005fffffffffffffffffffffffffffffffe69e9e9e9e9e9e9e +9e9e9e9e9ecbd6d6d6d6d6d6d6d6d6d6d6d6bb2800000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000078d6d6d6d6d6d6d6d66b000000000000 +0000000000000000000000000000000000000000000000dfffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff0f00000000000000000000000000000000000000 +000000000000000000003fffffffffffffffffffffffffffffffffffffffffff00000000 +00000000000000000000000000000000000000000000000000008fffffffffffffffffff +ffffffffffffff9e9e9e9e9e9e9e9e9e9e9e9ebdd6d6d6d6d6d6d6d6d6d6d6a00d000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000ad +d6d6d6d6d6d6d6a000000000000000000000000000000000000000000000000000000000 +006fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcf000000000000 +0000000000000000000000000000000000000000000000007fffffffffffffffffffffff +ffffffffffffffffffbf0000000000000000000000000000000000000000000000000000 +00000000cfffffffffffffffffffffffffffffffffb69e9e9e9e9e9e9e9e9e9e9eafd6d6 +d6d6d6d6d6d6d6d66b000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000001ac8d6d6d6d6d6d6d60d00000000000000000000000000 +0000000000000000000000000000000fdfffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffff9f00000000000000000000000000000000000000000000000000000000 +0000bfffffffffffffffffffffffffffffffffffffffff8f000000000000000000000000 +000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffce +9e9e9e9e9e9e9e9e9e9e9e9ed2d6d6d6d6d6d6d6c8350000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000042d6d6d6d6d6d6d6 +6b00000000000000000000000000000000000000000000000000000000003fffffffffff +ffffffffffffffffffffffffffffffffffffffcf3f3f3f3f5f7f7f7fafbfbfbfffffffff +ffffffffffffffffffffffffffffffffffffffffff5f0000000000000000000000000000 +00000000000000000000000000000000efffffffffffffffffffffffffffffffffffffff +ff5f00000000000000000000000000000000000000000000000000000000003fffffffff +ffffffffffffffffffffffffffe69e9e9e9e9e9e9e9e9e9e9e9ec1d6d6d6d6d6d6ad0d00 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000006bd6d6d6d6d6d6bb000000000000000000000000000000000000000000 +0000000000000000005fffffffffffffffffffffffffffffffffffffffffffffaf0f0000 +000000000000000000000000000f3f3f3f5f7f7f7fafbfbfbfffffffffffffffffffff2f +00000000000000000000000000000000000000000000000000000000001fffffffffffff +ffffffffffffffffffffffffffffff1f0000000000000000000000000000000000000000 +0000000000000000006fffffffffffffffffffffffffffffffffffffa49e9e9e9e9e9e9e +9e9e9e9eafd6d6d6d6d67800000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000005d35000000000000000000000000000000 +00000000000000000000000000000000000000000000a0d6d6d6d6d6d65d000000000000 +0000000000000000000000000000000000000000000000004fefffffffffffffffffffff +ffffffffffffffffdf4f0000000000000000000000000000000000000000000000000000 +00000000003fffffffffffffff0000000000000000000000000000000000000000000000 +000000000000005fffffffffffffffffffffffffffffffffffffffffef00000000000000 +0000000000000000000000000000000000000000000000afffffffffffffffffffffffff +ffffffffffffbc9e9e9e9e9e9e9e9e9e9e9e9ed2d6d6c842000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000085d6c80d +000000000000000000000000000000000000000000000000000000000000000000000000 +0dc8d6d6d6d6d6c80d000000000000000000000000000000000000000000000000000000 +0000000f7fefffffffffffffffffffffffffffffcf5f0000000000000000000000000000 +0000000000000000000000000000000000002fefffffffffffffbf000000000000000000 +0000000000000000000000000000000000000000008fffffffffffffffffffffffffffff +ffffffffffffbf0000000000000000000000000000000000000000000000000000000000 +00dfffffffffffffffffffffffffffffffffffffda9e9e9e9e9e9e9e9e9e9e9e9ebdd6ad +280000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000dadd6d6d69300000000000000000000000000000000000000000000 +00000000000000000000000000000035d6d6d6d6d6d69300000000000000000000000000 +000000000000000000000000000000000000000f4f8fbfffffffffffffffbf9f6f1f0000 +000000000000000000000000000000000000000000000000000000000000000fefffffff +ffffffff7f000000000000000000000000000000000000000000000000000000000000cf +ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000 +00000000000000000000000000000ffffffffffffffffffffffffffffffffffffffff89e +9e9e9e9e9e9e9e9e9e9e9ea5850d00000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000028c8d6d6d6d6d64200000000000000 +0000000000000000000000000000000000000000000000000000000000005dd6d6d6d6d6 +d65d00000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000fcfffffffffffffffff4f0000000000000000000000000000000000 +00000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff3f00 +000000000000000000000000000000000000000000000000000000004fffffffffffffff +ffffffffffffffffffffffffffbc9e9e9e9e9e9e9e9e9e9e9e3b00000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000050d6 +d6d6d6d6d6d6c81a00000000000000000000000000000000000000000000000000000000 +00000000000000000093d6d6d6d6d6d65000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000002fdfffffffffffffffffff0f000000 +00000000000000000000000000000000000000000000000000003fffffffffffffffffff +ffffffffffffffffffffffff0f0000000000000000000000000000000000000000000000 +0000000000007fffffffffffffffffffffffffffffffffffffffffda9e9e9e9e9e9e9e9e +9e8a1d000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000078d6d6d6d6d6d6d6d6d6a00000000000000000000000000000 +00000000000000000000000000000000000000000000000dbbd6d6d6d6d6c82800000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000003fef +ffffffffffffffffffdf0000000000000000000000000000000000000000000000000000 +000000006fffffffffffffffffffffffffffffffffffffffffdf00000000000000000000 +0000000000000000000000000000000000000000bfffffffffffffffffffffffffffffff +ffffffffffffa49e9e9e9e9e9e9e6c090000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000da0d6d6d6d6d6d6d6d6d6d6d6 +5d0000000000000000000000000000000000000000000000000000000000000000000000 +000028d6d6d6d6d6d6c85000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000006fffffffffffffffffffffffaf000000000000000000000000 +000000000000000000000000000000000000afffffffffffffffffffffffffffffffffff +ffffff9f000000000000000000000000000000000000000000000000000000000000efff +ffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e450000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +28add6d6d6d6d6d6d6d6d6d6d6d6d6280000000000000000000000000000000000000000 +00000000000000000000000000000000005dd6d6d6d6d6d6d65d00000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000fbfffffffffffffffffffffff +ff6f000000000000000000000000000000000000000000000000000000000000dfffffff +ffffffffffffffffffffffffffffffffff6f000000000000000000000000000000000000 +00000000000000000000002ffffffffffffffffffffffffffffffffffffffffffffff29e +9e9e9e942700000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000042c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad000000000000 +000000000000000000000000000000000000000000000000000000000000000085d6d6d6 +d6d6d6d6850d000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +6fefffffffffffffffffffffffffff3f0000000000000000000000000000000000000000 +0000000000000000001fffffffffffffffffffffffffffffffffffffffffff3f00000000 +000000000000000000000000000000000000000000000000005fffffffffffffffffffff +ffffffffffffffffffffffffffb69e9e7609000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000006bd6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d678000000000000000000000000000000000000000000000000000000 +000000000000000000000dadd6d6d6d6d6d6d6bb35000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000003fcfffffffffffffffffffffffffffffff00000000000000 +00000000000000000000000000000000000000000000004fffffffffffffffffffffffff +ffffffffffffffffff000000000000000000000000000000000000000000000000000000 +0000008fffffffffffffffffffffffffffffffffffffffffffffffe69e58000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +00000d85d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d635000000000000000000000000 +0000000000000000000000000000000000000000000000000028c8d6d6d6d6d6d6d6ac6c +130000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000003fbfffffffffffffffffffff +ffffffffffffbf0000000000000000000000000000000000000000000000000000000000 +007fffffffffffffffffffffffffffffffffffffffffbf00000000000000000000000000 +0000000000000000000000000000000000bfffffffffffffffffffffffffffffffffffff +ffffffffffef310000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000001aadd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6bb0d000000000000000000000000000000000000000000000000000000000000000000 +0000000050d6d6d6d6d6d6c49e9e9e6c2700000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000f +6fdfffffffffffffffffffffffffffffffffffff8f000000000000000000000000000000 +000000000000000000000000000000bfffffffffffffffffffffffffffffffffffffffff +8f000000000000000000000000000000000000000000000000000000000000ffffffffff +ffffffffffffffffffffffffffffffffffffcf1f00000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000028c8d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d69300000000000000000000000000000000000000 +0000000000000000000000000000000000000085d6d6d6d6d2a59e9e9e9e9e8a45130000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000f5fafffffffffffffffffffffffffffffffffffffffffff5f00 +0000000000000000000000000000000000000000000000000000000000ffffffffffffff +ffffffffffffffffffffffffffff5f000000000000000000000000000000000000000000 +00000000000000003fffffffffffffffffffffffffffffffffffffffffffff9f0f000000 +000000000000000000000000000000000000000000000000000000000000000000000000 +000000000050d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d64200000000 +00000000000000000000000000000000000000000000000000000000000000000000add6 +d6d6b69e9e9e9e9e9e9e9e9e805831090000000000000000000000000000000000000000 +00000000000000000000000000000000000000000f4f8fcfffffffffffffffffffffffff +ffffffffffffffffffffffff1f0000000000000000000000000000000000000000000000 +0000000000002fffffffffffffffffffffffffffffffffffffffffff1f00000000000000 +000000000000000000000000000000000000000000006fffffffffffffffffffffffffff +ffffffffffffffef5f000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000085d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6c81a00000000000000000000000000000000000000000000000000 +0000000000000000000000001ac8d6c89e9e9e9e9e9e9e9e9e9e9e9e9eaacf9f7f3f1f00 +00000000000000000000000000000000000000000000000000000000002f3f7fafcfffff +ffffffffffffffffffffffffffffffffffffffffffffffffffef00000000000000000000 +00000000000000000000000000000000000000006fffffffffffffffffffffffffffffff +ffffffffffef000000000000000000000000000000000000000000000000000000000000 +9fffffffffffffffffffffffffffffffffffffffdf2f0000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000da0d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6a00000000000000000000000 +00000000000000000000000000000000000000000000000000000042d2a59e9e9e9e9e9e +9e9e9e9e9e9e9edaffffffffffffcfbf9f7f7f6f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f +7f7f7fafbfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffdf7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fbfff +ffffffffffffffffffffffffffffffffffffffdf7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f +7f7f7f7f7f7f7f7f7f7f7f7f7f7fdfffffffffffffffffffffffffffffffffffffaf0f00 +000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000028bbd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d65d0000000000000000000000000000000000000000000000000000000000000000 +0000000000005f9e9e9e9e9e9e9e9e9e9e9e9e9eb6ffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffff6f00000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000050c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6280000000000000000000000000000000000 +000000000000000000000000000000000000000000809e9e9e9e9e9e9e9e9e9e9e9eecff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffef2f000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000006bd6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6ad000000 +000000000000000000000000000000000000000000000000000000000000000000000009 +949e9e9e9e9e9e9e9e9e9ec8ffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf +0f0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000d93d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d678000000000000000000000000000000000000000000000000 +0000000000000000000000000000279e9e9e9e9e9e9e9e9ea4f8ffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000001aadd6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d635000000000000000000 +00000000000000000000000000000000000000000000000000000000004f9e9e9e9e9e9e +9e9ee0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffef4f00000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000035 +c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6bb0d000000000000000000000000000000000000000000000000000000000000 +0000000000000000769e9e9e9e9e9ec2ffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffcf1f000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000005dd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d69300000000000000000000000000000000 +00000000000000000000000000000000000000000000098a9e9e9e9eaaf8ffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffff9f000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000085d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d64200 +000000000000000000000000000000000000000000000000000000000000000000000000 +001d9e9e9e9eecffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffdf7f7f7f7f7f7f7f7f +7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f614f4f4f4f4f4f4f4f4f4f4f4f4f4f4f566b6b6b +6b6badd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6c86b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b +6b6b674f4f4f4f4f4f4f4f4f4f4f4f4f8a9e9ed4ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeca49e9e9e9e +9e9e9e9e9e9e9e9e9e9e9eb3d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cba89e9e9e9e9e9e9e9e9e9e9e9e9e9e9ec2ffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffe09e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eafd2d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8a19e9e9e9e9e9e +9e9e9e9e9e9e9e9e9eb0f8ffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffda9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9ea8cbd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6bd9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4f8ffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1c4d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d2af9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4 +ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +b3d2d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8a59e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9ea4ecffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc29e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea5c8d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d2b69e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9edaffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb3d2d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8a59e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9edaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffc29e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9ea1bad6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cfac9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4daffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffc29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea5c1d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6cfb39e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea4ecffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffc89e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9ea5c1d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cfb39e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9ea4ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +da9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1bacfd6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8af9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb0f8ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffeca49e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9eafc1d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6cfbaa59e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ec2f8ffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b09e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1afc1d2d6d6d6d6d6d6d6d6d6d6d6d6d6 +d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6cbbaa89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9ed4ffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffc89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9ea8b6c4cfd6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6c8bdafa19e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaaecffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffe6aa9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea1acb3bac4c8c8d6d6d6d6d6d6d6cfc8c8 +bdbaafa89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ebcf8ff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffff8c89e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9ea4e0ffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffe6aa9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ec2f8ffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +cea49e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb6e6 +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffff8c29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9eaadaffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2c29e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaadaffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffff2c29e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaada +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffffffffff8ceaa9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9eb6e0ffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffe0bc9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaac8f2ffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffffff8dabc9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaac8ecff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffff8e0c2aa9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e +9e9e9e9e9e9e9eb6d4ecffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffffffffffffffffffffffffffffffff2e0c8b6a49e9e9e9e9e9e9e9e +9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eaabccee6ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffff8e6d4cec2b6b6aa9e9e9e9e9e9e9e9eb6b6b6c8cee0e6ffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffff +end +%%PageTrailer +%%Trailer +%%EOF diff --git a/docs/html.sty b/docs/html.sty new file mode 100644 index 0000000..b5f8fbb --- /dev/null +++ b/docs/html.sty @@ -0,0 +1,887 @@ +% +% $Id: html.sty,v 1.23 1998/02/26 10:32:24 latex2html Exp $ +% LaTeX2HTML Version 96.2 : html.sty +% +% This file contains definitions of LaTeX commands which are +% processed in a special way by the translator. +% For example, there are commands for embedding external hypertext links, +% for cross-references between documents or for including raw HTML. +% This file includes the comments.sty file v2.0 by Victor Eijkhout +% In most cases these commands do nothing when processed by LaTeX. +% +% Place this file in a directory accessible to LaTeX (i.e., somewhere +% in the TEXINPUTS path.) +% +% NOTE: This file works with LaTeX 2.09 or (the newer) LaTeX2e. +% If you only have LaTeX 2.09, some complex LaTeX2HTML features +% like support for segmented documents are not available. + +% Changes: +% See the change log at end of file. + + +% Exit if the style file is already loaded +% (suggested by Lee Shombert <las@potomac.wash.inmet.com> +\ifx \htmlstyloaded\relax \endinput\else\let\htmlstyloaded\relax\fi +\makeatletter + +\providecommand{\latextohtml}{\LaTeX2\texttt{HTML}} + + +%%% LINKS TO EXTERNAL DOCUMENTS +% +% This can be used to provide links to arbitrary documents. +% The first argumment should be the text that is going to be +% highlighted and the second argument a URL. +% The hyperlink will appear as a hyperlink in the HTML +% document and as a footnote in the dvi or ps files. +% +\newcommand{\htmladdnormallinkfoot}[2]{#1\footnote{#2}} + + +% This is an alternative definition of the command above which +% will ignore the URL in the dvi or ps files. +\newcommand{\htmladdnormallink}[2]{#1} + + +% This command takes as argument a URL pointing to an image. +% The image will be embedded in the HTML document but will +% be ignored in the dvi and ps files. +% +\newcommand{\htmladdimg}[1]{} + + +%%% CROSS-REFERENCES BETWEEN (LOCAL OR REMOTE) DOCUMENTS +% +% This can be used to refer to symbolic labels in other Latex +% documents that have already been processed by the translator. +% The arguments should be: +% #1 : the URL to the directory containing the external document +% #2 : the path to the labels.pl file of the external document. +% If the external document lives on a remote machine then labels.pl +% must be copied on the local machine. +% +%e.g. \externallabels{http://cbl.leeds.ac.uk/nikos/WWW/doc/tex2html/latex2html} +% {/usr/cblelca/nikos/tmp/labels.pl} +% The arguments are ignored in the dvi and ps files. +% +\newcommand{\externallabels}[2]{} + + +% This complements the \externallabels command above. The argument +% should be a label defined in another latex document and will be +% ignored in the dvi and ps files. +% +\newcommand{\externalref}[1]{} + + +% Suggested by Uffe Engberg (http://www.brics.dk/~engberg/) +% This allows the same effect for citations in external bibliographies. +% An \externallabels command must be given, locating a labels.pl file +% which defines the location and keys used in the external .html file. +% +\newcommand{\externalcite}{\nocite} + + +%%% HTMLRULE +% This command adds a horizontal rule and is valid even within +% a figure caption. +% Here we introduce a stub for compatibility. +\newcommand{\htmlrule}{\protect\HTMLrule} +\newcommand{\HTMLrule}{\@ifstar\htmlrulestar\htmlrulestar} +\newcommand{\htmlrulestar}[1]{} + +% This command adds information within the <BODY> ... </BODY> tag +% +\newcommand{\bodytext}[1]{} +\newcommand{\htmlbody}{} + + +%%% HYPERREF +% Suggested by Eric M. Carol <eric@ca.utoronto.utcc.enfm> +% Similar to \ref but accepts conditional text. +% The first argument is HTML text which will become ``hyperized'' +% (underlined). +% The second and third arguments are text which will appear only in the paper +% version (DVI file), enclosing the fourth argument which is a reference to a label. +% +%e.g. \hyperref{using the tracer}{using the tracer (see Section}{)}{trace} +% where there is a corresponding \label{trace} +% +\newcommand{\hyperref}{\hyperrefx[ref]} +\def\hyperrefx[#1]{{\def\next{#1}% + \def\tmp{ref}\ifx\next\tmp\aftergroup\hyperrefref + \else\def\tmp{pageref}\ifx\next\tmp\aftergroup\hyperpageref + \else\def\tmp{page}\ifx\next\tmp\aftergroup\hyperpageref + \else\def\tmp{noref}\ifx\next\tmp\aftergroup\hypernoref + \else\def\tmp{no}\ifx\next\tmp\aftergroup\hypernoref + \else\typeout{*** unknown option \next\space to hyperref ***}% + \fi\fi\fi\fi\fi}} +\newcommand{\hyperrefref}[4]{#2\ref{#4}#3} +\newcommand{\hyperpageref}[4]{#2\pageref{#4}#3} +\newcommand{\hypernoref}[3]{#2} + + +%%% HYPERCITE --- added by RRM +% Suggested by Stephen Simpson <simpson@math.psu.edu> +% effects the same ideas as in \hyperref, but for citations. +% It does not allow an optional argument to the \cite, in LaTeX. +% +% \hypercite{<html-text>}{<LaTeX-text>}{<opt-text>}{<key>} +% +% uses the pre/post-texts in LaTeX, with a \cite{<key>} +% +% \hypercite[ext]{<html-text>}{<LaTeX-text>}{<key>} +% +% uses the pre/post-texts in LaTeX, with a \nocite{<key>} +% the actual reference comes from an \externallabels file. +% +\newcommand{\hypercite}{\hypercitex[int]} +\def\hypercitex[#1]{{\def\next{#1}% + \def\tmp{int}\ifx\next\tmp\aftergroup\hyperciteint + \else\def\tmp{cite}\ifx\next\tmp\aftergroup\hyperciteint + \else\def\tmp{ext}\ifx\next\tmp\aftergroup\hyperciteext + \else\def\tmp{nocite}\ifx\next\tmp\aftergroup\hyperciteext + \else\def\tmp{no}\ifx\next\tmp\aftergroup\hyperciteext + \else\typeout{*** unknown option \next\space to hypercite ***}% + \fi\fi\fi\fi\fi}} +\newcommand{\hyperciteint}[4]{#2{\def\tmp{#3}\def\emptyopt{}% + \ifx\tmp\emptyopt\cite{#4}\else\cite[#3]{#4}\fi}} +\newcommand{\hyperciteext}[3]{#2\nocite{#3}} + + + +%%% HTMLREF +% Reference in HTML version only. +% Mix between \htmladdnormallink and \hyperref. +% First arg is text for in both versions, second is label for use in HTML +% version. +\newcommand{\htmlref}[2]{#1} + +%%% HTMLCITE +% Reference in HTML version only. +% Mix between \htmladdnormallink and \hypercite. +% First arg is text for in both versions, second is citation for use in HTML +% version. +\newcommand{\htmlcite}[2]{#1} + + +%%% HTMLIMAGE +% This command can be used inside any environment that is converted +% into an inlined image (eg a "figure" environment) in order to change +% the way the image will be translated. The argument of \htmlimage +% is really a string of options separated by commas ie +% [scale=<scale factor>],[external],[thumbnail=<reduction factor> +% The scale option allows control over the size of the final image. +% The ``external'' option will cause the image not to be inlined +% (images are inlined by default). External images will be accessible +% via a hypertext link. +% The ``thumbnail'' option will cause a small inlined image to be +% placed in the caption. The size of the thumbnail depends on the +% reduction factor. The use of the ``thumbnail'' option implies +% the ``external'' option. +% +% Example: +% \htmlimage{scale=1.5,external,thumbnail=0.2} +% will cause a small thumbnail image 1/5th of the original size to be +% placed in the final document, pointing to an external image 1.5 +% times bigger than the original. +% +\newcommand{\htmlimage}[1]{} + + +% \htmlborder causes a border to be placed around an image or table +% when the image is placed within a <TABLE> cell. +\newcommand{\htmlborder}[1]{} + +% Put \begin{makeimage}, \end{makeimage} around LaTeX to ensure its +% translation into an image. +% This shields sensitive text from being translated. +\newenvironment{makeimage}{}{} + + +% A dummy environment that can be useful to alter the order +% in which commands are processed, in LaTeX2HTML +\newenvironment{tex2html_deferred}{}{} + + +%%% HTMLADDTONAVIGATION +% This command appends its argument to the buttons in the navigation +% panel. It is ignored by LaTeX. +% +% Example: +% \htmladdtonavigation{\htmladdnormallink +% {\htmladdimg{http://server/path/to/gif}} +% {http://server/path}} +\newcommand{\htmladdtonavigation}[1]{} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Comment.sty version 2.0, 19 June 1992 +% selectively in/exclude pieces of text: the user can define new +% comment versions, and each is controlled separately. +% This style can be used with plain TeX or LaTeX, and probably +% most other packages too. +% +% Examples of use in LaTeX and TeX follow \endinput +% +% Author +% Victor Eijkhout +% Department of Computer Science +% University Tennessee at Knoxville +% 104 Ayres Hall +% Knoxville, TN 37996 +% USA +% +% eijkhout@cs.utk.edu +% +% Usage: all text included in between +% \comment ... \endcomment +% or \begin{comment} ... \end{comment} +% is discarded. The closing command should appear on a line +% of its own. No starting spaces, nothing after it. +% This environment should work with arbitrary amounts +% of comment. +% +% Other 'comment' environments are defined by +% and are selected/deselected with +% \includecomment{versiona} +% \excludecoment{versionb} +% +% These environments are used as +% \versiona ... \endversiona +% or \begin{versiona} ... \end{versiona} +% with the closing command again on a line of its own. +% +% Basic approach: +% to comment something out, scoop up every line in verbatim mode +% as macro argument, then throw it away. +% For inclusions, both the opening and closing comands +% are defined as noop +% +% Changed \next to \html@next to prevent clashes with other sty files +% (mike@emn.fr) +% Changed \html@next to \htmlnext so the \makeatletter and +% \makeatother commands could be removed (they were causing other +% style files - changebar.sty - to crash) (nikos@cbl.leeds.ac.uk) +% Changed \htmlnext back to \html@next... + +\def\makeinnocent#1{\catcode`#1=12 } +\def\csarg#1#2{\expandafter#1\csname#2\endcsname} + +\def\ThrowAwayComment#1{\begingroup + \def\CurrentComment{#1}% + \let\do\makeinnocent \dospecials + \makeinnocent\^^L% and whatever other special cases + \endlinechar`\^^M \catcode`\^^M=12 \xComment} +{\catcode`\^^M=12 \endlinechar=-1 % + \gdef\xComment#1^^M{\def\test{#1}\edef\test{\meaning\test} + \csarg\ifx{PlainEnd\CurrentComment Test}\test + \let\html@next\endgroup + \else \csarg\ifx{LaLaEnd\CurrentComment Test}\test + \edef\html@next{\endgroup\noexpand\end{\CurrentComment}} + \else \csarg\ifx{LaInnEnd\CurrentComment Test}\test + \edef\html@next{\endgroup\noexpand\end{\CurrentComment}} + \else \let\html@next\xComment + \fi \fi \fi \html@next} +} + +\def\includecomment + #1{\expandafter\def\csname#1\endcsname{}% + \expandafter\def\csname end#1\endcsname{}} +\def\excludecomment + #1{\expandafter\def\csname#1\endcsname{\ThrowAwayComment{#1}}% + {\escapechar=-1\relax + \edef\tmp{\string\\end#1}% + \csarg\xdef{PlainEnd#1Test}{\meaning\tmp}% + \edef\tmp{\string\\end\string\{#1\string\}}% + \csarg\xdef{LaLaEnd#1Test}{\meaning\tmp}% + \edef\tmp{\string\\end \string\{#1\string\}}% + \csarg\xdef{LaInnEnd#1Test}{\meaning\tmp}% + }} + +\excludecomment{comment} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% end Comment.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% +% Alternative code by Robin Fairbairns, 22 September 1997 +% +\newcommand\@gobbleenv{\let\reserved@a\@currenvir\@gobble@nv} +\long\def\@gobble@nv#1\end#2{\def\reserved@b{#2}% + \ifx\reserved@a\reserved@b + \edef\reserved@a{\noexpand\end{\reserved@a}}% + \expandafter\reserved@a + \else + \expandafter\@gobble@nv + \fi} + +\renewcommand{\excludecomment}[1]{% + \csname newenvironment\endcsname{#1}{\@gobbleenv}{}} + +%%% RAW HTML +% +% Enclose raw HTML between a \begin{rawhtml} and \end{rawhtml}. +% The html environment ignores its body +% +\excludecomment{rawhtml} + + +%%% HTML ONLY +% +% Enclose LaTeX constructs which will only appear in the +% HTML output and will be ignored by LaTeX with +% \begin{htmlonly} and \end{htmlonly} +% +\excludecomment{htmlonly} +% Shorter version +\newcommand{\html}[1]{} + +% for images.tex only +\excludecomment{imagesonly} + +%%% LaTeX ONLY +% Enclose LaTeX constructs which will only appear in the +% DVI output and will be ignored by latex2html with +%\begin{latexonly} and \end{latexonly} +% +\newenvironment{latexonly}{}{} +% Shorter version +\newcommand{\latex}[1]{#1} + + +%%% LaTeX or HTML +% Combination of \latex and \html. +% Say \latexhtml{this should be latex text}{this html text} +% +%\newcommand{\latexhtml}[2]{#1} +\long\def\latexhtml#1#2{#1} + + +%%% tracing the HTML conversions +% This alters the tracing-level within the processing +% performed by latex2html by adjusting $VERBOSITY +% (see latex2html.config for the appropriate values) +% +\newcommand{\htmltracing}[1]{} +\newcommand{\htmltracenv}[1]{} + + +%%% \strikeout for HTML only +% uses <STRIKE>...</STRIKE> tags on the argument +% LaTeX just gobbles it up. +\newcommand{\strikeout}[1]{} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% JCL - stop input here if LaTeX2e is not present +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\ifx\if@compatibility\undefined + %LaTeX209 + \makeatother\relax\expandafter\endinput +\fi +\if@compatibility + %LaTeX2e in LaTeX209 compatibility mode + \makeatother\relax\expandafter\endinput +\fi + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Start providing LaTeX2e extension: +% This is currently: +% - additional optional argument for \htmladdimg +% - support for segmented documents +% + +\ProvidesPackage{html} + [1996/12/22 v1.1 hypertext commands for latex2html (nd, hws, rrm)] +%%%%MG + +% This command takes as argument a URL pointing to an image. +% The image will be embedded in the HTML document but will +% be ignored in the dvi and ps files. The optional argument +% denotes additional HTML tags. +% +% Example: \htmladdimg[ALT="portrait" ALIGN=CENTER]{portrait.gif} +% +\renewcommand{\htmladdimg}[2][]{} + +%%% HTMLRULE for LaTeX2e +% This command adds a horizontal rule and is valid even within +% a figure caption. +% +% This command is best used with LaTeX2e and HTML 3.2 support. +% It is like \hrule, but allows for options via key--value pairs +% as follows: \htmlrule[key1=value1, key2=value2, ...] . +% Use \htmlrule* to suppress the <BR> tag. +% Eg. \htmlrule[left, 15, 5pt, "none", NOSHADE] produces +% <BR CLEAR="left"><HR NOSHADE SIZE="15">. +% Renew the necessary part. +\renewcommand{\htmlrulestar}[1][all]{} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% renew some definitions to allow optional arguments +% +% The description of the options is missing, as yet. +% +\renewcommand{\latextohtml}{\textup{\LaTeX2\texttt{HTML}}} +\renewcommand{\htmladdnormallinkfoot}[3][]{#2\footnote{#3}} +\renewcommand{\htmladdnormallink}[3][]{#2} +\renewcommand{\htmlbody}[1][]{} +\renewcommand{\hyperref}[1][ref]{\hyperrefx[#1]} +\renewcommand{\hypercite}[1][int]{\hypercitex[#1]} +\renewcommand{\htmlref}[3][]{#2} +\renewcommand{\htmlcite}[1]{#1\htmlcitex} +\newcommand{\htmlcitex}[2][]{{\def\tmp{#1}\ifx\tmp\@empty\else~[#1]\fi}} +\renewcommand{\htmlimage}[2][]{} +\renewcommand{\htmlborder}[2][]{} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% HTML HTMLset HTMLsetenv +% +% These commands do nothing in LaTeX, but can be used to place +% HTML tags or set Perl variables during the LaTeX2HTML processing; +% They are intended for expert use only. + +\newcommand{\HTMLcode}[2][]{} +\ifx\undefined\HTML\newcommand{\HTML}[2][]{}\else +\typeout{*** Warning: \string\HTML\space had an incompatible definition ***}% +\typeout{*** instead use \string\HTMLcode\space for raw HTML code ***}% +\fi +\newcommand{\HTMLset}[3][]{} +\newcommand{\HTMLsetenv}[3][]{} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% The following commands pertain to document segmentation, and +% were added by Herbert Swan <dprhws@edp.Arco.com> (with help from +% Michel Goossens <goossens@cern.ch>): +% +% +% This command inputs internal latex2html tables so that large +% documents can to partitioned into smaller (more manageable) +% segments. +% +\newcommand{\internal}[2][internals]{} + +% +% Define a dummy stub \htmlhead{}. This command causes latex2html +% to define the title of the start of a new segment. It is not +% normally placed in the user's document. Rather, it is passed to +% latex2html via a .ptr file written by \segment. +% +\newcommand{\htmlhead}[3][]{} + +% In the LaTeX2HTML version this will eliminate the title line +% generated by a \segment command, but retains the title string +% for use in other places. +% +\newcommand{\htmlnohead}{} + + +% In the LaTeX2HTML version this put a URL into a <BASE> tag +% within the <HEAD>...</HEAD> portion of a document. +% +\newcommand{\htmlbase}[1]{} +% + +% +% The dummy command \endpreamble is needed by latex2html to +% mark the end of the preamble in document segments that do +% not contain a \begin{document} +% +\newcommand{\startdocument}{} + + +% \tableofchildlinks, \htmlinfo +% by Ross Moore --- extensions dated 27 September 1997 +% +% These do nothing in LaTeX but for LaTeX2HTML they mark +% where the table of child-links and info-page should be placed, +% when the user wants other than the default. +% \tableofchildlinks % put mini-TOC at this location +% \tableofchildlinks[off] % not on current page +% \tableofchildlinks[none] % not on current and subsequent pages +% \tableofchildlinks[on] % selectively on current page +% \tableofchildlinks[all] % on current and all subsequent pages +% \htmlinfo % put info-page at this location +% \htmlinfo[off] % no info-page in current document +% \htmlinfo[none] % no info-page in current document +% *-versions omit the preceding <BR> tag. +% +\newcommand{\tableofchildlinks}{% + \@ifstar\tableofchildlinksstar\tableofchildlinksstar} +\newcommand{\tableofchildlinksstar}[1][]{} + +\newcommand{\htmlinfo}{\@ifstar\htmlinfostar\htmlinfostar} +\newcommand{\htmlinfostar}[1][]{} + + +% This redefines \begin to allow for an optional argument +% which is used by LaTeX2HTML to specify `style-sheet' information + +\let\realLaTeX@begin=\begin +\renewcommand{\begin}[1][]{\realLaTeX@begin} + + +% +% Allocate a new set of section counters, which will get incremented +% for "*" forms of sectioning commands, and for a few miscellaneous +% commands. +% + +\newcounter{lpart} +\newcounter{lchapter}[part] +\@ifundefined{c@chapter}% + {\let\Hchapter\relax \newcounter{lsection}[part]}% + {\let\Hchapter=\chapter \newcounter{lsection}[chapter]} +\newcounter{lsubsection}[section] +\newcounter{lsubsubsection}[subsection] +\newcounter{lparagraph}[subsubsection] +\newcounter{lsubparagraph}[paragraph] +\newcounter{lequation} + +% +% Redefine "*" forms of sectioning commands to increment their +% respective counters. +% +\let\Hpart=\part +%\let\Hchapter=\chapter +\let\Hsection=\section +\let\Hsubsection=\subsection +\let\Hsubsubsection=\subsubsection +\let\Hparagraph=\paragraph +\let\Hsubparagraph=\subparagraph +\let\Hsubsubparagraph=\subsubparagraph + +\ifx\c@subparagraph\undefined + \newcounter{lsubsubparagraph}[lsubparagraph] +\else + \newcounter{lsubsubparagraph}[subparagraph] +\fi + +% +% The following definitions are specific to LaTeX2e: +% (They must be commented out for LaTeX 2.09) +% +\renewcommand{\part}{\@ifstar{\stepcounter{lpart}% + \bgroup\def\tmp{*}\H@part}{\bgroup\def\tmp{}\H@part}} +\newcommand{\H@part}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hpart\tmp} + +\ifx\Hchapter\relax\else + \def\chapter{\resetsections \@ifstar{\stepcounter{lchapter}% + \bgroup\def\tmp{*}\H@chapter}{\bgroup\def\tmp{}\H@chapter}}\fi +\newcommand{\H@chapter}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hchapter\tmp} + +\renewcommand{\section}{\resetsubsections + \@ifstar{\stepcounter{lsection}\bgroup\def\tmp{*}% + \H@section}{\bgroup\def\tmp{}\H@section}} +\newcommand{\H@section}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsection\tmp} + +\renewcommand{\subsection}{\resetsubsubsections + \@ifstar{\stepcounter{lsubsection}\bgroup\def\tmp{*}% + \H@subsection}{\bgroup\def\tmp{}\H@subsection}} +\newcommand{\H@subsection}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubsection\tmp} + +\renewcommand{\subsubsection}{\resetparagraphs + \@ifstar{\stepcounter{lsubsubsection}\bgroup\def\tmp{*}% + \H@subsubsection}{\bgroup\def\tmp{}\H@subsubsection}} +\newcommand{\H@subsubsection}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubsubsection\tmp} + +\renewcommand{\paragraph}{\resetsubparagraphs + \@ifstar{\stepcounter{lparagraph}\bgroup\def\tmp{*}% + \H@paragraph}{\bgroup\def\tmp{}\H@paragraph}} +\newcommand\H@paragraph[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hparagraph\tmp} + +\renewcommand{\subparagraph}{\resetsubsubparagraphs + \@ifstar{\stepcounter{lsubparagraph}\bgroup\def\tmp{*}% + \H@subparagraph}{\bgroup\def\tmp{}\H@subparagraph}} +\newcommand\H@subparagraph[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubparagraph\tmp} + +\ifx\Hsubsubparagraph\relax\else\@ifundefined{subsubparagraph}{}{% +\def\subsubparagraph{% + \@ifstar{\stepcounter{lsubsubparagraph}\bgroup\def\tmp{*}% + \H@subsubparagraph}{\bgroup\def\tmp{}\H@subsubparagraph}}}\fi +\newcommand\H@subsubparagraph[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubsubparagraph\tmp} + +\def\check@align{\def\empty{}\ifx\tmp@a\empty + \else\def\tmp@b{center}\ifx\tmp@a\tmp@b\let\tmp@a\empty + \else\def\tmp@b{left}\ifx\tmp@a\tmp@b\let\tmp@a\empty + \else\def\tmp@b{right}\ifx\tmp@a\tmp@b\let\tmp@a\empty + \else\expandafter\def\expandafter\tmp@a\expandafter{\expandafter[\tmp@a]}% + \fi\fi\fi \def\empty{}\ifx\tmp\empty\let\tmp=\tmp@a \else + \expandafter\def\expandafter\tmp\expandafter{\expandafter*\tmp@a}% + \fi\fi} +% +\def\resetsections{\setcounter{section}{0}\setcounter{lsection}{0}% + \reset@dependents{section}\resetsubsections } +\def\resetsubsections{\setcounter{subsection}{0}\setcounter{lsubsection}{0}% + \reset@dependents{subsection}\resetsubsubsections } +\def\resetsubsubsections{\setcounter{subsubsection}{0}\setcounter{lsubsubsection}{0}% + \reset@dependents{subsubsection}\resetparagraphs } +% +\def\resetparagraphs{\setcounter{lparagraph}{0}\setcounter{lparagraph}{0}% + \reset@dependents{paragraph}\resetsubparagraphs } +\def\resetsubparagraphs{\ifx\c@subparagraph\undefined\else + \setcounter{subparagraph}{0}\fi \setcounter{lsubparagraph}{0}% + \reset@dependents{subparagraph}\resetsubsubparagraphs } +\def\resetsubsubparagraphs{\ifx\c@subsubparagraph\undefined\else + \setcounter{subsubparagraph}{0}\fi \setcounter{lsubsubparagraph}{0}} +% +\def\reset@dependents#1{\begingroup\let \@elt \@stpelt + \csname cl@#1\endcsname\endgroup} +% +% +% Define a helper macro to dump a single \secounter command to a file. +% +\newcommand{\DumpPtr}[2]{% +\count255=\arabic{#1}\def\dummy{dummy}\def\tmp{#2}% +\ifx\tmp\dummy\else\advance\count255 by \arabic{#2}\fi +\immediate\write\ptrfile{% +\noexpand\setcounter{#1}{\number\count255}}} + +% +% Define a helper macro to dump all counters to the file. +% The value for each counter will be the sum of the l-counter +% actual LaTeX section counter. +% Also dump an \htmlhead{section-command}{section title} command +% to the file. +% +\newwrite\ptrfile +\def\DumpCounters#1#2#3#4{% +\begingroup\let\protect=\noexpand +\immediate\openout\ptrfile = #1.ptr +\DumpPtr{part}{lpart}% +\ifx\Hchapter\relax\else\DumpPtr{chapter}{lchapter}\fi +\DumpPtr{section}{lsection}% +\DumpPtr{subsection}{lsubsection}% +\DumpPtr{subsubsection}{lsubsubsection}% +\DumpPtr{paragraph}{lparagraph}% +\DumpPtr{subparagraph}{lsubparagraph}% +\DumpPtr{equation}{lequation}% +\DumpPtr{footnote}{dummy}% +\def\tmp{#4}\ifx\tmp\@empty +\immediate\write\ptrfile{\noexpand\htmlhead{#2}{#3}}\else +\immediate\write\ptrfile{\noexpand\htmlhead[#4]{#2}{#3}}\fi +\dumpcitestatus \dumpcurrentcolor +\immediate\closeout\ptrfile +\endgroup } + + +%% interface to natbib.sty + +\def\dumpcitestatus{} +\def\loadcitestatus{\def\dumpcitestatus{% + \ifciteindex\immediate\write\ptrfile{\noexpand\citeindextrue}% + \else\immediate\write\ptrfile{\noexpand\citeindexfalse}\fi }% +} +\@ifpackageloaded{natbib}{\loadcitestatus}{% + \AtBeginDocument{\@ifpackageloaded{natbib}{\loadcitestatus}{}}} + + +%% interface to color.sty + +\def\dumpcurrentcolor{} +\def\loadsegmentcolors{% + \let\real@pagecolor=\pagecolor + \let\pagecolor\segmentpagecolor + \let\segmentcolor\color + \ifx\current@page@color\undefined \def\current@page@color{{}}\fi + \def\dumpcurrentcolor{\bgroup\def\@empty@{{}}% + \expandafter\def\expandafter\tmp\space####1@{\def\thiscol{####1}}% + \ifx\current@color\@empty@\def\thiscol{}\else + \expandafter\tmp\current@color @\fi + \immediate\write\ptrfile{\noexpand\segmentcolor{\thiscol}}% + \ifx\current@page@color\@empty@\def\thiscol{}\else + \expandafter\tmp\current@page@color @\fi + \immediate\write\ptrfile{\noexpand\segmentpagecolor{\thiscol}}% + \egroup}% + \global\let\loadsegmentcolors=\relax +} + +% These macros are needed within images.tex since this inputs +% the <segment>.ptr files for a segment, so that counters are +% colors are synchronised. +% +\newcommand{\segmentpagecolor}[1][]{% + \@ifpackageloaded{color}{\loadsegmentcolors\bgroup + \def\tmp{#1}\ifx\@empty\tmp\def\next{[]}\else\def\next{[#1]}\fi + \expandafter\segmentpagecolor@\next}% + {\@gobble}} +\def\segmentpagecolor@[#1]#2{\def\tmp{#1}\def\tmpB{#2}% + \ifx\tmpB\@empty\let\next=\egroup + \else + \let\realendgroup=\endgroup + \def\endgroup{\edef\next{\noexpand\realendgroup + \def\noexpand\current@page@color{\current@color}}\next}% + \ifx\tmp\@empty\real@pagecolor{#2}\def\model{}% + \else\real@pagecolor[#1]{#2}\def\model{[#1]}% + \fi + \edef\next{\egroup\def\noexpand\current@page@color{\current@page@color}% + \noexpand\real@pagecolor\model{#2}}% + \fi\next} +% +\newcommand{\segmentcolor}[2][named]{\@ifpackageloaded{color}% + {\loadsegmentcolors\segmentcolor[#1]{#2}}{}} + +\@ifpackageloaded{color}{\loadsegmentcolors}{\let\real@pagecolor=\@gobble + \AtBeginDocument{\@ifpackageloaded{color}{\loadsegmentcolors}{}}} + + +% Define the \segment[align]{file}{section-command}{section-title} command, +% and its helper macros. This command does four things: +% 1) Begins a new LaTeX section; +% 2) Writes a list of section counters to file.ptr, each +% of which represents the sum of the LaTeX section +% counters, and the l-counters, defined above; +% 3) Write an \htmlhead{section-title} command to file.ptr; +% 4) Inputs file.tex. + +\def\segment{\@ifstar{\@@htmls}{\@@html}} +\def\endsegment{} +\newcommand{\@@htmls}[1][]{\@@htmlsx{#1}} +\newcommand{\@@html}[1][]{\@@htmlx{#1}} +\def\@@htmlsx#1#2#3#4{\csname #3\endcsname* {#4}% + \DumpCounters{#2}{#3*}{#4}{#1}\input{#2}} +\def\@@htmlx#1#2#3#4{\csname #3\endcsname {#4}% + \DumpCounters{#2}{#3}{#4}{#1}\input{#2}} + +\makeatother +\endinput + + +% Modifications: +% +% (The listing of Initiales see Changes) + +% $Log: html.sty,v $ +% Revision 1.23 1998/02/26 10:32:24 latex2html +% -- use \providecommand for \latextohtml +% -- implemented \HTMLcode to do what \HTML did previously +% \HTML still works, unless already defined by another package +% -- fixed problems remaining with undefined \chapter +% -- defined \endsegment +% +% Revision 1.22 1997/12/05 11:38:18 RRM +% -- implemented an optional argument to \begin for style-sheet info. +% -- modified use of an optional argument with sectioning-commands +% +% Revision 1.21 1997/11/05 10:28:56 RRM +% -- replaced redefinition of \@htmlrule with \htmlrulestar +% +% Revision 1.20 1997/10/28 02:15:58 RRM +% -- altered the way some special html-macros are defined, so that +% star-variants are explicitly defined for LaTeX +% -- it is possible for these to occur within images.tex +% e.g. \htmlinfostar \htmlrulestar \tableofchildlinksstar +% +% Revision 1.19 1997/10/11 05:47:48 RRM +% -- allow the dummy {tex2html_nowrap} environment in LaTeX +% use it to make its contents be evaluated in environment order +% +% Revision 1.18 1997/10/04 06:56:50 RRM +% -- uses Robin Fairbairns' code for ignored environments, +% replacing the previous comment.sty stuff. +% -- extensions to the \tableofchildlinks command +% -- extensions to the \htmlinfo command +% +% Revision 1.17 1997/07/08 11:23:39 RRM +% include value of footnote counter in .ptr files for segments +% +% Revision 1.16 1997/07/03 08:56:34 RRM +% use \textup within the \latextohtml macro +% +% Revision 1.15 1997/06/15 10:24:58 RRM +% new command \htmltracenv as environment-ordered \htmltracing +% +% Revision 1.14 1997/06/06 10:30:37 RRM +% - new command: \htmlborder puts environment into a <TABLE> cell +% with a border of specified width, + other attributes. +% - new commands: \HTML for setting arbitrary HTML tags, with attributes +% \HTMLset for setting Perl variables, while processing +% \HTMLsetenv same as \HTMLset , but it gets processed +% as if it were an environment. +% - new command: \latextohtml --- to set the LaTeX2HTML name/logo +% - fixed some remaining problems with \segmentcolor & \segmentpagecolor +% +% Revision 1.13 1997/05/19 13:55:46 RRM +% alterations and extra options to \hypercite +% +% Revision 1.12 1997/05/09 12:28:39 RRM +% - Added the optional argument to \htmlhead, also in \DumpCounters +% - Implemented \HTMLset as a no-op in LaTeX. +% - Fixed a bug in accessing the page@color settings. +% +% Revision 1.11 1997/03/26 09:32:40 RRM +% - Implements LaTeX versions of \externalcite and \hypercite commands. +% Thanks to Uffe Engberg and Stephen Simpson for the suggestions. +% +% Revision 1.10 1997/03/06 07:37:58 RRM +% Added the \htmltracing command, for altering $VERBOSITY . +% +% Revision 1.9 1997/02/17 02:26:26 RRM +% - changes to counter handling (RRM) +% - shuffled around some definitions +% - changed \htmlrule of 209 mode +% +% Revision 1.8 1997/01/26 09:04:12 RRM +% RRM: added optional argument to sectioning commands +% \htmlbase sets the <BASE HREF=...> tag +% \htmlinfo and \htmlinfo* allow the document info to be positioned +% +% Revision 1.7 1997/01/03 12:15:44 L2HADMIN +% % - fixes to the color and natbib interfaces +% % - extended usage of \hyperref, via an optional argument. +% % - extended use comment environments to allow shifting expansions +% % e.g. within \multicolumn (`bug' reported by Luc De Coninck). +% % - allow optional argument to: \htmlimage, \htmlhead, +% % \htmladdimg, \htmladdnormallink, \htmladdnormallinkfoot +% % - added new commands: \htmlbody, \htmlnohead +% % - added new command: \tableofchildlinks +% +% Revision 1.6 1996/12/25 03:04:54 JCL +% added patches to segment feature from Martin Wilck +% +% Revision 1.5 1996/12/23 01:48:06 JCL +% o introduced the environment makeimage, which may be used to force +% LaTeX2HTML to generate an image from the contents. +% There's no magic, all what we have now is a defined empty environment +% which LaTeX2HTML will not recognize and thus pass it to images.tex. +% o provided \protect to the \htmlrule commands to allow for usage +% within captions. +% +% Revision 1.4 1996/12/21 19:59:22 JCL +% - shuffled some entries +% - added \latexhtml command +% +% Revision 1.3 1996/12/21 12:22:59 JCL +% removed duplicate \htmlrule, changed \htmlrule back not to create a \hrule +% to allow occurrence in caption +% +% Revision 1.2 1996/12/20 04:03:41 JCL +% changed occurrence of \makeatletter, \makeatother +% added new \htmlrule command both for the LaTeX2.09 and LaTeX2e +% sections +% +% +% jcl 30-SEP-96 +% - Stuck the commands commonly used by both LaTeX versions to the top, +% added a check which stops input or reads further if the document +% makes use of LaTeX2e. +% - Introduced rrm's \dumpcurrentcolor and \bodytext +% hws 31-JAN-96 - Added support for document segmentation +% hws 10-OCT-95 - Added \htmlrule command +% jz 22-APR-94 - Added support for htmlref +% nd - Created diff --git a/docs/man/xend-config.sxp.pod.5 b/docs/man/xend-config.sxp.pod.5 new file mode 100644 index 0000000..3851bba --- /dev/null +++ b/docs/man/xend-config.sxp.pod.5 @@ -0,0 +1,148 @@ +=head1 NAME + +xend-config.sxp - Xen daemon configuration file + +=head1 SYNOPSIS + +/etc/xen/xend-config.sxp + +=head1 DESCRIPTION + +The xend(1) program requires xend-config.sxp to specify operating +parameters which determine the behavior of the daemon at runtime. + +The parameters are specified in S-expression format. See the example +configuration file in I</etc/xen/xend-config.sxp> for details. + +=head1 OPTIONS + +The following lists the daemon configuration parameters: + +=over 4 + +=item I<logfile> + +The location of the file to record runtime log messages. Defaults to +I</var/log/xen/xend.log>. + +=item I<loglevel> + +Filters out messages below the specified level. Possible values are +DEBUG, INFO, WARNING, ERROR, CRITICAL. Defaults to I<DEBUG>. + +=item I<xend-http-server> + +A boolean value that tells xend whether or not to start the http +stream socket management server. Defaults to I<no>. + +=item I<xend-unix-server> + +A boolean value that tells xend whether or not to start the unix +domain socket management server. This is required for the CLI tools +to operate. Defaults to I<yes>. + +=item I<xend-relocation-server> + +A boolean value that tells xend whether or not to start the relocation +server. This is required for cross-machine migrations. Defaults to +I<no>. + +=item I<xend-unix-path> + +The location of the unix domain socket the xend-unix-server will use +to communicate with the management tools. Defaults to +I</var/lib/xend/xend-socket>. + +=item I<xend-port> + +The port that will be used by the http management server. Defaults to +I<8000>. + +=item I<xend-relocation-port> + +The port that will be used by the relocation server. Defaults to +I<8002>. + +=item I<xend-address> + +The address to which the http management server will bind. Defaults +to I<''> which means "all interfaces". + +=item I<xend-relocation-address> + +The address to which the relocation server will bind. Defaults to +I<''> which means "all interfaces". + +=item I<console-limit> + +The kilobyte buffer limit that will be enforced by the console server. +This limit is set per-domain, and is needed to prevent a single domain +from overwhelming the console server with massive amounts of data. +Defaults to I<1024>. + +=item I<network-script> + +The name of the script in I</etc/xen/scripts> that will be run to +setup the networking environment. This can be any name, but in +general is either I<network-bridge> or I<network-route>. + +=item I<vif-script> + +The name of the script in I</etc/xen/scripts> that will be run to +setup a virtual interface when it is created or destroyed. This needs +to (in general) work in unison with the I<network-script>. + +=item I<dom0-min-mem> + +This specifies the minimum number of megabytes that will be reserved +for Domain0. If this value is positive, Domain0 will be automatically +ballooned down to this limit to make space for new domains. If this +is set to 0, Domain0 will not be automatically ballooned. + +=item I<dom0-cpus> + +This specifies the number of CPUs that Domain0 will be allowed to use. +If the value is 0, all available CPUs will be used by Domain0. + +=item I<enable-dump> + +A boolean value that tells xend whether or not core dumps of guest +domains should be saved when a crash occurs. Defaults to I<no>. + +=item I<external-migration-tool> + +The name of an application or script that can handle external device +migration, such as for example virtual TPM migration. An example +script is I</etc/xen/scripts/external-device-migrate>. + +=back + +=head1 EXAMPLES + +An example configuration with relocation enabled for the local network: + +=over 4 + + (xend-relocation-server yes) + (xend-relocation-address 192.0.2.192) + (network-script network-bridge) + (vif-script vif-bridge) + (dom0-min-mem 0) + (dom0-cpus 0) + +=back + +=head1 CAVEATS + +Note that relocation is currently unsecured and is very dangerous if +left enabled. No authentication is performed, and very little sanity +checking takes place. Enable at your own risk. + +=head1 SEE ALSO + +B<xend>(1) + +=head1 AUTHOR + +Dan Smith <danms@us.ibm.com> + diff --git a/docs/man/xm.pod.1 b/docs/man/xm.pod.1 new file mode 100644 index 0000000..ad36fce --- /dev/null +++ b/docs/man/xm.pod.1 @@ -0,0 +1,1399 @@ +=head1 NAME + +xm - Xen management user interface + +=head1 SYNOPSIS + +B<xm> I<subcommand> [I<args>] + +=head1 DESCRIPTION + +The B<xm> program is the main interface for managing Xen guest +domains. The program can be used to create, pause, and shutdown +domains. It can also be used to list current domains, enable or pin +VCPUs, and attach or detach virtual block devices. + +The basic structure of every B<xm> command is almost always: + +=over 2 + +B<xm> I<subcommand> I<domain-id> [I<OPTIONS>] + +=back + +Where I<subcommand> is one of the subcommands listed below, I<domain-id> +is the numeric domain id, or the domain name (which will be internally +translated to domain id), and I<OPTIONS> are subcommand specific +options. There are a few exceptions to this rule in the cases where +the subcommand in question acts on all domains, the entire machine, +or directly on the Xen hypervisor. Those exceptions will be clear for +each of those subcommands. + +=head1 NOTES + +All B<xm> operations rely upon the Xen control daemon, aka B<xend>. +For any B<xm> commands to run, xend must also be running. For this +reason you should start xend as a service when your system first boots +using Xen. + +Most B<xm> commands require root privileges to run due to the +communications channels used to talk to the hypervisor. Running as +non root will return an error. + +Most B<xm> commands act asynchronously, so just because the B<xm> +command returned doesn't mean the action is complete. This is +important, as many operations on domains, like create and shutdown, +can take considerable time (30 seconds or more) to bring the machine +into a fully compliant state. If you want to know when one of these +actions has finished you must poll through B<xm list> periodically. + +=head1 DOMAIN SUBCOMMANDS + +The following subcommands manipulate domains directly. As stated +previously, most commands take I<domain-id> as the first parameter. + +=over 4 + +=item B<console> I<domain-id> + +Attach to domain I<domain-id>'s console. If you've set up your domains to +have a traditional log in console this will look much like a normal +text log in screen. + +This uses the back end xenconsole service which currently only +works for para-virtual domains. + +The attached console will perform much like a standard serial console, +so running curses based interfaces over the console B<is not +advised>. Vi tends to get very odd when using it over this interface. + +=item B<create> I<configfile> [I<OPTIONS>] [I<vars>].. + +The create subcommand requires a config file and can optionally take a +series of I<vars> that add to or override variables defined +in the config file. See L<xmdomain.cfg> for full details of that file +format, and possible options used in either the configfile or for I<vars>. + +I<configfile> can either be an absolute path to a file, or a relative +path to a file located in /etc/xen. + +Create will return B<as soon> as the domain is started. This B<does +not> mean the guest OS in the domain has actually booted, or is +available for input. + +B<OPTIONS> + +=over 4 + +=item B<--help_config> + +Print the available configuration variables I<vars>. These variables may be +used on the command line or in the configuration file I<configfile>. + +=item B<-q>, B<--quiet> + +No console output. + +=item B<--path> + +Search path for configuration scripts. The value of PATH is a +colon-separated directory list. + +=item B<-f=FILE>, B<--defconfig=FILE> + +Use the given Python configuration script. The configuration +script is loaded after arguments have been processed. Each +command-line option sets a configuration variable named after +its long option name, and these variables are placed in the +environment of the script before it is loaded. Variables +for options that may be repeated have list values. Other +variables can be set using name=value on the command line. +After the script is loaded, option values that were not set +on the command line are replaced by the values set in the script. + +=item B<-F=FILE>, B<--config=FILE> + +Use the given SXP formated configuration script. +SXP is the underlying configuration format used by Xen. +SXP configuration scripts can be hand-written or generated +from Python configuration scripts, using the -n +(dryrun) option to print the configuration. An SXP formatted +configuration file may also be generated for a given I<domain-id> by +redirecting the output from the the B<xm list --long I<domain-id>> +to a file. + +=item B<-n>, B<--dryrun> + +Dry run - prints the resulting configuration in SXP +but does not create the domain. + +=item B<-x>, B<--xmldryrun> + +XML dry run - prints the resulting configuration in +XML but does not create the domain. + +=item B<-s>, B<--skipdtd> + +Skip DTD checking - skips checks on XML before +creating. Experimental. Can decrease create time. + +=item B<-p>, B<--paused> + +Leave the domain paused after it is created. + +=item B<-c>, B<--console_autoconnect> + +Attach console to the domain as soon as it has started. This is +useful for determining issues with crashing domains. + +=back + +B<EXAMPLES> + +=over 4 + +=item I<with config file> + + xm create Fedora4 + +This creates a domain with the file /etc/xen/Fedora4, and returns as +soon as it is run. + +=item I<without config file> + + xm create /dev/null ramdisk=initrd.img \ + kernel=/boot/vmlinuz-2.6.12.6-xenU \ + name=ramdisk vif='' vcpus=1 \ + memory=64 root=/dev/ram0 + +This creates the domain without using a config file (more specifically +using /dev/null as an empty config file), kernel and ramdisk as +specified, setting the name of the domain to "ramdisk", also disabling +virtual networking. (This example comes from the xm-test test suite.) + +=back + +=item B<delete> + +Remove a domain from Xend domain management. The B<xm list> command +shows the domain names. + +=item B<destroy> I<domain-id> + +Immediately terminate the domain I<domain-id>. This doesn't give the +domain OS any chance to react, and is the equivalent of ripping the +power cord out on a physical machine. In most cases you will want to +use the B<shutdown> command instead. + +=item B<domid> I<domain-name> + +Converts a domain name to a domain id using xend's internal mapping. + +=item B<domname> I<domain-id> + +Converts a domain id to a domain name using xend's internal mapping. + +=item B<dump-core> [I<OPTIONS>] I<domain-id> [I<filename>] + +Dumps the virtual machine's memory for the specified domain to the +I<filename> specified. The dump file will be written to a distribution +specific directory for dump files. Such as: /var/lib/xen/dump or +/var/xen/dump Defaults to dumping the core without pausing the domain +if no I<OPTIONS> are specified. + +B<OPTIONS> + +=over 4 + +=item B<-L>, B<--live> + +Dump core without pausing the domain. + +=item B<-C>, B<--crash> + +Crash domain after dumping core. + +=back + +=item B<help> [B<--long>] + +Displays the short help message (i.e. common commands). + +The B<--long> option prints out the complete set of B<xm> subcommands, +grouped by function. + +=item B<list> [I<OPTIONS>] [I<domain-id> ...] + +Prints information about one or more domains. If no domains are +specified it prints out information about all domains. + + +B<OPTIONS> + +=over 4 + +=item B<-l>, B<--long> + +The output for B<xm list> is not the table view shown below, but +instead presents the data in SXP format. + +=item B<--label> + +Security labels are added to the output of xm list and the lines +are sorted by the labels (ignoring case). +See the ACCESS CONTROL SUBCOMMAND section of this man page for more +information about labels. + +=item B<--state=<state>> + +Output information for VMs in the specified state. + +=back + +B<EXAMPLE> + +An example format for the list is as follows: + + Name ID Mem(MiB) VCPUs State Time(s) + Domain-0 0 98 1 r----- 5068.6 + Fedora3 164 128 1 r----- 7.6 + Fedora4 165 128 1 ------ 0.6 + Mandrake2006 166 128 1 -b---- 3.6 + Mandrake10.2 167 128 1 ------ 2.5 + Suse9.2 168 100 1 ------ 1.8 + +Name is the name of the domain. ID the numeric domain id. Mem is the +desired amount of memory to allocate to the domain (although it may +not be the currently allocated amount). VCPUs is the number of +virtual CPUs allocated to the domain. State is the run state (see +below). Time is the total run time of the domain as accounted for by +Xen. + +B<STATES> + +The State field lists 6 states for a Xen domain, and which ones the +current domain is in. + +=over 4 + +=item B<r - running> + +The domain is currently running on a CPU. + +=item B<b - blocked> + +The domain is blocked, and not running or runnable. This can be caused +because the domain is waiting on IO (a traditional wait state) or has +gone to sleep because there was nothing else for it to do. + +=item B<p - paused> + +The domain has been paused, usually occurring through the administrator +running B<xm pause>. When in a paused state the domain will still +consume allocated resources like memory, but will not be eligible for +scheduling by the Xen hypervisor. + +=item B<s - shutdown> + +FIXME: Why would you ever see this state? + +=item B<c - crashed> + +The domain has crashed, which is always a violent ending. Usually +this state can only occur if the domain has been configured not to +restart on crash. See L<xmdomain.cfg> for more info. + +=item B<d - dying> + +The domain is in process of dying, but hasn't completely shutdown or +crashed. + +FIXME: Is this right? + +=back + +B<NOTES> + +=over 4 + +The Time column is deceptive. Virtual IO (network and block devices) +used by domains requires coordination by Domain0, which means that +Domain0 is actually charged for much of the time that a DomainU is +doing IO. Use of this time value to determine relative utilizations +by domains is thus very suspect, as a high IO workload may show as +less utilized than a high CPU workload. Consider yourself warned. + +=back + +=item B<mem-max> I<domain-id> I<mem> + +Specify the maximum amount of memory the domain is able to use. I<mem> +is specified in megabytes. + +The mem-max value may not correspond to the actual memory used in the +domain, as it may balloon down its memory to give more back to the OS. + +=item B<mem-set> I<domain-id> I<mem> + +Set the domain's used memory using the balloon driver. + +Because this operation requires cooperation from the domain operating +system, there is no guarantee that it will succeed. This command will +definitely not work unless the domain has the required paravirt +driver. + +B<Warning:> There is no good way to know in advance how small of a +mem-set will make a domain unstable and cause it to crash. Be very +careful when using this command on running domains. + +=item B<migrate> I<domain-id> I<host> [I<OPTIONS>] + +Migrate a domain to another host machine. Xend must be running on +other host machine, it must be running the same version of Xen, it +must have the migration TCP port open and accepting connections from +the source host, and there must be sufficient resources for the domain +to run (memory, disk, etc). + +Migration is pretty complicated, and has many security implications. +Please read the Xen User's Guide to ensure you understand the +ramifications and limitations on migration before attempting it in +production. + +B<OPTIONS> + +=over 4 + +=item B<-l>, B<--live> + +Use live migration. This will migrate the domain between hosts +without shutting down the domain. See the Xen User's Guide for more +information. + +=item B<-r>, B<--resource> I<Mbs> + +Set maximum Mbs allowed for migrating the domain. This ensures that +the network link is not saturated with migration traffic while +attempting to do other useful work. + +=back + +=item B<new> I<configfile> [I<OPTIONS>] [I<vars>]... + +Adds a domain to Xend domain management. + +The new subcommand requires a config file and can optionally +take a series of I<vars> that add to or override variables +defined in the config file. See xmdomain.cfg for full details of that +file format, and possible options used in either the configfile or for +I<vars>. + +I<configfile> can either be an absolute path to a file, or a relative +path to a file located in /etc/xen. + +The new subcommand will return without starting the domain. The +domain needs to be started using the B<xm start> command. + +B<OPTIONS> + +=over 4 + +=item B<--help_config> + +Print the available configuration variables I<vars>. These variables may be +used on the command line or in the configuration file I<configfile>. + +=item B<-q>, B<--quiet> + +No console output. + +=item B<--path> + +Search path for configuration scripts. The value of PATH is a +colon-separated directory list. + +=item B<-f=FILE>, B<--defconfig=FILE> + + +Use the given Python configuration script. The configuration +script is loaded after arguments have been processed. Each +command-line option sets a configuration variable named after +its long option name, and these variables are placed in the +environment of the script before it is loaded. Variables +for options that may be repeated have list values. Other +variables can be set using name=value on the command line. +After the script is loaded, option values that were not set +on the command line are replaced by the values set in the script. + +=item B<-F=FILE>, B<--config=FILE> + +Use the given SXP formated configuration script. +SXP is the underlying configuration format used by Xen. +SXP configuration scripts can be hand-written or generated +from Python configuration scripts, using the -n +(dryrun) option to print the configuration. An SXP formatted +configuration file may also be generated for a given I<domain-id> by +redirecting the output from the the B<xm list --long I<domain-id>> +to a file. + +=item B<-n>, B<--dryrun> + +Dry run - prints the resulting configuration in SXP +but does not create the domain. + +=item B<-x>, B<--xmldryrun> + +XML dry run - prints the resulting configuration in +XML but does not create the domain. + +=item B<-s>, B<--skipdtd> + +Skip DTD checking - skips checks on XML before +creating. Experimental. Can decrease create time. + +=item B<-p>, B<--paused> + +Leave the domain paused after it is created. + +=item B<-c>, B<--console_autoconnect> + +Attach console to the domain as soon as it has started. This is +useful for determining issues with crashing domains. + +=back + +=item B<pause> I<domain-id> + +Pause a domain. When in a paused state the domain will still consume +allocated resources such as memory, but will not be eligible for +scheduling by the Xen hypervisor. + +=item B<reboot> [I<OPTIONS>] I<domain-id> + +Reboot a domain. This acts just as if the domain had the B<reboot> +command run from the console. The command returns as soon as it has +executed the reboot action, which may be significantly before the +domain actually reboots. + +The behavior of what happens to a domain when it reboots is set by the +B<on_reboot> parameter of the xmdomain.cfg file when the domain was +created. + +B<OPTIONS> + +=over 4 + +=item B<-a>, B<--all> + +Reboot all domains. + +=item B<-w>, B<--wait> + +Wait for reboot to complete before returning. This may take a while, +as all services in the domain will have to be shut down cleanly. + +=back + +=item B<restore> I<state-file> + +Build a domain from an B<xm save> state file. See B<save> for more info. + +=item B<resume> I<domain-name> [I<OPTIONS>] + +Moves a domain out of the suspended state and back into memory. + +B<OPTIONS> + +=over 4 + +=item B<-p>, <--paused> + +Moves a domain back into memory but leaves the domain in a paused state. +The B<xm unpause> subcommand may then be used to bring it out of the +paused state. + +=back + +=item B<save> I<domain-id> I<state-file> + +Saves a running domain to a state file so that it can be restored +later. Once saved, the domain will no longer be running on the +system, thus the memory allocated for the domain will be free for +other domains to use. B<xm restore> restores from this state file. + +This is roughly equivalent to doing a hibernate on a running computer, +with all the same limitations. Open network connections may be +severed upon restore, as TCP timeouts may have expired. + +=item B<shutdown> [I<OPTIONS>] I<domain-id> + +Gracefully shuts down a domain. This coordinates with the domain OS +to perform graceful shutdown, so there is no guarantee that it will +succeed, and may take a variable length of time depending on what +services must be shutdown in the domain. The command returns +immediately after signally the domain unless that B<-w> flag is used. + +The behavior of what happens to a domain when it reboots is set by the +B<on_shutdown> parameter of the xmdomain.cfg file when the domain was +created. + +B<OPTIONS> + +=over 4 + +=item B<-a> + +Shutdown B<all> domains. Often used when doing a complete shutdown of +a Xen system. + +=item B<-w> + +Wait for the domain to complete shutdown before returning. + +=back + +=item B<start> I<domain-name> [I<OPTIONS>] + +Start a Xend managed domain that was added using the B<xm new> command. + + +B<OPTIONS> + +=over 4 + +=item B<-p>, B<--paused> + +Do not unpause domain after starting it. + +=item B<-c>, B<--console_autoconnect> + +Connect to the console after the domain is created. + +=back + +=item B<suspend> I<domain-name> + +Suspend a domain to a state file so that it can be later +resumed using the B<xm resume> subcommand. Similar to the B<xm save> +subcommand although the state file may not be specified. + +=item B<sysrq> I<domain-id> I<letter> + +Send a I<Magic System Request> signal to the domain. For more +information on available magic sys req operations, see sysrq.txt in +your Linux Kernel sources. + +=item B<unpause> I<domain-id> + +Moves a domain out of the paused state. This will allow a previously +paused domain to now be eligible for scheduling by the Xen hypervisor. + +=item B<vcpu-set> I<domain-id> I<vcpu-count> + +Enables the I<vcpu-count> virtual CPUs for the domain in question. +Like mem-set, this command can only allocate up to the maximum virtual +CPU count configured at boot for the domain. + +If the I<vcpu-count> is smaller than the current number of active +VCPUs, the highest number VCPUs will be hotplug removed. This may be +important for pinning purposes. + +Attempting to set the VCPUs to a number larger than the initially +configured VCPU count is an error. Trying to set VCPUs to < 1 will be +quietly ignored. + +Because this operation requires cooperation from the domain operating +system, there is no guarantee that it will succeed. This command will +not work with a full virt domain. + +=item B<vcpu-list> [I<domain-id>] + +Lists VCPU information for a specific domain. If no domain is +specified, VCPU information for all domains will be provided. + +=item B<vcpu-pin> I<domain-id> I<vcpu> I<cpus> + +Pins the the VCPU to only run on the specific CPUs. The keyword +B<all> can be used to apply the I<cpus> list to all VCPUs in the +domain. + +Normally VCPUs can float between available CPUs whenever Xen deems a +different run state is appropriate. Pinning can be used to restrict +this, by ensuring certain VCPUs can only run on certain physical +CPUs. + +=back + +=head1 XEN HOST SUBCOMMANDS + +=over 4 + +=item B<dmesg> [B<-c>] + +Reads the Xen message buffer, similar to dmesg on a Linux system. The +buffer contains informational, warning, and error messages created +during Xen's boot process. If you are having problems with Xen, this +is one of the first places to look as part of problem determination. + +B<OPTIONS> + +=over 4 + +=item B<-c>, B<--clear> + +Clears Xen's message buffer. + +=back + +=item B<info> + +Print information about the Xen host in I<name : value> format. When +reporting a Xen bug, please provide this information as part of the +bug report. + +Sample output looks as follows (lines wrapped manually to make the man +page more readable): + + host : talon + release : 2.6.12.6-xen0 + version : #1 Mon Nov 14 14:26:26 EST 2005 + machine : i686 + nr_cpus : 2 + nr_nodes : 1 + cores_per_socket : 1 + threads_per_core : 1 + cpu_mhz : 696 + hw_caps : 0383fbff:00000000:00000000:00000040 + total_memory : 767 + free_memory : 37 + xen_major : 3 + xen_minor : 0 + xen_extra : -devel + xen_caps : xen-3.0-x86_32 + xen_scheduler : credit + xen_pagesize : 4096 + platform_params : virt_start=0xfc000000 + xen_changeset : Mon Nov 14 18:13:38 2005 +0100 + 7793:090e44133d40 + cc_compiler : gcc version 3.4.3 (Mandrakelinux + 10.2 3.4.3-7mdk) + cc_compile_by : sdague + cc_compile_domain : (none) + cc_compile_date : Mon Nov 14 14:16:48 EST 2005 + xend_config_format : 3 + +B<FIELDS> + +Not all fields will be explained here, but some of the less obvious +ones deserve explanation: + +=over 4 + +=item B<hw_caps> + +A vector showing what hardware capabilities are supported by your +processor. This is equivalent to, though more cryptic, the flags +field in /proc/cpuinfo on a normal Linux machine. + +=item B<free_memory> + +Available memory (in MB) not allocated to Xen, or any other domains. + +=item B<xen_caps> + +The Xen version and architecture. Architecture values can be one of: +x86_32, x86_32p (i.e. PAE enabled), x86_64, ia64. + +=item B<xen_changeset> + +The Xen mercurial changeset id. Very useful for determining exactly +what version of code your Xen system was built from. + +=back + +=item B<log> + +Print out the xend log. This log file can be found in +/var/log/xend.log. + +=item B<top> + +Executes the B<xentop> command, which provides real time monitoring of +domains. Xentop is a curses interface, and reasonably self +explanatory. + +=item B<uptime> + +Prints the current uptime of the domains running. + +=back + +=head1 SCHEDULER SUBCOMMANDS + +Xen ships with a number of domain schedulers, which can be set at boot +time with the B<sched=> parameter on the Xen command line. By +default B<credit> is used for scheduling. + +FIXME: we really need a scheduler expert to write up this section. + +=over 4 + +=item B<sched-credit> [ B<-d> I<domain-id> [ B<-w>[B<=>I<WEIGHT>] | B<-c>[B<=>I<CAP>] ] ] + +Set credit scheduler parameters. The credit scheduler is a +proportional fair share CPU scheduler built from the ground up to be +work conserving on SMP hosts. + +Each domain (including Domain0) is assigned a weight and a cap. + +B<PARAMETERS> + +=over 4 + +=item I<WEIGHT> + +A domain with a weight of 512 will get twice as much CPU as a domain +with a weight of 256 on a contended host. Legal weights range from 1 +to 65535 and the default is 256. + +=item I<CAP> + +The cap optionally fixes the maximum amount of CPU a domain will be +able to consume, even if the host system has idle CPU cycles. The cap +is expressed in percentage of one physical CPU: 100 is 1 physical CPU, +50 is half a CPU, 400 is 4 CPUs, etc. The default, 0, means there is +no upper cap. + +=back + +=item B<sched-sedf> I<period> I<slice> I<latency-hint> I<extratime> I<weight> + +Set Simple EDF (Earliest Deadline First) scheduler parameters. This +scheduler provides weighted CPU sharing in an intuitive way and uses +realtime-algorithms to ensure time guarantees. For more information +see docs/misc/sedf_scheduler_mini-HOWTO.txt in the Xen distribution. + +B<PARAMETERS> + +=over 4 + +=item I<period> + +The normal EDF scheduling usage in nanoseconds + +=item I<slice> + +The normal EDF scheduling usage in nanoseconds + +FIXME: these are lame, should explain more. + +=item I<latency-hint> + +Scaled period if domain is doing heavy I/O. + +=item I<extratime> + +Flag for allowing domain to run in extra time. + +=item I<weight> + +Another way of setting CPU slice. + +=back + +B<EXAMPLES> + +I<normal EDF (20ms/5ms):> + + xm sched-sedf <dom-id> 20000000 5000000 0 0 0 + +I<best-effort domains (i.e. non-realtime):> + + xm sched-sedf <dom-id> 20000000 0 0 1 0 + +I<normal EDF (20ms/5ms) + share of extra-time:> + + xm sched-sedf <dom-id> 20000000 5000000 0 1 0 + +I<4 domains with weights 2:3:4:2> + + xm sched-sedf <d1> 0 0 0 0 2 + xm sched-sedf <d2> 0 0 0 0 3 + xm sched-sedf <d3> 0 0 0 0 4 + xm sched-sedf <d4> 0 0 0 0 2 + +I<1 fully-specified (10ms/3ms) domain, 3 other domains share available +rest in 2:7:3 ratio:> + + xm sched-sedf <d1> 10000000 3000000 0 0 0 + xm sched-sedf <d2> 0 0 0 0 2 + xm sched-sedf <d3> 0 0 0 0 7 + xm sched-sedf <d4> 0 0 0 0 3 + +=back + +=head1 VIRTUAL DEVICE COMMANDS + +Most virtual devices can be added and removed while guests are +running. The effect to the guest OS is much the same as any hotplug +event. + +=head2 BLOCK DEVICES + +=over 4 + +=item B<block-attach> I<domain-id> I<be-dev> I<fe-dev> I<mode> [I<bedomain-id>] + +Create a new virtual block device. This will trigger a hotplug event +for the guest. + +B<OPTIONS> + +=over 4 + +=item I<domain-id> + +The domain id of the guest domain that the device will be attached to. + +=item I<be-dev> + +The device in the backend domain (usually domain 0) to be exported. +This can be specified as a physical partition (phy:sda7) or as a file +mounted as loopback (file://path/to/loop.iso). + +=item I<fe-dev> + +How the device should be presented to the guest domain. It can be +specified as either a symbolic name, such as /dev/hdc, for common +devices, or by device id, such as 0x1400 (/dev/hdc device id in hex). + +=item I<mode> + +The access mode for the device from the guest domain. Supported modes +are B<w> (read/write) or B<r> (read-only). + +=item I<bedomain-id> + +The back end domain hosting the device. This defaults to domain 0. + +=back + +B<EXAMPLES> + +=over 4 + +=item I<Mount an ISO as a Disk> + +xm block-attach guestdomain file://path/to/dsl-2.0RC2.iso /dev/hdc ro + +This will mount the dsl ISO as /dev/hdc in the guestdomain as a read +only device. This will probably not be detected as a CD-ROM by the +guest, but mounting /dev/hdc manually will work. + +=back + +=item B<block-detach> I<domain-id> I<devid> [B<--force>] + +Detach a domain's virtual block device. I<devid> may be the symbolic +name or the numeric device id given to the device by domain 0. You +will need to run B<xm block-list> to determine that number. + +Detaching the device requires the cooperation of the domain. If the +domain fails to release the device (perhaps because the domain is hung +or is still using the device), the detach will fail. The B<--force> +parameter will forcefully detach the device, but may cause IO errors +in the domain. + +=item B<block-list> [B<-l>|B<--long>] I<domain-id> + +List virtual block devices for a domain. The returned output is +formatted as a list or as an S-Expression if the B<--long> option was given. + +=back + +=head2 NETWORK DEVICES + +=over 4 + +=item B<network-attach> I<domain-id> [B<script=>I<scriptname>] [B<ip=>I<ipaddr>] +[B<mac=>I<macaddr>] [B<bridge=>I<bridge-name>] [B<backend=>I<bedomain-id>] + +Creates a new network device in the domain specified by I<domain-id>. It +takes the following optional options: + +=back + +B<OPTIONS> + +=over 4 + +=item B<script=>I<scriptname> + +Use the specified script name to bring up the network. Defaults to +the default setting in xend-config.sxp for B<vif-script>. + +=item B<ip=>I<ipaddr> + +Passes the specified IP Address to the adapter on creation. + +FIXME: this currently appears to be B<broken>. I'm not sure under what +circumstances this should actually work. + +=item B<mac=>I<macaddr> + +The MAC address that the domain will see on its Ethernet device. If +the device is not specified it will be randomly generated with the +00:16:3e vendor id prefix. + +=item B<bridge=>I<bridge-name> + +The name of the bridge to attach the vif to, in case you have more +than one. This defaults to xenbr0. + +=item B<backend=>I<bedomain-id> + +The backend domain id. By default this is domain 0. + +=back + +=over 4 + +=item B<network-detach> I<domain-id> I<devid> + +Removes the network device from the domain specified by I<domain-id>. +I<devid> is the virtual interface device number within the domain +(i.e. the 3 in vif22.3). + +FIXME: this is currently B<broken>. Network devices aren't completely +removed from domain 0. + +=item B<network-list> [B<-l>|B<--long>]> I<domain-id> + +List virtual network interfaces for a domain. The returned output is +formatted as a list or as an S-Expression if the B<--long> option was given. + +=back + +=head2 VIRTUAL TPM DEVICES + +=over 4 + +=item B<vtpm-list> [B<-l>|B<--long>] I<domain-id> + +Show the virtual TPM device for a domain. The returned output is +formatted as a list or as an S-Expression if the B<--long> option was given. + +=back + +=head1 VNET COMMANDS + +The Virtual Network interfaces for Xen. + +FIXME: This needs a lot more explanation, or it needs to be ripped +out entirely. + +=over 4 + +=item B<vnet-list> [B<-l>|B<--long>] + +List vnets. + +=item B<vnet-create> I<config> + +Create a vnet from a config file. + +=item B<vnet-delete> I<vnetid> + +Delete a vnet. + +=back + +=head1 ACCESS CONTROL SUBCOMMANDS + +Access Control in Xen consists of two components: (i) The Access +Control Policy (ACP) defines security labels and access rules based on +these labels. (ii) The Access Control Module (ACM) makes access control +decisions by interpreting the policy when domains require to +communicate or to access resources. The Xen access control has +sufficient mechanisms in place to enforce the access decisions even +against maliciously acting user domains (mandatory access control). + +Access rights for domains in Xen are determined by the domain security +label only and not based on the domain Name or ID. The ACP specifies +security labels that can then be assigned to domains and +resources. Every domain must be assigned exactly one security label, +otherwise access control decisions could become indeterministic. ACPs +are distinguished by their name, which is a parameter to most of the +subcommands described below. Currently, the ACP specifies two ways to +interpret labels: + +(1) Simple Type Enforcement: Labels are interpreted to decide access +of domains to communication means and virtual or physical +resources. Communication between domains as well as access to +resources are forbidden by default and can only take place if they are +explicitly allowed by the security policy. The proper assignment of +labels to domains controls the sharing of information (directly +through communication or indirectly through shared resources) between +domains. This interpretation allows to control the overt (intended) +communication channels in Xen. + +(2) Chinese Wall: Labels are interpreted to decide which domains can +co-exist (be run simultaneously) on the same system. This +interpretation allows to prevent direct covert (unintended) channels +and mitigates risks caused by imperfect core domain isolation +(trade-off between security and other system requirements). For a +short introduction to covert channels, please refer to +http://www.multicians.org/timing-chn.html. + +The following subcommands help you to manage security policies in Xen +and to assign security labels to domains. To enable access control +security in Xen, you must compile Xen with ACM support enabled as +described under "Configuring Security" below. There, you will find +also examples of each subcommand described here. + +=over 4 + +=item B<setpolicy> ACM I<policy> + +Makes the given ACM policy available to xend as a I<xend-managed policy>. +The policy is compiled and a mapping (.map) as well as a binary (.bin) +version of the policy is created. The policy is loaded and the system's +bootloader is prepared to boot the system with this policy the next time +it is started. + +=back + +=over 4 + +I<policy> is a dot-separated list of names. The last part is the file +name pre-fix for the policy XML file. The preceding name parts are +translated into the local path pointing to the policy XML file +relative to the global policy root directory +(/etc/xen/acm-security/policies). For example, +example.chwall_ste.client_v1 denotes the policy file +example/chwall_ste/client_v1-security_policy.xml relative to the +global policy root directory. + +=back + +=over 4 + +=item B<resetpolicy> + +Reset the system's policy to the default state where the DEFAULT policy +is loaded and enforced. This operation may fail if for example guest VMs are +running and and one of them uses a different label than what Domain-0 +does. It is best to make sure that no guests are running before issuing +this command. + +=item B<getpolicy> [--dumpxml] + +Displays information about the current xend-managed policy, such as +name and type of the policy, the uuid xend has assigned to it on the +local system, the version of the XML representation and the status +of the policy, such as whether it is currently loaded into Xen or +whether the policy is automatically loaded during system boot. With +the I<--dumpxml> option, the XML representation of the policy is +displayed. + +=item B<dumppolicy> + +Prints the current security policy state information of Xen. + +=item B<labels> [I<policy>] [B<type=dom>|B<res>|B<any>] + +Lists all labels of a I<type> (domain, resource, or both) that are +defined in the I<policy>. Unless specified, the default I<policy> is +the currently enforced access control policy. The default for I<type> +is 'dom'. The labels are arranged in alphabetical order. + +=item B<addlabel> I<label> B<dom> I<configfile> [I<policy>] + +=item B<addlabel> I<label> B<mgt> I<domain name> [I<policy type>:I<policy>] + +=item B<addlabel> I<label> B<res> I<resource> [I<policy>] + +=item B<addlabel> I<label> B<vif-idx> I<domain name> [I<policy type>:I<policy>] + + +Adds the security label with name I<label> to a domain +I<configfile> (dom), a Xend-managed domain (mgt), to the global resource label +file for the given I<resource> (res), or to a managed domain's virtual network +interface (vif) that is specified by its index. Unless specified, +the default I<policy> is the currently enforced access control policy. +This subcommand also verifies that the I<policy> definition supports the +specified I<label> name. + +The only I<policy type> that is currently supported is I<ACM>. + +=item B<rmlabel> B<dom> I<configfile> + +=item B<rmlabel> B<mgt> I<domain name> + +=item B<rmlabel> B<res> I<resource> + +=item B<rmlabel> B<vif-idx> I<domain name> + +Works the same as the B<addlabel> command (above), except that this +command will remove the label from the domain I<configfile> (dom), +a Xend-managed domain (mgt), the global resource label file (res), +or a managed domain's network interface (vif). + +=item B<getlabel> B<dom> I<configfile> + +=item B<getlabel> B<mgt> I<domain name> + +=item B<getlabel> B<res> I<resource> + +=item B<getlabel> B<vif-idx> I<domain name> + +Shows the label for a domain's configuration in the given I<configfile>, +a xend-managed domain (mgt), a resource, or a managed domain's network +interface (vif). + +=item B<resources> + +Lists all resources in the global resource label file. Each resource +is listed with its associated label and policy name. + +=item B<dry-run> I<configfile> + +Determines if the specified I<configfile> describes a domain with a valid +security configuration for type enforcement. The test shows the policy +decision made for each resource label against the domain label as well as +the overall decision. + +B<CONFIGURING SECURITY> + +=over 4 + +In xen_source_dir/Config.mk set the following parameter: + + XSM_ENABLE ?= y + ACM_SECURITY ?= y + +Then recompile and install xen and the security tools and then reboot: + + cd xen_source_dir; make clean; make install + reboot into Xen + +=back + +B<RESETTING THE SYSTEM'S SECURITY> + +=over 4 + +To set the system's security policy enforcement into its default state, +the follow command can be issued. Make sure that no guests are running +while doing this. + + xm resetpolicy + +After this command has successfully completed, the system's DEFAULT policy +is enforced. + +=back + +B<SETTING A SECURITY POLICY> + +=over 4 + +This step sets the system's policy and automatically loads it into Xen +for enforcement. + + xm setpolicy ACM example.client_v1 + +=back + +B<LISTING SECURITY LABELS> + +=over 4 + +This subcommand shows all labels that are defined and which can be +attached to domains. + + xm labels example.client_v1 type=dom + +will print for our example policy: + + dom_BoincClient + dom_Fun + dom_HomeBanking + dom_NetworkDomain + dom_StorageDomain + dom_SystemManagement + +=back + +B<ATTACHING A SECURITY LABEL TO A DOMAIN> + +=over 4 + +The B<addlabel> subcommand can attach a security label to a domain +configuration file, here a HomeBanking label. The example policy +ensures that this domain does not share information with other +non-homebanking user domains (i.e., domains labeled as dom_Fun or +dom_Boinc) and that it will not run simultaneously with domains +labeled as dom_Fun. + +We assume that the specified myconfig.xm configuration file actually +instantiates a domain that runs workloads related to home-banking, +probably just a browser environment for online-banking. + + xm addlabel dom_HomeBanking dom myconfig.xm + +The very simple configuration file might now look as printed +below. The B<addlabel> subcommand added the B<access_control> entry at +the end of the file, consisting of a label name and the policy that +specifies this label name: + + kernel = "/boot/vmlinuz-2.6.16-xen" + ramdisk="/boot/U1_home_banking_ramdisk.img" + memory = 164 + name = "homebanking" + vif = [ '' ] + dhcp = "dhcp" + access_control = ['policy=example.chwall_ste.client_v1, + label=dom_HomeBanking'] + +Security labels must be assigned to domain configurations because +these labels are essential for making access control decisions as +early as during the configuration phase of a newly instantiated +domain. Consequently, a security-enabled Xen hypervisor will only +start domains that have a security label configured and whose security +label is consistent with the currently enforced policy. Otherwise, +starting the domain will fail with the error condition "operation not +permitted". + +=back + +B<ATTACHING A SECURITY LABEL TO A XEND-MANAGED DOMAIN> + +=over 4 + +The addlabel subcommand supports labeling of domains that are managed +by xend. This includes domains that are currently running, such as for +example Domain-0, or those that are in a dormant state. +Depending on the state of the system, it is possible that the new label +is rejected. An example for a reason for the rejection of the relabeling +of a domain would be if a domain is currently allowed to +access its labeled resources but due to the new label would be prevented +from accessing one or more of them. + + xm addlabel dom_Fun mgt Domain-0 + +This changes the label of Domain-0 to dom_Fun under the condition that +this new label of Domain-0 would not prevent any other domain from +accessing its resources that are provided through Domain-0, such as for +example network or block device access. + +=back + +B<ATTACHING A SECURITY LABEL TO A RESOURCE> + +=over 4 + +The B<addlabel> subcommand can also be used to attach a security +label to a resource. Following the home banking example from above, +we can label a disk resource (e.g., a physical partition or a file) +to make it accessible to the home banking domain. The example policy +provides a resource label, res_LogicalDiskPartition1(hda1), that is +compatible with the HomeBanking domain label. + + xm addlabel "res_LogicalDiskPartition1(hda1)" res phy:hda6 + +After labeling this disk resource, it can be attached to the domain +by adding a line to the domain configuration file. The line below +attaches this disk to the domain at boot time. + + disk = [ 'phy:hda6,sda2,w' ] + +Alternatively, the resource can be attached after booting the domain +by using the B<block-attach> subcommand. + + xm block-attach homebanking phy:hda6 sda2 w + +Note that labeled resources cannot be used when security is turned +off. Any attempt to use labeled resources with security turned off +will result in a failure with a corresponding error message. The +solution is to enable security or, if security is no longer desired, +to remove the resource label using the B<rmlabel> subcommand. + +=back + +B<STARTING AND LISTING LABELED DOMAINS> + +=over 4 + + xm create myconfig.xm + + xm list --label + + Name ID ... Time(s) Label + homebanking 23 ... 4.4 dom_HomeBanking + Domain-0 0 ... 2658.8 dom_SystemManagement + +=back + +B<LISTING LABELED RESOURCES> + +=over 4 + + xm resources + + phy:hda6 + type: ACM + policy: example.chwall_ste.client_v1 + label: res_LogicalDiskPartition1(hda1) + file:/xen/disk_image/disk.img + type: ACM + policy: example.chwall_ste.client_v1 + label: res_LogicalDiskPartition2(hda2) + +=back + +B<POLICY REPRESENTATIONS> + +=over 4 + +We distinguish three representations of the Xen access control policy: +the source XML version, its binary counterpart, and a mapping +representation that enables the tools to deterministically translate +back and forth between label names of the XML policy and label +identifiers of the binary policy. All three versions must be kept +consistent to achieve predictable security guarantees. + +The XML version is the version that users are supposed to create or +change, either by manually editing the XML file or by using the Xen +policy generation tool (B<xensec_gen>). After changing the XML file, +run the B<setpolicy> subcommand to ensure that the new policy is +available to xend. Use, for example, the subcommand +B<activatepolicy> to activate the changes during the next system +reboot. + +The binary version of the policy is derived from the XML policy by +tokenizing the specified labels and is used inside Xen only. It is +created with the B<setpolicy> subcommand. Essentially, the binary +version is much more compact than the XML version and is easier to +evaluate during access control decisions. + +The mapping version of the policy is created during the XML-to-binary +policy translation (B<setpolicy>) and is used by xend and the management +tools to translate between label names used as input to the tools and +their binary identifiers (ssidrefs) used inside Xen. + +=back + +=back + +=head1 SEE ALSO + +B<xmdomain.cfg>(5), B<xentop>(1) + +=head1 AUTHOR + + Sean Dague <sean at dague dot net> + Daniel Stekloff <dsteklof at us dot ibm dot com> + Reiner Sailer <sailer at us dot ibm dot com> + Stefan Berger <stefanb at us dot ibm dot com> + +=head1 BUGS diff --git a/docs/man/xmdomain.cfg.pod.5 b/docs/man/xmdomain.cfg.pod.5 new file mode 100644 index 0000000..f4a8b79 --- /dev/null +++ b/docs/man/xmdomain.cfg.pod.5 @@ -0,0 +1,360 @@ +=head1 NAME + +xmdomain.cfg - xm domain config file format + +=head1 SYNOPSIS + + /etc/xen/myxendomain + /etc/xen/myxendomain2 + /etc/xen/auto/myxenautostarted + +=head1 DESCRIPTION + +The B<xm>(1) program uses python executable config files to define +domains to create from scratch. Each of these config files needs to +contain a number of required options, and may specify many more. + +Domain configuration files live in /etc/xen by default, if you store +config files anywhere else the full path to the config file must be +specified in the I<xm create> command. + +/etc/xen/auto is a special case. Domain config files in that +directory will be started automatically at system boot if the +xendomain init script is enabled. The contents of /etc/xen/auto +should be symlinks to files in /etc/xen to allow I<xm create> to be +used without full paths. + +Options are specified by I<name = value> statements in the +xmdomain.cfg files. + +=head1 OPTIONS + +The following lists the most commonly used options for a domain config +file. + +=over 4 + +=item B<kernel> + +The kernel image for the domain. The format of the parameter is the +fully qualified path to the kernel image file, +i.e. I</boot/vmlinuz-2.6.12-xenU>. + + +=item B<ramdisk> + +The initial ramdisk for the domain. The format of the parameter is +the fully qualified path to the initrd, i.e. I</boot/initrd.gz>. On +many Linux distros you will not need a ramdisk if using the default +xen kernel. + +=item B<memory> + +The amount of RAM, in megabytes, to allocate to the domain when it +starts. Allocating insufficient memory for a domain may produce +extremely bizarre behavior. If there isn't enough free memory left on +the machine to fulfill this request, the domain will fail to start. + +Xen does not support overcommit of memory, so the total memory of all +guests (+ 64 MB needed for Xen) must be less than or equal to the +physical RAM in the machine. + +=item B<name> + +A unique name for the domain. Attempting to create two domains with +the same name will cause an error. + +=item B<root> + +Specifies the root device for the domain. This is required for Linux +domains, and possibly other OSes. + +=item B<nics> + +The number of network interfaces allocated to the domain on boot. It +defaults to 1. + +=item B<disk> + +An array of block device stanzas, in the form: + + disk = [ "stanza1", "stanza2", ... ] + +Each stanza has 3 terms, separated by commas, +"backend-dev,frontend-dev,mode". + +=over 4 + +=item I<backend-dev> + +The device in the backend domain that will be exported to the guest +(frontend) domain. Supported formats include: + +I<phy:device> - export the physical device listed. The device can be +in symbolic form, as in sda7, or as the hex major/minor number, as in +0x301 (which is hda1). + +I<file://path/to/file> - export the file listed as a loopback device. +This will take care of the loopback setup before exporting the device. + +=item I<frontend-dev> + +How the device should appear in the guest domain. The device can be +in symbolic form, as in sda7, or as the hex major/minor number, as in +0x301 (which is hda1). + +=item I<mode> + +The access mode for the device. There are currently 2 valid options, +I<r> (read-only), I<w> (read/write). + +=back + +=item B<vif> + +An array of virtual interface stanzas in the form: + + vif = [ "stanza1", "stanza2", ... ] + +Each stanza specifies a set of I<name = value> options separated by +commas, in the form: "name1=value1, name2=value2, ..." + +B<OPTIONS> + +=over 4 + +=item I<bridge> + +The network bridge to be used for this device. This is especially +needed if multiple bridges exist on the machine. + +=item I<mac> + +The MAC address for the virtual interface. If mac is not specified, +one will be randomly chosen by xen with the 00:16:3e vendor id prefix. + +=back + +=item B<vfb> + +A virtual frame buffer stanza in the form: + + vfb = [ "stanza" ] + +The stanza specifies a set of I<name = value> options separated by +commas, in the form: "name1=value1, name2=value2, ..." + +B<OPTIONS> + +=over 4 + +=item I<type> + +There are currently two valid options: I<vnc> starts a VNC server that +lets you connect an external VNC viewer, and I<sdl> starts an internal +viewer. + +=item I<vncdisplay> + +The VNC display number to use, defaults to the domain ID. The +VNC server listens on port 5900 + display number. + +=item I<vnclisten> + +The listening address for the VNC server, default 127.0.0.1. + +=item I<vncunused> + +If non-zero, the VNC server listens on the first unused port above +5900. + +=item I<vncpasswd> + +Overrides the XenD configured default password. + +=item I<display> + +Display to use for the internal viewer, defaults to environment +variable I<DISPLAY>. + +=item I<xauthority> + +Authority file to use for the internal viewer, defaults to environment +variable I<XAUTHORITY>. + +=back + +=back + +=head1 ADDITIONAL OPTIONS + +The following options are also supported in the config file, though +are far more rarely used. + +=over 4 + +=item B<builder> + +Which builder should be used to construct the domain. This defaults +to the I<linux> if not specified, which is the builder for +paravirtualized Linux domains. + +=item B<cpu> + +Specifies which CPU the domain should be started on, where 0 specifies +the first cpu, 1 the second, and so on. This defaults to -1, which +means Xen is free to pick which CPU to start on. + +=item B<cpus> + +Specifies a list of CPUs on which the domains' VCPUs are allowed to +execute upon. The syntax supports ranges (0-3), and negation, ^1. +For instance: + + cpus = "0-3,5,^1" + +Will result in CPUs 0, 2, 3, 5 being available for use by the domain. + +=item B<extra> + +Extra information to append to the end of the kernel parameter line. +The format is a string, the contents of which can be anything that the +kernel supports. For instance: + + extra = "4" + +Will cause the domain to boot to runlevel 4. + +=item B<nfs_server> + +The IP address of the NFS server to use as the root device for the +domain. In order to do this you'll need to specify I<root=/dev/nfs>, +and specify I<nfs_root>. + +=item B<nfs_root> + +The directory on the NFS server to be used as the root filesystem. +Specified as a fully qualified path, i.e. I</full/path/to/root/dir>. + +=item B<vcpus> + +The number of virtual cpus to allocate to the domain. In order to use +this the xen kernel must be compiled with SMP support. + +This defaults to 1, meaning running the domain as a UP. + +=back + +=head1 DOMAIN SHUTDOWN OPTIONS + +There are 3 options which control domain shutdown (both planned and +unplanned) under certain events. The 3 events currently captured are: + +=over 4 + +=item B<on_shutdown> + +Triggered on either an I<xm shutdown> or graceful shutdown from inside +the DomU. + +=item B<on_reboot> + +Triggered on either an I<xm reboot> or graceful reboot from inside the +DomU. + +=item B<on_crash> + +Triggered when a DomU goes to the crashed state for any reason. + +=back + +All of them take one of 4 valid states listed below. + +=over 4 + +=item B<destroy> + +The domain will be cleaned up completely. No attempt at respawning +will occur. This is what a typical shutdown would look like. + +=item B<restart> + +The domain will be restarted with the same name as the old domain. +This is what a typical reboot would look like. + +=item B<preserve> + +The domain will not be cleaned up at all. This is often useful for +crash state domains which ensures that enough evidence is to debug the +real issue. + +=item B<rename-restart> + +The old domain will not be cleaned up, but will be renamed so a new +domain can be restarted in it's place. The old domain will be renamed with +a suffix -1, -2, etc, and assigned a new random UUID; the new domain will +keep the original name and UUID. The old domain will release the devices that +it holds, so that the new one may take them. + +=back + +=over 4 + +Additionally, the "on_crash" event can also take: + +=item B<coredump-destroy> + +Dump the crashed domain's core and then destroy it. + +=back + +=item B<coredump-restart> + +Dump the crashed domain's core and then restart it. + +=back + +=head1 EXAMPLES + +The following are quick examples of ways that domains might be +configured. They should not be considered an exhaustive set. + +=over 4 + +=item I<A Loopback File as Root> + + kernel = "/boot/vmlinuz-2.6-xenU" + memory = 128 + name = "MyLinux" + root = "/dev/hda1 ro" + disk = [ "file:/var/xen/mylinux.img,hda1,w" ] + +This creates a domain called MyLinux with 128 MB of memory using a +default xen kernel, and the file /var/xen/mylinux.img loopback mounted +at hda1, which is the root filesystem. + +=item I<NFS Root> + +FIXME: write me + +=item I<LVM Root> + +FIXME: write me + +=item I<Two Networks> + +FIXME: write me + +=back + +=head1 SEE ALSO + +B<xm>(1) + +=head1 AUTHOR + + Sean Dague <sean at dague dot net> + +=head1 BUGS + +Not all options are currently documented diff --git a/docs/misc/VMX_changes.txt b/docs/misc/VMX_changes.txt new file mode 100644 index 0000000..276af66 --- /dev/null +++ b/docs/misc/VMX_changes.txt @@ -0,0 +1,90 @@ +Changes to Xen in support of Intel(R) Vanderpool Technology +------------------------------------------------------------- + +Our VT extensions to the Xen hypervisor provide full platform +virtualization, including CPU(s), memory, and I/O infrastructure. The +generic code in Xen handles and schedules those virtual machines as it +does for the existing para-virtualized domains. + +Full virtualization required by the OS guests requires full device +virtualization as well. The device models in BOCHS +(http://bochs.sourceforge.net/) were decoupled from the CPU +virtualization, and are used to virtualize the legacy devices (such as +keyboard, mouse, VGA, IDE) in the PC platform. At this point, the +device models run in user mode on domain 0, not in the Xen hypervisor. + +We would like to thank Ian Pratt and Keir Fraser for reviewing our +design and code intensively, and for providing numerous useful +suggestions to improve the architecture and code. + +We have a list of Intel team members who take credit for making this +release happen: Yunhong Jiang, Nitin Kamble, Chengyuan Li, Xin Li, +Xiaofeng Ling, Benjamin Liu, Asit Mallick, Jun Nakajima, Sunil Saxena, +Arun Sharma, Edwin Zhai, Jeff Zheng, and Louis Zhuang. We'll continue +to add more features to complete full virtualization in Xen using VT. + +The notes document the changes to the Xen hypervisor in order to add +VT support. The changes to other areas, such as Control Panel will be +added as we deliver the code. + +Summary of changes for the first release +---------------------------------------- +December 15, 2004 + + * VT specific event handling and domain management were added. + + * Shadow mode was extended to support full 32-bit guests + + * Domain switching code was extended to support VT domain + + * I/O request handling was added to communicate with the device model + + * Domain builder was extended to provide the environment when the + guest enters the protected mode, including E820 memory and VGA + info, typically obtained by BIOS calls. + +New code: +--------- + VT (Vanderpool Technology) is based on the new VMX (Virtual + Machine Extensions) architecture. The current release of the + software supports 32-bit only. + + * arch/x86/vmx.[ch] and arch/x86/vmx_*.[ch]: created to handle + VMX-specific events in order to provide virtual machine. + + * arch/x86/x86_32/entry.S: new code path was added to have the + first-level handler from VM exits. The first-level handler calls + the second-level handler in arch/x86/vmx.c. + + * arch/x86/setup.c: new function start_vmx() to init_intel() to + enable VMX mode. + + * include/asm-x86/config.h: #ifdef CONFIG_VMX was added. + + * arch/x86/domain.c: new code patch was added to create a VMX + domain given the flag from the control panel. + + * include/public/io/ioreq.h: A new data structure was added to + define the I/O requests between the Xen hypervisor and the + device models. + +Changes to the existing code: +----------------------------- + + * arch/x86/shadow.[ch]: new mode SHM_full_32 was added to support + full virtualization. The current Xen code assumes that the guest + page directory and tables have _machine_ (or host) physical page + frame numbers, and the new code allows to support _guest_ + physical page frame numbers + + * include/asm-x86/processor.h: struct arch_vmx_struct arch_vmx has + been added to the thread_struct data structure. The arch_vmx has + the additional VMX-related CPU context. + + * arch/x86/io_apic.c: reverse mapping between vector and irq has + been added. We will revisit this code when considering MSI + support. + +--- Jun + + diff --git a/docs/misc/blkif-drivers-explained.txt b/docs/misc/blkif-drivers-explained.txt new file mode 100644 index 0000000..7f50494 --- /dev/null +++ b/docs/misc/blkif-drivers-explained.txt @@ -0,0 +1,485 @@ +=== How the Blkif Drivers Work === +Andrew Warfield +andrew.warfield@cl.cam.ac.uk + +The intent of this is to explain at a fairly detailed level how the +split device drivers work in Xen 1.3 (aka 2.0beta). The intended +audience for this, I suppose, is anyone who intends to work with the +existing blkif interfaces and wants something to help them get up to +speed with the code in a hurry. Secondly though, I hope to break out +the general mechanisms that are used in the drivers that are likely to +be necessary to implement other drivers interfaces. + +As a point of warning before starting, it is worth mentioning that I +anticipate much of the specifics described here changing in the near +future. There has been talk about making the blkif protocol +a bit more efficient than it currently is. Keir's addition of grant +tables will change the current remapping code that is used when shared +pages are initially set up. + +Also, writing other control interface types will likely need support +from Xend, which at the moment has a steep learning curve... this +should be addressed in the future. + +For more information on the driver model as a whole, read the +"Reconstructing I/O" technical report +(http://www.cl.cam.ac.uk/Research/SRG/netos/papers/2004-xenngio.pdf). + +==== High-level structure of a split-driver interface ==== + +Why would you want to write a split driver in the first place? As Xen +is a virtual machine manager and focuses on isolation as an initial +design principle, it is generally considered unwise to share physical +access to devices across domains. The reasons for this are obvious: +when device resources are shared, misbehaving code or hardware can +result in the failure of all of the client applications. Moreover, as +virtual machines in Xen are entire OSs, standard device drives that +they might use cannot have multiple instantiations for a single piece +of hardware. In light of all this, the general approach in Xen is to +give a single virtual machine hardware access to a device, and where +other VMs want to share the device, export a higher-level interface to +facilitate that sharing. If you don't want to share, that's fine. +There are currently Xen users actively exploring running two +completely isolated X-Servers on a Xen host, each with it's own video +card, keyboard, and mouse. In these situations, the guests need only +be given physical access to the necessary devices and left to go on +their own. However, for devices such as disks and network interfaces, +where sharing is required, the split driver approach is a good +solution. + +The structure is like this: + + +--------------------------+ +--------------------------+ + | Domain 0 (privileged) | | Domain 1 (unprivileged) | + | | | | + | Xend ( Application ) | | | + | Blkif Backend Driver | | Blkif Frontend Driver | + | Physical Device Driver | | | + +--------------------------+ +--------------------------+ + +--------------------------------------------------------+ + | X E N | + +--------------------------------------------------------+ + + +The Blkif driver is in two parts, which we refer to as frontend (FE) +and a backend (BE). Together, they serve to proxy device requests +between the guest operating system in an unprivileged domain, and the +physical device driver in the physical domain. An additional benefit +to this approach is that the FE driver can provide a single interface +for a whole class of physical devices. The blkif interface mounts +IDE, SCSI, and our own VBD-structured disks, independent of the +physical driver underneath. Moreover, supporting additional OSs only +requires that a new FE driver be written to connect to the existing +backend. + +==== Inter-Domain Communication Mechanisms ==== + +===== Event Channels ===== + +Before getting into the specifics of the block interface driver, it is +worth discussing the mechanisms that are used to communicate between +domains. Two mechanisms are used to allow the construction of +high-performance drivers: event channels and shared-memory rings. + +Event channels are an asynchronous interdomain notification +mechanism. Xen allows channels to be instantiated between two +domains, and domains can request that a virtual irq be attached to +notifications on a given channel. The result of this is that the +frontend domain can send a notification on an event channel, resulting +in an interrupt entry into the backend at a later time. + +The event channel between two domains is instantiated in the Xend code +during driver startup (described later). Xend's channel.py +(tools/python/xen/xend/server/channel.py) defines the function + + +def eventChannel(dom1, dom2): + return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2) + + +which maps to xc_evtchn_bind_interdomain() in tools/libxc/xc_evtchn.c, +which in turn generates a hypercall to Xen to patch the event channel +between the domains. Only a privileged domain can request the +creation of an event channel. + +Once the event channel is created in Xend, its ends are passed to both the +front and backend domains over the control channel. The end that is +passed to a domain is just an integer "port" uniquely identifying the +event channel's local connection to that domain. An example of this +setup code is in linux-2.6.x/drivers/xen/blkfront/blkfront.c in +blkif_connect(), which receives several status change events as +the driver starts up. It is passed an event channel end in a +BLKIF_INTERFACE_STATUS_CONNECTED message, and patches it in like this: + + + blkif_evtchn = status->evtchn; + blkif_irq = bind_evtchn_to_irq(blkif_evtchn); + if ( (rc = request_irq(blkif_irq, blkif_int, + SA_SAMPLE_RANDOM, "blkif", NULL)) ) + printk(KERN_ALERT"blkfront request_irq failed (%ld)\n",rc); + + +This code associates a virtual irq with the event channel, and +attaches the function blkif_int() as an interrupt handler for that +irq. blkif_int() simply handles the notification and returns, it does +not need to interact with the channel at all. + +An example of generating a notification can also be seen in blkfront.c: + + +static inline void flush_requests(void) +{ + DISABLE_SCATTERGATHER(); + wmb(); /* Ensure that the frontend can see the requests. */ + blk_ring->req_prod = req_prod; + notify_via_evtchn(blkif_evtchn); +} +}}} + +notify_via_evtchn() issues a hypercall to set the event waiting flag on +the other domain's end of the channel. + +===== Communication Rings ===== + +Event channels are strictly a notification mechanism between domains. +To move large chunks of data back and forth, Xen allows domains to +share pages of memory. We use communication rings as a means of +managing access to a shared memory page for message passing between +domains. These rings are not explicitly a mechanism of Xen, which is +only concerned with the actual sharing of the page and not how it is +used, they are however worth discussing as they are used in many +places in the current code and are a useful model for communicating +across a shared page. + +A shared page is set up by a front end guest first allocating and passing +the address of a page in its own address space to the backend driver. + +Consider the following code, also from blkfront.c. Note: this code +is in blkif_disconnect(). The driver transitions from STATE_CLOSED +to STATE_DISCONNECTED before becoming CONNECTED. The state automata +is in blkif_status(). + + blk_ring = (blkif_ring_t *)__get_free_page(GFP_KERNEL); + blk_ring->req_prod = blk_ring->resp_prod = resp_cons = req_prod = 0; + ... + /* Construct an interface-CONNECT message for the domain controller. */ + cmsg.type = CMSG_BLKIF_FE; + cmsg.subtype = CMSG_BLKIF_FE_INTERFACE_CONNECT; + cmsg.length = sizeof(blkif_fe_interface_connect_t); + up.handle = 0; + up.shmem_frame = virt_to_machine(blk_ring) >> PAGE_SHIFT; + memcpy(cmsg.msg, &up, sizeof(up)); + + +blk_ring will be the shared page. The producer and consumer pointers +are then initialised (these will be discussed soon), and then the +machine address of the page is send to the backend via a control +channel to Xend. This control channel itself uses the notification +and shared memory mechanisms described here, but is set up for each +domain automatically at startup. + +The backend, which is a privileged domain then takes the page address +and maps it into its own address space (in +linux26/drivers/xen/blkback/interface.c:blkif_connect()): + + +void blkif_connect(blkif_be_connect_t *connect) + + ... + unsigned long shmem_frame = connect->shmem_frame; + ... + + if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL ) + { + connect->status = BLKIF_BE_STATUS_OUT_OF_MEMORY; + return; + } + + prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED); + error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr), + shmem_frame<<PAGE_SHIFT, PAGE_SIZE, + prot, domid); + + ... + + blkif->blk_ring_base = (blkif_ring_t *)vma->addr +}}} + +The machine address of the page is passed in the shmem_frame field of +the connect message. This is then mapped into the virtual address +space of the backend domain, and saved in the blkif structure +representing this particular backend connection. + +NOTE: New mechanisms will be added very shortly to allow domains to +explicitly grant access to their pages to other domains. This "grant +table" support is in the process of being added to the tree, and will +change the way a shared page is set up. In particular, it will remove +the need of the remapping domain to be privileged. + +Sending data across shared rings: + +Shared rings avoid the potential for write interference between +domains in a very cunning way. A ring is partitioned into a request +and a response region, and domains only work within their own space. +This can be thought of as a double producer-consumer ring -- the ring +is described by four pointers into a circular buffer of fixed-size +records. Pointers may only advance, and may not pass one another. + + + resp_cons----+ + V + +----+----+----+----+----+----+----+ + | | | free(A) |RSP1|RSP2| + +----+----+----+----+----+----+----+ + req_prod->| | --------> |RSP3| + +----+ +----+ + |REQ8| | |<-resp_prod + +----+ +----+ + |REQ7| | | + +----+ +----+ + |REQ6| <-------- | | + +----+----+----+----+----+----+----+ + |REQ5|REQ4| free(B) | | | + +----+----+----+----+----+----+----+ + req_cons---------^ + + + +By adopting the convention that every request will receive a response, +not all four pointers need be shared and flow control on the ring +becomes very easy to manage. Each domain manages its own +consumer pointer, and the two producer pointers are visible to both +(xen/include/public/io/blkif.h): + + +/* NB. Ring size must be small enough for sizeof(blkif_ring_t) <=PAGE_SIZE.*/ + #define BLKIF_RING_SIZE 64 + + ... + +/* + * We use a special capitalised type name because it is _essential_ that all + * arithmetic on indexes is done on an integer type of the correct size. + */ +typedef u32 BLKIF_RING_IDX; + +/* + * Ring indexes are 'free running'. That is, they are not stored modulo the + * size of the ring buffer. The following macro converts a free-running counter + * into a value that can directly index a ring-buffer array. + */ +#define MASK_BLKIF_IDX(_i) ((_i)&(BLKIF_RING_SIZE-1)) + +typedef struct { + BLKIF_RING_IDX req_prod; /* 0: Request producer. Updated by front-end. */ + BLKIF_RING_IDX resp_prod; /* 4: Response producer. Updated by back-end. */ + union { /* 8 */ + blkif_request_t req; + blkif_response_t resp; + } PACKED ring[BLKIF_RING_SIZE]; +} PACKED blkif_ring_t; + + + +As shown in the diagram above, the rules for using a shared memory +ring are simple. + + 1. A ring is full when a domain's producer and consumer pointers are + equal (e.g. req_prod == resp_cons). In this situation, the + consumer pointer must be advanced. Furthermore, if the consumer + pointer is equal to the other domain's producer pointer, + (e.g. resp_cons = resp_prod), then the other domain has all the + buffers. + +2. Producer pointers point to the next buffer that will be written to. + (So blk_ring[MASK_BLKIF_IDX(req_prod)] should not be consumed.) + +3. Consumer pointers point to a valid message, so long as they are not + equal to the associated producer pointer. + +4. A domain should only ever write to the message pointed + to by its producer index, and read from the message at it's + consumer. More generally, the domain may be thought of to have + exclusive access to the messages between its consumer and producer, + and should absolutely not read or write outside this region. + + Thus the front end has exclusive access to the free(A) region + in the figure above, and the back end driver has exclusive + access to the free(B) region. + +In general, drivers keep a private copy of their producer pointer and +then set the shared version when they are ready for the other end to +process a set of messages. Additionally, it is worth paying attention +to the use of memory barriers (rmb/wmb) in the code, to ensure that +rings that are shared across processors behave as expected. + +==== Structure of the Blkif Drivers ==== + +Now that the communications primitives have been discussed, I'll +quickly cover the general structure of the blkif driver. This is +intended to give a high-level idea of what is going on, in an effort +to make reading the code a more approachable task. + +There are three key software components that are involved in the blkif +drivers (not counting Xen itself). The frontend and backend driver, +and Xend, which coordinates their initial connection. Xend may also +be involved in control-channel signalling in some cases after startup, +for instance to manage reconnection if the backend is restarted. + +===== Frontend Driver Structure ===== + +The frontend domain uses a single event channel and a shared memory +ring to trade control messages with the backend. These are both setup +during domain startup, which will be discussed shortly. The shared +memory ring is called blkif_ring, and the private ring indexes are +resp_cons, and req_prod. The ring is protected by blkif_io_lock. +Additionally, the frontend keeps a list of outstanding requests in +rec_ring[]. These are uniquely identified by a guest-local id number, +which is associated with each request sent to the backend, and +returned with the matching responses. Information about the actual +disks are stored in major_info[], of which only the first nr_vbds +entries are valid. Finally, the global 'recovery' indicates that the +connection between the backend and frontend drivers has been broken +(possibly due to a backend driver crash) and that the frontend is in +recovery mode, in which case it will attempt to reconnect and reissue +outstanding requests. + +The frontend driver is single-threaded and after setup is entered only +through three points: (1) read/write requests from the XenLinux guest +that it is a part of, (2) interrupts from the backend driver on its +event channel (blkif_int()), and (3) control messages from Xend +(blkif_ctrlif_rx). + +===== Backend Driver Structure ===== + +The backend driver is slightly more complex as it must manage any +number of concurrent frontend connections. For each domain it +manages, the backend driver maintains a blkif structure, which +describes all the connection and disk information associated with that +particular domain. This structure is associated with the interrupt +registration, and allows the backend driver to have immediate context +when it takes a notification from some domain. + +All of the blkif structures are stored in a hash table (blkif_hash), +which is indexed by a hash of the domain id, and a "handle", really a +per-domain blkif identifier, in case it wants to have multiple connections. + +The per-connection blkif structure is of type blkif_t. It contains +all of the communication details (event channel, irq, shared memory +ring and indexes), and blk_ring_lock, which is the backend mutex on +the shared ring. The structure also contains vbd_rb, which is a +red-black tree, containing an entry for each device/partition that is +assigned to that domain. This structure is filled by xend passing +disk information to the backend at startup, and is protected by +vbd_lock. Finally, the blkif struct contains a status field, which +describes the state of the connection. + +The backend driver spawns a kernel thread at startup +(blkio_schedule()), which handles requests to and from the actual disk +device drivers. This scheduler thread maintains a list of blkif +structures that have pending requests, and services them round-robin +with a maximum per-round request limit. blkifs are added to the list +in the interrupt handler (blkif_be_int()) using +add_to_blkdev_list_tail(), and removed in the scheduler loop after +calling do_block_io_op(), which processes a batch of requests. The +scheduler thread is explicitly activated at several points in the code +using maybe_trigger_blkio_schedule(). + +Pending requests between the backend driver and the physical device +drivers use another ring, pending_ring. Requests are placed in this +ring in the scheduler thread and issued to the device. A completion +callback, end_block_io_op, indicates that requests have been serviced +and generates a response on the appropriate blkif ring. pending +reqs[] stores a list of outstanding requests with the physical drivers. + +So, control entries to the backend are (1) the blkio scheduler thread, +which sends requests to the real device drivers, (2) end_block_io_op, +which is called as serviced requests complete, (3) blkif_be_int() +handles notifications from the frontend drivers in other domains, and +(4) blkif_ctrlif_rx() handles control messages from xend. + +==== Driver Startup ==== + +Prior to starting a new guest using the frontend driver, the backend +will have been started in a privileged domain. The backend +initialisation code initialises all of its data structures, such as +the blkif hash table, and starts the scheduler thread as a kernel +thread. It then sends a driver status up message to let xend know it +is ready to take frontend connections. + +When a new domain that uses the blkif frontend driver is started, +there are a series of interactions between it, xend, and the specified +backend driver. These interactions are as follows: + +The domain configuration given to xend will specify the backend domain +and disks that the new guest is to use. Prior to actually running the +domain, xend and the backend driver interact to setup the initial +blkif record in the backend. + +(1) Xend sends a BLKIF_BE_CREATE message to backend. + + Backend does blkif_create(), having been passed FE domid and handle. + It creates and initialises a new blkif struct, and puts it in the + hash table. + It then returns a STATUS_OK response to xend. + +(2) Xend sends a BLKIF_BE_VBD_CREATE message to the backend. + + Backend adds a vbd entry in the red-black tree for the + specified (dom, handle) blkif entry. + Sends a STATUS_OK response. + +(3) Xend sends a BLKIF_BE_VBD_GROW message to the backend. + + Backend takes the physical device information passed in the + message and assigns them to the newly created vbd struct. + +(2) and (3) repeat as any additional devices are added to the domain. + +At this point, the backend has enough state to allow the frontend +domain to start. The domain is run, and eventually gets to the +frontend driver initialisation code. After setting up the frontend +data structures, this code continues the communications with xend and +the backend to negotiate a connection: + +(4) Frontend sends Xend a BLKIF_FE_DRIVER_STATUS_CHANGED message. + + This message tells xend that the driver is up. The init function + now spin-waits until driver setup is complete in order to prevent + Linux from attempting to boot before the disks are connected. + +(5) Xend sends the frontend an INTERFACE_STATUS_CHANGED message + + This message specifies that the interface is now disconnected + (instead of closed). + The domain updates it's state, and allocates the shared blk_ring + page. Next, + +(6) Frontend sends Xend a BLKIF_INTERFACE_CONNECT message + + This message specifies the domain and handle, and includes the + address of the newly created page. + +(7) Xend sends the backend a BLKIF_BE_CONNECT message + + The backend fills in the blkif connection information, maps the + shared page, and binds an irq to the event channel. + +(8) Xend sends the frontend an INTERFACE_STATUS_CHANGED message + + This message takes the frontend driver to a CONNECTED state, at + which point it binds an irq to the event channel and calls + xlvbd_init to initialise the individual block devices. + +The frontend Linux is stall spin waiting at this point, until all of +the disks have been probed. Messaging now is directly between the +front and backend domain using the new shared ring and event channel. + +(9) The frontend sends a BLKIF_OP_PROBE directly to the backend. + + This message includes a reference to an additional page, that the + backend can use for it's reply. The backend responds with an array + of the domains disks (as vdisk_t structs) on the provided page. + +The frontend now initialises each disk, calling xlvbd_init_device() +for each one. diff --git a/docs/misc/crashdb.txt b/docs/misc/crashdb.txt new file mode 100644 index 0000000..b41a538 --- /dev/null +++ b/docs/misc/crashdb.txt @@ -0,0 +1,63 @@ +Xen crash debugger notes +------------------------ + +Xen has a simple gdb stub for doing post-mortem debugging i.e. once +you've crashed it, you get to poke around and find out why. There's +also a special key handler for making it crash, which is handy. + +You need to have crash_debug=y set when compiling , and you also need +to enable it on the Xen command line, eg by gdb=com1. + +If you need to have a serial port shared between gdb and the console, +you can use gdb=com1H. CDB will then set the high bit on every byte +it sends, and only respond to bytes with the high bit set. Similarly +for com2. If you do this you will need a demultiplexing program on +the debugging workstation, such as perhaps tools/misc/nsplitd. + +The next step depends on your individual setup. This is how to do it +if you have a simple null modem connection between the test box and +the workstation, and aren't using a H/L split console: + + * Set debug=y in Config.mk + * Set crash_debug=y in xen/Rules.mk + * Make the changes in the attached patch, and build. + * Arrange to pass gdb=com1 as a hypervisor command line argument + (I already have com1=38400,8n1 console=com1,vga sync_console) + + * Boot the system with minicom (or your favourite terminal program) + connected from your workstation via a null modem cable in the + usual way. + * In minicom, give the escape character (^A by default) three times + to talk to Xen (Xen prints `(XEN) *** Serial input -> Xen...'). + * Press % and observe the messages + (XEN) '%' pressed -> trapping into debugger + (XEN) GDB connection activated. + (XEN) Waiting for GDB to attach... + * Disconnect from minicom without allowing minicom to send any + modem control sequences. + * Start gdb with gdb /path/to/build/tree/xen/xen-syms and then + (gdb) set remotebaud 38400 + Remote debugging using /dev/ttyS0 + 0xff124d61 in idle_loop () at domain.c:78 + 78 safe_halt(); + (gdb) + +There is code which was once intended to make it possible to resume +after entering the debugger. However this does not presently work; it +has been nonfunctional for quite some time. + +As soon as you reach the debugger, we disable interrupts, the +watchdog, and every other CPU, so the state of the world shouldn't +change too much behind your back. + + +Reasons why we might fail to reach the debugger: +----------------------------------------------- + +-- In order to stop the other processors, we need to acquire the SMP + call lock. If you happen to have crashed in the middle of that, + you're screwed. +-- If the page tables are wrong, you're screwed +-- If the serial port setup is wrong, badness happens +-- Obviously, the low level processor state can be screwed in any + number of wonderful ways diff --git a/docs/misc/dump-core-format.txt b/docs/misc/dump-core-format.txt new file mode 100644 index 0000000..bc1842c --- /dev/null +++ b/docs/misc/dump-core-format.txt @@ -0,0 +1,243 @@ +xen dump-core format + Written by Isaku Yamahata <yamahata at valinux co jp> Feb. 2007 + + +Introduction +------------ +With xm dump-core command, the guest domain's core can be created as a file. +Its format was changed to be based on ELF format because elf format is easily +extensible and handy. This document describes the new format. +In this document the new format is called new xen dump-core format, +xen dump-core format or simply dump-core format. The file of xen dump-core +format is called xen dump-core file or dump-core file. + +The usual process core file includes program headers and no section header. +On the other hand the xen dump-core file includes no program headers and +some sections because of its peculiar requirements. + + +Reference +--------- +For ELF format itself, see Tool Interface Standard(TIS) Executable and +Linking Format(ELF) Specification version 1.2. +For xen related structure, please see the xen header files. + + +Elf header +---------- +The elf header members are set as follows + e_ident[EI_CLASS] = ELFCLASS64 = 2 + e_ident[EI_OSABI] = ELFOSABI_SYSV = 0 + e_type = ET_CORE = 4 +ELFCLASS64 is always used independent of architecture. +e_ident[EI_DATA] and e_flags are set according to the dumping system's +architecture. Other members are set as usual. + +Sections +-------- +Currently the following sections are defined. Some sections are optional. + +".note.Xen" section + name ".note.Xen" + type SHT_NOTE + description + This section is used as note section to store xen dump-core + file specific informations. The each detailed informations are + described in note section. This section must exist. + +".xen_prstatus" section + name ".xen_prstatus" + type SHT_PROGBITS + structure array of vcpu_guest_context_t + description + This section stores the array of vcpu_guest_context_t + which is obtained by XEN_DOMCTL_getvcpucontext hypercall + when the xen dump-core file is created. + The size of array is stored in xch_nr_vcpus member of header + note descriptor in .note.Xen note section. + This section must exist. + +".xen_shared_info" section + name ".xen_shared_info" + type SHT_PROGBITS + structure shared_info_t + description + This section stores the contents of shared info page + of a domain. This section is optional. + +".xen_p2m" section + name ".xen_p2m" + type SHT_PROGBITS + structure array of struct xen_dumpcore_p2m + struct xen_dumpcore_p2m { + uint64_t pfn; + uint64_t gmfn; + }; + description + This elements represents the frame number of the page + in .xen_pages section. + pfn: guest-specific pseudo-physical frame number + gmfn: machine physical frame number + The size of arrays is stored in xch_nr_pages member of header + note descriptor in .note.Xen note section. + The entries are stored in pfn-ascending order. + The value, {~(uint64_t)0, ~(uint64_t)0}, means invalid + (pfn, gmfn) and the corresponding page has zero. There might + exist invalid (pfn, gmfn)'s at the end part of this array. + This section must exist when the domain is non auto + translated physmap mode. Currently x86 paravirtualized domain. + +".xen_pfn" section + name ".xen_pfn" + type SHT_PROGBITS + structure array of uint64_t + description + This elements represents the frame number of the page + in .xen_pages section. + The size of arrays is stored in xch_nr_pages member of header + note descriptor in .note.Xen note section. + The entries are stored in ascending order. + The value, ~(uint64_t)0, means invalid pfn and the + corresponding page has zero. There might exist invalid + pfn's at the end part of this array. + This section must exist when the domain is auto translated + physmap mode. Currently x86 full virtualized domain and + ia64 domain. + +".xen_pages" section + name ".xen_pages" + type SHT_PROGBITS + structure array of page where page is page size byte array + description + This section includes the contents of pages. + The corresponding address is described in .xen_p2m section + or .xen_pfn section. + The page size is stored in xch_page_size member of header note + descriptor in .note.Xen section. + The array size is stored in xch_nr_pages member of header note + descriptor in .note.Xen section. + This section must exist. + + +".xen_ia64_mapped_regs" section + name ".xen_ia64_mapped_regs" + type SHT_PROGBITS + structure array of mapped_regs_t + description + This section stores the array of mapped_regs_t. + The size of array is stored in xch_nr_vcpus member of header + note descriptor in .note.Xen note section. + This section is ia64 specific and must exist for ia64 PV + domain. + This section must not exist for non-ia64 domain or ia64 HVM + domain. + + +note section +------------ +The note types are defined in xen/include/public/elfnote.h. +The note descriptors are defined in tools/libxc/xc_core.h +Currently the following note informations are defined. + + +elf note section + +"Xen" is used as elf note name in elf note info + namesz 4 + name "Xen" (null-terminated) + + +Descriptors + +none note descriptor + type XEN_ELFNOTE_DUMPCORE_NONE = 0x2000000 + structure struct xen_dumpcore_elfnote_none_desc { + /* nothing is defined */ + }; + description + This note descriptor is defined to just indicate that this + file is xen dump-core format without any specific information. + This note information must exist. + +header note descriptor + type XEN_ELFNOTE_DUMPCORE_HEADER = 0x2000001 + structure 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; + }; + description + This note descriptor stores basic information of the domain. + xch_magic magic number + XC_CORE_MAGIC = 0xF00FEBED for paravirtualized domain + XC_CORE_MAGIC_HVM = 0xF00FEBEE for full virtualized domain + xch_nr_vcpus the number of vcpus + xch_nr_pages the number of pages + xch_page_size guest OS's page size + This note information must exist. + +xen_version descriptor + type XEN_ELFNOTE_DUMPCORE_XEN_VERSION = 0x2000002 + structure 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; + }; + description + This note descriptor stores basic information about xen + hypervisor. The each members store the result of + __HYPERVISOR_xen_version hypercall. + major_version 16msb bit of the result of XENVER_version + minor_version 16lsb bit of the result of XENVER_version + uint64_t is used to make struct + xen_dumpcore_elfnote_xen_version_desc independent + on 32bit/64bit instead of uint32_t. + extra_version the result of XENVER_extraversion + compile_info the result of XENVER_compile_info + capabilities the result of XENVER_capabilities + changeset the result of XENVER_changeset + platform_parameters + the result of XENVER_platform_parameters + pagesize the result of XENVER_pagesize + This note information must exist. + +format_version descriptor + type XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION = 0x2000003 + structure struct xen_dumpcore_elfnote_format_version_desc { + uint64_t version; + }; + description + This note descriptor stores xen dump-core format version. + The 32msb bit is major version and the 32lsb bit is minor + version. + The minor version will be incremented when the format + is changed in compatible way. e.g. new sections, new note + descriptors are added. + the major version will be incremented when the format is + changed in incompatible way. + This note information must exit. Analysis tools should check + this format version. + This note information must exist. + + +Format version history +---------------------- +Currently only (major, minor) = (0, 1) is used. +[When the format is changed, it would be described here.] + +(0, 1) update +- .xen_p2m, .xen_pfn section + Invalid pfn/gmfn. +- .xen_p2m, .xen_pfn section + Arrays must be in pfn ascending order for efficient looking up. +- EI_CLASS member of elf header was changed to ELFCLASS64 independent of + architecture. This is mainly for x86_32pae. + The format version isn't bumped because analysis tools can distinguish it. +- .xen_ia64_mapped_regs section was made only for ia64 PV domain. + In case of IA64 HVM domain, this section doesn't exist. diff --git a/docs/misc/grant-tables.txt b/docs/misc/grant-tables.txt new file mode 100644 index 0000000..19db4ec --- /dev/null +++ b/docs/misc/grant-tables.txt @@ -0,0 +1,325 @@ +******************************************************************************** + A Rough Introduction to Using Grant Tables + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Christopher Clark, March, 2005. + +Grant tables are a mechanism for sharing and transferring frames between +domains, without requiring the participating domains to be privileged. + +The first mode of use allows domA to grant domB access to a specific frame, +whilst retaining ownership. The block front driver uses this to grant memory +access to the block back driver, so that it may read or write as requested. + + 1. domA creates a grant access reference, and transmits the ref id to domB. + 2. domB uses the reference to map the granted frame. + 3. domB performs the memory access. + 4. domB unmaps the granted frame. + 5. domA removes its grant. + + +The second mode allows domA to accept a transfer of ownership of a frame from +domB. The net front and back driver will use this for packet tx/rx. This +mechanism is still being implemented, though the xen<->guest interface design +is complete. + + 1. domA creates an accept transfer grant reference, and transmits it to domB. + 2. domB uses the ref to hand over a frame it owns. + 3. domA accepts the transfer + 4. domA clears the used reference. + + +******************************************************************************** + Data structures + ~~~~~~~~~~~~~~~ + + The following data structures are used by Xen and the guests to implement + grant tables: + + 1. Shared grant entries + 2. Active grant entries + 3. Map tracking + + These are not the users primary interface to grant tables, but are discussed + because an understanding of how they work may be useful. Each of these is a + finite resource. + + Shared grant entries + ~~~~~~~~~~~~~~~~~~~~ + + A set of pages are shared between Xen and a guest, holding the shared grant + entries. The guest writes into these entries to create grant references. The + index of the entry is transmitted to the remote domain: this is the + reference used to activate an entry. Xen will write into a shared entry to + indicate to a guest that its grant is in use. + sha->domid : remote domain being granted rights + sha->frame : machine frame being granted + sha->flags : allow access, allow transfer, remote is reading/writing, etc. + + Active grant entries + ~~~~~~~~~~~~~~~~~~~~ + + Xen maintains a set of private frames per domain, holding the active grant + entries for safety, and to reference count mappings. + act->domid : remote domain being granted rights + act->frame : machine frame being granted + act->pin : used to hold reference counts + + Map tracking + ~~~~~~~~~~~~ + + Every time a frame is mapped, a map track entry is stored in the metadata of + the mapping domain. The index of this entry is returned from the map call, + and is used to unmap the frame. Map track entries are also searched whenever a + page table entry containing a foreign frame number is overwritten: the first + matching map track entry is then removed, as if unmap had been invoked. + These are not used by the transfer mechanism. + map->domid : owner of the mapped frame + map->ref_and_flags : grant reference, ro/rw, mapped for host or device access + +******************************************************************************** + + Granting a foreign domain access to frames + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + domA [frame]--> domB + + + domA: #include <asm-xen/gnttab.h> + grant_ref_t gref[BATCH_SIZE]; + + for ( i = 0; i < BATCH_SIZE; i++ ) + gref[i] = gnttab_grant_foreign_access( domBid, mfn, (readonly ? 1 : 0) ); + + + .. gref is then somehow transmitted to domB for use. + + + Mapping foreign frames + ~~~~~~~~~~~~~~~~~~~~~~ + + domB: #include <asm-xen/hypervisor.h> + unsigned long mmap_vstart; + gnttab_op_t aop[BATCH_SIZE]; + grant_ref_t mapped_handle[BATCH_SIZE]; + + if ( (mmap_vstart = allocate_empty_lowmem_region(BATCH_SIZE)) == 0 ) + BUG(); + + for ( i = 0; i < BATCH_SIZE; i++ ) + { + aop[i].u.map_grant_ref.host_virt_addr = + mmap_vstart + (i * PAGE_SIZE); + aop[i].u.map_grant_ref.dom = domAid; + aop[i].u.map_grant_ref.ref = gref[i]; + aop[i].u.map_grant_ref.flags = ( GNTMAP_host_map | GNTMAP_readonly ); + } + + if ( unlikely(HYPERVISOR_grant_table_op( + GNTTABOP_map_grant_ref, aop, BATCH_SIZE))) + BUG(); + + for ( i = 0; i < BATCH_SIZE; i++ ) + { + if ( unlikely(aop[i].u.map_grant_ref.handle < 0) ) + { + tidyup_all(aop, i); + goto panic; + } + + phys_to_machine_mapping[__pa(mmap_vstart + (i * PAGE_SIZE))>>PAGE_SHIFT] = + FOREIGN_FRAME(aop[i].u.map_grant_ref.dev_bus_addr); + + mapped_handle[i] = aop[i].u.map_grant_ref.handle; + } + + + + Unmapping foreign frames + ~~~~~~~~~~~~~~~~~~~~~~~~ + + domB: + for ( i = 0; i < BATCH_SIZE; i++ ) + { + aop[i].u.unmap_grant_ref.host_virt_addr = mmap_vstart + (i * PAGE_SIZE); + aop[i].u.unmap_grant_ref.dev_bus_addr = 0; + aop[i].u.unmap_grant_ref.handle = mapped_handle[i]; + } + if ( unlikely(HYPERVISOR_grant_table_op( + GNTTABOP_unmap_grant_ref, aop, BATCH_SIZE))) + BUG(); + + + Ending foreign access + ~~~~~~~~~~~~~~~~~~~~~ + + Note that this only prevents further mappings; it does _not_ revoke access. + Should _only_ be used when the remote domain has unmapped the frame. + gnttab_query_foreign_access( gref ) will indicate the state of any mapping. + + domA: + if ( gnttab_query_foreign_access( gref[i] ) == 0 ) + gnttab_end_foreign_access( gref[i], readonly ); + + TODO: readonly yet to be implemented. + + +******************************************************************************** + + Transferring ownership of a frame to another domain + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + [ XXX: Transfer mechanism is alpha-calibre code, untested, use at own risk XXX ] + [ XXX: show use of batch operations below, rather than single frame XXX ] + [ XXX: linux internal interface could/should be wrapped to be tidier XXX ] + + + Prepare to accept a frame from a foreign domain + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + domA: + if ( (p = alloc_page(GFP_HIGHUSER)) == NULL ) + { + printk("Cannot alloc a frame to surrender\n"); + break; + } + pfn = p - mem_map; + mfn = phys_to_machine_mapping[pfn]; + + if ( !PageHighMem(p) ) + { + v = phys_to_virt(pfn << PAGE_SHIFT); + scrub_pages(v, 1); + queue_l1_entry_update(get_ptep((unsigned long)v), 0); + } + + /* Ensure that ballooned highmem pages don't have cached mappings. */ + kmap_flush_unused(); + + /* Flush updates through and flush the TLB. */ + xen_tlb_flush(); + + phys_to_machine_mapping[pfn] = INVALID_P2M_ENTRY; + + if ( HYPERVISOR_dom_mem_op( + MEMOP_decrease_reservation, &mfn, 1, 0) != 1 ) + { + printk("MEMOP_decrease_reservation failed\n"); + /* er... ok. free the page then */ + __free_page(p); + break; + } + + accepting_pfn = pfn; + ref = gnttab_grant_foreign_transfer( (domid_t) args.arg[0], pfn ); + printk("Accepting dom %lu frame at ref (%d)\n", args.arg[0], ref); + + + Transfer a frame to a foreign domain + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + domB: + mmu_update_t update; + domid_t domid; + grant_ref_t gref; + unsigned long pfn, mfn, *v; + struct page *transfer_page = 0; + + /* alloc a page and grant access. + * alloc page returns a page struct. */ + if ( (transfer_page = alloc_page(GFP_HIGHUSER)) == NULL ) + return -ENOMEM; + + pfn = transfer_page - mem_map; + mfn = phys_to_machine_mapping[pfn]; + + /* need to remove all references to this page */ + if ( !PageHighMem(transfer_page) ) + { + v = phys_to_virt(pfn << PAGE_SHIFT); + scrub_pages(v, 1); + sprintf((char *)v, "This page (%lx) was transferred.\n", mfn); + queue_l1_entry_update(get_ptep((unsigned long)v), 0); + } +#ifdef CONFIG_XEN_SCRUB_PAGES + else + { + v = kmap(transfer_page); + scrub_pages(v, 1); + sprintf((char *)v, "This page (%lx) was transferred.\n", mfn); + kunmap(transfer_page); + } +#endif + /* Delete any cached kmappings */ + kmap_flush_unused(); + + /* Flush updates through and flush the TLB */ + xen_tlb_flush(); + + /* invalidate in P2M */ + phys_to_machine_mapping[pfn] = INVALID_P2M_ENTRY; + + domid = (domid_t)args.arg[0]; + gref = (grant_ref_t)args.arg[1]; + + update.ptr = MMU_EXTENDED_COMMAND; + update.ptr |= ((gref & 0x00FF) << 2); + update.ptr |= mfn << PAGE_SHIFT; + + update.val = MMUEXT_TRANSFER_PAGE; + update.val |= (domid << 16); + update.val |= (gref & 0xFF00); + + ret = HYPERVISOR_mmu_update(&update, 1, NULL); + + + Map a transferred frame + ~~~~~~~~~~~~~~~~~~~~~~~ + + TODO: + + + Clear the used transfer reference + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + TODO: + + +******************************************************************************** + + Using a private reserve of grant references + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Where it is known in advance how many grant references are required, and +failure to allocate them on demand would cause difficulty, a batch can be +allocated and held in a private reserve. + +To reserve a private batch: + + /* housekeeping data - treat as opaque: */ + grant_ref_t gref_head, gref_terminal; + + if ( 0 > gnttab_alloc_grant_references( number_to_reserve, + &gref_head, &gref_terminal )) + return -ENOSPC; + + +To release a batch back to the shared pool: + + gnttab_free_grant_references( number_reserved, gref_head ); + + +To claim a reserved reference: + + ref = gnttab_claim_grant_reference( &gref_head, gref_terminal ); + + +To release a claimed reference back to the reserve pool: + + gnttab_release_grant_reference( &gref_head, gref ); + + +To use a claimed reference to grant access, use these alternative functions +that take an additional parameter of the grant reference to use: + + gnttab_grant_foreign_access_ref + gnttab_grant_foreign_transfer_ref diff --git a/docs/misc/hg-cheatsheet.txt b/docs/misc/hg-cheatsheet.txt new file mode 100644 index 0000000..fa13eef --- /dev/null +++ b/docs/misc/hg-cheatsheet.txt @@ -0,0 +1,438 @@ + +Mercurial(hg) Cheatsheet for Xen +================================ + +Written by Andrew Warfield, extended by Michael Fetterman and Ian Pratt +June 29, 2005, extended by Grzegorz Milos 04 July 2005. + +Overview +-------- +The Xen project has moved from BitKeeper to Mercurial for source +control. This note aims to provide a quick guide to getting up and +running with the new tools as quickly as possible, and is written from +the perspective of someone who has been using BK. + +For a more detailed exposition, see the mercurial tutorial: + http://www.serpentine.com/mercurial/index.cgi?Tutorial + +The Hg manpage is available at: + http://www.selenic.com/mercurial/hg.1.html + +There's also a very useful FAQ that explains the terminology: + http://www.selenic.com/mercurial/FAQ.html + +There's also a good README: + http://www.selenic.com/mercurial/README + +Necessary software +------------------ +Mercurial is available at: + http://www.selenic.com/mercurial/ + +You will also need a Python version >= 2.3 + +How Mercurial is different from BK +---------------------------------- +There are several pertinent differences between hg and bk. This +section aims to give an overview of the conceptual differences between +the two SCMs -- if you just want examples to get going, skip ahead to +"Getting Xen". The key differences are: + + - No explicit per-file locking. You do not need to explicitly + check a file out before editing it. + - No notion (currently) of file renames. + - A repository can have multiple active heads. + - Automatic merge support is currently inferior to BK's. + - No graphical tools. + - No per-file revision history, only per-changeset (we never really used this anyhow) + - Hg repositories tend to be rather bigger than Bk ones, but Hg does seem faster. + +Mercurial is based on the notion of changesets as complete, immutable, +versions of the repository. You make changes to a working version of +the repository that is based on a particular changeset. When you +commit, you will generate a new child changeset with whatever changes +you choose to apply. + +A major difference between Hg and BK is that you aren't forced to +resolve conflicts immediately: BK forced you to resolve conflicts +immediately on any merge, and it then immediately created a changeset +with those conflicts' resolutions. Frequently, you then had to add +yet another changeset to fixup the things for which the automatic +merge yielded bad results. Hg puts the results of the merge into your +work directory, and remembers what you merged with (so that it can +later record both of the merge parents, if you decide to make a +changeset), but it doesn't immediately create a changeset. + +A further feature of Hg is that it allows a repository to have +multiple heads. This means that you can have changesets with no common +descendent in one repository -- something BK won't allow. This is +actually pretty neat. For example, it would in principle enable you to +have both the 2.0-testing and unstable trees in a single +repository. We shyed away from doing this as we thought the risk of +committing to the wrong head was too great. + +One slightly confusing aspect of Hg is that many of the commands have +aliases, and hence when looking things up in the man page its not +always obvious what the underlying command is. For example 'co' is +actually an alias for the 'update' command, but 'co' seems to make +more sense, at least to RCS refugees like me. + + +Getting Xen +----------- + +The URL for the mainline Xen mercurial repository is: + + http://xenbits.xensource.com/xen-unstable.hg + (similarly for xen-2.0 and xen-2.0-testing) + +You can point a browser and this and use Hg's web interface to view +revision history, or use it as the nominated source when issuing +"hg init" or "hg pull" commands. + +However, to avoid taxing the Mercurial server with a complete pull of +the Xen repository, it is best to download a tarball of a seed +repository from: + + http://www.cl.cam.ac.uk/Research/SRG/netos/xen/downloads/xen-unstable.hg.tar.gz + + (or copy from /usr/groups/netos/html/xen/downloads/xen-unstable.hg.tar.gz) + +Untar the repository on your disk, cd into it, and then pull the most +recent changes: + + hg pull -u + +By default hg does not automatically checkout ('update') files from +the repository as used to happen with bk. The above is equivalent to +"hg pull; hg co" + +The repository parent is stored in a repository configuration file, +.hg/hgrc, from the repository root. If you look at this file, you +will see: + + | [paths] + | default = http://xenbits.xensource.com/xen-unstable.hg + +"default" specifies the appropriate parent repository for hg to pull +from. Hg allows you to pull additional repositories, for instance if +you want to work between unstable and testing concurrently. + +The command "hg pull" simply adds changesets to your repository, +without any merging of any kind. "hg pull -u" implies merging with +the current state of your working directory. If you weren't already +"updated" to your local repository's tip, you might be surprised to +find yourself merging the results of the pull with a non-tip node in +your local repository. + + +Revision History +---------------- + +You can view the repository revision history with: + + hg history + +In practice, you'll probably want to use pipe the output through +'head' or 'more' as it prints the entire history. + +Looking at the first few lines of output, you can see the changeset at +the head of the current branch, known as the 'tip' (the tip is +automatically given a special tag to make it easy to refer to): + + | changeset: 5599:6cbf9ec05cd9e05c0c46a85df7fc00262633cd3d + | tag: tip + | user: kaf24@firebug.cl.cam.ac.uk + | date: Tue Jun 28 18:47:14 2005 + | summary: bitkeeper revision 1.1768 (42c18d2259NPELcGV7ohyZNh72ufSw) + +By default, Hg just shows the first line of the changset comments. You +can find further information with "hg -v history". + +The changeset identifier has two parts, a _local_ monotonically +increasing changeset id, 5599 above, and a _global_ hash, which +follows the colon on the changeset line. The hash uniquely identifies +the changeset and its lineage back to the root of the changeset tree +-- it is useful for distributed management and so on. However, as it +is a bit unruly, the local id will allow you to work easily with the +local repo. Hg commands will take either identifier. Additionally, a +tags mechanism lets you give common names to specific changesets. + +You should always use the global hash when referring to versions of +the mainline Xen respoitory. With Bk you could often get away with +using the shortform version, but with Hg the local ids are pretty much +guaranteed to be different. + + +Creating a child repository from an existing repository +------------------------------------------------------- +If you wanted to create additional local child repositories, + + hg init [path or url] + +is effectively equivalent to bk clone. The major difference is that +it should be run from the root of your new repository. So: + + bk clone /foo/bar + +would be replaced with: + + mkdir bar + cd bar + hg init /foo/bar + +NB: newer version of Hg support a 'clone' command that works in the +same manner as bk. + +Editing files +------------- + +Normal edits may be made in place. File creation needs explicit +marking, though deletes should be picked up automatically + +creation: + + touch a.txt (or otherwise created a file) + hg add a.txt + +You can see what has changed using: + + hg status + + | C foo/foo.c + | R foo/bar.c + | ? a.txt + +This shows that in the current repo, foo.c has been changed, bar.c has +been deleted, and a.txt is new, but has not been added. '?' changes +to 'A' after "hg add a.txt". There is a .hgignore file which contains +regexps of files that should be ignored when scanning for new +files. We try to ensure that all the generated files in a build are +covered by the regexps. + +You can add all the new files in a repository with "hg addremove". If +you discover that you've added a file you didn't want, you can remove +it from the list of files to be included in the next commit using +"hg forget". + +Committing changes +----------------- + +After you've checked that hg knows about any new files you've created, +you probably want to see a diff of what you're about to commit. You +can do this with: + + hg diff + +Once you're happy with what you have, invoke: + + hg commit + +This will pop up an editor with a list of files to be committed to the +repository. It will look vaguely like this: + + | + | HG: manifest hash 6397b04bd5c2a992482d973b685a7e5e498788e7 + | HG: changed doc/thesis/new.tex + | HG: removed doc/2005-hotdep-protection/paper.tex + +Your comments can go anywhere in this file. The first line is the +most important, as it will show as the changeset description in +non-verbose-mode history listings. + +You can do commits without the editor and of partial sets of files +using command-line switches. See: + + hg help commit + +You can use the -A (--addremove) flag to commit e.g. "hg -A commit" to +ask mercurial to scan the tree looking for newly created files to add +in to the changeset. This avoids having to explicitly use "hg add", +but you probably want to be careful of adding any new generated files +too. + + +Generating a patch +------------------ +Generating a patch is easy, + + hg export [changeset] + +will generate a patch describing the diff between that changeset and +its parent. + +To generate a patch between two specified revisions use: + hg diff -r A -r B [files] +NB: BK syntax -rA..B isn't supported by Hg. + + +Pushing changesets to a parent repository +----------------------------------------- + + hg push + +Pushes changes up to a parent. You can't push if you pulled the +repository off the web interface. In fact, you can currently only push +to an ssh target -- filesystem directory targets don't work, but this +will be fixed soon. +For now it is possible to set up asymmetric pull/push paths. Pulls can +be done via web interface while pushes via ssh. Example of .hg/hgrc config +file: + | [paths] + | default = http://your.server/repository_name + | default-push = ssh://[username@]your.server//repository_location + + +Repository history +------------------ + +Here are a collection of common commands to get you started: + + hg history | less + +shows the history of changesets, starting from the most recent. You +want to pipe it to some sort of pager. For more complete details, + + hg -v history | less + +will include files modified and full (not just first-line) comments. + +Additionally, you can see just the tip (head of the current +branch) of the repository by typing: + + hg [-v] tip + + +Moving to a specific changeset +------------------------------ + +The co command lets you change the working version of the repository +to a different changeset. + + hg co [changeset] + +NB: 'co' is an alias for 'update' + +This command enables you to rewind the working repository to previous +changesets, for example to isolate the changeset in which a bug is +introduced. + +If you try and do a 'co' but have modified files in your repository Hg +won't let you unless you ask it explicitly to merge the checked out +version into the current tree using the "-m" option. The "-C" +(--clean) option will force overwrite any locally modified files. + +Any commits that are made to non-head changesets will obviously fork +the tree, creating a new head. You can see all the heads in a tree with +"hg heads". + +In general, "hg co" does the right thing, although it doesn't +currently seem to clean up unused directories that have been created +by other checked-out versions. This can confuse the Xen build +system. Hg will probably get fixed soon, but in the meantime you can +cleanup with "find -depth -type d -print | xargs -r rmdir". + +You can return to the tip by omitting an explicit changeset id. + +The manifest command lets you see the contents of the repository for +the current changeset. + + hg manifest + +This will print a bunch of records of the form: + + | 98856c45c35a504bc6da06a62b7787ddfdfd1c8b 644 COPYING + | f28971eedc5b54e7a9b26dd18d52992955354981 644 Config.mk + | a3575cc4db59e50bbac8a039a0c74f081a8dfc4f 644 Makefile + | 7fc869aae2945a9f4626fad96552db3103e61cb9 644 README + | ... + +This lists the hash of each file, its 1-bit 'executable' attribute +(either file permission mode 644 or 755), and the file name. So, to +determine the files that change across two changesets, you would dump +the respective manifests to files, and use diff. + + +Managing changeset tags +----------------------- +To create a tag to the current changeset: + + hg tag tagname + +This will _immediately_ generate a changeset with a change to the file +.hgtags in the repository root. The new tag in this file will look +something like: + + | 35159ed4b30538e7a52c60ad0a63f7e9af156e4c tagname + +and may be used to identify that changeset throughout the repo. +Storing tags in this file and generating changesets immediately +forces people to merge and keep tags up to date across the repository. + +Note that tags are resolved by searching .hgtags in each of the +repository heads, sequentially, and using the first match. "hg heads" +lists the current heads. + +The "hg tags" command, will lists all the currently valid tags. + + +Hg server and source browser +---------------------------- + + hg serve -p port + +Launches a web server on the specified port, serving a source browser +for the repository. This browser may be used to examine the +changeset history, view annotated source files, generate diffs. +Additionally "hg pull" may be run against it. + +Additional useful commands +(that probably only need one-line descriptions) +----------------------------------------------- + +(Slightly) more detail on all of these is available with + + hg help [command] + +Shows the differences between whatever changeset you most recently +checked out, and your current working directory: + + hg diff + +View an annotated version of a source file: + + hg annotate + +Get a historical version of a file: + + hg cat + + NB: Most commands accepting a version number want the changeset's + version number. "hg cat" is different in that it wants the + *file*'s version number. + +Unadd a file to the current commit: + + hg forget + +List all heads in the current repository: + + hg heads + +Undo exactly one (and ONLY one) changeset: + + hg undo + +Show the parents of a changeset: + + hg parents + + NB: Changesets have either one or two parent changesets. If your + working directory contains the uncommitted results of a merge, then + you have two parents. Otherwise, the single parent is the changeset + which you most recently checked out. + +Show the revision history for a single file + + hg [-v] log <filename> + diff --git a/docs/misc/kexec_and_kdump.txt b/docs/misc/kexec_and_kdump.txt new file mode 100644 index 0000000..0659219 --- /dev/null +++ b/docs/misc/kexec_and_kdump.txt @@ -0,0 +1,213 @@ + +======================= +Kexec and Kdump for Xen +======================= + +This is a breif guide to using Kexec and Kdump in conjunction with Xen. +This functionaly works at the level of the hypervisor and dom0 kernel. +And will thus affect all guests running on a machine. + +At this stage it does not work in conjunction with domU kernels. + +This document should be read in conjunction with +Documentation/kdump/kdump.txt from the Linux kernel source. +Some of the information in this document has been +sourced from that document. + + +Kexec +===== + +It is possible to kexec from Xen or Linux to either Xen or Linux. + +Pattern | Before Kexec | After Kexec +---------------+--------------------+-------------------- +Xen -> Xen | first hypervisor & | second hypervisor & + | dom0 kernel | dom0 kernel +---------------+--------------------+-------------------- +Xen -> Linux | first hypervisor & | second kernel + | dom0 kernel | +---------------+--------------------+-------------------- +Linux -> Xen | first kernel | second hypervisor & + | | dom0 kernel +---------------+--------------------+-------------------- +Linux -> Linux | first kernel | second kernel + +If you are kexecing to Xen then you will also need to preapare the second +hypervisor and dom0 kernel that will run after kexec. These may be the same +as the first hypervisor and dom0 kernel that are used before kexec if you +are kexecing from Xen to Xen. + +If you are kexecing to Linux then you will need to prepare the second Linux +kernel that will run after kexec. In the case that you are kexecing from +Linux, it may be the same as the first kernel image that that runs before +kexec. + +Regardless of which kexec pattern you wish to run, you will +need to have kexec-tools installed. This provides the kexec command. + +1. Load +------- + +Before kexecing the second kernel or hypervisor & dom0 kernel +need to be loaded into the running hypervisor or kernel using +the kexec command. + + a. To kexec to Xen (Xen->Xen or Linux->Xen) + + kexec -l --append="XEN_ARGS -- DOM0_ARGS" \ + --vmm="XEN_IMAGE" "DOM0_IMAGE" KEXEC_ARGS + + where: + XEN_ARGS: command line arguments to the xen hypervisor + On x86 the no-real-mode argument should be included + DOM0_ARGS: command line arguments to the dom0 kernel + XEN_IMAGE: xen hypervisor image + DOM0_IMAGE: dom0 kernel image + KEXEC_ARGS: additional kexec-tools command line arguments + + e.g. kexec -l --append "no-real-mode" --vmm="/boot/xen.gz" /boot/vmlinuz.gz + + OR + + b. To kexec to Linux (Xen->Linux or Linux->Linux) + + kexec -l LINUX_IMAGE --append "$LINUX_ARGS" KEXEC_ARGS + + where: + LINUX_IMAGE: the second linux kernel image + LINUX_ARGS: command line arguments to the second linux kernel + KEXEC_ARGS: additional kexec-tools command line arguments + + e.g. kexec -l /boot/second-vmlinuz.gz + +2. Execute +---------- + +Once the second kernel is loaded, it can be executed at any time. +If you don't see the second kernel booting within a second or so, +you are in trouble :( + + kexec -e + +Kdump +===== + +It is possible to kdump from Xen or Linux to a Linux crash kernel. +It is not possible to use xen as a crash kernel. + +Pattern | Before Kexec | After Kexec +---------------+--------------------+-------------------- +Xen -> Linux | first hypervisor & | crash kernel + | dom0 kernel | +---------------+--------------------+-------------------- +Linux -> Linux | first kernel | crash kernel + +Regardless of if you are kdumping from Xen or Linux you will need to +prepare a linux crash kernel. You will also need to have kexec-tools +installed. This provides the kexec command. + +0. Set-Up The Crash Kernel Region +--------------------------------- + +In order to use kdump an area of memory has to be reserved at boot time. +This is the area of memory that the crash kernel will use, thus allowing it +to run without disrupting the memory used by the first kernel. This area is +called the crash kernel region and is reserved using the crashkernel +command line parameter to the Xen hypervisor. It has two forms: + + i) crashkernel=size + + This is the simplest and recommended way to reserve the crash kernel + region. Just specify how large the region should be and the hypervisor + will find a good location for it. A good size to start with is 128Mb + + e.g. + + crashkernel=128M + + ii) crashkernel=size@base + + In this form the base address is provided in addition to + the size. Use this if auto-placement doesn't work for some reason. + It is strongly recommended that the base address be aligned + to 64Mb, else memory below the alignment point will not + be usable. + + e.g. crashkernel=128M@256M + + Regardless of which of the two forms of the crashkernel command line you + use, the crash kernel region should appear in /proc/iomem on x86 or + /proc/iomem_machine on ia64. If it doesn't then either the crashkernel + parameter is missing, or for some reason the region couldn't be placed - + for instance because it is too large. + + # cat /proc/iomem + ... + 00100000-07feffff : System RAM + 00100000-00bfffff : Hypervisor code and data + 0533f000-0733efff : Crash kernel + ... + + +1. Load +------- + +Once you are running in a kexec-enabled hypervisor and dom0, +you can prepare to kdump by loading the crash kernel into the +running kernel. + + kexec -p CRASH_KERNEL_IMAGE --append "$CRASH_KERNEL_ARGS" KEXEC_ARGS + + where: + CRASH_KERNEL_IMAGE: the crash kernel image + CRASH_KERNEL_ARGS: command line arguments to the crash kernel + init 1 is strongly recommended + irqpoll is strongly recommended + maxcpus=1 is required if the crash kernel is SMP + reset_devices is strongly recommended + KEXEC_ARGS: additional kexec-tools command line arguments + On x86 --args-linux should be supplied if an uncompressed + vmlinux image is used as the crash kernel + + e.g. kexec -p /boot/crash-vmlinuz \ + --append "init 1 irqpoll maxcpus=1 reset_devices" --args-linux + +On x86 systems the crash kernel may be either +- A uncompressed vmlinux image if the kernel is not relocatable +- A compressed bzImage or vmlinuz image if the kernel is relocatable +- Relocatability is crontroled by the CONFIG_RELOCATABLE kernel + compile configuration parameter. This option may not be available + depending on the kernel version +On ia64 + Either a vmlinuz or vmlinux.gz image may be used + + +2. Execute +---------- + +Once the second kernel is loaded, the crash kernel will be executed if the +hypervisor panics. It will also be executed if dom0 panics or if dom0 +oopses and /proc/sys/kernel/panic_on_oops is set to a non-zero value + +echo 1 > /proc/sys/kernel/panic_on_oops + +Kdump may also be triggered (for testing) + + a. From Domain 0 + + echo c > /proc/sysrq-trigger + + b. From Xen + + Enter the xen console + + ctrl^a ctrl^a (may be bound to a different key, this is the default) + + Select C for "trigger a crashdump" + + C + +If you don't see the crash kernel booting within a second or so, +you are in trouble :( + diff --git a/docs/misc/sedf_scheduler_mini-HOWTO.txt b/docs/misc/sedf_scheduler_mini-HOWTO.txt new file mode 100644 index 0000000..52e5b6e --- /dev/null +++ b/docs/misc/sedf_scheduler_mini-HOWTO.txt @@ -0,0 +1,44 @@ +sEDF scheduler +-------------- +Author: + Stephan.Diestelhorst@{cl.cam.ac.uk, inf.tu-dresden.de} + +Overview: + This scheduler provides weighted CPU sharing in an intuitive way and + uses realtime-algorithms to ensure time guarantees. + +Usage: +   -add "sched=sedf" on Xen's boot command-line +   -create domains as usual +   -use "xm sched-sedf <dom-id> <period> <slice> <latency-hint> <extra> <weight>" +  Where: +  -period/slice are the normal EDF scheduling parameters in nanosecs +  -latency-hint is the scaled period in case the domain is doing heavy I/O + (unused by the currently compiled version) +  -extra is a flag (0/1), which controls whether the domain can run in + extra-time +  -weight is mutually exclusive with period/slice and specifies another + way of setting a domains cpu slice + +Examples: + normal EDF (20ms/5ms): +  xm sched-sedf <dom-id> 20000000 5000000 0 0 0 + + best-effort domains (i.e. non-realtime): +  xm sched-sedf <dom-id> 20000000 0 0 1 0 +  + normal EDF (20ms/5ms) + share of extra-time: +  xm sched-sedf <dom-id> 20000000 5000000 0 1 0 + + 4 domains with weights 2:3:4:2 +  xm sched-sedf <d1> 0 0 0 0 2 +  xm sched-sedf <d2> 0 0 0 0 3 +  xm sched-sedf <d3> 0 0 0 0 4 +  xm sched-sedf <d4> 0 0 0 0 2 + + 1 fully-specified (10ms/3ms) domain, 3 other domains share + available rest in 2:7:3 ratio: +  xm sched-sedf <d1> 10000000 3000000 0 0 0 +  xm sched-sedf <d2> 0 0 0 0 2 +  xm sched-sedf <d3> 0 0 0 0 7 +  xm sched-sedf <d4> 0 0 0 0 3 diff --git a/docs/misc/vtd.txt b/docs/misc/vtd.txt new file mode 100644 index 0000000..bd6fd10 --- /dev/null +++ b/docs/misc/vtd.txt @@ -0,0 +1,133 @@ +Title : How to do PCI Passthrough with VT-d +Authors : Allen Kay <allen.m.kay@intel.com> + Weidong Han <weidong.han@intel.com> + Yuji Shimada <shimada-yxb@necst.nec.co.jp> +Created : October-24-2007 +Updated : September-09-2008 + +How to turn on VT-d in Xen +-------------------------- + +1 ) cd xen-unstable.hg +2 ) make install +3 ) make linux-2.6-xen-config CONFIGMODE=menuconfig +4 ) change XEN->"PCI-device backend driver" from "M" to "*". +5 ) make linux-2.6-xen-build +6 ) make linux-2.6-xen-install +7 ) depmod 2.6.18.8-xen +8 ) mkinitrd -v -f --with=ahci --with=aacraid --with=sd_mod --with=scsi_mod initrd-2.6.18-xen.img 2.6.18.8-xen +9 ) cp initrd-2.6.18-xen.img /boot +10) lspci - select the PCI BDF you want to assign to guest OS +11) "hide" pci device from dom0 as following sample grub entry: + +title Xen-Fedora Core (2.6.18-xen) + root (hd0,0) + kernel /boot/xen.gz com1=115200,8n1 console=com1 iommu=1 + module /boot/vmlinuz-2.6.18.8-xen root=LABEL=/ ro xencons=ttyS console=tty0 console=ttyS0, pciback.hide=(01:00.0)(03:00.0) + module /boot/initrd-2.6.18-xen.img + +12) reboot system +13) add "pci" line in /etc/xen/hvm.conf for to assigned devices + pci = [ '01:00.0', '03:00.0' ] +15) start hvm guest and use "lspci" to see the passthru device and + "ifconfig" to see if IP address has been assigned to NIC devices. + + +Enable MSI/MSI-x for assigned devices +------------------------------------- +Add "msi=1" option in kernel line of host grub. + + +Caveat on Conventional PCI Device Passthrough +--------------------------------------------- + +VT-d spec specifies that all conventional PCI devices behind a +PCIe-to-PCI bridge have to be assigned to the same domain. + +PCIe devices do not have this restriction. + + +VT-d Works on OS: +----------------- + +1) Host OS: PAE, 64-bit +2) Guest OS: 32-bit, PAE, 64-bit + + +Combinations Tested: +-------------------- + +1) 64-bit host: 32/PAE/64 Linux/XP/Win2003/Vista guests +2) PAE host: 32/PAE Linux/XP/Win2003/Vista guests + + +VTd device hotplug: +------------------- + +2 virtual PCI slots (6~7) are reserved in HVM guest to support VTd hotplug. If you have more VTd devices, only 2 of them can support hotplug. Usage is simple: + + 1. List the VTd device by dom. You can see a VTd device 0:2:0.0 is inserted in the HVM domain's PCI slot 6. '''lspci''' inside the guest should see the same. + + [root@vt-vtd ~]# xm pci-list HVMDomainVtd + VSlt domain bus slot func + 0x6 0x0 0x02 0x00 0x0 + + 2. Detach the device from the guest by the physical BDF. Then HVM guest will receive a virtual PCI hot removal event to detach the physical device + + [root@vt-vtd ~]# xm pci-detach HVMDomainVtd 0:2:0.0 + + 3. Attach a PCI device to the guest by the physical BDF and desired virtual slot(optional). Following command would insert the physical device into guest's virtual slot 7 + + [root@vt-vtd ~]# xm pci-attach HVMDomainVtd 0:2:0.0 7 + +VTd hotplug usage model: +------------------------ + + * For live migration: As you know, VTd device would break the live migration as physical device can't be save/restored like virtual device. With hotplug, live migration is back again. Just hot remove all the VTd devices before live migration and hot add new VTd devices on target machine after live migration. + + * VTd hotplug for device switch: VTd hotplug can be used to dynamically switch physical device between different HVM guest without shutdown. + + +VT-d Enabled Systems +-------------------- + +1) For VT-d enabling work on Xen, we have been using development +systems using following Intel motherboards: + - DQ35MP + - DQ35JO + +2) As far as we know, following OEM systems also has vt-d enabled. +Feel free to add others as they become available. + +- Dell: Optiplex 755 +http://www.dell.com/content/products/category.aspx/optix?c=us&cs=555&l=en&s=biz + +- HP Compaq: DC7800 +http://h10010.www1.hp.com/wwpc/us/en/en/WF04a/12454-12454-64287-321860-3328898.html + +For more information, pls refer to http://wiki.xensource.com/xenwiki/VTdHowTo. + + +Assigning devices to HVM domains +-------------------------------- + +Most device types such as NIC, HBA, EHCI and UHCI can be assigned to +an HVM domain. + +But some devices have design features which make them unsuitable for +assignment to an HVM domain. Examples include: + + * Device has an internal resource, such as private memory, which is + mapped to memory address space with BAR (Base Address Register). + * Driver submits command with a pointer to a buffer within internal + resource. Device decodes the pointer (address), and accesses to the + buffer. + +In an HVM domain, the BAR is virtualized, and host-BAR value and +guest-BAR value are different. The addresses of internal resource from +device's view and driver's view are different. Similarly, the +addresses of buffer within internal resource from device's view and +driver's view are different. As a result, device can't access to the +buffer specified by driver. + +Such devices assigned to HVM domain currently do not work. diff --git a/docs/misc/vtpm.txt b/docs/misc/vtpm.txt new file mode 100644 index 0000000..9e34db5 --- /dev/null +++ b/docs/misc/vtpm.txt @@ -0,0 +1,152 @@ +Copyright: IBM Corporation (C), Intel Corporation +29 June 2006 +Authors: Stefan Berger <stefanb@us.ibm.com> (IBM), + Employees of Intel Corp + +This document gives a short introduction to the virtual TPM support +in XEN and goes as far as connecting a user domain to a virtual TPM +instance and doing a short test to verify success. It is assumed +that the user is fairly familiar with compiling and installing XEN +and Linux on a machine. + +Production Prerequisites: An x86-based machine machine with a +Linux-supported TPM on the motherboard (NSC, Atmel, Infineon, TPM V1.2). +Development Prerequisites: An emulator for TESTING ONLY is provided + + +Compiling the XEN tree: +----------------------- + +Compile the XEN tree as usual after the following lines set in the +linux-2.6.??-xen/.config file: + +CONFIG_XEN_TPMDEV_BACKEND=m + +CONFIG_TCG_TPM=m +CONFIG_TCG_TIS=m (supported after 2.6.17-rc4) +CONFIG_TCG_NSC=m +CONFIG_TCG_ATMEL=m +CONFIG_TCG_INFINEON=m +CONFIG_TCG_XEN=m +<possible other TPM drivers supported by Linux> + +If the frontend driver needs to be compiled into the user domain +kernel, then the following two lines should be changed. + +CONFIG_TCG_TPM=y +CONFIG_TCG_XEN=y + + +You must also enable the virtual TPM to be built: + +In Config.mk in the Xen root directory set the line + +VTPM_TOOLS ?= y + +and in + +tools/vtpm/Rules.mk set the line + +BUILD_EMULATOR = y + +Now build the Xen sources from Xen's root directory: + +make install + + +Also build the initial RAM disk if necessary. + +Reboot the machine with the created Xen kernel. + +Note: If you do not want any TPM-related code compiled into your +kernel or built as module then comment all the above lines like +this example: +# CONFIG_TCG_TPM is not set + + +Modifying VM Configuration files: +--------------------------------- + +VM configuration files need to be adapted to make a TPM instance +available to a user domain. The following VM configuration file is +an example of how a user domain can be configured to have a TPM +available. It works similar to making a network interface +available to a domain. + +kernel = "/boot/vmlinuz-2.6.x" +ramdisk = "/xen/initrd_domU/U1_ramdisk.img" +memory = 32 +name = "TPMUserDomain0" +vtpm = ['instance=1,backend=0'] +root = "/dev/ram0 cosole=tty ro" +vif = ['backend=0'] + +In the above configuration file the line 'vtpm = ...' provides +information about the domain where the virtual TPM is running and +where the TPM backend has been compiled into - this has to be +domain 0 at the moment - and which TPM instance the user domain +is supposed to talk to. Note that each running VM must use a +different instance and that using instance 0 is NOT allowed. The +instance parameter is taken as the desired instance number, but +the actual instance number that is assigned to the virtual machine +can be different. This is the case if for example that particular +instance is already used by another virtual machine. The association +of which TPM instance number is used by which virtual machine is +kept in the file /var/vtpm/vtpm.db. Associations are maintained by +a xend-internal vTPM UUID and vTPM instance number. + +Note: If you do not want TPM functionality for your user domain simply +leave out the 'vtpm' line in the configuration file. + + +Running the TPM: +---------------- + +To run the vTPM, the device /dev/vtpm must be available. +Verify that 'ls -l /dev/vtpm' shows the following output: + +crw------- 1 root root 10, 225 Aug 11 06:58 /dev/vtpm + +If it is not available, run the following command as 'root'. +mknod /dev/vtpm c 10 225 + +Make sure that the vTPM is running in domain 0. To do this run the +following: + +modprobe tpmbk + +/usr/bin/vtpm_managerd + +Start a user domain using the 'xm create' command. Once you are in the +shell of the user domain, you should be able to do the following as +user 'root': + +Insert the TPM frontend into the kernel if it has been compiled as a +kernel module. + +> modprobe tpm_xenu + +Check the status of the TPM + +> cd /sys/devices/xen/vtpm-0 +> ls +[...] cancel caps pcrs pubek [...] +> cat pcrs +PCR-00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-01: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-02: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-03: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-04: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-05: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-06: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-07: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-08: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +[...] + +At this point the user domain has been successfully connected to its +virtual TPM instance. + +For further information please read the documentation in +tools/vtpm_manager/README and tools/vtpm/README + +Stefan Berger and Employees of the Intel Corp diff --git a/docs/misc/xen_config.html b/docs/misc/xen_config.html new file mode 100644 index 0000000..585adea --- /dev/null +++ b/docs/misc/xen_config.html @@ -0,0 +1,194 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"> + <title>Xen Configuration Syntax + + +
+

Xen Configuration Syntax

+
+ +Version 0.2
2004 July 19
+
+ +

Xen Configuration

+The Xen virtual machine creation tools provide command-line interfaces and +HTTP interfaces to creating virtual machines. Underneath all these interfaces +virtual machine configuration data is represented in a common configuration syntax +called SXP. + +The SXP syntax is s-expressions (sxprs), a simple bracketed abstract syntax. +Python lists are used to represent its parsed form, with a support +api providing access to fields and values (class xen.xend.sxp). + +

SXP syntax

+

A general s-expression has the syntax: +

+s-exp = '(' s-exp* ')' | atom | string
+
+Where an atom is an unquoted string, such as fred or /domain/0. +A string is a quoted string, supporting the usual escape sequences. +Strings support single or double quotes: 'fred 1' or "fred 2" are both accepted. +The characters ()[]<>{} are separators. + +

An element is an s-expression representing data similar to XML. +It has the form: +

+element    = '(' name attributes? value* ')'
+name       = atom
+attributes = '(' '@' attribute* ')'
+attribute  = '(' attrname attrval ')'
+attrname   = atom
+attrval    = atom | string
+value      = element | atom | string
+
+The name is the element name (roughly indentifying its type). +The attributes is a list of attribute-values. +We usually prefer to avoid using attributes, and use sub-elements instead. +As in XML, any element may have the attribute id to give it an identifier +for later reference. + +

VM configuration

+A vm configuration is a single SXP vm element, with subelements for +vm parameters and devices. The supported elements and their fields +are listed below. + +

(vm) element

+The top-level element, a virtual machine configuration. +
    +
  • name: string, required. Domain name. +
  • id: int, optional, default generated. Domain unique id. +
  • memory: int, required. Domain memory in MB. +
  • maxmem: int, optional. Maximum domain memory in MB. +
  • cpu: int, optional. Cpu to run on. +
  • cpu_weight, float, optional, default 1. Cpu weight - controls share of CPU. +
  • image: linux | netbsd | ..., required. Domain image (OS-specific element). +
  • backend: any backend device type, optional, default none. +
  • device: any device type, optional, repeats. Device. +
  • restart: string, optional, default onreboot. Restart mode, one of +
    • onreboot: restart the domain when it exits with code reboot. +
    • always: always restart the domain when it exits. +
    • never: never restart the domain. +
    +
  • console: int, optional, default 9600 + domain id. Console port. +
+ +

(image (linux)) element

+Defines a linux kernel image and its command-line parameters. +
    +
  • kernel: string, required. Kernel image file (absolute path). +
  • root: string, optional. Root device. +
  • ip: string, optional. IP address specifier. +
  • ramdisk: string, optional, default none. Ramdisk file (absolute path). +
  • args: string, optional. Extra kernel args. +
+ +

(image (netbsd)) element

+Defines a netbsd kernel image and its command-line parameters. +
    +
  • kernel: string, required. Kernel image file. +
  • root: string, optional. Root device. +
  • ip: string, optional. IP address specifier. +
  • ramdisk: string, optional, default none. Ramdisk file. +
  • args: string, optional. Extra kernel args. +
+ +

(backend (blkif)) element

+The vm is a block device backend. +The vm can have pci devices configured. + +

(backend (netif)) element

+The vm is a net device backend. + +

(device (vif)) element

+Defines a virtual network interface. +
    +
  • mac: string, required. Interface MAC address. +
  • bridge: string, optional, default system-dependent. Bridge to connect to. +
  • script: string, optional, default system-dependent. Vif script to use + when bringing the interface up or down. +
  • ip: IP address, optional, no default. May be repeated. An IP address + or CIDR-format subnet the vif may use. +
  • backend: domain name or id, optional, default 0. + Defines the domain to use as the backend for the interface. +
+ +

(device (vbd)) element

+Defines a virtual block device. +
    +
  • uname: string, required. Virtual block device id, e.g phys:hda1. +
  • dev: string, required. Device file name in domain, e.g. xda1. +
  • mode: string, optional, default r. Read-write mode: r | rw | w. +
  • backend: domain name or id, optional, default 0. + Defines the domain to use as the backend for the device. +
+The uname field defines the device being exported from the block device +controller. The dev field defines the device name the vm will see. +The device is read-only unless the mode contains w. + +

(device (pci)) element

+Defines a pci device. +
    +
  • bus: int, required. PCI bus id. +
  • dev: int, required. PCI dev id. +
  • func: int, required. PCI func id. +
+The bus, dev or func may be given in hex, +e.g. 0xff. With no leading 0x they are interpreted as decimal. + +

(vnet) element

+Defines the virtual networks associated with vifs (may go away soon). +Contains a list of vif elements: +
    +
  • id: id of the vif being configured. +
  • vnet: id of vnet the vif is assigned to. +
+ +

Examples

+

A vm with 64 MB memory, root on /dev/xda1 (mapped from /dev/hda1), +one vif with default MAC. +

+(vm
+    (name xendom1)
+    (memory 64)
+    (image
+        (linux
+            (kernel /boot/vmlinuz-2.6.12-xen)
+            (ip ::::xendom1:eth0:dhcp)
+            (root /dev/xda1)
+            (args 'rw fastboot 4')
+        )
+    )
+    (device (vif))
+    (device (vbd (uname phy:hda1) (dev xda1) (mode w)))
+)
+
+ +

A vm with 64 MB memory, NFS root, and 2 vifs on separate vnets. +

+(vm 
+   (name xendom2)
+   (memory 64)
+    # Define a linux image.
+    (image
+        (linux 
+            (kernel "/boot/vmlinuz-2.4.26-xen")
+            (ip     "::::xendom2:eth0:dhcp")
+            (root   "/dev/nfs")
+            (args   "rw fastboot nfsroot=15.144.25.79:/opt/xen/xendom2 4")
+         )
+    )
+    # Define some vifs, with ids for use later.
+    (device (vif (@ (id vif1)) (mac aa:00:00:00:00:12)))
+    (device (vif (@ (id vif2)) (mac aa:00:00:00:10:12)))
+
+     # Configure vnets. Refer to vifs by id.
+     (vnet (vif (id vif1) (vnet 1))
+           (vif (id vif2) (vnet 2)))
+
+)
+
+ + + diff --git a/docs/misc/xend.tex b/docs/misc/xend.tex new file mode 100644 index 0000000..7963803 --- /dev/null +++ b/docs/misc/xend.tex @@ -0,0 +1,435 @@ +% -*- mode: LaTeX -*- +\def\seca{\chapter} +\def\secb{\section} +\def\secc{\subsection} +\def\secd{\subsubsection} +\def\refa{chapter} +\def\refb{section} +\def\refc{section} +\def\refd{section} + +%\def\seca{\section} +%\def\secb{\subsection} +%\def\secc{\subsubsection} +%\def\refa{section} +%\def\refb{section} +%\def\refc{section} + +\documentclass[11pt,twoside,final,openright]{report} +\usepackage{a4,graphicx,setspace} +\setstretch{1.15} + +\begin{document} + +% TITLE PAGE +\pagestyle{empty} +\begin{center} +\vspace*{\fill} +\includegraphics{figs/xenlogo.eps} +\vfill +\vfill +\vfill +\begin{tabular}{l} +{\Huge \bf Xend} \\[4mm] +{\huge Xen v2.0 for x86} \\[80mm] + +{\Large Xen is Copyright (c) 2004, The Xen Team} \\[3mm] +{\Large University of Cambridge, UK} \\[20mm] +{\large Last updated 30 August 2004} +\end{tabular} +\vfill +\end{center} +\cleardoublepage + +% TABLE OF CONTENTS +\pagestyle{plain} +\pagenumbering{roman} +{ \parskip 0pt plus 1pt + \tableofcontents } +\cleardoublepage +% PREPARE FOR MAIN TEXT +\pagenumbering{arabic} +\raggedbottom +\widowpenalty=10000 +\clubpenalty=10000 +\parindent=0pt +\renewcommand{\topfraction}{.8} +\renewcommand{\bottomfraction}{.8} +\renewcommand{\textfraction}{.2} +\renewcommand{\floatpagefraction}{.8} + +\setstretch{1.15} + +\seca{Introduction} +Xend is the control daemon used to manage a machine running the Xen hypervisor. +Xend is responsible for creating and destroying domains and managing their +resources, such as virtual block devices and virtual network interfaces. + +Xend exists because the Xen hypervisor itself only manages the memory image +of a domain and its scheduling. Xen provides the event channels that connect +a domain to its devices, but is intentionally not involved in setting them up. + +Xend runs as a daemon in the privileged domain 0 and uses a low-level api +to communicate with Xen via the domain 0 kernel. Xend exports its control +interface to its clients using HTTP. Most programming languages have +HTTP client libraries, so this interface can be used from most popular +languages, for example Python, Perl, C, Java. +Xend itself is written in Python, as are most of the Xen tools. + +The xend interface is intended to be a complete interface for the creation +and management of domains. It supports domain creation, shutdown, reboot, +destruction, save, restore and migration. + +When xend creates a domain it creates the domain memory image and communicates +with the device driver domain(s) to configure the devices for the domain. +This sets up connections between the domain and backend device controllers +in the driver domain. When a domain shuts down its memory image cannot be fully released +unless its backend devices are released and disconnected. This is done by xend. +In order to protect against loss of this information when xend is restarted, +xend maintains a persistent database of domain configurations. This allows +xend to be stopped and restarted without loss of configuration information. +For example, in order to upgrade the xend software. + +\seca{Domain lifecycle} +\secb{Domain creation} +Xend is instructed to create a domain by posting a domain\_create message to it, +containing the domain configuration to be instantiated. The domain configuration +is in sxp format and is as far as possible {\em fully-bound}, that is, all +parameters are fully-specified. The domain configuration is saved in the filesystem +so that it can be reused later if necessary. + +The domain configuration specifies the domain name, memory size, kernel image +and parameters, and all the domain devices. Xend uses the Xen api to create +the domain memory image, and then {\em builds} the memory image for the domain +using the kernel image. At this point the domain exists, but it cannot be run because +it has no devices. Xend then communicates with the device driver domain to create +the configured devices. Once the devices are created it sets up event channels +for them between the driver domain and the new domain, and notifies the new domain +that its devices are connected. At this point the domain can be started. + +Xend is also responsible for managing domain consoles. When a domain is created, +xend sets up a console event channel to the domain, and creates a TCP listening port +for the domain console. When a connection is accepted to the port, xend +connects input and output for the port to the domain console channel. + +\secb{Domain destruction} +When a domain terminates, because it has been shutdown or it has crashed, the +domain resources must be released so that the domain memory image can be +finally removed from xen. Xend monitors the domains, and is also signaled by +xen (using a VIRQ) when a domain exits. Xend examines the domain states and +determines which domains have exited. It then communicates with the driver domain +to release the devices for exited domains. Xend also closes any open console +connections and removes the TCP listeners for exited domains. +Once all devices have been released it instructs xen to destroy the memory image. + +\secb{Domain restart} +Domain restart is the xen equivalent of a machine reboot. When a domain +exits because it has been shutdown in reboot mode, its exit code is reboot. +When examining domains to find those that have exited and destroy them, +xend detects those that have exited for reboot and does not completely destroy +them. It disconnects all their devices, and detaches the console listener +from its channel to the domain, but does not close it. Instead it schedules +a call to rebuild the domain from its configuration. This proceeds almost +identically to creating the domain, except that the console listener is +reused and connected to the new domain. This allows existing console +connections to remain connected across a domain restart. The restarted +domain keeps the same name and domain id. + +The determination of when to restart a domain is in fact slightly more +complex than described above. A domain is configured with a +{\em restart mode}. If the restart mode is {\em onreboot}, the default, +restart happens when the domain is shutdown normally and +exits with code reboot. If the restart mode is {\em never} the domain is +not restarted. If the restart mode is {\em always} the domain is always +restarted, regardless of how it exited. + +In order to prevent continual domain crash causing restart loops, xend +has a {\em minimum restart time}. Xend remembers when a domain was last +restarted and will fail a restart that happens inside the minimum +restart time. + +\seca{Devices} +\secb{Virtual network interfaces} +Each virtual network interface (vif) has 2 parts: the font-end device in its domain, +and the back-end device in the driver domain. Usually the driver domain is domain 0, +and there is a linux network device corresponding to the vif. The linux device for +interface N on domain D is called vifD.N. When a packet is sent on the vif in the +domain the packet is received from the linux device. The linux devices are connected +to network interfaces using ethernet bridging. + +The default setup is a bridge xen-br0, with eth0 connected to it, and the routes +for eth0 directed at xen-br0. This is controlled by the xend network setup script, +default {\tt /etc/xen/network}, which is run when xend starts. + +When the vifs for a domain are created, a vif control script, default {\tt /etc/xen/vif-bridge}, +is run to connect the vif to its bridge. The default script connects the vif +to xen-br0 and optionally sets up iptables rules to prevent IP address spoofing. +The bridge a vif is connected to can be defined in its configuration, and this is useful +for setting up virtual networks using several bridges. + +\secb{Virtual block devices} +Virtual block devices in a domain are interfaces onto back-end device drivers +that export physical devices to domains. In the default configuration the back-end +driver is in domain 0 and can export any linux block device to a domain. This includes +physical disk partitions, LVM volumes and loopback mounts of files. In fact anything +that linux can represent as a block device can be exported to a domain as virtual +block device. + +\seca{Xend invocation} +Xend is started (by root) using the command +\begin{verbatim} +xend start +\end{verbatim} +Xend can be stopped using +\begin{verbatim} +xend stop +\end{verbatim} +Xend must be started before any domains (apart from domain 0) can be created. +If you try to use the {\tt xm} tool when xend is not running you will get a +'connection refused' message. + +\secb{Xend configuration} +Xend reads its own configuration from {\tt /etc/xen/xend-config.sxp}, which is +a sequence of s-expressions. The configuration parameters are: +\begin{itemize} + +\item xend-port: Port xend should use for the HTTP interface (default 8000). + +\item xend-address: Address xend should listen on. + Specifying 'localhost' prevents remote connections. + Specifying the empty string '' allows all connections, and is the default. + +\item network-script: The script used to start/stop networking for xend (default network). + +\item vif-bridge: The default bridge that virtual interfaces should be connected to + (default xen-br0). + +\item vif-script: The default script used to control virtual interfaces + (default vif-bridge). + +\item vif-antispoof: Whether iptables should be set up to prevent IP spoofing for + virtual interfaces (default yes). +\end{itemize} + +Configuration scripts ({\it e.g.} for network-script) are looked for in {\tt /etc/xen} +unless their name begins with '/'. + +Xend sends its log output to {\tt /var/log/xen/xend.log}. This is a rotating logfile, +and logs are moved onto {\tt xend.log.1} {\it etc.} as they get large. Old logs may +be deleted. + +\secb{Xend database} +Xend needs to make some data persistent, and it uses files under {\tt /var/xen/xend-db} +for this. The persistent data is stored in files in SXP format. Domain information +is saved when domains are created. When xend starts it reads the file {\tt /var/xen/lastboot} +(if it exists) to determine the last time the system was rebooted. It compares this time +with the last reboot time in {\tt wtmp} to determine if the system has been rebooted +since xend last ran. If the system has been rebooted xend removes all its saved data +that is not persistent across reboots (for example domain data). + +\seca{Xend HTTP Interface} + The xend interface uses HTTP 1.1 \cite{http} as its transport. +Simple PUT and GET calls can encode parameters using the standard url-encoding +for parameters: MIME type {\tt application/x-www-form-urlencoded}. +When file upload is required, the {\tt multipart/form-data} encoding is used. +See the HTML 4.1 specification for details \cite{html}. + +Xend functions as a webserver and supports two interfaces: one +for web-browsers and one for programs. +The web-browser interface returns replies in HTML and includes forms +for interactive operations such as stopping domains and creating domains +from an uploaded configuration. The programmatic interface usually returns replies +in s-expression format. Both interfaces are accessed +in exactly the same way over HTTP - the only difference is the data returned. + +The webserver distinguishes browsers from programs using the {\tt User-Agent} +and {\tt Accept} headers in the HTTP request. If there is no {\tt User-Agent} or no +{\tt Acccept} header, or {\tt Accept} includes the type {\tt application/sxp}, the +webserver assumes the client is a program and returns SXP. Otherwise +it assumes the client is a webserver and returns HTML. +In some cases the return value is essentially a string, so {\tt Content-Type} +{\tt text/plain} is returned. + +The HTTP api supported is listed below. All paths in it are relative to the +server root, for example {\tt http://localhost:8000/xend}. +As defined in the HTTP specification, we use GET for side-effect free +operations that may safely be repeated, and POST for operations with +side-effects. For each request we list the HTTP method (GET or POST), +the url relative to the server root, the operation name and arguments (if any). +The operation name is passed as request parameter 'op', and the arguments +are passed by name. Operation name and parameters can be encoded using either +encoding described above. We also list the corresponding api function from the +Python client interface in {\tt xen.xend.XendClient}. + +\begin{itemize} +\item {\tt GET /},\\ + {\tt xend()}:\\ + Get list of urls under xend root. + +\item {\tt GET /node},\\ + {\tt xend\_node()}:\\ + Get node information. + +\item {\tt POST /node shutdown()},\\ + {\tt xend\_node\_shutdown()}:\\ + Shutdown the node + +\item {\tt POST /node reboot()},\\ + {\tt xend\_node\_reboot()}:\\ + Reboot the node + +\item {\tt POST /node notify()}:\\ + Set node notification url + +\item {\tt GET /node/dmesg},\\ + {\tt xend\_node\_dmesg()}:\\ + Get xen boot message. + +\item {\tt GET /node/log},\\ + {\tt xend\_node\_log()}:\\ + Get xend log. + +\item {\tt GET /domain}\\ + {\tt xend\_domains()}:\\ + Get list of domains. + +\item {\tt POST /domain create(config)},\\ + {\tt xend\_domain\_create(config)}:\\ + Create a domain. + +\item {\tt POST /domain restore(file)},\\ + {\tt xend\_domain\_restore(filename)}:\\ + Restore a saved domain. + +\item {\tt GET /domain/},\\ + {\tt xend\_domain(dom)}:\\ + Get domain information. + +\item {\tt POST /domain/[dom] configure(config)},\\ + {\tt xend\_domain\_configure(dom, conf)}:\\ + Configure an existing domain (for internal use by restore and migrate). + +\item {\tt POST /domain/[dom] unpause()},\\ + {\tt xend\_domain\_unpause(dom)}:\\ + Start domain running + +\item {\tt POST /domain/[dom] pause()},\\ + {\tt xend\_domain\_pause(dom)}:\\ + Stop domain running. + +\item {\tt POST /domain/[dom] shutdown(reason)},\\ + {\tt xend\_domain\_shutdown(dom, reason)}:\\ + Shutdown domain, reason can be reboot, poweroff, halt. + +\item {\tt POST /domain/[dom] destroy(reason)},\\ + {\tt xend\_domain\_destroy(dom, reason)}:\\ + Destroy domain, reason can be reboot, halt. + +\item {\tt POST /domain/[dom] save(file)},\\ + {\tt xend\_domain\_save(dom, filename)}:\\ + Save a domain to a file. + +\item {\tt POST /domain/[dom] migrate(dst)},\\ + {\tt xend\_domain\_migrate(dom, dst)}:\\ + Migrate a domain. + +\item {\tt POST /domain/[dom] pincpu(cpu)},\\ + {\tt xend\_domain\_pincpu(self, id, cpu)}\\: + Pin a domain to a cpu. + +\item {\tt POST /domain/[dom] maxmem\_set(memory)},\\ + {\tt xend\_domain\_maxmem\_set(dom, memory)}:\\ + Set domain maximum memory limit. + +\item {\tt POST /domain/[dom] device\_create(config)}\\ + {\tt xend\_domain\_device\_create(dom, config)}:\\ + Add a device to a domain. + +\item {\tt POST /domain/[dom] device\_destroy(type, index)},\\ + {\tt xend\_domain\_device\_destroy(dom, type, index)}:\\ + Delete a device from a domain + +\item {\tt GET /domain/[dom] vifs()},\\ + {\tt xend\_domain\_vifs(dom)}:\\ + Get virtual network interfaces. + +\item {\tt GET /domain/[dom] vif(vif)},\\ + {\tt xend\_domain\_vif(dom, vif)}:\\ + Get virtual network interface. + +\item {\tt GET /domain/[dom] vbds()},\\ + {\tt xend\_domain\_vbds(dom)}:\\ + Get virtual block devices. + +\item {\tt GET /domain/[dom] vbd(vbd)},\\ + {\tt xend\_domain\_vbd(dom, vbd)}:\\ + Get virtual block device. + +\item {\tt GET /console},\\ + {\tt xend\_consoles()}:\\ + Get list of consoles. + +\item {\tt GET /console/[id]}\\ + {\tt xend\_console(id)}:\\ + Get information about a console. + +\item {\tt GET /console/[id] disconnect()}\\ + {\tt xend\_console\_disconnect(self, id)}:\\ + Disconnect any console TCP connection. + +\item {\tt GET /vnet}\\ + {\tt xend\_vnets()}:\\ + Get list of vnets (virtual networks). + +\item {\tt GET /vnet/[id]}\\ + {\tt xend\_vnet(id)}:\\ + Get information about a virtual network. + +\item {\tt POST /vnet create(config)}\\ + {\tt xend\_vnet\_create(conf)}:\\ + Create a vnet. + +\item {\tt POST /vnet/[id] delete()}\\ + {\tt xend\_vnet\_delete(id)}:\\ + Delete a vnet. + +\item {\tt POST /event inject(event)}\\ + {\tt xend\_event\_inject(sxpr)}:\\ + Inject an event. + +\end{itemize} + +\secb{Xend debugging interface} +Xend also listens on port 8001. Connecting to this port (for example via telnet) +allows access to some debugging functions: +\begin{itemize} +\item help: list functions +\item traceon: turn xend tracing on +\item traceoff: turn xend tracing off +\item quit: disconnect. +\item info: list console listeners, block and network device controllers. +\end{itemize} + +When tracing is on xend logs all functions calls and exceptions to +{\tt /var/log/xen/xend.trace}. + +\begin{thebibliography}{99} + +\bibitem{html} +HTML 4.01 Specification,\\ +http://www.w3.org/TR/html4/,\\ +W3C Recommendation, 24 December 1999. + +\bibitem{http} +Hypertext Transfer Protocol -- HTTP/1.1,\\ +http://www.ietf.org/rfc/rfc2616.txt,\\ +RFC 2616, IETF 1999. + +\bibitem{ssh} +http://www.openssh.org. + +\bibitem{stunnel} +http://www.stunnel.org. + +\end{thebibliography} +\end{document} diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt new file mode 100644 index 0000000..f46cf4f --- /dev/null +++ b/docs/misc/xenstore.txt @@ -0,0 +1,319 @@ +Xenstore protocol specification +------------------------------- + +Xenstore implements a database which maps filename-like pathnames +(also known as `keys') to values. Clients may read and write values, +watch for changes, and set permissions to allow or deny access. There +is a rudimentary transaction system. + +While xenstore and most tools and APIs are capable of dealing with +arbitrary binary data as values, this should generally be avoided. +Data should generally be human-readable for ease of management and +debugging; xenstore is not a high-performance facility and should be +used only for small amounts of control plane data. Therefore xenstore +values should normally be 7-bit ASCII text strings containing bytes +0x20..0x7f only, and should not contain a trailing nul byte. (The +APIs used for accessing xenstore generally add a nul when reading, for +the caller's convenience.) + +A separate specification will detail the keys and values which are +used in the Xen system and what their meanings are. (Sadly that +specification currently exists only in multiple out-of-date versions.) + + +Paths are /-separated and start with a /, just as Unix filenames. + +We can speak of two paths being and , which is the +case if they're identical, or if is /, or if / is an +initial substring of . (This includes being a child of +itself.) + +If a particular path exists, all of its parents do too. Every +existing path maps to a possibly empty value, and may also have zero +or more immediate children. There is thus no particular distinction +between directories and leaf nodes. However, it is conventional not +to store nonempty values at nodes which also have children. + +The permitted character for paths set is ASCII alphanumerics and plus +the four punctuation characters -/_@ (hyphen slash underscore atsign). +@ should be avoided except to specify special watches (see below). +Doubled slashes and trailing slashes (except to specify the root) are +forbidden. The empty path is also forbidden. Paths longer than 3072 +bytes are forbidden; clients specifying relative paths should keep +them to within 2048 bytes. (See XENSTORE_*_PATH_MAX in xs_wire.h.) + + +Communication with xenstore is via either sockets, or event channel +and shared memory, as specified in io/xs_wire.h: each message in +either direction is a header formatted as a struct xsd_sockmsg +followed by xsd_sockmsg.len bytes of payload. + +The payload syntax varies according to the type field. Generally +requests each generate a reply with an identical type, req_id and +tx_id. However, if an error occurs, a reply will be returned with +type ERROR, and only req_id and tx_id copied from the request. + +A caller who sends several requests may receive the replies in any +order and must use req_id (and tx_id, if applicable) to match up +replies to requests. (The current implementation always replies to +requests in the order received but this should not be relied on.) + +The payload length (len field of the header) is limited to 4096 +(XENSTORE_PAYLOAD_MAX) in both directions. If a client exceeds the +limit, its xenstored connection will be immediately killed by +xenstored, which is usually catastrophic from the client's point of +view. Clients (particularly domains, which cannot just reconnect) +should avoid this. + +Existing clients do not always contain defences against overly long +payloads. Increasing xenstored's limit is therefore difficult; it +would require negotiation with the client, and obviously would make +parts of xenstore inaccessible to some clients. In any case passing +bulk data through xenstore is not recommended as the performance +properties are poor. + + +---------- Xenstore protocol details - introduction ---------- + +The payload syntax and semantics of the requests and replies are +described below. In the payload syntax specifications we use the +following notations: + + | A nul (zero) byte. + A string guaranteed not to contain any nul bytes. + Binary data (which may contain zero or more nul bytes) + |* Zero or more strings each followed by a trailing nul + |+ One or more strings each followed by a trailing nul + ? Reserved value (may not contain nuls) + ?? Reserved value (may contain nuls) + +Except as otherwise noted, reserved values are believed to be sent as +empty strings by all current clients. Clients should not send +nonempty strings for reserved values; those parts of the protocol may +be used for extension in the future. + + +Error replies are as follows: + +ERROR E| + Where E is the name of an errno value + listed in io/xs_wire.h. Note that the string name + is transmitted, not a numeric value. + + +Where no reply payload format is specified below, success responses +have the following payload: + OK| + +Values commonly included in payloads include: + + + Specifies a path in the hierarchical key structure. + If starts with a / it simply represents that path. + + is allowed not to start with /, in which case the + caller must be a domain (rather than connected via a socket) + and the path is taken to be relative to /local/domain/ + (eg, `x/y' sent by domain 3 would mean `/local/domain/3/x/y'). + + + Integer domid, represented as decimal number 0..65535. + Parsing errors and values out of range generally go + undetected. The special DOMID_... values (see xen.h) are + represented as integers; unless otherwise specified it + is an error not to specify a real domain id. + + + +The following are the actual type values, including the request and +reply payloads as applicable: + + +---------- Database read, write and permissions operations ---------- + +READ | +WRITE | + Store and read the octet string at . + WRITE creates any missing parent paths, with empty values. + +MKDIR | + Ensures that the exists, by necessary by creating + it and any missing parents with empty values. If + or any parent already exists, its value is left unchanged. + +RM | + Ensures that the does not exist, by deleting + it and all of its children. It is not an error if does + not exist, but it _is_ an error if 's immediate parent + does not exist either. + +DIRECTORY | |* + Gives a list of the immediate children of , as only the + leafnames. The resulting children are each named + /. + +GET_PERMS | |+ +SET_PERMS ||+? + is one of the following + w write only + r read only + b both read and write + n no access + See http://wiki.xensource.com/xenwiki/XenBus section + `Permissions' for details of the permissions system. + +---------- Watches ---------- + +WATCH ||? + Adds a watch. + + When a is modified (including path creation, removal, + contents change or permissions change) this generates an event + on the changed . Changes made in transactions cause an + event only if and when committed. Each occurring event is + matched against all the watches currently set up, and each + matching watch results in a WATCH_EVENT message (see below). + + The event's path matches the watch's if it is an child + of . + + can be a to watch or @. In the + latter case may have any syntax but it matches + (according to the rules above) only the following special + events which are invented by xenstored: + @introduceDomain occurs on INTRODUCE + @releaseDomain occurs on any domain crash or + shutdown, and also on RELEASE + and domain destruction + + When a watch is first set up it is triggered once straight + away, with equal to . Watches may be triggered + spuriously. The tx_id in a WATCH request is ignored. + + Watches are supposed to be restricted by the permissions + system but in practice the implementation is imperfect. + Applications should not rely on being sent a notification for + paths that they cannot read; however, an application may rely + on being sent a watch when a path which it _is_ able to read + is deleted even if that leaves only a nonexistent unreadable + parent. A notification may omitted if a node's permissions + are changed so as to make it unreadable, in which case future + notifications may be suppressed (and if the node is later made + readable, some notifications may have been lost). + +WATCH_EVENT || + Unsolicited `reply' generated for matching modfication events + as described above. req_id and tx_id are both 0. + + is the event's path, ie the actual path that was + modified; however if the event was the recursive removal of an + parent of , is just + (rather than the actual path which was removed). So + is a child of , regardless. + + Iff for the watch was specified as a relative pathname, + the path will also be relative (with the same base, + obviously). + +UNWATCH ||? + +---------- Transactions ---------- + +TRANSACTION_START | | + is an opaque uint32_t allocated by xenstored + represented as unsigned decimal. After this, transaction may + be referenced by using (as 32-bit binary) in the + tx_id request header field. When transaction is started whole + db is copied; reads and writes happen on the copy. + It is not legal to send non-0 tx_id in TRANSACTION_START. + Currently xenstored has the bug that after 2^32 transactions + it will allocate the transid 0 for an actual transaction. + +TRANSACTION_END T| +TRANSACTION_END F| + tx_id must refer to existing transaction. After this + request the tx_id is no longer valid and may be reused by + xenstore. If F, the transaction is discarded. If T, + it is committed: if there were any other intervening writes + then our END gets get EAGAIN. + + The plan is that in the future only intervening `conflicting' + writes cause EAGAIN, meaning only writes or other commits + which changed paths which were read or written in the + transaction at hand. + +---------- Domain management and xenstored communications ---------- + +INTRODUCE |||? + Notifies xenstored to communicate with this domain. + + INTRODUCE is currently only used by xend (during domain + startup and various forms of restore and resume), and + xenstored prevents its use other than by dom0. + + must be a real domain id (not 0 and not a special + DOMID_... value). must be a machine page in that domain + represented in signed decimal (!). must be event + channel is an unbound event channel in (likewise in + decimal), on which xenstored will call bind_interdomain. + Violations of these rules may result in undefined behaviour; + for example passing a high-bit-set 32-bit mfn as an unsigned + decimal will attempt to use 0x7fffffff instead (!). + +RELEASE | + Manually requests that xenstored disconnect from the domain. + The event channel is unbound at the xenstored end and the page + unmapped. If the domain is still running it won't be able to + communicate with xenstored. NB that xenstored will in any + case detect domain destruction and disconnect by itself. + xenstored prevents the use of RELEASE other than by dom0. + +GET_DOMAIN_PATH | | + Returns the domain's base path, as is used for relative + transactions: ie, /local/domain/ (with + normalised). The answer will be useless unless is a + real domain id. + +IS_DOMAIN_INTRODUCED | T| or F| + Returns T if xenstored is in communication with the domain: + ie, if INTRODUCE for the domain has not yet been followed by + domain destruction or explicit RELEASE. + +RESUME | + + Arranges that @releaseDomain events will once more be + generated when the domain becomes shut down. This might have + to be used if a domain were to be shut down (generating one + @releaseDomain) and then subsequently restarted, since the + state-sensitive algorithm in xenstored will not otherwise send + further watch event notifications if the domain were to be + shut down again. + + It is not clear whether this is possible since one would + normally expect a domain not to be restarted after being shut + down without being destroyed in the meantime. There are + currently no users of this request in xen-unstable. + + xenstored prevents the use of RESUME other than by dom0. + +SET_TARGET || + Notifies xenstored that domain is targeting domain + . This grants domain full access to paths + owned by . Domain also inherits all + permissions granted to on all other paths. This + allows to behave as if it were dom0 when modifying + paths related to . + + xenstored prevents the use of SET_TARGET other than by dom0. + +---------- Miscellaneous ---------- + +DEBUG print||?? sends to debug log +DEBUG print| EINVAL +DEBUG check|?? checks xenstored innards +DEBUG no-op (future extension) + + These requests should not generally be used and may be + withdrawn in the future. + + diff --git a/docs/pythfilter.py b/docs/pythfilter.py new file mode 100644 index 0000000..3054f7c --- /dev/null +++ b/docs/pythfilter.py @@ -0,0 +1,658 @@ +#!/usr/bin/env python + +# pythfilter.py v1.5.5, written by Matthias Baas (baas@ira.uka.de) + +# Doxygen filter which can be used to document Python source code. +# Classes (incl. methods) and functions can be documented. +# Every comment that begins with ## is literally turned into an +# Doxygen comment. Consecutive comment lines are turned into +# comment blocks (-> /** ... */). +# All the stuff is put inside a namespace with the same name as +# the source file. + +# Conversions: +# ============ +# ##-blocks -> /** ... */ +# "class name(base): ..." -> "class name : public base {...}" +# "def name(params): ..." -> "name(params) {...}" + +# Changelog: +# 21.01.2003: Raw (r"") or unicode (u"") doc string will now be properly +# handled. (thanks to Richard Laager for the patch) +# 22.12.2003: Fixed a bug where no function names would be output for "def" +# blocks that were not in a class. +# (thanks to Richard Laager for the patch) +# 12.12.2003: Implemented code to handle static and class methods with +# this logic: Methods with "self" as the first argument are +# non-static. Methods with "cls" are Python class methods, +# which translate into static methods for Doxygen. Other +# methods are assumed to be static methods. As should be +# obvious, this logic doesn't take into account if the method +# is actually setup as a classmethod() or a staticmethod(), +# just if it follows the normal conventions. +# (thanks to Richard Laager for the patch) +# 11.12.2003: Corrected #includes to use os.path.sep instead of ".". Corrected +# namespace code to use "::" instead of ".". +# (thanks to Richard Laager for the patch) +# 11.12.2003: Methods beginning with two underscores that end with +# something other than two underscores are considered private +# and are handled accordingly. +# (thanks to Richard Laager for the patch) +# 03.12.2003: The first parameter of class methods (self) is removed from +# the documentation. +# 03.11.2003: The module docstring will be used as namespace documentation +# (thanks to Joe Bronkema for the patch) +# 08.07.2003: Namespaces get a default documentation so that the namespace +# and its contents will show up in the generated documentation. +# 05.02.2003: Directories will be delted during synchronization. +# 31.01.2003: -f option & filtering entire directory trees. +# 10.08.2002: In base classes the '.' will be replaced by '::' +# 18.07.2002: * and ** will be translated into arguments +# 18.07.2002: Argument lists may contain default values using constructors. +# 18.06.2002: Support for ## public: +# 21.01.2002: from ... import will be translated to "using namespace ...;" +# TODO: "from ... import *" vs "from ... import names" +# TODO: Using normal imports: name.name -> name::name +# 20.01.2002: #includes will be placed in front of the namespace + +###################################################################### + +# The program is written as a state machine with the following states: +# +# - OUTSIDE The current position is outside any comment, +# class definition or function. +# +# - BUILD_COMMENT Begins with first "##". +# Ends with the first token that is no "##" +# at the same column as before. +# +# - BUILD_CLASS_DECL Begins with "class". +# Ends with ":" +# - BUILD_CLASS_BODY Begins just after BUILD_CLASS_DECL. +# The first following token (which is no comment) +# determines indentation depth. +# Ends with a token that has a smaller indendation. +# +# - BUILD_DEF_DECL Begins with "def". +# Ends with ":". +# - BUILD_DEF_BODY Begins just after BUILD_DEF_DECL. +# The first following token (which is no comment) +# determines indentation depth. +# Ends with a token that has a smaller indendation. + +import getopt +import glob +import os.path +import re +import shutil +import string +import sys +import token +import tokenize + +from stat import * + +OUTSIDE = 0 +BUILD_COMMENT = 1 +BUILD_CLASS_DECL = 2 +BUILD_CLASS_BODY = 3 +BUILD_DEF_DECL = 4 +BUILD_DEF_BODY = 5 +IMPORT = 6 +IMPORT_OP = 7 +IMPORT_APPEND = 8 + +# Output file stream +outfile = sys.stdout + +# Output buffer +outbuffer = [] + +out_row = 1 +out_col = 0 + +# Variables used by rec_name_n_param() +name = "" +param = "" +doc_string = "" +record_state = 0 +bracket_counter = 0 + +# Tuple: (row,column) +class_spos = (0,0) +def_spos = (0,0) +import_spos = (0,0) + +# Which import was used? ("import" or "from") +import_token = "" + +# Comment block buffer +comment_block = [] +comment_finished = 0 + +# Imported modules +modules = [] + +# Program state +stateStack = [OUTSIDE] + +# Keep track of whether module has a docstring +module_has_docstring = False + +# Keep track of member protection +protection_level = "public" +private_member = False + +# Keep track of the module namespace +namespace = "" + +###################################################################### +# Output string s. '\n' may only be at the end of the string (not +# somewhere in the middle). +# +# In: s - String +# spos - Startpos +###################################################################### +def output(s,spos, immediate=0): + global outbuffer, out_row, out_col, outfile + + os = string.rjust(s,spos[1]-out_col+len(s)) + + if immediate: + outfile.write(os) + else: + outbuffer.append(os) + + assert -1 == string.find(s[0:-2], "\n"), s + + if (s[-1:]=="\n"): + out_row = out_row+1 + out_col = 0 + else: + out_col = spos[1]+len(s) + + +###################################################################### +# Records a name and parameters. The name is either a class name or +# a function name. Then the parameter is either the base class or +# the function parameters. +# The name is stored in the global variable "name", the parameters +# in "param". +# The variable "record_state" holds the current state of this internal +# state machine. +# The recording is started by calling start_recording(). +# +# In: type, tok +###################################################################### +def rec_name_n_param(type, tok): + global record_state,name,param,doc_string,bracket_counter + s = record_state + # State 0: Do nothing. + if (s==0): + return + # State 1: Remember name. + elif (s==1): + name = tok + record_state = 2 + # State 2: Wait for opening bracket or colon + elif (s==2): + if (tok=='('): + bracket_counter = 1 + record_state=3 + if (tok==':'): record_state=4 + # State 3: Store parameter (or base class) and wait for an ending bracket + elif (s==3): + if (tok=='*' or tok=='**'): + tok='' + if (tok=='('): + bracket_counter = bracket_counter+1 + if (tok==')'): + bracket_counter = bracket_counter-1 + if bracket_counter==0: + record_state=4 + else: + param=param+tok + # State 4: Look for doc string + elif (s==4): + if (type==token.NEWLINE or type==token.INDENT or type==token.SLASHEQUAL): + return + elif (tok==":"): + return + elif (type==token.STRING): + while tok[:1]=='r' or tok[:1]=='u': + tok=tok[1:] + while tok[:1]=='"': + tok=tok[1:] + while tok[-1:]=='"': + tok=tok[:-1] + doc_string=tok + record_state=0 + +###################################################################### +# Starts the recording of a name & param part. +# The function rec_name_n_param() has to be fed with tokens. After +# the necessary tokens are fed the name and parameters can be found +# in the global variables "name" und "param". +###################################################################### +def start_recording(): + global record_state,param,name, doc_string + record_state=1 + name="" + param="" + doc_string="" + +###################################################################### +# Test if recording is finished +###################################################################### +def is_recording_finished(): + global record_state + return record_state==0 + +###################################################################### +## Gather comment block +###################################################################### +def gather_comment(type,tok,spos): + global comment_block,comment_finished + if (type!=tokenize.COMMENT): + comment_finished = 1 + else: + # Output old comment block if a new one is started. + if (comment_finished): + print_comment(spos) + comment_finished=0 + if (tok[0:2]=="##" and tok[0:3]!="###"): + append_comment_lines(tok[2:]) + +###################################################################### +## Output comment block and empty buffer. +###################################################################### +def print_comment(spos): + global comment_block,comment_finished + if (comment_block!=[]): + output("/** ",spos) + for c in comment_block: + output(c,spos) + output("*/\n",spos) + comment_block = [] + comment_finished = 0 + +###################################################################### +def set_state(s): + global stateStack + stateStack[len(stateStack)-1]=s + +###################################################################### +def get_state(): + global stateStack + return stateStack[len(stateStack)-1] + +###################################################################### +def push_state(s): + global stateStack + stateStack.append(s) + +###################################################################### +def pop_state(): + global stateStack + stateStack.pop() + + +###################################################################### +def tok_eater(type, tok, spos, epos, line): + global stateStack,name,param,class_spos,def_spos,import_spos + global doc_string, modules, import_token, module_has_docstring + global protection_level, private_member + global out_row + + while out_row + 1 < spos[0]: + output("\n", (0, 0)) + + rec_name_n_param(type,tok) + if (string.replace(string.strip(tok)," ","")=="##private:"): + protection_level = "private" + output("private:\n",spos) + elif (string.replace(string.strip(tok)," ","")=="##protected:"): + protection_level = "protected" + output("protected:\n",spos) + elif (string.replace(string.strip(tok)," ","")=="##public:"): + protection_level = "public" + output("public:\n",spos) + else: + gather_comment(type,tok,spos) + + state = get_state() + +# sys.stderr.write("%d: %s\n"%(state, tok)) + + # OUTSIDE + if (state==OUTSIDE): + if (tok=="class"): + start_recording() + class_spos = spos + push_state(BUILD_CLASS_DECL) + elif (tok=="def"): + start_recording() + def_spos = spos + push_state(BUILD_DEF_DECL) + elif (tok=="import") or (tok=="from"): + import_token = tok + import_spos = spos + modules = [] + push_state(IMPORT) + elif (spos[1] == 0 and tok[:3] == '"""'): + # Capture module docstring as namespace documentation + module_has_docstring = True + append_comment_lines("\\namespace %s\n" % namespace) + append_comment_lines(tok[3:-3]) + print_comment(spos) + + # IMPORT + elif (state==IMPORT): + if (type==token.NAME): + modules.append(tok) + set_state(IMPORT_OP) + # IMPORT_OP + elif (state==IMPORT_OP): + if (tok=="."): + set_state(IMPORT_APPEND) + elif (tok==","): + set_state(IMPORT) + else: + for m in modules: + output('#include "'+m.replace('.',os.path.sep)+'.py"\n', import_spos, immediate=1) + if import_token=="from": + output('using namespace '+m.replace('.', '::')+';\n', import_spos) + pop_state() + # IMPORT_APPEND + elif (state==IMPORT_APPEND): + if (type==token.NAME): + modules[len(modules)-1]+="."+tok + set_state(IMPORT_OP) + # BUILD_CLASS_DECL + elif (state==BUILD_CLASS_DECL): + if (is_recording_finished()): + s = "class "+name + if (param!=""): s = s+" : public "+param.replace('.','::') + if (doc_string!=""): + append_comment_lines(doc_string) + print_comment(class_spos) + output(s+"\n",class_spos) + output("{\n",(class_spos[0]+1,class_spos[1])) + protection_level = "public" + output(" public:\n",(class_spos[0]+2,class_spos[1])) + set_state(BUILD_CLASS_BODY) + # BUILD_CLASS_BODY + elif (state==BUILD_CLASS_BODY): + if (type!=token.INDENT and type!=token.NEWLINE and type!=40 and + type!=tokenize.NL and type!=tokenize.COMMENT and + (spos[1]<=class_spos[1])): + output("}; // end of class\n",(out_row+1,class_spos[1])) + pop_state() + elif (tok=="def"): + start_recording() + def_spos = spos + push_state(BUILD_DEF_DECL) + # BUILD_DEF_DECL + elif (state==BUILD_DEF_DECL): + if (is_recording_finished()): + param = param.replace("\n", " ") + param = param.replace("=", " = ") + params = param.split(",") + if BUILD_CLASS_BODY in stateStack: + if len(name) > 1 \ + and name[0:2] == '__' \ + and name[len(name)-2:len(name)] != '__' \ + and protection_level != 'private': + private_member = True + output(" private:\n",(def_spos[0]+2,def_spos[1])) + + if (doc_string != ""): + append_comment_lines(doc_string) + + print_comment(def_spos) + + output_function_decl(name, params) +# output("{\n",(def_spos[0]+1,def_spos[1])) + set_state(BUILD_DEF_BODY) + # BUILD_DEF_BODY + elif (state==BUILD_DEF_BODY): + if (type!=token.INDENT and type!=token.NEWLINE \ + and type!=40 and type!=tokenize.NL \ + and (spos[1]<=def_spos[1])): +# output("} // end of method/function\n",(out_row+1,def_spos[1])) + if private_member and protection_level != 'private': + private_member = False + output(" " + protection_level + ":\n",(def_spos[0]+2,def_spos[1])) + pop_state() +# else: +# output(tok,spos) + + +def output_function_decl(name, params): + global def_spos + + # Do we document a class method? then remove the 'self' parameter + if params[0] == 'self': + preamble = '' + params = params[1:] + else: + preamble = 'static ' + if params[0] == 'cls': + params = params[1:] + + param_string = string.join(params, ", Type ") + + if param_string == '': + param_string = '(' + param_string + ');\n' + else: + param_string = '(Type ' + param_string + ');\n' + + output(preamble, def_spos) + output(name, def_spos) + output(param_string, def_spos) + + +def append_comment_lines(lines): + map(append_comment_line, doc_string.split('\n')) + +paramRE = re.compile(r'(@param \w+):') + +def append_comment_line(line): + global paramRE + + comment_block.append(paramRE.sub(r'\1', line) + '\n') + +def dump(filename): + f = open(filename) + r = f.readlines() + for s in r: + sys.stdout.write(s) + +def filter(filename): + global name, module_has_docstring, source_root + + path,name = os.path.split(filename) + root,ext = os.path.splitext(name) + + if source_root and path.find(source_root) == 0: + path = path[len(source_root):] + + if path[0] == os.sep: + path = path[1:] + + ns = path.split(os.sep) + else: + ns = [] + + ns.append(root) + + for n in ns: + output("namespace " + n + " {\n",(0,0)) + + # set module name for tok_eater to use if there's a module doc string + name = root + +# sys.stderr.write('Filtering "'+filename+'"...') + f = open(filename) + tokenize.tokenize(f.readline, tok_eater) + f.close() + print_comment((0,0)) + + output("\n",(0,0)) + + for n in ns: + output("} // end of namespace\n",(0,0)) + + if not module_has_docstring: + # Put in default namespace documentation + output('/** \\namespace '+root+' \n',(0,0)) + output(' \\brief Module "%s" */\n'%(root),(0,0)) + + for s in outbuffer: + outfile.write(s) + + +def filterFile(filename, out=sys.stdout): + global outfile + + outfile = out + + try: + root,ext = os.path.splitext(filename) + + if ext==".py": + filter(filename) + else: + dump(filename) + +# sys.stderr.write("OK\n") + except IOError,e: + sys.stderr.write(e[1]+"\n") + + +###################################################################### + +# preparePath +def preparePath(path): + """Prepare a path. + + Checks if the path exists and creates it if it does not exist. + """ + if not os.path.exists(path): + parent = os.path.dirname(path) + if parent!="": + preparePath(parent) + os.mkdir(path) + +# isNewer +def isNewer(file1,file2): + """Check if file1 is newer than file2. + + file1 must be an existing file. + """ + if not os.path.exists(file2): + return True + return os.stat(file1)[ST_MTIME]>os.stat(file2)[ST_MTIME] + +# convert +def convert(srcpath, destpath): + """Convert a Python source tree into a C+ stub tree. + + All *.py files in srcpath (including sub-directories) are filtered + and written to destpath. If destpath exists, only the files + that have been modified are filtered again. Files that were deleted + from srcpath are also deleted in destpath if they are still present. + The function returns the number of processed *.py files. + """ + count=0 + sp = os.path.join(srcpath,"*") + sfiles = glob.glob(sp) + dp = os.path.join(destpath,"*") + dfiles = glob.glob(dp) + leftovers={} + for df in dfiles: + leftovers[os.path.basename(df)]=1 + + for srcfile in sfiles: + basename = os.path.basename(srcfile) + if basename in leftovers: + del leftovers[basename] + + # Is it a subdirectory? + if os.path.isdir(srcfile): + sdir = os.path.join(srcpath,basename) + ddir = os.path.join(destpath,basename) + count+=convert(sdir, ddir) + continue + # Check the extension (only *.py will be converted) + root, ext = os.path.splitext(srcfile) + if ext.lower()!=".py": + continue + + destfile = os.path.join(destpath,basename) + if destfile==srcfile: + print "WARNING: Input and output names are identical!" + sys.exit(1) + + count+=1 +# sys.stdout.write("%s\015"%(srcfile)) + + if isNewer(srcfile, destfile): + preparePath(os.path.dirname(destfile)) +# out=open(destfile,"w") +# filterFile(srcfile, out) +# out.close() + os.system("python %s -f %s>%s"%(sys.argv[0],srcfile,destfile)) + + # Delete obsolete files in destpath + for df in leftovers: + dname=os.path.join(destpath,df) + if os.path.isdir(dname): + try: + shutil.rmtree(dname) + except: + print "Can't remove obsolete directory '%s'"%dname + else: + try: + os.remove(dname) + except: + print "Can't remove obsolete file '%s'"%dname + + return count + + +###################################################################### +###################################################################### +###################################################################### + +filter_file = False +source_root = None + +try: + opts, args = getopt.getopt(sys.argv[1:], "hfr:", ["help"]) +except getopt.GetoptError,e: + print e + sys.exit(1) + +for o,a in opts: + if o=="-f": + filter_file = True + + if o=="-r": + source_root = os.path.abspath(a) + +if filter_file: + # Filter the specified file and print the result to stdout + filename = string.join(args) + filterFile(os.path.abspath(filename)) +else: + + if len(args)!=2: + sys.stderr.write("%s options input output\n"%(os.path.basename(sys.argv[0]))) + sys.exit(1) + + # Filter an entire Python source tree + print '"%s" -> "%s"\n'%(args[0],args[1]) + c=convert(args[0],args[1]) + print "%d files"%(c) + diff --git a/docs/src/interface.tex b/docs/src/interface.tex new file mode 100644 index 0000000..5fa0c5f --- /dev/null +++ b/docs/src/interface.tex @@ -0,0 +1,2257 @@ +\documentclass[11pt,twoside,final,openright,a4paper]{report} +\usepackage{graphicx,html,setspace,times} +\usepackage{parskip} +\setstretch{1.15} + +% LIBRARY FUNCTIONS + +\newcommand{\hypercall}[1]{\vspace{2mm}{\sf #1}} + +\begin{document} + +% TITLE PAGE +\pagestyle{empty} +\begin{center} +\vspace*{\fill} +\includegraphics{figs/xenlogo.eps} +\vfill +\vfill +\vfill +\begin{tabular}{l} +{\Huge \bf Interface manual} \\[4mm] +{\huge Xen v3.0 for x86} \\[80mm] + +{\Large Xen is Copyright (c) 2002-2005, The Xen Team} \\[3mm] +{\Large University of Cambridge, UK} \\[20mm] +\end{tabular} +\end{center} + +{\bf DISCLAIMER: This documentation is always under active development +and as such there may be mistakes and omissions --- watch out for +these and please report any you find to the developer's mailing list. +The latest version is always available on-line. Contributions of +material, suggestions and corrections are welcome. } + +\vfill +\cleardoublepage + +% TABLE OF CONTENTS +\pagestyle{plain} +\pagenumbering{roman} +{ \parskip 0pt plus 1pt + \tableofcontents } +\cleardoublepage + +% PREPARE FOR MAIN TEXT +\pagenumbering{arabic} +\raggedbottom +\widowpenalty=10000 +\clubpenalty=10000 +\parindent=0pt +\parskip=5pt +\renewcommand{\topfraction}{.8} +\renewcommand{\bottomfraction}{.8} +\renewcommand{\textfraction}{.2} +\renewcommand{\floatpagefraction}{.8} +\setstretch{1.1} + +\chapter{Introduction} + +Xen allows the hardware resources of a machine to be virtualized and +dynamically partitioned, allowing multiple different {\em guest} +operating system images to be run simultaneously. Virtualizing the +machine in this manner provides considerable flexibility, for example +allowing different users to choose their preferred operating system +(e.g., Linux, NetBSD, or a custom operating system). Furthermore, Xen +provides secure partitioning between virtual machines (known as +{\em domains} in Xen terminology), and enables better resource +accounting and QoS isolation than can be achieved with a conventional +operating system. + +Xen essentially takes a `whole machine' virtualization approach as +pioneered by IBM VM/370. However, unlike VM/370 or more recent +efforts such as VMware and Virtual PC, Xen does not attempt to +completely virtualize the underlying hardware. Instead parts of the +hosted guest operating systems are modified to work with the VMM; the +operating system is effectively ported to a new target architecture, +typically requiring changes in just the machine-dependent code. The +user-level API is unchanged, and so existing binaries and operating +system distributions work without modification. + +In addition to exporting virtualized instances of CPU, memory, network +and block devices, Xen exposes a control interface to manage how these +resources are shared between the running domains. Access to the +control interface is restricted: it may only be used by one +specially-privileged VM, known as {\em domain 0}. This domain is a +required part of any Xen-based server and runs the application software +that manages the control-plane aspects of the platform. Running the +control software in {\it domain 0}, distinct from the hypervisor +itself, allows the Xen framework to separate the notions of +mechanism and policy within the system. + + +\chapter{Virtual Architecture} + +In a Xen/x86 system, only the hypervisor runs with full processor +privileges ({\it ring 0} in the x86 four-ring model). It has full +access to the physical memory available in the system and is +responsible for allocating portions of it to running domains. + +On a 32-bit x86 system, guest operating systems may use {\it rings 1}, +{\it 2} and {\it 3} as they see fit. Segmentation is used to prevent +the guest OS from accessing the portion of the address space that is +reserved for Xen. We expect most guest operating systems will use +ring 1 for their own operation and place applications in ring 3. + +On 64-bit systems it is not possible to protect the hypervisor from +untrusted guest code running in rings 1 and 2. Guests are therefore +restricted to run in ring 3 only. The guest kernel is protected from its +applications by context switching between the kernel and currently +running application. + +In this chapter we consider the basic virtual architecture provided by +Xen: CPU state, exception and interrupt handling, and time. +Other aspects such as memory and device access are discussed in later +chapters. + + +\section{CPU state} + +All privileged state must be handled by Xen. The guest OS has no +direct access to CR3 and is not permitted to update privileged bits in +EFLAGS. Guest OSes use \emph{hypercalls} to invoke operations in Xen; +these are analogous to system calls but occur from ring 1 to ring 0. + +A list of all hypercalls is given in Appendix~\ref{a:hypercalls}. + + +\section{Exceptions} + +A virtual IDT is provided --- a domain can submit a table of trap +handlers to Xen via the {\bf set\_trap\_table} hypercall. The +exception stack frame presented to a virtual trap handler is identical +to its native equivalent. + + +\section{Interrupts and events} + +Interrupts are virtualized by mapping them to \emph{event channels}, +which are delivered asynchronously to the target domain using a callback +supplied via the {\bf set\_callbacks} hypercall. A guest OS can map +these events onto its standard interrupt dispatch mechanisms. Xen is +responsible for determining the target domain that will handle each +physical interrupt source. For more details on the binding of event +sources to event channels, see Chapter~\ref{c:devices}. + + +\section{Time} + +Guest operating systems need to be aware of the passage of both real +(or wallclock) time and their own `virtual time' (the time for which +they have been executing). Furthermore, Xen has a notion of time which +is used for scheduling. The following notions of time are provided: + +\begin{description} +\item[Cycle counter time.] + + This provides a fine-grained time reference. The cycle counter time + is used to accurately extrapolate the other time references. On SMP + machines it is currently assumed that the cycle counter time is + synchronized between CPUs. The current x86-based implementation + achieves this within inter-CPU communication latencies. + +\item[System time.] + + This is a 64-bit counter which holds the number of nanoseconds that + have elapsed since system boot. + +\item[Wall clock time.] + + This is the time of day in a Unix-style {\bf struct timeval} + (seconds and microseconds since 1 January 1970, adjusted by leap + seconds). An NTP client hosted by {\it domain 0} can keep this + value accurate. + +\item[Domain virtual time.] + + This progresses at the same pace as system time, but only while a + domain is executing --- it stops while a domain is de-scheduled. + Therefore the share of the CPU that a domain receives is indicated + by the rate at which its virtual time increases. + +\end{description} + + +Xen exports timestamps for system time and wall-clock time to guest +operating systems through a shared page of memory. Xen also provides +the cycle counter time at the instant the timestamps were calculated, +and the CPU frequency in Hertz. This allows the guest to extrapolate +system and wall-clock times accurately based on the current cycle +counter time. + +Since all time stamps need to be updated and read \emph{atomically} +a version number is also stored in the shared info page, which is +incremented before and after updating the timestamps. Thus a guest can +be sure that it read a consistent state by checking the two version +numbers are equal and even. + +Xen includes a periodic ticker which sends a timer event to the +currently executing domain every 10ms. The Xen scheduler also sends a +timer event whenever a domain is scheduled; this allows the guest OS +to adjust for the time that has passed while it has been inactive. In +addition, Xen allows each domain to request that they receive a timer +event sent at a specified system time by using the {\bf + set\_timer\_op} hypercall. Guest OSes may use this timer to +implement timeout values when they block. + + +\section{Xen CPU Scheduling} + +Xen offers a uniform API for CPU schedulers. It is possible to choose +from a number of schedulers at boot and it should be easy to add more. +The SEDF and Credit schedulers are part of the normal Xen +distribution. SEDF will be going away and its use should be +avoided once the credit scheduler has stabilized and become the default. +The Credit scheduler provides proportional fair shares of the +host's CPUs to the running domains. It does this while transparently +load balancing runnable VCPUs across the whole system. + +\paragraph*{Note: SMP host support} +Xen has always supported SMP host systems. When using the credit scheduler, +a domain's VCPUs will be dynamically moved across physical CPUs to maximise +domain and system throughput. VCPUs can also be manually restricted to be +mapped only on a subset of the host's physical CPUs, using the pinning +mechanism. + + +%% More information on the characteristics and use of these schedulers +%% is available in {\bf Sched-HOWTO.txt}. + + +\section{Privileged operations} + +Xen exports an extended interface to privileged domains (viz.\ {\it + Domain 0}). This allows such domains to build and boot other domains +on the server, and provides control interfaces for managing +scheduling, memory, networking, and block devices. + +\chapter{Memory} +\label{c:memory} + +Xen is responsible for managing the allocation of physical memory to +domains, and for ensuring safe use of the paging and segmentation +hardware. + + +\section{Memory Allocation} + +As well as allocating a portion of physical memory for its own private +use, Xen also reserves s small fixed portion of every virtual address +space. This is located in the top 64MB on 32-bit systems, the top +168MB on PAE systems, and a larger portion in the middle of the +address space on 64-bit systems. Unreserved physical memory is +available for allocation to domains at a page granularity. Xen tracks +the ownership and use of each page, which allows it to enforce secure +partitioning between domains. + +Each domain has a maximum and current physical memory allocation. A +guest OS may run a `balloon driver' to dynamically adjust its current +memory allocation up to its limit. + + +\section{Pseudo-Physical Memory} + +Since physical memory is allocated and freed on a page granularity, +there is no guarantee that a domain will receive a contiguous stretch +of physical memory. However most operating systems do not have good +support for operating in a fragmented physical address space. To aid +porting such operating systems to run on top of Xen, we make a +distinction between \emph{machine memory} and \emph{pseudo-physical + memory}. + +Put simply, machine memory refers to the entire amount of memory +installed in the machine, including that reserved by Xen, in use by +various domains, or currently unallocated. We consider machine memory +to comprise a set of 4kB \emph{machine page frames} numbered +consecutively starting from 0. Machine frame numbers mean the same +within Xen or any domain. + +Pseudo-physical memory, on the other hand, is a per-domain +abstraction. It allows a guest operating system to consider its memory +allocation to consist of a contiguous range of physical page frames +starting at physical frame 0, despite the fact that the underlying +machine page frames may be sparsely allocated and in any order. + +To achieve this, Xen maintains a globally readable {\it + machine-to-physical} table which records the mapping from machine +page frames to pseudo-physical ones. In addition, each domain is +supplied with a {\it physical-to-machine} table which performs the +inverse mapping. Clearly the machine-to-physical table has size +proportional to the amount of RAM installed in the machine, while each +physical-to-machine table has size proportional to the memory +allocation of the given domain. + +Architecture dependent code in guest operating systems can then use +the two tables to provide the abstraction of pseudo-physical memory. +In general, only certain specialized parts of the operating system +(such as page table management) needs to understand the difference +between machine and pseudo-physical addresses. + + +\section{Page Table Updates} + +In the default mode of operation, Xen enforces read-only access to +page tables and requires guest operating systems to explicitly request +any modifications. Xen validates all such requests and only applies +updates that it deems safe. This is necessary to prevent domains from +adding arbitrary mappings to their page tables. + +To aid validation, Xen associates a type and reference count with each +memory page. A page has one of the following mutually-exclusive types +at any point in time: page directory ({\sf PD}), page table ({\sf + PT}), local descriptor table ({\sf LDT}), global descriptor table +({\sf GDT}), or writable ({\sf RW}). Note that a guest OS may always +create readable mappings of its own memory regardless of its current +type. + +%%% XXX: possibly explain more about ref count 'lifecyle' here? +This mechanism is used to maintain the invariants required for safety; +for example, a domain cannot have a writable mapping to any part of a +page table as this would require the page concerned to simultaneously +be of types {\sf PT} and {\sf RW}. + +\hypercall{mmu\_update(mmu\_update\_t *req, int count, int *success\_count, domid\_t domid)} + +This hypercall is used to make updates to either the domain's +pagetables or to the machine to physical mapping table. It supports +submitting a queue of updates, allowing batching for maximal +performance. Explicitly queuing updates using this interface will +cause any outstanding writable pagetable state to be flushed from the +system. + +\section{Writable Page Tables} + +Xen also provides an alternative mode of operation in which guests +have the illusion that their page tables are directly writable. Of +course this is not really the case, since Xen must still validate +modifications to ensure secure partitioning. To this end, Xen traps +any write attempt to a memory page of type {\sf PT} (i.e., that is +currently part of a page table). If such an access occurs, Xen +temporarily allows write access to that page while at the same time +\emph{disconnecting} it from the page table that is currently in use. +This allows the guest to safely make updates to the page because the +newly-updated entries cannot be used by the MMU until Xen revalidates +and reconnects the page. Reconnection occurs automatically in a +number of situations: for example, when the guest modifies a different +page-table page, when the domain is preempted, or whenever the guest +uses Xen's explicit page-table update interfaces. + +Writable pagetable functionality is enabled when the guest requests +it, using a {\bf vm\_assist} hypercall. Writable pagetables do {\em +not} provide full virtualisation of the MMU, so the memory management +code of the guest still needs to be aware that it is running on Xen. +Since the guest's page tables are used directly, it must translate +pseudo-physical addresses to real machine addresses when building page +table entries. The guest may not attempt to map its own pagetables +writably, since this would violate the memory type invariants; page +tables will automatically be made writable by the hypervisor, as +necessary. + +\section{Shadow Page Tables} + +Finally, Xen also supports a form of \emph{shadow page tables} in +which the guest OS uses a independent copy of page tables which are +unknown to the hardware (i.e.\ which are never pointed to by {\tt + cr3}). Instead Xen propagates changes made to the guest's tables to +the real ones, and vice versa. This is useful for logging page writes +(e.g.\ for live migration or checkpoint). A full version of the shadow +page tables also allows guest OS porting with less effort. + + +\section{Segment Descriptor Tables} + +At start of day a guest is supplied with a default GDT, which does not reside +within its own memory allocation. If the guest wishes to use other +than the default `flat' ring-1 and ring-3 segments that this GDT +provides, it must register a custom GDT and/or LDT with Xen, allocated +from its own memory. + +The following hypercall is used to specify a new GDT: + +\begin{quote} + int {\bf set\_gdt}(unsigned long *{\em frame\_list}, int {\em + entries}) + + \emph{frame\_list}: An array of up to 14 machine page frames within + which the GDT resides. Any frame registered as a GDT frame may only + be mapped read-only within the guest's address space (e.g., no + writable mappings, no use as a page-table page, and so on). Only 14 + pages may be specified because pages 15 and 16 are reserved for + the hypervisor's GDT entries. + + \emph{entries}: The number of descriptor-entry slots in the GDT. +\end{quote} + +The LDT is updated via the generic MMU update mechanism (i.e., via the +{\bf mmu\_update} hypercall. + +\section{Start of Day} + +The start-of-day environment for guest operating systems is rather +different to that provided by the underlying hardware. In particular, +the processor is already executing in protected mode with paging +enabled. + +{\it Domain 0} is created and booted by Xen itself. For all subsequent +domains, the analogue of the boot-loader is the {\it domain builder}, +user-space software running in {\it domain 0}. The domain builder is +responsible for building the initial page tables for a domain and +loading its kernel image at the appropriate virtual address. + +\section{VM assists} + +Xen provides a number of ``assists'' for guest memory management. +These are available on an ``opt-in'' basis to provide commonly-used +extra functionality to a guest. + +\hypercall{vm\_assist(unsigned int cmd, unsigned int type)} + +The {\bf cmd} parameter describes the action to be taken, whilst the +{\bf type} parameter describes the kind of assist that is being +referred to. Available commands are as follows: + +\begin{description} +\item[VMASST\_CMD\_enable] Enable a particular assist type +\item[VMASST\_CMD\_disable] Disable a particular assist type +\end{description} + +And the available types are: + +\begin{description} +\item[VMASST\_TYPE\_4gb\_segments] Provide emulated support for + instructions that rely on 4GB segments (such as the techniques used + by some TLS solutions). +\item[VMASST\_TYPE\_4gb\_segments\_notify] Provide a callback to the + guest if the above segment fixups are used: allows the guest to + display a warning message during boot. +\item[VMASST\_TYPE\_writable\_pagetables] Enable writable pagetable + mode - described above. +\end{description} + + +\chapter{Xen Info Pages} + +The {\bf Shared info page} is used to share various CPU-related state +between the guest OS and the hypervisor. This information includes VCPU +status, time information and event channel (virtual interrupt) state. +The {\bf Start info page} is used to pass build-time information to +the guest when it boots and when it is resumed from a suspended state. +This chapter documents the fields included in the {\bf +shared\_info\_t} and {\bf start\_info\_t} structures for use by the +guest OS. + +\section{Shared info page} + +The {\bf shared\_info\_t} is accessed at run time by both Xen and the +guest OS. It is used to pass information relating to the +virtual CPU and virtual machine state between the OS and the +hypervisor. + +The structure is declared in {\bf xen/include/public/xen.h}: + +\scriptsize +\begin{verbatim} +typedef struct shared_info { + vcpu_info_t vcpu_info[MAX_VIRT_CPUS]; + + /* + * A domain can create "event channels" on which it can send and receive + * asynchronous event notifications. There are three classes of event that + * are delivered by this mechanism: + * 1. Bi-directional inter- and intra-domain connections. Domains must + * arrange out-of-band to set up a connection (usually by allocating + * an unbound 'listener' port and advertising that via a storage service + * such as xenstore). + * 2. Physical interrupts. A domain with suitable hardware-access + * privileges can bind an event-channel port to a physical interrupt + * source. + * 3. Virtual interrupts ('events'). A domain can bind an event-channel + * port to a virtual interrupt source, such as the virtual-timer + * device or the emergency console. + * + * Event channels are addressed by a "port index". Each channel is + * associated with two bits of information: + * 1. PENDING -- notifies the domain that there is a pending notification + * to be processed. This bit is cleared by the guest. + * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING + * will cause an asynchronous upcall to be scheduled. This bit is only + * updated by the guest. It is read-only within Xen. If a channel + * becomes pending while the channel is masked then the 'edge' is lost + * (i.e., when the channel is unmasked, the guest must manually handle + * pending notifications as no upcall will be scheduled by Xen). + * + * To expedite scanning of pending notifications, any 0->1 pending + * transition on an unmasked channel causes a corresponding bit in a + * per-vcpu selector word to be set. Each bit in the selector covers a + * 'C long' in the PENDING bitfield array. + */ + unsigned long evtchn_pending[sizeof(unsigned long) * 8]; + unsigned long evtchn_mask[sizeof(unsigned long) * 8]; + + /* + * Wallclock time: updated only by control software. Guests should base + * their gettimeofday() syscall on this wallclock-base value. + */ + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; /* Secs 00:00:00 UTC, Jan 1, 1970. */ + uint32_t wc_nsec; /* Nsecs 00:00:00 UTC, Jan 1, 1970. */ + + arch_shared_info_t arch; + +} shared_info_t; +\end{verbatim} +\normalsize + +\begin{description} +\item[vcpu\_info] An array of {\bf vcpu\_info\_t} structures, each of + which holds either runtime information about a virtual CPU, or is + ``empty'' if the corresponding VCPU does not exist. +\item[evtchn\_pending] Guest-global array, with one bit per event + channel. Bits are set if an event is currently pending on that + channel. +\item[evtchn\_mask] Guest-global array for masking notifications on + event channels. +\item[wc\_version] Version counter for current wallclock time. +\item[wc\_sec] Whole seconds component of current wallclock time. +\item[wc\_nsec] Nanoseconds component of current wallclock time. +\item[arch] Host architecture-dependent portion of the shared info + structure. +\end{description} + +\subsection{vcpu\_info\_t} + +\scriptsize +\begin{verbatim} +typedef struct vcpu_info { + /* + * 'evtchn_upcall_pending' is written non-zero by Xen to indicate + * a pending notification for a particular VCPU. It is then cleared + * by the guest OS /before/ checking for pending work, thus avoiding + * a set-and-check race. Note that the mask is only accessed by Xen + * on the CPU that is currently hosting the VCPU. This means that the + * pending and mask flags can be updated by the guest without special + * synchronisation (i.e., no need for the x86 LOCK prefix). + * This may seem suboptimal because if the pending flag is set by + * a different CPU then an IPI may be scheduled even when the mask + * is set. However, note: + * 1. The task of 'interrupt holdoff' is covered by the per-event- + * channel mask bits. A 'noisy' event that is continually being + * triggered can be masked at source at this very precise + * granularity. + * 2. The main purpose of the per-VCPU mask is therefore to restrict + * reentrant execution: whether for concurrency control, or to + * prevent unbounded stack usage. Whatever the purpose, we expect + * that the mask will be asserted only for short periods at a time, + * and so the likelihood of a 'spurious' IPI is suitably small. + * The mask is read before making an event upcall to the guest: a + * non-zero mask therefore guarantees that the VCPU will not receive + * an upcall activation. The mask is cleared when the VCPU requests + * to block: this avoids wakeup-waiting races. + */ + uint8_t evtchn_upcall_pending; + uint8_t evtchn_upcall_mask; + unsigned long evtchn_pending_sel; + arch_vcpu_info_t arch; + vcpu_time_info_t time; +} vcpu_info_t; /* 64 bytes (x86) */ +\end{verbatim} +\normalsize + +\begin{description} +\item[evtchn\_upcall\_pending] This is set non-zero by Xen to indicate + that there are pending events to be received. +\item[evtchn\_upcall\_mask] This is set non-zero to disable all + interrupts for this CPU for short periods of time. If individual + event channels need to be masked, the {\bf evtchn\_mask} in the {\bf + shared\_info\_t} is used instead. +\item[evtchn\_pending\_sel] When an event is delivered to this VCPU, a + bit is set in this selector to indicate which word of the {\bf + evtchn\_pending} array in the {\bf shared\_info\_t} contains the + event in question. +\item[arch] Architecture-specific VCPU info. On x86 this contains the + virtualized CR2 register (page fault linear address) for this VCPU. +\item[time] Time values for this VCPU. +\end{description} + +\subsection{vcpu\_time\_info} + +\scriptsize +\begin{verbatim} +typedef struct vcpu_time_info { + /* + * Updates to the following values are preceded and followed by an + * increment of 'version'. The guest can therefore detect updates by + * looking for changes to 'version'. If the least-significant bit of + * the version number is set then an update is in progress and the guest + * must wait to read a consistent set of values. + * The correct way to interact with the version number is similar to + * Linux's seqlock: see the implementations of read_seqbegin/read_seqretry. + */ + uint32_t version; + uint32_t pad0; + uint64_t tsc_timestamp; /* TSC at last update of time vals. */ + uint64_t system_time; /* Time, in nanosecs, since boot. */ + /* + * Current system time: + * system_time + ((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul + * CPU frequency (Hz): + * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift + */ + uint32_t tsc_to_system_mul; + int8_t tsc_shift; + int8_t pad1[3]; +} vcpu_time_info_t; /* 32 bytes */ +\end{verbatim} +\normalsize + +\begin{description} +\item[version] Used to ensure the guest gets consistent time updates. +\item[tsc\_timestamp] Cycle counter timestamp of last time value; + could be used to expolate in between updates, for instance. +\item[system\_time] Time since boot (nanoseconds). +\item[tsc\_to\_system\_mul] Cycle counter to nanoseconds multiplier +(used in extrapolating current time). +\item[tsc\_shift] Cycle counter to nanoseconds shift (used in +extrapolating current time). +\end{description} + +\subsection{arch\_shared\_info\_t} + +On x86, the {\bf arch\_shared\_info\_t} is defined as follows (from +xen/public/arch-x86\_32.h): + +\scriptsize +\begin{verbatim} +typedef struct arch_shared_info { + unsigned long max_pfn; /* max pfn that appears in table */ + /* Frame containing list of mfns containing list of mfns containing p2m. */ + unsigned long pfn_to_mfn_frame_list_list; +} arch_shared_info_t; +\end{verbatim} +\normalsize + +\begin{description} +\item[max\_pfn] The maximum PFN listed in the physical-to-machine + mapping table (P2M table). +\item[pfn\_to\_mfn\_frame\_list\_list] Machine address of the frame + that contains the machine addresses of the P2M table frames. +\end{description} + +\section{Start info page} + +The start info structure is declared as the following (in {\bf +xen/include/public/xen.h}): + +\scriptsize +\begin{verbatim} +#define MAX_GUEST_CMDLINE 1024 +typedef struct start_info { + /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME. */ + char magic[32]; /* "Xen-.". */ + unsigned long nr_pages; /* Total pages allocated to this domain. */ + unsigned long shared_info; /* MACHINE address of shared info struct. */ + uint32_t flags; /* SIF_xxx flags. */ + unsigned long store_mfn; /* MACHINE page number of shared page. */ + uint32_t store_evtchn; /* Event channel for store communication. */ + unsigned long console_mfn; /* MACHINE address of console page. */ + uint32_t console_evtchn; /* Event channel for console messages. */ + /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME). */ + unsigned long pt_base; /* VIRTUAL address of page directory. */ + unsigned long nr_pt_frames; /* Number of bootstrap p.t. frames. */ + unsigned long mfn_list; /* VIRTUAL address of page-frame list. */ + unsigned long mod_start; /* VIRTUAL address of pre-loaded module. */ + unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ + int8_t cmd_line[MAX_GUEST_CMDLINE]; +} start_info_t; +\end{verbatim} +\normalsize + +The fields are in two groups: the first group are always filled in +when a domain is booted or resumed, the second set are only used at +boot time. + +The always-available group is as follows: + +\begin{description} +\item[magic] A text string identifying the Xen version to the guest. +\item[nr\_pages] The number of real machine pages available to the + guest. +\item[shared\_info] Machine address of the shared info structure, + allowing the guest to map it during initialisation. +\item[flags] Flags for describing optional extra settings to the + guest. +\item[store\_mfn] Machine address of the Xenstore communications page. +\item[store\_evtchn] Event channel to communicate with the store. +\item[console\_mfn] Machine address of the console data page. +\item[console\_evtchn] Event channel to notify the console backend. +\end{description} + +The boot-only group may only be safely referred to during system boot: + +\begin{description} +\item[pt\_base] Virtual address of the page directory created for us + by the domain builder. +\item[nr\_pt\_frames] Number of frames used by the builders' bootstrap + pagetables. +\item[mfn\_list] Virtual address of the list of machine frames this + domain owns. +\item[mod\_start] Virtual address of any pre-loaded modules + (e.g. ramdisk) +\item[mod\_len] Size of pre-loaded module (if any). +\item[cmd\_line] Kernel command line passed by the domain builder. +\end{description} + + +% by Mark Williamson + +\chapter{Event Channels} +\label{c:eventchannels} + +Event channels are the basic primitive provided by Xen for event +notifications. An event is the Xen equivalent of a hardware +interrupt. They essentially store one bit of information, the event +of interest is signalled by transitioning this bit from 0 to 1. + +Notifications are received by a guest via an upcall from Xen, +indicating when an event arrives (setting the bit). Further +notifications are masked until the bit is cleared again (therefore, +guests must check the value of the bit after re-enabling event +delivery to ensure no missed notifications). + +Event notifications can be masked by setting a flag; this is +equivalent to disabling interrupts and can be used to ensure atomicity +of certain operations in the guest kernel. + +\section{Hypercall interface} + +\hypercall{event\_channel\_op(evtchn\_op\_t *op)} + +The event channel operation hypercall is used for all operations on +event channels / ports. Operations are distinguished by the value of +the {\bf cmd} field of the {\bf op} structure. The possible commands +are described below: + +\begin{description} + +\item[EVTCHNOP\_alloc\_unbound] + Allocate a new event channel port, ready to be connected to by a + remote domain. + \begin{itemize} + \item Specified domain must exist. + \item A free port must exist in that domain. + \end{itemize} + Unprivileged domains may only allocate their own ports, privileged + domains may also allocate ports in other domains. +\item[EVTCHNOP\_bind\_interdomain] + Bind an event channel for interdomain communications. + \begin{itemize} + \item Caller domain must have a free port to bind. + \item Remote domain must exist. + \item Remote port must be allocated and currently unbound. + \item Remote port must be expecting the caller domain as the ``remote''. + \end{itemize} +\item[EVTCHNOP\_bind\_virq] + Allocate a port and bind a VIRQ to it. + \begin{itemize} + \item Caller domain must have a free port to bind. + \item VIRQ must be valid. + \item VCPU must exist. + \item VIRQ must not currently be bound to an event channel. + \end{itemize} +\item[EVTCHNOP\_bind\_ipi] + Allocate and bind a port for notifying other virtual CPUs. + \begin{itemize} + \item Caller domain must have a free port to bind. + \item VCPU must exist. + \end{itemize} +\item[EVTCHNOP\_bind\_pirq] + Allocate and bind a port to a real IRQ. + \begin{itemize} + \item Caller domain must have a free port to bind. + \item PIRQ must be within the valid range. + \item Another binding for this PIRQ must not exist for this domain. + \item Caller must have an available port. + \end{itemize} +\item[EVTCHNOP\_close] + Close an event channel (no more events will be received). + \begin{itemize} + \item Port must be valid (currently allocated). + \end{itemize} +\item[EVTCHNOP\_send] Send a notification on an event channel attached + to a port. + \begin{itemize} + \item Port must be valid. + \item Only valid for Interdomain, IPI or Allocated Unbound ports. + \end{itemize} +\item[EVTCHNOP\_status] Query the status of a port; what kind of port, + whether it is bound, what remote domain is expected, what PIRQ or + VIRQ it is bound to, what VCPU will be notified, etc. + Unprivileged domains may only query the state of their own ports. + Privileged domains may query any port. +\item[EVTCHNOP\_bind\_vcpu] Bind event channel to a particular VCPU - + receive notification upcalls only on that VCPU. + \begin{itemize} + \item VCPU must exist. + \item Port must be valid. + \item Event channel must be either: allocated but unbound, bound to + an interdomain event channel, bound to a PIRQ. + \end{itemize} + +\end{description} + +%% +%% grant_tables.tex +%% +%% Made by Mark Williamson +%% Login +%% + +\chapter{Grant tables} +\label{c:granttables} + +Xen's grant tables provide a generic mechanism to memory sharing +between domains. This shared memory interface underpins the split +device drivers for block and network IO. + +Each domain has its own {\bf grant table}. This is a data structure +that is shared with Xen; it allows the domain to tell Xen what kind of +permissions other domains have on its pages. Entries in the grant +table are identified by {\bf grant references}. A grant reference is +an integer, which indexes into the grant table. It acts as a +capability which the grantee can use to perform operations on the +granter's memory. + +This capability-based system allows shared-memory communications +between unprivileged domains. A grant reference also encapsulates the +details of a shared page, removing the need for a domain to know the +real machine address of a page it is sharing. This makes it possible +to share memory correctly with domains running in fully virtualised +memory. + +\section{Interface} + +\subsection{Grant table manipulation} + +Creating and destroying grant references is done by direct access to +the grant table. This removes the need to involve Xen when creating +grant references, modifying access permissions, etc. The grantee +domain will invoke hypercalls to use the grant references. Four main +operations can be accomplished by directly manipulating the table: + +\begin{description} +\item[Grant foreign access] allocate a new entry in the grant table + and fill out the access permissions accordingly. The access + permissions will be looked up by Xen when the grantee attempts to + use the reference to map the granted frame. +\item[End foreign access] check that the grant reference is not + currently in use, then remove the mapping permissions for the frame. + This prevents further mappings from taking place but does not allow + forced revocations of existing mappings. +\item[Grant foreign transfer] allocate a new entry in the table + specifying transfer permissions for the grantee. Xen will look up + this entry when the grantee attempts to transfer a frame to the + granter. +\item[End foreign transfer] remove permissions to prevent a transfer + occurring in future. If the transfer is already committed, + modifying the grant table cannot prevent it from completing. +\end{description} + +\subsection{Hypercalls} + +Use of grant references is accomplished via a hypercall. The grant +table op hypercall takes three arguments: + +\hypercall{grant\_table\_op(unsigned int cmd, void *uop, unsigned int count)} + +{\bf cmd} indicates the grant table operation of interest. {\bf uop} +is a pointer to a structure (or an array of structures) describing the +operation to be performed. The {\bf count} field describes how many +grant table operations are being batched together. + +The core logic is situated in {\bf xen/common/grant\_table.c}. The +grant table operation hypercall can be used to perform the following +actions: + +\begin{description} +\item[GNTTABOP\_map\_grant\_ref] Given a grant reference from another + domain, map the referred page into the caller's address space. +\item[GNTTABOP\_unmap\_grant\_ref] Remove a mapping to a granted frame + from the caller's address space. This is used to voluntarily + relinquish a mapping to a granted page. +\item[GNTTABOP\_setup\_table] Setup grant table for caller domain. +\item[GNTTABOP\_dump\_table] Debugging operation. +\item[GNTTABOP\_transfer] Given a transfer reference from another + domain, transfer ownership of a page frame to that domain. +\end{description} + +%% +%% xenstore.tex +%% +%% Made by Mark Williamson +%% Login +%% + +\chapter{Xenstore} + +Xenstore is the mechanism by which control-plane activities occur. +These activities include: + +\begin{itemize} +\item Setting up shared memory regions and event channels for use with + the split device drivers. +\item Notifying the guest of control events (e.g. balloon driver + requests) +\item Reporting back status information from the guest + (e.g. performance-related statistics, etc). +\end{itemize} + +The store is arranged as a hierarchical collection of key-value pairs. +Each domain has a directory hierarchy containing data related to its +configuration. Domains are permitted to register for notifications +about changes in subtrees of the store, and to apply changes to the +store transactionally. + +\section{Guidelines} + +A few principles govern the operation of the store: + +\begin{itemize} +\item Domains should only modify the contents of their own + directories. +\item The setup protocol for a device channel should simply consist of + entering the configuration data into the store. +\item The store should allow device discovery without requiring the + relevant device drivers to be loaded: a Xen ``bus'' should be + visible to probing code in the guest. +\item The store should be usable for inter-tool communications, + allowing the tools themselves to be decomposed into a number of + smaller utilities, rather than a single monolithic entity. This + also facilitates the development of alternate user interfaces to the + same functionality. +\end{itemize} + +\section{Store layout} + +There are three main paths in XenStore: + +\begin{description} +\item[/vm] stores configuration information about domain +\item[/local/domain] stores information about the domain on the local node (domid, etc.) +\item[/tool] stores information for the various tools +\end{description} + +The {\bf /vm} path stores configuration information for a domain. +This information doesn't change and is indexed by the domain's UUID. +A {\bf /vm} entry contains the following information: + +\begin{description} +\item[uuid] uuid of the domain (somewhat redundant) +\item[on\_reboot] the action to take on a domain reboot request (destroy or restart) +\item[on\_poweroff] the action to take on a domain halt request (destroy or restart) +\item[on\_crash] the action to take on a domain crash (destroy or restart) +\item[vcpus] the number of allocated vcpus for the domain +\item[memory] the amount of memory (in megabytes) for the domain Note: appears to sometimes be empty for domain-0 +\item[vcpu\_avail] the number of active vcpus for the domain (vcpus - number of disabled vcpus) +\item[name] the name of the domain +\end{description} + + +{\bf /vm/$<$uuid$>$/image/} + +The image path is only available for Domain-Us and contains: +\begin{description} +\item[ostype] identifies the builder type (linux or vmx) +\item[kernel] path to kernel on domain-0 +\item[cmdline] command line to pass to domain-U kernel +\item[ramdisk] path to ramdisk on domain-0 +\end{description} + +{\bf /local} + +The {\tt /local} path currently only contains one directory, {\tt +/local/domain} that is indexed by domain id. It contains the running +domain information. The reason to have two storage areas is that +during migration, the uuid doesn't change but the domain id does. The +{\tt /local/domain} directory can be created and populated before +finalizing the migration enabling localhost to localhost migration. + +{\bf /local/domain/$<$domid$>$} + +This path contains: + +\begin{description} +\item[cpu\_time] xend start time (this is only around for domain-0) +\item[handle] private handle for xend +\item[name] see /vm +\item[on\_reboot] see /vm +\item[on\_poweroff] see /vm +\item[on\_crash] see /vm +\item[vm] the path to the VM directory for the domain +\item[domid] the domain id (somewhat redundant) +\item[running] indicates that the domain is currently running +\item[memory] the current memory in megabytes for the domain (empty for domain-0?) +\item[maxmem\_KiB] the maximum memory for the domain (in kilobytes) +\item[memory\_KiB] the memory allocated to the domain (in kilobytes) +\item[cpu] the current CPU the domain is pinned to (empty for domain-0?) +\item[cpu\_weight] the weight assigned to the domain +\item[vcpu\_avail] a bitmap telling the domain whether it may use a given VCPU +\item[online\_vcpus] how many vcpus are currently online +\item[vcpus] the total number of vcpus allocated to the domain +\item[console/] a directory for console information + \begin{description} + \item[ring-ref] the grant table reference of the console ring queue + \item[port] the event channel being used for the console ring queue (local port) + \item[tty] the current tty the console data is being exposed of + \item[limit] the limit (in bytes) of console data to buffer + \end{description} +\item[backend/] a directory containing all backends the domain hosts + \begin{description} + \item[vbd/] a directory containing vbd backends + \begin{description} + \item[$<$domid$>$/] a directory containing vbd's for domid + \begin{description} + \item[$<$virtual-device$>$/] a directory for a particular + virtual-device on domid + \begin{description} + \item[frontend-id] domain id of frontend + \item[frontend] the path to the frontend domain + \item[physical-device] backend device number + \item[sector-size] backend sector size + \item[info] 0 read/write, 1 read-only (is this right?) + \item[domain] name of frontend domain + \item[params] parameters for device + \item[type] the type of the device + \item[dev] the virtual device (as given by the user) + \item[node] output from block creation script + \end{description} + \end{description} + \end{description} + + \item[vif/] a directory containing vif backends + \begin{description} + \item[$<$domid$>$/] a directory containing vif's for domid + \begin{description} + \item[$<$vif number$>$/] a directory for each vif + \item[frontend-id] the domain id of the frontend + \item[frontend] the path to the frontend + \item[mac] the mac address of the vif + \item[bridge] the bridge the vif is connected to + \item[handle] the handle of the vif + \item[script] the script used to create/stop the vif + \item[domain] the name of the frontend + \end{description} + \end{description} + + \item[vtpm/] a directory containing vtpm backends + \begin{description} + \item[$<$domid$>$/] a directory containing vtpm's for domid + \begin{description} + \item[$<$vtpm number$>$/] a directory for each vtpm + \item[frontend-id] the domain id of the frontend + \item[frontend] the path to the frontend + \item[instance] the instance of the virtual TPM that is used + \item[pref{\textunderscore}instance] the instance number as given in the VM configuration file; + may be different from {\bf instance} + \item[domain] the name of the domain of the frontend + \end{description} + \end{description} + + \end{description} + + \item[device/] a directory containing the frontend devices for the + domain + \begin{description} + \item[vbd/] a directory containing vbd frontend devices for the + domain + \begin{description} + \item[$<$virtual-device$>$/] a directory containing the vbd frontend for + virtual-device + \begin{description} + \item[virtual-device] the device number of the frontend device + \item[backend-id] the domain id of the backend + \item[backend] the path of the backend in the store (/local/domain + path) + \item[ring-ref] the grant table reference for the block request + ring queue + \item[event-channel] the event channel used for the block request + ring queue + \end{description} + + \item[vif/] a directory containing vif frontend devices for the + domain + \begin{description} + \item[$<$id$>$/] a directory for vif id frontend device for the domain + \begin{description} + \item[backend-id] the backend domain id + \item[mac] the mac address of the vif + \item[handle] the internal vif handle + \item[backend] a path to the backend's store entry + \item[tx-ring-ref] the grant table reference for the transmission ring queue + \item[rx-ring-ref] the grant table reference for the receiving ring queue + \item[event-channel] the event channel used for the two ring queues + \end{description} + \end{description} + + \item[vtpm/] a directory containing the vtpm frontend device for the + domain + \begin{description} + \item[$<$id$>$] a directory for vtpm id frontend device for the domain + \begin{description} + \item[backend-id] the backend domain id + \item[backend] a path to the backend's store entry + \item[ring-ref] the grant table reference for the tx/rx ring + \item[event-channel] the event channel used for the ring + \end{description} + \end{description} + + \item[device-misc/] miscellaneous information for devices + \begin{description} + \item[vif/] miscellaneous information for vif devices + \begin{description} + \item[nextDeviceID] the next device id to use + \end{description} + \end{description} + \end{description} + \end{description} + + \item[security/] access control information for the domain + \begin{description} + \item[ssidref] security reference identifier used inside the hypervisor + \item[access\_control/] security label used by management tools + \begin{description} + \item[label] security label name + \item[policy] security policy name + \end{description} + \end{description} + + \item[store/] per-domain information for the store + \begin{description} + \item[port] the event channel used for the store ring queue + \item[ring-ref] - the grant table reference used for the store's + communication channel + \end{description} + + \item[image] - private xend information +\end{description} + + +\chapter{Devices} +\label{c:devices} + +Virtual devices under Xen are provided by a {\bf split device driver} +architecture. The illusion of the virtual device is provided by two +co-operating drivers: the {\bf frontend}, which runs an the +unprivileged domain and the {\bf backend}, which runs in a domain with +access to the real device hardware (often called a {\bf driver +domain}; in practice domain 0 usually fulfills this function). + +The frontend driver appears to the unprivileged guest as if it were a +real device, for instance a block or network device. It receives IO +requests from its kernel as usual, however since it does not have +access to the physical hardware of the system it must then issue +requests to the backend. The backend driver is responsible for +receiving these IO requests, verifying that they are safe and then +issuing them to the real device hardware. The backend driver appears +to its kernel as a normal user of in-kernel IO functionality. When +the IO completes the backend notifies the frontend that the data is +ready for use; the frontend is then able to report IO completion to +its own kernel. + +Frontend drivers are designed to be simple; most of the complexity is +in the backend, which has responsibility for translating device +addresses, verifying that requests are well-formed and do not violate +isolation guarantees, etc. + +Split drivers exchange requests and responses in shared memory, with +an event channel for asynchronous notifications of activity. When the +frontend driver comes up, it uses Xenstore to set up a shared memory +frame and an interdomain event channel for communications with the +backend. Once this connection is established, the two can communicate +directly by placing requests / responses into shared memory and then +sending notifications on the event channel. This separation of +notification from data transfer allows message batching, and results +in very efficient device access. + +This chapter focuses on some individual split device interfaces +available to Xen guests. + + +\section{Network I/O} + +Virtual network device services are provided by shared memory +communication with a backend domain. From the point of view of other +domains, the backend may be viewed as a virtual ethernet switch +element with each domain having one or more virtual network interfaces +connected to it. + +From the point of view of the backend domain itself, the network +backend driver consists of a number of ethernet devices. Each of +these has a logical direct connection to a virtual network device in +another domain. This allows the backend domain to route, bridge, +firewall, etc the traffic to / from the other domains using normal +operating system mechanisms. + +\subsection{Backend Packet Handling} + +The backend driver is responsible for a variety of actions relating to +the transmission and reception of packets from the physical device. +With regard to transmission, the backend performs these key actions: + +\begin{itemize} +\item {\bf Validation:} To ensure that domains do not attempt to + generate invalid (e.g. spoofed) traffic, the backend driver may + validate headers ensuring that source MAC and IP addresses match the + interface that they have been sent from. + + Validation functions can be configured using standard firewall rules + ({\small{\tt iptables}} in the case of Linux). + +\item {\bf Scheduling:} Since a number of domains can share a single + physical network interface, the backend must mediate access when + several domains each have packets queued for transmission. This + general scheduling function subsumes basic shaping or rate-limiting + schemes. + +\item {\bf Logging and Accounting:} The backend domain can be + configured with classifier rules that control how packets are + accounted or logged. For example, log messages might be generated + whenever a domain attempts to send a TCP packet containing a SYN. +\end{itemize} + +On receipt of incoming packets, the backend acts as a simple +demultiplexer: Packets are passed to the appropriate virtual interface +after any necessary logging and accounting have been carried out. + +\subsection{Data Transfer} + +Each virtual interface uses two ``descriptor rings'', one for +transmit, the other for receive. Each descriptor identifies a block +of contiguous machine memory allocated to the domain. + +The transmit ring carries packets to transmit from the guest to the +backend domain. The return path of the transmit ring carries messages +indicating that the contents have been physically transmitted and the +backend no longer requires the associated pages of memory. + +To receive packets, the guest places descriptors of unused pages on +the receive ring. The backend will return received packets by +exchanging these pages in the domain's memory with new pages +containing the received data, and passing back descriptors regarding +the new packets on the ring. This zero-copy approach allows the +backend to maintain a pool of free pages to receive packets into, and +then deliver them to appropriate domains after examining their +headers. + +% Real physical addresses are used throughout, with the domain +% performing translation from pseudo-physical addresses if that is +% necessary. + +If a domain does not keep its receive ring stocked with empty buffers +then packets destined to it may be dropped. This provides some +defence against receive livelock problems because an overloaded domain +will cease to receive further data. Similarly, on the transmit path, +it provides the application with feedback on the rate at which packets +are able to leave the system. + +Flow control on rings is achieved by including a pair of producer +indexes on the shared ring page. Each side will maintain a private +consumer index indicating the next outstanding message. In this +manner, the domains cooperate to divide the ring into two message +lists, one in each direction. Notification is decoupled from the +immediate placement of new messages on the ring; the event channel +will be used to generate notification when {\em either} a certain +number of outstanding messages are queued, {\em or} a specified number +of nanoseconds have elapsed since the oldest message was placed on the +ring. + +%% Not sure if my version is any better -- here is what was here +%% before: Synchronization between the backend domain and the guest is +%% achieved using counters held in shared memory that is accessible to +%% both. Each ring has associated producer and consumer indices +%% indicating the area in the ring that holds descriptors that contain +%% data. After receiving {\it n} packets or {\t nanoseconds} after +%% receiving the first packet, the hypervisor sends an event to the +%% domain. + + +\subsection{Network ring interface} + +The network device uses two shared memory rings for communication: one +for transmit, one for receive. + +Transmit requests are described by the following structure: + +\scriptsize +\begin{verbatim} +typedef struct netif_tx_request { + grant_ref_t gref; /* Reference to buffer page */ + uint16_t offset; /* Offset within buffer page */ + uint16_t flags; /* NETTXF_* */ + uint16_t id; /* Echoed in response message. */ + uint16_t size; /* Packet size in bytes. */ +} netif_tx_request_t; +\end{verbatim} +\normalsize + +\begin{description} +\item[gref] Grant reference for the network buffer +\item[offset] Offset to data +\item[flags] Transmit flags (currently only NETTXF\_csum\_blank is + supported, to indicate that the protocol checksum field is + incomplete). +\item[id] Echoed to guest by the backend in the ring-level response so + that the guest can match it to this request +\item[size] Buffer size +\end{description} + +Each transmit request is followed by a transmit response at some later +date. This is part of the shared-memory communication protocol and +allows the guest to (potentially) retire internal structures related +to the request. It does not imply a network-level response. This +structure is as follows: + +\scriptsize +\begin{verbatim} +typedef struct netif_tx_response { + uint16_t id; + int16_t status; +} netif_tx_response_t; +\end{verbatim} +\normalsize + +\begin{description} +\item[id] Echo of the ID field in the corresponding transmit request. +\item[status] Success / failure status of the transmit request. +\end{description} + +Receive requests must be queued by the frontend, accompanied by a +donation of page-frames to the backend. The backend transfers page +frames full of data back to the guest + +\scriptsize +\begin{verbatim} +typedef struct { + uint16_t id; /* Echoed in response message. */ + grant_ref_t gref; /* Reference to incoming granted frame */ +} netif_rx_request_t; +\end{verbatim} +\normalsize + +\begin{description} +\item[id] Echoed by the frontend to identify this request when + responding. +\item[gref] Transfer reference - the backend will use this reference + to transfer a frame of network data to us. +\end{description} + +Receive response descriptors are queued for each received frame. Note +that these may only be queued in reply to an existing receive request, +providing an in-built form of traffic throttling. + +\scriptsize +\begin{verbatim} +typedef struct { + uint16_t id; + uint16_t offset; /* Offset in page of start of received packet */ + uint16_t flags; /* NETRXF_* */ + int16_t status; /* -ve: BLKIF_RSP_* ; +ve: Rx'ed pkt size. */ +} netif_rx_response_t; +\end{verbatim} +\normalsize + +\begin{description} +\item[id] ID echoed from the original request, used by the guest to + match this response to the original request. +\item[offset] Offset to data within the transferred frame. +\item[flags] Transmit flags (currently only NETRXF\_csum\_valid is + supported, to indicate that the protocol checksum field has already + been validated). +\item[status] Success / error status for this operation. +\end{description} + +Note that the receive protocol includes a mechanism for guests to +receive incoming memory frames but there is no explicit transfer of +frames in the other direction. Guests are expected to return memory +to the hypervisor in order to use the network interface. They {\em +must} do this or they will exceed their maximum memory reservation and +will not be able to receive incoming frame transfers. When necessary, +the backend is able to replenish its pool of free network buffers by +claiming some of this free memory from the hypervisor. + +\section{Block I/O} + +All guest OS disk access goes through the virtual block device VBD +interface. This interface allows domains access to portions of block +storage devices visible to the the block backend device. The VBD +interface is a split driver, similar to the network interface +described above. A single shared memory ring is used between the +frontend and backend drivers for each virtual device, across which +IO requests and responses are sent. + +Any block device accessible to the backend domain, including +network-based block (iSCSI, *NBD, etc), loopback and LVM/MD devices, +can be exported as a VBD. Each VBD is mapped to a device node in the +guest, specified in the guest's startup configuration. + +\subsection{Data Transfer} + +The per-(virtual)-device ring between the guest and the block backend +supports two messages: + +\begin{description} +\item [{\small {\tt READ}}:] Read data from the specified block + device. The front end identifies the device and location to read + from and attaches pages for the data to be copied to (typically via + DMA from the device). The backend acknowledges completed read + requests as they finish. + +\item [{\small {\tt WRITE}}:] Write data to the specified block + device. This functions essentially as {\small {\tt READ}}, except + that the data moves to the device instead of from it. +\end{description} + +%% Rather than copying data, the backend simply maps the domain's +%% buffers in order to enable direct DMA to them. The act of mapping +%% the buffers also increases the reference counts of the underlying +%% pages, so that the unprivileged domain cannot try to return them to +%% the hypervisor, install them as page tables, or any other unsafe +%% behaviour. +%% +%% % block API here + +\subsection{Block ring interface} + +The block interface is defined by the structures passed over the +shared memory interface. These structures are either requests (from +the frontend to the backend) or responses (from the backend to the +frontend). + +The request structure is defined as follows: + +\scriptsize +\begin{verbatim} +typedef struct blkif_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment { + grant_ref_t gref; /* reference to I/O buffer frame */ + /* @first_sect: first sector in frame to transfer (inclusive). */ + /* @last_sect: last sector in frame to transfer (inclusive). */ + uint8_t first_sect, last_sect; + } seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +} blkif_request_t; +\end{verbatim} +\normalsize + +The fields are as follows: + +\begin{description} +\item[operation] operation ID: one of the operations described above +\item[nr\_segments] number of segments for scatter / gather IO + described by this request +\item[handle] identifier for a particular virtual device on this + interface +\item[id] this value is echoed in the response message for this IO; + the guest may use it to identify the original request +\item[sector\_number] start sector on the virtual device for this + request +\item[frame\_and\_sects] This array contains structures encoding + scatter-gather IO to be performed: + \begin{description} + \item[gref] The grant reference for the foreign I/O buffer page. + \item[first\_sect] First sector to access within the buffer page (0 to 7). + \item[last\_sect] Last sector to access within the buffer page (0 to 7). + \end{description} + Data will be transferred into frames at an offset determined by the + value of {\tt first\_sect}. +\end{description} + +\section{Virtual TPM} + +Virtual TPM (VTPM) support provides TPM functionality to each virtual +machine that requests this functionality in its configuration file. +The interface enables domains to access their own private TPM like it +was a hardware TPM built into the machine. + +The virtual TPM interface is implemented as a split driver, +similar to the network and block interfaces described above. +The user domain hosting the frontend exports a character device /dev/tpm0 +to user-level applications for communicating with the virtual TPM. +This is the same device interface that is also offered if a hardware TPM +is available in the system. The backend provides a single interface +/dev/vtpm where the virtual TPM is waiting for commands from all domains +that have located their backend in a given domain. + +\subsection{Data Transfer} + +A single shared memory ring is used between the frontend and backend +drivers. TPM requests and responses are sent in pages where a pointer +to those pages and other information is placed into the ring such that +the backend can map the pages into its memory space using the grant +table mechanism. + +The backend driver has been implemented to only accept well-formed +TPM requests. To meet this requirement, the length indicator in the +TPM request must correctly indicate the length of the request. +Otherwise an error message is automatically sent back by the device driver. + +The virtual TPM implementation listens for TPM request on /dev/vtpm. Since +it must be able to apply the TPM request packet to the virtual TPM instance +associated with the virtual machine, a 4-byte virtual TPM instance +identifier is pretended to each packet by the backend driver (in network +byte order) for internal routing of the request. + +\subsection{Virtual TPM ring interface} + +The TPM protocol is a strict request/response protocol and therefore +only one ring is used to send requests from the frontend to the backend +and responses on the reverse path. + +The request/response structure is defined as follows: + +\scriptsize +\begin{verbatim} +typedef struct { + unsigned long addr; /* Machine address of packet. */ + grant_ref_t ref; /* grant table access reference. */ + uint16_t unused; /* unused */ + uint16_t size; /* Packet size in bytes. */ +} tpmif_tx_request_t; +\end{verbatim} +\normalsize + +The fields are as follows: + +\begin{description} +\item[addr] The machine address of the page associated with the TPM + request/response; a request/response may span multiple + pages +\item[ref] The grant table reference associated with the address. +\item[size] The size of the remaining packet; up to + PAGE{\textunderscore}SIZE bytes can be found in the + page referenced by 'addr' +\end{description} + +The frontend initially allocates several pages whose addresses +are stored in the ring. Only these pages are used for exchange of +requests and responses. + + +\chapter{Further Information} + +If you have questions that are not answered by this manual, the +sources of information listed below may be of interest to you. Note +that bug reports, suggestions and contributions related to the +software (or the documentation) should be sent to the Xen developers' +mailing list (address below). + + +\section{Other documentation} + +If you are mainly interested in using (rather than developing for) +Xen, the \emph{Xen Users' Manual} is distributed in the {\tt docs/} +directory of the Xen source distribution. + +% Various HOWTOs are also available in {\tt docs/HOWTOS}. + + +\section{Online references} + +The official Xen web site can be found at: +\begin{quote} {\tt http://www.xensource.com} +\end{quote} + + +This contains links to the latest versions of all online +documentation, including the latest version of the FAQ. + +Information regarding Xen is also available at the Xen Wiki at +\begin{quote} {\tt http://wiki.xensource.com/xenwiki/}\end{quote} +The Xen project uses Bugzilla as its bug tracking system. You'll find +the Xen Bugzilla at http://bugzilla.xensource.com/bugzilla/. + + +\section{Mailing lists} + +There are several mailing lists that are used to discuss Xen related +topics. The most widely relevant are listed below. An official page of +mailing lists and subscription information can be found at \begin{quote} + {\tt http://lists.xensource.com/} \end{quote} + +\begin{description} +\item[xen-devel@lists.xensource.com] Used for development + discussions and bug reports. Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-devel}} +\item[xen-users@lists.xensource.com] Used for installation and usage + discussions and requests for help. Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-users}} +\item[xen-announce@lists.xensource.com] Used for announcements only. + Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-announce}} +\item[xen-changelog@lists.xensource.com] Changelog feed + from the unstable and 2.0 trees - developer oriented. Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-changelog}} +\end{description} + +\appendix + + +\chapter{Xen Hypercalls} +\label{a:hypercalls} + +Hypercalls represent the procedural interface to Xen; this appendix +categorizes and describes the current set of hypercalls. + +\section{Invoking Hypercalls} + +Hypercalls are invoked in a manner analogous to system calls in a +conventional operating system; a software interrupt is issued which +vectors to an entry point within Xen. On x86/32 machines the +instruction required is {\tt int \$82}; the (real) IDT is setup so +that this may only be issued from within ring 1. The particular +hypercall to be invoked is contained in {\tt EAX} --- a list +mapping these values to symbolic hypercall names can be found +in {\tt xen/include/public/xen.h}. + +On some occasions a set of hypercalls will be required to carry +out a higher-level function; a good example is when a guest +operating wishes to context switch to a new process which +requires updating various privileged CPU state. As an optimization +for these cases, there is a generic mechanism to issue a set of +hypercalls as a batch: + +\begin{quote} +\hypercall{multicall(void *call\_list, int nr\_calls)} + +Execute a series of hypervisor calls; {\tt nr\_calls} is the length of +the array of {\tt multicall\_entry\_t} structures pointed to be {\tt +call\_list}. Each entry contains the hypercall operation code followed +by up to 7 word-sized arguments. +\end{quote} + +Note that multicalls are provided purely as an optimization; there is +no requirement to use them when first porting a guest operating +system. + + +\section{Virtual CPU Setup} + +At start of day, a guest operating system needs to setup the virtual +CPU it is executing on. This includes installing vectors for the +virtual IDT so that the guest OS can handle interrupts, page faults, +etc. However the very first thing a guest OS must setup is a pair +of hypervisor callbacks: these are the entry points which Xen will +use when it wishes to notify the guest OS of an occurrence. + +\begin{quote} +\hypercall{set\_callbacks(unsigned long event\_selector, unsigned long + event\_address, unsigned long failsafe\_selector, unsigned long + failsafe\_address) } + +Register the normal (``event'') and failsafe callbacks for +event processing. In each case the code segment selector and +address within that segment are provided. The selectors must +have RPL 1; in XenLinux we simply use the kernel's CS for both +{\bf event\_selector} and {\bf failsafe\_selector}. + +The value {\bf event\_address} specifies the address of the guest OSes +event handling and dispatch routine; the {\bf failsafe\_address} +specifies a separate entry point which is used only if a fault occurs +when Xen attempts to use the normal callback. + +\end{quote} + +On x86/64 systems the hypercall takes slightly different +arguments. This is because callback CS does not need to be specified +(since teh callbacks are entered via SYSRET), and also because an +entry address needs to be specified for SYSCALLs from guest user +space: + +\begin{quote} +\hypercall{set\_callbacks(unsigned long event\_address, unsigned long + failsafe\_address, unsigned long syscall\_address)} +\end{quote} + + +After installing the hypervisor callbacks, the guest OS can +install a `virtual IDT' by using the following hypercall: + +\begin{quote} +\hypercall{set\_trap\_table(trap\_info\_t *table)} + +Install one or more entries into the per-domain +trap handler table (essentially a software version of the IDT). +Each entry in the array pointed to by {\bf table} includes the +exception vector number with the corresponding segment selector +and entry point. Most guest OSes can use the same handlers on +Xen as when running on the real hardware. + + +\end{quote} + +A further hypercall is provided for the management of virtual CPUs: + +\begin{quote} +\hypercall{vcpu\_op(int cmd, int vcpuid, void *extra\_args)} + +This hypercall can be used to bootstrap VCPUs, to bring them up and +down and to test their current status. + +\end{quote} + +\section{Scheduling and Timer} + +Domains are preemptively scheduled by Xen according to the +parameters installed by domain 0 (see Section~\ref{s:dom0ops}). +In addition, however, a domain may choose to explicitly +control certain behavior with the following hypercall: + +\begin{quote} +\hypercall{sched\_op\_new(int cmd, void *extra\_args)} + +Request scheduling operation from hypervisor. The following +sub-commands are available: + +\begin{description} +\item[SCHEDOP\_yield] voluntarily yields the CPU, but leaves the +caller marked as runnable. No extra arguments are passed to this +command. +\item[SCHEDOP\_block] removes the calling domain from the run queue +and causes it to sleep until an event is delivered to it. No extra +arguments are passed to this command. +\item[SCHEDOP\_shutdown] is used to end the calling domain's +execution. The extra argument is a {\bf sched\_shutdown} structure +which indicates the reason why the domain suspended (e.g., for reboot, +halt, power-off). +\item[SCHEDOP\_poll] allows a VCPU to wait on a set of event channels +with an optional timeout (all of which are specified in the {\bf +sched\_poll} extra argument). The semantics are similar to the UNIX +{\bf poll} system call. The caller must have event-channel upcalls +masked when executing this command. +\end{description} +\end{quote} + +{\bf sched\_op\_new} was not available prior to Xen 3.0.2. Older versions +provide only the following hypercall: + +\begin{quote} +\hypercall{sched\_op(int cmd, unsigned long extra\_arg)} + +This hypercall supports the following subset of {\bf sched\_op\_new} commands: + +\begin{description} +\item[SCHEDOP\_yield] (extra argument is 0). +\item[SCHEDOP\_block] (extra argument is 0). +\item[SCHEDOP\_shutdown] (extra argument is numeric reason code). +\end{description} +\end{quote} + +To aid the implementation of a process scheduler within a guest OS, +Xen provides a virtual programmable timer: + +\begin{quote} +\hypercall{set\_timer\_op(uint64\_t timeout)} + +Request a timer event to be sent at the specified system time (time +in nanoseconds since system boot). + +\end{quote} + +Note that calling {\bf set\_timer\_op} prior to {\bf sched\_op} +allows block-with-timeout semantics. + + +\section{Page Table Management} + +Since guest operating systems have read-only access to their page +tables, Xen must be involved when making any changes. The following +multi-purpose hypercall can be used to modify page-table entries, +update the machine-to-physical mapping table, flush the TLB, install +a new page-table base pointer, and more. + +\begin{quote} +\hypercall{mmu\_update(mmu\_update\_t *req, int count, int *success\_count)} + +Update the page table for the domain; a set of {\bf count} updates are +submitted for processing in a batch, with {\bf success\_count} being +updated to report the number of successful updates. + +Each element of {\bf req[]} contains a pointer (address) and value; +the least significant 2-bits of the pointer are used to distinguish +the type of update requested as follows: +\begin{description} + +\item[MMU\_NORMAL\_PT\_UPDATE:] update a page directory entry or +page table entry to the associated value; Xen will check that the +update is safe, as described in Chapter~\ref{c:memory}. + +\item[MMU\_MACHPHYS\_UPDATE:] update an entry in the + machine-to-physical table. The calling domain must own the machine + page in question (or be privileged). +\end{description} + +\end{quote} + +Explicitly updating batches of page table entries is extremely +efficient, but can require a number of alterations to the guest +OS. Using the writable page table mode (Chapter~\ref{c:memory}) is +recommended for new OS ports. + +Regardless of which page table update mode is being used, however, +there are some occasions (notably handling a demand page fault) where +a guest OS will wish to modify exactly one PTE rather than a +batch, and where that PTE is mapped into the current address space. +This is catered for by the following: + +\begin{quote} +\hypercall{update\_va\_mapping(unsigned long va, uint64\_t val, + unsigned long flags)} + +Update the currently installed PTE that maps virtual address {\bf va} +to new value {\bf val}. As with {\bf mmu\_update}, Xen checks the +modification is safe before applying it. The {\bf flags} determine +which kind of TLB flush, if any, should follow the update. + +\end{quote} + +Finally, sufficiently privileged domains may occasionally wish to manipulate +the pages of others: + +\begin{quote} +\hypercall{update\_va\_mapping\_otherdomain(unsigned long va, uint64\_t val, + unsigned long flags, domid\_t domid)} + +Identical to {\bf update\_va\_mapping} save that the pages being +mapped must belong to the domain {\bf domid}. + +\end{quote} + +An additional MMU hypercall provides an ``extended command'' +interface. This provides additional functionality beyond the basic +table updating commands: + +\begin{quote} + +\hypercall{mmuext\_op(struct mmuext\_op *op, int count, int *success\_count, domid\_t domid)} + +This hypercall is used to perform additional MMU operations. These +include updating {\tt cr3} (or just re-installing it for a TLB flush), +requesting various kinds of TLB flush, flushing the cache, installing +a new LDT, or pinning \& unpinning page-table pages (to ensure their +reference count doesn't drop to zero which would require a +revalidation of all entries). Some of the operations available are +restricted to domains with sufficient system privileges. + +It is also possible for privileged domains to reassign page ownership +via an extended MMU operation, although grant tables are used instead +of this where possible; see Section~\ref{s:idc}. + +\end{quote} + +Finally, a hypercall interface is exposed to activate and deactivate +various optional facilities provided by Xen for memory management. + +\begin{quote} +\hypercall{vm\_assist(unsigned int cmd, unsigned int type)} + +Toggle various memory management modes (in particular writable page +tables). + +\end{quote} + +\section{Segmentation Support} + +Xen allows guest OSes to install a custom GDT if they require it; +this is context switched transparently whenever a domain is +[de]scheduled. The following hypercall is effectively a +`safe' version of {\tt lgdt}: + +\begin{quote} +\hypercall{set\_gdt(unsigned long *frame\_list, int entries)} + +Install a global descriptor table for a domain; {\bf frame\_list} is +an array of up to 16 machine page frames within which the GDT resides, +with {\bf entries} being the actual number of descriptor-entry +slots. All page frames must be mapped read-only within the guest's +address space, and the table must be large enough to contain Xen's +reserved entries (see {\bf xen/include/public/arch-x86\_32.h}). + +\end{quote} + +Many guest OSes will also wish to install LDTs; this is achieved by +using {\bf mmu\_update} with an extended command, passing the +linear address of the LDT base along with the number of entries. No +special safety checks are required; Xen needs to perform this task +simply since {\tt lldt} requires CPL 0. + + +Xen also allows guest operating systems to update just an +individual segment descriptor in the GDT or LDT: + +\begin{quote} +\hypercall{update\_descriptor(uint64\_t ma, uint64\_t desc)} + +Update the GDT/LDT entry at machine address {\bf ma}; the new +8-byte descriptor is stored in {\bf desc}. +Xen performs a number of checks to ensure the descriptor is +valid. + +\end{quote} + +Guest OSes can use the above in place of context switching entire +LDTs (or the GDT) when the number of changing descriptors is small. + +\section{Context Switching} + +When a guest OS wishes to context switch between two processes, +it can use the page table and segmentation hypercalls described +above to perform the the bulk of the privileged work. In addition, +however, it will need to invoke Xen to switch the kernel (ring 1) +stack pointer: + +\begin{quote} +\hypercall{stack\_switch(unsigned long ss, unsigned long esp)} + +Request kernel stack switch from hypervisor; {\bf ss} is the new +stack segment, which {\bf esp} is the new stack pointer. + +\end{quote} + +A useful hypercall for context switching allows ``lazy'' save and +restore of floating point state: + +\begin{quote} +\hypercall{fpu\_taskswitch(int set)} + +This call instructs Xen to set the {\tt TS} bit in the {\tt cr0} +control register; this means that the next attempt to use floating +point will cause a trap which the guest OS can trap. Typically it will +then save/restore the FP state, and clear the {\tt TS} bit, using the +same call. +\end{quote} + +This is provided as an optimization only; guest OSes can also choose +to save and restore FP state on all context switches for simplicity. + +Finally, a hypercall is provided for entering vm86 mode: + +\begin{quote} +\hypercall{switch\_vm86} + +This allows the guest to run code in vm86 mode, which is needed for +some legacy software. +\end{quote} + +\section{Physical Memory Management} + +As mentioned previously, each domain has a maximum and current +memory allocation. The maximum allocation, set at domain creation +time, cannot be modified. However a domain can choose to reduce +and subsequently grow its current allocation by using the +following call: + +\begin{quote} +\hypercall{memory\_op(unsigned int op, void *arg)} + +Increase or decrease current memory allocation (as determined by +the value of {\bf op}). The available operations are: + +\begin{description} +\item[XENMEM\_increase\_reservation] Request an increase in machine + memory allocation; {\bf arg} must point to a {\bf + xen\_memory\_reservation} structure. +\item[XENMEM\_decrease\_reservation] Request a decrease in machine + memory allocation; {\bf arg} must point to a {\bf + xen\_memory\_reservation} structure. +\item[XENMEM\_maximum\_ram\_page] Request the frame number of the + highest-addressed frame of machine memory in the system. {\bf arg} + must point to an {\bf unsigned long} where this value will be + stored. +\item[XENMEM\_current\_reservation] Returns current memory reservation + of the specified domain. +\item[XENMEM\_maximum\_reservation] Returns maximum memory reservation + of the specified domain. +\end{description} + +\end{quote} + +In addition to simply reducing or increasing the current memory +allocation via a `balloon driver', this call is also useful for +obtaining contiguous regions of machine memory when required (e.g. +for certain PCI devices, or if using superpages). + + +\section{Inter-Domain Communication} +\label{s:idc} + +Xen provides a simple asynchronous notification mechanism via +\emph{event channels}. Each domain has a set of end-points (or +\emph{ports}) which may be bound to an event source (e.g. a physical +IRQ, a virtual IRQ, or an port in another domain). When a pair of +end-points in two different domains are bound together, then a `send' +operation on one will cause an event to be received by the destination +domain. + +The control and use of event channels involves the following hypercall: + +\begin{quote} +\hypercall{event\_channel\_op(evtchn\_op\_t *op)} + +Inter-domain event-channel management; {\bf op} is a discriminated +union which allows the following 7 operations: + +\begin{description} + +\item[alloc\_unbound:] allocate a free (unbound) local + port and prepare for connection from a specified domain. +\item[bind\_virq:] bind a local port to a virtual +IRQ; any particular VIRQ can be bound to at most one port per domain. +\item[bind\_pirq:] bind a local port to a physical IRQ; +once more, a given pIRQ can be bound to at most one port per +domain. Furthermore the calling domain must be sufficiently +privileged. +\item[bind\_interdomain:] construct an interdomain event +channel; in general, the target domain must have previously allocated +an unbound port for this channel, although this can be bypassed by +privileged domains during domain setup. +\item[close:] close an interdomain event channel. +\item[send:] send an event to the remote end of a +interdomain event channel. +\item[status:] determine the current status of a local port. +\end{description} + +For more details see +{\bf xen/include/public/event\_channel.h}. + +\end{quote} + +Event channels are the fundamental communication primitive between +Xen domains and seamlessly support SMP. However they provide little +bandwidth for communication {\sl per se}, and hence are typically +married with a piece of shared memory to produce effective and +high-performance inter-domain communication. + +Safe sharing of memory pages between guest OSes is carried out by +granting access on a per page basis to individual domains. This is +achieved by using the {\tt grant\_table\_op} hypercall. + +\begin{quote} +\hypercall{grant\_table\_op(unsigned int cmd, void *uop, unsigned int count)} + +Used to invoke operations on a grant reference, to setup the grant +table and to dump the tables' contents for debugging. + +\end{quote} + +\section{IO Configuration} + +Domains with physical device access (i.e.\ driver domains) receive +limited access to certain PCI devices (bus address space and +interrupts). However many guest operating systems attempt to +determine the PCI configuration by directly access the PCI BIOS, +which cannot be allowed for safety. + +Instead, Xen provides the following hypercall: + +\begin{quote} +\hypercall{physdev\_op(void *physdev\_op)} + +Set and query IRQ configuration details, set the system IOPL, set the +TSS IO bitmap. + +\end{quote} + + +For examples of using {\tt physdev\_op}, see the +Xen-specific PCI code in the linux sparse tree. + +\section{Administrative Operations} +\label{s:dom0ops} + +A large number of control operations are available to a sufficiently +privileged domain (typically domain 0). These allow the creation and +management of new domains, for example. A complete list is given +below: for more details on any or all of these, please see +{\tt xen/include/public/dom0\_ops.h} + + +\begin{quote} +\hypercall{dom0\_op(dom0\_op\_t *op)} + +Administrative domain operations for domain management. The options are: + +\begin{description} +\item [DOM0\_GETMEMLIST:] get list of pages used by the domain + +\item [DOM0\_SCHEDCTL:] + +\item [DOM0\_ADJUSTDOM:] adjust scheduling priorities for domain + +\item [DOM0\_CREATEDOMAIN:] create a new domain + +\item [DOM0\_DESTROYDOMAIN:] deallocate all resources associated +with a domain + +\item [DOM0\_PAUSEDOMAIN:] remove a domain from the scheduler run +queue. + +\item [DOM0\_UNPAUSEDOMAIN:] mark a paused domain as schedulable + once again. + +\item [DOM0\_GETDOMAININFO:] get statistics about the domain + +\item [DOM0\_SETDOMAININFO:] set VCPU-related attributes + +\item [DOM0\_MSR:] read or write model specific registers + +\item [DOM0\_DEBUG:] interactively invoke the debugger + +\item [DOM0\_SETTIME:] set system time + +\item [DOM0\_GETPAGEFRAMEINFO:] + +\item [DOM0\_READCONSOLE:] read console content from hypervisor buffer ring + +\item [DOM0\_PINCPUDOMAIN:] pin domain to a particular CPU + +\item [DOM0\_TBUFCONTROL:] get and set trace buffer attributes + +\item [DOM0\_PHYSINFO:] get information about the host machine + +\item [DOM0\_SCHED\_ID:] get the ID of the current Xen scheduler + +\item [DOM0\_SHADOW\_CONTROL:] switch between shadow page-table modes + +\item [DOM0\_SETDOMAINMAXMEM:] set maximum memory allocation of a domain + +\item [DOM0\_GETPAGEFRAMEINFO2:] batched interface for getting +page frame info + +\item [DOM0\_ADD\_MEMTYPE:] set MTRRs + +\item [DOM0\_DEL\_MEMTYPE:] remove a memory type range + +\item [DOM0\_READ\_MEMTYPE:] read MTRR + +\item [DOM0\_PERFCCONTROL:] control Xen's software performance +counters + +\item [DOM0\_MICROCODE:] update CPU microcode + +\item [DOM0\_IOPORT\_PERMISSION:] modify domain permissions for an +IO port range (enable / disable a range for a particular domain) + +\item [DOM0\_GETVCPUCONTEXT:] get context from a VCPU + +\item [DOM0\_GETVCPUINFO:] get current state for a VCPU +\item [DOM0\_GETDOMAININFOLIST:] batched interface to get domain +info + +\item [DOM0\_PLATFORM\_QUIRK:] inform Xen of a platform quirk it +needs to handle (e.g. noirqbalance) + +\item [DOM0\_PHYSICAL\_MEMORY\_MAP:] get info about dom0's memory +map + +\item [DOM0\_MAX\_VCPUS:] change max number of VCPUs for a domain + +\item [DOM0\_SETDOMAINHANDLE:] set the handle for a domain + +\end{description} +\end{quote} + +Most of the above are best understood by looking at the code +implementing them (in {\tt xen/common/dom0\_ops.c}) and in +the user-space tools that use them (mostly in {\tt tools/libxc}). + +\section{Access Control Module Hypercalls} +\label{s:acmops} + +Hypercalls relating to the management of the Access Control Module are +also restricted to domain 0 access for now. For more details on any or +all of these, please see {\tt xen/include/public/acm\_ops.h}. A +complete list is given below: + +\begin{quote} + +\hypercall{acm\_op(int cmd, void *args)} + +This hypercall can be used to configure the state of the ACM, query +that state, request access control decisions and dump additional +information. + +\begin{description} + +\item [ACMOP\_SETPOLICY:] set the access control policy + +\item [ACMOP\_GETPOLICY:] get the current access control policy and + status + +\item [ACMOP\_DUMPSTATS:] get current access control hook invocation + statistics + +\item [ACMOP\_GETSSID:] get security access control information for a + domain + +\item [ACMOP\_GETDECISION:] get access decision based on the currently + enforced access control policy + +\end{description} +\end{quote} + +Most of the above are best understood by looking at the code +implementing them (in {\tt xen/common/acm\_ops.c}) and in the +user-space tools that use them (mostly in {\tt tools/security} and +{\tt tools/python/xen/lowlevel/acm}). + + +\section{Debugging Hypercalls} + +A few additional hypercalls are mainly useful for debugging: + +\begin{quote} +\hypercall{console\_io(int cmd, int count, char *str)} + +Use Xen to interact with the console; operations are: + +{CONSOLEIO\_write}: Output count characters from buffer str. + +{CONSOLEIO\_read}: Input at most count characters into buffer str. +\end{quote} + +A pair of hypercalls allows access to the underlying debug registers: +\begin{quote} +\hypercall{set\_debugreg(int reg, unsigned long value)} + +Set debug register {\bf reg} to {\bf value} + +\hypercall{get\_debugreg(int reg)} + +Return the contents of the debug register {\bf reg} +\end{quote} + +And finally: +\begin{quote} +\hypercall{xen\_version(int cmd)} + +Request Xen version number. +\end{quote} + +This is useful to ensure that user-space tools are in sync +with the underlying hypervisor. + + +\end{document} diff --git a/docs/src/user.tex b/docs/src/user.tex new file mode 100644 index 0000000..c4a8171 --- /dev/null +++ b/docs/src/user.tex @@ -0,0 +1,5293 @@ +\documentclass[11pt,twoside,final,openright]{report} +\usepackage{a4,graphicx,html,parskip,setspace,times,xspace,url} +\setstretch{1.15} + +\renewcommand{\ttdefault}{pcr} + +\def\Xend{{Xend}\xspace} +\def\xend{{xend}\xspace} + +\latexhtml{\renewcommand{\path}[1]{{\small {\tt #1}}}}{\renewcommand{\path}[1]{{\tt #1}}} + + +\begin{document} + +% TITLE PAGE +\pagestyle{empty} +\begin{center} +\vspace*{\fill} +\includegraphics{figs/xenlogo.eps} +\vfill +\vfill +\vfill +\begin{tabular}{l} +{\Huge \bf Users' Manual} \\[4mm] +{\huge Xen v3.3} \\[80mm] +\end{tabular} +\end{center} + +{\bf DISCLAIMER: This documentation is always under active development +and as such there may be mistakes and omissions --- watch out for +these and please report any you find to the developers' mailing list, +xen-devel@lists.xensource.com. The latest version is always available +on-line. Contributions of material, suggestions and corrections are +welcome.} + +\vfill +\clearpage + + +% COPYRIGHT NOTICE +\pagestyle{empty} + +\vspace*{\fill} + +Xen is Copyright \copyright 2002-2008, Citrix Systems, Inc., University of Cambridge, UK, XenSource Inc., IBM Corp., Hewlett-Packard Co., Intel Corp., AMD Inc., and others. All rights reserved. + +Xen is an open-source project. Most portions of Xen are licensed for copying +under the terms of the GNU General Public License, version 2. Other portions +are licensed under the terms of the GNU Lesser General Public License, the +Zope Public License 2.0, or under ``BSD-style'' licenses. Please refer to the +COPYING file for details. + +Xen includes software by Christopher Clark. This software is covered by the +following licence: + +\begin{quote} +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: + +\begin{itemize} +\item Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +\item 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. + +\item 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. +\end{itemize} + +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. +\end{quote} + +\cleardoublepage + + +% TABLE OF CONTENTS +\pagestyle{plain} +\pagenumbering{roman} +{ \parskip 0pt plus 1pt + \tableofcontents } +\cleardoublepage + + +% PREPARE FOR MAIN TEXT +\pagenumbering{arabic} +\raggedbottom +\widowpenalty=10000 +\clubpenalty=10000 +\parindent=0pt +\parskip=5pt +\renewcommand{\topfraction}{.8} +\renewcommand{\bottomfraction}{.8} +\renewcommand{\textfraction}{.2} +\renewcommand{\floatpagefraction}{.8} +\setstretch{1.1} + + +%% Chapter Introduction moved to introduction.tex +\chapter{Introduction} + + +Xen is an open-source \emph{para-virtualizing} virtual machine monitor +(VMM), or ``hypervisor'', for a variety of processor architectures including x86. Xen can securely execute multiple virtual machines on a single physical system with near native performance. Xen facilitates enterprise-grade functionality, including: + +\begin{itemize} +\item Virtual machines with performance close to native hardware. +\item Live migration of running virtual machines between physical hosts. +\item Up to 32\footnote{IA64 supports up to 64 virtual CPUs per guest virtual machine} virtual CPUs per guest virtual machine, with VCPU hotplug. +\item x86/32 with PAE, x86/64, and IA64 platform support. +\item Intel and AMD Virtualization Technology for unmodified guest operating systems (including Microsoft Windows). +\item Excellent hardware support (supports almost all Linux device + drivers). +\end{itemize} + + +\section{Usage Scenarios} + +Usage scenarios for Xen include: + +\begin{description} +\item [Server Consolidation.] Move multiple servers onto a single + physical host with performance and fault isolation provided at the + virtual machine boundaries. +\item [Hardware Independence.] Allow legacy applications and operating + systems to exploit new hardware. +\item [Multiple OS configurations.] Run multiple operating systems + simultaneously, for development or testing purposes. +\item [Kernel Development.] Test and debug kernel modifications in a + sand-boxed virtual machine --- no need for a separate test machine. +\item [Cluster Computing.] Management at VM granularity provides more + flexibility than separately managing each physical host, but better + control and isolation than single-system image solutions, + particularly by using live migration for load balancing. +\item [Hardware support for custom OSes.] Allow development of new + OSes while benefiting from the wide-ranging hardware support of + existing OSes such as Linux. +\end{description} + + +\section{Operating System Support} + +Para-virtualization permits very high performance virtualization, even +on architectures like x86 that are traditionally very hard to +virtualize. + +This approach requires operating systems to be \emph{ported} to run on +Xen. Porting an OS to run on Xen is similar to supporting a new +hardware platform, however the process is simplified because the +para-virtual machine architecture is very similar to the underlying +native hardware. Even though operating system kernels must explicitly +support Xen, a key feature is that user space applications and +libraries \emph{do not} require modification. + +With hardware CPU virtualization as provided by Intel VT and AMD +SVM technology, the ability to run an unmodified guest OS kernel +is available. No porting of the OS is required, although some +additional driver support is necessary within Xen itself. Unlike +traditional full virtualization hypervisors, which suffer a tremendous +performance overhead, the combination of Xen and VT or Xen and +Pacifica technology complement one another to offer superb performance +for para-virtualized guest operating systems and full support for +unmodified guests running natively on the processor. + +Paravirtualized Xen support is available for increasingly many +operating systems: currently, mature Linux support is available and +included in the standard distribution. Other OS ports, including +NetBSD, FreeBSD and Solaris are also complete. + + +\section{Hardware Support} + +Xen currently runs on the IA64 and x86 architectures. Multiprocessor +machines are supported, and there is support for HyperThreading (SMT). + +The default 32-bit Xen requires processor support for Physical +Addressing Extensions (PAE), which enables the hypervisor to address +up to 16GB of physical memory. Xen also supports x86/64 platforms +such as Intel EM64T and AMD Opteron which can currently address up to +1TB of physical memory. + +Xen offloads most of the hardware support issues to the guest OS +running in the \emph{Domain~0} management virtual machine. Xen itself +contains only the code required to detect and start secondary +processors, set up interrupt routing, and perform PCI bus +enumeration. Device drivers run within a privileged guest OS rather +than within Xen itself. This approach provides compatibility with the +majority of device hardware supported by Linux. The default XenLinux +build contains support for most server-class network and disk +hardware, but you can add support for other hardware by configuring +your XenLinux kernel in the normal way. + + +\section{Structure of a Xen-Based System} + +A Xen system has multiple layers, the lowest and most privileged of +which is Xen itself. + +Xen may host multiple \emph{guest} operating systems, each of which is +executed within a secure virtual machine. In Xen terminology, a +\emph{domain}. Domains are scheduled by Xen to make effective use of the +available physical CPUs. Each guest OS manages its own applications. +This management includes the responsibility of scheduling each +application within the time allotted to the VM by Xen. + +The first domain, \emph{domain~0}, is created automatically when the +system boots and has special management privileges. Domain~0 builds +other domains and manages their virtual devices. It also performs +administrative tasks such as suspending, resuming and migrating other +virtual machines. + +Within domain~0, a process called \emph{xend} runs to manage the system. +\Xend\ is responsible for managing virtual machines and providing access +to their consoles. Commands are issued to \xend\ over an HTTP interface, +via a command-line tool. + + +\section{History} + +Xen was originally developed by the Systems Research Group at the +University of Cambridge Computer Laboratory as part of the XenoServers +project, funded by the UK-EPSRC\@. + +XenoServers aim to provide a ``public infrastructure for global +distributed computing''. Xen plays a key part in that, allowing one to +efficiently partition a single machine to enable multiple independent +clients to run their operating systems and applications in an +environment. This environment provides protection, resource isolation +and accounting. The project web page contains further information along +with pointers to papers and technical reports: +\path{http://www.cl.cam.ac.uk/xeno} + +Xen has grown into a fully-fledged project in its own right, enabling us +to investigate interesting research issues regarding the best techniques +for virtualizing resources such as the CPU, memory, disk and network. +Project contributors now include Citrix, Intel, IBM, HP, AMD, Novell, +RedHat, Sun, Fujitsu, and Samsung. + +Xen was first described in a paper presented at SOSP in +2003\footnote{\tt + http://www.cl.cam.ac.uk/netos/papers/2003-xensosp.pdf}, and the first +public release (1.0) was made that October. Since then, Xen has +significantly matured and is now used in production scenarios on many +sites. + +\section{What's New} + +Xen 3.3.0 offers: + +\begin{itemize} +\item IO Emulation (stub domains) for HVM IO performance and scailability +\item Replacement of Intel VT vmxassist by new 16b emulation code +\item Improved VT-d device pass-through e.g. for graphics devices +\item Enhanced C and P state power management +\item Exploitation of multi-queue support on modern NICs +\item Removal of domain lock for improved PV guest scalability +\item 2MB page support for HVM and PV guests +\item CPU Portability +\end{itemize} + +Xen 3.3 delivers the capabilities needed by enterprise customers and gives computing industry leaders a solid, secure platform to build upon for their virtualization solutions. This latest release establishes Xen as the definitive open source solution for virtualization. + + + +\part{Installation} + +%% Chapter Basic Installation +\chapter{Basic Installation} + +The Xen distribution includes three main components: Xen itself, ports +of Linux and NetBSD to run on Xen, and the userspace tools required to +manage a Xen-based system. This chapter describes how to install the +Xen~3.3 distribution from source. Alternatively, there may be pre-built +packages available as part of your operating system distribution. + + +\section{Prerequisites} +\label{sec:prerequisites} + +The following is a full list of prerequisites. Items marked `$\dag$' are +required by the \xend\ control tools, and hence required if you want to +run more than one virtual machine; items marked `$*$' are only required +if you wish to build from source. +\begin{itemize} +\item A working Linux distribution using the GRUB bootloader and running + on a P6-class or newer CPU\@. +\item [$\dag$] The \path{iproute2} package. +\item [$\dag$] The Linux bridge-utils\footnote{Available from {\tt + http://bridge.sourceforge.net}} (e.g., \path{/sbin/brctl}) +\item [$\dag$] The Linux hotplug system\footnote{Available from {\tt + http://linux-hotplug.sourceforge.net/}} (e.g., + \path{/sbin/hotplug} and related scripts). On newer distributions, + this is included alongside the Linux udev system\footnote{See {\tt + http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html/}}. +\item [$*$] Build tools (gcc v3.2.x or v3.3.x, binutils, GNU make). +\item [$*$] Development installation of zlib (e.g.,\ zlib-dev). +\item [$*$] Development installation of Python v2.2 or later (e.g.,\ + python-dev). +\item [$*$] \LaTeX\ and transfig are required to build the + documentation. +\end{itemize} + +Once you have satisfied these prerequisites, you can now install either +a binary or source distribution of Xen. + +\section{Installing from Binary Tarball} + +Pre-built tarballs are available for download from the XenSource downloads +page: +\begin{quote} {\tt http://www.xensource.com/downloads/} +\end{quote} + +Once you've downloaded the tarball, simply unpack and install: +\begin{verbatim} +# tar zxvf xen-3.0-install.tgz +# cd xen-3.0-install +# sh ./install.sh +\end{verbatim} + +Once you've installed the binaries you need to configure your system as +described in Section~\ref{s:configure}. + +\section{Installing from RPMs} +Pre-built RPMs are available for download from the XenSource downloads +page: +\begin{quote} {\tt http://www.xensource.com/downloads/} +\end{quote} + +Once you've downloaded the RPMs, you typically install them via the +RPM commands: + +\verb|# rpm -iv rpmname| + +See the instructions and the Release Notes for each RPM set referenced at: + \begin{quote} + {\tt http://www.xensource.com/downloads/}. + \end{quote} + +\section{Installing from Source} + +This section describes how to obtain, build and install Xen from source. + +\subsection{Obtaining the Source} + +The Xen source tree is available as either a compressed source tarball +or as a clone of our master Mercurial repository. + +\begin{description} +\item[Obtaining the Source Tarball]\mbox{} \\ + Stable versions and daily snapshots of the Xen source tree are + available from the Xen download page: + \begin{quote} {\tt \tt http://www.xensource.com/downloads/} + \end{quote} +\item[Obtaining the source via Mercurial]\mbox{} \\ + The source tree may also be obtained via the public Mercurial + repository at: + \begin{quote}{\tt http://xenbits.xensource.com} + \end{quote} See the instructions and the Getting Started Guide + referenced at: + \begin{quote} + {\tt http://www.xensource.com/downloads/} + \end{quote} +\end{description} + +% \section{The distribution} +% +% The Xen source code repository is structured as follows: +% +% \begin{description} +% \item[\path{tools/}] Xen node controller daemon (Xend), command line +% tools, control libraries +% \item[\path{xen/}] The Xen VMM. +% \item[\path{buildconfigs/}] Build configuration files +% \item[\path{linux-*-xen-sparse/}] Xen support for Linux. +% \item[\path{patches/}] Experimental patches for Linux. +% \item[\path{docs/}] Various documentation files for users and +% developers. +% \item[\path{extras/}] Bonus extras. +% \end{description} + +\subsection{Building from Source} + +The top-level Xen Makefile includes a target ``world'' that will do the +following: + +\begin{itemize} +\item Build Xen. +\item Build the control tools, including \xend. +\item Download (if necessary) and unpack the Linux 2.6 source code, and + patch it for use with Xen. +\item Build a Linux kernel to use in domain~0 and a smaller unprivileged + kernel, which can be used for unprivileged virtual machines. +\end{itemize} + +After the build has completed you should have a top-level directory +called \path{dist/} in which all resulting targets will be placed. Of +particular interest are the two XenLinux kernel images, one with a +``-xen0'' extension which contains hardware device drivers and drivers +for Xen's virtual devices, and one with a ``-xenU'' extension that +just contains the virtual ones. These are found in +\path{dist/install/boot/} along with the image for Xen itself and the +configuration files used during the build. + +%The NetBSD port can be built using: +%\begin{quote} +%\begin{verbatim} +%# make netbsd20 +%\end{verbatim}\end{quote} +%NetBSD port is built using a snapshot of the netbsd-2-0 cvs branch. +%The snapshot is downloaded as part of the build process if it is not +%yet present in the \path{NETBSD\_SRC\_PATH} search path. The build +%process also downloads a toolchain which includes all of the tools +%necessary to build the NetBSD kernel under Linux. + +To customize the set of kernels built you need to edit the top-level +Makefile. Look for the line: +\begin{quote} +\begin{verbatim} +KERNELS ?= linux-2.6-xen0 linux-2.6-xenU +\end{verbatim} +\end{quote} + +You can edit this line to include any set of operating system kernels +which have configurations in the top-level \path{buildconfigs/} +directory. + +%% Inspect the Makefile if you want to see what goes on during a +%% build. Building Xen and the tools is straightforward, but XenLinux +%% is more complicated. The makefile needs a `pristine' Linux kernel +%% tree to which it will then add the Xen architecture files. You can +%% tell the makefile the location of the appropriate Linux compressed +%% tar file by +%% setting the LINUX\_SRC environment variable, e.g. \\ +%% \verb!# LINUX_SRC=/tmp/linux-2.6.11.tar.bz2 make world! \\ or by +%% placing the tar file somewhere in the search path of {\tt +%% LINUX\_SRC\_PATH} which defaults to `{\tt .:..}'. If the +%% makefile can't find a suitable kernel tar file it attempts to +%% download it from kernel.org (this won't work if you're behind a +%% firewall). + +%% After untaring the pristine kernel tree, the makefile uses the {\tt +%% mkbuildtree} script to add the Xen patches to the kernel. + +%% \framebox{\parbox{5in}{ +%% {\bf Distro specific:} \\ +%% {\it Gentoo} --- if not using udev (most installations, +%% currently), you'll need to enable devfs and devfs mount at boot +%% time in the xen0 config. }} + +\subsection{Custom Kernels} + +% If you have an SMP machine you may wish to give the {\tt '-j4'} +% argument to make to get a parallel build. + +If you wish to build a customized XenLinux kernel (e.g.\ to support +additional devices or enable distribution-required features), you can +use the standard Linux configuration mechanisms, specifying that the +architecture being built for is \path{xen}, e.g: +\begin{quote} +\begin{verbatim} +# cd linux-2.6.12-xen0 +# make ARCH=xen xconfig +# cd .. +# make +\end{verbatim} +\end{quote} + +You can also copy an existing Linux configuration (\path{.config}) into +e.g.\ \path{linux-2.6.12-xen0} and execute: +\begin{quote} +\begin{verbatim} +# make ARCH=xen oldconfig +\end{verbatim} +\end{quote} + +You may be prompted with some Xen-specific options. We advise accepting +the defaults for these options. + +Note that the only difference between the two types of Linux kernels +that are built is the configuration file used for each. The ``U'' +suffixed (unprivileged) versions don't contain any of the physical +hardware device drivers, leading to a 30\% reduction in size; hence you +may prefer these for your non-privileged domains. The ``0'' suffixed +privileged versions can be used to boot the system, as well as in driver +domains and unprivileged domains. + +\subsection{Installing Generated Binaries} + +The files produced by the build process are stored under the +\path{dist/install/} directory. To install them in their default +locations, do: +\begin{quote} +\begin{verbatim} +# make install +\end{verbatim} +\end{quote} + +Alternatively, users with special installation requirements may wish to +install them manually by copying the files to their appropriate +destinations. + +%% Files in \path{install/boot/} include: +%% \begin{itemize} +%% \item \path{install/boot/xen-3.0.gz} Link to the Xen 'kernel' +%% \item \path{install/boot/vmlinuz-2.6-xen0} Link to domain 0 +%% XenLinux kernel +%% \item \path{install/boot/vmlinuz-2.6-xenU} Link to unprivileged +%% XenLinux kernel +%% \end{itemize} + +The \path{dist/install/boot} directory will also contain the config +files used for building the XenLinux kernels, and also versions of Xen +and XenLinux kernels that contain debug symbols such as +(\path{xen-syms-3.0.0} and \path{vmlinux-syms-2.6.12.6-xen0}) which are +essential for interpreting crash dumps. Retain these files as the +developers may wish to see them if you post on the mailing list. + + +\section{Configuration} +\label{s:configure} + +Once you have built and installed the Xen distribution, it is simple to +prepare the machine for booting and running Xen. + +\subsection{GRUB Configuration} + +An entry should be added to \path{grub.conf} (often found under +\path{/boot/} or \path{/boot/grub/}) to allow Xen / XenLinux to boot. +This file is sometimes called \path{menu.lst}, depending on your +distribution. The entry should look something like the following: + +%% KMSelf Thu Dec 1 19:06:13 PST 2005 262144 is useful for RHEL/RH and +%% related Dom0s. +{\small +\begin{verbatim} +title Xen 3.0 / XenLinux 2.6 + kernel /boot/xen-3.0.gz dom0_mem=262144 + module /boot/vmlinuz-2.6-xen0 root=/dev/sda4 ro console=tty0 +\end{verbatim} +} + +The kernel line tells GRUB where to find Xen itself and what boot +parameters should be passed to it (in this case, setting the domain~0 +memory allocation in kilobytes and the settings for the serial port). +For more details on the various Xen boot parameters see +Section~\ref{s:xboot}. + +The module line of the configuration describes the location of the +XenLinux kernel that Xen should start and the parameters that should be +passed to it. These are standard Linux parameters, identifying the root +device and specifying it be initially mounted read only and instructing +that console output be sent to the screen. Some distributions such as +SuSE do not require the \path{ro} parameter. + +%% \framebox{\parbox{5in}{ +%% {\bf Distro specific:} \\ +%% {\it SuSE} --- Omit the {\tt ro} option from the XenLinux +%% kernel command line, since the partition won't be remounted rw +%% during boot. }} + +To use an initrd, add another \path{module} line to the configuration, +like: {\small +\begin{verbatim} + module /boot/my_initrd.gz +\end{verbatim} +} + +%% KMSelf Thu Dec 1 19:05:30 PST 2005 Other configs as an appendix? + +When installing a new kernel, it is recommended that you do not delete +existing menu options from \path{menu.lst}, as you may wish to boot your +old Linux kernel in future, particularly if you have problems. + +\subsection{Serial Console (optional)} + +Serial console access allows you to manage, monitor, and interact with +your system over a serial console. This can allow access from another +nearby system via a null-modem (``LapLink'') cable or remotely via a serial +concentrator. + +You system's BIOS, bootloader (GRUB), Xen, Linux, and login access must +each be individually configured for serial console access. It is +\emph{not} strictly necessary to have each component fully functional, +but it can be quite useful. + +For general information on serial console configuration under Linux, +refer to the ``Remote Serial Console HOWTO'' at The Linux Documentation +Project: \url{http://www.tldp.org} + +\subsubsection{Serial Console BIOS configuration} + +Enabling system serial console output neither enables nor disables +serial capabilities in GRUB, Xen, or Linux, but may make remote +management of your system more convenient by displaying POST and other +boot messages over serial port and allowing remote BIOS configuration. + +Refer to your hardware vendor's documentation for capabilities and +procedures to enable BIOS serial redirection. + + +\subsubsection{Serial Console GRUB configuration} + +Enabling GRUB serial console output neither enables nor disables Xen or +Linux serial capabilities, but may made remote management of your system +more convenient by displaying GRUB prompts, menus, and actions over +serial port and allowing remote GRUB management. + +Adding the following two lines to your GRUB configuration file, +typically either \path{/boot/grub/menu.lst} or \path{/boot/grub/grub.conf} +depending on your distro, will enable GRUB serial output. + +\begin{quote} +{\small \begin{verbatim} + serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 + terminal --timeout=10 serial console +\end{verbatim}} +\end{quote} + +Note that when both the serial port and the local monitor and keyboard +are enabled, the text ``\emph{Press any key to continue}'' will appear +at both. Pressing a key on one device will cause GRUB to display to +that device. The other device will see no output. If no key is +pressed before the timeout period expires, the system will boot to the +default GRUB boot entry. + +Please refer to the GRUB documentation for further information. + + +\subsubsection{Serial Console Xen configuration} + +Enabling Xen serial console output neither enables nor disables Linux +kernel output or logging in to Linux over serial port. It does however +allow you to monitor and log the Xen boot process via serial console and +can be very useful in debugging. + +%% kernel /boot/xen-2.0.gz dom0_mem=131072 console=com1,vga com1=115200,8n1 +%% module /boot/vmlinuz-2.6-xen0 root=/dev/sda4 ro + +In order to configure Xen serial console output, it is necessary to +add a boot option to your GRUB config; e.g.\ replace the previous +example kernel line with: +\begin{quote} {\small \begin{verbatim} + kernel /boot/xen.gz dom0_mem=131072 com1=115200,8n1 console=com1,vga +\end{verbatim}} +\end{quote} + +This configures Xen to output on COM1 at 115,200 baud, 8 data bits, no +parity and 1 stop bit. Modify these parameters for your environment. +See Section~\ref{s:xboot} for an explanation of all boot parameters. + +One can also configure XenLinux to share the serial console; to achieve +this append ``\path{console=ttyS0}'' to your module line. + + +\subsubsection{Serial Console Linux configuration} + +Enabling Linux serial console output at boot neither enables nor +disables logging in to Linux over serial port. It does however allow +you to monitor and log the Linux boot process via serial console and can be +very useful in debugging. + +To enable Linux output at boot time, add the parameter +\path{console=ttyS0} (or ttyS1, ttyS2, etc.) to your kernel GRUB line. +Under Xen, this might be: +\begin{quote} +{\footnotesize \begin{verbatim} + module /vmlinuz-2.6-xen0 ro root=/dev/VolGroup00/LogVol00 \ + console=ttyS0, 115200 +\end{verbatim}} +\end{quote} +to enable output over ttyS0 at 115200 baud. + + + +\subsubsection{Serial Console Login configuration} + +Logging in to Linux via serial console, under Xen or otherwise, requires +specifying a login prompt be started on the serial port. To permit root +logins over serial console, the serial port must be added to +\path{/etc/securetty}. + +\newpage +To automatically start a login prompt over the serial port, +add the line: \begin{quote} {\small {\tt c:2345:respawn:/sbin/mingetty +ttyS0}} \end{quote} to \path{/etc/inittab}. Run \path{init q} to force +a reload of your inttab and start getty. + +To enable root logins, add \path{ttyS0} to \path{/etc/securetty} if not +already present. + +Your distribution may use an alternate getty; options include getty, +mgetty and agetty. Consult your distribution's documentation +for further information. + + +\subsection{TLS Libraries} + +Users of the XenLinux 2.6 kernel should disable Thread Local Storage +(TLS) (e.g.\ by doing a \path{mv /lib/tls /lib/tls.disabled}) before +attempting to boot a XenLinux kernel\footnote{If you boot without first + disabling TLS, you will get a warning message during the boot process. + In this case, simply perform the rename after the machine is up and + then run \path{/sbin/ldconfig} to make it take effect.}. You can +always reenable TLS by restoring the directory to its original location +(i.e.\ \path{mv /lib/tls.disabled /lib/tls}). + +The reason for this is that the current TLS implementation uses +segmentation in a way that is not permissible under Xen. If TLS is not +disabled, an emulation mode is used within Xen which reduces performance +substantially. To ensure full performance you should install a +`Xen-friendly' (nosegneg) version of the library. + + +\section{Booting Xen} + +It should now be possible to restart the system and use Xen. Reboot and +choose the new Xen option when the Grub screen appears. + +What follows should look much like a conventional Linux boot. The first +portion of the output comes from Xen itself, supplying low level +information about itself and the underlying hardware. The last portion +of the output comes from XenLinux. + +You may see some error messages during the XenLinux boot. These are not +necessarily anything to worry about---they may result from kernel +configuration differences between your XenLinux kernel and the one you +usually use. + +When the boot completes, you should be able to log into your system as +usual. If you are unable to log in, you should still be able to reboot +with your normal Linux kernel by selecting it at the GRUB prompt. + + +% Booting Xen +\chapter{Booting a Xen System} + +Booting the system into Xen will bring you up into the privileged +management domain, Domain0. At that point you are ready to create +guest domains and ``boot'' them using the \texttt{xm create} command. + +\section{Booting Domain0} + +After installation and configuration is complete, reboot the system +and and choose the new Xen option when the Grub screen appears. + +What follows should look much like a conventional Linux boot. The +first portion of the output comes from Xen itself, supplying low level +information about itself and the underlying hardware. The last +portion of the output comes from XenLinux. + +%% KMSelf Wed Nov 30 18:09:37 PST 2005: We should specify what these are. + +When the boot completes, you should be able to log into your system as +usual. If you are unable to log in, you should still be able to +reboot with your normal Linux kernel by selecting it at the GRUB prompt. + +The first step in creating a new domain is to prepare a root +filesystem for it to boot. Typically, this might be stored in a normal +partition, an LVM or other volume manager partition, a disk file or on +an NFS server. A simple way to do this is simply to boot from your +standard OS install CD and install the distribution into another +partition on your hard drive. + +To start the \xend\ control daemon, type +\begin{quote} + \verb!# xend start! +\end{quote} + +If you wish the daemon to start automatically, see the instructions in +Section~\ref{s:xend}. Once the daemon is running, you can use the +\path{xm} tool to monitor and maintain the domains running on your +system. This chapter provides only a brief tutorial. We provide full +details of the \path{xm} tool in the next chapter. + +% \section{From the web interface} +% +% Boot the Xen machine and start Xensv (see Chapter~\ref{cha:xensv} +% for more details) using the command: \\ +% \verb_# xensv start_ \\ +% This will also start Xend (see Chapter~\ref{cha:xend} for more +% information). +% +% The domain management interface will then be available at {\tt +% http://your\_machine:8080/}. This provides a user friendly wizard +% for starting domains and functions for managing running domains. +% +% \section{From the command line} +\section{Booting Guest Domains} + +\subsection{Creating a Domain Configuration File} + +Before you can start an additional domain, you must create a +configuration file. We provide two example files which you can use as +a starting point: +\begin{itemize} +\item \path{/etc/xen/xmexample1} is a simple template configuration + file for describing a single VM\@. +\item \path{/etc/xen/xmexample2} file is a template description that + is intended to be reused for multiple virtual machines. Setting the + value of the \path{vmid} variable on the \path{xm} command line + fills in parts of this template. +\end{itemize} + +There are also a number of other examples which you may find useful. +Copy one of these files and edit it as appropriate. Typical values +you may wish to edit include: + +\begin{quote} +\begin{description} +\item[kernel] Set this to the path of the kernel you compiled for use + with Xen (e.g.\ \path{kernel = ``/boot/vmlinuz-2.6-xenU''}) +\item[memory] Set this to the size of the domain's memory in megabytes + (e.g.\ \path{memory = 64}) +\item[disk] Set the first entry in this list to calculate the offset + of the domain's root partition, based on the domain ID\@. Set the + second to the location of \path{/usr} if you are sharing it between + domains (e.g.\ \path{disk = ['phy:your\_hard\_drive\%d,sda1,w' \% + (base\_partition\_number + vmid), + 'phy:your\_usr\_partition,sda6,r' ]} +\item[dhcp] Uncomment the dhcp variable, so that the domain will + receive its IP address from a DHCP server (e.g.\ \path{dhcp=``dhcp''}) +\end{description} +\end{quote} + +You may also want to edit the {\bf vif} variable in order to choose +the MAC address of the virtual ethernet interface yourself. For +example: + +\begin{quote} +\verb_vif = ['mac=00:16:3E:F6:BB:B3']_ +\end{quote} +If you do not set this variable, \xend\ will automatically generate a +random MAC address from the range 00:16:3E:xx:xx:xx, assigned by IEEE to +XenSource as an OUI (organizationally unique identifier). XenSource +Inc. gives permission for anyone to use addresses randomly allocated +from this range for use by their Xen domains. + +For a list of IEEE OUI assignments, see +\url{http://standards.ieee.org/regauth/oui/oui.txt} + + +\subsection{Booting the Guest Domain} + +The \path{xm} tool provides a variety of commands for managing +domains. Use the \path{create} command to start new domains. Assuming +you've created a configuration file \path{myvmconf} based around +\path{/etc/xen/xmexample2}, to start a domain with virtual machine +ID~1 you should type: + +\begin{quote} +\begin{verbatim} +# xm create -c myvmconf vmid=1 +\end{verbatim} +\end{quote} + +The \path{-c} switch causes \path{xm} to turn into the domain's +console after creation. The \path{vmid=1} sets the \path{vmid} +variable used in the \path{myvmconf} file. + +You should see the console boot messages from the new domain appearing +in the terminal in which you typed the command, culminating in a login +prompt. + + +\section{Starting / Stopping Domains Automatically} + +It is possible to have certain domains start automatically at boot +time and to have dom0 wait for all running domains to shutdown before +it shuts down the system. + +To specify a domain is to start at boot-time, place its configuration +file (or a link to it) under \path{/etc/xen/auto/}. + +A Sys-V style init script for Red Hat and LSB-compliant systems is +provided and will be automatically copied to \path{/etc/init.d/} +during install. You can then enable it in the appropriate way for +your distribution. + +For instance, on Red Hat: + +\begin{quote} + \verb_# chkconfig --add xendomains_ +\end{quote} + +By default, this will start the boot-time domains in runlevels 3, 4 +and 5. + +You can also use the \path{service} command to run this script +manually, e.g: + +\begin{quote} + \verb_# service xendomains start_ + + Starts all the domains with config files under /etc/xen/auto/. +\end{quote} + +\begin{quote} + \verb_# service xendomains stop_ + + Shuts down all running Xen domains. +\end{quote} + + + +\part{Configuration and Management} + +%% Chapter Domain Management Tools and Daemons +\chapter{Domain Management Tools} + +This chapter summarizes the management software and tools available. + + +\section{\Xend\ } +\label{s:xend} + + +The \Xend\ node control daemon performs system management functions +related to virtual machines. It forms a central point of control of +virtualized resources, and must be running in order to start and manage +virtual machines. \Xend\ must be run as root because it needs access to +privileged system management functions. + +An initialization script named \texttt{/etc/init.d/xend} is provided to +start \Xend\ at boot time. Use the tool appropriate (i.e. chkconfig) for +your Linux distribution to specify the runlevels at which this script +should be executed, or manually create symbolic links in the correct +runlevel directories. + +\Xend\ can be started on the command line as well, and supports the +following set of parameters: + +\begin{tabular}{ll} + \verb!# xend start! & start \xend, if not already running \\ + \verb!# xend stop! & stop \xend\ if already running \\ + \verb!# xend restart! & restart \xend\ if running, otherwise start it \\ + % \verb!# xend trace_start! & start \xend, with very detailed debug logging \\ + \verb!# xend status! & indicates \xend\ status by its return code +\end{tabular} + +A SysV init script called {\tt xend} is provided to start \xend\ at +boot time. {\tt make install} installs this script in +\path{/etc/init.d}. To enable it, you have to make symbolic links in +the appropriate runlevel directories or use the {\tt chkconfig} tool, +where available. Once \xend\ is running, administration can be done +using the \texttt{xm} tool. + +\subsection{Logging} + +As \xend\ runs, events will be logged to \path{/var/log/xen/xend.log} and +(less frequently) to \path{/var/log/xen/xend-debug.log}. These, along with +the standard syslog files, are useful when troubleshooting problems. + +\subsection{Configuring \Xend\ } + +\Xend\ is written in Python. At startup, it reads its configuration +information from the file \path{/etc/xen/xend-config.sxp}. The Xen +installation places an example \texttt{xend-config.sxp} file in the +\texttt{/etc/xen} subdirectory which should work for most installations. + +See the example configuration file \texttt{xend-debug.sxp} and the +section 5 man page \texttt{xend-config.sxp} for a full list of +parameters and more detailed information. Some of the most important +parameters are discussed below. + +An HTTP interface and a Unix domain socket API are available to +communicate with \Xend. This allows remote users to pass commands to the +daemon. By default, \Xend does not start an HTTP server. It does start a +Unix domain socket management server, as the low level utility +\texttt{xm} requires it. For support of cross-machine migration, \Xend\ +can start a relocation server. This support is not enabled by default +for security reasons. + +Note: the example \texttt{xend} configuration file modifies the defaults and +starts up \Xend\ as an HTTP server as well as a relocation server. + +From the file: + +\begin{verbatim} +#(xend-http-server no) +(xend-http-server yes) +#(xend-unix-server yes) +#(xend-relocation-server no) +(xend-relocation-server yes) +\end{verbatim} + +Comment or uncomment lines in that file to disable or enable features +that you require. + +Connections from remote hosts are disabled by default: + +\begin{verbatim} +# Address xend should listen on for HTTP connections, if xend-http-server is +# set. +# Specifying 'localhost' prevents remote connections. +# Specifying the empty string '' (the default) allows all connections. +#(xend-address '') +(xend-address localhost) +\end{verbatim} + +It is recommended that if migration support is not needed, the +\texttt{xend-relocation-server} parameter value be changed to +``\texttt{no}'' or commented out. + +\section{Xm} +\label{s:xm} + +The xm tool is the primary tool for managing Xen from the console. The +general format of an xm command line is: + +\begin{verbatim} +# xm command [switches] [arguments] [variables] +\end{verbatim} + +The available \emph{switches} and \emph{arguments} are dependent on the +\emph{command} chosen. The \emph{variables} may be set using +declarations of the form {\tt variable=value} and command line +declarations override any of the values in the configuration file being +used, including the standard variables described above and any custom +variables (for instance, the \path{xmdefconfig} file uses a {\tt vmid} +variable). + +For online help for the commands available, type: + +\begin{quote} +\begin{verbatim} +# xm help +\end{verbatim} +\end{quote} + +This will list the most commonly used commands. The full list can be obtained +using \verb_xm help --long_. You can also type \path{xm help $<$command$>$} +for more information on a given command. + +\subsection{Basic Management Commands} + +One useful command is \verb_# xm list_ which lists all domains running in rows +of the following format: +\begin{center} {\tt name domid memory vcpus state cputime} +\end{center} + +The meaning of each field is as follows: +\begin{quote} + \begin{description} + \item[name] The descriptive name of the virtual machine. + \item[domid] The number of the domain ID this virtual machine is + running in. + \item[memory] Memory size in megabytes. + \item[vcpus] The number of virtual CPUs this domain has. + \item[state] Domain state consists of 5 fields: + \begin{description} + \item[r] running + \item[b] blocked + \item[p] paused + \item[s] shutdown + \item[c] crashed + \end{description} + \item[cputime] How much CPU time (in seconds) the domain has used so + far. + \end{description} +\end{quote} + +The \path{xm list} command also supports a long output format when the +\path{-l} switch is used. This outputs the full details of the +running domains in \xend's SXP configuration format. + +If you want to know how long your domains have been running for, then +you can use the \verb_# xm uptime_ command. + + +You can get access to the console of a particular domain using +the \verb_# xm console_ command (e.g.\ \verb_# xm console myVM_). + +\subsection{Domain Scheduling Management Commands} + +The credit CPU scheduler automatically load balances guest VCPUs +across all available physical CPUs on an SMP host. The user need +not manually pin VCPUs to load balance the system. However, she +can restrict which CPUs a particular VCPU may run on using +the \path{xm vcpu-pin} command. + +Each guest domain is assigned a \path{weight} and a \path{cap}. + +A domain with a weight of 512 will get twice as much CPU as a +domain with a weight of 256 on a contended host. Legal weights +range from 1 to 65535 and the default is 256. + +The cap optionally fixes the maximum amount of CPU a guest will +be able to consume, even if the host system has idle CPU cycles. +The cap is expressed in percentage of one physical CPU: 100 is +1 physical CPU, 50 is half a CPU, 400 is 4 CPUs, etc... The +default, 0, means there is no upper cap. + +When you are running with the credit scheduler, you can check and +modify your domains' weights and caps using the \path{xm sched-credit} +command: + +\begin{tabular}{ll} +\verb!xm sched-credit -d ! & lists weight and cap \\ +\verb!xm sched-credit -d -w ! & sets the weight \\ +\verb!xm sched-credit -d -c ! & sets the cap +\end{tabular} + + + +%% Chapter Domain Configuration +\chapter{Domain Configuration} +\label{cha:config} + +The following contains the syntax of the domain configuration files +and description of how to further specify networking, driver domain +and general scheduling behavior. + + +\section{Configuration Files} +\label{s:cfiles} + +Xen configuration files contain the following standard variables. +Unless otherwise stated, configuration items should be enclosed in +quotes: see the configuration scripts in \path{/etc/xen/} +for concrete examples. + +\begin{description} +\item[kernel] Path to the kernel image. +\item[ramdisk] Path to a ramdisk image (optional). + % \item[builder] The name of the domain build function (e.g. + % {\tt'linux'} or {\tt'netbsd'}. +\item[memory] Memory size in megabytes. +\item[vcpus] The number of virtual CPUs. +\item[console] Port to export the domain console on (default 9600 + + domain ID). +\item[vif] Network interface configuration. This may simply contain +an empty string for each desired interface, or may override various +settings, e.g.\ +\begin{verbatim} +vif = [ 'mac=00:16:3E:00:00:11, bridge=xen-br0', + 'bridge=xen-br1' ] +\end{verbatim} + to assign a MAC address and bridge to the first interface and assign + a different bridge to the second interface, leaving \xend\ to choose + the MAC address. The settings that may be overridden in this way are + type, mac, bridge, ip, script, backend, and vifname. +\item[disk] List of block devices to export to the domain e.g. + \verb_disk = [ 'phy:hda1,sda1,r' ]_ + exports physical device \path{/dev/hda1} to the domain as + \path{/dev/sda1} with read-only access. Exporting a disk read-write + which is currently mounted is dangerous -- if you are \emph{certain} + you wish to do this, you can specify \path{w!} as the mode. +\item[dhcp] Set to {\tt `dhcp'} if you want to use DHCP to configure + networking. +\item[netmask] Manually configured IP netmask. +\item[gateway] Manually configured IP gateway. +\item[hostname] Set the hostname for the virtual machine. +\item[root] Specify the root device parameter on the kernel command + line. +\item[nfs\_server] IP address for the NFS server (if any). +\item[nfs\_root] Path of the root filesystem on the NFS server (if + any). +\item[extra] Extra string to append to the kernel command line (if + any) +\end{description} + +Additional fields are documented in the example configuration files +(e.g. to configure virtual TPM functionality). + +For additional flexibility, it is also possible to include Python +scripting commands in configuration files. An example of this is the +\path{xmexample2} file, which uses Python code to handle the +\path{vmid} variable. + + +%\part{Advanced Topics} + + +\section{Network Configuration} + +For many users, the default installation should work ``out of the +box''. More complicated network setups, for instance with multiple +Ethernet interfaces and/or existing bridging setups will require some +special configuration. + +The purpose of this section is to describe the mechanisms provided by +\xend\ to allow a flexible configuration for Xen's virtual networking. + +\subsection{Xen virtual network topology} + +Each domain network interface is connected to a virtual network +interface in dom0 by a point to point link (effectively a ``virtual +crossover cable''). These devices are named {\tt + vif$<$domid$>$.$<$vifid$>$} (e.g.\ {\tt vif1.0} for the first +interface in domain~1, {\tt vif3.1} for the second interface in +domain~3). + +Traffic on these virtual interfaces is handled in domain~0 using +standard Linux mechanisms for bridging, routing, rate limiting, etc. +Xend calls on two shell scripts to perform initial configuration of +the network and configuration of new virtual interfaces. By default, +these scripts configure a single bridge for all the virtual +interfaces. Arbitrary routing / bridging configurations can be +configured by customizing the scripts, as described in the following +section. + +\subsection{Xen networking scripts} + +Xen's virtual networking is configured by two shell scripts (by +default \path{network-bridge} and \path{vif-bridge}). These are called +automatically by \xend\ when certain events occur, with arguments to +the scripts providing further contextual information. These scripts +are found by default in \path{/etc/xen/scripts}. The names and +locations of the scripts can be configured in +\path{/etc/xen/xend-config.sxp}. + +\begin{description} +\item[network-bridge:] This script is called whenever \xend\ is started or + stopped to respectively initialize or tear down the Xen virtual + network. In the default configuration initialization creates the + bridge `xen-br0' and moves eth0 onto that bridge, modifying the + routing accordingly. When \xend\ exits, it deletes the Xen bridge + and removes eth0, restoring the normal IP and routing configuration. + + %% In configurations where the bridge already exists, this script + %% could be replaced with a link to \path{/bin/true} (for instance). + +\item[vif-bridge:] This script is called for every domain virtual + interface and can configure firewalling rules and add the vif to the + appropriate bridge. By default, this adds and removes VIFs on the + default Xen bridge. +\end{description} + +Other example scripts are available (\path{network-route} and +\path{vif-route}, \path{network-nat} and \path{vif-nat}). +For more complex network setups (e.g.\ where routing is required or +integrate with existing bridges) these scripts may be replaced with +customized variants for your site's preferred configuration. + +\section{Driver Domain Configuration} +\label{s:ddconf} + +\subsection{PCI} +\label{ss:pcidd} + +Individual PCI devices can be assigned to a given domain (a PCI driver domain) +to allow that domain direct access to the PCI hardware. + +While PCI Driver Domains can increase the stability and security of a system +by addressing a number of security concerns, there are some security issues +that remain that you can read about in Section~\ref{s:ddsecurity}. + +\subsubsection{Compile-Time Setup} +To use this functionality, ensure +that the PCI Backend is compiled in to a privileged domain (e.g. domain 0) +and that the domains which will be assigned PCI devices have the PCI Frontend +compiled in. In XenLinux, the PCI Backend is available under the Xen +configuration section while the PCI Frontend is under the +architecture-specific "Bus Options" section. You may compile both the backend +and the frontend into the same kernel; they will not affect each other. + +\subsubsection{PCI Backend Configuration - Binding at Boot} +The PCI devices you wish to assign to unprivileged domains must be "hidden" +from your backend domain (usually domain 0) so that it does not load a driver +for them. Use the \path{pciback.hide} kernel parameter which is specified on +the kernel command-line and is configurable through GRUB (see +Section~\ref{s:configure}). Note that devices are not really hidden from the +backend domain. The PCI Backend appears to the Linux kernel as a regular PCI +device driver. The PCI Backend ensures that no other device driver loads +for the devices by binding itself as the device driver for those devices. +PCI devices are identified by hexadecimal slot/function numbers (on Linux, +use \path{lspci} to determine slot/function numbers of your devices) and +can be specified with or without the PCI domain: \\ +\centerline{ {\tt ({\em bus}:{\em slot}.{\em func})} example {\tt (02:1d.3)}} \\ +\centerline{ {\tt ({\em domain}:{\em bus}:{\em slot}.{\em func})} example {\tt (0000:02:1d.3)}} \\ + +An example kernel command-line which hides two PCI devices might be: \\ +\centerline{ {\tt root=/dev/sda4 ro console=tty0 pciback.hide=(02:01.f)(0000:04:1d.0) } } \\ + +\subsubsection{PCI Backend Configuration - Late Binding} +PCI devices can also be bound to the PCI Backend after boot through the manual +binding/unbinding facilities provided by the Linux kernel in sysfs (allowing +for a Xen user to give PCI devices to driver domains that were not specified +on the kernel command-line). There are several attributes with the PCI +Backend's sysfs directory (\path{/sys/bus/pci/drivers/pciback}) that can be +used to bind/unbind devices: + +\begin{description} +\item[slots] lists all of the PCI slots that the PCI Backend will try to seize + (or "hide" from Domain 0). A PCI slot must appear in this list before it can + be bound to the PCI Backend through the \path{bind} attribute. +\item[new\_slot] write the name of a slot here (in 0000:00:00.0 format) to + have the PCI Backend seize the device in this slot. +\item[remove\_slot] write the name of a slot here (same format as + \path{new\_slot}) to have the PCI Backend no longer try to seize devices in + this slot. Note that this does not unbind the driver from a device it has + already seized. +\item[bind] write the name of a slot here (in 0000:00:00.0 format) to have + the Linux kernel attempt to bind the device in that slot to the PCI Backend + driver. +\item[unbind] write the name of a skit here (same format as \path{bind}) to have + the Linux kernel unbind the device from the PCI Backend. DO NOT unbind a + device while it is currently given to a PCI driver domain! +\end{description} + +Some examples: + +Bind a device to the PCI Backend which is not bound to any other driver. +\begin{verbatim} +# # Add a new slot to the PCI Backend's list +# echo -n 0000:01:04.d > /sys/bus/pci/drivers/pciback/new_slot +# # Now that the backend is watching for the slot, bind to it +# echo -n 0000:01:04.d > /sys/bus/pci/drivers/pciback/bind +\end{verbatim} + +Unbind a device from its driver and bind to the PCI Backend. +\begin{verbatim} +# # Unbind a PCI network card from its network driver +# echo -n 0000:05:02.0 > /sys/bus/pci/drivers/3c905/unbind +# # And now bind it to the PCI Backend +# echo -n 0000:05:02.0 > /sys/bus/pci/drivers/pciback/new_slot +# echo -n 0000:05:02.0 > /sys/bus/pci/drivers/pciback/bind +\end{verbatim} + +Note that the "-n" option in the example is important as it causes echo to not +output a new-line. + +\subsubsection{PCI Backend Configuration - User-space Quirks} +Quirky devices (such as the Broadcom Tigon 3) may need write access to their +configuration space registers. Xen can be instructed to allow specified PCI +devices write access to specific configuration space registers. The policy may +be found in: + +\centerline{ \path{/etc/xen/xend-pci-quirks.sxp} } + +The policy file is heavily commented and is intended to provide enough +documentation for developers to extend it. + +\subsubsection{PCI Backend Configuration - Permissive Flag} +If the user-space quirks approach doesn't meet your needs you may want to enable +the permissive flag for that device. To do so, first get the PCI domain, bus, +slot, and function information from dom0 via \path{lspci}. Then augment the +user-space policy for permissive devices. The permissive policy can be found +in: + +\centerline{ \path{/etc/xen/xend-pci-permissive.sxp} } + +Currently, the only way to reset the permissive flag is to unbind the device +from the PCI Backend driver. + +\subsubsection{PCI Backend - Checking Status} +There two important sysfs nodes that provide a mechanism to view specifics on +quirks and permissive devices: +\begin{description} +\item \path{/sys/bus/drivers/pciback/permissive} \\ + Use \path{cat} on this file to view a list of permissive slots. +\item \path{/sys/bus/drivers/pciback/quirks} \\ + Use \path{cat} on this file view a hierarchical view of devices bound to the +PCI backend, their PCI vendor/device ID, and any quirks that are associated with +that particular slot. +\end{description} + +You may notice that every device bound to the PCI backend has 17 quirks standard +"quirks" regardless of \path{xend-pci-quirks.sxp}. These default entries are +necessary to support interactions between the PCI bus manager and the device bound +to it. Even non-quirky devices should have these standard entries. + +In this case, preference was given to accuracy over aesthetics by choosing to +show the standard quirks in the quirks list rather than hide them from the +inquiring user + +\subsubsection{PCI Frontend Configuration} +To configure a domU to receive a PCI device: + +\begin{description} +\item[Command-line:] + Use the {\em pci} command-line flag. For multiple devices, use the option + multiple times. \\ +\centerline{ {\tt xm create netcard-dd pci=01:00.0 pci=02:03.0 }} \\ + +\item[Flat Format configuration file:] + Specify all of your PCI devices in a python list named {\em pci}. \\ +\centerline{ {\tt pci=['01:00.0','02:03.0'] }} \\ + +\item[SXP Format configuration file:] + Use a single PCI device section for all of your devices (specify the numbers + in hexadecimal with the preceding '0x'). Note that {\em domain} here refers + to the PCI domain, not a virtual machine within Xen. +{\small +\begin{verbatim} +(device (pci + (dev (domain 0x0)(bus 0x3)(slot 0x1a)(func 0x1) + (dev (domain 0x0)(bus 0x1)(slot 0x5)(func 0x0) +) +\end{verbatim} +} +\end{description} + +%% There are two possible types of privileges: IO privileges and +%% administration privileges. + +\section{Support for virtual Trusted Platform Module (vTPM)} +\label{ss:vtpm} + +Paravirtualized domains can be given access to a virtualized version +of a TPM. This enables applications in these domains to use the services +of the TPM device for example through a TSS stack +\footnote{Trousers TSS stack: http://sourceforge.net/projects/trousers}. +The Xen source repository provides the necessary software components to +enable virtual TPM access. Support is provided through several +different pieces. First, a TPM emulator has been modified to provide TPM's +functionality for the virtual TPM subsystem. Second, a virtual TPM Manager +coordinates the virtual TPMs efforts, manages their creation, and provides +protected key storage using the TPM. Third, a device driver pair providing +a TPM front- and backend is available for XenLinux to deliver TPM commands +from the domain to the virtual TPM manager, which dispatches it to a +software TPM. Since the TPM Manager relies on a HW TPM for protected key +storage, therefore this subsystem requires a Linux-supported hardware TPM. +For development purposes, a TPM emulator is available for use on non-TPM +enabled platforms. + +\subsubsection{Compile-Time Setup} +To enable access to the virtual TPM, the virtual TPM backend driver must +be compiled for a privileged domain (e.g. domain 0). Using the XenLinux +configuration, the necessary driver can be selected in the Xen configuration +section. Unless the driver has been compiled into the kernel, its module +must be activated using the following command: + +\begin{verbatim} +modprobe tpmbk +\end{verbatim} + +Similarly, the TPM frontend driver must be compiled for the kernel trying +to use TPM functionality. Its driver can be selected in the kernel +configuration section Device Driver / Character Devices / TPM Devices. +Along with that the TPM driver for the built-in TPM must be selected. +If the virtual TPM driver has been compiled as module, it +must be activated using the following command: + +\begin{verbatim} +modprobe tpm_xenu +\end{verbatim} + +Furthermore, it is necessary to build the virtual TPM manager and software +TPM by making changes to entries in Xen build configuration files. +The following entry in the file Config.mk in the Xen root source +directory must be made: + +\begin{verbatim} +VTPM_TOOLS ?= y +\end{verbatim} + +After a build of the Xen tree and a reboot of the machine, the TPM backend +drive must be loaded. Once loaded, the virtual TPM manager daemon +must be started before TPM-enabled guest domains may be launched. +To enable being the destination of a virtual TPM Migration, the virtual TPM +migration daemon must also be loaded. + +\begin{verbatim} +vtpm_managerd +\end{verbatim} +\begin{verbatim} +vtpm_migratord +\end{verbatim} + +Once the VTPM manager is running, the VTPM can be accessed by loading the +front end driver in a guest domain. + +\subsubsection{Development and Testing TPM Emulator} +For development and testing on non-TPM enabled platforms, a TPM emulator +can be used in replacement of a platform TPM. First, the entry in the file +tools/vtpm/Rules.mk must look as follows: + +\begin{verbatim} +BUILD_EMULATOR = y +\end{verbatim} + +Second, the entry in the file tool/vtpm\_manager/Rules.mk must be uncommented +as follows: + +\begin{verbatim} +# TCS talks to fifo's rather than /dev/tpm. TPM Emulator assumed on fifos +CFLAGS += -DDUMMY_TPM +\end{verbatim} + +Before starting the virtual TPM Manager, start the emulator by executing +the following in dom0: + +\begin{verbatim} +tpm_emulator clear +\end{verbatim} + +\subsubsection{vTPM Frontend Configuration} +To provide TPM functionality to a user domain, a line must be added to +the virtual TPM configuration file using the following format: + +\begin{verbatim} +vtpm = ['instance=, backend='] +\end{verbatim} + +The { \it instance number} reflects the preferred virtual TPM instance +to associate with the domain. If the selected instance is +already associated with another domain, the system will automatically +select the next available instance. An instance number greater than +zero must be provided. It is possible to omit the instance +parameter from the configuration file. + +The {\it domain id} provides the ID of the domain where the +virtual TPM backend driver and virtual TPM are running in. It should +currently always be set to '0'. + + +Examples for valid vtpm entries in the configuration file are + +\begin{verbatim} + vtpm = ['instance=1, backend=0'] +\end{verbatim} +and +\begin{verbatim} + vtpm = ['backend=0']. +\end{verbatim} + +\subsubsection{Using the virtual TPM} + +Access to TPM functionality is provided by the virtual TPM frontend driver. +Similar to existing hardware TPM drivers, this driver provides basic TPM +status information through the {\it sysfs} filesystem. In a Xen user domain +the sysfs entries can be found in /sys/devices/xen/vtpm-0. + +Commands can be sent to the virtual TPM instance using the character +device /dev/tpm0 (major 10, minor 224). + +% Chapter Storage and FileSytem Management +\chapter{Storage and File System Management} + +Storage can be made available to virtual machines in a number of +different ways. This chapter covers some possible configurations. + +The most straightforward method is to export a physical block device (a +hard drive or partition) from dom0 directly to the guest domain as a +virtual block device (VBD). + +Storage may also be exported from a filesystem image or a partitioned +filesystem image as a \emph{file-backed VBD}. + +Finally, standard network storage protocols such as NBD, iSCSI, NFS, +etc., can be used to provide storage to virtual machines. + + +\section{Exporting Physical Devices as VBDs} +\label{s:exporting-physical-devices-as-vbds} + +One of the simplest configurations is to directly export individual +partitions from domain~0 to other domains. To achieve this use the +\path{phy:} specifier in your domain configuration file. For example a +line like +\begin{quote} + \verb_disk = ['phy:hda3,sda1,w']_ +\end{quote} +specifies that the partition \path{/dev/hda3} in domain~0 should be +exported read-write to the new domain as \path{/dev/sda1}; one could +equally well export it as \path{/dev/hda} or \path{/dev/sdb5} should +one wish. + +In addition to local disks and partitions, it is possible to export +any device that Linux considers to be ``a disk'' in the same manner. +For example, if you have iSCSI disks or GNBD volumes imported into +domain~0 you can export these to other domains using the \path{phy:} +disk syntax. E.g.: +\begin{quote} + \verb_disk = ['phy:vg/lvm1,sda2,w']_ +\end{quote} + +\begin{center} + \framebox{\bf Warning: Block device sharing} +\end{center} +\begin{quote} + Block devices should typically only be shared between domains in a + read-only fashion otherwise the Linux kernel's file systems will get + very confused as the file system structure may change underneath + them (having the same ext3 partition mounted \path{rw} twice is a + sure fire way to cause irreparable damage)! \Xend\ will attempt to + prevent you from doing this by checking that the device is not + mounted read-write in domain~0, and hasn't already been exported + read-write to another domain. If you want read-write sharing, + export the directory to other domains via NFS from domain~0 (or use + a cluster file system such as GFS or ocfs2). +\end{quote} + + +\section{Using File-backed VBDs} + +It is also possible to use a file in Domain~0 as the primary storage +for a virtual machine. As well as being convenient, this also has the +advantage that the virtual block device will be \emph{sparse} --- +space will only really be allocated as parts of the file are used. So +if a virtual machine uses only half of its disk space then the file +really takes up half of the size allocated. + +For example, to create a 2GB sparse file-backed virtual block device +(actually only consumes no disk space at all): +\begin{quote} + \verb_# dd if=/dev/zero of=vm1disk bs=1k seek=2048k count=0_ +\end{quote} + +Make a file system in the disk file: +\begin{quote} + \verb_# mkfs -t ext3 vm1disk_ +\end{quote} + +(when the tool asks for confirmation, answer `y') + +Populate the file system e.g.\ by copying from the current root: +\begin{quote} +\begin{verbatim} +# mount -o loop vm1disk /mnt +# cp -ax /{root,dev,var,etc,usr,bin,sbin,lib} /mnt +# mkdir /mnt/{proc,sys,home,tmp} +\end{verbatim} +\end{quote} + +Tailor the file system by editing \path{/etc/fstab}, +\path{/etc/hostname}, etc.\ Don't forget to edit the files in the +mounted file system, instead of your domain~0 filesystem, e.g.\ you +would edit \path{/mnt/etc/fstab} instead of \path{/etc/fstab}. For +this example put \path{/dev/sda1} to root in fstab. + +Now unmount (this is important!): +\begin{quote} + \verb_# umount /mnt_ +\end{quote} + +In the configuration file set: +\begin{quote} + \verb_disk = ['tap:aio:/full/path/to/vm1disk,sda1,w']_ +\end{quote} + +As the virtual machine writes to its `disk', the sparse file will be +filled in and consume more space up to the original 2GB. + +{\em{Note:}} Users that have worked with file-backed VBDs on Xen in previous +versions will be interested to know that this support is now provided through +the blktap driver instead of the loopback driver. This change results in +file-based block devices that are higher-performance, more scalable, and which +provide better safety properties for VBD data. All that is required to update +your existing file-backed VM configurations is to change VBD configuration +lines from: +\begin{quote} + \verb_disk = ['file:/full/path/to/vm1disk,sda1,w']_ +\end{quote} +to: +\begin{quote} + \verb_disk = ['tap:aio:/full/path/to/vm1disk,sda1,w']_ +\end{quote} + + +\subsection{Loopback-mounted file-backed VBDs (deprecated)} + +{\em{{\bf{Note:}} Loopback mounted VBDs have now been replaced with + blktap-based support for raw image files, as described above. This + section remains to detail a configuration that was used by older Xen + versions.}} + +Raw image file-backed VBDs may also be attached to VMs using the +Linux loopback driver. The only required change to the raw file +instructions above are to specify the configuration entry as: +\begin{quote} + \verb_disk = ['file:/full/path/to/vm1disk,sda1,w']_ +\end{quote} + +{\bf Note that loopback file-backed VBDs may not be appropriate for backing + I/O-intensive domains.} This approach is known to experience +substantial slowdowns under heavy I/O workloads, due to the I/O +handling by the loopback block device used to support file-backed VBDs +in dom0. Loopback support remains for old Xen installations, and users +are strongly encouraged to use the blktap-based file support (using +``{\tt{tap:aio}}'' as described above). + +Additionally, Linux supports a maximum of eight loopback file-backed +VBDs across all domains by default. This limit can be statically +increased by using the \emph{max\_loop} module parameter if +CONFIG\_BLK\_DEV\_LOOP is compiled as a module in the dom0 kernel, or +by using the \emph{max\_loop=n} boot option if CONFIG\_BLK\_DEV\_LOOP +is compiled directly into the dom0 kernel. Again, users are encouraged +to use the blktap-based file support described above which scales to much +larger number of active VBDs. + + +\section{Using LVM-backed VBDs} +\label{s:using-lvm-backed-vbds} + +A particularly appealing solution is to use LVM volumes as backing for +domain file-systems since this allows dynamic growing/shrinking of +volumes as well as snapshot and other features. + +To initialize a partition to support LVM volumes: +\begin{quote} +\begin{verbatim} +# pvcreate /dev/sda10 +\end{verbatim} +\end{quote} + +Create a volume group named `vg' on the physical partition: +\begin{quote} +\begin{verbatim} +# vgcreate vg /dev/sda10 +\end{verbatim} +\end{quote} + +Create a logical volume of size 4GB named `myvmdisk1': +\begin{quote} +\begin{verbatim} +# lvcreate -L4096M -n myvmdisk1 vg +\end{verbatim} +\end{quote} + +You should now see that you have a \path{/dev/vg/myvmdisk1} Make a +filesystem, mount it and populate it, e.g.: +\begin{quote} +\begin{verbatim} +# mkfs -t ext3 /dev/vg/myvmdisk1 +# mount /dev/vg/myvmdisk1 /mnt +# cp -ax / /mnt +# umount /mnt +\end{verbatim} +\end{quote} + +Now configure your VM with the following disk configuration: +\begin{quote} +\begin{verbatim} + disk = [ 'phy:vg/myvmdisk1,sda1,w' ] +\end{verbatim} +\end{quote} + +LVM enables you to grow the size of logical volumes, but you'll need +to resize the corresponding file system to make use of the new space. +Some file systems (e.g.\ ext3) now support online resize. See the LVM +manuals for more details. + +You can also use LVM for creating copy-on-write (CoW) clones of LVM +volumes (known as writable persistent snapshots in LVM terminology). +This facility is new in Linux 2.6.8, so isn't as stable as one might +hope. In particular, using lots of CoW LVM disks consumes a lot of +dom0 memory, and error conditions such as running out of disk space +are not handled well. Hopefully this will improve in future. + +To create two copy-on-write clones of the above file system you would +use the following commands: + +\begin{quote} +\begin{verbatim} +# lvcreate -s -L1024M -n myclonedisk1 /dev/vg/myvmdisk1 +# lvcreate -s -L1024M -n myclonedisk2 /dev/vg/myvmdisk1 +\end{verbatim} +\end{quote} + +Each of these can grow to have 1GB of differences from the master +volume. You can grow the amount of space for storing the differences +using the lvextend command, e.g.: +\begin{quote} +\begin{verbatim} +# lvextend +100M /dev/vg/myclonedisk1 +\end{verbatim} +\end{quote} + +Don't let the `differences volume' ever fill up otherwise LVM gets +rather confused. It may be possible to automate the growing process by +using \path{dmsetup wait} to spot the volume getting full and then +issue an \path{lvextend}. + +In principle, it is possible to continue writing to the volume that +has been cloned (the changes will not be visible to the clones), but +we wouldn't recommend this: have the cloned volume as a `pristine' +file system install that isn't mounted directly by any of the virtual +machines. + + +\section{Using NFS Root} + +First, populate a root filesystem in a directory on the server +machine. This can be on a distinct physical machine, or simply run +within a virtual machine on the same node. + +Now configure the NFS server to export this filesystem over the +network by adding a line to \path{/etc/exports}, for instance: + +\begin{quote} + \begin{small} +\begin{verbatim} +/export/vm1root 192.0.2.4/24 (rw,sync,no_root_squash) +\end{verbatim} + \end{small} +\end{quote} + +Finally, configure the domain to use NFS root. In addition to the +normal variables, you should make sure to set the following values in +the domain's configuration file: + +\begin{quote} + \begin{small} +\begin{verbatim} +root = '/dev/nfs' +nfs_server = '2.3.4.5' # substitute IP address of server +nfs_root = '/path/to/root' # path to root FS on the server +\end{verbatim} + \end{small} +\end{quote} + +The domain will need network access at boot time, so either statically +configure an IP address using the config variables \path{ip}, +\path{netmask}, \path{gateway}, \path{hostname}; or enable DHCP +(\path{dhcp='dhcp'}). + +Note that the Linux NFS root implementation is known to have stability +problems under high load (this is not a Xen-specific problem), so this +configuration may not be appropriate for critical servers. + + +\chapter{CPU Management} + +%% KMS Something sage about CPU / processor management. + +Xen allows a domain's virtual CPU(s) to be associated with one or more +host CPUs. This can be used to allocate real resources among one or +more guests, or to make optimal use of processor resources when +utilizing dual-core, hyperthreading, or other advanced CPU technologies. + +Xen enumerates physical CPUs in a `depth first' fashion. For a system +with both hyperthreading and multiple cores, this would be all the +hyperthreads on a given core, then all the cores on a given socket, +and then all sockets. I.e. if you had a two socket, dual core, +hyperthreaded Xeon the CPU order would be: + + +\begin{center} +\begin{tabular}{l|l|l|l|l|l|l|r} +\multicolumn{4}{c|}{socket0} & \multicolumn{4}{c}{socket1} \\ \hline +\multicolumn{2}{c|}{core0} & \multicolumn{2}{c|}{core1} & +\multicolumn{2}{c|}{core0} & \multicolumn{2}{c}{core1} \\ \hline +ht0 & ht1 & ht0 & ht1 & ht0 & ht1 & ht0 & ht1 \\ +\#0 & \#1 & \#2 & \#3 & \#4 & \#5 & \#6 & \#7 \\ +\end{tabular} +\end{center} + + +Having multiple vcpus belonging to the same domain mapped to the same +physical CPU is very likely to lead to poor performance. It's better to +use `vcpus-set' to hot-unplug one of the vcpus and ensure the others are +pinned on different CPUs. + +If you are running IO intensive tasks, its typically better to dedicate +either a hyperthread or whole core to running domain 0, and hence pin +other domains so that they can't use CPU 0. If your workload is mostly +compute intensive, you may want to pin vcpus such that all physical CPU +threads are available for guest domains. + +\chapter{Migrating Domains} + +\section{Domain Save and Restore} + +The administrator of a Xen system may suspend a virtual machine's +current state into a disk file in domain~0, allowing it to be resumed at +a later time. + +For example you can suspend a domain called ``VM1'' to disk using the +command: +\begin{verbatim} +# xm save VM1 VM1.chk +\end{verbatim} + +This will stop the domain named ``VM1'' and save its current state +into a file called \path{VM1.chk}. + +To resume execution of this domain, use the \path{xm restore} command: +\begin{verbatim} +# xm restore VM1.chk +\end{verbatim} + +This will restore the state of the domain and resume its execution. +The domain will carry on as before and the console may be reconnected +using the \path{xm console} command, as described earlier. + +\section{Migration and Live Migration} + +Migration is used to transfer a domain between physical hosts. There +are two varieties: regular and live migration. The former moves a +virtual machine from one host to another by pausing it, copying its +memory contents, and then resuming it on the destination. The latter +performs the same logical functionality but without needing to pause +the domain for the duration. In general when performing live migration +the domain continues its usual activities and---from the user's +perspective---the migration should be imperceptible. + +To perform a live migration, both hosts must be running Xen / \xend\ and +the destination host must have sufficient resources (e.g.\ memory +capacity) to accommodate the domain after the move. Furthermore we +currently require both source and destination machines to be on the same +L2 subnet. + +Currently, there is no support for providing automatic remote access +to filesystems stored on local disk when a domain is migrated. +Administrators should choose an appropriate storage solution (i.e.\ +SAN, NAS, etc.) to ensure that domain filesystems are also available +on their destination node. GNBD is a good method for exporting a +volume from one machine to another. iSCSI can do a similar job, but is +more complex to set up. + +When a domain migrates, it's MAC and IP address move with it, thus it is +only possible to migrate VMs within the same layer-2 network and IP +subnet. If the destination node is on a different subnet, the +administrator would need to manually configure a suitable etherip or IP +tunnel in the domain~0 of the remote node. + +A domain may be migrated using the \path{xm migrate} command. To live +migrate a domain to another machine, we would use the command: + +\begin{verbatim} +# xm migrate --live mydomain destination.ournetwork.com +\end{verbatim} + +Without the \path{--live} flag, \xend\ simply stops the domain and +copies the memory image over to the new node and restarts it. Since +domains can have large allocations this can be quite time consuming, +even on a Gigabit network. With the \path{--live} flag \xend\ attempts +to keep the domain running while the migration is in progress, resulting +in typical down times of just 60--300ms. + +For now it will be necessary to reconnect to the domain's console on the +new machine using the \path{xm console} command. If a migrated domain +has any open network connections then they will be preserved, so SSH +connections do not have this limitation. + + +%% Chapter Securing Xen +\chapter{Securing Xen} + +This chapter describes how to secure a Xen system. It describes a number +of scenarios and provides a corresponding set of best practices. It +begins with a section devoted to understanding the security implications +of a Xen system. + + +\section{Xen Security Considerations} + +When deploying a Xen system, one must be sure to secure the management +domain (Domain-0) as much as possible. If the management domain is +compromised, all other domains are also vulnerable. The following are a +set of best practices for Domain-0: + +\begin{enumerate} +\item \textbf{Run the smallest number of necessary services.} The less + things that are present in a management partition, the better. + Remember, a service running as root in the management domain has full + access to all other domains on the system. +\item \textbf{Use a firewall to restrict the traffic to the management + domain.} A firewall with default-reject rules will help prevent + attacks on the management domain. +\item \textbf{Do not allow users to access Domain-0.} The Linux kernel + has been known to have local-user root exploits. If you allow normal + users to access Domain-0 (even as unprivileged users) you run the risk + of a kernel exploit making all of your domains vulnerable. +\end{enumerate} + +\section{Driver Domain Security Considerations} +\label{s:ddsecurity} + +Driver domains address a range of security problems that exist regarding +the use of device drivers and hardware. On many operating systems in common +use today, device drivers run within the kernel with the same privileges as +the kernel. Few or no mechanisms exist to protect the integrity of the kernel +from a misbehaving (read "buggy") or malicious device driver. Driver +domains exist to aid in isolating a device driver within its own virtual +machine where it cannot affect the stability and integrity of other +domains. If a driver crashes, the driver domain can be restarted rather than +have the entire machine crash (and restart) with it. Drivers written by +unknown or untrusted third-parties can be confined to an isolated space. +Driver domains thus address a number of security and stability issues with +device drivers. + +However, due to limitations in current hardware, a number of security +concerns remain that need to be considered when setting up driver domains (it +should be noted that the following list is not intended to be exhaustive). + +\begin{enumerate} +\item \textbf{Without an IOMMU, a hardware device can DMA to memory regions + outside of its controlling domain.} Architectures which do not have an + IOMMU (e.g. most x86-based platforms) to restrict DMA usage by hardware + are vulnerable. A hardware device which can perform arbitrary memory reads + and writes can read/write outside of the memory of its controlling domain. + A malicious or misbehaving domain could use a hardware device it controls + to send data overwriting memory in another domain or to read arbitrary + regions of memory in another domain. +\item \textbf{Shared buses are vulnerable to sniffing.} Devices that share + a data bus can sniff (and possible spoof) each others' data. Device A that + is assigned to Domain A could eavesdrop on data being transmitted by + Domain B to Device B and then relay that data back to Domain A. +\item \textbf{Devices which share interrupt lines can either prevent the + reception of that interrupt by the driver domain or can trigger the + interrupt service routine of that guest needlessly.} A devices which shares + a level-triggered interrupt (e.g. PCI devices) with another device can + raise an interrupt and never clear it. This effectively blocks other devices + which share that interrupt line from notifying their controlling driver + domains that they need to be serviced. A device which shares an + any type of interrupt line can trigger its interrupt continually which + forces execution time to be spent (in multiple guests) in the interrupt + service routine (potentially denying time to other processes within that + guest). System architectures which allow each device to have its own + interrupt line (e.g. PCI's Message Signaled Interrupts) are less + vulnerable to this denial-of-service problem. +\item \textbf{Devices may share the use of I/O memory address space.} Xen can + only restrict access to a device's physical I/O resources at a certain + granularity. For interrupt lines and I/O port address space, that + granularity is very fine (per interrupt line and per I/O port). However, + Xen can only restrict access to I/O memory address space on a page size + basis. If more than one device shares use of a page in I/O memory address + space, the domains to which those devices are assigned will be able to + access the I/O memory address space of each other's devices. +\end{enumerate} + + +\section{Security Scenarios} + + +\subsection{The Isolated Management Network} + +In this scenario, each node has two network cards in the cluster. One +network card is connected to the outside world and one network card is a +physically isolated management network specifically for Xen instances to +use. + +As long as all of the management partitions are trusted equally, this is +the most secure scenario. No additional configuration is needed other +than forcing Xend to bind to the management interface for relocation. + + +\subsection{A Subnet Behind a Firewall} + +In this scenario, each node has only one network card but the entire +cluster sits behind a firewall. This firewall should do at least the +following: + +\begin{enumerate} +\item Prevent IP spoofing from outside of the subnet. +\item Prevent access to the relocation port of any of the nodes in the + cluster except from within the cluster. +\end{enumerate} + +The following iptables rules can be used on each node to prevent +migrations to that node from outside the subnet assuming the main +firewall does not do this for you: + +\begin{verbatim} +# this command disables all access to the Xen relocation +# port: +iptables -A INPUT -p tcp --destination-port 8002 -j REJECT + +# this command enables Xen relocations only from the specific +# subnet: +iptables -I INPUT -p tcp -{}-source 192.0.2.0/24 \ + --destination-port 8002 -j ACCEPT +\end{verbatim} + +\subsection{Nodes on an Untrusted Subnet} + +Migration on an untrusted subnet is not safe in current versions of Xen. +It may be possible to perform migrations through a secure tunnel via an +VPN or SSH. The only safe option in the absence of a secure tunnel is to +disable migration completely. The easiest way to do this is with +iptables: + +\begin{verbatim} +# this command disables all access to the Xen relocation port +iptables -A INPUT -p tcp -{}-destination-port 8002 -j REJECT +\end{verbatim} + +%% Chapter Xen Mandatory Access Control Framework +\chapter{sHype/Xen Access Control} +The Xen mandatory access control framework is an implementation of the +sHype Hypervisor Security Architecture +(www.research.ibm.com/ssd\_shype). It permits or denies communication +and resource access of domains based on a security policy. The +mandatory access controls are enforced in addition to the Xen core +controls, such as memory protection. They are designed to remain +transparent during normal operation of domains (policy-conform +behavior) but to intervene when domains move outside their intended +sharing behavior. This chapter will describe how the sHype access +controls in Xen can be configured to prevent viruses from spilling +over from one into another workload type and secrets from leaking from +one workload type to another. sHype/Xen depends on the correct +behavior of Domain-0 (cf previous chapter). + +Benefits of configuring sHype/ACM in Xen include: +\begin{itemize} +\item robust workload and resource protection effective against rogue + user domains +\item simple, platform- and operating system-independent security + policies (ideal for heterogeneous distributed environments) +\item safety net with minimal performance overhead in case operating + system security is missing, does not scale, or fails +\end{itemize} + +These benefits are very valuable because today's operating systems +become increasingly complex and often have no or insufficient +mandatory access controls. (Discretionary access controls, supported +by most operating systems, are not effective against viruses or +misbehaving programs.) Where mandatory access control exists (e.g., +SELinux), they usually deploy platform-specific, complex, and difficult +to understand security policies. Multi-tier applications in business +environments typically require different operating systems +(e.g., AIX, Windows, Linux) in different tiers. Related distributed +transactions and workloads cannot be easily protected on the OS level. +The Xen access control framework steps in to offer a coarse-grained +but very robust and consistent security layer and safety net across +different platforms and operating systems. + +To control sharing between domains, Xen mediates all inter-domain +communication (shared memory, events) as well as the access of domains +to resources such as storage disks. Thus, Xen can confine distributed +workloads (domain payloads) by permitting sharing among domains +running the same type of workload and denying sharing between pairs of +domains that run different workload types. We assume that--from a Xen +perspective--only one workload type is running per user domain. To +enable Xen to associate domains and resources with workload types, +security labels including the workload types are attached to domains +and resources. These labels and the hypervisor sHype controls cannot +be manipulated or bypassed by user domains and are effective even +against compromised or rogue domains. + +\section{Overview} +This section gives an overview of how workloads can be protected using +the sHype mandatory access control framework in Xen. +Figure~\ref{fig:acmoverview} shows the necessary steps in activating +the Xen workload protection. These steps are described in detail in +Section~\ref{section:acmexample}. + +\begin{figure} +\centering +\includegraphics[width=13cm]{figs/acm_overview.eps} +\caption{Overview of activating sHype workload protection in Xen. + Section numbers point to representative examples.} +\label{fig:acmoverview} +\end{figure} + +First, the sHype/ACM access control must be enabled in the Xen +distribution and the distribution must be built and installed (cf +Subsection~\ref{subsection:acmexampleconfigure}). Before we can +enforce security, a Xen security policy must be created (cf +Subsection~\ref{subsection:acmexamplecreate}) and deployed (cf +Subsection~\ref{subsection:acmexampleinstall}). This policy defines +the workload types differentiated during access control. It also +defines the rules that compare workload types of domains and resources +to decide about access requests. Workload types are represented by +security labels that can be securely associated to domains and resources (cf +Subsections~\ref{subsection:acmexamplelabeldomains} +and~\ref{subsection:acmexamplelabelresources}). The functioning of +the active sHype/Xen workload protection is demonstrated using simple +resource assignment, and domain creation tests in +Subsection~\ref{subsection:acmexampletest}. +Section~\ref{section:acmpolicy} describes the syntax and semantics of +the sHype/Xen security policy in detail and introduces briefly the +tools that are available to help you create your own sHype security policies. + +The next section describes all the necessary steps to create, deploy, +and test a simple workload protection policy. It is meant to enable +Xen users and developers to quickly try out the sHype/Xen workload +protection. Those readers who are interested in learning more about +how the sHype access control in Xen works and how it is configured +using the XML security policy should read Section~\ref{section:acmpolicy} +as well. Section~\ref{section:acmlimitations} concludes this chapter with +current limitations of the sHype implementation for Xen. + +\section{Xen Workload Protection Step-by-Step} +\label{section:acmexample} + +You are about to configure and deploy the Xen sHype workload protection +by following 5 simple steps: +\begin{itemize} +\item configure and install sHype/Xen +\item create a simple workload protection security policy +\item deploy the sHype/Xen security policy +\item associate domains and resources with workload labels, +\item test the workload protection +\end{itemize} +The essential commands to create and deploy an sHype/Xen security +policy are numbered throughout the following sections. If you want a +quick-guide or return at a later time to go quickly through this +demonstration, simply look for the numbered commands and apply them in +order. + +\subsection{Configuring/Building sHype Support into Xen} +\label{subsection:acmexampleconfigure} +First, we need to configure the access control module in Xen and +install the ACM-enabled Xen hypervisor. This step installs security +tools and compiles sHype/ACM controls into the Xen hypervisor. + +To enable sHype/ACM in Xen, please edit the Config.mk file in the top +Xen directory. + +\begin{verbatim} + (1) In Config.mk + Change: XSM_ENABLE ?= n + To: XSM_ENABLE ?= y + + Change: ACM_SECURITY ?= n + To: ACM_SECURITY ?= y +\end{verbatim} + +Then install the security-enabled Xen environment as follows: + +\begin{verbatim} + (2) # make world + # make install +\end{verbatim} + +Reboot into the security-enabled Xen hypervisor. + +\begin{verbatim} + (3) # reboot +\end{verbatim} + +Xen will boot into the default security policy. After reboot, +you can explore the simple DEFAULT policy. +\begin{scriptsize} +\begin{verbatim} +# xm getpolicy +Supported security subsystems : ACM +Policy name : DEFAULT +Policy type : ACM +Version of XML policy : 1.0 +Policy configuration : loaded + +# xm labels +SystemManagement + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 941 1 r----- 38.1 ACM:DEFAULT:SystemManagement +\end{verbatim} +\end{scriptsize} + +In this state, no domains can be started. +Now, a policy can be created and loaded into the hypervisor. + +\subsection{Creating A WLP Policy in 3 Simple Steps with ezPolicy} +\label{subsection:acmexamplecreate} + +We will use the ezPolicy tool to quickly create a policy that protects +workloads. You will need both the Python and wxPython packages to run +this tool. To run the tool in Domain-0, you can download the wxPython +package from www.wxpython.org or use the command \verb|yum install wxPython| +in Redhat/Fedora. To run the tool on MS Windows, you also need to download +the Python package from www.python.org. After these packages are installed, +start the ezPolicy tool with the following command: + +\begin{verbatim} + (4) # xensec_ezpolicy +\end{verbatim} + +Figure~\ref{fig:acmezpolicy} shows a screen-shot of the tool. The +following steps illustrate how you can create the workload definition +shown in Figure~\ref{fig:acmezpolicy}. You can use \verb|-h| to +pop up a help window at any time. The indicators (a), (b), and (c) in +Figure~\ref{fig:acmezpolicy} show the buttons that are used during the +3 steps of creating a policy: +\begin{enumerate} +\item defining workloads +\item defining run-time conflicts +\item translating the workload definition into an sHype/Xen access + control policy +\end{enumerate} + +\paragraph{Defining workloads.} Workloads are defined for each +organization and department that you enter in the left panel. + +To ease the transition from an unlabeled to a fully labeled workload-protection +environment, we have added support to sHype/Xen to run unlabeled domains accessing +unlabeled resources in addition to labeled domains accessing labeled resources. + +Support for running unlabeled domains on sHype/Xen is enabled by adding the +predefined workload type and label \verb|__UNLABELED__| to the security +policy. (This is a double underscore +followed by the string ''\verb|UNLABELED|'' followed by a double underscore.) +The ezPolicy tool automatically adds this organization-level workload type +to a new workload definition (cf Figure~\ref{fig:acmezpolicy}). It can simply be +deleted from the workload definition if no such support is desired. If unlabeled domains +are supported in the policy, then any domain or resource that has no label will implicitly +inherit this label when access control decisions are made. In effect, unlabeled +domains and resources define a new workload type \verb|__UNLABELED__|, which is +confined from any other labeled workload. + +Please use now the ``New Org'' button to add the organization workload types +``A-Bank'', ``B-Bank'', and ``AutoCorp''. + +You can refine an organization to differentiate between multiple +department workloads by right-clicking the organization and selecting +\verb|Add Department| (or selecting an organization and pressing +\verb|-a|). Create department workloads ``SecurityUnderwriting'', +and ``MarketAnalysis'' for the ``A-Bank''. The resulting layout of the +tool should be similar to the left panel shown in +Figure~\ref{fig:acmezpolicy}. + +\begin{figure}[htb] +\centering +\includegraphics[width=13cm]{figs/acm_ezpolicy_gui.eps} +\caption{Final layout including workload definition and Run-time Exclusion rules.} +\label{fig:acmezpolicy} +\end{figure} + +\paragraph{Defining run-time conflicts.} Workloads that shall be +prohibited from running concurrently on the same hypervisor platform +are grouped into ``Run-time Exclusion rules'' on the right panel of +the window. Cautious users should include the \verb|__UNLABELED__| +workload type in all run-time exclusion rules because any workload +could run inside unlabeled domains. + +To prevent A-Bank and B-Bank workloads (including their +departmental workloads) from running simultaneously on the same +hypervisor system, select the organization ``A-Bank'' and, while +pressing the \verb||-key, select the organization ``B-Bank''. +Being cautious, we also prevent unlabeled workloads from running with +any of those workloads by pressing the \verb||-key and selecting +``\_\_UNLABELED\_\_''. Now press the button named ``Create run-time exclusion +rule from selection''. A popup window will ask for the name for this run-time +exclusion rule (enter a name or just hit \verb||). A rule will +appear on the right panel. The name is used as reference only and does +not affect access control decisions. + +Please repeat this process to create another run-time exclusion rule +for the department workloads ``A-Bank.SecurityUnderwriting'', +``A-Bank.MarketAnalysis''. Also add the ``\_\_UNLABELED\_\_'' +workload type to this conflict set. + +The resulting layout of your window should be similar to +Figure~\ref{fig:acmezpolicy}. Save this workload definition by +selecting ``Save Workload Definition as ...'' in the ``File'' menu. +This workload definition can be later refined if required. + +\paragraph{Translating the workload definition into an sHype/Xen access + control policy.} To translate the workload definition into a access +control policy understood by Xen, please select the ``Save as Xen ACM +Security Policy'' in the ``File'' menu. Enter the following policy +name in the popup window: \verb|mytest|. If you are running ezPolicy in +Domain-0, the resulting policy file mytest\_security-policy.xml will +automatically be placed into the right directory (/etc/xen/acm-security/policies/). +If you run the tool on another system, then you need to copy the +resulting policy file into Domain-0 before continuing. See +Section~\ref{subsection:acmnaming} for naming conventions of security +policies. + +\begin{scriptsize} +\textbf{Note:} The support for \verb|__UNLABELED__| domains and +resources is meant to help transitioning from an uncontrolled +environment to a workload-protected environment by starting with +unlabeled domains and resources and then step-by-step labeling domains +and resources. Once all workloads are labeled, the \verb|__UNLABELED__| +type can simply be removed from the Domain-0 label or from the policy +through a policy update. Section~\ref{subsection:acmpolicymanagement} will +show how unlabeled domains can be disabled by updating the +\verb|mytest| policy at run-time. +\end{scriptsize} + +\subsection{Deploying a WLP Policy} +\label{subsection:acmexampleinstall} +To deploy the workload protection policy we created in +Section~\ref{subsection:acmexamplecreate}, we create a policy +representation (mytest.bin), load it into the Xen +hypervisor, and configure Xen to also load this policy during +reboot. + +The following command translates the source policy representation +into a format that can be loaded into Xen with sHype/ACM support, +activates the policy, and configures this policy for future boot +cycles into the boot sequence. Please refer to the \verb|xm| +man page for further details: + +\begin{verbatim} + (5) # xm setpolicy ACM mytest + Successfully set the new policy. + Supported security subsystems : ACM + Policy name : mytest + Policy type : ACM + Version of XML policy : 1.0 + Policy configuration : loaded, activated for boot +\end{verbatim} + +Alternatively, if installing the policy fails (e.g., because it cannot +identify the Xen boot entry), you can manually install the policy in 3 +steps a-c. + +(\textit{Alternatively to 5 - step a}) Manually copy the policy binary +file into the boot directory: + +\begin{scriptsize} +\begin{verbatim} +# cp /etc/xen/acm-security/policies/mytest.bin /boot/mytest.bin +\end{verbatim} +\end{scriptsize} + +(\textit{Alternatively to 5 - step b}) Manually add a module line to your +Xen boot entry so that grub loads this policy file during startup: + +\begin{scriptsize} +\begin{verbatim} +title XEN Devel with 2.6.18.8 + kernel /xen.gz + module /vmlinuz-2.6.18.8-xen root=/dev/sda3 ro console=tty0 + module /initrd-2.6.18.8-xen.img + module /mytest.bin +\end{verbatim} +\end{scriptsize} + +(\textit{Alternatively to 5 - step c}) Reboot. Xen will choose the +bootstrap label defined in the policy as Domain-0 label during reboot. +After reboot, you can re-label Domain-0 at run-time, +cf Section~\ref{subsection:acmlabeldom0}. + +Assuming that command (5) succeeded or you followed the alternative +instructions above, you should see the new policy and label appear +when listing domains: + +\begin{scriptsize} +\begin{verbatim} +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 941 1 r----- 81.5 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +If the security label at the end of the line says ``INACTIVE'' then the +security is not enabled. Verify the previous steps. Note: Domain-0 is +assigned a default label (see \verb|bootstrap| policy attribute +explained in Section~\ref{section:acmpolicy}). All other domains must +be explicitly labeled, which we describe in detail below. + +\subsection{Labeling Unmanaged User Domains} +\label{subsection:acmexamplelabeldomains} + +Unmanaged domains are started in Xen by using a configuration +file. Please refer to Section~\ref{subsection:acmlabelmanageddomains} +if you are using managed domains. + +The following configuration file defines \verb|domain1|: + +\begin{scriptsize} +\begin{verbatim} +# cat domain1.xm +kernel= "/boot/vmlinuz-2.6.18.8-xen" +memory = 128 +name = "domain1" +vif = [''] +dhcp = "dhcp" +disk = ['file:/home/xen/dom_fc5/fedora.fc5.img,sda1,w', \ + 'file:/home/xen/dom_fc5/fedora.fc5.swap,sda2,w'] +root = "/dev/sda1 ro xencons=tty" +\end{verbatim} +\end{scriptsize} + +Every domain must be associated with a security label before it can start +on sHype/Xen. Otherwise, sHype/Xen would not be able to enforce the policy +consistently. Our \verb|mytest| policy is configured so that Xen +assigns a default label \verb|__UNLABELED__| to domains and resources that +have no label and supports them in a controlled manner. Since neither the domain, +nor the resources are (yet) labeled, this domain can start under the \verb|mytest| +policy: + +\begin{scriptsize} +\begin{verbatim} +# xm create domain1.xm +Using config file "./domain1.xm". +Started domain domain1 + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 1 128 1 -b---- 0.7 ACM:mytest:__UNLABELED__ +Domain-0 0 875 1 r----- 84.6 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +Please shutdown domain1 so that we can move it into the protection +domain of workload \verb|A-Bank|. + +\begin{scriptsize} +\begin{verbatim} +# xm shutdown domain1 +(wait some seconds until the domain has shut down) + +#xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 875 1 r----- 86.4 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +We assume that the processing in domain1 contributes to the \verb|A-Bank| workload. +We explore now how to transition this domain into the ``A-Bank'' workload-protection. +The following command prints all domain labels available in the active policy: + +\begin{scriptsize} +\begin{verbatim} +# xm labels +A-Bank +A-Bank.MarketAnalysis +A-Bank.SecurityUnderwriting +AutoCorp +B-Bank +SystemManagement +__UNLABELED__ +\end{verbatim} +\end{scriptsize} + +Now label \verb|domain1| with the A-Bank label and another \verb|domain2| +with the B-Bank label. Please refer to the xm man page for +further information. + +\begin{verbatim} + (6) # xm addlabel A-Bank dom domain1.xm + # xm addlabel B-Bank dom domain2.xm +\end{verbatim} + +Let us try to start the domain again: + +\begin{scriptsize} +\begin{verbatim} +# xm create domain1.xm +Using config file "./domain1.xm". +Error: VM's access to block device 'file:/home/xen/dom_fc5/fedora.fc5.img' denied +\end{verbatim} +\end{scriptsize} + +This error indicates that \verb|domain1|, if started, would not be able to +access its image and swap files because they are not labeled. This +makes sense because to confine workloads, access of domains to +resources must be controlled. Otherwise, domains that are not allowed +to communicate or run simultaneously could share data through storage +resources. + +\subsection{Labeling Resources} +\label{subsection:acmexamplelabelresources} +You can use the \verb|xm labels type=res| command to list available +resource labels. Let us assign the A-Bank resource label to the +\verb|domain1| image file representing \verb|/dev/sda1| and to its swap file: + +\begin{verbatim} + (7) # xm addlabel A-Bank res \ + file:/home/xen/dom_fc5/fedora.fc5.img + + # xm addlabel A-Bank res \ + file:/home/xen/dom_fc5/fedora.fc5.swap +\end{verbatim} + +The following command lists all labeled resources on the system, e.g., +to lookup or verify the labeling: + +\begin{scriptsize} +\begin{verbatim} +# xm resources +file:/home/xen/dom_fc5/fedora.fc5.swap + type: ACM + policy: mytest + label: A-Bank +file:/home/xen/dom_fc5/fedora.fc5.img + type: ACM + policy: mytest + label: A-Bank +\end{verbatim} +\end{scriptsize} + +Starting \verb|domain1| will now succeed: + +\begin{scriptsize} +\begin{verbatim} +# xm create domain1.xm +Using config file "./domain1.xm". +Started domain domain1 + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 3 128 1 -b---- 0.8 ACM:mytest:A-Bank +Domain-0 0 875 1 r----- 90.9 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +Currently, if a labeled resource is moved to another location, the +label must first be manually removed, and after the move re-attached +using the xm commands \verb|rmlabel| and \verb|addlabel| +respectively. Please see Section~\ref{section:acmlimitations} for +further details. + +\begin{verbatim} + (8) Label the resources of domain2 as B-Bank + but please do not start this domain yet. +\end{verbatim} + +\subsection{Testing The Xen Workload Protection} +\label{subsection:acmexampletest} + +We are about to demonstrate the sHype/Xen workload protection by verifying +\begin{itemize} +\item that user domains with conflicting workloads cannot run + simultaneously +\item that user domains cannot access resources of workloads other than the + one they are associated with +\item that user domains cannot exchange network packets if they are not + associated with the same workload type (not yet supported in Xen) +\end{itemize} + +\paragraph{Test 1: Run-time exclusion rules.} We assume that \verb|domain1| +with the A-Bank label is still running. While \verb|domain1| is running, +the run-time exclusion set of our policy implies that \verb|domain2| cannot +start because the label of \verb|domain1| includes the CHWALL type A-Bank +and the label of \verb|domain2| includes the CHWALL type B-Bank. The +run-time exclusion rule of our policy enforces that A-Bank and +B-Bank cannot run at the same time on the same hypervisor platform. +Once domain1 is stopped, saved, or migrated to another platform, +\verb|domain2| can start. Once \verb|domain2| is started, however, +\verb|domain1| can no longer start or resume on this system. When creating the +Chinese Wall types for the workload labels, the ezPolicy tool policy +translation component ensures that department workloads inherit all the +organization types (and with it any organization exclusions). + +\begin{scriptsize} +\begin{verbatim} +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 3 128 1 -b---- 0.8 ACM:mytest:A-Bank +Domain-0 0 875 1 r----- 90.9 ACM:mytest:SystemManagement + +# xm create domain2.xm +Using config file "./domain2.xm". +Error: 'Domain in conflict set with running domains' + +# xm shutdown domain1 +(wait some seconds until domain 1 is shut down) + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 873 1 r----- 95.3 ACM:mytest:SystemManagement + +# xm create domain2.xm +Using config file "./domain2.xm". +Started domain domain2 + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain2 5 164 1 -b---- 0.3 ACM:mytest:B-Bank +Domain-0 0 839 1 r----- 96.4 ACM:mytest:SystemManagement + +# xm create domain1.xm +Using config file "domain1.xm". +Error: 'Domain in conflict with running domains' + +# xm shutdown domain2 +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 839 1 r----- 97.8 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +You can verify that domains with AutoCorp label can run together with +domains labeled A-Bank or B-Bank. + +\paragraph{Test2: Resource access.} In this test, we will re-label the +swap file for \verb|domain1| with the \verb|B-Bank| resource label. In a +real environment, the swap file must be sanitized (scrubbed/zeroed) before +it is reassigned to prevent data leaks from the A-Bank to the B-Bank workload +through the swap file. + +We expect that \verb|domain1| will no longer start because it cannot access +this resource. This test checks the sharing abilities of domains, which are +defined by the Simple Type Enforcement Policy component. + +\begin{scriptsize} +\begin{verbatim} +# xm rmlabel res file:/home/xen/dom_fc5/fedora.fc5.swap + +# xm addlabel B-Bank res file:/home/xen/dom_fc5/fedora.fc5.swap + +# xm resources +file:/home/xen/dom_fc5/fedora.fc5.swap + type: ACM + policy: mytest + label: B-Bank +file:/home/xen/dom_fc5/fedora.fc5.img + type: ACM + policy: mytest + label: A-Bank + +# xm create domain1.xm +Using config file "./domain1.xm". +Error: +VM's access to block device 'file:/home/xen/dom_fc5/fedora.fc5.swap' denied +\end{verbatim} +\end{scriptsize} + +The resource authorization checks are performed before the domain is actually started +so that failures during the startup are prevented. A domain is only started if all +the resources specified in its configuration are accessible. + +\paragraph{Test 3: Communication.} In this test we would verify that +two domains with labels A-Bank and B-Bank cannot exchange network packets +by using the 'ping' connectivity test. It is also related to the STE +policy. {\bf Note:} sHype/Xen does control direct communication between +domains. However, domains associated with different workloads can +currently still communicate through the Domain-0 virtual network. We +are working on the sHype/ACM controls for local and remote network +traffic through Domain-0. Please monitor the xen-devel mailing list +for updated information. + + +\subsection{Labeling Domain-0 --or-- Restricting System Authorization} +\label{subsection:acmlabeldom0} +The major use case for explicitly labeling or relabeling Domain-0 is to restrict +or extend which workload types can run on a virtualized Xen system. This enables +flexible partitioning of the physical infrastructure as well as the workloads +running on it in a multi-platform environment. + +In case no Domain-0 label is explicitly stated, we automatically assigned Domain-0 +the \verb|SystemManagement| label, which includes all STE (workload) types that +are known to the policy. In effect, the Domain-0 label authorizes the Xen system +to run only those workload types, whose STE types are included in the Domain-0 +label. Hence, choosing the \verb|SystemManagement| label for Domain-0 permits any +labeled domain to run. Resetting the label for Domain-0 at boot or run-time to +a label with a subset of the known STE workload types restricts which user domains +can run on this system. If Domain-0 is relabeled at run-time, then the new label +must at least include all STE types of those domains that are currently running. +The operation fails otherwise. This requirement ensures that the system remains +in a valid security configuration after re-labelling. + +Restricting the Domain-0 authorization through the label creates a flexible +policy-driven way to strongly partition the physical infrastructure and the +workloads running on it. This partitioning will be automatically enforced during +migration, start, or resume of domains and simplifies the security management +considerably. Strongly competing workloads can be forced to run on separate physical +infrastructure and become less depend on the domain isolation capabilities +of the hypervisor. + +First, we relabel the swap image back to A-Bank and then start up domain1: +\begin{scriptsize} +\begin{verbatim} +# xm rmlabel res file:/home/xen/dom_fc5/fedora.fc5.swap + +# xm addlabel A-Bank res file:/home/xen/dom_fc5/fedora.fc5.swap + +# xm create domain1.xm +Using config file "./domain1.xm". +Started domain domain1 + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 7 128 1 -b---- 0.7 ACM:mytest:A-Bank +Domain-0 0 839 1 r----- 103.1 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +The following command will restrict the Xen system to only run STE types +included in the A-Bank label. + +\begin{scriptsize} +\begin{verbatim} +# xm addlabel A-Bank mgt Domain-0 +Successfully set the label of domain 'Domain-0' to 'A-Bank'. + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 839 1 r----- 103.7 ACM:mytest:A-Bank +domain1 7 128 1 -b---- 0.7 ACM:mytest:A-Bank + +\end{verbatim} +\end{scriptsize} + +In our example policy in Figure~\ref{fig:acmxmlfileb}, this means that +only \verb|A-Bank| domains and workloads (types) can run after the +successful completion of this command because the \verb|A-Bank| label +includes only a single STE type, namely \verb|A-Bank|. This command +fails if any running domain has an STE type in its label that is not +included in the A-Bank label. + +If we now label a domain3 with AutoCorp, it cannot start because Domain-0 is +no longer authorized to run the workload type \verb|AutoCorp|. +\begin{scriptsize} +\begin{verbatim} +# xm addlabel AutoCorp dom domain3.xm + (remember to label its resources, too) + +# xm create domain3.xm +Using config file "./domain3.xm". +Error: VM is not authorized to run. + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 839 1 r----- 104.7 ACM:mytest:A-Bank +domain1 7 128 1 -b---- 0.7 ACM:mytest:A-Bank +\end{verbatim} +\end{scriptsize} + +At this point, unlabeled domains cannot start either. Let domain4.xm +describe an unlabeled domain, then trying to start domain4 +will fail: +\begin{scriptsize} +\begin{verbatim} +# xm getlabel dom domain4.xm +Error: 'Domain not labeled' + +# xm create domain4.xm +Using config file "./domain4.xm". +Error: VM is not authorized to run. +\end{verbatim} +\end{scriptsize} + +Relabeling Domain-0 with the SystemManagement label will enable domain3 to start. +\begin{scriptsize} +\begin{verbatim} +# xm addlabel SystemManagement mgt Domain-0 +Successfully set the label of domain 'Domain-0' to 'SystemManagement'. + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 7 128 1 -b---- 0.8 ACM:mytest:A-Bank +Domain-0 0 839 1 r----- 106.6 ACM:mytest:SystemManagement + +# xm create domain3.xm +Using config file "./domain3.xm". +Started domain domain3 + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 7 128 1 -b---- 0.8 ACM:mytest:A-Bank +domain3 8 164 1 -b---- 0.3 ACM:mytest:AutoCorp +Domain-0 0 711 1 r----- 107.6 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + + +\subsection{Labeling Managed User Domains} +\label{subsection:acmlabelmanageddomains} + +Xend has been extended with functionality to manage domains along with their +configuration information. Such domains are configured and started via Xen-API +calls. Since managed domains do not have an associated xm configuration file, +the existing \verb|addlabel| command, which adds the security label into a +domain's configuration file, will not work for such managed domains. + +Therefore, we have extended the \verb|xm addlabel| and \verb|xm rmlabel| +subcommands to enable adding security labels to and removing security +labels from managed domain configurations. The following example shows how +the \verb|A-Bank| label can be assigned to the xend-managed +domain configuration of \verb|domain1|. Removing labels from managed user +domain configurations works similarly. + +Below, we show a dormant configuration of the managed domain1 +with ID \verb|"-1"| and state \verb|"-----"| before labeling: +\begin{scriptsize} +\begin{verbatim} +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 -1 128 1 ------ 0.0 ACM:mytest:__UNLABELED__ +Domain-0 0 711 1 r----- 128.4 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +Now we label the managed domain: +\begin{scriptsize} +\begin{verbatim} +# xm addlabel A-Bank mgt domain1 +Successfully set the label of the dormant domain 'domain1' to 'A-Bank'. +\end{verbatim} +\end{scriptsize} + +After labeling, you can see that the security label is part of the +domain configuration: +\begin{scriptsize} +\begin{verbatim} +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 -1 128 1 ------ 0.0 ACM:mytest:A-Bank +Domain-0 0 711 1 r----- 129.7 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +This command extension does not support relabeling of individual running user domains +for several reasons. For one, because of the difficulty to revoke resources +in cases where a running domain's new label does not permit access to resources +that were accessible under the old label. Another reason is that changing the +label of a single domain of a workload is rarely a good choice and will affect +the workload isolation properties of the overall workload. + +However, the name and contents of the label associated with running domains can +be indirectly changed through a global policy change, which will update the whole +workload consistently (domains and resources), cf. +Section~\ref{subsection:acmpolicymanagement}. + +\section{Xen Access Control Policy} +\label{section:acmpolicy} + +This section describes the sHype/Xen access control policy in detail. +It gives enough information to enable the reader to write custom +access control policies and to use the available Xen policy tools. The +policy language is expressive enough to specify most symmetric access +relationships between domains and resources efficiently. + +The Xen access control policy consists of two policy components. The +first component, called Simple Type Enforcement (STE) policy, controls +the sharing between running domains, i.e., communication or access to +shared resources. The second component, called Chinese Wall (CHWALL) +policy, controls which domains can run simultaneously on the same +virtualized platform. The CHWALL and STE policy components complement +each other. The XML policy file includes all information +needed by Xen to enforce those policies. + +Figures~\ref{fig:acmxmlfilea} and \ref{fig:acmxmlfileb} show the fully +functional but very simple example Xen security policy that is created +by ezPolicy as shown in Figure~\ref{fig:acmezpolicy}. The policy can +distinguish the 6 workload types shown in lines 11-17 in +Fig.~\ref{fig:acmxmlfilea}. The whole XML Security Policy consists of +four parts: +\begin{enumerate} +\item Policy header including the policy name +\item Simple Type Enforcement block +\item Chinese Wall Policy block +\item Label definition block +\end{enumerate} + +\begin{figure} +\begin{scriptsize} +\begin{verbatim} +01 +02 +03 +04 +05 mytest +06 Mon Nov 19 22:51:56 2007 +07 1.0 +08 +09 +10 +11 SystemManagement +12 __UNLABELED__ +13 A-Bank +14 A-Bank.SecurityUnderwriting +15 A-Bank.MarketAnalysis +16 B-Bank +17 AutoCorp +18 +19 +20 +21 +22 SystemManagement +23 __UNLABELED__ +24 A-Bank +25 A-Bank.SecurityUnderwriting +26 A-Bank.MarketAnalysis +27 B-Bank +28 AutoCorp +29 +30 +31 +32 A-Bank +33 B-Bank +34 __UNLABELED__ +35 +36 +37 A-Bank.MarketAnalysis +38 A-Bank.SecurityUnderwriting +39 __UNLABELED__ +40 +41 +42 +\end{verbatim} +\end{scriptsize} +\caption{Example XML security policy file -- Part I: Types and Rules Definition.} +\label{fig:acmxmlfilea} +\end{figure} + +\subsection{Policy Header and Policy Name} +\label{subsection:acmnaming} +Lines 1-2 (cf Figure~\ref{fig:acmxmlfilea}) include the usual XML +header. The security policy definition starts in Line 3 and refers to +the policy schema. The XML-Schema definition for the Xen policy can be +found in the file +\textit{/etc/xen/acm-security/policies/security-policy.xsd}. Examples +for security policies can be found in the example subdirectory. The +acm-security directory is only installed if ACM security is configured +during installation (cf Section~\ref{subsection:acmexampleconfigure}). + +The \verb|Policy Header| spans lines 4-8. It includes a date field and +defines the policy name \verb|mytest| as well +as the version of the XML. It can also include optional fields that are +not shown and are for future use (see schema definition). + +The policy name serves two purposes: First, it provides a unique name +for the security policy. This name is also exported by the Xen +hypervisor to the Xen management tools in order to ensure that both +the Xen hypervisor and Domain-0 enforce the same policy. +We plan to extend the policy name with a +digital fingerprint of the policy contents to better protect this +correlation. Second, it implicitly points the xm tools to the +location where the XML policy file is stored on the Xen system. +Replacing the colons in the policy name by slashes yields the local +path to the policy file starting from the global policy directory +\verb|/etc/xen/acm-security/policies|. The last part of the policy +name is the prefix for the XML policy file name, completed by +\verb|-security_policy.xml|. Our example policy with the name +\verb|mytest| can be found in the XML policy file named +\verb|mytest-security_policy.xml| that is stored under the global +policy directory. Another, preinstalled example policy named +\verb|example.test| can be found in the \verb|test-security_policy.xml| +under \verb|/etc/xen/acm-security/policies/example|. + +\subsection{Simple Type Enforcement Policy Component} + +The Simple Type Enforcement (STE) policy controls which domains can +communicate or share resources. This way, Xen can enforce confinement +of workload types by confining the domains running those workload +types and their resources. The mandatory access control framework +enforces its policy when +domains access intended communication or cooperation means (shared +memory, events, shared resources such as block devices). It builds on +top of the core hypervisor isolation, which restricts the ways of +inter-communication to those intended means. STE does not protect or +intend to protect from covert channels in the hypervisor or hardware; +this is an orthogonal problem that can be mitigated by using the +Run-time Exclusion rules described above or by fixing the problem leading +to those covert channels in the core hypervisor or hardware platform. + +Xen controls sharing between domains on the resource and domain level +because this is the abstraction the hypervisor and its management +understand naturally. While this is coarse-grained, it is also very +reliable and robust and it requires minimal changes to implement +mandatory access controls in the hypervisor. It enables platform- and +operating system-independent policies as part of a layered security +approach. + +Lines 11-17 (cf Figure~\ref{fig:acmxmlfilea}) define the Simple Type +Enforcement policy component. Essentially, they define the workload +type names \verb|SystemManagement|, \verb|A-Bank|, +\verb|AutoCorp| etc. that are available in the STE policy component. The +policy rules are implicit: Xen permits two domains to communicate with +each other if and only if their security labels have at least one STE type in +common. Similarly, Xen permits a user domain to access a +resource if and only if the labels of the domain and the resource +have at least one STE workload type in common. + +\subsection{Chinese Wall Policy Component} + +The Chinese Wall security policy interpretation of sHype enables users +to prevent certain workloads from running simultaneously on the same +hypervisor platform. Run-time Exclusion rules (RER), also called +Conflict Sets or Anti-Collocation rules, define a set of workload types +that are not permitted to run simultaneously on the same virtualized +platform. Of all the workloads specified in a Run-time +Exclusion rule, at most one type can run on the same hypervisor +platform at a time. Run-time Exclusion Rules implement a less +rigorous variant of the original Chinese Wall security component. They +do not implement the *-property of the policy, which would require to +restrict also types that are not part of an exclusion rule once they +are running together with a type in an exclusion rule +(http://www.gammassl.co.uk/topics/chinesewall.html provides more information +on the original Chinese Wall policy). + +Xen considers the \verb|ChineseWallTypes| part of the label for the +enforcement of the Run-time Exclusion rules. It is illegal to define +labels including conflicting Chinese Wall types. + +Lines 20-41 (cf Figure~\ref{fig:acmxmlfilea}) define the Chinese Wall +policy component. Lines 22-28 define the known Chinese Wall types, +which coincide here with the STE types defined above. This usually +holds if the criteria for sharing among domains and sharing of the +hardware platform are the same. Lines 30-41 define one Run-time +Exclusion rules, the first of which is depicted below: + +\begin{scriptsize} +\begin{verbatim} +31 +32 A-Bank +33 B-Bank +34 __UNLABELED__ +35 +\end{verbatim} +\end{scriptsize} + +Based on this rule, Xen enforces that only one of the types +\verb|A-Bank|, \verb|B-Bank|, or \verb|__UNLABELED__| will run +on a single hypervisor platform at a time. For example, once a domain assigned a +\verb|A-Bank| workload type is started, domains with the +\verb|B-Bank| type or unlabeled domains will be denied to start. +When the former domain stops and no other domains with the \verb|A-Bank| +type are running, then domains with the \verb|B-Bank| type or unlabeled domains +can start. + +Xen maintains reference counts on each running workload type to keep +track of which workload types are running. Every time a domain starts +or resumes, the reference count on those Chinese Wall types that are +referenced in the domain's label are incremented. Every time a domain +is destroyed or saved, the reference counts of its Chinese Wall types +are decremented. sHype in Xen fully supports migration and live-migration, +which is subject to access control the same way as saving a domain on +the source platform and resuming it on the destination platform. + +Here are some reasons why users might want to restrict workloads or domains +from sharing the system hardware simultaneously: + +\begin{itemize} +\item Imperfect resource management or control might enable a compromised + user domain to starve other domains and the workload running in them. +\item Redundant user domains might run the same workload to increase + availability; such domains should not run on the same hardware to + avoid single points of failure. +\item Imperfect Xen core domain isolation might enable two rogue + domains running different workload types to use unintended and + unknown ways (covert channels) to exchange some bits of information. + This way, they bypass the policed Xen access control mechanisms. Such + imperfections cannot be completely eliminated and are a result of + trade-offs between security and other design requirements. For a + simple example of a covert channel see + http://www.multicians.org/timing-chn.html. Such covert channels + exist also between workloads running on different platforms if they + are connected through networks. The Xen Chinese Wall policy provides + an approximated ``air-gap'' between selected workload types. +\end{itemize} + +\subsection{Security Labels} + +To enable Xen to associate domains with workload types running in +them, each domain is assigned a security label that includes the +workload types of the domain. + +\begin{figure}[htb] + \begin{tabular*}{\textwidth}{@{\extracolsep{\fill}}l|l} + \begin{minipage}{0.475\textwidth} + \begin{tiny} + \begin{verbatim} + + + + SystemManagement + + SystemManagement + __UNLABELED__ + A-Bank + A-Bank.SecurityUnderwriting + A-Bank.MarketAnalysis + B-Bank + AutoCorp + + + SystemManagement + + + + __UNLABELED__ + + __UNLABELED__ + + + __UNLABELED__ + + + + A-Bank + + A-Bank + + + A-Bank + + + + A-Bank.SecurityUnderwriting + + A-Bank.SecurityUnderwriting + + + A-Bank + A-Bank.SecurityUnderwriting + + + + A-Bank.MarketAnalysis + + A-Bank.MarketAnalysis + + + A-Bank + A-Bank.MarketAnalysis + + + + B-Bank + + B-Bank + + + B-Bank + + +\end{verbatim} +\end{tiny} +\end{minipage} & +\begin{minipage}{0.475\textwidth} +\begin{tiny} +\begin{verbatim} + + AutoCorp + + AutoCorp + + + AutoCorp + + + + + + SystemManagement + + SystemManagement + + + + __UNLABELED__ + + __UNLABELED__ + + + + A-Bank + + A-Bank + + + + A-Bank.SecurityUnderwriting + + A-Bank.SecurityUnderwriting + + + + A-Bank.MarketAnalysis + + A-Bank.MarketAnalysis + + + + B-Bank + + B-Bank + + + + AutoCorp + + AutoCorp + + + + + + + + + + + + + +\end{verbatim} +\end{tiny} +\end{minipage} +\end{tabular*} +\caption{Example XML security policy file -- Part II: Label Definition.} +\label{fig:acmxmlfileb} +\end{figure} +% DO NOT MODIFY WHITESPACE ABOVE, it balances the columns +The \verb|SecurityLabelTemplate| (cf Figure~\ref{fig:acmxmlfileb}) defines +the security labels that can be associated with domains and resources when +this policy is active (use the \verb|xm labels type=any| command described in +Section~\ref{subsection:acmexamplelabeldomains} to list all available labels). + +The domain labels include +Chinese Wall types while resource labels do not include Chinese Wall types. +The \verb|SubjectLabels| policy section defines the labels that can be +assigned to domains. The VM label +\verb|A-Bank.SecurityUnderwriting| in Figure~\ref{fig:acmxmlfileb}) +associates the domain that carries it with the workload STE type +\verb|A-Bank.SecurityUnderwriting| and with the CHWALL types \verb|A-Bank| +and \verb|A-Bank.SecurityUnderwriting|. The ezPolicy tool +assumes that any department workload will inherit any conflict set that +is specified for its organization, i.e., if \verb|B-Bank| is running, not +only \verb|A-Bank| but also all its departmental workloads are prevented +from running by this first run-time exclusion set. The separation of STE +and CHWALL types in the label definition ensures that +all departmental workloads are isolated from each other and from their generic +organization workloads, while they are sharing CHWALL types to +simplify the formulation of run-time exclusion sets. + +The \verb|bootstrap| attribute of the \verb|| XML node +in our example policy shown in Figure~\ref{fig:acmxmlfileb} names +the label \verb|SystemManagement| as the label that Xen will assign +to Domain-0 at boot time (if this policy is installed as boot policy). The +label of Domain-0 can be persistently changed at run-time with the +\verb|addlabel| command, which adds an overriding option to the grub.conf +boot entry (cf Section~\ref{subsection:acmlabeldom0}). +All user domains are assigned labels according to their domain configuration +(see Section~\ref{subsection:acmexamplelabeldomains} for examples of +how to label domains). + +The \verb|ObjectLabels| depicted in Figure~\ref{fig:acmxmlfileb} can be +assigned to resources when this policy is active. + +In general, user domains should be assigned labels that have only a +single SimpleTypeEnforcement workload type. This way, workloads remain +confined even if user domains become rogue. Any domain that is +assigned a label with multiple STE types must be trusted to keep +information belonging to the different STE types separate (confined). +For example, Domain-0 is assigned the bootstrap label +\verb|SystemManagement|, which includes all existing STE types. +Therefore, Domain-0 must take care not to enable unauthorized +information flow (eg. through block devices or virtual networking) +between domains or resources that are assigned different STE types. + +Security administrators simply use the name of a label (specified in +the \verb|| field) to associate a label with a domain (cf. +Section~\ref{subsection:acmexamplelabeldomains}). The types inside the +label are used by the Xen access control enforcement. While the name +can be arbitrarily chosen (as long as it is unique), it is advisable +to choose the label name in accordance to the security types included. +Similarly, the STE and CHWALL types should be named according to the +workloads they represent. While the XML representation of the label +in the above example seems unnecessary flexible, labels in general +must be able to include multiple types. + +We assume in the following example, that \verb|A-Bank.SecurityUnderwriting| and +\verb|A-Bank.MarketAnalysis| workloads use virtual disks that are provided +by a virtual I/O domain hosting a physical storage device and carrying +the following label: + +\begin{scriptsize} +\begin{verbatim} + + VIOServer + + A-Bank + A-Bank.SecurityUnderwriting + A-Bank.MarketAnalysis + VIOServer + + + VIOServer + + +\end{verbatim} +\end{scriptsize} + +This Virtual I/O domain (VIO) exports its virtualized disks by +communicating to all domains labeled with the +\verb|A-Bank.SecurityUnderwriting|, the \verb|A-Bank|, or the +\verb|A-Bank.MarketAnalysis| label. This requires the +VIO domain to carry those STE types. In addition, this label includes a +new \verb|VIOServer| type that can be used to restrict direct access to the +physical storage resource to the VIODomain. + +In this example, the confinement of these A-Bank workloads depends on the +VIO domain that must keep the data of those different workloads separate. +The virtual disks are labeled as well to keep track of their assignments +to workload types (see Section~\ref{subsection:acmexamplelabelresources} +for labeling resources) and enforcement functions inside the VIO +domain must ensure that the labels of the domain mounting a virtual +disk and the virtual disk label share a common STE type. The VIO label +carrying its own VIOServer CHWALL type introduces the flexibility to +permit the trusted VIO server to run together with \verb|A-Bank.SecurityUnderwriting| +or \verb|A-Bank.MarketAnalysis| workloads. + +Alternatively, a system that has two hard-drives does not need a VIO +domain but can directly assign one hardware storage device to each of +the workloads if the platform offers an IO-MMU, cf +Section~\ref{s:ddsecurity}. Sharing hardware through virtualized devices +is a trade-off between the amount of trusted code (size of the trusted +computing base) and the amount of acceptable over-provisioning. This +holds both for peripherals and for system platforms. + + +\subsection{Managing sHype/Xen Security Policies at Run-time} +\label{subsection:acmpolicymanagement} + +\subsubsection{Removing the sHype/Xen Security Policy} +When resetting the policy, no labeled domains can be running. +Please stop or shutdown all running labeled domains. Then you can reset +the policy to the default policy using the \verb|resetpolicy| command: + +\begin{scriptsize} +\begin{verbatim} +# xm getpolicy +Supported security subsystems : ACM +Policy name : mytest +Policy type : ACM +Version of XML policy : 1.0 +Policy configuration : loaded, activated for boot + +# xm resetpolicy +Successfully reset the system's policy. + +# xm getpolicy +Supported security subsystems : ACM +Policy name : DEFAULT +Policy type : ACM +Version of XML policy : 1.0 +Policy configuration : loaded + +# xm resources +file:/home/xen/dom_fc5/fedora.fc5.swap + type: INV_ACM + policy: mytest + label: A-Bank +file:/home/xen/dom_fc5/fedora.fc5.img + type: INV_ACM + policy: mytest + label: A-Bank +\end{verbatim} +\end{scriptsize} + +As the \verb|xm resources| output shows, all resource labels have +invalidated type information but their semantics remain associated +with the resources so that they can later on either be relabeled +with semantically equivalent labels or sanitized and reused +(storage resources). + +At this point, the system is in the same initial state as after +configuring XSM and sHype/ACM and rebooting the system without +a specific policy. No user domains can run. + +\subsubsection{Changing to a Different sHype/Xen Security Policy} +The easiest way to change to a different, unrelated policy is to reset the system +policy and then set the new policy. Please consider that the existing +domain and resource labels become invalid at this point. Please refer +to the next section for an example of how to seamlessly update an +active policy at run-time without invalidating labels. + +\begin{scriptsize} +\begin{verbatim} +# xm resetpolicy +Successfully reset the system's policy. + +# xm setpolicy ACM example.test +Successfully set the new policy. +Supported security subsystems : ACM +Policy name : example.test +Policy type : ACM +Version of XML policy : 1.0 +Policy configuration : loaded, activated for boot + +# xm labels +CocaCola +PepsiCo +SystemManagement +VIO +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 873 1 r----- 56.3 ACM:example.test:SystemManagement + +# xm resetpolicy +Successfully reset the system's policy. + +# xm getpolicy +Supported security subsystems : ACM +Policy name : DEFAULT +Policy type : ACM +Version of XML policy : 1.0 +Policy configuration : loaded + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 873 1 r----- 57.2 ACM:DEFAULT:SystemManagement + +# xm setpolicy ACM mytest +Successfully set the new policy. +Supported security subsystems : ACM +Policy name : mytest +Policy type : ACM +Version of XML policy : 1.0 +Policy configuration : loaded, activated for boot + +# xm labels +A-Bank +A-Bank.MarketAnalysis +A-Bank.SecurityUnderwriting +AutoCorp +B-Bank +SystemManagement +__UNLABELED__ + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +Domain-0 0 873 1 r----- 58.0 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +The described way of changing policies by resetting the existing +policy is useful for testing different policies. For real deployment +environments, a policy update as described in the following section +is more appropriate and can be applied seamlessly at run-time while +user domains are running. + +\subsubsection{Update an sHype/Xen Security Policy at Run-time} + +Once an ACM security policy is activated (loaded into the Xen +hypervisor), the policy may be updated at run-time without the +need to re-boot the system. The XML update-policy contains several +additional information fields that are required to safely link the +new policy contents to the old policy and ensure a consistent +transformation of the system security state from the old to the +new policy. Those additional fields are required for policies that +are updating an existing policy at run-time. + +The major benefit of policy updates is the ability to add, delete, +or rename workload types, labels, and conflict sets (run-time +exclusion rules) to accommodate changes in the managed virtual +environment without the need to reboot the Xen system. When a +new policy renames labels of the current policy, the labels +attached to resources and domains are automatically updated +during a successful policy update. + +We have manually crafted an update policy for the \verb|mytest| +security policy and stored it in the file mytest\_update-security\_policy.xml +in the policies directory. We will discuss this policy in detail before +using it to update a running sHype/Xen system. The following figures contain +the whole contents of the update policy file. + +Figure~\ref{fig:acmupdateheader} shows the policy +header of an update-policy and the new \verb|FromPolicy| XML +node. For the policy update to succeed, the policy name and the +policy version fields of the \verb|FromPolicy| XML node must +exactly match those of the currently enforced policy. This +ensures a controlled update path of the policy. + +\begin{figure}[htb] +\begin{scriptsize} +\begin{verbatim} + + + + + mytest + Tue Nov 27 21:53:45 2007 + 1.1 + + mytest + 1.0 + + +\end{verbatim} +\end{scriptsize} +\caption{XML security policy update -- Part I: Updated Policy Header.} +\label{fig:acmupdateheader} +\end{figure} + +The version number of the new policy, which is shown in the +node following the \verb|Date| node, must be a logical increment +to the current policy's version. Therefore at least the minor +number of the policy version must be incremented. This ensures +that a policy update is applied only to exactly the policy for +which this update was created and minimizes unforseen side-effects + of policy updates. + +\paragraph{Types and Conflic Sets} +The type names and the assignment of types to labels or conflict +sets (run-time exclusion rules) can +simply be changed consistently throughout the policy. Types, +as opposed to labels, are not directly associated or referenced +outside the policy so they do not need to carry their history +in a ``From'' field. The figure below shows the update for the +types and conflict sets. The \verb|__UNLABELED__| type is removed +to disable support for running unlabeled domains. Additionally, +we have renamed the two \verb|A-Bank| department types with +abbreviated names \verb|A-Bank.SU| and \verb|A-Bank.MA|. You +can also see how those type names are +consistently changed within the conflict set definition. + +\begin{figure}[htb] +\begin{scriptsize} +\begin{verbatim} + + + SystemManagement + A-Bank + A-Bank.SU + A-Bank.MA + B-Bank + AutoCorp + + + + + + SystemManagement + A-Bank + A-Bank.SU + A-Bank.MA + B-Bank + AutoCorp + + + + + A-Bank + B-Bank + + + A-Bank.MA + A-Bank.SU + + + +\end{verbatim} +\end{scriptsize} +\caption{XML security policy update -- Part II: Updated Types and Conflict Sets.} +\label{fig:acmupdatetypesnrules} +\end{figure} + +In the same way, new types can be introduced and new conflict sets +can be defined by simply adding the types or conflict sets to the +update policy. + +\paragraph{Labels} Virtual machine and resource labels of an existing policy can be +deleted through a policy update simply by omitting them in the +update-policy. However, if a currently running virtual machine +or a currently used resource is labeled with a label not stated +in the update-policy, then the policy update is rejected. This +ensures that a policy update leaves the system in a consistent +security state. + +A policy update also enables the renaming of virtual machine and +resource labels. Linking the old label name with the new label +name is achieved through the \verb|from| attribute in the +\verb|VirtualMachineLabel| or \verb|ResourceLabel| nodes in the +update-policy. Figure~\ref{fig:acmupdatelabels} shown how subject +and resource labels +are updated from their old name \verb|A-Bank.SecurityUnterwriting| +to their new name \verb|A-Bank.SU| using the \verb|from| attribute. + +\begin{figure}[htb] +\begin{tabular*}{\textwidth}{@{\extracolsep{\fill}}l|l} +\begin{minipage}{0.475\textwidth} +\begin{tiny} +\begin{verbatim} + + + + SystemManagement + + SystemManagement + A-Bank + A-Bank.SU + A-Bank.MA + B-Bank + AutoCorp + + + SystemManagement + + + + A-Bank-WL + + SystemManagement + A-Bank + A-Bank.SU + A-Bank.MA + + + SystemManagement + + + + A-Bank + + A-Bank + + + A-Bank + + + + + A-Bank.SU + + A-Bank.SU + + + A-Bank + A-Bank.SU + + + + + A-Bank.MA + + A-Bank.MA + + + A-Bank + A-Bank.MA + + +\end{verbatim} +\end{tiny} +\end{minipage} & +\begin{minipage}{0.475\textwidth} +\begin{tiny} +\begin{verbatim} + + B-Bank + + B-Bank + + + B-Bank + + + + AutoCorp + + AutoCorp + + + AutoCorp + + + + + + + SystemManagement + + SystemManagement + + + + A-Bank + + A-Bank + + + + + A-Bank.SU + + A-Bank.SU + + + + + A-Bank.MA + + A-Bank.MA + + + + B-Bank + + B-Bank + + + + AutoCorp + + AutoCorp + + + + + +\end{verbatim} +\end{tiny} +\end{minipage} +\end{tabular*} +\caption{XML security policy update -- Part III: Updated Label Definition.} +\label{fig:acmupdatelabels} +\end{figure} +% DO NOT MODIFY WHITESPACE ABOVE, it balances the columns + +The updated label definition also includes a new label \verb|A-Bank-WL| +that includes all STE types related to A-Bank. Its CHWALL type +is \verb|SystemManagement|. This indicates that this label is designed +as Domain-0 label. A Xen system can be restricted to only run A-Bank +related workloads by relabeling Domain-0 with the \verb|A-Bank-WL| label. + +We assume that the update-policy shown in +Figures~\ref{fig:acmupdateheader}, \ref{fig:acmupdatetypesnrules} +and \ref{fig:acmupdatelabels} +is stored in the XML file mytest\_update-security\_policy.xml located +in the ACM policy directory. See Section~\ref{subsection:acmnaming} +for information about policy names and locations. + +The following \verb|xm setpolicy| command updates the active ACM +security policy at run-time. + +\begin{scriptsize} +\begin{verbatim} +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 2 128 1 -b---- 0.6 ACM:mytest:A-Bank +domain4 3 164 1 -b---- 0.3 ACM:mytest:A-Bank.SecurityUnderwriting +Domain-0 0 711 1 r----- 71.8 ACM:mytest:SystemManagement + +# xm resources +file:/home/xen/dom_fc5/fedora.fc5.swap + type: ACM + policy: mytest + label: A-Bank +file:/home/xen/dom_fc5/fedora.fc5.img + type: ACM + policy: mytest + label: A-Bank + +# xm setpolicy ACM mytest_update +Successfully set the new policy. +Supported security subsystems : ACM +Policy name : mytest +Policy type : ACM +Version of XML policy : 1.1 +Policy configuration : loaded, activated for boot + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 2 128 1 -b---- 0.7 ACM:mytest:A-Bank +domain4 3 164 1 -b---- 0.3 ACM:mytest:A-Bank.SU +Domain-0 0 711 1 r----- 72.8 ACM:mytest:SystemManagement + +# xm labels +A-Bank +A-Bank-WL +A-Bank.MA +A-Bank.SU +AutoCorp +B-Bank + +# xm resources +file:/home/xen/dom_fc5/fedora.fc5.swap + type: ACM + policy: mytest + label: A-Bank +file:/home/xen/dom_fc5/fedora.fc5.img + type: ACM + policy: mytest + label: A-Bank + \end{verbatim} +\end{scriptsize} + +After successful completion of this command, \verb|xm list --label| +shows that the labels of running domains changed to their new names. +\verb|xm labels| shows that new labels \verb|A-Bank.SU| and \verb|A-Bank.AM| +are now available in the policy. The resource labels remain valid after +the successful update as \verb|xm resources| confirms. + +The \verb|setpolicy| command fails if the new policy is inconsistent +with the current one or the policy is inconsistent internally (e.g., types +are renamed in the type definition but not in the label definition part of +the policy). In this case, the old policy remains active. + +After relabeling Domain-0 with the new \verb|A-Bank-WL| label, we can no +longer run domains labeled \verb|B-Bank| or \verb|AutoCorp| since their +STE types are not a subset of the new Domain-0 label. + +\begin{scriptsize} +\begin{verbatim} +# xm addlabel A-Bank-WL mgt Domain-0 +Successfully set the label of domain 'Domain-0' to 'A-Bank-WL'. + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 2 128 1 -b---- 0.8 ACM:mytest:A-Bank +Domain-0 0 711 1 r----- 74.5 ACM:mytest:A-Bank-WL +domain4 3 164 1 -b---- 0.3 ACM:mytest:A-Bank.SU + +# xm getlabel dom domain3.xm +policytype=ACM,policy=mytest,label=AutoCorp + +# xm create domain3.xm +Using config file "./domain3.xm". +Error: VM is not authorized to run. + +# xm addlabel SystemManagement mgt Domain-0 +Successfully set the label of domain 'Domain-0' to 'SystemManagement'. + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 2 128 1 -b---- 0.8 ACM:mytest:A-Bank +domain4 3 164 1 -b---- 0.3 ACM:mytest:A-Bank.SU +Domain-0 0 709 1 r----- 76.4 ACM:mytest:SystemManagement + +# xm create domain3.xm +Using config file "./domain3.xm". +Started domain domain3 + +# xm list --label +Name ID Mem VCPUs State Time(s) Label +domain1 2 128 1 -b---- 0.8 ACM:mytest:A-Bank +domain4 3 164 1 -b---- 0.3 ACM:mytest:A-Bank.SU +domain3 4 164 1 -b---- 0.3 ACM:mytest:AutoCorp +Domain-0 0 547 1 r----- 77.5 ACM:mytest:SystemManagement +\end{verbatim} +\end{scriptsize} + +In the same manner, you can add new labels to support new workloads and +add, delete, or rename workload types (STE and/or CHWALL types) simply +by changing the composition of labels. Another use case is to add new +workload types to the current Domain-0 label to enable them to run. +Conflict sets (run-time exclusion rules) can be simply omitted or added. +The policy and label changes become active at once and new workloads +can be run in protected mode without rebooting the Xen system. + +In all these cases, if any running user domain would--under the new policy--not +be allowed to run or would not be allowed to access any of the resources +it currently uses, then the policy update is rejected. In this case, you +can stop domains that conflict with the new policy and update the policy +afterwards. The old policy remains active until a policy update succeeds +or Xen is re-booted into a new policy. + +\subsection{Tools For Creating sHype/Xen Security Policies} +To create a security policy for Xen, you can use one of the following +tools: +\begin{itemize} +\item \verb|ezPolicy| GUI tool -- start writing policies +\item \verb|xensec_gen| tool -- refine policies created with \verb|ezPolicy| +\item text or XML editor +\end{itemize} + +We use the \verb|ezPolicy| tool in +Section~\ref{subsection:acmexamplecreate} to quickly create a workload +protection policy. If desired, the resulting XML policy file can be +loaded into the \verb|xensec_gen| tool to refine it. It can also be +directly edited using an XML editor. Any XML policy file is verified +against the security policy schema when it is translated (see +Subsection~\ref{subsection:acmexampleinstall}). + +\section{Current Limitations} +\label{section:acmlimitations} + +The sHype/ACM configuration for Xen is work in progress. There is +ongoing work for protecting virtualized resources and planned and +ongoing work for protecting access to remote resources and domains. +The following sections describe limitations of some of the areas into +which access control is being extended. + +\subsection{Network Traffic} +Local and remote network traffic is currently not controlled. +Solutions to add sHype/ACM policy enforcement to the virtual network +exist but need to be discussed before they can become part of Xen. +Subjecting external network traffic to the ACM security policy is work +in progress. Manually setting up filters in domain 0 is required for +now but does not scale well. + +\subsection{Resource Access and Usage Control} + +Enforcing the security policy across multiple hypervisor systems and +on access to remote shared resources is work in progress. Extending +access control to new types of resources is ongoing work (e.g. network +storage). + +On a single Xen system, information about the association of resources +and security labels is stored in +\verb|/var/lib/xend/security/policies/resource_labels|. This file relates +a full resource path with a security label. This association is weak +and will break if resources are moved or renamed without adapting the +label file. Improving the protection of label-resource relationships +is ongoing work. + +Controlling resource usage and enforcing resource limits in general is +ongoing work in the Xen community. + +\subsection{Domain Migration} + +Labels on domains are enforced during domain migration and the +destination hypervisor will ensure that the domain label is valid and +the domain is permitted to run (considering the Chinese Wall policy +rules) before it accepts the migration. However, the network between +the source and destination hypervisor as well as both hypervisors must +be trusted. Architectures and prototypes exist that both protect the +network connection and ensure that the hypervisors enforce access +control consistently but patches are not yet available for the main +stream. + +\subsection{Covert Channels} + +The sHype access control aims at system independent security policies. +It builds on top of the core hypervisor isolation. Any covert channels +that exist in the core hypervisor or in the hardware (e.g., shared +processor cache) will be inherited. If those covert channels are not +the result of trade-offs between security and other system properties, +then they are most effectively minimized or eliminated where they are +caused. sHype offers however some means to mitigate their impact, e.g., +run-time exclusion rules (cf Section~\ref{subsection:acmexamplecreate}) +or limiting the system authorization (cf Section~\ref{subsection:acmlabeldom0}). + + +\part{Reference} + +%% Chapter Build and Boot Options +\chapter{Build and Boot Options} + +This chapter describes the build- and boot-time options which may be +used to tailor your Xen system. + +\section{Top-level Configuration Options} + +Top-level configuration is achieved by editing one of two +files: \path{Config.mk} and \path{Makefile}. + +The former allows the overall build target architecture to be +specified. You will typically not need to modify this unless +you are cross-compiling. Additional configuration options are +documented in the \path{Config.mk} file. + +The top-level \path{Makefile} is chiefly used to customize the set of +kernels built. Look for the line: +\begin{quote} +\begin{verbatim} +KERNELS ?= linux-2.6-xen0 linux-2.6-xenU +\end{verbatim} +\end{quote} + +Allowable options here are any kernels which have a corresponding +build configuration file in the \path{buildconfigs/} directory. + + + +\section{Xen Build Options} + +Xen provides a number of build-time options which should be set as +environment variables or passed on make's command-line. + +\begin{description} +\item[verbose=y] Enable debugging messages when Xen detects an + unexpected condition. Also enables console output from all domains. +\item[debug=y] Enable debug assertions. Implies {\bf verbose=y}. + (Primarily useful for tracing bugs in Xen). +\item[debugger=y] Enable the in-Xen debugger. This can be used to + debug Xen, guest OSes, and applications. +\item[perfc=y] Enable performance counters for significant events + within Xen. The counts can be reset or displayed on Xen's console + via console control keys. +\end{description} + + +\section{Xen Boot Options} +\label{s:xboot} + +These options are used to configure Xen's behaviour at runtime. They +should be appended to Xen's command line, either manually or by +editing \path{grub.conf}. + +\begin{description} +\item [ noreboot ] Don't reboot the machine automatically on errors. + This is useful to catch debug output if you aren't catching console + messages via the serial line. +\item [ nosmp ] Disable SMP support. This option is implied by + `ignorebiostables'. +\item [ watchdog ] Enable NMI watchdog which can report certain + failures. +\item [ noirqbalance ] Disable software IRQ balancing and affinity. + This can be used on systems such as Dell 1850/2850 that have + workarounds in hardware for IRQ-routing issues. +\item [ badpage=$<$page number$>$,$<$page number$>$, \ldots ] Specify + a list of pages not to be allocated for use because they contain bad + bytes. For example, if your memory tester says that byte 0x12345678 + is bad, you would place `badpage=0x12345' on Xen's command line. +\item [ serial\_tx\_buffer=$<$size$>$ ] Size of serial transmit + buffers. Default is 16kB. +\item [ com1=$<$baud$>$,DPS,$<$io\_base$>$,$<$irq$>$ + com2=$<$baud$>$,DPS,$<$io\_base$>$,$<$irq$>$ ] \mbox{}\\ + Xen supports up to two 16550-compatible serial ports. For example: + `com1=9600, 8n1, 0x408, 5' maps COM1 to a 9600-baud port, 8 data + bits, no parity, 1 stop bit, I/O port base 0x408, IRQ 5. If some + configuration options are standard (e.g., I/O base and IRQ), then + only a prefix of the full configuration string need be specified. If + the baud rate is pre-configured (e.g., by the bootloader) then you + can specify `auto' in place of a numeric baud rate. +\item [ console=$<$specifier list$>$ ] Specify the destination for Xen + console I/O. This is a comma-separated list of, for example: + \begin{description} + \item[ vga ] Use VGA console (until domain 0 boots, unless {\bf + vga=...keep } is specified). + \item[ com1 ] Use serial port com1. + \item[ com2H ] Use serial port com2. Transmitted chars will have the + MSB set. Received chars must have MSB set. + \item[ com2L] Use serial port com2. Transmitted chars will have the + MSB cleared. Received chars must have MSB cleared. + \end{description} + The latter two examples allow a single port to be shared by two + subsystems (e.g.\ console and debugger). Sharing is controlled by + MSB of each transmitted/received character. [NB. Default for this + option is `com1,vga'] +\item [ vga=$<$mode$>$(,keep) ] The mode is one of the following options: + \begin{description} + \item[ ask ] Display a vga menu allowing manual selection of video + mode. + \item[ current ] Use existing vga mode without modification. + \item[ text-$<$mode$>$ ] Select text-mode resolution, where mode is + one of 80x25, 80x28, 80x30, 80x34, 80x43, 80x50, 80x60. + \item[ gfx-$<$mode$>$ ] Select VESA graphics mode + $<$width$>$x$<$height$>$x$<$depth$>$ (e.g., `vga=gfx-1024x768x32'). + \item[ mode-$<$mode$>$ ] Specify a mode number as discovered by `vga + ask'. Note that the numbers are displayed in hex and hence must be + prefixed by `0x' here (e.g., `vga=mode-0x0335'). + \end{description} +The mode may optionally be followed by `{\bf,keep}' to cause Xen to keep +writing to the VGA console after domain 0 starts booting (e.g., `vga=text-80x50,keep'). +\item [ no-real-mode ] (x86 only) Do not execute real-mode bootstrap + code when booting Xen. This option should not be used except for + debugging. It will effectively disable the {\bf vga} option, which + relies on real mode to set the video mode. +\item [ edid=no,force ] (x86 only) Either force retrieval of monitor + EDID information via VESA DDC, or disable it (edid=no). This option + should not normally be required except for debugging purposes. +\item [ edd=off,on,skipmbr ] (x86 only) Control retrieval of Extended + Disc Data (EDD) from the BIOS during boot. +\item [ console\_to\_ring ] Place guest console output into the + hypervisor console ring buffer. This is disabled by default. + When enabled, both hypervisor output and guest console output + is available from the ring buffer. This can be useful for logging + and/or remote presentation of console data. +\item [ sync\_console ] Force synchronous console output. This is + useful if you system fails unexpectedly before it has sent all + available output to the console. In most cases Xen will + automatically enter synchronous mode when an exceptional event + occurs, but this option provides a manual fallback. +\item [ conswitch=$<$switch-char$><$auto-switch-char$>$ ] Specify how + to switch serial-console input between Xen and DOM0. The required + sequence is CTRL-$<$switch-char$>$ pressed three times. Specifying + the backtick character disables switching. The + $<$auto-switch-char$>$ specifies whether Xen should auto-switch + input to DOM0 when it boots --- if it is `x' then auto-switching is + disabled. Any other value, or omitting the character, enables + auto-switching. [NB. Default switch-char is `a'.] +\item [ loglvl=$<$level$>/<$level$>$ ] + Specify logging level. Messages of the specified severity level (and + higher) will be printed to the Xen console. Valid levels are `none', + `error', `warning', `info', `debug', and `all'. The second level + specifier is optional: it is used to specify message severities + which are to be rate limited. Default is `loglvl=warning'. +\item [ guest\_loglvl=$<$level$>/<$level$>$ ] As for loglvl, but + applies to messages relating to guests. Default is + `guest\_loglvl=none/warning'. +\item [ console\_timestamps ] + Adds a timestamp prefix to each line of Xen console output. +\item [ nmi=xxx ] + Specify what to do with an NMI parity or I/O error. \\ + `nmi=fatal': Xen prints a diagnostic and then hangs. \\ + `nmi=dom0': Inform DOM0 of the NMI. \\ + `nmi=ignore': Ignore the NMI. +\item [ mem=xxx ] Set the physical RAM address limit. Any RAM + appearing beyond this physical address in the memory map will be + ignored. This parameter may be specified with a B, K, M or G suffix, + representing bytes, kilobytes, megabytes and gigabytes respectively. + The default unit, if no suffix is specified, is kilobytes. +\item [ dom0\_mem=$<$specifier list$>$ ] Set the amount of memory to + be allocated to domain 0. This is a comma-separated list containing + the following optional components: + \begin{description} + \item[ min:$<$min\_amt$>$ ] Minimum amount to allocate to domain 0 + \item[ max:$<$min\_amt$>$ ] Maximum amount to allocate to domain 0 + \item[ $<$amt$>$ ] Precise amount to allocate to domain 0 + \end{description} + Each numeric parameter may be specified with a B, K, M or + G suffix, representing bytes, kilobytes, megabytes and gigabytes + respectively; if no suffix is specified, the parameter defaults to + kilobytes. Negative values are subtracted from total available + memory. If $<$amt$>$ is not specified, it defaults to all available + memory less a small amount (clamped to 128MB) for uses such as DMA + buffers. +\item [ dom0\_vcpus\_pin ] Pins domain 0 VCPUs on their respective + physical CPUS (default=false). +\item [ tbuf\_size=xxx ] Set the size of the per-cpu trace buffers, in + pages (default 0). +\item [ sched=xxx ] Select the CPU scheduler Xen should use. The + current possibilities are `credit' (default), and `sedf'. +\item [ apic\_verbosity=debug,verbose ] Print more detailed + information about local APIC and IOAPIC configuration. +\item [ lapic ] Force use of local APIC even when left disabled by + uniprocessor BIOS. +\item [ nolapic ] Ignore local APIC in a uniprocessor system, even if + enabled by the BIOS. +\item [ apic=bigsmp,default,es7000,summit ] Specify NUMA platform. + This can usually be probed automatically. +\item [ dma\_bits=xxx ] Specify width of DMA addresses in bits. This + is used in NUMA systems to prevent this special DMA memory from + being exhausted in one node when remote nodes have available memory. +\end{description} + +In addition, the following options may be specified on the Xen command +line. Since domain 0 shares responsibility for booting the platform, +Xen will automatically propagate these options to its command line. +These options are taken from Linux's command-line syntax with +unchanged semantics. + +\begin{description} +\item [ acpi=off,force,strict,ht,noirq,\ldots ] Modify how Xen (and + domain 0) parses the BIOS ACPI tables. +\item [ acpi\_skip\_timer\_override ] Instruct Xen (and domain~0) to + ignore timer-interrupt override instructions specified by the BIOS + ACPI tables. +\item [ noapic ] Instruct Xen (and domain~0) to ignore any IOAPICs + that are present in the system, and instead continue to use the + legacy PIC. +\end{description} + + +\section{XenLinux Boot Options} + +In addition to the standard Linux kernel boot options, we support: +\begin{description} +\item[ xencons=xxx ] Specify the device node to which the Xen virtual + console driver is attached. The following options are supported: + \begin{center} + \begin{tabular}{l} + `xencons=off': disable virtual console \\ + `xencons=tty': attach console to /dev/tty1 (tty0 at boot-time) \\ + `xencons=ttyS': attach console to /dev/ttyS0 \\ + `xencons=xvc': attach console to /dev/xvc0 + \end{tabular} +\end{center} +The default is ttyS for dom0 and xvc for all other domains. +\end{description} + + +%% Chapter Further Support +\chapter{Further Support} + +If you have questions that are not answered by this manual, the +sources of information listed below may be of interest to you. Note +that bug reports, suggestions and contributions related to the +software (or the documentation) should be sent to the Xen developers' +mailing list (address below). + + +\section{Other Documentation} + +For developers interested in porting operating systems to Xen, the +\emph{Xen Interface Manual} is distributed in the \path{docs/} +directory of the Xen source distribution. + + +\section{Online References} + +The official Xen web site can be found at: +\begin{quote} {\tt http://www.xen.org} +\end{quote} + +This contains links to the latest versions of all online +documentation, including the latest version of the FAQ. + +Information regarding Xen is also available at the Xen Wiki at +\begin{quote} {\tt http://wiki.xensource.com/xenwiki/}\end{quote} +The Xen project uses Bugzilla as its bug tracking system. You'll find +the Xen Bugzilla at http://bugzilla.xensource.com/bugzilla/. + + +\section{Mailing Lists} + +There are several mailing lists that are used to discuss Xen related +topics. The most widely relevant are listed below. An official page of +mailing lists and subscription information can be found at \begin{quote} + {\tt http://lists.xensource.com/} \end{quote} + +\begin{description} +\item[xen-devel@lists.xensource.com] Used for development + discussions and bug reports. Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-devel}} +\item[xen-users@lists.xensource.com] Used for installation and usage + discussions and requests for help. Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-users}} +\item[xen-announce@lists.xensource.com] Used for announcements only. + Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-announce}} +\item[xen-changelog@lists.xensource.com] Changelog feed + from the unstable and 3.x trees - developer oriented. Subscribe at: \\ + {\small {\tt http://lists.xensource.com/xen-changelog}} +\end{description} + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\appendix + +\chapter{Unmodified (HVM) guest domains in Xen with Hardware support for Virtualization} + +Xen supports guest domains running unmodified guest operating systems using +virtualization extensions available on recent processors. Currently processors +featuring the Intel Virtualization Extension (Intel-VT) or the AMD extension +(AMD-V) are supported. The technology covering both implementations is +called HVM (for Hardware Virtual Machine) in Xen. More information about the +virtualization extensions are available on the respective websites: + {\small {\tt http://www.intel.com/technology/computing/vptech}} + + + {\small {\tt http://www.amd.com/us-en/assets/content\_type/white\_papers\_and\_tech\_docs/24593.pdf}} + +\section{Building Xen with HVM support} + +The following packages need to be installed in order to build Xen with HVM support. Some Linux distributions do not provide these packages by default. + +\begin{tabular}{lp{11.0cm}} +{\bfseries Package} & {\bfseries Description} \\ + +dev86 & The dev86 package provides an assembler and linker for real mode 80x86 instructions. You need to have this package installed in order to build the BIOS code which runs in (virtual) real mode. + +If the dev86 package is not available on the x86\_64 distribution, you can install the i386 version of it. The dev86 rpm package for various distributions can be found at {\scriptsize {\tt http://www.rpmfind.net/linux/rpm2html/search.php?query=dev86\&submit=Search}} \\ + +SDL-devel, SDL & Simple DirectMedia Layer (SDL) is another way of virtualizing the unmodified guest console. It provides an X window for the guest console. + +If the SDL and SDL-devel packages are not installed by default on the build system, they can be obtained from {\scriptsize {\tt http://www.rpmfind.net/linux/rpm2html/search.php?query=SDL\&submit=Search}} + + +{\scriptsize {\tt http://www.rpmfind.net/linux/rpm2html/search.php?query=SDL-devel\&submit=Search}} \\ + +\end{tabular} + +\section{Configuration file for unmodified HVM guests} + +The Xen installation includes a sample configuration file, {\small {\tt /etc/xen/xmexample.hvm}}. There are comments describing all the options. In addition to the common options that are the same as those for paravirtualized guest configurations, HVM guest configurations have the following settings: + +\begin{tabular}{lp{11.0cm}} + +{\bfseries Parameter} & {\bfseries Description} \\ + +kernel & The HVM firmware loader, {\small {\tt /usr/lib/xen/boot/hvmloader}}\\ + +builder & The domain build function. The HVM domain uses the 'hvm' builder.\\ + +acpi & Enable HVM guest ACPI, default=1 (enabled)\\ + +apic & Enable HVM guest APIC, default=1 (enabled)\\ + +pae & Enable HVM guest PAE, default=1 (enabled)\\ + +hap & Enable hardware-assisted paging support, such as AMD-V's nested paging +or Intel\textregistered VT's extended paging. If available, Xen will +use hardware-assisted paging instead of shadow paging for this guest's memory +management.\\ + +vif & Optionally defines MAC address and/or bridge for the network interfaces. Random MACs are assigned if not given. {\small {\tt type=ioemu}} means ioemu is used to virtualize the HVM NIC. If no type is specified, vbd is used, as with paravirtualized guests.\\ + +disk & Defines the disk devices you want the domain to have access to, and what you want them accessible as. If using a physical device as the HVM guest's disk, each disk entry is of the form + +{\small {\tt phy:UNAME,ioemu:DEV,MODE,}} + +where UNAME is the host device file, DEV is the device name the domain will see, and MODE is r for read-only, w for read-write. ioemu means the disk will use ioemu to virtualize the HVM disk. If not adding ioemu, it uses vbd like paravirtualized guests. + +If using disk image file, its form should be like + +{\small {\tt file:FILEPATH,ioemu:DEV,MODE}} + +Optical devices can be emulated by appending cdrom to the device type + +{\small {\tt ',hdc:cdrom,r'}} + +If using more than one disk, there should be a comma between each disk entry. For example: + +{\scriptsize {\tt disk = ['file:/var/images/image1.img,ioemu:hda,w', 'phy:hda1,hdb1,w', 'file:/var/images/install1.iso,hdc:cdrom,r']}}\\ + +boot & Boot from floppy (a), hard disk (c) or CD-ROM (d). For example, to boot from CD-ROM and fallback to HD, the entry should be: + +boot='dc'\\ + +device\_model & The device emulation tool for HVM guests. This parameter should not be changed.\\ + +sdl & Enable SDL library for graphics, default = 0 (disabled)\\ + +vnc & Enable VNC library for graphics, default = 1 (enabled)\\ + +vncconsole & Enable spawning of the vncviewer (only valid when vnc=1), default = 0 (disabled) + +If vnc=1 and vncconsole=0, user can use vncviewer to manually connect HVM from remote. For example: + +{\small {\tt vncviewer domain0\_IP\_address:HVM\_domain\_id}} \\ + +serial & Enable redirection of HVM serial output to pty device\\ + +\end{tabular} + +\begin{tabular}{lp{10cm}} + +usb & Enable USB support without defining a specific USB device. +This option defaults to 0 (disabled) unless the option usbdevice is +specified in which case this option then defaults to 1 (enabled).\\ + +usbdevice & Enable USB support and also enable support for the given +device. Devices that can be specified are {\small {\tt mouse}} (a PS/2 style +mouse), {\small {\tt tablet}} (an absolute pointing device) and +{\small {\tt host:id1:id2}} (a physical USB device on the host machine whose +ids are {\small {\tt id1}} and {\small {\tt id2}}). The advantage +of {\small {\tt tablet}} is that Windows guests will automatically recognize +and support this device so specifying the config line + +{\small +\begin{verbatim} + usbdevice='tablet' +\end{verbatim} +} + +will create a mouse that works transparently with Windows guests under VNC. +Linux doesn't recognize the USB tablet yet so Linux guests under VNC will +still need the Summagraphics emulation. +Details about mouse emulation are provided in section \textbf{A.4.3}.\\ + +localtime & Set the real time clock to local time [default=0, that is, set to UTC].\\ + +soundhw & Enable sound card support and specify the hardware to emulate. Values can be sb16, es1370 or all. Default is none.\\ + +full-screen & Start in full screen.\\ + +nographic & Another way to redirect serial output. If enabled, no 'sdl' or 'vnc' can work. Not recommended.\\ + +\end{tabular} + + +\section{Creating virtual disks from scratch} +\subsection{Using physical disks} +If you are using a physical disk or physical disk partition, you need to install a Linux OS on the disk first. Then the boot loader should be installed in the correct place. For example {\small {\tt dev/sda}} for booting from the whole disk, or {\small {\tt /dev/sda1}} for booting from partition 1. + +\subsection{Using disk image files} +You need to create a large empty disk image file first; then, you need to install a Linux OS onto it. There are two methods you can choose. One is directly installing it using a HVM guest while booting from the OS installation CD-ROM. The other is copying an installed OS into it. The boot loader will also need to be installed. + +\subsubsection*{To create the image file:} +The image size should be big enough to accommodate the entire OS. This example assumes the size is 1G (which is probably too small for most OSes). + +{\small {\tt \# dd if=/dev/zero of=hd.img bs=1M count=0 seek=1024}} + +\subsubsection*{To directly install Linux OS into an image file using a HVM guest:} + +Install Xen and create HVM with the original image file with booting from CD-ROM. Then it is just like a normal Linux OS installation. The HVM configuration file should have a stanza for the CD-ROM as well as a boot device specification: + +{\small {\tt disk=['file:/var/images/your-hd.img,hda,w', ',hdc:cdrom,r' ] +boot='d'}} + +If this method does not succeed, you can choose the following method of copying an installed Linux OS into an image file. + +\subsubsection*{To copy a installed OS into an image file:} +Directly installing is an easier way to make partitions and install an OS in a disk image file. But if you want to create a specific OS in your disk image, then you will most likely want to use this method. + +\begin{enumerate} +\item {\bfseries Install a normal Linux OS on the host machine}\\ +You can choose any way to install Linux, such as using yum to install Red Hat Linux or YAST to install Novell SuSE Linux. The rest of this example assumes the Linux OS is installed in {\small {\tt /var/guestos/}}. + +\item {\bfseries Make the partition table}\\ +The image file will be treated as hard disk, so you should make the partition table in the image file. For example: + +{\scriptsize {\tt \# losetup /dev/loop0 hd.img\\ +\# fdisk -b 512 -C 4096 -H 16 -S 32 /dev/loop0\\ +press 'n' to add new partition\\ +press 'p' to choose primary partition\\ +press '1' to set partition number\\ +press "Enter" keys to choose default value of "First Cylinder" parameter.\\ +press "Enter" keys to choose default value of "Last Cylinder" parameter.\\ +press 'w' to write partition table and exit\\ +\# losetup -d /dev/loop0}} + +\item {\bfseries Make the file system and install grub}\\ +{\scriptsize {\tt \# ln -s /dev/loop0 /dev/loop\\ +\# losetup /dev/loop0 hd.img\\ +\# losetup -o 16384 /dev/loop1 hd.img\\ +\# mkfs.ext3 /dev/loop1\\ +\# mount /dev/loop1 /mnt\\ +\# mkdir -p /mnt/boot/grub\\ +\# cp /boot/grub/stage* /boot/grub/e2fs\_stage1\_5 /mnt/boot/grub\\ +\# umount /mnt\\ +\# grub\\ +grub> device (hd0) /dev/loop\\ +grub> root (hd0,0)\\ +grub> setup (hd0)\\ +grub> quit\\ +\# rm /dev/loop\\ +\# losetup -d /dev/loop0\\ +\# losetup -d /dev/loop1}} + +The {\small {\tt losetup}} option {\small {\tt -o 16384}} skips the partition table in the image file. It is the number of sectors times 512. We need {\small {\tt /dev/loop}} because grub is expecting a disk device \emph{name}, where \emph{name} represents the entire disk and \emph{name1} represents the first partition. + +\item {\bfseries Copy the OS files to the image}\\ +If you have Xen installed, you can easily use {\small {\tt lomount}} instead of {\small {\tt losetup}} and {\small {\tt mount}} when coping files to some partitions. {\small {\tt lomount}} just needs the partition information. + +{\scriptsize {\tt \# lomount -t ext3 -diskimage hd.img -partition 1 /mnt/guest\\ +\# cp -ax /var/guestos/\{root,dev,var,etc,usr,bin,sbin,lib\} /mnt/guest\\ +\# mkdir /mnt/guest/\{proc,sys,home,tmp\}}} + +\item {\bfseries Edit the {\small {\tt /etc/fstab}} of the guest image}\\ +The fstab should look like this: + +{\scriptsize {\tt \# vim /mnt/guest/etc/fstab\\ +/dev/hda1 / ext3 defaults 1 1\\ +none /dev/pts devpts gid=5,mode=620 0 0\\ +none /dev/shm tmpfs defaults 0 0\\ +none /proc proc defaults 0 0\\ +none /sys sysfs efaults 0 0}} + +\item {\bfseries umount the image file}\\ +{\small {\tt \# umount /mnt/guest}} +\end{enumerate} + +Now, the guest OS image {\small {\tt hd.img}} is ready. You can also reference {\small {\tt http://free.oszoo.org}} for quickstart images. But make sure to install the boot loader. + +\section{HVM Guests} +\subsection{Editing the Xen HVM config file} +Make a copy of the example HVM configuration file {\small {\tt /etc/xen/xmexample.hvm}} and edit the line that reads + +{\small {\tt disk = [ 'file:/var/images/\emph{min-el3-i386.img},hda,w' ]}} + +replacing \emph{min-el3-i386.img} with the name of the guest OS image file you just made. + +\subsection{Creating HVM guests} +Simply follow the usual method of creating the guest, providing the filename of your HVM configuration file:\\ + +{\small {\tt \# xend start\\ +\# xm create /etc/xen/hvmguest.hvm}} + +In the default configuration, VNC is on and SDL is off. Therefore VNC windows will open when HVM guests are created. If you want to use SDL to create HVM guests, set {\small {\tt sdl=1}} in your HVM configuration file. You can also turn off VNC by setting {\small {\tt vnc=0}}. + +\subsection{Mouse issues, especially under VNC} +Mouse handling when using VNC is a little problematic. +The problem is that the VNC viewer provides a virtual pointer which is +located at an absolute location in the VNC window and only absolute +coordinates are provided. +The HVM device model converts these absolute mouse coordinates +into the relative motion deltas that are expected by the PS/2 +mouse driver running in the guest. +Unfortunately, +it is impossible to keep these generated mouse deltas +accurate enough for the guest cursor to exactly match +the VNC pointer. +This can lead to situations where the guest's cursor +is in the center of the screen and there's no way to +move that cursor to the left +(it can happen that the VNC pointer is at the left +edge of the screen and, +therefore, +there are no longer any left mouse deltas that +can be provided by the device model emulation code.) + +To deal with these mouse issues there are 4 different +mouse emulations available from the HVM device model: + +\begin{description} +\item[PS/2 mouse over the PS/2 port.] +This is the default mouse +that works perfectly well under SDL. +Under VNC the guest cursor will get +out of sync with the VNC pointer. +When this happens you can re-synchronize +the guest cursor to the VNC pointer by +holding down the +\textbf{left-ctl} +and +\textbf{left-alt} +keys together. +While these keys are down VNC pointer motions +will not be reported to the guest so +that the VNC pointer can be moved +to a place where it is possible +to move the guest cursor again. + +\item[Summagraphics mouse over the serial port.] +The device model also provides emulation +for a Summagraphics tablet, +an absolute pointer device. +This emulation is provided over the second +serial port, +\textbf{/dev/ttyS1} +for Linux guests and +\textbf{COM2} +for Windows guests. +Unfortunately, +neither Linux nor Windows provides +default support for the Summagraphics +tablet so the guest will have to be +manually configured for this mouse. + +\textbf{Linux configuration.} + +First, +configure the GPM service to use the Summagraphics tablet. +This can vary between distributions but, +typically, +all that needs to be done is modify the file +\path{/etc/sysconfig/mouse} to contain the lines: + +{\small +\begin{verbatim} + MOUSETYPE="summa" + XMOUSETYPE="SUMMA" + DEVICE=/dev/ttyS1 +\end{verbatim} +} + +and then restart the GPM daemon. + +Next, +modify the X11 config +\path{/etc/X11/xorg.conf} +to support the Summgraphics tablet by replacing +the input device stanza with the following: + +{\small +\begin{verbatim} + Section "InputDevice" + Identifier "Mouse0" + Driver "summa" + Option "Device" "/dev/ttyS1" + Option "InputFashion" "Tablet" + Option "Mode" "Absolute" + Option "Name" "EasyPen" + Option "Compatible" "True" + Option "Protocol" "Auto" + Option "SendCoreEvents" "on" + Option "Vendor" "GENIUS" + EndSection +\end{verbatim} +} + +Restart X and the X cursor should now properly +track the VNC pointer. + + +\textbf{Windows configuration.} + +Get the file +\path{http://www.cad-plan.de/files/download/tw2k.exe} +and execute that file on the guest, +answering the questions as follows: + +\begin{enumerate} +\item When the program asks for \textbf{model}, +scroll down and select \textbf{SummaSketch (MM Compatible)}. + +\item When the program asks for \textbf{COM Port} specify \textbf{com2}. + +\item When the programs asks for a \textbf{Cursor Type} specify +\textbf{4 button cursor/puck}. + +\item The guest system will then reboot and, +when it comes back up, +the guest cursor will now properly track +the VNC pointer. +\end{enumerate} + +\item[PS/2 mouse over USB port.] +This is just the same PS/2 emulation except it is +provided over a USB port. +This emulation is enabled by the configuration flag: +{\small +\begin{verbatim} + usbdevice='mouse' +\end{verbatim} +} + +\item[USB tablet over USB port.] +The USB tablet is an absolute pointing device +that has the advantage that it is automatically +supported under Windows guests, +although Linux guests still require some +manual configuration. +This mouse emulation is enabled by the +configuration flag: +{\small +\begin{verbatim} + usbdevice='tablet' +\end{verbatim} +} + +\textbf{Linux configuration.} + +Unfortunately, +there is no GPM support for the +USB tablet at this point in time. +If you intend to use a GPM pointing +device under VNC you should +configure the guest for Summagraphics +emulation. + +Support for X11 is available by following +the instructions at\\ +\verb+http://stz-softwaretechnik.com/~ke/touchscreen/evtouch.html+\\ +with one minor change. +The +\path{xorg.conf} +given in those instructions +uses the wrong values for the X \& Y minimums and maximums, +use the following config stanza instead: + +{\small +\begin{verbatim} + Section "InputDevice" + Identifier "Tablet" + Driver "evtouch" + Option "Device" "/dev/input/event2" + Option "DeviceName" "touchscreen" + Option "MinX" "0" + Option "MinY" "0" + Option "MaxX" "32256" + Option "MaxY" "32256" + Option "ReportingMode" "Raw" + Option "Emulate3Buttons" + Option "Emulate3Timeout" "50" + Option "SendCoreEvents" "On" + EndSection +\end{verbatim} +} + +\textbf{Windows configuration.} + +Just enabling the USB tablet in the +guest's configuration file is sufficient, +Windows will automatically recognize and +configure device drivers for this +pointing device. + +\end{description} + +\subsection{USB Support} +There is support for an emulated USB mouse, +an emulated USB tablet +and physical low speed USB devices +(support for high speed USB 2.0 devices is +still under development). + +\begin{description} +\item[USB PS/2 style mouse.] +Details on the USB mouse emulation are +given in sections +\textbf{A.2} +and +\textbf{A.4.3}. +Enabling USB PS/2 style mouse emulation +is just a matter of adding the line + +{\small +\begin{verbatim} + usbdevice='mouse' +\end{verbatim} +} + +to the configuration file. +\item[USB tablet.] +Details on the USB tablet emulation are +given in sections +\textbf{A.2} +and +\textbf{A.4.3}. +Enabling USB tablet emulation +is just a matter of adding the line + +{\small +\begin{verbatim} + usbdevice='tablet' +\end{verbatim} +} + +to the configuration file. +\item[USB physical devices.] +Access to a physical (low speed) USB device +is enabled by adding a line of the form + +{\small +\begin{verbatim} + usbdevice='host:vid:pid' +\end{verbatim} +} + +into the the configuration file.\footnote{ +There is an alternate +way of specifying a USB device that +uses the syntax +\textbf{host:bus.addr} +but this syntax suffers from +a major problem that makes +it effectively useless. +The problem is that the +\textbf{addr} +portion of this address +changes every time the USB device +is plugged into the system. +For this reason this addressing +scheme is not recommended and +will not be documented further. +} +\textbf{vid} +and +\textbf{pid} +are a +product id and +vendor id +that uniquely identify +the USB device. +These ids can be identified +in two ways: + +\begin{enumerate} +\item Through the control window. +As described in section +\textbf{A.4.6} +the control window +is activated by pressing +\textbf{ctl-alt-2} +in the guest VGA window. +As long as USB support is +enabled in the guest by including +the config file line +{\small +\begin{verbatim} + usb=1 +\end{verbatim} +} +then executing the command +{\small +\begin{verbatim} + info usbhost +\end{verbatim} +} +in the control window +will display a list of all +usb devices and their ids. +For example, +this output: +{\small +\begin{verbatim} + Device 1.3, speed 1.5 Mb/s + Class 00: USB device 04b3:310b +\end{verbatim} +} +was created from a USB mouse with +vendor id +\textbf{04b3} +and product id +\textbf{310b}. +This device could be made available +to the HVM guest by including the +config file entry +{\small +\begin{verbatim} + usbdevice='host:04be:310b' +\end{verbatim} +} + +It is also possible to +enable access to a USB +device dynamically through +the control window. +The control window command +{\small +\begin{verbatim} + usb_add host:vid:pid +\end{verbatim} +} +will also allow access to a +USB device with vendor id +\textbf{vid} +and product id +\textbf{pid}. +\item Through the +\path{/proc} file system. +The contents of the pseudo file +\path{/proc/bus/usb/devices} +can also be used to identify +vendor and product ids. +Looking at this file, +the line starting with +\textbf{P:} +has a field +\textbf{Vendor} +giving the vendor id and +another field +\textbf{ProdID} +giving the product id. +The contents of +\path{/proc/bus/usb/devices} +for the example mouse is as +follows: +{\small +\begin{verbatim} +T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 3 Spd=1.5 MxCh= 0 +D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=04b3 ProdID=310b Rev= 1.60 +C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=(none) +E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=10ms +\end{verbatim} +} +Note that the +\textbf{P:} +line correctly identifies the +vendor id and product id +for this mouse as +\textbf{04b3:310b}. +\end{enumerate} +There is one other issue to +be aware of when accessing a +physical USB device from the guest. +The Dom0 kernel must not have +a device driver loaded for +the device that the guest wishes +to access. +This means that the Dom0 +kernel must not have that +device driver compiled into +the kernel or, +if using modules, +that driver module must +not be loaded. +Note that this is the device +specific USB driver that must +not be loaded, +either the +\textbf{UHCI} +or +\textbf{OHCI} +USB controller driver must +still be loaded. + +Going back to the USB mouse +as an example, +if \textbf{lsmod} +gives the output: + +{\small +\begin{verbatim} +Module Size Used by +usbmouse 4128 0 +usbhid 28996 0 +uhci_hcd 35409 0 +\end{verbatim} +} + +then the USB mouse is being +used by the Dom0 kernel and is +not available to the guest. +Executing the command +\textbf{rmmod usbhid}\footnote{ +Turns out the +\textbf{usbhid} +driver is the significant +one for the USB mouse, +the presence or absence of +the module +\textbf{usbmouse} +has no effect on whether or +not the guest can see a USB mouse.} +will remove the USB mouse +driver from the Dom0 kernel +and the mouse will now be +accessible by the HVM guest. + +Be aware the the Linux USB +hotplug system will reload +the drivers if a USB device +is removed and plugged back +in. +This means that just unloading +the driver module might not +be sufficient if the USB device +is removed and added back. +A more reliable technique is +to first +\textbf{rmmod} +the driver and then rename the +driver file in the +\path{/lib/modules} +directory, +just to make sure it doesn't get +reloaded. +\end{description} + +\subsection{Destroy HVM guests} +HVM guests can be destroyed in the same way as can paravirtualized guests. We recommend that you shut-down the guest using the guest OS' provided method, for Linux, type the command + +{\small {\tt poweroff}} + +in the HVM guest's console, for Windows use Start -> Shutdown first to prevent +data loss. Depending on the configuration the guest will be automatically +destroyed, otherwise execute the command + +{\small {\tt xm destroy \emph{vmx\_guest\_id} }} + +at the Domain0 console. + +\subsection{HVM window (X or VNC) Hot Key} +If you are running in the X environment after creating a HVM guest, an X window is created. There are several hot keys for control of the HVM guest that can be used in the window. + +{\bfseries Ctrl+Alt+2} switches from guest VGA window to the control window. Typing {\small {\tt help }} shows the control commands help. For example, 'q' is the command to destroy the HVM guest.\\ +{\bfseries Ctrl+Alt+1} switches back to HVM guest's VGA.\\ +{\bfseries Ctrl+Alt+3} switches to serial port output. It captures serial output from the HVM guest. It works only if the HVM guest was configured to use the serial port. \\ + +\chapter{Vnets - Domain Virtual Networking} + +Xen optionally supports virtual networking for domains using {\em vnets}. +These emulate private LANs that domains can use. Domains on the same +vnet can be hosted on the same machine or on separate machines, and the +vnets remain connected if domains are migrated. Ethernet traffic +on a vnet is tunneled inside IP packets on the physical network. A vnet is a virtual +network and addressing within it need have no relation to addressing on +the underlying physical network. Separate vnets, or vnets and the physical network, +can be connected using domains with more than one network interface and +enabling IP forwarding or bridging in the usual way. + +Vnet support is included in \texttt{xm} and \xend: +\begin{verbatim} +# xm vnet-create +\end{verbatim} +creates a vnet using the configuration in the file \verb||. +When a vnet is created its configuration is stored by \xend and the vnet persists until it is +deleted using +\begin{verbatim} +# xm vnet-delete +\end{verbatim} +The vnets \xend knows about are listed by +\begin{verbatim} +# xm vnet-list +\end{verbatim} +More vnet management commands are available using the +\texttt{vn} tool included in the vnet distribution. + +The format of a vnet configuration file is +\begin{verbatim} +(vnet (id ) + (bridge ) + (vnetif ) + (security )) +\end{verbatim} +White space is not significant. The parameters are: +\begin{itemize} + \item \verb||: vnet id, the 128-bit vnet identifier. This can be given + as 8 4-digit hex numbers separated by colons, or in short form as a single 4-digit hex number. + The short form is the same as the long form with the first 7 fields zero. + Vnet ids must be non-zero and id 1 is reserved. + + \item \verb||: the name of a bridge interface to create for the vnet. Domains + are connected to the vnet by connecting their virtual interfaces to the bridge. + Bridge names are limited to 14 characters by the kernel. + + \item \verb||: the name of the virtual interface onto the vnet (optional). The + interface encapsulates and decapsulates vnet traffic for the network and is attached + to the vnet bridge. Interface names are limited to 14 characters by the kernel. + + \item \verb||: security level for the vnet (optional). The level may be one of + \begin{itemize} + \item \verb|none|: no security (default). Vnet traffic is in clear on the network. + \item \verb|auth|: authentication. Vnet traffic is authenticated using IPSEC + ESP with hmac96. + \item \verb|conf|: confidentiality. Vnet traffic is authenticated and encrypted + using IPSEC ESP with hmac96 and AES-128. + \end{itemize} + Authentication and confidentiality are experimental and use hard-wired keys at present. +\end{itemize} +When a vnet is created its configuration is stored by \xend and the vnet persists until it is +deleted using \texttt{xm vnet-delete }. The interfaces and bridges used by vnets +are visible in the output of \texttt{ifconfig} and \texttt{brctl show}. + +\section{Example} +If the file \path{vnet97.sxp} contains +\begin{verbatim} +(vnet (id 97) (bridge vnet97) (vnetif vnif97) + (security none)) +\end{verbatim} +Then \texttt{xm vnet-create vnet97.sxp} will define a vnet with id 97 and no security. +The bridge for the vnet is called vnet97 and the virtual interface for it is vnif97. +To add an interface on a domain to this vnet set its bridge to vnet97 +in its configuration. In Python: +\begin{verbatim} +vif="bridge=vnet97" +\end{verbatim} +In sxp: +\begin{verbatim} +(dev (vif (mac aa:00:00:01:02:03) (bridge vnet97))) +\end{verbatim} +Once the domain is started you should see its interface in the output of \texttt{brctl show} +under the ports for \texttt{vnet97}. + +To get best performance it is a good idea to reduce the MTU of a domain's interface +onto a vnet to 1400. For example using \texttt{ifconfig eth0 mtu 1400} or putting +\texttt{MTU=1400} in \texttt{ifcfg-eth0}. +You may also have to change or remove cached config files for eth0 under +\texttt{/etc/sysconfig/networking}. Vnets work anyway, but performance can be reduced +by IP fragmentation caused by the vnet encapsulation exceeding the hardware MTU. + +\section{Installing vnet support} +Vnets are implemented using a kernel module, which needs to be loaded before +they can be used. You can either do this manually before starting \xend, using the +command \texttt{vn insmod}, or configure \xend to use the \path{network-vnet} +script in the xend configuration file \texttt{/etc/xend/xend-config.sxp}: +\begin{verbatim} +(network-script network-vnet) +\end{verbatim} +This script insmods the module and calls the \path{network-bridge} script. + +The vnet code is not compiled and installed by default. +To compile the code and install on the current system +use \texttt{make install} in the root of the vnet source tree, +\path{tools/vnet}. It is also possible to install to an installation +directory using \texttt{make dist}. See the \path{Makefile} in +the source for details. + +The vnet module creates vnet interfaces \texttt{vnif0002}, +\texttt{vnif0003} and \texttt{vnif0004} by default. You can test that +vnets are working by configuring IP addresses on these interfaces +and trying to ping them across the network. For example, using machines +hostA and hostB: +\begin{verbatim} +hostA# ifconfig vnif0004 192.0.2.100 up +hostB# ifconfig vnif0004 192.0.2.101 up +hostB# ping 192.0.2.100 +\end{verbatim} + +The vnet implementation uses IP multicast to discover vnet interfaces, so +all machines hosting vnets must be reachable by multicast. Network switches +are often configured not to forward multicast packets, so this often +means that all machines using a vnet must be on the same LAN segment, +unless you configure vnet forwarding. + +You can test multicast coverage by pinging the vnet multicast address: +\begin{verbatim} +# ping -b 224.10.0.1 +\end{verbatim} +You should see replies from all machines with the vnet module running. +You can see if vnet packets are being sent or received by dumping traffic +on the vnet UDP port: +\begin{verbatim} +# tcpdump udp port 1798 +\end{verbatim} + +If multicast is not being forwarded between machines you can configure +multicast forwarding using vn. Suppose we have machines hostA on 192.0.2.200 +and hostB on 192.0.2.211 and that multicast is not forwarded between them. +We use vn to configure each machine to forward to the other: +\begin{verbatim} +hostA# vn peer-add hostB +hostB# vn peer-add hostA +\end{verbatim} +Multicast forwarding needs to be used carefully - you must avoid creating forwarding +loops. Typically only one machine on a subnet needs to be configured to forward, +as it will forward multicasts received from other machines on the subnet. + +%% Chapter Glossary of Terms moved to glossary.tex +\chapter{Glossary of Terms} + +\begin{description} + +\item[Domain] A domain is the execution context that contains a + running {\bf virtual machine}. The relationship between virtual + machines and domains on Xen is similar to that between programs and + processes in an operating system: a virtual machine is a persistent + entity that resides on disk (somewhat like a program). When it is + loaded for execution, it runs in a domain. Each domain has a {\bf + domain ID}. + +\item[Domain 0] The first domain to be started on a Xen machine. + Domain 0 is responsible for managing the system. + +\item[Domain ID] A unique identifier for a {\bf domain}, analogous to + a process ID in an operating system. + +\item[Full virtualization] An approach to virtualization which + requires no modifications to the hosted operating system, providing + the illusion of a complete system of real hardware devices. + +\item[Hypervisor] An alternative term for {\bf VMM}, used because it + means `beyond supervisor', since it is responsible for managing + multiple `supervisor' kernels. + +\item[Live migration] A technique for moving a running virtual machine + to another physical host, without stopping it or the services + running on it. + +\item[Paravirtualization] An approach to virtualization which requires + modifications to the operating system in order to run in a virtual + machine. Xen uses paravirtualization but preserves binary + compatibility for user space applications. + +\item[Shadow pagetables] A technique for hiding the layout of machine + memory from a virtual machine's operating system. Used in some {\bf + VMMs} to provide the illusion of contiguous physical memory, in + Xen this is used during {\bf live migration}. + +\item[Virtual Block Device] Persistent storage available to a virtual + machine, providing the abstraction of an actual block storage device. + {\bf VBD}s may be actual block devices, filesystem images, or + remote/network storage. + +\item[Virtual Machine] The environment in which a hosted operating + system runs, providing the abstraction of a dedicated machine. A + virtual machine may be identical to the underlying hardware (as in + {\bf full virtualization}, or it may differ, as in {\bf + paravirtualization}). + +\item[VMM] Virtual Machine Monitor - the software that allows multiple + virtual machines to be multiplexed on a single physical machine. + +\item[Xen] Xen is a paravirtualizing virtual machine monitor, + developed primarily by the Systems Research Group at the University + of Cambridge Computer Laboratory. + +\item[XenLinux] A name for the port of the Linux kernel that + runs on Xen. + +\end{description} + + +\end{document} + + +%% Other stuff without a home + +%% Instructions Re Python API + +%% Other Control Tasks using Python +%% ================================ + +%% A Python module 'Xc' is installed as part of the tools-install +%% process. This can be imported, and an 'xc object' instantiated, to +%% provide access to privileged command operations: + +%% # import Xc +%% # xc = Xc.new() +%% # dir(xc) +%% # help(xc.domain_create) + +%% In this way you can see that the class 'xc' contains useful +%% documentation for you to consult. + +%% A further package of useful routines (xenctl) is also installed: + +%% # import xenctl.utils +%% # help(xenctl.utils) + +%% You can use these modules to write your own custom scripts or you +%% can customise the scripts supplied in the Xen distribution. + + + +% Explain about AGP GART + + +%% If you're not intending to configure the new domain with an IP +%% address on your LAN, then you'll probably want to use NAT. The +%% 'xen_nat_enable' installs a few useful iptables rules into domain0 +%% to enable NAT. [NB: We plan to support RSIP in future] + + + +%% Installing the file systems from the CD +%% ======================================= + +%% If you haven't got an existing Linux installation onto which you +%% can just drop down the Xen and Xenlinux images, then the file +%% systems on the CD provide a quick way of doing an install. However, +%% you would be better off in the long run doing a proper install of +%% your preferred distro and installing Xen onto that, rather than +%% just doing the hack described below: + +%% Choose one or two partitions, depending on whether you want a +%% separate /usr or not. Make file systems on it/them e.g.: +%% mkfs -t ext3 /dev/hda3 +%% [or mkfs -t ext2 /dev/hda3 && tune2fs -j /dev/hda3 if using an old +%% version of mkfs] + +%% Next, mount the file system(s) e.g.: +%% mkdir /mnt/root && mount /dev/hda3 /mnt/root +%% [mkdir /mnt/usr && mount /dev/hda4 /mnt/usr] + +%% To install the root file system, simply untar /usr/XenDemoCD/root.tar.gz: +%% cd /mnt/root && tar -zxpf /usr/XenDemoCD/root.tar.gz + +%% You'll need to edit /mnt/root/etc/fstab to reflect your file system +%% configuration. Changing the password file (etc/shadow) is probably a +%% good idea too. + +%% To install the usr file system, copy the file system from CD on +%% /usr, though leaving out the "XenDemoCD" and "boot" directories: +%% cd /usr && cp -a X11R6 etc java libexec root src bin dict kerberos +%% local sbin tmp doc include lib man share /mnt/usr + +%% If you intend to boot off these file systems (i.e. use them for +%% domain 0), then you probably want to copy the /usr/boot +%% directory on the cd over the top of the current symlink to /boot +%% on your root filesystem (after deleting the current symlink) +%% i.e.: +%% cd /mnt/root ; rm boot ; cp -a /usr/boot . diff --git a/docs/xen-api/Makefile b/docs/xen-api/Makefile new file mode 100644 index 0000000..a25286a --- /dev/null +++ b/docs/xen-api/Makefile @@ -0,0 +1,44 @@ +#!/usr/bin/make -f + +XEN_ROOT=../.. +include $(XEN_ROOT)/Config.mk +include $(XEN_ROOT)/docs/Docs.mk + + +TEX := $(wildcard *.tex) +EPS := $(wildcard *.eps) +EPSDOT := $(patsubst %.dot,%.eps,$(wildcard *.dot)) + +.PHONY: all +all: build + +.PHONY: build +build: xenapi.pdf xenapi.ps + +install: + $(INSTALL_DIR) $(DESTDIR)$(DOCDIR)/ps + $(INSTALL_DIR) $(DESTDIR)$(DOCDIR)/pdf + + [ -e xenapi.ps ] && cp xenapi.ps $(DESTDIR)$(DOCDIR)/ps || true + [ -e xenapi.pdf ] && cp xenapi.pdf $(DESTDIR)$(DOCDIR)/pdf || true + +xenapi.dvi: $(TEX) $(EPS) $(EPSDOT) + $(LATEX) xenapi.tex + $(LATEX) xenapi.tex + rm -f *.aux *.log + +%.pdf: %.ps + $(PS2PDF) $< $@ + +%.ps: %.dvi + $(DVIPS) $< -o $@ + +%.eps: %.dot + $(DOT) -Tps $< >$@ + +xenapi-datamodel-graph.eps: xenapi-datamodel-graph.dot + $(NEATO) -Goverlap=false -Tps $< >$@ + +.PHONY: clean +clean: + rm -f *.pdf *.ps *.dvi *.aux *.log *.out $(EPSDOT) diff --git a/docs/xen-api/coversheet.tex b/docs/xen-api/coversheet.tex new file mode 100644 index 0000000..3d35bf6 --- /dev/null +++ b/docs/xen-api/coversheet.tex @@ -0,0 +1,63 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +\pagestyle{empty} + +\doctitle{} \hfill \revstring{} + +\vspace{1cm} + +\begin{center} +\resizebox{8cm}{!}{\includegraphics{\coversheetlogo}} + +\vspace{2cm} + +\begin{Huge} + \doctitle{} +\end{Huge} + +\vspace{1cm} +\begin{Large} +Version: \revstring{}\\ +Date: \datestring{} +\\ +\releasestatement{} + +\vspace{1cm} +\begin{tabular}{rl} +\docauthors{} +\end{tabular} +\end{Large} +\end{center} +\vspace{.5cm} +\begin{large} +\textbf{Contributors:} \\ +\\ +\begin{tabular}{p{0.5\textwidth}l} +Stefan Berger, IBM & Vincent Hanquez, XenSource \\ +Daniel Berrang\'e, Red Hat & John Levon, Sun Microsystems \\ +Gareth Bestor, IBM & Jon Ludlam, XenSource \\ +Hollis Blanchard, IBM & Alastair Tse, XenSource \\ +Mike Day, IBM & Daniel Veillard, Red Hat \\ +Jim Fehlig, Novell & Tom Wilkie, University of Cambridge \\ +Jon Harrop, XenSource & Yosuke Iwamatsu, NEC \\ +\end{tabular} +\end{large} + +\vfill + +\noindent +\legalnotice{} + +\newpage +\pagestyle{fancy} diff --git a/docs/xen-api/fdl.tex b/docs/xen-api/fdl.tex new file mode 100644 index 0000000..d821457 --- /dev/null +++ b/docs/xen-api/fdl.tex @@ -0,0 +1,488 @@ +\chapter{GNU Free Documentation License} +%\label{label_fdl} + + \begin{center} + + Version 1.2, November 2002 + + + Copyright \copyright 2000,2001,2002 Free Software Foundation, Inc. + + \bigskip + + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + \bigskip + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. +\end{center} + + +\begin{center} +{\bf\large Preamble} +\end{center} + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +\begin{center} +{\Large\bf 1. APPLICABILITY AND DEFINITIONS} +\addcontentsline{toc}{section}{1. APPLICABILITY AND DEFINITIONS} +\end{center} + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The \textbf{"Document"}, below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as \textbf{"you"}. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A \textbf{"Modified Version"} of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A \textbf{"Secondary Section"} is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The \textbf{"Invariant Sections"} are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The \textbf{"Cover Texts"} are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A \textbf{"Transparent"} copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called \textbf{"Opaque"}. + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The \textbf{"Title Page"} means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section \textbf{"Entitled XYZ"} means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as \textbf{"Acknowledgements"}, +\textbf{"Dedications"}, \textbf{"Endorsements"}, or \textbf{"History"}.) +To \textbf{"Preserve the Title"} +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +\begin{center} +{\Large\bf 2. VERBATIM COPYING} +\addcontentsline{toc}{section}{2. VERBATIM COPYING} +\end{center} + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +\begin{center} +{\Large\bf 3. COPYING IN QUANTITY} +\addcontentsline{toc}{section}{3. COPYING IN QUANTITY} +\end{center} + + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +\begin{center} +{\Large\bf 4. MODIFICATIONS} +\addcontentsline{toc}{section}{4. MODIFICATIONS} +\end{center} + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +\begin{itemize} +\item[A.] + Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. + +\item[B.] + List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. + +\item[C.] + State on the Title page the name of the publisher of the + Modified Version, as the publisher. + +\item[D.] + Preserve all the copyright notices of the Document. + +\item[E.] + Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + +\item[F.] + Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. + +\item[G.] + Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. + +\item[H.] + Include an unaltered copy of this License. + +\item[I.] + Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. + +\item[J.] + Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. + +\item[K.] + For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. + +\item[L.] + Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. + +\item[M.] + Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + +\item[N.] + Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. + +\item[O.] + Preserve any Warranty Disclaimers. +\end{itemize} + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +\begin{center} +{\Large\bf 5. COMBINING DOCUMENTS} +\addcontentsline{toc}{section}{5. COMBINING DOCUMENTS} +\end{center} + + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + +\begin{center} +{\Large\bf 6. COLLECTIONS OF DOCUMENTS} +\addcontentsline{toc}{section}{6. COLLECTIONS OF DOCUMENTS} +\end{center} + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +\begin{center} +{\Large\bf 7. AGGREGATION WITH INDEPENDENT WORKS} +\addcontentsline{toc}{section}{7. AGGREGATION WITH INDEPENDENT WORKS} +\end{center} + + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +\begin{center} +{\Large\bf 8. TRANSLATION} +\addcontentsline{toc}{section}{8. TRANSLATION} +\end{center} + + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +\begin{center} +{\Large\bf 9. TERMINATION} +\addcontentsline{toc}{section}{9. TERMINATION} +\end{center} + + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document 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. + + +\begin{center} +{\Large\bf 10. FUTURE REVISIONS OF THIS LICENSE} +\addcontentsline{toc}{section}{10. FUTURE REVISIONS OF THIS LICENSE} +\end{center} + + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +\begin{center} +{\Large\bf ADDENDUM: How to use this License for your documents} +\addcontentsline{toc}{section}{ADDENDUM: How to use this License for your documents} +\end{center} + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +\bigskip +\begin{quote} + Copyright \copyright YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". +\end{quote} +\bigskip + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + +\bigskip +\begin{quote} + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. +\end{quote} +\bigskip + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/docs/xen-api/presentation.tex b/docs/xen-api/presentation.tex new file mode 100644 index 0000000..17fe3c5 --- /dev/null +++ b/docs/xen-api/presentation.tex @@ -0,0 +1,146 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +The API is presented here as a set of Remote Procedure Calls, with a wire +format based upon XML-RPC. No specific language bindings are prescribed, +although examples will be given in the python programming language. + +Although we adopt some terminology from object-oriented programming, +future client language bindings may or may not be object oriented. +The API reference uses the terminology {\em classes\/} and {\em objects\/}. +For our purposes a {\em class\/} is simply a hierarchical namespace; +an {\em object\/} is an instance of a class with its fields set to +specific values. Objects are persistent and exist on the server-side. +Clients may obtain opaque references to these server-side objects and then +access their fields via get/set RPCs. + +%In each class there is a $\mathit{uid}$ field that assigns an indentifier +%to each object. This $\mathit{uid}$ serves as an object reference +%on both client- and server-side, and is often included as an argument in +%RPC messages. + +For each class we specify a list of +fields along with their {\em types\/} and {\em qualifiers\/}. A +qualifier is one of: +\begin{itemize} + \item $\mathit{RO}_\mathit{run}$: the field is Read +Only. Furthermore, its value is automatically computed at runtime. +For example: current CPU load and disk IO throughput. + \item $\mathit{RO}_\mathit{ins}$: the field must be manually set +when a new object is created, but is then Read Only for +the duration of the object's life. +For example, the maximum memory addressable by a guest is set +before the guest boots. + \item $\mathit{RW}$: the field is Read/Write. For example, the name +of a VM. +\end{itemize} + +A full list of types is given in Chapter~\ref{api-reference}. However, +there are three types that require explicit mention: +\begin{itemize} + \item $t~\mathit{Ref}$: signifies a reference to an object +of type $t$. + \item $t~\mathit{Set}$: signifies a set containing +values of type $t$. + \item $(t_1, t_2)~\mathit{Map}$: signifies a mapping from values of +type $t_1$ to values of type $t_2$. +\end{itemize} + +Note that there are a number of cases where {\em Ref}s are {\em doubly +linked\/}---e.g.\ a VM has a field called {\tt VIFs} of type +$(\mathit{VIF}~\mathit{Ref})~\mathit{Set}$; this field lists +the network interfaces attached to a particular VM. Similarly, the VIF +class has a field called {\tt VM} of type $(\mathit{VM}~{\mathit +Ref})$ which references the VM to which the interface is connected. +These two fields are {\em bound together\/}, in the sense that +creating a new VIF causes the {\tt VIFs} field of the corresponding +VM object to be updated automatically. + +The API reference explicitly lists the fields that are +bound together in this way. It also contains a diagram that shows +relationships between classes. In this diagram an edge signifies the +existence of a pair of fields that are bound together, using standard +crows-foot notation to signify the type of relationship (e.g.\ +one-many, many-many). + +\section{RPCs associated with fields} + +Each field, {\tt f}, has an RPC accessor associated with it +that returns {\tt f}'s value: +\begin{itemize} +\item ``{\tt get\_f(Ref x)}'': takes a +{\tt Ref} that refers to an object and returns the value of {\tt f}. +\end{itemize} + +Each field, {\tt f}, with attribute +{\em RW} and whose outermost type is {\em Set\/} has the following +additional RPCs associated with it: +\begin{itemize} +\item an ``{\tt add\_to\_f(Ref x, v)}'' RPC adds a new element v to the set\footnote{ +% +Since sets cannot contain duplicate values this operation has no action in the case +that {\tt v} was already in the set. +% +}; +\item a ``{\tt remove\_from\_f(Ref x, v)}'' RPC removes element {\tt v} from the set; +\end{itemize} + +Each field, {\tt f}, with attribute +{\em RW} and whose outermost type is {\em Map\/} has the following +additional RPCs associated with it: +\begin{itemize} +\item an ``{\tt add\_to\_f(Ref x, k, v)}'' RPC adds new pair {\tt (k, v)} +to the mapping stored in {\tt f} in object {\tt x}. Adding a new pair for duplicate +key, {\tt k}, overwrites any previous mapping for {\tt k}. +\item a ``{\tt remove\_from\_f(Ref x, k)}'' RPC removes the pair with key {\tt k} +from the mapping stored in {\tt f} in object {\tt x}. +\end{itemize} + +Each field whose outermost type is neither {\em Set\/} nor {\em Map\/}, +but whose attribute is {\em RW} has an RPC acessor associated with it +that sets its value: +\begin{itemize} +\item For {\em RW\/} ({\em R\/}ead/{\em +W\/}rite), a ``{\tt set\_f(Ref x, v)}'' RPC function is also provided. +This sets field {\tt f} on object {\tt x} to value {\tt v}. +\end{itemize} + +\section{RPCs associated with classes} + +\begin{itemize} +\item Each class has a {\em constructor\/} RPC named ``{\tt create}'' that +takes as parameters all fields marked {\em RW\/} and +$\mathit{RO}_\mathit{ins}$. The result of this RPC is that a new {\em +persistent\/} object is created on the server-side with the specified field +values. + +\item Each class has a {\tt get\_by\_uuid(uuid)} RPC that returns the object +of that class that has the specified {\tt uuid}. + +\item Each class that has a {\tt name\_label} field has a +``{\tt get\_by\_name\_label(name)}'' RPC that returns a set of objects of that +class that have the specified {\tt label}. + +\item Each class has a ``{\tt destroy(Ref x)}'' RPC that explicitly deletes +the persistent object specified by {\tt x} from the system. This is a +non-cascading delete -- if the object being removed is referenced by another +object then the {\tt destroy} call will fail. + +\end{itemize} + +\subsection{Additional RPCs} + +As well as the RPCs enumerated above, some classes have additional RPCs +associated with them. For example, the {\tt VM} class has RPCs for cloning, +suspending, starting etc. Such additional RPCs are described explicitly +in the API reference. diff --git a/docs/xen-api/revision-history.tex b/docs/xen-api/revision-history.tex new file mode 100644 index 0000000..b65fc82 --- /dev/null +++ b/docs/xen-api/revision-history.tex @@ -0,0 +1,60 @@ +{ \bf Revision History} + +\begin{center} + \begin{tabular}{|l|l|l|l|} + \hline + 1.0.0 & 27th April 07 & Xensource et al. & + \begin{minipage}[t][.7cm]{7cm} + Initial Revision + \end{minipage}\\ + \hline + 1.0.1 & 10th Dec. 07 & S. Berger & + \begin{minipage}[t]{7cm} + \begin{flushleft} + Added XSPolicy.reset\_xspolicy, VTPM.get\_other\_config, + VTPM.set\_otherconfig. ACMPolicy.get\_enforced\_binary methods. + \end{flushleft} + \end{minipage}\\ + \hline + 1.0.2 & 25th Jan. 08 & J. Fehlig & + \begin{minipage}[t]{7cm} + \begin{flushleft} + Added Crashed VM power state. + \end{flushleft} + \end{minipage}\\ + \hline + 1.0.3 & 11th Feb. 08 & S. Berger & + \begin{minipage}[t]{7cm} + \begin{flushleft} + Added table of contents and hyperlink cross reference. + \end{flushleft} + \end{minipage}\\ + \hline + 1.0.4 & 23rd March 08 & S. Berger & + \begin{minipage}[t]{7cm} + \begin{flushleft} + Added XSPolicy.can\_run + \end{flushleft} + \end{minipage}\\ + \hline + 1.0.5 & 17th Apr. 08 & S. Berger & + \begin{minipage}[t]{7cm} + \begin{flushleft} + Added undocumented fields and methods for default\_netmask and + default\_gateway to the Network class. Removed an unimplemented + method from the XSPolicy class and removed the 'optional' from + 'oldlabel' parameters. + \end{flushleft} + \end{minipage}\\ + \hline + 1.0.6 & 24th Jul. 08 & Y. Iwamatsu & + \begin{minipage}[t]{7cm} + \begin{flushleft} + Added definitions of new classes DPCI and PPCI. Updated the table + and the diagram representing relationships between classes. + Added host.PPCIs and VM.DPCIs fields. + \end{flushleft} + \end{minipage}\\ + \hline + \end{tabular} +\end{center} diff --git a/docs/xen-api/todo.tex b/docs/xen-api/todo.tex new file mode 100644 index 0000000..615a5e5 --- /dev/null +++ b/docs/xen-api/todo.tex @@ -0,0 +1,135 @@ +% +% Copyright (c) 2006 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +\section{To-Do} + +Lots and lots! Including: + +\subsection{Clarity} + +\begin{itemize} + +\item Roll constructors and get\_by\_uuid etc (section 1.2) into section 2 so +that it is clearer that each class has these. + +\item Emphasise that enums are strings on the wire, and so are not restricted +to a certain number of bits. + +\item Clarify return values, in particular that void means return a status +code, potential error description, but otherwise no value. + +\item Talk about UUID generation. + +\item Clarify session behaviour wrt timeouts and disconnects. + +\item Clarify behaviour of progress field on asynchronous request polling when +that request fails. + +\item Clarify which calls have asynchronous counterparts by marking them as such in the reference. (Individual getters and setters are too small and quick to justify having async versions) + +\end{itemize} + +\subsection{Content} + +\subsubsection{Model} + +\begin{itemize} + +\item Improve the set of available power\_states and corresponding lifecycle +semantics. Rename power\_state, maybe. + +\item Specify the CPU scheduler configuration properly, inc CPU affinity, +weights, etc. + +\item Add Vm.architecture and Host.compatible\_architecture fields. + +\item Add migration calls, including the ability to test whether a migration +will succeed, and authentication token exchange. + +\item Improve asynchronous task handling, with a registration call, a +``blocking poll'' call, and an explicit notification destination. Registration +for ``power\_state'' is useful. + +\item Specify that session keys outlive the HTTP session, and add a timeout +for them (configurable in the tools). + +\item Add places for people to store extra data (``otherConfig'' perhaps) + +\item Specify how hardware UUIDs are used / accessed. + +\item Marking VDIs as exclusive / shareable (locking?) + +\item Consider how to represent CDROMs (as VDIs?) + +\item Define lists of exceptions which may be thrown by each RPC, including +error codes and parameters. + +\item Host characteristics: minimum amount of memory, TPM, network bandwidth, +amount of host memory, amount consumed by VMs, max amount available for new +VMs? + +\item Cooked resource monitoring interface. + +\item Network needs additional attributes that provide media characteristics +of the NIC: + +\begin{itemize} + +\item RO bandwidth integer Bandwidth in mbps +\item RO latency integer time in ms for an icmp roundtrip to a host on the +same subnet. + +\end{itemize} + +\item ACM +\begin{itemize} + +\item A Xen system can be running an access control policy where each +VM's run-time access to resources is restricted by the label it has been given +compared to those of the resources. Currently a VM's configuration file may +contain a line like access\_control[policy='$<$name of the system's +policy$>$',label='$<$label given to VM$>$']. I think the identifiers 'policy' +and 'label' should also be part of the VM class either directly in the form +'access\_control/policy' or indirectly in an access\_control class. + +\end{itemize} + +\item Mike Day's Vm.profile field? + +\item Clone customisation? + +\item NIC teaming? The NIC field of the Network class should be a list (Set) +so that we can signify NIC teaming. (Combining physical NICs in a single host +interface to achieve greater bandwidth). + +\end{itemize} + +\subsubsection{Transport} + +\begin{itemize} + +\item Allow non-HTTP transports. Explicitly allow stdio transport, for SSH. + +\end{itemize} + +\subsubsection{Authentication} + +\begin{itemize} + +\item Delegation to the transport layer. + +\item Extend PAM exchange across the wire. + +\item Fine-grained access control. + +\end{itemize} diff --git a/docs/xen-api/vm-lifecycle.tex b/docs/xen-api/vm-lifecycle.tex new file mode 100644 index 0000000..c584b67 --- /dev/null +++ b/docs/xen-api/vm-lifecycle.tex @@ -0,0 +1,43 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +\section{VM Lifecycle} + +\begin{figure} +\centering +\resizebox{0.9\textwidth}{!}{\includegraphics{vm_lifecycle}} +\caption{VM Lifecycle} +\label{fig-vm-lifecycle} +\end{figure} + +Figure~\ref{fig-vm-lifecycle} shows the states that a VM can be in +and the API calls that can be used to move the VM between these states. The crashed +state indicates that the guest OS running within the VM has crashed. There is no +API to explicitly move to the crashed state, however a hardShutdown will move the +VM to the powered down state. + +\section{VM boot parameters} + +The VM class contains a number of fields that control the way in which the VM is booted. +With reference to the fields defined in the VM class (see later in this document), +this section outlines the boot options available and the mechanisms provided for controlling them. + +VM booting is controlled by setting one of the two mutually exclusive groups: ``PV'', and ``HVM''. If HVM.boot\_policy is the empty string, then paravirtual domain building and booting will be used; otherwise the VM will be loaded as an HVM domain, and booted using an emulated BIOS. + +When paravirtual booting is in use, the PV/bootloader field indicates the bootloader to use. It may be ``pygrub'', in which case the platform's default installation of pygrub will be used, or a full path within the control domain to some other bootloader. The other fields, PV/kernel, PV/ramdisk, PV/args and PV/bootloader\_args will be passed to the bootloader unmodified, and interpretation of those fields is then specific to the bootloader itself, including the possibility that the bootloader will ignore some or all of those given values. Finally the paths of all bootable disks are added to the bootloader commandline (a disk is bootable if its VBD has the bootable flag set). There may be zero, one or many bootable disks; the bootloader decides which disk (if any) to boot from. + +If the bootloader is pygrub, then the menu.lst is parsed if present in the guest's filesystem, otherwise the specified kernel and ramdisk are used, or an autodetected kernel is used if nothing is specified and autodetection is possible. PV/args is appended to the kernel command line, no matter which mechanism is used for finding the kernel. + +If PV/bootloader is empty but PV/kernel is specified, then the kernel and ramdisk values will be treated as paths within the control domain. If both PV/bootloader and PV/kernel are empty, then the behaviour is as if PV/bootloader was specified as ``pygrub''. + +When using HVM booting, HVM/boot\_policy and HVM/boot\_params specify the boot handling. Only one policy is currently defined: ``BIOS order''. In this case, HVM/boot\_params should contain one key-value pair ``order'' = ``N'' where N is the string that will be passed to QEMU. \ No newline at end of file diff --git a/docs/xen-api/vm_lifecycle.dot b/docs/xen-api/vm_lifecycle.dot new file mode 100644 index 0000000..2c062f9 --- /dev/null +++ b/docs/xen-api/vm_lifecycle.dot @@ -0,0 +1,17 @@ +digraph g{ + +node [shape=box]; "powered down" paused running suspended crashed; + +"powered down" -> paused [label="start(paused=true)"]; +"powered down" -> running [label="start(paused=false)"]; +running -> suspended [label="suspend"]; +suspended -> running [label="resume(paused=false)"]; +suspended -> paused [label="resume(paused=true)"]; +paused -> suspended [label="suspend"]; +paused -> running [label="resume"]; +running -> "powered down" [label="cleanShutdown /\nhardShutdown"]; +running -> paused [label="pause"]; +running -> crashed [label="guest OS crash"] +crashed -> "powered down" [label="hardShutdown"] + +} \ No newline at end of file diff --git a/docs/xen-api/wire-protocol.tex b/docs/xen-api/wire-protocol.tex new file mode 100644 index 0000000..6c0dc29 --- /dev/null +++ b/docs/xen-api/wire-protocol.tex @@ -0,0 +1,304 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +\section{Wire Protocol for Remote API Calls} + +API calls are sent over a network to a Xen-enabled host using +the XML-RPC protocol. In this Section we describe how the +higher-level types used in our API Reference are mapped to +primitive XML-RPC types. + +In our API Reference we specify the signatures of API functions in the following +style: +\begin{verbatim} + (ref_vm Set) VM.get_all() +\end{verbatim} +This specifies that the function with name {\tt VM.get\_all} takes +no parameters and returns a Set of {\tt ref\_vm}s. +These types are mapped onto XML-RPC types in a straight-forward manner: +\begin{itemize} + \item Floats, Bools, DateTimes and Strings map directly to the XML-RPC {\tt + double}, {\tt boolean}, {\tt dateTime.iso8601}, and {\tt string} elements. + + \item all ``{\tt ref\_}'' types are opaque references, encoded as the + XML-RPC's {\tt String} type. Users of the API should not make assumptions + about the concrete form of these strings and should not expect them to + remain valid after the client's session with the server has terminated. + + \item fields named ``{\tt uuid}'' of type ``{\tt String}'' are mapped to + the XML-RPC {\tt String} type. The string itself is the OSF + DCE UUID presentation format (as output by {\tt uuidgen}, etc). + + \item ints are all assumed to be 64-bit in our API and are encoded as a + string of decimal digits (rather than using XML-RPC's built-in 32-bit {\tt + i4} type). + + \item values of enum types are encoded as strings. For example, a value of + {\tt destroy} of type {\tt on\_normal\_exit}, would be conveyed as: + \begin{verbatim} + destroy + \end{verbatim} + + \item for all our types, {\tt t}, our type {\tt t Set} simply maps to + XML-RPC's {\tt Array} type, so for example a value of type {\tt cpu\_feature + Set} would be transmitted like this: + + \begin{verbatim} + + + CX8 + PSE36 + FPU + + + \end{verbatim} + + \item for types {\tt k} and {\tt v}, our type {\tt (k, v) Map} maps onto an + XML-RPC struct, with the key as the name of the struct. Note that the {\tt + (k, v) Map} type is only valid when {\tt k} is a {\tt String}, {\tt Ref}, or + {\tt Int}, and in each case the keys of the maps are stringified as + above. For example, the {\tt (String, double) Map} containing a the mappings + Mike $\rightarrow$ 2.3 and John $\rightarrow$ 1.2 would be represented as: + + \begin{verbatim} + + + + Mike + 2.3 + + + John + 1.2 + + + + \end{verbatim} + + \item our {\tt Void} type is transmitted as an empty string. + +\end{itemize} + +\subsection{Note on References vs UUIDs} + +References are opaque types --- encoded as XML-RPC strings on the wire --- understood +only by the particular server which generated them. Servers are free to choose +any concrete representation they find convenient; clients should not make any +assumptions or attempt to parse the string contents. References are not guaranteed +to be permanent identifiers for objects; clients should not assume that references +generated during one session are valid for any future session. References do not +allow objects to be compared for equality. Two references to the same object are +not guaranteed to be textually identical. + +UUIDs are intended to be permanent names for objects. They are +guaranteed to be in the OSF DCE UUID presentation format (as output by {\tt uuidgen}. +Clients may store UUIDs on disk and use them to lookup objects in subsequent sessions +with the server. Clients may also test equality on objects by comparing UUID strings. + +The API provides mechanisms +for translating between UUIDs and opaque references. Each class that contains a UUID +field provides: +\begin{itemize} +\item A ``{\tt get\_by\_uuid}'' method that takes a UUID, $u$, and returns an opaque reference +to the server-side object that has UUID=$u$; +\item A {\tt get\_uuid} function (a regular ``field getter'' RPC) that takes an opaque reference, +$r$, and returns the UUID of the server-side object that is referenced by $r$. +\end{itemize} + +\subsection{Return Values/Status Codes} +\label{synchronous-result} + +The return value of an RPC call is an XML-RPC {\tt Struct}. + +\begin{itemize} +\item The first element of the struct is named {\tt Status}; it +contains a string value indicating whether the result of the call was +a ``{\tt Success}'' or a ``{\tt Failure}''. +\end{itemize} + +If {\tt Status} was set to {\tt Success} then the Struct contains a second +element named {\tt Value}: +\begin{itemize} +\item The element of the struct named {\tt Value} contains the function's return value. +\end{itemize} + +In the case where {\tt Status} is set to {\tt Failure} then +the struct contains a second element named {\tt ErrorDescription}: +\begin{itemize} +\item The element of the struct named {\tt ErrorDescription} contains +an array of string values. The first element of the array is an error code; +the remainder of the array are strings representing error parameters relating +to that code. +\end{itemize} + +For example, an XML-RPC return value from the {\tt host.get\_resident\_VMs} +function above +may look like this: +\begin{verbatim} + + + Status + Success + + + Value + + + + 81547a35-205c-a551-c577-00b982c5fe00 + 61c85a22-05da-b8a2-2e55-06b0847da503 + 1d401ec4-3c17-35a6-fc79-cee6bd9811fe + + + + + +\end{verbatim} + +\section{Making XML-RPC Calls} + +\subsection{Transport Layer} + +The following transport layers are currently supported: +\begin{itemize} +\item HTTP/S for remote administration +\item HTTP over Unix domain sockets for local administration +\end{itemize} + +\subsection{Session Layer} + +The XML-RPC interface is session-based; before you can make arbitrary RPC calls +you must login and initiate a session. For example: +\begin{verbatim} + session_id session.login_with_password(string uname, string pwd) +\end{verbatim} +Where {\tt uname} and {\tt password} refer to your username and password +respectively, as defined by the Xen administrator. +The {\tt session\_id} returned by {\tt session.login\_with\_password} is passed +to subsequent RPC calls as an authentication token. + +A session can be terminated with the {\tt session.logout} function: +\begin{verbatim} + void session.logout(session_id session) +\end{verbatim} + +\subsection{Synchronous and Asynchronous invocation} + +Each method call (apart from methods on ``Session'' and ``Task'' objects +and ``getters'' and ``setters'' derived from fields) +can be made either synchronously or asynchronously. +A synchronous RPC call blocks until the +return value is received; the return value of a synchronous RPC call is +exactly as specified in Section~\ref{synchronous-result}. + +Only synchronous API calls are listed explicitly in this document. +All asynchronous versions are in the special {\tt Async} namespace. +For example, synchronous call {\tt VM.clone(...)} +(described in Chapter~\ref{api-reference}) +has an asynchronous counterpart, {\tt +Async.VM.clone(...)}, that is non-blocking. + +Instead of returning its result directly, an asynchronous RPC call +returns a {\tt task-id}; this identifier is subsequently used +to track the status of a running asynchronous RPC. Note that an asynchronous +call may fail immediately, before a {\tt task-id} has even been created---to +represent this eventuality, the returned {\tt task-id} +is wrapped in an XML-RPC struct with a {\tt Status}, {\tt ErrorDescription} and +{\tt Value} fields, exactly as specified in Section~\ref{synchronous-result}. + +The {\tt task-id} is provided in the {\tt Value} field if {\tt Status} is set to +{\tt Success}. + +The RPC call +\begin{verbatim} + (ref_task Set) Task.get_all(session_id s) +\end{verbatim} +returns a set of all task IDs known to the system. The status (including any +returned result and error codes) of these tasks +can then be queried by accessing the fields of the Task object in the usual way. +Note that, in order to get a consistent snapshot of a task's state, it is advisable to call the ``get\_record'' function. + +\section{Example interactive session} + +This section describes how an interactive session might look, using the python +XML-RPC client library. + +First, initialise python and import the library {\tt xmlrpclib}: + +\begin{verbatim} +\$ python2.4 +... +>>> import xmlrpclib +\end{verbatim} + +Create a python object referencing the remote server: + +\begin{verbatim} +>>> xen = xmlrpclib.Server("http://test:4464") +\end{verbatim} + +Acquire a session token by logging in with a username and password +(error-handling omitted for brevity; the session token is pointed to by the +key {\tt 'Value'} in the returned dictionary) + +\begin{verbatim} +>>> session = session.login_with_password("user", "passwd")['Value'] +\end{verbatim} + +When serialised, this call looks like the following: + +\begin{verbatim} + + + session.login_with_password + + + user + + + passwd + + + +\end{verbatim} + +Next, the user may acquire a list of all the VMs known to the host: (Note the +call takes the session token as the only parameter) + +\begin{verbatim} +>>> all_vms = host.get_resident_VMs(session)['Value'] +>>> all_vms +['OpaqueRef:1', 'OpaqueRef:2', 'OpaqueRef:3', 'OpaqueRef:4' ] +\end{verbatim} + +The VM references here have the form {\tt OpaqueRef:X}, though they may not be +that simple in the future, and you should treat them as opaque strings. +Once a reference to a VM has been acquired a lifecycle operation may be invoked: + +\begin{verbatim} +>>> xen.VM.start(session, all_vms[3], False) +{'Status': 'Failure', 'ErrorDescription': ['VM_BAD_POWER_STATE', 'Halted', 'Running']} +\end{verbatim} + +In this case the {\tt start} message has been rejected, because the VM is +already running, and so an error response has been returned. These high-level +errors are returned as structured data (rather than as XML-RPC faults), +allowing them to be internationalised. Finally, here are some examples of +using accessors for object fields: + +\begin{verbatim} +>>> xen.VM.get_name_label(session, all_vms[3])['Value'] +'SMP' +>>> xen.VM.get_name_description(session, all_vms[3])['Value'] +'Debian for Xen' +\end{verbatim} diff --git a/docs/xen-api/xen.eps b/docs/xen-api/xen.eps new file mode 100644 index 0000000..da14fe9 --- /dev/null +++ b/docs/xen-api/xen.eps @@ -0,0 +1,44 @@ +%!PS-Adobe-3.1 EPSF-3.0 %%Title: xen3-1.0.eps %%Creator: Adobe Illustrator(R) 11 %%AI8_CreatorVersion: 11.0.0 %AI9_PrintingDataBegin %%For: Rich Quarles %%CreationDate: 6/26/06 %%BoundingBox: 0 0 215 94 %%HiResBoundingBox: 0 0 214.1646 93.5196 %%CropBox: 0 0 214.1646 93.5196 %%LanguageLevel: 2 %%DocumentData: Clean7Bit %%Pages: 1 %%DocumentNeededResources: %%DocumentSuppliedResources: procset Adobe_AGM_Image (1.0 0) %%+ procset Adobe_CoolType_Utility_T42 (1.0 0) %%+ procset Adobe_CoolType_Utility_MAKEOCF (1.19 0) %%+ procset Adobe_CoolType_Core (2.23 0) %%+ procset Adobe_AGM_Core (2.0 0) %%+ procset Adobe_AGM_Utils (1.0 0) %%DocumentFonts: %%DocumentNeededFonts: %%DocumentNeededFeatures: %%DocumentSuppliedFeatures: %%DocumentProcessColors: Black %%DocumentCustomColors: %%CMYKCustomColor: %%RGBCustomColor: %ADO_ContainsXMP: MainFirst %AI7_Thumbnail: 128 56 8 %%BeginData: 6266 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD19FFA8A87DA8FD077DA8A8FD70FF7D7D527D527D527D7D7D527D %527D52FD047DFD6AFFA87D527D7D7D52FD0B7D52FD047DA8A8FD65FFA87D %7D527D52FD047DFD09A87D7D527D527D527D7DFD63FFA8FD057DA8A8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8A8FD057DA8FD5FFFA87D527D527D7DA8 %A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A87D7D527D527DA8FD5CFF %A87D527D7DA8A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8A87D7D527DA8FD5AFF7D7D527D7DFD04A8FFA8A8A8FFA8A8A8FFA8A8A8 %FFA8A8A8FFA8A8A8FFFD04A8527D527DA8FD58FFFD057DFFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8A8FD047DA8FD56 %FF7D7D527D7DA8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8A8A8FFA8 %A8A8FFA8A8A8FFA8A8FD047DA8FD54FFA8FD047DFFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFD047DA8FD %52FFA87D527D7DFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8A8A8FFA8A8A8FF %A8A8A8FFFD05A8FFA8FFA8FFFD047DA8FD51FF7D7D7D27FD0E527DA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFFFA827FD0552272727FD09527DFD47FFA8 %527D7D52FD0FF852A8A8A8FFA8A8A8FFA8A8A8FFA8FFA87DFD12F852FD48 %FF7D7D7DA8A827FD0FF87DFFFFA8FFA8FFA8FFA8FFA8FFFF7DFD11F8277D %FD48FF7D7D527DA8FFA827FD0FF8FD04A8FFA8A8A8FFA8FFA852FD11F827 %A8FD49FF7D7D7DA8FFA8FF7DFD0FF827FFA8FFA8FFA8FFA8FFA827FD11F8 %52FD4AFFA8527D7DFFA8A8A8FF52FD0FF852FFA8FFA8A8A8FF7DFD12F87D %FD4BFF7D7D7DA8A8FFA8FFA8FF52FD0FF87DFFA8FFA8FF7DFD12F852A8FD %4AFFA87D527DA8A8A8FFA8A8A8FF27FD0EF827A8FFA8FF52FD11F8277D7D %7DA8FD07FFA8FFA8FFA8FD23FFA8FFA8FD17FFA8527D7DFFA8FFA8FFA8FF %FFFFFD0FF852FFFF27FD11F852FF7D7D7DFFA87D52522727F827F827F827 %27527DFD0BFFFD04A87DA8A8A87DFD04A8FFFFFFA87D5227F827F8272752 %7DFD14FF7D7D52FD04A8FFA8A8A8FFA8A8FD0FF85227FD10F8277DFFA8A8 %5227FD11F82752FD07FFA8FD0CF8FFFFA827FD0CF87DFFFFFFA8F8F8F87D %27F8527DF8F8FD04FF7D7D7DA8FFA8FFA8FFA8FFA8FFFF7DFD1FF827A8FF %FF7D27FD16F8A8FD05FFA8FD0BF827FF7DFD0FF87DFFFFFFA8F87DFF27F8 %2727F8F8FFFFFFA8527D7DA8A8FFA8A8A8FFA8A8A8FFA852FD1DF852A8FF %A852FD19F87DFD04FF52FD0BF82727FD11F8A8FFFFA8F852FFF827F8F852 %F8FFFFFFA87D7DA8A8FFA8FFA8FFA8FFA8FFA8FFA852FD1BF8A8FFFFA827 %FD0BF87DA8FF7D52FD0BF8FD04FF52FD1EF852FFFFFF52A8FFFD047DA852 %FFFFFFA8527D7DFFA8FFA8A8A8FFA8A8A8FFA8FFA827FD18F827A8A8FFA8 %27FD0AF827FD06FF27FD0AF827FFFFFFFD0FF82727FD0EF852FD0FFFA87D %52A8A8FFA8FFA8FFA8FFA8FFA8FFA8FFA827FD16F852FFFFFFA852FD0BF8 %FD07FF52FD0AF827FFFFA8FD0DF827A8FFFFFF52FD0CF852FD0FFFA8527D %7DFFA8A8A8FFA8A8A8FFA8A8A8FFA8FF7DFD15F852FFA8A8A87DFD0BF852 %FD04FFA8FFFF52FD0BF8A8FF7DFD0CF827FD05FFA8FD0CF852FD0FFFA87D %7DA8A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF52FD12F827A8FFA8FFA8FF27 %FD0CF8FD0727FD0CF8A8FF52FD0CF8FD07FFFD0CF87DFD0FFFA8527D7DFF %A8A8A8FFA8A8A8FFA8A8A8FFA8FF52FD12F852A8FFA8A8A8FF7DFD20F8FF %FF27FD0BF852FD06FF7DFD0CF8A8FD0FFFA87D7DA8A8FFA8FFA8FFA8FFA8 %FFA8FFFFA827FD12F827A8FFA8FFA8FFA87DFD1FF827FFFFFD0CF87DFD06 %FF7DFD0BF827FD10FFA8527D7DA8A8FFA8A8A8FFA8A8A8FFA87DFD15F852 %A8A8A8FFA8FF52FD0CF827F827F827F827F827F827F827F827F827F82752 %FF7DFD0CF8A8FD06FF27FD0BF827FD10FFA87D7D7DA8FFA8FFA8FFA8FFA8 %FFFF7DFD17F87DFFFFA8FFA852FD0BF8A8FD15FF7DFD0BF827FD07FF27FD %0BF87DFD11FF7D7D7DA8A8FFA8A8A8FFA8FFA852FD19F87DA8FFA8FF52FD %0BF87DFD15FF27FD0BF827FD06FFA8FD0CF8A8FD11FFA8527DA8FFA8FFA8 %FFA8FFA827FD1BF8A8A8FFFF7DFD0BF87DFD06FF7D272752275227522752 %27277DFFFF27FD0BF87DFD06FF7DFD0CF8FD12FF7D7D52FD06A8FF52FD1D %F827FFA8FFA8FD0CF8A8FD04FF52FD0CF8A8FFA8FD0CF87DFD06FF52FD0B %F852FD13FF7D7D7DFFA8FFFFFF52FD1FF852FFA8FF7DFD0CF8525252FD0D %F8A8FFFF7DFD0CF8FD07FF27FD0BF852FD13FF7D527DA8FFA8A827FD11F8 %2727FD0EF852FFA8FF52FD19F827A8FFFFFF52FD0BF852FD06FFA8FD0CF8 %A8FD13FFA87D52A8FF7DFD12F852FF52FD0FF87DFFA8FF7D27FD15F8277D %FD05FFFD0CF852FD06FF7DFD0CF8FD15FF527D7D52FD12F87DFFA8FF27FD %0EF827A8FFA8FFA87D2727FD0FF85252A8FD06FFA8522727275227272752 %272727A8FD06FF52FD04275227272752272752FD15FFA85227FD11F827A8 %FFA8FFA8A8FD0FF852FFFFA8FF7D7D7DA8A8A87D7D527D52FD047DA8A8FD %40FF7DFD12F852A8FFA8FFA8A8A87DFD0FF87DFFFF7D7D527DFD4DFF7DFD %12F87DA8FFA8FFA8FFA8FFA852FD0FF8A8A87D7D7DA8FD4CFF52FD12F8FD %04A8FFA8A8A8FFA8FF7DFD10F8FD047DFD4CFF27FD11F827FFFFFFA8FFA8 %FFA8FFA8FFA8FF7DFD0FF8277D7DFD4BFFA8FD12F852FFA8FFA8A8A8FFA8 %A8A8FFA8A8A8FF27FD0FF827FD4BFF7DFD0FF827F8277DFFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8A8FD10F87DFD4AFF7DFD0AA87D5252527D7DA8A8FF %A8FFA8A8A8FFA8A8A8FFA8A8A8FFFD04A87DA87DA87DA87DA87DFD0452A8 %A8A8FD56FFA87D7D7DA8A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FD05FFA8FD047DFD5BFFA8527D527D7DA8A8FFA8A8A8FFA8A8A8FF %A8A8A8FFA8A8A8FFA8A8A8FFA8A8FD047D52FD5EFFFD057DA8A8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8A8FD057DFD60FF7D7D527D527D7D %FD04A8FFA8A8A8FFA8A8A8FFFD04A87D7D527D527D7DFD62FFA8A8FD077D %FD04A8FFFD06A8FD077DA8FD67FF7D7D527D527D527D527D527D7D7D527D %527D527D527D7DFD6BFFA8FD047D527D7D7D527D7D7D52FD047DFD70FFFD %04A8FD077DA8A8FFA8FD58FFFF %%EndData %%EndComments %%BeginDefaults %%ViewingOrientation: 1 0 0 1 %%EndDefaults %%BeginProlog %%BeginResource: procset Adobe_AGM_Utils 1.0 0 %%Version: 1.0 0 %%Copyright: Copyright (C) 2000-2003 Adobe Systems, Inc. All Rights Reserved. systemdict /setpacking known { currentpacking true setpacking } if userdict /Adobe_AGM_Utils 68 dict dup begin put /bdf { bind def } bind def /nd{ null def }bdf /xdf { exch def }bdf /ldf { load def }bdf /ddf { put }bdf /xddf { 3 -1 roll put }bdf /xpt { exch put }bdf /ndf { exch dup where{ pop pop pop }{ xdf }ifelse }def /cdndf { exch dup currentdict exch known{ pop pop }{ exch def }ifelse }def /bdict { mark }bdf /edict { counttomark 2 idiv dup dict begin {def} repeat pop currentdict end }def /ps_level /languagelevel where{ pop systemdict /languagelevel get exec }{ 1 }ifelse def /level2 ps_level 2 ge def /level3 ps_level 3 ge def /ps_version {version cvr} stopped { -1 }if def /makereadonlyarray { /packedarray where{ pop packedarray }{ array astore readonly }ifelse }bdf /map_reserved_ink_name { dup type /stringtype eq{ dup /Red eq{ pop (_Red_) }{ dup /Green eq{ pop (_Green_) }{ dup /Blue eq{ pop (_Blue_) }{ dup () cvn eq{ pop (Process) }if }ifelse }ifelse }ifelse }if }bdf /AGMUTIL_GSTATE 22 dict def /get_gstate { AGMUTIL_GSTATE begin /AGMUTIL_GSTATE_clr_spc currentcolorspace def /AGMUTIL_GSTATE_clr_indx 0 def /AGMUTIL_GSTATE_clr_comps 12 array def mark currentcolor counttomark {AGMUTIL_GSTATE_clr_comps AGMUTIL_GSTATE_clr_indx 3 -1 roll put /AGMUTIL_GSTATE_clr_indx AGMUTIL_GSTATE_clr_indx 1 add def} repeat pop /AGMUTIL_GSTATE_fnt rootfont def /AGMUTIL_GSTATE_lw currentlinewidth def /AGMUTIL_GSTATE_lc currentlinecap def /AGMUTIL_GSTATE_lj currentlinejoin def /AGMUTIL_GSTATE_ml currentmiterlimit def currentdash /AGMUTIL_GSTATE_do xdf /AGMUTIL_GSTATE_da xdf /AGMUTIL_GSTATE_sa currentstrokeadjust def /AGMUTIL_GSTATE_clr_rnd currentcolorrendering def /AGMUTIL_GSTATE_op currentoverprint def /AGMUTIL_GSTATE_bg currentblackgeneration cvlit def /AGMUTIL_GSTATE_ucr currentundercolorremoval cvlit def currentcolortransfer cvlit /AGMUTIL_GSTATE_gy_xfer xdf cvlit /AGMUTIL_GSTATE_b_xfer xdf cvlit /AGMUTIL_GSTATE_g_xfer xdf cvlit /AGMUTIL_GSTATE_r_xfer xdf /AGMUTIL_GSTATE_ht currenthalftone def /AGMUTIL_GSTATE_flt currentflat def end }def /set_gstate { AGMUTIL_GSTATE begin AGMUTIL_GSTATE_clr_spc setcolorspace AGMUTIL_GSTATE_clr_indx {AGMUTIL_GSTATE_clr_comps AGMUTIL_GSTATE_clr_indx 1 sub get /AGMUTIL_GSTATE_clr_indx AGMUTIL_GSTATE_clr_indx 1 sub def} repeat setcolor AGMUTIL_GSTATE_fnt setfont AGMUTIL_GSTATE_lw setlinewidth AGMUTIL_GSTATE_lc setlinecap AGMUTIL_GSTATE_lj setlinejoin AGMUTIL_GSTATE_ml setmiterlimit AGMUTIL_GSTATE_da AGMUTIL_GSTATE_do setdash AGMUTIL_GSTATE_sa setstrokeadjust AGMUTIL_GSTATE_clr_rnd setcolorrendering AGMUTIL_GSTATE_op setoverprint AGMUTIL_GSTATE_bg cvx setblackgeneration AGMUTIL_GSTATE_ucr cvx setundercolorremoval AGMUTIL_GSTATE_r_xfer cvx AGMUTIL_GSTATE_g_xfer cvx AGMUTIL_GSTATE_b_xfer cvx AGMUTIL_GSTATE_gy_xfer cvx setcolortransfer AGMUTIL_GSTATE_ht /HalftoneType get dup 9 eq exch 100 eq or { currenthalftone /HalftoneType get AGMUTIL_GSTATE_ht /HalftoneType get ne { mark AGMUTIL_GSTATE_ht {sethalftone} stopped cleartomark } if }{ AGMUTIL_GSTATE_ht sethalftone } ifelse AGMUTIL_GSTATE_flt setflat end }def /get_gstate_and_matrix { AGMUTIL_GSTATE begin /AGMUTIL_GSTATE_ctm matrix currentmatrix def end get_gstate }def /set_gstate_and_matrix { set_gstate AGMUTIL_GSTATE begin AGMUTIL_GSTATE_ctm setmatrix end }def /AGMUTIL_str256 256 string def /AGMUTIL_src256 256 string def /AGMUTIL_dst64 64 string def /AGMUTIL_srcLen nd /AGMUTIL_ndx nd /agm_sethalftone { dup begin /_Data load /Thresholds xdf end level3 { sethalftone }{ dup /HalftoneType get 3 eq { sethalftone } {pop} ifelse }ifelse } def /rdcmntline { currentfile AGMUTIL_str256 readline pop (%) anchorsearch {pop} if } bdf /filter_cmyk { dup type /filetype ne{ exch () /SubFileDecode filter } { exch pop } ifelse [ exch { AGMUTIL_src256 readstring pop dup length /AGMUTIL_srcLen exch def /AGMUTIL_ndx 0 def AGMCORE_plate_ndx 4 AGMUTIL_srcLen 1 sub{ 1 index exch get AGMUTIL_dst64 AGMUTIL_ndx 3 -1 roll put /AGMUTIL_ndx AGMUTIL_ndx 1 add def }for pop AGMUTIL_dst64 0 AGMUTIL_ndx getinterval } bind /exec cvx ] cvx } bdf /filter_indexed_devn { cvi Names length mul names_index add Lookup exch get } bdf /filter_devn { 4 dict begin /srcStr xdf /dstStr xdf dup type /filetype ne{ 0 () /SubFileDecode filter }if [ exch [ /devicen_colorspace_dict /AGMCORE_gget cvx /begin cvx currentdict /srcStr get /readstring cvx /pop cvx /dup cvx /length cvx 0 /gt cvx [ Adobe_AGM_Utils /AGMUTIL_ndx 0 /ddf cvx names_index Names length currentdict /srcStr get length 1 sub { 1 /index cvx /exch cvx /get cvx currentdict /dstStr get /AGMUTIL_ndx /load cvx 3 -1 /roll cvx /put cvx Adobe_AGM_Utils /AGMUTIL_ndx /AGMUTIL_ndx /load cvx 1 /add cvx /ddf cvx } for currentdict /dstStr get 0 /AGMUTIL_ndx /load cvx /getinterval cvx ] cvx /if cvx /end cvx ] cvx bind /exec cvx ] cvx end } bdf /AGMUTIL_imagefile nd /read_image_file { AGMUTIL_imagefile 0 setfileposition 10 dict begin /imageDict xdf /imbufLen Width BitsPerComponent mul 7 add 8 idiv def /imbufIdx 0 def /origDataSource imageDict /DataSource get def /origMultipleDataSources imageDict /MultipleDataSources get def /origDecode imageDict /Decode get def /dstDataStr imageDict /Width get colorSpaceElemCnt mul string def /srcDataStrs [ imageDict begin currentdict /MultipleDataSources known {MultipleDataSources {DataSource length}{1}ifelse}{1} ifelse { Width Decode length 2 div mul cvi string } repeat end ] def imageDict /MultipleDataSources known {MultipleDataSources}{false} ifelse { /imbufCnt imageDict /DataSource get length def /imbufs imbufCnt array def 0 1 imbufCnt 1 sub { /imbufIdx xdf imbufs imbufIdx imbufLen string put imageDict /DataSource get imbufIdx [ AGMUTIL_imagefile imbufs imbufIdx get /readstring cvx /pop cvx ] cvx put } for DeviceN_PS2 { imageDict begin /DataSource [ DataSource /devn_sep_datasource cvx ] cvx def /MultipleDataSources false def /Decode [0 1] def end } if }{ /imbuf imbufLen string def Indexed_DeviceN level3 not and DeviceN_NoneName or { imageDict begin /DataSource [AGMUTIL_imagefile Decode BitsPerComponent false 1 /filter_indexed_devn load dstDataStr srcDataStrs devn_alt_datasource /exec cvx] cvx def /Decode [0 1] def end }{ imageDict /DataSource {AGMUTIL_imagefile imbuf readstring pop} put } ifelse } ifelse imageDict exch load exec imageDict /DataSource origDataSource put imageDict /MultipleDataSources origMultipleDataSources put imageDict /Decode origDecode put end } bdf /write_image_file { begin { (AGMUTIL_imagefile) (w+) file } stopped{ false }{ Adobe_AGM_Utils/AGMUTIL_imagefile xddf 2 dict begin /imbufLen Width BitsPerComponent mul 7 add 8 idiv def MultipleDataSources {DataSource 0 get}{DataSource}ifelse type /filetype eq { /imbuf imbufLen string def }if 1 1 Height { pop MultipleDataSources { 0 1 DataSource length 1 sub { DataSource type dup /arraytype eq { pop DataSource exch get exec }{ /filetype eq { DataSource exch get imbuf readstring pop }{ DataSource exch get } ifelse } ifelse AGMUTIL_imagefile exch writestring } for }{ DataSource type dup /arraytype eq { pop DataSource exec }{ /filetype eq { DataSource imbuf readstring pop }{ DataSource } ifelse } ifelse AGMUTIL_imagefile exch writestring } ifelse }for end true }ifelse end } bdf /close_image_file { AGMUTIL_imagefile closefile (AGMUTIL_imagefile) deletefile }def statusdict /product known userdict /AGMP_current_show known not and{ /pstr statusdict /product get def pstr (HP LaserJet 2200) eq pstr (HP LaserJet 4000 Series) eq or pstr (HP LaserJet 4050 Series ) eq or pstr (HP LaserJet 8000 Series) eq or pstr (HP LaserJet 8100 Series) eq or pstr (HP LaserJet 8150 Series) eq or pstr (HP LaserJet 5000 Series) eq or pstr (HP LaserJet 5100 Series) eq or pstr (HP Color LaserJet 4500) eq or pstr (HP Color LaserJet 4600) eq or pstr (HP LaserJet 5Si) eq or pstr (HP LaserJet 1200 Series) eq or pstr (HP LaserJet 1300 Series) eq or pstr (HP LaserJet 4100 Series) eq or { userdict /AGMP_current_show /show load put userdict /show { currentcolorspace 0 get /Pattern eq {false charpath f} {AGMP_current_show} ifelse } put }if currentdict /pstr undef } if /consumeimagedata { begin currentdict /MultipleDataSources known not {/MultipleDataSources false def} if MultipleDataSources { 1 dict begin /flushbuffer Width cvi string def 1 1 Height cvi { pop 0 1 DataSource length 1 sub { DataSource exch get dup type dup /filetype eq { exch flushbuffer readstring pop pop }if /arraytype eq { exec pop }if }for }for end } { /DataSource load type dup /filetype eq { 1 dict begin /flushbuffer Width Decode length 2 div mul cvi string def 1 1 Height { pop DataSource flushbuffer readstring pop pop} for end }if /arraytype eq { 1 1 Height { pop DataSource pop } for }if }ifelse end }bdf /addprocs { 2{/exec load}repeat 3 1 roll [ 5 1 roll ] bind cvx }def /modify_halftone_xfer { currenthalftone dup length dict copy begin currentdict 2 index known{ 1 index load dup length dict copy begin currentdict/TransferFunction known{ /TransferFunction load }{ currenttransfer }ifelse addprocs /TransferFunction xdf currentdict end def currentdict end sethalftone }{ currentdict/TransferFunction known{ /TransferFunction load }{ currenttransfer }ifelse addprocs /TransferFunction xdf currentdict end sethalftone pop }ifelse }def /clonearray { dup xcheck exch dup length array exch Adobe_AGM_Core/AGMCORE_tmp -1 ddf { Adobe_AGM_Core/AGMCORE_tmp AGMCORE_tmp 1 add ddf dup type /dicttype eq { AGMCORE_tmp exch clonedict Adobe_AGM_Core/AGMCORE_tmp 4 -1 roll ddf } if dup type /arraytype eq { AGMCORE_tmp exch clonearray Adobe_AGM_Core/AGMCORE_tmp 4 -1 roll ddf } if exch dup AGMCORE_tmp 4 -1 roll put }forall exch {cvx} if }bdf /clonedict { dup length dict begin { dup type /dicttype eq { clonedict } if dup type /arraytype eq { clonearray } if def }forall currentdict end }bdf /DeviceN_PS2 { /currentcolorspace AGMCORE_gget 0 get /DeviceN eq level3 not and } bdf /Indexed_DeviceN { /indexed_colorspace_dict AGMCORE_gget dup null ne { /CSD known }{ pop false } ifelse } bdf /DeviceN_NoneName { /Names where { pop false Names { (None) eq or } forall }{ false }ifelse } bdf /DeviceN_PS2_inRip_seps { /AGMCORE_in_rip_sep where { pop dup type dup /arraytype eq exch /packedarraytype eq or { dup 0 get /DeviceN eq level3 not and AGMCORE_in_rip_sep and { /currentcolorspace exch AGMCORE_gput false } { true }ifelse } { true } ifelse } { true } ifelse } bdf /base_colorspace_type { dup type /arraytype eq {0 get} if } bdf /doc_setup{ Adobe_AGM_Utils begin }bdf /doc_trailer{ currentdict Adobe_AGM_Utils eq{ end }if }bdf systemdict /setpacking known { setpacking } if %%EndResource %%BeginResource: procset Adobe_AGM_Core 2.0 0 %%Version: 2.0 0 %%Copyright: Copyright (C) 1997-2003 Adobe Systems, Inc. All Rights Reserved. systemdict /setpacking known { currentpacking true setpacking } if userdict /Adobe_AGM_Core 216 dict dup begin put /nd{ null def }bind def /Adobe_AGM_Core_Id /Adobe_AGM_Core_2.0_0 def /AGMCORE_str256 256 string def /AGMCORE_save nd /AGMCORE_graphicsave nd /AGMCORE_c 0 def /AGMCORE_m 0 def /AGMCORE_y 0 def /AGMCORE_k 0 def /AGMCORE_cmykbuf 4 array def /AGMCORE_screen [currentscreen] cvx def /AGMCORE_tmp 0 def /AGMCORE_&setgray nd /AGMCORE_&setcolor nd /AGMCORE_&setcolorspace nd /AGMCORE_&setcmykcolor nd /AGMCORE_cyan_plate nd /AGMCORE_magenta_plate nd /AGMCORE_yellow_plate nd /AGMCORE_black_plate nd /AGMCORE_plate_ndx nd /AGMCORE_get_ink_data nd /AGMCORE_is_cmyk_sep nd /AGMCORE_host_sep nd /AGMCORE_avoid_L2_sep_space nd /AGMCORE_distilling nd /AGMCORE_composite_job nd /AGMCORE_producing_seps nd /AGMCORE_ps_level -1 def /AGMCORE_ps_version -1 def /AGMCORE_environ_ok nd /AGMCORE_CSA_cache 0 dict def /AGMCORE_CSD_cache 0 dict def /AGMCORE_pattern_cache 0 dict def /AGMCORE_currentoverprint false def /AGMCORE_deltaX nd /AGMCORE_deltaY nd /AGMCORE_name nd /AGMCORE_sep_special nd /AGMCORE_err_strings 4 dict def /AGMCORE_cur_err nd /AGMCORE_ovp nd /AGMCORE_current_spot_alias false def /AGMCORE_inverting false def /AGMCORE_feature_dictCount nd /AGMCORE_feature_opCount nd /AGMCORE_feature_ctm nd /AGMCORE_ConvertToProcess false def /AGMCORE_Default_CTM matrix def /AGMCORE_Default_PageSize nd /AGMCORE_currentbg nd /AGMCORE_currentucr nd /AGMCORE_gradientcache 32 dict def /AGMCORE_in_pattern false def /knockout_unitsq nd /AGMCORE_CRD_cache where{ pop }{ /AGMCORE_CRD_cache 0 dict def }ifelse /AGMCORE_key_known { where{ /Adobe_AGM_Core_Id known }{ false }ifelse }ndf /flushinput { save 2 dict begin /CompareBuffer 3 -1 roll def /readbuffer 256 string def mark { currentfile readbuffer {readline} stopped {cleartomark mark} { not {pop exit} if CompareBuffer eq {exit} if }ifelse }loop cleartomark end restore }bdf /getspotfunction { AGMCORE_screen exch pop exch pop dup type /dicttype eq{ dup /HalftoneType get 1 eq{ /SpotFunction get }{ dup /HalftoneType get 2 eq{ /GraySpotFunction get }{ pop { abs exch abs 2 copy add 1 gt{ 1 sub dup mul exch 1 sub dup mul add 1 sub }{ dup mul exch dup mul add 1 exch sub }ifelse }bind }ifelse }ifelse }if } def /clp_npth { clip newpath } def /eoclp_npth { eoclip newpath } def /npth_clp { newpath clip } def /add_grad { AGMCORE_gradientcache 3 1 roll put }bdf /exec_grad { AGMCORE_gradientcache exch get exec }bdf /graphic_setup { /AGMCORE_graphicsave save def concat 0 setgray 0 setlinecap 0 setlinejoin 1 setlinewidth [] 0 setdash 10 setmiterlimit newpath false setoverprint false setstrokeadjust Adobe_AGM_Core/spot_alias get exec /Adobe_AGM_Image where { pop Adobe_AGM_Image/spot_alias 2 copy known{ get exec }{ pop pop }ifelse } if 100 dict begin /dictstackcount countdictstack def /showpage {} def mark } def /graphic_cleanup { cleartomark dictstackcount 1 countdictstack 1 sub {end}for end AGMCORE_graphicsave restore } def /compose_error_msg { grestoreall initgraphics /Helvetica findfont 10 scalefont setfont /AGMCORE_deltaY 100 def /AGMCORE_deltaX 310 def clippath pathbbox newpath pop pop 36 add exch 36 add exch moveto 0 AGMCORE_deltaY rlineto AGMCORE_deltaX 0 rlineto 0 AGMCORE_deltaY neg rlineto AGMCORE_deltaX neg 0 rlineto closepath 0 AGMCORE_&setgray gsave 1 AGMCORE_&setgray fill grestore 1 setlinewidth gsave stroke grestore currentpoint AGMCORE_deltaY 15 sub add exch 8 add exch moveto /AGMCORE_deltaY 12 def /AGMCORE_tmp 0 def AGMCORE_err_strings exch get { dup 32 eq { pop AGMCORE_str256 0 AGMCORE_tmp getinterval stringwidth pop currentpoint pop add AGMCORE_deltaX 28 add gt { currentpoint AGMCORE_deltaY sub exch pop clippath pathbbox pop pop pop 44 add exch moveto } if AGMCORE_str256 0 AGMCORE_tmp getinterval show ( ) show 0 1 AGMCORE_str256 length 1 sub { AGMCORE_str256 exch 0 put }for /AGMCORE_tmp 0 def } { AGMCORE_str256 exch AGMCORE_tmp xpt /AGMCORE_tmp AGMCORE_tmp 1 add def } ifelse } forall } bdf /doc_setup{ Adobe_AGM_Core begin /AGMCORE_ps_version xdf /AGMCORE_ps_level xdf errordict /AGM_handleerror known not{ errordict /AGM_handleerror errordict /handleerror get put errordict /handleerror { Adobe_AGM_Core begin $error /newerror get AGMCORE_cur_err null ne and{ $error /newerror false put AGMCORE_cur_err compose_error_msg }if $error /newerror true put end errordict /AGM_handleerror get exec } bind put }if /AGMCORE_environ_ok ps_level AGMCORE_ps_level ge ps_version AGMCORE_ps_version ge and AGMCORE_ps_level -1 eq or def AGMCORE_environ_ok not {/AGMCORE_cur_err /AGMCORE_bad_environ def} if /AGMCORE_&setgray systemdict/setgray get def level2{ /AGMCORE_&setcolor systemdict/setcolor get def /AGMCORE_&setcolorspace systemdict/setcolorspace get def }if /AGMCORE_currentbg currentblackgeneration def /AGMCORE_currentucr currentundercolorremoval def /AGMCORE_distilling /product where{ pop systemdict/setdistillerparams known product (Adobe PostScript Parser) ne and }{ false }ifelse def level2 not{ /xput{ dup load dup length exch maxlength eq{ dup dup load dup length dup 0 eq {pop 1} if 2 mul dict copy def }if load begin def end }def }{ /xput{ load 3 1 roll put }def }ifelse /AGMCORE_GSTATE AGMCORE_key_known not{ /AGMCORE_GSTATE 21 dict def /AGMCORE_tmpmatrix matrix def /AGMCORE_gstack 32 array def /AGMCORE_gstackptr 0 def /AGMCORE_gstacksaveptr 0 def /AGMCORE_gstackframekeys 10 def /AGMCORE_&gsave /gsave ldf /AGMCORE_&grestore /grestore ldf /AGMCORE_&grestoreall /grestoreall ldf /AGMCORE_&save /save ldf /AGMCORE_gdictcopy { begin { def } forall end }def /AGMCORE_gput { AGMCORE_gstack AGMCORE_gstackptr get 3 1 roll put }def /AGMCORE_gget { AGMCORE_gstack AGMCORE_gstackptr get exch get }def /gsave { AGMCORE_&gsave AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gstackptr 1 add dup 32 ge {limitcheck} if Adobe_AGM_Core exch /AGMCORE_gstackptr xpt AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gdictcopy }def /grestore { AGMCORE_&grestore AGMCORE_gstackptr 1 sub dup AGMCORE_gstacksaveptr lt {1 add} if Adobe_AGM_Core exch /AGMCORE_gstackptr xpt }def /grestoreall { AGMCORE_&grestoreall Adobe_AGM_Core /AGMCORE_gstackptr AGMCORE_gstacksaveptr put }def /save { AGMCORE_&save AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gstackptr 1 add dup 32 ge {limitcheck} if Adobe_AGM_Core begin /AGMCORE_gstackptr exch def /AGMCORE_gstacksaveptr AGMCORE_gstackptr def end AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gdictcopy }def 0 1 AGMCORE_gstack length 1 sub { AGMCORE_gstack exch AGMCORE_gstackframekeys dict put } for }if level3 /AGMCORE_&sysshfill AGMCORE_key_known not and { /AGMCORE_&sysshfill systemdict/shfill get def /AGMCORE_&usrshfill /shfill load def /AGMCORE_&sysmakepattern systemdict/makepattern get def /AGMCORE_&usrmakepattern /makepattern load def }if /currentcmykcolor [0 0 0 0] AGMCORE_gput /currentstrokeadjust false AGMCORE_gput /currentcolorspace [/DeviceGray] AGMCORE_gput /sep_tint 0 AGMCORE_gput /devicen_tints [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] AGMCORE_gput /sep_colorspace_dict null AGMCORE_gput /devicen_colorspace_dict null AGMCORE_gput /indexed_colorspace_dict null AGMCORE_gput /currentcolor_intent () AGMCORE_gput /customcolor_tint 1 AGMCORE_gput << /MaxPatternItem currentsystemparams /MaxPatternCache get >> setuserparams end }def /page_setup { /setcmykcolor where{ pop Adobe_AGM_Core/AGMCORE_&setcmykcolor /setcmykcolor load put }if Adobe_AGM_Core begin /setcmykcolor { 4 copy AGMCORE_cmykbuf astore /currentcmykcolor exch AGMCORE_gput 1 sub 4 1 roll 3 { 3 index add neg dup 0 lt { pop 0 } if 3 1 roll } repeat setrgbcolor pop }ndf /currentcmykcolor { /currentcmykcolor AGMCORE_gget aload pop }ndf /setoverprint { pop }ndf /currentoverprint { false }ndf /AGMCORE_deviceDPI 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt def /AGMCORE_cyan_plate 1 0 0 0 test_cmyk_color_plate def /AGMCORE_magenta_plate 0 1 0 0 test_cmyk_color_plate def /AGMCORE_yellow_plate 0 0 1 0 test_cmyk_color_plate def /AGMCORE_black_plate 0 0 0 1 test_cmyk_color_plate def /AGMCORE_plate_ndx AGMCORE_cyan_plate{ 0 }{ AGMCORE_magenta_plate{ 1 }{ AGMCORE_yellow_plate{ 2 }{ AGMCORE_black_plate{ 3 }{ 4 }ifelse }ifelse }ifelse }ifelse def /AGMCORE_have_reported_unsupported_color_space false def /AGMCORE_report_unsupported_color_space { AGMCORE_have_reported_unsupported_color_space false eq { (Warning: Job contains content that cannot be separated with on-host methods. This content appears on the black plate, and knocks out all other plates.) == Adobe_AGM_Core /AGMCORE_have_reported_unsupported_color_space true ddf } if }def /AGMCORE_composite_job AGMCORE_cyan_plate AGMCORE_magenta_plate and AGMCORE_yellow_plate and AGMCORE_black_plate and def /AGMCORE_in_rip_sep /AGMCORE_in_rip_sep where{ pop AGMCORE_in_rip_sep }{ AGMCORE_distilling { false }{ userdict/Adobe_AGM_OnHost_Seps known{ false }{ level2{ currentpagedevice/Separations 2 copy known{ get }{ pop pop false }ifelse }{ false }ifelse }ifelse }ifelse }ifelse def /AGMCORE_producing_seps AGMCORE_composite_job not AGMCORE_in_rip_sep or def /AGMCORE_host_sep AGMCORE_producing_seps AGMCORE_in_rip_sep not and def /AGM_preserve_spots /AGM_preserve_spots where{ pop AGM_preserve_spots }{ AGMCORE_distilling AGMCORE_producing_seps or }ifelse def /AGM_is_distiller_preserving_spotimages { currentdistillerparams/PreserveOverprintSettings known { currentdistillerparams/PreserveOverprintSettings get { currentdistillerparams/ColorConversionStrategy known { currentdistillerparams/ColorConversionStrategy get /LeaveColorUnchanged eq }{ true }ifelse }{ false }ifelse }{ false }ifelse }def /convert_spot_to_process where {pop}{ /convert_spot_to_process { dup map_alias { /Name get exch pop } if dup dup (None) eq exch (All) eq or { pop false }{ AGMCORE_host_sep { gsave 1 0 0 0 setcmykcolor currentgray 1 exch sub 0 1 0 0 setcmykcolor currentgray 1 exch sub 0 0 1 0 setcmykcolor currentgray 1 exch sub 0 0 0 1 setcmykcolor currentgray 1 exch sub add add add 0 eq { pop false }{ false setoverprint 1 1 1 1 5 -1 roll findcmykcustomcolor 1 setcustomcolor currentgray 0 eq }ifelse grestore }{ AGMCORE_distilling { pop AGM_is_distiller_preserving_spotimages not }{ Adobe_AGM_Core/AGMCORE_name xddf false Adobe_AGM_Core/AGMCORE_in_pattern known {Adobe_AGM_Core/AGMCORE_in_pattern get}{false} ifelse not currentpagedevice/OverrideSeparations known and { currentpagedevice/OverrideSeparations get { /HqnSpots /ProcSet resourcestatus { pop pop pop true }if }if }if { AGMCORE_name /HqnSpots /ProcSet findresource /TestSpot get exec not }{ gsave [/Separation AGMCORE_name /DeviceGray {}]setcolorspace false currentpagedevice/SeparationColorNames 2 copy known { get { AGMCORE_name eq or}forall not }{ pop pop pop true }ifelse grestore }ifelse }ifelse }ifelse }ifelse }def }ifelse /convert_to_process where {pop}{ /convert_to_process { dup length 0 eq { pop false }{ AGMCORE_host_sep { dup true exch { dup (Cyan) eq exch dup (Magenta) eq 3 -1 roll or exch dup (Yellow) eq 3 -1 roll or exch dup (Black) eq 3 -1 roll or {pop} {convert_spot_to_process and}ifelse } forall { true exch { dup (Cyan) eq exch dup (Magenta) eq 3 -1 roll or exch dup (Yellow) eq 3 -1 roll or exch (Black) eq or and }forall not }{pop false}ifelse }{ false exch { dup (Cyan) eq exch dup (Magenta) eq 3 -1 roll or exch dup (Yellow) eq 3 -1 roll or exch dup (Black) eq 3 -1 roll or {pop} {convert_spot_to_process or}ifelse } forall }ifelse }ifelse }def }ifelse /AGMCORE_avoid_L2_sep_space version cvr 2012 lt level2 and AGMCORE_producing_seps not and def /AGMCORE_is_cmyk_sep AGMCORE_cyan_plate AGMCORE_magenta_plate or AGMCORE_yellow_plate or AGMCORE_black_plate or def /AGM_avoid_0_cmyk where{ pop AGM_avoid_0_cmyk }{ AGM_preserve_spots userdict/Adobe_AGM_OnHost_Seps known userdict/Adobe_AGM_InRip_Seps known or not and }ifelse { /setcmykcolor[ { 4 copy add add add 0 eq currentoverprint and{ pop 0.0005 }if }/exec cvx /AGMCORE_&setcmykcolor load dup type/operatortype ne{ /exec cvx }if ]cvx def }if AGMCORE_host_sep{ /setcolortransfer { AGMCORE_cyan_plate{ pop pop pop }{ AGMCORE_magenta_plate{ 4 3 roll pop pop pop }{ AGMCORE_yellow_plate{ 4 2 roll pop pop pop }{ 4 1 roll pop pop pop }ifelse }ifelse }ifelse settransfer } def /AGMCORE_get_ink_data AGMCORE_cyan_plate{ {pop pop pop} }{ AGMCORE_magenta_plate{ {4 3 roll pop pop pop} }{ AGMCORE_yellow_plate{ {4 2 roll pop pop pop} }{ {4 1 roll pop pop pop} }ifelse }ifelse }ifelse def /AGMCORE_RemoveProcessColorNames { 1 dict begin /filtername { dup /Cyan eq 1 index (Cyan) eq or {pop (_cyan_)}if dup /Magenta eq 1 index (Magenta) eq or {pop (_magenta_)}if dup /Yellow eq 1 index (Yellow) eq or {pop (_yellow_)}if dup /Black eq 1 index (Black) eq or {pop (_black_)}if }def dup type /arraytype eq {[exch {filtername}forall]} {filtername}ifelse end }def /AGMCORE_IsSeparationAProcessColor { dup (Cyan) eq exch dup (Magenta) eq exch dup (Yellow) eq exch (Black) eq or or or }def level3 { /AGMCORE_IsCurrentColor { gsave false setoverprint 1 1 1 1 5 -1 roll findcmykcustomcolor 1 setcustomcolor currentgray 0 eq grestore }def /AGMCORE_filter_functiondatasource { 5 dict begin /data_in xdf data_in type /stringtype eq { /ncomp xdf /comp xdf /string_out data_in length ncomp idiv string def 0 ncomp data_in length 1 sub { string_out exch dup ncomp idiv exch data_in exch ncomp getinterval comp get 255 exch sub put }for string_out }{ string /string_in xdf /string_out 1 string def /component xdf [ data_in string_in /readstring cvx [component /get cvx 255 /exch cvx /sub cvx string_out /exch cvx 0 /exch cvx /put cvx string_out]cvx [/pop cvx ()]cvx /ifelse cvx ]cvx /ReusableStreamDecode filter }ifelse end }def /AGMCORE_separateShadingFunction { 2 dict begin /paint? xdf /channel xdf begin FunctionType 0 eq { /DataSource channel Range length 2 idiv DataSource AGMCORE_filter_functiondatasource def currentdict /Decode known {/Decode Decode channel 2 mul 2 getinterval def}if paint? not {/Decode [1 1]def}if }if FunctionType 2 eq { paint? { /C0 [C0 channel get 1 exch sub] def /C1 [C1 channel get 1 exch sub] def }{ /C0 [1] def /C1 [1] def }ifelse }if FunctionType 3 eq { /Functions [Functions {channel paint? AGMCORE_separateShadingFunction} forall] def }if currentdict /Range known {/Range [0 1] def}if currentdict end end }def /AGMCORE_separateShading { 3 -1 roll begin currentdict /Function known { currentdict /Background known {[1 index{Background 3 index get 1 exch sub}{1}ifelse]/Background xdf}if Function 3 1 roll AGMCORE_separateShadingFunction /Function xdf /ColorSpace [/DeviceGray] def }{ ColorSpace dup type /arraytype eq {0 get}if /DeviceCMYK eq { /ColorSpace [/DeviceN [/_cyan_ /_magenta_ /_yellow_ /_black_] /DeviceCMYK {}] def }{ ColorSpace dup 1 get AGMCORE_RemoveProcessColorNames 1 exch put }ifelse ColorSpace 0 get /Separation eq { { [1 /exch cvx /sub cvx]cvx }{ [/pop cvx 1]cvx }ifelse ColorSpace 3 3 -1 roll put pop }{ { [exch ColorSpace 1 get length 1 sub exch sub /index cvx 1 /exch cvx /sub cvx ColorSpace 1 get length 1 add 1 /roll cvx ColorSpace 1 get length{/pop cvx} repeat]cvx }{ pop [ColorSpace 1 get length {/pop cvx} repeat cvx 1]cvx }ifelse ColorSpace 3 3 -1 roll bind put }ifelse ColorSpace 2 /DeviceGray put }ifelse end }def /AGMCORE_separateShadingDict { dup /ColorSpace get dup type /arraytype ne {[exch]}if dup 0 get /DeviceCMYK eq { exch begin currentdict AGMCORE_cyan_plate {0 true}if AGMCORE_magenta_plate {1 true}if AGMCORE_yellow_plate {2 true}if AGMCORE_black_plate {3 true}if AGMCORE_plate_ndx 4 eq {0 false}if dup not currentoverprint and {/AGMCORE_ignoreshade true def}if AGMCORE_separateShading currentdict end exch }if dup 0 get /Separation eq { exch begin ColorSpace 1 get dup /None ne exch /All ne and { ColorSpace 1 get AGMCORE_IsCurrentColor AGMCORE_plate_ndx 4 lt and ColorSpace 1 get AGMCORE_IsSeparationAProcessColor not and { ColorSpace 2 get dup type /arraytype eq {0 get}if /DeviceCMYK eq { /ColorSpace [ /Separation ColorSpace 1 get /DeviceGray [ ColorSpace 3 get /exec cvx 4 AGMCORE_plate_ndx sub -1 /roll cvx 4 1 /roll cvx 3 [/pop cvx]cvx /repeat cvx 1 /exch cvx /sub cvx ]cvx ]def }{ AGMCORE_report_unsupported_color_space AGMCORE_black_plate not { currentdict 0 false AGMCORE_separateShading }if }ifelse }{ currentdict ColorSpace 1 get AGMCORE_IsCurrentColor 0 exch dup not currentoverprint and {/AGMCORE_ignoreshade true def}if AGMCORE_separateShading }ifelse }if currentdict end exch }if dup 0 get /DeviceN eq { exch begin ColorSpace 1 get convert_to_process { ColorSpace 2 get dup type /arraytype eq {0 get}if /DeviceCMYK eq { /ColorSpace [ /DeviceN ColorSpace 1 get /DeviceGray [ ColorSpace 3 get /exec cvx 4 AGMCORE_plate_ndx sub -1 /roll cvx 4 1 /roll cvx 3 [/pop cvx]cvx /repeat cvx 1 /exch cvx /sub cvx ]cvx ]def }{ AGMCORE_report_unsupported_color_space AGMCORE_black_plate not { currentdict 0 false AGMCORE_separateShading /ColorSpace [/DeviceGray] def }if }ifelse }{ currentdict false -1 ColorSpace 1 get { AGMCORE_IsCurrentColor { 1 add exch pop true exch exit }if 1 add }forall exch dup not currentoverprint and {/AGMCORE_ignoreshade true def}if AGMCORE_separateShading }ifelse currentdict end exch }if dup 0 get dup /DeviceCMYK eq exch dup /Separation eq exch /DeviceN eq or or not { exch begin ColorSpace dup type /arraytype eq {0 get}if /DeviceGray ne { AGMCORE_report_unsupported_color_space AGMCORE_black_plate not { ColorSpace 0 get /CIEBasedA eq { /ColorSpace [/Separation /_ciebaseda_ /DeviceGray {}] def }if ColorSpace 0 get dup /CIEBasedABC eq exch dup /CIEBasedDEF eq exch /DeviceRGB eq or or { /ColorSpace [/DeviceN [/_red_ /_green_ /_blue_] /DeviceRGB {}] def }if ColorSpace 0 get /CIEBasedDEFG eq { /ColorSpace [/DeviceN [/_cyan_ /_magenta_ /_yellow_ /_black_] /DeviceCMYK {}] }if currentdict 0 false AGMCORE_separateShading }if }if currentdict end exch }if pop dup /AGMCORE_ignoreshade known { begin /ColorSpace [/Separation (None) /DeviceGray {}] def currentdict end }if }def /shfill { clonedict AGMCORE_separateShadingDict dup /AGMCORE_ignoreshade known {pop} {AGMCORE_&sysshfill}ifelse }def /makepattern { exch dup /PatternType get 2 eq { clonedict begin /Shading Shading AGMCORE_separateShadingDict def currentdict end exch AGMCORE_&sysmakepattern }{ exch AGMCORE_&usrmakepattern }ifelse }def }if }if AGMCORE_in_rip_sep{ /setcustomcolor { exch aload pop dup 7 1 roll inRip_spot_has_ink not { 4 {4 index mul 4 1 roll} repeat /DeviceCMYK setcolorspace 6 -2 roll pop pop }{ Adobe_AGM_Core begin /AGMCORE_k xdf /AGMCORE_y xdf /AGMCORE_m xdf /AGMCORE_c xdf end [/Separation 4 -1 roll /DeviceCMYK {dup AGMCORE_c mul exch dup AGMCORE_m mul exch dup AGMCORE_y mul exch AGMCORE_k mul} ] setcolorspace }ifelse setcolor }ndf /setseparationgray { [/Separation (All) /DeviceGray {}] setcolorspace_opt 1 exch sub setcolor }ndf }{ /setseparationgray { AGMCORE_&setgray }ndf }ifelse /findcmykcustomcolor { 5 makereadonlyarray }ndf /setcustomcolor { exch aload pop pop 4 {4 index mul 4 1 roll} repeat setcmykcolor pop }ndf /has_color /colorimage where{ AGMCORE_producing_seps{ pop true }{ systemdict eq }ifelse }{ false }ifelse def /map_index { 1 index mul exch getinterval {255 div} forall } bdf /map_indexed_devn { Lookup Names length 3 -1 roll cvi map_index } bdf /n_color_components { base_colorspace_type dup /DeviceGray eq{ pop 1 }{ /DeviceCMYK eq{ 4 }{ 3 }ifelse }ifelse }bdf level2{ /mo /moveto ldf /li /lineto ldf /cv /curveto ldf /knockout_unitsq { 1 setgray 0 0 1 1 rectfill }def /level2ScreenFreq{ begin 60 HalftoneType 1 eq{ pop Frequency }if HalftoneType 2 eq{ pop GrayFrequency }if HalftoneType 5 eq{ pop Default level2ScreenFreq }if end }def /currentScreenFreq{ currenthalftone level2ScreenFreq }def level2 /setcolorspace AGMCORE_key_known not and{ /AGMCORE_&&&setcolorspace /setcolorspace ldf /AGMCORE_ReplaceMappedColor { dup type dup /arraytype eq exch /packedarraytype eq or { dup 0 get dup /Separation eq { pop dup length array copy dup dup 1 get current_spot_alias { dup map_alias { begin /sep_colorspace_dict currentdict AGMCORE_gput pop pop pop [ /Separation Name CSA map_csa dup /MappedCSA xdf /sep_colorspace_proc load ] dup Name end }if }if map_reserved_ink_name 1 xpt }{ /DeviceN eq { dup length array copy dup dup 1 get [ exch { current_spot_alias{ dup map_alias{ /Name get exch pop }if }if map_reserved_ink_name } forall ] 1 xpt }if }ifelse }if }def /setcolorspace { dup type dup /arraytype eq exch /packedarraytype eq or { dup 0 get /Indexed eq { AGMCORE_distilling { /PhotoshopDuotoneList where { pop false }{ true }ifelse }{ true }ifelse { aload pop 3 -1 roll AGMCORE_ReplaceMappedColor 3 1 roll 4 array astore }if }{ AGMCORE_ReplaceMappedColor }ifelse }if DeviceN_PS2_inRip_seps {AGMCORE_&&&setcolorspace} if }def }if }{ /adj { currentstrokeadjust{ transform 0.25 sub round 0.25 add exch 0.25 sub round 0.25 add exch itransform }if }def /mo{ adj moveto }def /li{ adj lineto }def /cv{ 6 2 roll adj 6 2 roll adj 6 2 roll adj curveto }def /knockout_unitsq { 1 setgray 8 8 1 [8 0 0 8 0 0] {} image }def /currentstrokeadjust{ /currentstrokeadjust AGMCORE_gget }def /setstrokeadjust{ /currentstrokeadjust exch AGMCORE_gput }def /currentScreenFreq{ currentscreen pop pop }def /setcolorspace { /currentcolorspace exch AGMCORE_gput } def /currentcolorspace { /currentcolorspace AGMCORE_gget } def /setcolor_devicecolor { base_colorspace_type dup /DeviceGray eq{ pop setgray }{ /DeviceCMYK eq{ setcmykcolor }{ setrgbcolor }ifelse }ifelse }def /setcolor { currentcolorspace 0 get dup /DeviceGray ne{ dup /DeviceCMYK ne{ dup /DeviceRGB ne{ dup /Separation eq{ pop currentcolorspace 3 get exec currentcolorspace 2 get }{ dup /Indexed eq{ pop currentcolorspace 3 get dup type /stringtype eq{ currentcolorspace 1 get n_color_components 3 -1 roll map_index }{ exec }ifelse currentcolorspace 1 get }{ /AGMCORE_cur_err /AGMCORE_invalid_color_space def AGMCORE_invalid_color_space }ifelse }ifelse }if }if }if setcolor_devicecolor } def }ifelse /sop /setoverprint ldf /lw /setlinewidth ldf /lc /setlinecap ldf /lj /setlinejoin ldf /ml /setmiterlimit ldf /dsh /setdash ldf /sadj /setstrokeadjust ldf /gry /setgray ldf /rgb /setrgbcolor ldf /cmyk /setcmykcolor ldf /sep /setsepcolor ldf /devn /setdevicencolor ldf /idx /setindexedcolor ldf /colr /setcolor ldf /csacrd /set_csa_crd ldf /sepcs /setsepcolorspace ldf /devncs /setdevicencolorspace ldf /idxcs /setindexedcolorspace ldf /cp /closepath ldf /clp /clp_npth ldf /eclp /eoclp_npth ldf /f /fill ldf /ef /eofill ldf /@ /stroke ldf /nclp /npth_clp ldf /gset /graphic_setup ldf /gcln /graphic_cleanup ldf currentdict{ dup xcheck 1 index type dup /arraytype eq exch /packedarraytype eq or and { bind }if def }forall /currentpagedevice currentpagedevice def /getrampcolor { /indx exch def 0 1 NumComp 1 sub { dup Samples exch get dup type /stringtype eq { indx get } if exch Scaling exch get aload pop 3 1 roll mul add } for ColorSpaceFamily /Separation eq { sep } { ColorSpaceFamily /DeviceN eq { devn } { setcolor }ifelse }ifelse } bind def /sssetbackground { aload pop setcolor } bind def /RadialShade { 40 dict begin /ColorSpaceFamily exch def /background exch def /ext1 exch def /ext0 exch def /BBox exch def /r2 exch def /c2y exch def /c2x exch def /r1 exch def /c1y exch def /c1x exch def /rampdict exch def /setinkoverprint where {pop /setinkoverprint{pop}def}if gsave BBox length 0 gt { newpath BBox 0 get BBox 1 get moveto BBox 2 get BBox 0 get sub 0 rlineto 0 BBox 3 get BBox 1 get sub rlineto BBox 2 get BBox 0 get sub neg 0 rlineto closepath clip newpath } if c1x c2x eq { c1y c2y lt {/theta 90 def}{/theta 270 def} ifelse } { /slope c2y c1y sub c2x c1x sub div def /theta slope 1 atan def c2x c1x lt c2y c1y ge and { /theta theta 180 sub def} if c2x c1x lt c2y c1y lt and { /theta theta 180 add def} if } ifelse gsave clippath c1x c1y translate theta rotate -90 rotate { pathbbox } stopped { 0 0 0 0 } if /yMax exch def /xMax exch def /yMin exch def /xMin exch def grestore xMax xMin eq yMax yMin eq or { grestore end } { /max { 2 copy gt { pop } {exch pop} ifelse } bind def /min { 2 copy lt { pop } {exch pop} ifelse } bind def rampdict begin 40 dict begin background length 0 gt { background sssetbackground gsave clippath fill grestore } if gsave c1x c1y translate theta rotate -90 rotate /c2y c1x c2x sub dup mul c1y c2y sub dup mul add sqrt def /c1y 0 def /c1x 0 def /c2x 0 def ext0 { 0 getrampcolor c2y r2 add r1 sub 0.0001 lt { c1x c1y r1 360 0 arcn pathbbox /aymax exch def /axmax exch def /aymin exch def /axmin exch def /bxMin xMin axmin min def /byMin yMin aymin min def /bxMax xMax axmax max def /byMax yMax aymax max def bxMin byMin moveto bxMax byMin lineto bxMax byMax lineto bxMin byMax lineto bxMin byMin lineto eofill } { c2y r1 add r2 le { c1x c1y r1 0 360 arc fill } { c2x c2y r2 0 360 arc fill r1 r2 eq { /p1x r1 neg def /p1y c1y def /p2x r1 def /p2y c1y def p1x p1y moveto p2x p2y lineto p2x yMin lineto p1x yMin lineto fill } { /AA r2 r1 sub c2y div def /theta AA 1 AA dup mul sub sqrt div 1 atan def /SS1 90 theta add dup sin exch cos div def /p1x r1 SS1 SS1 mul SS1 SS1 mul 1 add div sqrt mul neg def /p1y p1x SS1 div neg def /SS2 90 theta sub dup sin exch cos div def /p2x r1 SS2 SS2 mul SS2 SS2 mul 1 add div sqrt mul def /p2y p2x SS2 div neg def r1 r2 gt { /L1maxX p1x yMin p1y sub SS1 div add def /L2maxX p2x yMin p2y sub SS2 div add def } { /L1maxX 0 def /L2maxX 0 def }ifelse p1x p1y moveto p2x p2y lineto L2maxX L2maxX p2x sub SS2 mul p2y add lineto L1maxX L1maxX p1x sub SS1 mul p1y add lineto fill } ifelse } ifelse } ifelse } if c1x c2x sub dup mul c1y c2y sub dup mul add 0.5 exp 0 dtransform dup mul exch dup mul add 0.5 exp 72 div 0 72 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 1 index 1 index lt { exch } if pop /hires exch def hires mul /numpix exch def /numsteps NumSamples def /rampIndxInc 1 def /subsampling false def numpix 0 ne { NumSamples numpix div 0.5 gt { /numsteps numpix 2 div round cvi dup 1 le { pop 2 } if def /rampIndxInc NumSamples 1 sub numsteps div def /subsampling true def } if } if /xInc c2x c1x sub numsteps div def /yInc c2y c1y sub numsteps div def /rInc r2 r1 sub numsteps div def /cx c1x def /cy c1y def /radius r1 def newpath xInc 0 eq yInc 0 eq rInc 0 eq and and { 0 getrampcolor cx cy radius 0 360 arc stroke NumSamples 1 sub getrampcolor cx cy radius 72 hires div add 0 360 arc 0 setlinewidth stroke } { 0 numsteps { dup subsampling { round cvi } if getrampcolor cx cy radius 0 360 arc /cx cx xInc add def /cy cy yInc add def /radius radius rInc add def cx cy radius 360 0 arcn eofill rampIndxInc add } repeat pop } ifelse ext1 { c2y r2 add r1 lt { c2x c2y r2 0 360 arc fill } { c2y r1 add r2 sub 0.0001 le { c2x c2y r2 360 0 arcn pathbbox /aymax exch def /axmax exch def /aymin exch def /axmin exch def /bxMin xMin axmin min def /byMin yMin aymin min def /bxMax xMax axmax max def /byMax yMax aymax max def bxMin byMin moveto bxMax byMin lineto bxMax byMax lineto bxMin byMax lineto bxMin byMin lineto eofill } { c2x c2y r2 0 360 arc fill r1 r2 eq { /p1x r2 neg def /p1y c2y def /p2x r2 def /p2y c2y def p1x p1y moveto p2x p2y lineto p2x yMax lineto p1x yMax lineto fill } { /AA r2 r1 sub c2y div def /theta AA 1 AA dup mul sub sqrt div 1 atan def /SS1 90 theta add dup sin exch cos div def /p1x r2 SS1 SS1 mul SS1 SS1 mul 1 add div sqrt mul neg def /p1y c2y p1x SS1 div sub def /SS2 90 theta sub dup sin exch cos div def /p2x r2 SS2 SS2 mul SS2 SS2 mul 1 add div sqrt mul def /p2y c2y p2x SS2 div sub def r1 r2 lt { /L1maxX p1x yMax p1y sub SS1 div add def /L2maxX p2x yMax p2y sub SS2 div add def } { /L1maxX 0 def /L2maxX 0 def }ifelse p1x p1y moveto p2x p2y lineto L2maxX L2maxX p2x sub SS2 mul p2y add lineto L1maxX L1maxX p1x sub SS1 mul p1y add lineto fill } ifelse } ifelse } ifelse } if grestore grestore end end end } ifelse } bind def /GenStrips { 40 dict begin /ColorSpaceFamily exch def /background exch def /ext1 exch def /ext0 exch def /BBox exch def /y2 exch def /x2 exch def /y1 exch def /x1 exch def /rampdict exch def /setinkoverprint where {pop /setinkoverprint{pop}def}if gsave BBox length 0 gt { newpath BBox 0 get BBox 1 get moveto BBox 2 get BBox 0 get sub 0 rlineto 0 BBox 3 get BBox 1 get sub rlineto BBox 2 get BBox 0 get sub neg 0 rlineto closepath clip newpath } if x1 x2 eq { y1 y2 lt {/theta 90 def}{/theta 270 def} ifelse } { /slope y2 y1 sub x2 x1 sub div def /theta slope 1 atan def x2 x1 lt y2 y1 ge and { /theta theta 180 sub def} if x2 x1 lt y2 y1 lt and { /theta theta 180 add def} if } ifelse gsave clippath x1 y1 translate theta rotate { pathbbox } stopped { 0 0 0 0 } if /yMax exch def /xMax exch def /yMin exch def /xMin exch def grestore xMax xMin eq yMax yMin eq or { grestore end } { rampdict begin 20 dict begin background length 0 gt { background sssetbackground gsave clippath fill grestore } if gsave x1 y1 translate theta rotate /xStart 0 def /xEnd x2 x1 sub dup mul y2 y1 sub dup mul add 0.5 exp def /ySpan yMax yMin sub def /numsteps NumSamples def /rampIndxInc 1 def /subsampling false def xStart 0 transform xEnd 0 transform 3 -1 roll sub dup mul 3 1 roll sub dup mul add 0.5 exp 72 div 0 72 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 1 index 1 index lt { exch } if pop mul /numpix exch def numpix 0 ne { NumSamples numpix div 0.5 gt { /numsteps numpix 2 div round cvi dup 1 le { pop 2 } if def /rampIndxInc NumSamples 1 sub numsteps div def /subsampling true def } if } if ext0 { 0 getrampcolor xMin xStart lt { xMin yMin xMin neg ySpan rectfill } if } if /xInc xEnd xStart sub numsteps div def /x xStart def 0 numsteps { dup subsampling { round cvi } if getrampcolor x yMin xInc ySpan rectfill /x x xInc add def rampIndxInc add } repeat pop ext1 { xMax xEnd gt { xEnd yMin xMax xEnd sub ySpan rectfill } if } if grestore grestore end end end } ifelse } bind def }def /page_trailer { end }def /doc_trailer{ }def systemdict /findcolorrendering known{ /findcolorrendering systemdict /findcolorrendering get def }if systemdict /setcolorrendering known{ /setcolorrendering systemdict /setcolorrendering get def }if /test_cmyk_color_plate { gsave setcmykcolor currentgray 1 ne grestore }def /inRip_spot_has_ink { dup Adobe_AGM_Core/AGMCORE_name xddf convert_spot_to_process not }def /map255_to_range { 1 index sub 3 -1 roll 255 div mul add }def /set_csa_crd { /sep_colorspace_dict null AGMCORE_gput begin CSA map_csa setcolorspace_opt set_crd end } def /setsepcolor { /sep_colorspace_dict AGMCORE_gget begin dup /sep_tint exch AGMCORE_gput TintProc end } def /setdevicencolor { /devicen_colorspace_dict AGMCORE_gget begin Names length copy Names length 1 sub -1 0 { /devicen_tints AGMCORE_gget 3 1 roll xpt } for TintProc end } def /sep_colorspace_proc { Adobe_AGM_Core/AGMCORE_tmp xddf /sep_colorspace_dict AGMCORE_gget begin currentdict/Components known{ Components aload pop TintMethod/Lab eq{ 2 {AGMCORE_tmp mul NComponents 1 roll} repeat LMax sub AGMCORE_tmp mul LMax add NComponents 1 roll }{ TintMethod/Subtractive eq{ NComponents{ AGMCORE_tmp mul NComponents 1 roll }repeat }{ NComponents{ 1 sub AGMCORE_tmp mul 1 add NComponents 1 roll } repeat }ifelse }ifelse }{ ColorLookup AGMCORE_tmp ColorLookup length 1 sub mul round cvi get aload pop }ifelse end } def /sep_colorspace_gray_proc { Adobe_AGM_Core/AGMCORE_tmp xddf /sep_colorspace_dict AGMCORE_gget begin GrayLookup AGMCORE_tmp GrayLookup length 1 sub mul round cvi get end } def /sep_proc_name { dup 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or level2 not and has_color not and{ pop [/DeviceGray] /sep_colorspace_gray_proc }{ /sep_colorspace_proc }ifelse } def /setsepcolorspace { current_spot_alias{ dup begin Name map_alias{ exch pop }if end }if dup /sep_colorspace_dict exch AGMCORE_gput begin /MappedCSA CSA map_csa def Adobe_AGM_Core/AGMCORE_sep_special Name dup () eq exch (All) eq or ddf AGMCORE_avoid_L2_sep_space{ [/Indexed MappedCSA sep_proc_name 255 exch { 255 div } /exec cvx 3 -1 roll [ 4 1 roll load /exec cvx ] cvx ] setcolorspace_opt /TintProc { 255 mul round cvi setcolor }bdf }{ MappedCSA 0 get /DeviceCMYK eq currentdict/Components known and AGMCORE_sep_special not and{ /TintProc [ Components aload pop Name findcmykcustomcolor /exch cvx /setcustomcolor cvx ] cvx bdf }{ AGMCORE_host_sep Name (All) eq and{ /TintProc { 1 exch sub setseparationgray }bdf }{ AGMCORE_in_rip_sep MappedCSA 0 get /DeviceCMYK eq and AGMCORE_host_sep or Name () eq and{ /TintProc [ MappedCSA sep_proc_name exch 0 get /DeviceCMYK eq{ cvx /setcmykcolor cvx }{ cvx /setgray cvx }ifelse ] cvx bdf }{ AGMCORE_producing_seps MappedCSA 0 get dup /DeviceCMYK eq exch /DeviceGray eq or and AGMCORE_sep_special not and{ /TintProc [ /dup cvx MappedCSA sep_proc_name cvx exch 0 get /DeviceGray eq{ 1 /exch cvx /sub cvx 0 0 0 4 -1 /roll cvx }if /Name cvx /findcmykcustomcolor cvx /exch cvx AGMCORE_host_sep{ AGMCORE_is_cmyk_sep /Name cvx /AGMCORE_IsSeparationAProcessColor load /exec cvx /not cvx /and cvx }{ Name inRip_spot_has_ink not }ifelse [ /pop cvx 1 ] cvx /if cvx /setcustomcolor cvx ] cvx bdf }{ /TintProc /setcolor ldf [/Separation Name MappedCSA sep_proc_name load ] setcolorspace_opt }ifelse }ifelse }ifelse }ifelse }ifelse set_crd setsepcolor end } def /additive_blend { 3 dict begin /numarrays xdf /numcolors xdf 0 1 numcolors 1 sub { /c1 xdf 1 0 1 numarrays 1 sub { 1 exch add /index cvx c1 /get cvx /mul cvx }for numarrays 1 add 1 /roll cvx }for numarrays [/pop cvx] cvx /repeat cvx end }def /subtractive_blend { 3 dict begin /numarrays xdf /numcolors xdf 0 1 numcolors 1 sub { /c1 xdf 1 1 0 1 numarrays 1 sub { 1 3 3 -1 roll add /index cvx c1 /get cvx /sub cvx /mul cvx }for /sub cvx numarrays 1 add 1 /roll cvx }for numarrays [/pop cvx] cvx /repeat cvx end }def /exec_tint_transform { /TintProc [ /TintTransform cvx /setcolor cvx ] cvx bdf MappedCSA setcolorspace_opt } bdf /devn_makecustomcolor { 2 dict begin /names_index xdf /Names xdf 1 1 1 1 Names names_index get findcmykcustomcolor /devicen_tints AGMCORE_gget names_index get setcustomcolor Names length {pop} repeat end } bdf /setdevicencolorspace { dup /AliasedColorants known {false}{true}ifelse current_spot_alias and { 6 dict begin /names_index 0 def dup /names_len exch /Names get length def /new_names names_len array def /new_LookupTables names_len array def /alias_cnt 0 def dup /Names get { dup map_alias { exch pop dup /ColorLookup known { dup begin new_LookupTables names_index ColorLookup put end }{ dup /Components known { dup begin new_LookupTables names_index Components put end }{ dup begin new_LookupTables names_index [null null null null] put end } ifelse } ifelse new_names names_index 3 -1 roll /Name get put /alias_cnt alias_cnt 1 add def }{ /name xdf new_names names_index name put dup /LookupTables known { dup begin new_LookupTables names_index LookupTables names_index get put end }{ dup begin new_LookupTables names_index [null null null null] put end } ifelse } ifelse /names_index names_index 1 add def } forall alias_cnt 0 gt { /AliasedColorants true def 0 1 names_len 1 sub { /names_index xdf new_LookupTables names_index get 0 get null eq { dup /Names get names_index get /name xdf name (Cyan) eq name (Magenta) eq name (Yellow) eq name (Black) eq or or or not { /AliasedColorants false def exit } if } if } for AliasedColorants { dup begin /Names new_names def /AliasedColorants true def /LookupTables new_LookupTables def currentdict /TTTablesIdx known not { /TTTablesIdx -1 def } if currentdict /NComponents known not { /NComponents TintMethod /Subtractive eq {4}{3}ifelse def } if end } if }if end } if dup /devicen_colorspace_dict exch AGMCORE_gput begin /MappedCSA CSA map_csa def currentdict /AliasedColorants known { AliasedColorants }{ false } ifelse /TintTransform load type /nulltype eq or { /TintTransform [ 0 1 Names length 1 sub { /TTTablesIdx TTTablesIdx 1 add def dup LookupTables exch get dup 0 get null eq { 1 index Names exch get dup (Cyan) eq { pop exch LookupTables length exch sub /index cvx 0 0 0 } { dup (Magenta) eq { pop exch LookupTables length exch sub /index cvx 0 /exch cvx 0 0 } { (Yellow) eq { exch LookupTables length exch sub /index cvx 0 0 3 -1 /roll cvx 0 } { exch LookupTables length exch sub /index cvx 0 0 0 4 -1 /roll cvx } ifelse } ifelse } ifelse 5 -1 /roll cvx /astore cvx } { dup length 1 sub LookupTables length 4 -1 roll sub 1 add /index cvx /mul cvx /round cvx /cvi cvx /get cvx } ifelse Names length TTTablesIdx add 1 add 1 /roll cvx } for Names length [/pop cvx] cvx /repeat cvx NComponents Names length TintMethod /Subtractive eq { subtractive_blend } { additive_blend } ifelse ] cvx bdf } if AGMCORE_host_sep { Names convert_to_process { exec_tint_transform } { currentdict /AliasedColorants known { AliasedColorants not }{ false } ifelse 5 dict begin /AvoidAliasedColorants xdf /painted? false def /names_index 0 def /names_len Names length def Names { AvoidAliasedColorants { /currentspotalias current_spot_alias def false set_spot_alias } if AGMCORE_is_cmyk_sep { dup (Cyan) eq AGMCORE_cyan_plate and exch dup (Magenta) eq AGMCORE_magenta_plate and exch dup (Yellow) eq AGMCORE_yellow_plate and exch (Black) eq AGMCORE_black_plate and or or or { /devicen_colorspace_dict AGMCORE_gget /TintProc [ Names names_index /devn_makecustomcolor cvx ] cvx ddf /painted? true def } if painted? {exit} if }{ 0 0 0 0 5 -1 roll findcmykcustomcolor 1 setcustomcolor currentgray 0 eq { /devicen_colorspace_dict AGMCORE_gget /TintProc [ Names names_index /devn_makecustomcolor cvx ] cvx ddf /painted? true def exit } if } ifelse AvoidAliasedColorants { currentspotalias set_spot_alias } if /names_index names_index 1 add def } forall painted? { /devicen_colorspace_dict AGMCORE_gget /names_index names_index put }{ /devicen_colorspace_dict AGMCORE_gget /TintProc [ names_len [/pop cvx] cvx /repeat cvx 1 /setseparationgray cvx 0 0 0 0 () /findcmykcustomcolor cvx 0 /setcustomcolor cvx ] cvx ddf } ifelse end } ifelse } { AGMCORE_in_rip_sep { Names convert_to_process not }{ level3 } ifelse { [/DeviceN Names MappedCSA /TintTransform load] setcolorspace_opt /TintProc level3 not AGMCORE_in_rip_sep and { [ Names /length cvx [/pop cvx] cvx /repeat cvx ] cvx bdf }{ /setcolor ldf } ifelse }{ exec_tint_transform } ifelse } ifelse set_crd /AliasedColorants false def end } def /setindexedcolorspace { dup /indexed_colorspace_dict exch AGMCORE_gput begin currentdict /CSD known { CSD get_csd /Names known { CSD get_csd begin currentdict devncs AGMCORE_host_sep{ 4 dict begin /devnCompCnt Names length def /NewLookup HiVal 1 add string def 0 1 HiVal { /tableIndex xdf Lookup dup type /stringtype eq { devnCompCnt tableIndex map_index }{ exec } ifelse setdevicencolor currentgray tableIndex exch HiVal mul cvi NewLookup 3 1 roll put } for [/Indexed currentcolorspace HiVal NewLookup] setcolorspace_opt end }{ level3 { [/Indexed [/DeviceN Names MappedCSA /TintTransform load] HiVal Lookup] setcolorspace_opt }{ [/Indexed MappedCSA HiVal [ Lookup dup type /stringtype eq {/exch cvx CSD get_csd /Names get length dup /mul cvx exch /getinterval cvx {255 div} /forall cvx} {/exec cvx}ifelse /TintTransform load /exec cvx ]cvx ]setcolorspace_opt }ifelse } ifelse end }{ } ifelse set_crd } { /MappedCSA CSA map_csa def AGMCORE_host_sep level2 not and{ 0 0 0 0 setcmykcolor }{ [/Indexed MappedCSA level2 not has_color not and{ dup 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or{ pop [/DeviceGray] }if HiVal GrayLookup }{ HiVal currentdict/RangeArray known{ { /indexed_colorspace_dict AGMCORE_gget begin Lookup exch dup HiVal gt{ pop HiVal }if NComponents mul NComponents getinterval {} forall NComponents 1 sub -1 0{ RangeArray exch 2 mul 2 getinterval aload pop map255_to_range NComponents 1 roll }for end } bind }{ Lookup }ifelse }ifelse ] setcolorspace_opt set_crd }ifelse }ifelse end }def /setindexedcolor { AGMCORE_host_sep { /indexed_colorspace_dict AGMCORE_gget dup /CSD known { begin CSD get_csd begin map_indexed_devn devn end end }{ AGMCORE_gget/Lookup get 4 3 -1 roll map_index pop setcmykcolor } ifelse }{ level3 not AGMCORE_in_rip_sep and /indexed_colorspace_dict AGMCORE_gget /CSD known and { /indexed_colorspace_dict AGMCORE_gget /CSD get get_csd begin map_indexed_devn devn end } { setcolor } ifelse }ifelse } def /ignoreimagedata { currentoverprint not{ gsave dup clonedict begin 1 setgray /Decode [0 1] def /DataSource def /MultipleDataSources false def /BitsPerComponent 8 def currentdict end systemdict /image get exec grestore }if consumeimagedata }def /add_csa { Adobe_AGM_Core begin /AGMCORE_CSA_cache xput end }def /get_csa_by_name { dup type dup /nametype eq exch /stringtype eq or{ Adobe_AGM_Core begin 1 dict begin /name xdf AGMCORE_CSA_cache { 0 get name eq { exit }{ pop } ifelse }forall end end }{ pop } ifelse }def /map_csa { dup type /nametype eq{ Adobe_AGM_Core/AGMCORE_CSA_cache get exch get }if }def /add_csd { Adobe_AGM_Core begin /AGMCORE_CSD_cache xput end }def /get_csd { dup type /nametype eq{ Adobe_AGM_Core/AGMCORE_CSD_cache get exch get }if }def /pattern_buf_init { /count get 0 0 put } def /pattern_buf_next { dup /count get dup 0 get dup 3 1 roll 1 add 0 xpt get } def /cachepattern_compress { 5 dict begin currentfile exch 0 exch /SubFileDecode filter /ReadFilter exch def /patarray 20 dict def /string_size 16000 def /readbuffer string_size string def currentglobal true setglobal patarray 1 array dup 0 1 put /count xpt setglobal /LZWFilter { exch dup length 0 eq { pop }{ patarray dup length 1 sub 3 -1 roll put } ifelse {string_size}{0}ifelse string } /LZWEncode filter def { ReadFilter readbuffer readstring exch LZWFilter exch writestring not {exit} if } loop LZWFilter closefile patarray end }def /cachepattern { 2 dict begin currentfile exch 0 exch /SubFileDecode filter /ReadFilter exch def /patarray 20 dict def currentglobal true setglobal patarray 1 array dup 0 1 put /count xpt setglobal { ReadFilter 16000 string readstring exch patarray dup length 1 sub 3 -1 roll put not {exit} if } loop patarray dup dup length 1 sub () put end }def /add_pattern { Adobe_AGM_Core begin /AGMCORE_pattern_cache xput end }def /get_pattern { dup type /nametype eq{ Adobe_AGM_Core/AGMCORE_pattern_cache get exch get dup wrap_paintproc }if }def /wrap_paintproc { statusdict /currentfilenameextend known{ begin /OldPaintProc /PaintProc load def /PaintProc { mark exch dup /OldPaintProc get stopped {closefile restore end} if cleartomark } def end } {pop} ifelse } def /make_pattern { dup matrix currentmatrix matrix concatmatrix 0 0 3 2 roll itransform exch 3 index /XStep get 1 index exch 2 copy div cvi mul sub sub exch 3 index /YStep get 1 index exch 2 copy div cvi mul sub sub matrix translate exch matrix concatmatrix 1 index begin BBox 0 get XStep div cvi XStep mul /xshift exch neg def BBox 1 get YStep div cvi YStep mul /yshift exch neg def BBox 0 get xshift add BBox 1 get yshift add BBox 2 get xshift add BBox 3 get yshift add 4 array astore /BBox exch def [ xshift yshift /translate load null /exec load ] dup 3 /PaintProc load put cvx /PaintProc exch def end gsave 0 setgray makepattern grestore }def /set_pattern { dup /PatternType get 1 eq{ dup /PaintType get 1 eq{ currentoverprint sop [/DeviceGray] setcolorspace 0 setgray }if }if setpattern }def /setcolorspace_opt { dup currentcolorspace eq{ pop }{ setcolorspace }ifelse }def /updatecolorrendering { currentcolorrendering/Intent known{ currentcolorrendering/Intent get }{ null }ifelse Intent ne{ false Intent AGMCORE_CRD_cache { exch pop begin dup Intent eq{ currentdict setcolorrendering_opt end exch pop true exch exit }if end } forall pop not{ systemdict /findcolorrendering known{ Intent findcolorrendering pop /ColorRendering findresource dup length dict copy setcolorrendering_opt }if }if }if } def /add_crd { AGMCORE_CRD_cache 3 1 roll put }def /set_crd { AGMCORE_host_sep not level2 and{ currentdict/CRD known{ AGMCORE_CRD_cache CRD get dup null ne{ setcolorrendering_opt }{ pop }ifelse }{ currentdict/Intent known{ updatecolorrendering }if }ifelse currentcolorspace dup type /arraytype eq {0 get}if /DeviceRGB eq { currentdict/UCR known {/UCR}{/AGMCORE_currentucr}ifelse load setundercolorremoval currentdict/BG known {/BG}{/AGMCORE_currentbg}ifelse load setblackgeneration }if }if }def /setcolorrendering_opt { dup currentcolorrendering eq{ pop }{ begin /Intent Intent def currentdict end setcolorrendering }ifelse }def /cpaint_gcomp { convert_to_process Adobe_AGM_Core/AGMCORE_ConvertToProcess xddf Adobe_AGM_Core/AGMCORE_ConvertToProcess get not { (%end_cpaint_gcomp) flushinput }if }def /cpaint_gsep { Adobe_AGM_Core/AGMCORE_ConvertToProcess get { (%end_cpaint_gsep) flushinput }if }def /cpaint_gend { newpath }def /path_rez { dup 0 ne{ AGMCORE_deviceDPI exch div dup 1 lt{ pop 1 }if setflat }{ pop }ifelse }def /set_spot_alias_ary { /AGMCORE_SpotAliasAry where{ pop pop }{ Adobe_AGM_Core/AGMCORE_SpotAliasAry xddf true set_spot_alias }ifelse }def /set_spot_alias { /AGMCORE_SpotAliasAry where{ /AGMCORE_current_spot_alias 3 -1 roll put }{ pop }ifelse }def /current_spot_alias { /AGMCORE_SpotAliasAry where{ /AGMCORE_current_spot_alias get }{ false }ifelse }def /map_alias { /AGMCORE_SpotAliasAry where{ begin /AGMCORE_name xdf false AGMCORE_SpotAliasAry{ dup/Name get AGMCORE_name eq{ save exch /Adobe_AGM_Core currentdict def /CSD get get_csd exch restore exch pop true exit }{ pop }ifelse }forall end }{ pop false }ifelse }bdf /spot_alias { true set_spot_alias /AGMCORE_&setcustomcolor AGMCORE_key_known not { Adobe_AGM_Core/AGMCORE_&setcustomcolor /setcustomcolor load put } if /customcolor_tint 1 AGMCORE_gput Adobe_AGM_Core begin /setcustomcolor { dup /customcolor_tint exch AGMCORE_gput current_spot_alias{ 1 index 4 get map_alias{ mark 3 1 roll setsepcolorspace counttomark 0 ne{ setsepcolor }if pop pop }{ AGMCORE_&setcustomcolor }ifelse }{ AGMCORE_&setcustomcolor }ifelse }bdf end }def /begin_feature { Adobe_AGM_Core/AGMCORE_feature_dictCount countdictstack put count Adobe_AGM_Core/AGMCORE_feature_opCount 3 -1 roll put {Adobe_AGM_Core/AGMCORE_feature_ctm matrix currentmatrix put}if }def /end_feature { 2 dict begin /spd /setpagedevice load def /setpagedevice { get_gstate spd set_gstate } def stopped{$error/newerror false put}if end count Adobe_AGM_Core/AGMCORE_feature_opCount get sub dup 0 gt{{pop}repeat}{pop}ifelse countdictstack Adobe_AGM_Core/AGMCORE_feature_dictCount get sub dup 0 gt{{end}repeat}{pop}ifelse {Adobe_AGM_Core/AGMCORE_feature_ctm get setmatrix}if }def /set_negative { Adobe_AGM_Core begin /AGMCORE_inverting exch def level2{ currentpagedevice/NegativePrint known{ currentpagedevice/NegativePrint get Adobe_AGM_Core/AGMCORE_inverting get ne{ true begin_feature true{ bdict /NegativePrint Adobe_AGM_Core/AGMCORE_inverting get edict setpagedevice }end_feature }if /AGMCORE_inverting false def }if }if AGMCORE_inverting{ [{1 exch sub}/exec load dup currenttransfer exch]cvx bind settransfer gsave newpath clippath 1 /setseparationgray where{pop setseparationgray}{setgray}ifelse /AGMIRS_&fill where {pop AGMIRS_&fill}{fill} ifelse grestore }if end }def /lw_save_restore_override { /md where { pop md begin initializepage /initializepage{}def /pmSVsetup{} def /endp{}def /pse{}def /psb{}def /orig_showpage where {pop} {/orig_showpage /showpage load def} ifelse /showpage {orig_showpage gR} def end }if }def /pscript_showpage_override { /NTPSOct95 where { begin showpage save /showpage /restore load def /restore {exch pop}def end }if }def /driver_media_override { /md where { pop md /initializepage known { md /initializepage {} put } if md /rC known { md /rC {4{pop}repeat} put } if }if /mysetup where { /mysetup [1 0 0 1 0 0] put }if Adobe_AGM_Core /AGMCORE_Default_CTM matrix currentmatrix put level2 {Adobe_AGM_Core /AGMCORE_Default_PageSize currentpagedevice/PageSize get put}if }def /driver_check_media_override { /PrepsDict where {pop} { Adobe_AGM_Core /AGMCORE_Default_CTM get matrix currentmatrix ne Adobe_AGM_Core /AGMCORE_Default_PageSize get type /arraytype eq { Adobe_AGM_Core /AGMCORE_Default_PageSize get 0 get currentpagedevice/PageSize get 0 get eq and Adobe_AGM_Core /AGMCORE_Default_PageSize get 1 get currentpagedevice/PageSize get 1 get eq and }if { Adobe_AGM_Core /AGMCORE_Default_CTM get setmatrix }if }ifelse }def AGMCORE_err_strings begin /AGMCORE_bad_environ (Environment not satisfactory for this job. Ensure that the PPD is correct or that the PostScript level requested is supported by this printer. ) def /AGMCORE_color_space_onhost_seps (This job contains colors that will not separate with on-host methods. ) def /AGMCORE_invalid_color_space (This job contains an invalid color space. ) def end end systemdict /setpacking known { setpacking } if %%EndResource %%BeginResource: procset Adobe_CoolType_Core 2.23 0 %%Copyright: Copyright 1997-2003 Adobe Systems Incorporated. All Rights Reserved. %%Version: 2.23 0 10 dict begin /Adobe_CoolType_Passthru currentdict def /Adobe_CoolType_Core_Defined userdict /Adobe_CoolType_Core known def Adobe_CoolType_Core_Defined { /Adobe_CoolType_Core userdict /Adobe_CoolType_Core get def } if userdict /Adobe_CoolType_Core 60 dict dup begin put /Adobe_CoolType_Version 2.23 def /Level2? systemdict /languagelevel known dup { pop systemdict /languagelevel get 2 ge } if def Level2? not { /currentglobal false def /setglobal /pop load def /gcheck { pop false } bind def /currentpacking false def /setpacking /pop load def /SharedFontDirectory 0 dict def } if currentpacking true setpacking /@_SaveStackLevels { Adobe_CoolType_Data begin @opStackCountByLevel @opStackLevel 2 copy known not { 2 copy 3 dict dup /args 7 index 5 add array put put get } { get dup /args get dup length 3 index lt { dup length 5 add array exch 1 index exch 0 exch putinterval 1 index exch /args exch put } { pop } ifelse } ifelse begin count 2 sub 1 index lt { pop count 1 sub } if dup /argCount exch def dup 0 gt { exch 1 index 2 add 1 roll args exch 0 exch getinterval astore pop } { pop } ifelse count 1 sub /restCount exch def end /@opStackLevel @opStackLevel 1 add def countdictstack 1 sub @dictStackCountByLevel exch @dictStackLevel exch put /@dictStackLevel @dictStackLevel 1 add def end } bind def /@_RestoreStackLevels { Adobe_CoolType_Data begin /@opStackLevel @opStackLevel 1 sub def @opStackCountByLevel @opStackLevel get begin count restCount sub dup 0 gt { { pop } repeat } { pop } ifelse args 0 argCount getinterval {} forall end /@dictStackLevel @dictStackLevel 1 sub def @dictStackCountByLevel @dictStackLevel get end countdictstack exch sub dup 0 gt { { end } repeat } { pop } ifelse } bind def /@_PopStackLevels { Adobe_CoolType_Data begin /@opStackLevel @opStackLevel 1 sub def /@dictStackLevel @dictStackLevel 1 sub def end } bind def /@Raise { exch cvx exch errordict exch get exec stop } bind def /@ReRaise { cvx $error /errorname get errordict exch get exec stop } bind def /@Stopped { 0 @#Stopped } bind def /@#Stopped { @_SaveStackLevels stopped { @_RestoreStackLevels true } { @_PopStackLevels false } ifelse } bind def /@Arg { Adobe_CoolType_Data begin @opStackCountByLevel @opStackLevel 1 sub get /args get exch get end } bind def currentglobal true setglobal /CTHasResourceForAllBug Level2? { 1 dict dup begin mark { (*) { pop stop } 128 string /Category resourceforall } stopped cleartomark currentdict eq dup { end } if not } { false } ifelse def /CTHasResourceStatusBug Level2? { mark { /steveamerige /Category resourcestatus } stopped { cleartomark true } { cleartomark currentglobal not } ifelse } { false } ifelse def setglobal /CTResourceStatus { mark 3 1 roll /Category findresource begin ({ResourceStatus} stopped) 0 () /SubFileDecode filter cvx exec { cleartomark false } { { 3 2 roll pop true } { cleartomark false } ifelse } ifelse end } bind def /CTWorkAroundBugs { Level2? { /cid_PreLoad /ProcSet resourcestatus { pop pop currentglobal mark { (*) { dup /CMap CTHasResourceStatusBug { CTResourceStatus } { resourcestatus } ifelse { pop dup 0 eq exch 1 eq or { dup /CMap findresource gcheck setglobal /CMap undefineresource } { pop CTHasResourceForAllBug { exit } { stop } ifelse } ifelse } { pop } ifelse } 128 string /CMap resourceforall } stopped { cleartomark } stopped pop setglobal } if } if } bind def /doc_setup { Adobe_CoolType_Core begin CTWorkAroundBugs /mov /moveto load def /nfnt /newencodedfont load def /mfnt /makefont load def /sfnt /setfont load def /ufnt /undefinefont load def /chp /charpath load def /awsh /awidthshow load def /wsh /widthshow load def /ash /ashow load def /sh /show load def end userdict /Adobe_CoolType_Data 10 dict dup begin /AddWidths? false def /CC 0 def /charcode 2 string def /@opStackCountByLevel 32 dict def /@opStackLevel 0 def /@dictStackCountByLevel 32 dict def /@dictStackLevel 0 def /InVMFontsByCMap 10 dict def /InVMDeepCopiedFonts 10 dict def end put } bind def /doc_trailer { currentdict Adobe_CoolType_Core eq { end } if } bind def /page_setup { Adobe_CoolType_Core begin } bind def /page_trailer { end } bind def /unload { systemdict /languagelevel known { systemdict/languagelevel get 2 ge { userdict/Adobe_CoolType_Core 2 copy known { undef } { pop pop } ifelse } if } if } bind def /ndf { 1 index where { pop pop pop } { dup xcheck { bind } if def } ifelse } def /findfont systemdict begin userdict begin /globaldict where { /globaldict get begin } if dup where pop exch get /globaldict where { pop end } if end end Adobe_CoolType_Core_Defined { /systemfindfont exch def } { /findfont 1 index def /systemfindfont exch def } ifelse /undefinefont { pop } ndf /copyfont { currentglobal 3 1 roll 1 index gcheck setglobal dup null eq { 0 } { dup length } ifelse 2 index length add 1 add dict begin exch { 1 index /FID eq { pop pop } { def } ifelse } forall dup null eq { pop } { { def } forall } ifelse currentdict end exch setglobal } bind def /copyarray { currentglobal exch dup gcheck setglobal dup length array copy exch setglobal } bind def /newencodedfont { currentglobal { SharedFontDirectory 3 index known { SharedFontDirectory 3 index get /FontReferenced known } { false } ifelse } { FontDirectory 3 index known { FontDirectory 3 index get /FontReferenced known } { SharedFontDirectory 3 index known { SharedFontDirectory 3 index get /FontReferenced known } { false } ifelse } ifelse } ifelse dup { 3 index findfont /FontReferenced get 2 index dup type /nametype eq {findfont} if ne { pop false } if } if { pop 1 index findfont /Encoding get exch 0 1 255 { 2 copy get 3 index 3 1 roll put } for pop pop pop } { dup type /nametype eq { findfont } if dup dup maxlength 2 add dict begin exch { 1 index /FID ne {def} {pop pop} ifelse } forall /FontReferenced exch def /Encoding exch dup length array copy def /FontName 1 index dup type /stringtype eq { cvn } if def dup currentdict end definefont def } ifelse } bind def /SetSubstituteStrategy { $SubstituteFont begin dup type /dicttype ne { 0 dict } if currentdict /$Strategies known { exch $Strategies exch 2 copy known { get 2 copy maxlength exch maxlength add dict begin { def } forall { def } forall currentdict dup /$Init known { dup /$Init get exec } if end /$Strategy exch def } { pop pop pop } ifelse } { pop pop } ifelse end } bind def /scff { $SubstituteFont begin dup type /stringtype eq { dup length exch } { null } ifelse /$sname exch def /$slen exch def /$inVMIndex $sname null eq { 1 index $str cvs dup length $slen sub $slen getinterval cvn } { $sname } ifelse def end { findfont } @Stopped { dup length 8 add string exch 1 index 0 (BadFont:) putinterval 1 index exch 8 exch dup length string cvs putinterval cvn { findfont } @Stopped { pop /Courier findfont } if } if $SubstituteFont begin /$sname null def /$slen 0 def /$inVMIndex null def end } bind def /isWidthsOnlyFont { dup /WidthsOnly known { pop pop true } { dup /FDepVector known { /FDepVector get { isWidthsOnlyFont dup { exit } if } forall } { dup /FDArray known { /FDArray get { isWidthsOnlyFont dup { exit } if } forall } { pop } ifelse } ifelse } ifelse } bind def /?str1 256 string def /?set { $SubstituteFont begin /$substituteFound false def /$fontname 4 index def /$doSmartSub false def end 3 index currentglobal false setglobal exch /CompatibleFonts /ProcSet resourcestatus { pop pop /CompatibleFonts /ProcSet findresource begin dup /CompatibleFont currentexception 1 index /CompatibleFont true setexception 1 index /Font resourcestatus { pop pop 3 2 roll setglobal end exch dup findfont /CompatibleFonts /ProcSet findresource begin 3 1 roll exch /CompatibleFont exch setexception end } { 3 2 roll setglobal 1 index exch /CompatibleFont exch setexception end findfont $SubstituteFont /$substituteFound true put } ifelse } { exch setglobal findfont } ifelse $SubstituteFont begin $substituteFound { false (%%[Using embedded font ) print 5 index ?str1 cvs print ( to avoid the font substitution problem noted earlier.]%%\n) print } { dup /FontName known { dup /FontName get $fontname eq 1 index /DistillerFauxFont known not and /currentdistillerparams where { pop false 2 index isWidthsOnlyFont not and } if } { false } ifelse } ifelse exch pop /$doSmartSub true def end { exch pop exch pop exch 2 dict dup /Found 3 index put exch findfont exch } { exch exec exch dup findfont dup /FontType get 3 eq { exch ?str1 cvs dup length 1 sub -1 0 { exch dup 2 index get 42 eq { exch 0 exch getinterval cvn 4 1 roll 3 2 roll pop exit } {exch pop} ifelse }for } { exch pop } ifelse 2 dict dup /Downloaded 6 5 roll put } ifelse dup /FontName 4 index put copyfont definefont pop } bind def /?str2 256 string def /?add { 1 index type /integertype eq { exch true 4 2 } { false 3 1 } ifelse roll 1 index findfont dup /Widths known { Adobe_CoolType_Data /AddWidths? true put gsave dup 1000 scalefont setfont } if /Downloaded known { exec exch { exch ?str2 cvs exch findfont /Downloaded get 1 dict begin /Downloaded 1 index def ?str1 cvs length ?str1 1 index 1 add 3 index putinterval exch length 1 add 1 index add ?str1 2 index (*) putinterval ?str1 0 2 index getinterval cvn findfont ?str1 3 index (+) putinterval 2 dict dup /FontName ?str1 0 6 index getinterval cvn put dup /Downloaded Downloaded put end copyfont dup /FontName get exch definefont pop pop pop } { pop } ifelse } { pop exch { findfont dup /Found get dup length exch ?str1 cvs pop ?str1 1 index (+) putinterval ?str1 1 index 1 add 4 index ?str2 cvs putinterval ?str1 exch 0 exch 5 4 roll ?str2 cvs length 1 add add getinterval cvn 1 dict exch 1 index exch /FontName exch put copyfont dup /FontName get exch definefont pop } { pop } ifelse } ifelse Adobe_CoolType_Data /AddWidths? get { grestore Adobe_CoolType_Data /AddWidths? false put } if } bind def /?sh { currentfont /Downloaded known { exch } if pop } bind def /?chp { currentfont /Downloaded known { pop } { false chp } ifelse } bind def /?mv { currentfont /Downloaded known { moveto pop pop } { pop pop moveto } ifelse } bind def setpacking userdict /$SubstituteFont 25 dict put 1 dict begin /SubstituteFont dup $error exch 2 copy known { get } { pop pop { pop /Courier } bind } ifelse def /currentdistillerparams where dup { pop pop currentdistillerparams /CannotEmbedFontPolicy 2 copy known { get /Error eq } { pop pop false } ifelse } if not { countdictstack array dictstack 0 get begin userdict begin $SubstituteFont begin /$str 128 string def /$fontpat 128 string def /$slen 0 def /$sname null def /$match false def /$fontname null def /$substituteFound false def /$inVMIndex null def /$doSmartSub true def /$depth 0 def /$fontname null def /$italicangle 26.5 def /$dstack null def /$Strategies 10 dict dup begin /$Type3Underprint { currentglobal exch false setglobal 11 dict begin /UseFont exch $WMode 0 ne { dup length dict copy dup /WMode $WMode put /UseFont exch definefont } if def /FontName $fontname dup type /stringtype eq { cvn } if def /FontType 3 def /FontMatrix [ .001 0 0 .001 0 0 ] def /Encoding 256 array dup 0 1 255 { /.notdef put dup } for pop def /FontBBox [ 0 0 0 0 ] def /CCInfo 7 dict dup begin /cc null def /x 0 def /y 0 def end def /BuildChar { exch begin CCInfo begin 1 string dup 0 3 index put exch pop /cc exch def UseFont 1000 scalefont setfont cc stringwidth /y exch def /x exch def x y setcharwidth $SubstituteFont /$Strategy get /$Underprint get exec 0 0 moveto cc show x y moveto end end } bind def currentdict end exch setglobal } bind def /$GetaTint 2 dict dup begin /$BuildFont { dup /WMode known { dup /WMode get } { 0 } ifelse /$WMode exch def $fontname exch dup /FontName known { dup /FontName get dup type /stringtype eq { cvn } if } { /unnamedfont } ifelse exch Adobe_CoolType_Data /InVMDeepCopiedFonts get 1 index /FontName get known { pop Adobe_CoolType_Data /InVMDeepCopiedFonts get 1 index get null copyfont } { $deepcopyfont } ifelse exch 1 index exch /FontBasedOn exch put dup /FontName $fontname dup type /stringtype eq { cvn } if put definefont Adobe_CoolType_Data /InVMDeepCopiedFonts get begin dup /FontBasedOn get 1 index def end } bind def /$Underprint { gsave x abs y abs gt { /y 1000 def } { /x -1000 def 500 120 translate } ifelse Level2? { [ /Separation (All) /DeviceCMYK { 0 0 0 1 pop } ] setcolorspace } { 0 setgray } ifelse 10 setlinewidth x .8 mul [ 7 3 ] { y mul 8 div 120 sub x 10 div exch moveto 0 y 4 div neg rlineto dup 0 rlineto 0 y 4 div rlineto closepath gsave Level2? { .2 setcolor } { .8 setgray } ifelse fill grestore stroke } forall pop grestore } bind def end def /$Oblique 1 dict dup begin /$BuildFont { currentglobal exch dup gcheck setglobal null copyfont begin /FontBasedOn currentdict /FontName known { FontName dup type /stringtype eq { cvn } if } { /unnamedfont } ifelse def /FontName $fontname dup type /stringtype eq { cvn } if def /currentdistillerparams where { pop } { /FontInfo currentdict /FontInfo known { FontInfo null copyfont } { 2 dict } ifelse dup begin /ItalicAngle $italicangle def /FontMatrix FontMatrix [ 1 0 ItalicAngle dup sin exch cos div 1 0 0 ] matrix concatmatrix readonly end 4 2 roll def def } ifelse FontName currentdict end definefont exch setglobal } bind def end def /$None 1 dict dup begin /$BuildFont {} bind def end def end def /$Oblique SetSubstituteStrategy /$findfontByEnum { dup type /stringtype eq { cvn } if dup /$fontname exch def $sname null eq { $str cvs dup length $slen sub $slen getinterval } { pop $sname } ifelse $fontpat dup 0 (fonts/*) putinterval exch 7 exch putinterval /$match false def $SubstituteFont /$dstack countdictstack array dictstack put mark { $fontpat 0 $slen 7 add getinterval { /$match exch def exit } $str filenameforall } stopped { cleardictstack currentdict true $SubstituteFont /$dstack get { exch { 1 index eq { pop false } { true } ifelse } { begin false } ifelse } forall pop } if cleartomark /$slen 0 def $match false ne { $match (fonts/) anchorsearch pop pop cvn } { /Courier } ifelse } bind def /$ROS 1 dict dup begin /Adobe 4 dict dup begin /Japan1 [ /Ryumin-Light /HeiseiMin-W3 /GothicBBB-Medium /HeiseiKakuGo-W5 /HeiseiMaruGo-W4 /Jun101-Light ] def /Korea1 [ /HYSMyeongJo-Medium /HYGoThic-Medium ] def /GB1 [ /STSong-Light /STHeiti-Regular ] def /CNS1 [ /MKai-Medium /MHei-Medium ] def end def end def /$cmapname null def /$deepcopyfont { dup /FontType get 0 eq { 1 dict dup /FontName /copied put copyfont begin /FDepVector FDepVector copyarray 0 1 2 index length 1 sub { 2 copy get $deepcopyfont dup /FontName /copied put /copied exch definefont 3 copy put pop pop } for def currentdict end } { $Strategies /$Type3Underprint get exec } ifelse } bind def /$buildfontname { dup /CIDFont findresource /CIDSystemInfo get begin Registry length Ordering length Supplement 8 string cvs 3 copy length 2 add add add string dup 5 1 roll dup 0 Registry putinterval dup 4 index (-) putinterval dup 4 index 1 add Ordering putinterval 4 2 roll add 1 add 2 copy (-) putinterval end 1 add 2 copy 0 exch getinterval $cmapname $fontpat cvs exch anchorsearch { pop pop 3 2 roll putinterval cvn /$cmapname exch def } { pop pop pop pop pop } ifelse length $str 1 index (-) putinterval 1 add $str 1 index $cmapname $fontpat cvs putinterval $cmapname length add $str exch 0 exch getinterval cvn } bind def /$findfontByROS { /$fontname exch def $ROS Registry 2 copy known { get Ordering 2 copy known { get } { pop pop [] } ifelse } { pop pop [] } ifelse false exch { dup /CIDFont resourcestatus { pop pop save 1 index /CIDFont findresource dup /WidthsOnly known { dup /WidthsOnly get } { false } ifelse exch pop exch restore { pop } { exch pop true exit } ifelse } { pop } ifelse } forall { $str cvs $buildfontname } { false (*) { save exch dup /CIDFont findresource dup /WidthsOnly known { dup /WidthsOnly get not } { true } ifelse exch /CIDSystemInfo get dup /Registry get Registry eq exch /Ordering get Ordering eq and and { exch restore exch pop true exit } { pop restore } ifelse } $str /CIDFont resourceforall { $buildfontname } { $fontname $findfontByEnum } ifelse } ifelse } bind def end end currentdict /$error known currentdict /languagelevel known and dup { pop $error /SubstituteFont known } if dup { $error } { Adobe_CoolType_Core } ifelse begin { /SubstituteFont /CMap /Category resourcestatus { pop pop { $SubstituteFont begin /$substituteFound true def dup length $slen gt $sname null ne or $slen 0 gt and { $sname null eq { dup $str cvs dup length $slen sub $slen getinterval cvn } { $sname } ifelse Adobe_CoolType_Data /InVMFontsByCMap get 1 index 2 copy known { get false exch { pop currentglobal { GlobalFontDirectory 1 index known { exch pop true exit } { pop } ifelse } { FontDirectory 1 index known { exch pop true exit } { GlobalFontDirectory 1 index known { exch pop true exit } { pop } ifelse } ifelse } ifelse } forall } { pop pop false } ifelse { exch pop exch pop } { dup /CMap resourcestatus { pop pop dup /$cmapname exch def /CMap findresource /CIDSystemInfo get { def } forall $findfontByROS } { 128 string cvs dup (-) search { 3 1 roll search { 3 1 roll pop { dup cvi } stopped { pop pop pop pop pop $findfontByEnum } { 4 2 roll pop pop exch length exch 2 index length 2 index sub exch 1 sub -1 0 { $str cvs dup length 4 index 0 4 index 4 3 roll add getinterval exch 1 index exch 3 index exch putinterval dup /CMap resourcestatus { pop pop 4 1 roll pop pop pop dup /$cmapname exch def /CMap findresource /CIDSystemInfo get { def } forall $findfontByROS true exit } { pop } ifelse } for dup type /booleantype eq { pop } { pop pop pop $findfontByEnum } ifelse } ifelse } { pop pop pop $findfontByEnum } ifelse } { pop pop $findfontByEnum } ifelse } ifelse } ifelse } { //SubstituteFont exec } ifelse /$slen 0 def end } } { { $SubstituteFont begin /$substituteFound true def dup length $slen gt $sname null ne or $slen 0 gt and { $findfontByEnum } { //SubstituteFont exec } ifelse end } } ifelse bind readonly def Adobe_CoolType_Core /scfindfont /systemfindfont load put } { /scfindfont { $SubstituteFont begin dup systemfindfont dup /FontName known { dup /FontName get dup 3 index ne } { /noname true } ifelse dup { /$origfontnamefound 2 index def /$origfontname 4 index def /$substituteFound true def } if exch pop { $slen 0 gt $sname null ne 3 index length $slen gt or and { pop dup $findfontByEnum findfont dup maxlength 1 add dict begin { 1 index /FID eq { pop pop } { def } ifelse } forall currentdict end definefont dup /FontName known { dup /FontName get } { null } ifelse $origfontnamefound ne { $origfontname $str cvs print ( substitution revised, using ) print dup /FontName known { dup /FontName get } { (unspecified font) } ifelse $str cvs print (.\n) print } if } { exch pop } ifelse } { exch pop } ifelse end } bind def } ifelse end end Adobe_CoolType_Core_Defined not { Adobe_CoolType_Core /findfont { $SubstituteFont begin $depth 0 eq { /$fontname 1 index dup type /stringtype ne { $str cvs } if def /$substituteFound false def } if /$depth $depth 1 add def end scfindfont $SubstituteFont begin /$depth $depth 1 sub def $substituteFound $depth 0 eq and { $inVMIndex null ne { dup $inVMIndex $AddInVMFont } if $doSmartSub { currentdict /$Strategy known { $Strategy /$BuildFont get exec } if } if } if end } bind put } if } if end /$AddInVMFont { exch /FontName 2 copy known { get 1 dict dup begin exch 1 index gcheck def end exch Adobe_CoolType_Data /InVMFontsByCMap get exch $DictAdd } { pop pop pop } ifelse } bind def /$DictAdd { 2 copy known not { 2 copy 4 index length dict put } if Level2? not { 2 copy get dup maxlength exch length 4 index length add lt 2 copy get dup length 4 index length add exch maxlength 1 index lt { 2 mul dict begin 2 copy get { forall } def 2 copy currentdict put end } { pop } ifelse } if get begin { def } forall end } bind def end end %%EndResource %%BeginResource: procset Adobe_CoolType_Utility_MAKEOCF 1.19 0 %%Copyright: Copyright 1987-2003 Adobe Systems Incorporated. %%Version: 1.19 0 systemdict /languagelevel known dup { currentglobal false setglobal } { false } ifelse exch userdict /Adobe_CoolType_Utility 2 copy known { 2 copy get dup maxlength 25 add dict copy } { 25 dict } ifelse put Adobe_CoolType_Utility begin /ct_Level2? exch def /ct_Clone? 1183615869 internaldict dup /CCRun known not exch /eCCRun known not ct_Level2? and or def ct_Level2? { globaldict begin currentglobal true setglobal } if /ct_AddStdCIDMap ct_Level2? { { ((Hex) 57 StartData 0615 1e27 2c39 1c60 d8a8 cc31 fe2b f6e0 7aa3 e541 e21c 60d8 a8c9 c3d0 6d9e 1c60 d8a8 c9c2 02d7 9a1c 60d8 a849 1c60 d8a8 cc36 74f4 1144 b13b 77) 0 () /SubFileDecode filter cvx exec } } { { eexec } } ifelse bind def userdict /cid_extensions known dup { cid_extensions /cid_UpdateDB known and } if { cid_extensions begin /cid_GetCIDSystemInfo { 1 index type /stringtype eq { exch cvn exch } if cid_extensions begin dup load 2 index known { 2 copy cid_GetStatusInfo dup null ne { 1 index load 3 index get dup null eq { pop pop cid_UpdateDB } { exch 1 index /Created get eq { exch pop exch pop } { pop cid_UpdateDB } ifelse } ifelse } { pop cid_UpdateDB } ifelse } { cid_UpdateDB } ifelse end } bind def end } if ct_Level2? { end setglobal } if /ct_UseNativeCapability? systemdict /composefont known def /ct_MakeOCF 35 dict def /ct_Vars 25 dict def /ct_GlyphDirProcs 6 dict def /ct_BuildCharDict 15 dict dup begin /charcode 2 string def /dst_string 1500 string def /nullstring () def /usewidths? true def end def ct_Level2? { setglobal } { pop } ifelse ct_GlyphDirProcs begin /GetGlyphDirectory { systemdict /languagelevel known { pop /CIDFont findresource /GlyphDirectory get } { 1 index /CIDFont findresource /GlyphDirectory get dup type /dicttype eq { dup dup maxlength exch length sub 2 index lt { dup length 2 index add dict copy 2 index /CIDFont findresource/GlyphDirectory 2 index put } if } if exch pop exch pop } ifelse + } def /+ { systemdict /languagelevel known { currentglobal false setglobal 3 dict begin /vm exch def } { 1 dict begin } ifelse /$ exch def systemdict /languagelevel known { vm setglobal /gvm currentglobal def $ gcheck setglobal } if ? { $ begin } if } def /? { $ type /dicttype eq } def /| { userdict /Adobe_CoolType_Data known { Adobe_CoolType_Data /AddWidths? known { currentdict Adobe_CoolType_Data begin begin AddWidths? { Adobe_CoolType_Data /CC 3 index put ? { def } { $ 3 1 roll put } ifelse CC charcode exch 1 index 0 2 index 256 idiv put 1 index exch 1 exch 256 mod put stringwidth 2 array astore currentfont /Widths get exch CC exch put } { ? { def } { $ 3 1 roll put } ifelse } ifelse end end } { ? { def } { $ 3 1 roll put } ifelse } ifelse } { ? { def } { $ 3 1 roll put } ifelse } ifelse } def /! { ? { end } if systemdict /languagelevel known { gvm setglobal } if end } def /: { string currentfile exch readstring pop } executeonly def end ct_MakeOCF begin /ct_cHexEncoding [/c00/c01/c02/c03/c04/c05/c06/c07/c08/c09/c0A/c0B/c0C/c0D/c0E/c0F/c10/c11/c12 /c13/c14/c15/c16/c17/c18/c19/c1A/c1B/c1C/c1D/c1E/c1F/c20/c21/c22/c23/c24/c25 /c26/c27/c28/c29/c2A/c2B/c2C/c2D/c2E/c2F/c30/c31/c32/c33/c34/c35/c36/c37/c38 /c39/c3A/c3B/c3C/c3D/c3E/c3F/c40/c41/c42/c43/c44/c45/c46/c47/c48/c49/c4A/c4B /c4C/c4D/c4E/c4F/c50/c51/c52/c53/c54/c55/c56/c57/c58/c59/c5A/c5B/c5C/c5D/c5E /c5F/c60/c61/c62/c63/c64/c65/c66/c67/c68/c69/c6A/c6B/c6C/c6D/c6E/c6F/c70/c71 /c72/c73/c74/c75/c76/c77/c78/c79/c7A/c7B/c7C/c7D/c7E/c7F/c80/c81/c82/c83/c84 /c85/c86/c87/c88/c89/c8A/c8B/c8C/c8D/c8E/c8F/c90/c91/c92/c93/c94/c95/c96/c97 /c98/c99/c9A/c9B/c9C/c9D/c9E/c9F/cA0/cA1/cA2/cA3/cA4/cA5/cA6/cA7/cA8/cA9/cAA /cAB/cAC/cAD/cAE/cAF/cB0/cB1/cB2/cB3/cB4/cB5/cB6/cB7/cB8/cB9/cBA/cBB/cBC/cBD /cBE/cBF/cC0/cC1/cC2/cC3/cC4/cC5/cC6/cC7/cC8/cC9/cCA/cCB/cCC/cCD/cCE/cCF/cD0 /cD1/cD2/cD3/cD4/cD5/cD6/cD7/cD8/cD9/cDA/cDB/cDC/cDD/cDE/cDF/cE0/cE1/cE2/cE3 /cE4/cE5/cE6/cE7/cE8/cE9/cEA/cEB/cEC/cED/cEE/cEF/cF0/cF1/cF2/cF3/cF4/cF5/cF6 /cF7/cF8/cF9/cFA/cFB/cFC/cFD/cFE/cFF] def /ct_CID_STR_SIZE 8000 def /ct_mkocfStr100 100 string def /ct_defaultFontMtx [.001 0 0 .001 0 0] def /ct_1000Mtx [1000 0 0 1000 0 0] def /ct_raise {exch cvx exch errordict exch get exec stop} bind def /ct_reraise { cvx $error /errorname get (Error: ) print dup ( ) cvs print errordict exch get exec stop } bind def /ct_cvnsi { 1 index add 1 sub 1 exch 0 4 1 roll { 2 index exch get exch 8 bitshift add } for exch pop } bind def /ct_GetInterval { Adobe_CoolType_Utility /ct_BuildCharDict get begin /dst_index 0 def dup dst_string length gt { dup string /dst_string exch def } if 1 index ct_CID_STR_SIZE idiv /arrayIndex exch def 2 index arrayIndex get 2 index arrayIndex ct_CID_STR_SIZE mul sub { dup 3 index add 2 index length le { 2 index getinterval dst_string dst_index 2 index putinterval length dst_index add /dst_index exch def exit } { 1 index length 1 index sub dup 4 1 roll getinterval dst_string dst_index 2 index putinterval pop dup dst_index add /dst_index exch def sub /arrayIndex arrayIndex 1 add def 2 index dup length arrayIndex gt { arrayIndex get } { pop exit } ifelse 0 } ifelse } loop pop pop pop dst_string 0 dst_index getinterval end } bind def ct_Level2? { /ct_resourcestatus currentglobal mark true setglobal { /unknowninstancename /Category resourcestatus } stopped { cleartomark setglobal true } { cleartomark currentglobal not exch setglobal } ifelse { { mark 3 1 roll /Category findresource begin ct_Vars /vm currentglobal put ({ResourceStatus} stopped) 0 () /SubFileDecode filter cvx exec { cleartomark false } { { 3 2 roll pop true } { cleartomark false } ifelse } ifelse ct_Vars /vm get setglobal end } } { { resourcestatus } } ifelse bind def /CIDFont /Category ct_resourcestatus { pop pop } { currentglobal true setglobal /Generic /Category findresource dup length dict copy dup /InstanceType /dicttype put /CIDFont exch /Category defineresource pop setglobal } ifelse ct_UseNativeCapability? { /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo 3 dict dup begin /Registry (Adobe) def /Ordering (Identity) def /Supplement 0 def end def /CMapName /Identity-H def /CMapVersion 1.000 def /CMapType 1 def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000> 0 endcidrange endcmap CMapName currentdict /CMap defineresource pop end end } if } { /ct_Category 2 dict begin /CIDFont 10 dict def /ProcSet 2 dict def currentdict end def /defineresource { ct_Category 1 index 2 copy known { get dup dup maxlength exch length eq { dup length 10 add dict copy ct_Category 2 index 2 index put } if 3 index 3 index put pop exch pop } { pop pop /defineresource /undefined ct_raise } ifelse } bind def /findresource { ct_Category 1 index 2 copy known { get 2 index 2 copy known { get 3 1 roll pop pop} { pop pop /findresource /undefinedresource ct_raise } ifelse } { pop pop /findresource /undefined ct_raise } ifelse } bind def /resourcestatus { ct_Category 1 index 2 copy known { get 2 index known exch pop exch pop { 0 -1 true } { false } ifelse } { pop pop /findresource /undefined ct_raise } ifelse } bind def /ct_resourcestatus /resourcestatus load def } ifelse /ct_CIDInit 2 dict begin /ct_cidfont_stream_init { { dup (Binary) eq { pop null currentfile ct_Level2? { { cid_BYTE_COUNT () /SubFileDecode filter } stopped { pop pop pop } if } if /readstring load exit } if dup (Hex) eq { pop currentfile ct_Level2? { { null exch /ASCIIHexDecode filter /readstring } stopped { pop exch pop (>) exch /readhexstring } if } { (>) exch /readhexstring } ifelse load exit } if /StartData /typecheck ct_raise } loop cid_BYTE_COUNT ct_CID_STR_SIZE le { 2 copy cid_BYTE_COUNT string exch exec pop 1 array dup 3 -1 roll 0 exch put } { cid_BYTE_COUNT ct_CID_STR_SIZE div ceiling cvi dup array exch 2 sub 0 exch 1 exch { 2 copy 5 index ct_CID_STR_SIZE string 6 index exec pop put pop } for 2 index cid_BYTE_COUNT ct_CID_STR_SIZE mod string 3 index exec pop 1 index exch 1 index length 1 sub exch put } ifelse cid_CIDFONT exch /GlyphData exch put 2 index null eq { pop pop pop } { pop /readstring load 1 string exch { 3 copy exec pop dup length 0 eq { pop pop pop pop pop true exit } if 4 index eq { pop pop pop pop false exit } if } loop pop } ifelse } bind def /StartData { mark { currentdict dup /FDArray get 0 get /FontMatrix get 0 get 0.001 eq { dup /CDevProc known not { /CDevProc 1183615869 internaldict /stdCDevProc 2 copy known { get } { pop pop { pop pop pop pop pop 0 -1000 7 index 2 div 880 } } ifelse def } if } { /CDevProc { pop pop pop pop pop 0 1 cid_temp /cid_CIDFONT get /FDArray get 0 get /FontMatrix get 0 get div 7 index 2 div 1 index 0.88 mul } def } ifelse /cid_temp 15 dict def cid_temp begin /cid_CIDFONT exch def 3 copy pop dup /cid_BYTE_COUNT exch def 0 gt { ct_cidfont_stream_init FDArray { /Private get dup /SubrMapOffset known { begin /Subrs SubrCount array def Subrs SubrMapOffset SubrCount SDBytes ct_Level2? { currentdict dup /SubrMapOffset undef dup /SubrCount undef /SDBytes undef } if end /cid_SD_BYTES exch def /cid_SUBR_COUNT exch def /cid_SUBR_MAP_OFFSET exch def /cid_SUBRS exch def cid_SUBR_COUNT 0 gt { GlyphData cid_SUBR_MAP_OFFSET cid_SD_BYTES ct_GetInterval 0 cid_SD_BYTES ct_cvnsi 0 1 cid_SUBR_COUNT 1 sub { exch 1 index 1 add cid_SD_BYTES mul cid_SUBR_MAP_OFFSET add GlyphData exch cid_SD_BYTES ct_GetInterval 0 cid_SD_BYTES ct_cvnsi cid_SUBRS 4 2 roll GlyphData exch 4 index 1 index sub ct_GetInterval dup length string copy put } for pop } if } { pop } ifelse } forall } if cleartomark pop pop end CIDFontName currentdict /CIDFont defineresource pop end end } stopped { cleartomark /StartData ct_reraise } if } bind def currentdict end def /ct_saveCIDInit { /CIDInit /ProcSet ct_resourcestatus { true } { /CIDInitC /ProcSet ct_resourcestatus } ifelse { pop pop /CIDInit /ProcSet findresource ct_UseNativeCapability? { pop null } { /CIDInit ct_CIDInit /ProcSet defineresource pop } ifelse } { /CIDInit ct_CIDInit /ProcSet defineresource pop null } ifelse ct_Vars exch /ct_oldCIDInit exch put } bind def /ct_restoreCIDInit { ct_Vars /ct_oldCIDInit get dup null ne { /CIDInit exch /ProcSet defineresource pop } { pop } ifelse } bind def /ct_BuildCharSetUp { 1 index begin CIDFont begin Adobe_CoolType_Utility /ct_BuildCharDict get begin /ct_dfCharCode exch def /ct_dfDict exch def CIDFirstByte ct_dfCharCode add dup CIDCount ge { pop 0 } if /cid exch def { GlyphDirectory cid 2 copy known { get } { pop pop nullstring } ifelse dup length FDBytes sub 0 gt { dup FDBytes 0 ne { 0 FDBytes ct_cvnsi } { pop 0 } ifelse /fdIndex exch def dup length FDBytes sub FDBytes exch getinterval /charstring exch def exit } { pop cid 0 eq { /charstring nullstring def exit } if /cid 0 def } ifelse } loop } def /ct_SetCacheDevice { 0 0 moveto dup stringwidth 3 -1 roll true charpath pathbbox 0 -1000 7 index 2 div 880 setcachedevice2 0 0 moveto } def /ct_CloneSetCacheProc { 1 eq { stringwidth pop -2 div -880 0 -1000 setcharwidth moveto } { usewidths? { currentfont /Widths get cid 2 copy known { get exch pop aload pop } { pop pop stringwidth } ifelse } { stringwidth } ifelse setcharwidth 0 0 moveto } ifelse } def /ct_Type3ShowCharString { ct_FDDict fdIndex 2 copy known { get } { currentglobal 3 1 roll 1 index gcheck setglobal ct_Type1FontTemplate dup maxlength dict copy begin FDArray fdIndex get dup /FontMatrix 2 copy known { get } { pop pop ct_defaultFontMtx } ifelse /FontMatrix exch dup length array copy def /Private get /Private exch def /Widths rootfont /Widths get def /CharStrings 1 dict dup /.notdef dup length string copy put def currentdict end /ct_Type1Font exch definefont dup 5 1 roll put setglobal } ifelse dup /CharStrings get 1 index /Encoding get ct_dfCharCode get charstring put rootfont /WMode 2 copy known { get } { pop pop 0 } ifelse exch 1000 scalefont setfont ct_str1 0 ct_dfCharCode put ct_str1 exch ct_dfSetCacheProc ct_SyntheticBold { currentpoint ct_str1 show newpath moveto ct_str1 true charpath ct_StrokeWidth setlinewidth stroke } { ct_str1 show } ifelse } def /ct_Type4ShowCharString { ct_dfDict ct_dfCharCode charstring FDArray fdIndex get dup /FontMatrix get dup ct_defaultFontMtx ct_matrixeq not { ct_1000Mtx matrix concatmatrix concat } { pop } ifelse /Private get Adobe_CoolType_Utility /ct_Level2? get not { ct_dfDict /Private 3 -1 roll { put } 1183615869 internaldict /superexec get exec } if 1183615869 internaldict Adobe_CoolType_Utility /ct_Level2? get { 1 index } { 3 index /Private get mark 6 1 roll } ifelse dup /RunInt known { /RunInt get } { pop /CCRun } ifelse get exec Adobe_CoolType_Utility /ct_Level2? get not { cleartomark } if } bind def /ct_BuildCharIncremental { { Adobe_CoolType_Utility /ct_MakeOCF get begin ct_BuildCharSetUp ct_ShowCharString } stopped { stop } if end end end end } bind def /BaseFontNameStr (BF00) def /ct_Type1FontTemplate 14 dict begin /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0] def /FontBBox [-250 -250 1250 1250] def /Encoding ct_cHexEncoding def /PaintType 0 def currentdict end def /BaseFontTemplate 11 dict begin /FontMatrix [0.001 0 0 0.001 0 0] def /FontBBox [-250 -250 1250 1250] def /Encoding ct_cHexEncoding def /BuildChar /ct_BuildCharIncremental load def ct_Clone? { /FontType 3 def /ct_ShowCharString /ct_Type3ShowCharString load def /ct_dfSetCacheProc /ct_CloneSetCacheProc load def /ct_SyntheticBold false def /ct_StrokeWidth 1 def } { /FontType 4 def /Private 1 dict dup /lenIV 4 put def /CharStrings 1 dict dup /.notdef put def /PaintType 0 def /ct_ShowCharString /ct_Type4ShowCharString load def } ifelse /ct_str1 1 string def currentdict end def /BaseFontDictSize BaseFontTemplate length 5 add def /ct_matrixeq { true 0 1 5 { dup 4 index exch get exch 3 index exch get eq and dup not { exit } if } for exch pop exch pop } bind def /ct_makeocf { 15 dict begin exch /WMode exch def exch /FontName exch def /FontType 0 def /FMapType 2 def dup /FontMatrix known { dup /FontMatrix get /FontMatrix exch def } { /FontMatrix matrix def } ifelse /bfCount 1 index /CIDCount get 256 idiv 1 add dup 256 gt { pop 256} if def /Encoding 256 array 0 1 bfCount 1 sub { 2 copy dup put pop } for bfCount 1 255 { 2 copy bfCount put pop } for def /FDepVector bfCount dup 256 lt { 1 add } if array def BaseFontTemplate BaseFontDictSize dict copy begin /CIDFont exch def CIDFont /FontBBox known { CIDFont /FontBBox get /FontBBox exch def } if CIDFont /CDevProc known { CIDFont /CDevProc get /CDevProc exch def } if currentdict end BaseFontNameStr 3 (0) putinterval 0 1 bfCount dup 256 eq { 1 sub } if { FDepVector exch 2 index BaseFontDictSize dict copy begin dup /CIDFirstByte exch 256 mul def FontType 3 eq { /ct_FDDict 2 dict def } if currentdict end 1 index 16 BaseFontNameStr 2 2 getinterval cvrs pop BaseFontNameStr exch definefont put } for ct_Clone? { /Widths 1 index /CIDFont get /GlyphDirectory get length dict def } if FontName currentdict end definefont ct_Clone? { gsave dup 1000 scalefont setfont ct_BuildCharDict begin /usewidths? false def currentfont /Widths get begin exch /CIDFont get /GlyphDirectory get { pop dup charcode exch 1 index 0 2 index 256 idiv put 1 index exch 1 exch 256 mod put stringwidth 2 array astore def } forall end /usewidths? true def end grestore } { exch pop } ifelse } bind def /ct_ComposeFont { ct_UseNativeCapability? { 2 index /CMap ct_resourcestatus { pop pop exch pop } { /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CMapName 3 index def /CMapVersion 1.000 def /CMapType 1 def exch /WMode exch def /CIDSystemInfo 3 dict dup begin /Registry (Adobe) def /Ordering CMapName ct_mkocfStr100 cvs (Adobe-) search { pop pop (-) search { dup length string copy exch pop exch pop } { pop (Identity)} ifelse } { pop (Identity) } ifelse def /Supplement 0 def end def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000> 0 endcidrange endcmap CMapName currentdict /CMap defineresource pop end end } ifelse composefont } { 3 2 roll pop 0 get /CIDFont findresource ct_makeocf } ifelse } bind def /ct_MakeIdentity { ct_UseNativeCapability? { 1 index /CMap ct_resourcestatus { pop pop } { /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CMapName 2 index def /CMapVersion 1.000 def /CMapType 1 def /CIDSystemInfo 3 dict dup begin /Registry (Adobe) def /Ordering CMapName ct_mkocfStr100 cvs (Adobe-) search { pop pop (-) search { dup length string copy exch pop exch pop } { pop (Identity) } ifelse } { pop (Identity) } ifelse def /Supplement 0 def end def 1 begincodespacerange <0000> endcodespacerange 1 begincidrange <0000> 0 endcidrange endcmap CMapName currentdict /CMap defineresource pop end end } ifelse composefont } { exch pop 0 get /CIDFont findresource ct_makeocf } ifelse } bind def currentdict readonly pop end end %%EndResource %%BeginResource: procset Adobe_CoolType_Utility_T42 1.0 0 %%Copyright: Copyright 1987-2003 Adobe Systems Incorporated. %%Version: 1.0 0 userdict /ct_T42Dict 15 dict put ct_T42Dict begin /Is2015? { version cvi 2015 ge } bind def /AllocGlyphStorage { Is2015? { pop } { {string} forall } ifelse } bind def /Type42DictBegin { 25 dict begin /FontName exch def /CharStrings 256 dict begin /.notdef 0 def currentdict end def /Encoding exch def /PaintType 0 def /FontType 42 def /FontMatrix [1 0 0 1 0 0] def 4 array astore cvx /FontBBox exch def /sfnts } bind def /Type42DictEnd { currentdict dup /FontName get exch definefont end ct_T42Dict exch dup /FontName get exch put } bind def /RD {string currentfile exch readstring pop} executeonly def /PrepFor2015 { Is2015? { /GlyphDirectory 16 dict def sfnts 0 get dup 2 index (glyx) putinterval 2 index (locx) putinterval pop pop } { pop pop } ifelse } bind def /AddT42Char { Is2015? { /GlyphDirectory get begin def end pop pop } { /sfnts get 4 index get 3 index 2 index putinterval pop pop pop pop } ifelse } bind def end %%EndResource Adobe_CoolType_Core begin /$Oblique SetSubstituteStrategy end %%BeginResource: procset Adobe_AGM_Image 1.0 0 %%Version: 1.0 0 %%Copyright: Copyright (C) 2000-2003 Adobe Systems, Inc. All Rights Reserved. systemdict /setpacking known { currentpacking true setpacking } if userdict /Adobe_AGM_Image 75 dict dup begin put /Adobe_AGM_Image_Id /Adobe_AGM_Image_1.0_0 def /nd{ null def }bind def /AGMIMG_&image nd /AGMIMG_&colorimage nd /AGMIMG_&imagemask nd /AGMIMG_mbuf () def /AGMIMG_ybuf () def /AGMIMG_kbuf () def /AGMIMG_c 0 def /AGMIMG_m 0 def /AGMIMG_y 0 def /AGMIMG_k 0 def /AGMIMG_tmp nd /AGMIMG_imagestring0 nd /AGMIMG_imagestring1 nd /AGMIMG_imagestring2 nd /AGMIMG_imagestring3 nd /AGMIMG_imagestring4 nd /AGMIMG_imagestring5 nd /AGMIMG_cnt nd /AGMIMG_fsave nd /AGMIMG_colorAry nd /AGMIMG_override nd /AGMIMG_name nd /AGMIMG_maskSource nd /invert_image_samples nd /knockout_image_samples nd /img nd /sepimg nd /devnimg nd /idximg nd /doc_setup { Adobe_AGM_Core begin Adobe_AGM_Image begin /AGMIMG_&image systemdict/image get def /AGMIMG_&imagemask systemdict/imagemask get def /colorimage where{ pop /AGMIMG_&colorimage /colorimage ldf }if end end }def /page_setup { Adobe_AGM_Image begin /AGMIMG_ccimage_exists {/customcolorimage where { pop /Adobe_AGM_OnHost_Seps where { pop false }{ /Adobe_AGM_InRip_Seps where { pop false }{ true }ifelse }ifelse }{ false }ifelse }bdf level2{ /invert_image_samples { Adobe_AGM_Image/AGMIMG_tmp Decode length ddf /Decode [ Decode 1 get Decode 0 get] def }def /knockout_image_samples { Operator/imagemask ne{ /Decode [1 1] def }if }def }{ /invert_image_samples { {1 exch sub} currenttransfer addprocs settransfer }def /knockout_image_samples { { pop 1 } currenttransfer addprocs settransfer }def }ifelse /img /imageormask ldf /sepimg /sep_imageormask ldf /devnimg /devn_imageormask ldf /idximg /indexed_imageormask ldf /_ctype 7 def currentdict{ dup xcheck 1 index type dup /arraytype eq exch /packedarraytype eq or and{ bind }if def }forall }def /page_trailer { end }def /doc_trailer { }def /imageormask_sys { begin save mark level2{ currentdict Operator /imagemask eq{ AGMIMG_&imagemask }{ use_mask { level3 {process_mask_L3 AGMIMG_&image}{masked_image_simulation}ifelse }{ AGMIMG_&image }ifelse }ifelse }{ Width Height Operator /imagemask eq{ Decode 0 get 1 eq Decode 1 get 0 eq and ImageMatrix /DataSource load AGMIMG_&imagemask }{ BitsPerComponent ImageMatrix /DataSource load AGMIMG_&image }ifelse }ifelse cleartomark restore end }def /overprint_plate { currentoverprint { 0 get dup type /nametype eq { dup /DeviceGray eq{ pop AGMCORE_black_plate not }{ /DeviceCMYK eq{ AGMCORE_is_cmyk_sep not }if }ifelse }{ false exch { AGMOHS_sepink eq or } forall not } ifelse }{ pop false }ifelse }def /process_mask_L3 { dup begin /ImageType 1 def end 4 dict begin /DataDict exch def /ImageType 3 def /InterleaveType 3 def /MaskDict 9 dict begin /ImageType 1 def /Width DataDict dup /MaskWidth known {/MaskWidth}{/Width} ifelse get def /Height DataDict dup /MaskHeight known {/MaskHeight}{/Height} ifelse get def /ImageMatrix [Width 0 0 Height neg 0 Height] def /NComponents 1 def /BitsPerComponent 1 def /Decode [0 1] def /DataSource AGMIMG_maskSource def currentdict end def currentdict end }def /use_mask { dup type /dicttype eq { dup /Mask known { dup /Mask get { level3 {true} { dup /MaskWidth known {dup /MaskWidth get 1 index /Width get eq}{true}ifelse exch dup /MaskHeight known {dup /MaskHeight get 1 index /Height get eq}{true}ifelse 3 -1 roll and } ifelse } {false} ifelse } {false} ifelse } {false} ifelse }def /make_line_source { begin MultipleDataSources { [ Decode length 2 div cvi {Width string} repeat ] }{ Width Decode length 2 div mul cvi string }ifelse end }def /datasource_to_str { exch dup type dup /filetype eq { pop exch readstring }{ /arraytype eq { exec exch copy }{ pop }ifelse }ifelse pop }def /masked_image_simulation { 3 dict begin dup make_line_source /line_source xdf /mask_source AGMIMG_maskSource /LZWDecode filter def dup /Width get 8 div ceiling cvi string /mask_str xdf begin gsave 0 1 translate 1 -1 Height div scale 1 1 Height { pop gsave MultipleDataSources { 0 1 DataSource length 1 sub { dup DataSource exch get exch line_source exch get datasource_to_str } for }{ DataSource line_source datasource_to_str } ifelse << /PatternType 1 /PaintProc [ /pop cvx << /ImageType 1 /Width Width /Height 1 /ImageMatrix Width 1.0 sub 1 matrix scale 0.5 0 matrix translate matrix concatmatrix /MultipleDataSources MultipleDataSources /DataSource line_source /BitsPerComponent BitsPerComponent /Decode Decode >> /image cvx ] cvx /BBox [0 0 Width 1] /XStep Width /YStep 1 /PaintType 1 /TilingType 2 >> matrix makepattern set_pattern << /ImageType 1 /Width Width /Height 1 /ImageMatrix Width 1 matrix scale /MultipleDataSources false /DataSource mask_source mask_str readstring pop /BitsPerComponent 1 /Decode [0 1] >> imagemask grestore 0 1 translate } for grestore end end }def /imageormask { begin SkipImageProc { currentdict consumeimagedata } { save mark level2 AGMCORE_host_sep not and{ currentdict Operator /imagemask eq DeviceN_PS2 not and { imagemask }{ AGMCORE_in_rip_sep currentoverprint and currentcolorspace 0 get /DeviceGray eq and{ [/Separation /Black /DeviceGray {}] setcolorspace /Decode [ Decode 1 get Decode 0 get ] def }if use_mask { level3 {process_mask_L3 image}{masked_image_simulation}ifelse }{ DeviceN_NoneName DeviceN_PS2 Indexed_DeviceN level3 not and or or AGMCORE_in_rip_sep and { Names convert_to_process not { 2 dict begin /imageDict xdf /names_index 0 def gsave imageDict write_image_file { Names { dup (None) ne { [/Separation 3 -1 roll /DeviceGray {1 exch sub}] setcolorspace Operator imageDict read_image_file names_index 0 eq {true setoverprint} if /names_index names_index 1 add def }{ pop } ifelse } forall close_image_file } if grestore end }{ Operator /imagemask eq { imagemask }{ image } ifelse } ifelse }{ Operator /imagemask eq { imagemask }{ image } ifelse } ifelse }ifelse }ifelse }{ Width Height Operator /imagemask eq{ Decode 0 get 1 eq Decode 1 get 0 eq and ImageMatrix /DataSource load /Adobe_AGM_OnHost_Seps where { pop imagemask }{ currentgray 1 ne{ currentdict imageormask_sys }{ currentoverprint not{ 1 AGMCORE_&setgray currentdict imageormask_sys }{ currentdict ignoreimagedata }ifelse }ifelse }ifelse }{ BitsPerComponent ImageMatrix MultipleDataSources{ 0 1 NComponents 1 sub{ DataSource exch get }for }{ /DataSource load }ifelse Operator /colorimage eq{ AGMCORE_host_sep{ MultipleDataSources level2 or NComponents 4 eq and{ AGMCORE_is_cmyk_sep{ MultipleDataSources{ /DataSource [ DataSource 0 get /exec cvx DataSource 1 get /exec cvx DataSource 2 get /exec cvx DataSource 3 get /exec cvx /AGMCORE_get_ink_data cvx ] cvx def }{ /DataSource Width BitsPerComponent mul 7 add 8 idiv Height mul 4 mul /DataSource load filter_cmyk 0 () /SubFileDecode filter def }ifelse /Decode [ Decode 0 get Decode 1 get ] def /MultipleDataSources false def /NComponents 1 def /Operator /image def invert_image_samples 1 AGMCORE_&setgray currentdict imageormask_sys }{ currentoverprint not Operator/imagemask eq and{ 1 AGMCORE_&setgray currentdict imageormask_sys }{ currentdict ignoreimagedata }ifelse }ifelse }{ MultipleDataSources NComponents AGMIMG_&colorimage }ifelse }{ true NComponents colorimage }ifelse }{ Operator /image eq{ AGMCORE_host_sep{ /DoImage true def HostSepColorImage{ invert_image_samples }{ AGMCORE_black_plate not Operator/imagemask ne and{ /DoImage false def currentdict ignoreimagedata }if }ifelse 1 AGMCORE_&setgray DoImage {currentdict imageormask_sys} if }{ use_mask { level3 {process_mask_L3 image}{masked_image_simulation}ifelse }{ image }ifelse }ifelse }{ Operator/knockout eq{ pop pop pop pop pop currentcolorspace overprint_plate not{ knockout_unitsq }if }if }ifelse }ifelse }ifelse }ifelse cleartomark restore }ifelse end }def /sep_imageormask { /sep_colorspace_dict AGMCORE_gget begin /MappedCSA CSA map_csa def begin SkipImageProc { currentdict consumeimagedata } { save mark AGMCORE_avoid_L2_sep_space{ /Decode [ Decode 0 get 255 mul Decode 1 get 255 mul ] def }if AGMIMG_ccimage_exists MappedCSA 0 get /DeviceCMYK eq and currentdict/Components known and Name () ne and Name (All) ne and Operator /image eq and AGMCORE_producing_seps not and level2 not and { Width Height BitsPerComponent ImageMatrix [ /DataSource load /exec cvx { 0 1 2 index length 1 sub{ 1 index exch 2 copy get 255 xor put }for } /exec cvx ] cvx bind MappedCSA 0 get /DeviceCMYK eq{ Components aload pop }{ 0 0 0 Components aload pop 1 exch sub }ifelse Name findcmykcustomcolor customcolorimage }{ AGMCORE_producing_seps not{ level2{ AGMCORE_avoid_L2_sep_space not currentcolorspace 0 get /Separation ne and{ [/Separation Name MappedCSA sep_proc_name exch 0 get exch load ] setcolorspace_opt /sep_tint AGMCORE_gget setcolor }if currentdict imageormask }{ currentdict Operator /imagemask eq{ imageormask }{ sep_imageormask_lev1 }ifelse }ifelse }{ AGMCORE_host_sep{ Operator/knockout eq{ currentdict/ImageMatrix get concat knockout_unitsq }{ currentgray 1 ne{ AGMCORE_is_cmyk_sep Name (All) ne and{ level2{ [ /Separation Name [/DeviceGray] { sep_colorspace_proc AGMCORE_get_ink_data 1 exch sub } bind ] AGMCORE_&setcolorspace /sep_tint AGMCORE_gget AGMCORE_&setcolor currentdict imageormask_sys }{ currentdict Operator /imagemask eq{ imageormask_sys }{ sep_image_lev1_sep }ifelse }ifelse }{ Operator/imagemask ne{ invert_image_samples }if currentdict imageormask_sys }ifelse }{ currentoverprint not Name (All) eq or Operator/imagemask eq and{ currentdict imageormask_sys }{ currentoverprint not { gsave knockout_unitsq grestore }if currentdict consumeimagedata }ifelse }ifelse }ifelse }{ currentcolorspace 0 get /Separation ne{ [/Separation Name MappedCSA sep_proc_name exch 0 get exch load ] setcolorspace_opt /sep_tint AGMCORE_gget setcolor }if currentoverprint MappedCSA 0 get /DeviceCMYK eq and Name inRip_spot_has_ink not and Name (All) ne and { imageormask_l2_overprint }{ currentdict imageormask }ifelse }ifelse }ifelse }ifelse cleartomark restore }ifelse end end }def /decode_image_sample { 4 1 roll exch dup 5 1 roll sub 2 4 -1 roll exp 1 sub div mul add } bdf /colorSpaceElemCnt { currentcolorspace 0 get dup /DeviceCMYK eq { pop 4 } { /DeviceRGB eq { pop 3 }{ 1 } ifelse } ifelse } bdf /devn_sep_datasource { 1 dict begin /dataSource xdf [ 0 1 dataSource length 1 sub { dup currentdict /dataSource get /exch cvx /get cvx /exec cvx /exch cvx names_index /ne cvx [ /pop cvx ] cvx /if cvx } for ] cvx bind end } bdf /devn_alt_datasource { 11 dict begin /srcDataStrs xdf /dstDataStr xdf /convProc xdf /origcolorSpaceElemCnt xdf /origMultipleDataSources xdf /origBitsPerComponent xdf /origDecode xdf /origDataSource xdf /dsCnt origMultipleDataSources {origDataSource length}{1}ifelse def /samplesNeedDecoding 0 0 1 origDecode length 1 sub { origDecode exch get add } for origDecode length 2 div div dup 1 eq { /decodeDivisor 2 origBitsPerComponent exp 1 sub def } if 2 origBitsPerComponent exp 1 sub ne def [ 0 1 dsCnt 1 sub [ currentdict /origMultipleDataSources get { dup currentdict /origDataSource get exch get dup type }{ currentdict /origDataSource get dup type } ifelse dup /filetype eq { pop currentdict /srcDataStrs get 3 -1 /roll cvx /get cvx /readstring cvx /pop cvx }{ /stringtype ne { /exec cvx } if currentdict /srcDataStrs get /exch cvx 3 -1 /roll cvx /xpt cvx } ifelse ] cvx /for cvx currentdict /srcDataStrs get 0 /get cvx /length cvx 0 /ne cvx [ 0 1 Width 1 sub [ Adobe_AGM_Utils /AGMUTIL_ndx /xddf cvx currentdict /origMultipleDataSources get { 0 1 dsCnt 1 sub [ Adobe_AGM_Utils /AGMUTIL_ndx1 /xddf cvx currentdict /srcDataStrs get /AGMUTIL_ndx1 /load cvx /get cvx /AGMUTIL_ndx /load cvx /get cvx samplesNeedDecoding { currentdict /decodeDivisor known { currentdict /decodeDivisor get /div cvx }{ currentdict /origDecode get /AGMUTIL_ndx1 /load cvx 2 /mul cvx 2 /getinterval cvx /aload cvx /pop cvxs BitsPerComponent /decode_image_sample load /exec cvx } ifelse } if ] cvx /for cvx }{ Adobe_AGM_Utils /AGMUTIL_ndx1 0 /ddf cvx currentdict /srcDataStrs get 0 /get cvx /AGMUTIL_ndx /load cvx currentdict /origDecode get length 2 idiv dup 3 1 /roll cvx /mul cvx /exch cvx /getinterval cvx [ samplesNeedDecoding { currentdict /decodeDivisor known { currentdict /decodeDivisor get /div cvx }{ currentdict /origDecode get /AGMUTIL_ndx1 /load cvx 2 /mul cvx 2 /getinterval cvx /aload cvx /pop cvx BitsPerComponent /decode_image_sample load /exec cvx Adobe_AGM_Utils /AGMUTIL_ndx1 /AGMUTIL_ndx1 /load cvx 1 /add cvx /ddf cvx } ifelse } if ] cvx /forall cvx } ifelse currentdict /convProc get /exec cvx currentdict /origcolorSpaceElemCnt get 1 sub -1 0 [ currentdict /dstDataStr get 3 1 /roll cvx /AGMUTIL_ndx /load cvx currentdict /origcolorSpaceElemCnt get /mul cvx /add cvx /exch cvx currentdict /convProc get /filter_indexed_devn load ne { 255 /mul cvx /cvi cvx } if /put cvx ] cvx /for cvx ] cvx /for cvx currentdict /dstDataStr get ] cvx /if cvx ] cvx bind end } bdf /devn_imageormask { /devicen_colorspace_dict AGMCORE_gget begin /MappedCSA CSA map_csa def 2 dict begin dup dup /dstDataStr exch /Width get colorSpaceElemCnt mul string def /srcDataStrs [ 3 -1 roll begin currentdict /MultipleDataSources known {MultipleDataSources {DataSource length}{1}ifelse}{1} ifelse { Width Decode length 2 div mul cvi string } repeat end ] def begin SkipImageProc { currentdict consumeimagedata } { save mark AGMCORE_producing_seps not { level3 not { Operator /imagemask ne { /DataSource [ DataSource Decode BitsPerComponent currentdict /MultipleDataSources known {MultipleDataSources}{false} ifelse colorSpaceElemCnt /devicen_colorspace_dict AGMCORE_gget /TintTransform get dstDataStr srcDataStrs devn_alt_datasource /exec cvx ] cvx 0 () /SubFileDecode filter def /MultipleDataSources false def /Decode colorSpaceElemCnt [ exch {0 1} repeat ] def } if }if currentdict imageormask }{ AGMCORE_host_sep{ Names convert_to_process { CSA map_csa 0 get /DeviceCMYK eq { /DataSource Width BitsPerComponent mul 7 add 8 idiv Height mul 4 mul [ DataSource Decode BitsPerComponent currentdict /MultipleDataSources known {MultipleDataSources}{false} ifelse 4 /devicen_colorspace_dict AGMCORE_gget /TintTransform get dstDataStr srcDataStrs devn_alt_datasource /exec cvx ] cvx filter_cmyk 0 () /SubFileDecode filter def /MultipleDataSources false def /Decode [1 0] def /DeviceGray setcolorspace currentdict imageormask_sys }{ AGMCORE_report_unsupported_color_space AGMCORE_black_plate { /DataSource [ DataSource Decode BitsPerComponent currentdict /MultipleDataSources known {MultipleDataSources}{false} ifelse CSA map_csa 0 get /DeviceRGB eq{3}{1}ifelse /devicen_colorspace_dict AGMCORE_gget /TintTransform get dstDataStr srcDataStrs devn_alt_datasource /exec cvx ] cvx 0 () /SubFileDecode filter def /MultipleDataSources false def /Decode colorSpaceElemCnt [ exch {0 1} repeat ] def currentdict imageormask_sys } { gsave knockout_unitsq grestore currentdict consumeimagedata } ifelse } ifelse } { /devicen_colorspace_dict AGMCORE_gget /names_index known { Operator/imagemask ne{ MultipleDataSources { /DataSource [ DataSource devn_sep_datasource /exec cvx ] cvx def /MultipleDataSources false def }{ /DataSource /DataSource load dstDataStr srcDataStrs 0 get filter_devn def } ifelse invert_image_samples } if currentdict imageormask_sys }{ currentoverprint not Operator/imagemask eq and{ currentdict imageormask_sys }{ currentoverprint not { gsave knockout_unitsq grestore }if currentdict consumeimagedata }ifelse }ifelse }ifelse }{ currentdict imageormask }ifelse }ifelse cleartomark restore }ifelse end end end }def /imageormask_l2_overprint { currentdict currentcmykcolor add add add 0 eq{ currentdict consumeimagedata }{ level3{ currentcmykcolor /AGMIMG_k xdf /AGMIMG_y xdf /AGMIMG_m xdf /AGMIMG_c xdf Operator/imagemask eq{ [/DeviceN [ AGMIMG_c 0 ne {/Cyan} if AGMIMG_m 0 ne {/Magenta} if AGMIMG_y 0 ne {/Yellow} if AGMIMG_k 0 ne {/Black} if ] /DeviceCMYK {}] setcolorspace AGMIMG_c 0 ne {AGMIMG_c} if AGMIMG_m 0 ne {AGMIMG_m} if AGMIMG_y 0 ne {AGMIMG_y} if AGMIMG_k 0 ne {AGMIMG_k} if setcolor }{ /Decode [ Decode 0 get 255 mul Decode 1 get 255 mul ] def [/Indexed [ /DeviceN [ AGMIMG_c 0 ne {/Cyan} if AGMIMG_m 0 ne {/Magenta} if AGMIMG_y 0 ne {/Yellow} if AGMIMG_k 0 ne {/Black} if ] /DeviceCMYK { AGMIMG_k 0 eq {0} if AGMIMG_y 0 eq {0 exch} if AGMIMG_m 0 eq {0 3 1 roll} if AGMIMG_c 0 eq {0 4 1 roll} if } ] 255 { 255 div mark exch dup dup dup AGMIMG_k 0 ne{ /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec 4 1 roll pop pop pop counttomark 1 roll }{ pop }ifelse AGMIMG_y 0 ne{ /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec 4 2 roll pop pop pop counttomark 1 roll }{ pop }ifelse AGMIMG_m 0 ne{ /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec 4 3 roll pop pop pop counttomark 1 roll }{ pop }ifelse AGMIMG_c 0 ne{ /sep_tint AGMCORE_gget mul MappedCSA sep_proc_name exch pop load exec pop pop pop counttomark 1 roll }{ pop }ifelse counttomark 1 add -1 roll pop } ] setcolorspace }ifelse imageormask_sys }{ write_image_file{ currentcmykcolor 0 ne{ [/Separation /Black /DeviceGray {}] setcolorspace gsave /Black [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {4 1 roll pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore }if 0 ne{ [/Separation /Yellow /DeviceGray {}] setcolorspace gsave /Yellow [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {4 2 roll pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore }if 0 ne{ [/Separation /Magenta /DeviceGray {}] setcolorspace gsave /Magenta [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {4 3 roll pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore }if 0 ne{ [/Separation /Cyan /DeviceGray {}] setcolorspace gsave /Cyan [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore } if close_image_file }{ imageormask }ifelse }ifelse }ifelse } def /indexed_imageormask { begin save mark currentdict AGMCORE_host_sep{ Operator/knockout eq{ /indexed_colorspace_dict AGMCORE_gget dup /CSA known { /CSA get map_csa }{ /CSD get get_csd /Names get } ifelse overprint_plate not{ knockout_unitsq }if }{ Indexed_DeviceN { /devicen_colorspace_dict AGMCORE_gget /names_index known { indexed_image_lev2_sep }{ currentoverprint not{ knockout_unitsq }if currentdict consumeimagedata } ifelse }{ AGMCORE_is_cmyk_sep{ Operator /imagemask eq{ imageormask_sys }{ level2{ indexed_image_lev2_sep }{ indexed_image_lev1_sep }ifelse }ifelse }{ currentoverprint not{ knockout_unitsq }if currentdict consumeimagedata }ifelse }ifelse }ifelse }{ level2{ Indexed_DeviceN { /indexed_colorspace_dict AGMCORE_gget begin CSD get_csd begin }{ /indexed_colorspace_dict AGMCORE_gget begin CSA map_csa 0 get /DeviceCMYK eq ps_level 3 ge and ps_version 3015.007 lt and { [/Indexed [/DeviceN [/Cyan /Magenta /Yellow /Black] /DeviceCMYK {}] HiVal Lookup] setcolorspace } if end } ifelse imageormask Indexed_DeviceN { end end } if }{ Operator /imagemask eq{ imageormask }{ indexed_imageormask_lev1 }ifelse }ifelse }ifelse cleartomark restore end }def /indexed_image_lev2_sep { /indexed_colorspace_dict AGMCORE_gget begin begin Indexed_DeviceN not { currentcolorspace dup 1 /DeviceGray put dup 3 currentcolorspace 2 get 1 add string 0 1 2 3 AGMCORE_get_ink_data 4 currentcolorspace 3 get length 1 sub { dup 4 idiv exch currentcolorspace 3 get exch get 255 exch sub 2 index 3 1 roll put }for put setcolorspace } if currentdict Operator /imagemask eq{ AGMIMG_&imagemask }{ use_mask { level3 {process_mask_L3 AGMIMG_&image}{masked_image_simulation}ifelse }{ AGMIMG_&image }ifelse }ifelse end end }def /OPIimage { dup type /dicttype ne{ 10 dict begin /DataSource xdf /ImageMatrix xdf /BitsPerComponent xdf /Height xdf /Width xdf /ImageType 1 def /Decode [0 1 def] currentdict end }if dup begin /NComponents 1 cdndf /MultipleDataSources false cdndf /SkipImageProc {false} cdndf /HostSepColorImage false cdndf /Decode [ 0 currentcolorspace 0 get /Indexed eq{ 2 BitsPerComponent exp 1 sub }{ 1 }ifelse ] cdndf /Operator /image cdndf end /sep_colorspace_dict AGMCORE_gget null eq{ imageormask }{ gsave dup begin invert_image_samples end sep_imageormask grestore }ifelse }def /cachemask_level2 { 3 dict begin /LZWEncode filter /WriteFilter xdf /readBuffer 256 string def /ReadFilter currentfile 0 (%EndMask) /SubFileDecode filter /ASCII85Decode filter /RunLengthDecode filter def { ReadFilter readBuffer readstring exch WriteFilter exch writestring not {exit} if }loop WriteFilter closefile end }def /cachemask_level3 { currentfile << /Filter [ /SubFileDecode /ASCII85Decode /RunLengthDecode ] /DecodeParms [ << /EODCount 0 /EODString (%EndMask) >> null null ] /Intent 1 >> /ReusableStreamDecode filter }def /spot_alias { /mapto_sep_imageormask { dup type /dicttype ne{ 12 dict begin /ImageType 1 def /DataSource xdf /ImageMatrix xdf /BitsPerComponent xdf /Height xdf /Width xdf /MultipleDataSources false def }{ begin }ifelse /Decode [/customcolor_tint AGMCORE_gget 0] def /Operator /image def /HostSepColorImage false def /SkipImageProc {false} def currentdict end sep_imageormask }bdf /customcolorimage { Adobe_AGM_Image/AGMIMG_colorAry xddf /customcolor_tint AGMCORE_gget bdict /Name AGMIMG_colorAry 4 get /CSA [ /DeviceCMYK ] /TintMethod /Subtractive /TintProc null /MappedCSA null /NComponents 4 /Components [ AGMIMG_colorAry aload pop pop ] edict setsepcolorspace mapto_sep_imageormask }ndf Adobe_AGM_Image/AGMIMG_&customcolorimage /customcolorimage load put /customcolorimage { Adobe_AGM_Image/AGMIMG_override false put dup 4 get map_alias{ /customcolor_tint AGMCORE_gget exch setsepcolorspace pop mapto_sep_imageormask }{ AGMIMG_&customcolorimage }ifelse }bdf }def /snap_to_device { 6 dict begin matrix currentmatrix dup 0 get 0 eq 1 index 3 get 0 eq and 1 index 1 get 0 eq 2 index 2 get 0 eq and or exch pop { 1 1 dtransform 0 gt exch 0 gt /AGMIMG_xSign? exch def /AGMIMG_ySign? exch def 0 0 transform AGMIMG_ySign? {floor 0.1 sub}{ceiling 0.1 add} ifelse exch AGMIMG_xSign? {floor 0.1 sub}{ceiling 0.1 add} ifelse exch itransform /AGMIMG_llY exch def /AGMIMG_llX exch def 1 1 transform AGMIMG_ySign? {ceiling 0.1 add}{floor 0.1 sub} ifelse exch AGMIMG_xSign? {ceiling 0.1 add}{floor 0.1 sub} ifelse exch itransform /AGMIMG_urY exch def /AGMIMG_urX exch def [AGMIMG_urX AGMIMG_llX sub 0 0 AGMIMG_urY AGMIMG_llY sub AGMIMG_llX AGMIMG_llY] concat }{ }ifelse end } def level2 not{ /colorbuf { 0 1 2 index length 1 sub{ dup 2 index exch get 255 exch sub 2 index 3 1 roll put }for }def /tint_image_to_color { begin Width Height BitsPerComponent ImageMatrix /DataSource load end Adobe_AGM_Image begin /AGMIMG_mbuf 0 string def /AGMIMG_ybuf 0 string def /AGMIMG_kbuf 0 string def { colorbuf dup length AGMIMG_mbuf length ne { dup length dup dup /AGMIMG_mbuf exch string def /AGMIMG_ybuf exch string def /AGMIMG_kbuf exch string def } if dup AGMIMG_mbuf copy AGMIMG_ybuf copy AGMIMG_kbuf copy pop } addprocs {AGMIMG_mbuf}{AGMIMG_ybuf}{AGMIMG_kbuf} true 4 colorimage end } def /sep_imageormask_lev1 { begin MappedCSA 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or has_color not and{ { 255 mul round cvi GrayLookup exch get } currenttransfer addprocs settransfer currentdict imageormask }{ /sep_colorspace_dict AGMCORE_gget/Components known{ MappedCSA 0 get /DeviceCMYK eq{ Components aload pop }{ 0 0 0 Components aload pop 1 exch sub }ifelse Adobe_AGM_Image/AGMIMG_k xddf Adobe_AGM_Image/AGMIMG_y xddf Adobe_AGM_Image/AGMIMG_m xddf Adobe_AGM_Image/AGMIMG_c xddf AGMIMG_y 0.0 eq AGMIMG_m 0.0 eq and AGMIMG_c 0.0 eq and{ {AGMIMG_k mul 1 exch sub} currenttransfer addprocs settransfer currentdict imageormask }{ currentcolortransfer {AGMIMG_k mul 1 exch sub} exch addprocs 4 1 roll {AGMIMG_y mul 1 exch sub} exch addprocs 4 1 roll {AGMIMG_m mul 1 exch sub} exch addprocs 4 1 roll {AGMIMG_c mul 1 exch sub} exch addprocs 4 1 roll setcolortransfer currentdict tint_image_to_color }ifelse }{ MappedCSA 0 get /DeviceGray eq { {255 mul round cvi ColorLookup exch get 0 get} currenttransfer addprocs settransfer currentdict imageormask }{ MappedCSA 0 get /DeviceCMYK eq { currentcolortransfer {255 mul round cvi ColorLookup exch get 3 get 1 exch sub} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 2 get 1 exch sub} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 1 get 1 exch sub} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 0 get 1 exch sub} exch addprocs 4 1 roll setcolortransfer currentdict tint_image_to_color }{ currentcolortransfer {pop 1} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 2 get} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 1 get} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 0 get} exch addprocs 4 1 roll setcolortransfer currentdict tint_image_to_color }ifelse }ifelse }ifelse }ifelse end }def /sep_image_lev1_sep { begin /sep_colorspace_dict AGMCORE_gget/Components known{ Components aload pop Adobe_AGM_Image/AGMIMG_k xddf Adobe_AGM_Image/AGMIMG_y xddf Adobe_AGM_Image/AGMIMG_m xddf Adobe_AGM_Image/AGMIMG_c xddf {AGMIMG_c mul 1 exch sub} {AGMIMG_m mul 1 exch sub} {AGMIMG_y mul 1 exch sub} {AGMIMG_k mul 1 exch sub} }{ {255 mul round cvi ColorLookup exch get 0 get 1 exch sub} {255 mul round cvi ColorLookup exch get 1 get 1 exch sub} {255 mul round cvi ColorLookup exch get 2 get 1 exch sub} {255 mul round cvi ColorLookup exch get 3 get 1 exch sub} }ifelse AGMCORE_get_ink_data currenttransfer addprocs settransfer currentdict imageormask_sys end }def /indexed_imageormask_lev1 { /indexed_colorspace_dict AGMCORE_gget begin begin currentdict MappedCSA 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or has_color not and{ {HiVal mul round cvi GrayLookup exch get HiVal div} currenttransfer addprocs settransfer imageormask }{ MappedCSA 0 get /DeviceGray eq { {HiVal mul round cvi Lookup exch get HiVal div} currenttransfer addprocs settransfer imageormask }{ MappedCSA 0 get /DeviceCMYK eq { currentcolortransfer {4 mul HiVal mul round cvi 3 add Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll {4 mul HiVal mul round cvi 2 add Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll {4 mul HiVal mul round cvi 1 add Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll {4 mul HiVal mul round cvi Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll setcolortransfer tint_image_to_color }{ currentcolortransfer {pop 1} exch addprocs 4 1 roll {3 mul HiVal mul round cvi 2 add Lookup exch get HiVal div} exch addprocs 4 1 roll {3 mul HiVal mul round cvi 1 add Lookup exch get HiVal div} exch addprocs 4 1 roll {3 mul HiVal mul round cvi Lookup exch get HiVal div} exch addprocs 4 1 roll setcolortransfer tint_image_to_color }ifelse }ifelse }ifelse end end }def /indexed_image_lev1_sep { /indexed_colorspace_dict AGMCORE_gget begin begin {4 mul HiVal mul round cvi Lookup exch get HiVal div 1 exch sub} {4 mul HiVal mul round cvi 1 add Lookup exch get HiVal div 1 exch sub} {4 mul HiVal mul round cvi 2 add Lookup exch get HiVal div 1 exch sub} {4 mul HiVal mul round cvi 3 add Lookup exch get HiVal div 1 exch sub} AGMCORE_get_ink_data currenttransfer addprocs settransfer currentdict imageormask_sys end end }def }if end systemdict /setpacking known { setpacking } if %%EndResource currentdict Adobe_AGM_Utils eq {end} if %%EndProlog %%BeginSetup Adobe_AGM_Utils begin 2 2010 Adobe_AGM_Core/doc_setup get exec Adobe_CoolType_Core/doc_setup get exec Adobe_AGM_Image/doc_setup get exec currentdict Adobe_AGM_Utils eq {end} if %%EndSetup %%Page: xen3-1.0.eps 1 %%EndPageComments %%BeginPageSetup /currentdistillerparams where {pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse { userdict /AI11_PDFMark5 /cleartomark load put userdict /AI11_ReadMetadata_PDFMark5 {flushfile cleartomark } bind put} { userdict /AI11_PDFMark5 /pdfmark load put userdict /AI11_ReadMetadata_PDFMark5 {/PUT pdfmark} bind put } ifelse [/NamespacePush AI11_PDFMark5 [/_objdef {ai_metadata_stream_123} /type /stream /OBJ AI11_PDFMark5 [{ai_metadata_stream_123} currentfile 0 (% &&end XMP packet marker&&) /SubFileDecode filter AI11_ReadMetadata_PDFMark5 + + + + Adobe PDF library 6.66 + + + + + + + 2006-05-14T09:34:14-07:00 + 2006-06-26T18:03:19Z + Illustrator + 2006-05-14T09:34:14-07:00 + + + + JPEG + 256 + 112 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAcAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FUn13zZoei L/ps49elVto/ilP+x7fNqYq8/wBW/NrVpyyabbpZx9pH/eyfPf4B9xxVIX1Tzpqx5fWLy4Rv5C6x /ctExVT/AMJ+Zpvia1Zj4vIgP/DNirY8v+arT4o4JoyNwYXBP/CMcVV7fzf5z0pwj3c4p/uq6Beo 8P3oJH0YqyvRfzcidli1i19OuxubepX5mM1P3E/LFWfafqVhqNuLmxnS4hP7aGtD4EdQfY4qicVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVececvzLMbSafobguKrNfdQ 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 uxV2KuxV/9k= + + + + + + + uuid:65ad4e0e-e367-11da-8f1a-000d93afebb2 + + + + application/postscript + + + + + % &&end XMP packet marker&& [{ai_metadata_stream_123} <> /PUT AI11_PDFMark5 [/Document 1 dict begin /Metadata {ai_metadata_stream_123} def currentdict end /BDC AI11_PDFMark5 Adobe_AGM_Utils begin Adobe_AGM_Core/page_setup get exec Adobe_CoolType_Core/page_setup get exec Adobe_AGM_Image/page_setup get exec %%EndPageSetup Adobe_AGM_Core/AGMCORE_save save ddf 1 -1 scale 0 -93.5196 translate [1 0 0 1 0 0 ] concat % page clip gsave newpath gsave % PSGState 0 0 mo 0 93.5196 li 214.165 93.5196 li 214.165 0 li clp [1 0 0 1 0 0 ] concat 8.25879 46.7579 mo 8.25879 22.3165 28.0782 2.5 52.521 2.5 cv 76.9634 2.5 96.7769 22.3165 96.7769 46.7579 cv 96.7769 71.2032 76.9634 91.0196 52.521 91.0196 cv 28.0782 91.0196 8.25879 71.2032 8.25879 46.7579 cv false sop /0 [/DeviceGray] add_csa 0.8706 gry f 5 lw 0 lc 0 lj 4 ml [] 0 dsh true sadj 8.25879 46.7579 mo 8.25879 22.3165 28.0782 2.5 52.521 2.5 cv 76.9634 2.5 96.7769 22.3165 96.7769 46.7579 cv 96.7769 71.2032 76.9634 91.0196 52.521 91.0196 cv 28.0782 91.0196 8.25879 71.2032 8.25879 46.7579 cv cp 0.5647 gry @ 116.116 47.1055 mo 117.075 42.9981 115.555 40.2793 110.896 40.2793 cv 106.516 40.2793 103.46 42.9356 102.483 47.1055 cv 116.116 47.1055 li cp 101.063 53.17 mo 99.8052 58.5411 101.595 61.004 106.256 61.004 cv 110.22 61.004 112.205 59.3594 113.233 57.3379 cv 133.266 57.3379 li 131.397 62.6465 123.05 67.7012 105.038 67.7012 cv 88.691 67.7012 78.7691 62.834 81.5796 50.8321 cv 84.4605 38.5137 97.022 33.586 112.466 33.586 cv 125.82 33.586 138.41 37.4395 135.127 51.4629 cv 134.728 53.17 li 101.063 53.17 li /1 [/DeviceCMYK] add_csa 0 0 0 1 cmyk f 139.871 47.2325 mo 140.86 42.9981 141.766 38.8282 142.365 34.7872 cv 162.536 34.7872 li 161.512 40.3458 li 161.648 40.3458 li 166.04 35.8575 172.068 33.586 179.16 33.586 cv 185.423 33.586 195.758 35.4805 192.936 47.5469 cv 188.498 66.4981 li 168.054 66.4981 li 172.026 49.5059 li 173.122 44.8301 171.49 43.1876 167.941 43.1876 cv 163.209 43.1876 160.644 45.8418 159.507 50.7051 cv 155.807 66.4981 li 135.358 66.4981 li 139.871 47.2325 li f 39.7618 47.836 mo 17.8775 20.8731 li 44.6934 20.92 li 56.3023 36.6368 li 75.6192 20.8731 li 106.646 20.8731 li 67.9107 50.6114 li 89.6958 78.6407 li 62.6739 78.6407 li 51.5777 62.3243 li 30.9258 78.6407 li 0 78.6407 li 39.7618 47.836 li f 199.061 36.5992 mo 197.165 36.5992 li 197.165 35.1919 li 203.389 35.1919 li 203.389 36.5992 li 201.493 36.5992 li 201.493 40.9673 li 199.061 40.9673 li 199.061 36.5992 li f 204.381 35.1919 mo 208.069 35.1919 li 209.276 39.063 li 209.292 39.063 li 210.46 35.1919 li 214.165 35.1919 li 214.165 40.9673 li 211.909 40.9673 li 211.909 36.6236 li 211.893 36.6236 li 210.309 40.9673 li 207.956 40.9673 li 206.469 36.6236 li 206.444 36.6236 li 206.444 40.9673 li 204.381 40.9673 li 204.381 35.1919 li f %ADOBeginClientInjection: EndPageContent "AI11EPS" userdict /annotatepage 2 copy known {get exec}{pop pop} ifelse %ADOEndClientInjection: EndPageContent "AI11EPS" % page clip grestore grestore % PSGState Adobe_AGM_Core/AGMCORE_save get restore %%PageTrailer [/EMC AI11_PDFMark5 [/NamespacePop AI11_PDFMark5 Adobe_AGM_Image/page_trailer get exec Adobe_CoolType_Core/page_trailer get exec Adobe_AGM_Core/page_trailer get exec currentdict Adobe_AGM_Utils eq {end} if %%Trailer Adobe_AGM_Image/doc_trailer get exec Adobe_CoolType_Core/doc_trailer get exec Adobe_AGM_Core/doc_trailer get exec %%EOF %AI9_PrintingDataEnd userdict /AI9_read_buffer 256 string put userdict begin /ai9_skip_data { mark { currentfile AI9_read_buffer { readline } stopped { } { not { exit } if (%AI9_PrivateDataEnd) eq { exit } if } ifelse } loop cleartomark } def end userdict /ai9_skip_data get exec %AI9_PrivateDataBegin %!PS-Adobe-3.0 EPSF-3.0 %%Creator: Adobe Illustrator(R) 11.0 %%AI8_CreatorVersion: 11.0.0 %%For: (Rich Quarles) (glassCanopy, LLC) %%Title: (xen.eps) %%CreationDate: 6/26/06 11:03 AM %AI9_DataStream %Gb"-6l#J&kFY9G%;<$r+h\4JAk%%?HMjl%q;R=s %l1aX&+/jcu>SYjZe[P=S*uao3P\knM>lDQAS)=/3bMVZhb-J_7 %r_2ae?%uR_PZRnScOWg$]RA]M^e=\\W2FB'pt8J\f+$>;(,1hrp%J+641uJ#(gBgs*W-hfTu.YeFoVAeGPA$PQGZPXY6Jc$(]EV? %SuiedFbrn0b9!]'i.'R,XhXS]:[e:)X4Ku,l`ng=(N1T$!6pg %.SFU7aLrMV;OE7e2"nC4=*jOAei9$c45`g:F>Ie"f6=r=:"0!CmGkt\o+/Qh4AH(+;;X7E\aEhn*ZgpP,+%R_s5CG2hq`e[oR-Q# %rortQr/@!"n*WAes)@l"Ncn6)^j`ho`tj/Z^ZA'idrZ=`1Akq.&[iB)M8&.;po:R5mf*1amOte)DVs87IX:kc-f+i1h7qu%Z?cHA %p&0BW?Ke&fI]W=nhgU$aHiF!(4`2e(2^@5Rl_jK$&$uZ(hYk>DmL&>dTnP/ph#eQ %2d_#`p\;@us$a'gGu[j(lh)!7M>nu4!CDRR$L2<.WfEYT<2bsMQh5j9%-td!oFX5[_,mk%^O3cqmf)_md_PjOIt#+]+80jgFn6./ %"0HnJ5O*`9/G/hgVn5H5I/hI[rq-$(+!5FiEH%r:WpQrs,/0FPVB>!U:U`$jRIM=7Zm4TG2?>E5?6 %:V7&3LHg&"H'Rm/o(2C.\d#D#\pebH\:$L)GSQ:/NXPTO>E:B!G4BXaAt:Th6=4sHp.e[9p($QR?LtYY5Q$@>nbU9)nX/gZp_0jI %hRP:]AaA43OlIhYlIIhoCIM;D[[^D+Y6E_u3i>+.gc?L!IP-[#[TmH5qo3KnX7YX]45\BAo8?hu4-N`%cT.$qf)cX\nO?7f5As(>MdI*UnJ-Fhg(D(&V/$J%0eh_rH-F\44,8Y<2I4[YJUm]bGdl %=.]C5mgf"gSXV=Q1_.cVX17'W2ge-F9)]Mo@/9/1>"do.8=^bb41+VSfYtV9hsiY&p."2S8_\X8I-:Nbg2WtC[<]FBp,BHcC2EK; %BM0D9I]e"lF=/kaUuhB8Y&E'plET8"^jGfse+;DN@bN.*4j`6K\?*\T80i74780rS0o]%:*CF\Ya$p-S`>fb:PG?$5k_pbtAOT8gV[IV]Km31B;=Do\rQ9nV-3HWW5!WCBD1H&j/mE_Aok0n %>nc"'?>nfEbdU\hP3Xd'Y#[!1h`[1I(l^.46X/V#gK)<]fAALRpF/3,c!4go$r\3np6OjMf=g6ZRX@lj^6Q)Cc#,M]EdcMOng^n$ %[4:mUa68qToV`-\(:nHm%I5J,kB5063P5!6rIeJ_kM^V@;P'Yq1sDZ\G8RBJEj#B(>nZh:$f_fPZ::m>O&9-XpuF:_#MU-h%qcK( %!uF2l(mJTXROq:j;<`W)dbNZuD8nd'lD3U03hFM4PPLr=#26+8o.&\i_u=;;b5Kj2V)VVhA&gV(jJt!l%b]4IeFRifd_b!<-s"He]E"')D,7n'i^@?e9)rXe\fu %E5C%e@^"2gh%=u!q2bJP2@-rM97k7&DMBkFnWQk-9'6P21\^4mHTj%ZccLe2XFg1FZ]Q.ab&(=.HWYk0S7haA5KES^Q@hkFC$2a" %ERF>rb0i8YP>\YQ-c;WV;!Z!oD0IHsgQikH6T:`df[!it4-:PPQ0re5'$Wr.C[CHE67kW%"5!3=/^9`m`md2+dBcmYmN8W!%?Or[ %5r1oQP+D\DoS_PcHaHB6?e_IhBIEYTer$Z&T,sT\CnO0^O(]R %52L+IiTm!#]=[h3%hH-.]A%P%5Mb/nk0s:Hqn&=NoeLa_H[j'f?ZJfIh;#`VIt%.f_m^/#s)E*!Y2T7rWUb7:q&S;UDXR;N4*0_Y %^\+2!^ZTD#br,7pRRaC7h)t58:OMPn\b=-Pqnu5)ba3e@90\2)h9BS^mJGZ,^:Sa+Gn1@s]=W=nj*gOhe(sTtp>Y,g4#DrQr:0Br %_qh5[mr+h<"MXob%G^L4p@8)'+(1>)a^b[b]t\jeo_fPMHprbnFt5%5ns5Q0^6k3bIj4ktLZ5@J]j^UnoE)uO"! %5!6[B/t;CeB:.tr%o8f=m_8W6hXO.l4ZqMLO$3O+mM)L6]@9tIn45egtU_[obCqM+=3GN!WBnGW=#hr=e. %KBC5P?i=?h52YpimQNj";+kW$pib+JWm9FJHOh-_jT//kq&Z.!oH"eSc#qZOJ%Y`)k8Qs1i6kDNHM[OOQAE0+QmK="IrBRCm!Ri: %4>ah6c[TYqlS&3*^Z:]*=8^oUo6^K%4jMQtO7tTqDOuXp\*oo.]g(EEA[h,A6h$E(*URbDo_J$hgZQm)*c=2l %JpmFdGk^BjGIMO`qq[9+]^>ask'%n3J%5BpeSjA=2L6%)g-*/s6=5sa>^^*&6=TDF5MSY6hF(GN4o'=%038L&:oh7_]RTejAC\P7qp2mpn*[?YVSJ*3?laagoa`/Dq"4(#%K@404a4U$ %nb`U2pFDUN[#quXI#m.nhgG+d)8>t1kGQY`s75^7-he-.107hV;>g7[b=D?f:KQ$:kDt,(qVBWL_p>LF$fih@"i#HaA?S2l%(LJo %r_V!2F`22LO:_)uJ![3"o=]Y!g:[_5\:;#:^'f)1>Q=TYSR9.,U!0o"rlS<6\`9T7jt?XU(;m#GAXMI5Up\2!e<< %s/mFbY&(iLp?DC*R9KH/p\=3$%o;)P@J$/lD7o`&\'Ns8-(m/HeUcOXi^ %Wk4]@c1h6sRhmL10?EZK9<E,6urg$9U!X5Mc2[b1r9AVS2HbLQsr %2#K.'`<;A[_A)Ys>qJcW"oX-(KrLW`m5Vr;NT2_d@gD=u)/JGG3>TM]fXYE3[P1@D@2t#qLNUuSbtnr$;"XX6#?&jUTA\rY[%W8_ %dmAt-BNa")h&b916Ye&_2>TK0d"g1mH6DdaW#(ltsS@/98i;tMHMTrkSRJ]$k1_CuHRR(.`P(g>^" %'lC@QhF#uD-JZH`i3jV68d:Y">kP4OC[kE@`0GIBm@ilS12!!&$!0)[1%Z$S)^i=Ql%_o?+d>e1>T0h3AmD"5?uY(`N`K"Q];pW4 %@(d:89,,>dapuuPXD-;mY_e7"3`S!aPkt%33d31q4<$FoUgVnZlAZ#LAooR0!RWlB[P\1T+6>.]KUkXcUpEaOjt(&LoRsOqdQ&NA">VmQ5ja=.C8cY`1pBA0+F!u>M@C-4/J,e4)[ddI %'bJGU(=+*r@nX/+B-a+0SXbUc,0k3Ei^m84&P+L,0jq^]Gm?Gf[_bs1oGe&+'ZsLmq;OMXTaXCd4,)>rAMok.@!4n? %%UqJdk>8mF>"G-40>l$:cErXf>mGhC_<9cY;V2UBCkO&X;66:tOV-+dlA5'E.HE$Z''\JQ[$&QsV5FAb@R3["NeA]>n2YrY=,qAU %eq(6R<'NLC6m"QAtcG1>D4Oh[&]JXV=`=TN(<2T-",G$l[K5sZO2K;0d-TYY-9(&":J(1ElX\'r*Wl&R3a %fLVJ1KTUA2PLN1LE0>Z:bD;9kN^MJkUr9q[ISr2K;+S-9QHEJ_Nu9oOr>fmKI*U\YnA<.9l_oJn)Z<(K1f8U'(ojJIP&Cdkhms-hM999jJ\V6WCe*/PKe&5gBg\]i,Cm\K46H2,LD@LWZ&/I492ZR6ooLCTuM_kPFE %_Ti@=*>Nj3&:4-$0.qURF(P?sR(J8:7*BVZaYrO?qD'AR\:h6/`)Xp['%^[_$?;VnZL7KeELst3i^g.t=a.4?kHL]`Z/'an>O.l( %6=M/WN6]btlseec\9Cp[/'XA#K`&)W[GM)H1IKiIF:)daQJ8Xa/^sfhCZolCTJ(PSm=A&"IVC;ped+@3hn`?J:#?LBYBHb;'5nE5 %c,@(cYh3np&$VUeUMGF&f>QX5BQo2_1,$?gP(I]"ofsa`RP\tm<"=YYKS+?,.u@u_a7k:kZag>gVXoqU40Q057n6mep0d!#"ZrOmeLrC92Pg,=JJts7^5]_h'GPW3"IHoiqF;Ng,Q)4At_E=;`qq(^OL7KX'([2e?l2@;`1NJQZ7$ %j;KpXH!FF)=)Af1-*(g@%'pNFIA0m67L(TiHId9oRYAjs'jtP88EgC;1+Kt7S60L<'PTZSM^pFk#E2bXC2Y\\D'_IL0-f_n2hJLm %l7?m9imleB`FS\r$u7^tnTlA_`1EjY$J2sQ?PW>KM/8a6^@;+(,2Oi8$S#+nr3]i]#KR:r9.J.,M9!rdjXkqW[+6hN09[2'7gc!T %7D/!.HJ=7;M/lk:*`T8gE0#F)<=J"q6M@^D_4k#7HB:/d63=Q3spt$A(JJ93g!C;Ep4^%'MG>UBoH1fa\lkF%+efr %<>:m$?H'3i,X89dg63MQibQFMDdmOFF;?FJ^.ERI8pHY)0M6u-?ehtWHH'I-m$F0$[7d8N4\GT]H\8\Sd2/,Hj,UXmA_"Gll:>m; %8ZJ[jSlk'0":hmbEU:WpA>G6>d'<71a$BP:>:JKp-$GjjPVjLo9`E2J[suMHQ[TM%)OJ/T%PW9.>f6^sbIY6q:&^;;>_Y1CE2CF4 %l2SThPC<"c37u()ORfVq[rUk2mG`0I'fn^`dbHFsm!l(D9)ifSbEZ87Upu@O*7$n*`7T$9R\)HcgULq@m>uM=@+t9``H&DgKbdG/ %1jfp2pfQ4Fh3:6eaE;bVQ4%VZG/F.oVM&T\5lF""eOI*i"3reTN2%U"<\V^A^@>)/ZHJ\uG@HH?^kV2r7V)%`@fNDZ&G4fC!jp-D %d,HimAEZr\BG\e;@Mrmm?)/-\Lq8#>4_-oST9rD^Y(N+/]f:TYl3ug26"!`e&^R\IfEh-\@ipPiHFjd7K3pG-*"2X=M!nG;"&Y1= %Tk5OZ(bn)jQ_)3/n:"pf)Pr9[C9'TDlt;'J7Tt5+q1u^n7Z6[!/Ic8]_qEM#%V!0[%MoLs$`Et>1u]f9J!D^>,R(Gg*u-Qp;&Q?L %8?"n-aF2&T3n;-p%'_Bk7\H3`fi^d2a+?gV1Lm/SU*CnXj3.Mf+GG@RK=1:hV*J6X1N&Jf*iC'gslhf>!0K'=LB]jeLd:,!([89!e^nfDc]Tanp-6bGRn\SJBA@/`J#?;_*;!k:Y"!*EaBKjYOslaF^>2Zs;_cR>j&jU,0I";fojcPF=6 %Jh[]H1R#*2pi!aNWNP3Ukecr:\;-]mOG+EZ+1?knCP:)E#fiO0,k9sUg["^ICqA%Wo&;.'"Wf9-$a5)P;(hMBlW6mn#3iC#0,!h% %2MYM`;f`/5J%deIqqN.LC")ES^"$USn&51jqtKPtIpd(`qZ`WgX7J6sPTc_?.Re+jOqOphKWOsTd7&h#OBPY%n=j73auHY]12GW*Z&&R`7#Vba8Y*(V`#gQOP+D,6/r;/HV?grM_*"_[*aSdg]_jh8/ %4nY[pjHhTa(WMJ[*ThI*D.##:jDgR)$2b6R2]29q_h$>OQWQ6(I;%itXZA4EA41Pf8>^$f:u/-fHC?jQK[ZSLM,d27Z'80:,LIJk;&sr$;-%D<0m\bS[]$Jm%oQ%2hKs/V\%U5bmLQQL3mpBn;T.EDKS].D+[Z,#'.ICaHO_6Q %$4nUL*-XUVZ:!_AHoOWHYgPn#*@13mR8&C:22>E"VCIP2:o*6+V=01X\6so=kLg65M=hfSrg@)d5KjrWY#bG/Y+gnFcQ)m^&Q2d)mrZTiiY@8@:RU&gT>'5 %-mX0bGsRoplc<,'<[sn2lM8RT$+n9tdX/_!I-.e,92fF$W_>C*Z\j=Wc/djVP;De-A1JA]b97Vt,oUrN8B$%7L>\%*V'DQ?7ZD]c6)"aD#l3DorO"'G'&uZ4f1nL6c[NEKoNh8]u>H>qA!j#huS'ibLPH>Z+ASd]N:L2WRM:d/mD,93gUm %hCl"n?lTMGI6\u"8Q?srQ7R+O>qom^"m=^]dNYr>+Hb %s!Q=WV+=SuP:ELFBdgo5Nl($t`RQ>THa2?)5d%GtN1I8&Zpto[H#HIIdDW6@s6P_Ss3LQ!q*2OLeWTgaO[rJt9eksjS;_-+rNJ)B!en$IPt&`@s98p(l:C<[P!]0 %7;<<+Cat[J&2JRR@e+QWm,K:A&!_,UF%P`/G`8p)Tf>5of<`!"H`j.6T$E&Z*]riN]FbWan?1$OKfEL/auGQQSop0f_>tFpj`r7\ %)j?*re7)":Fdqcnd[B)@*p:EKD3/1sW6XK=&5BKAj5KTa28VLm/j,(HOUiOJf-'`':%%+Cck4jg=U?H'CUZg'%nP.EF>1dX[^P5e %W4_J?H?'60O.+U,,=cWeagm$R3Xm%BQ1FJs\UY(V5NblAlM9lHmAHlFX5+;1E3MfMiG`Z+E!>3qVOb2I4k0QVWdkf"dt&Y.gF_cR %*Lp;(CM-u$IPjnbQN_G,*HP#:(3U:DXiqL.[dg99i*BGgp=-!2\T!F>KEIbj]'tJ'\01u5KM[spMJ!d=4OO!RL/LApp-+"8'&d;Y %[W^[U(((X0G#>'PBQ4Yi&h"hG'Ja+f-8Ha_,ue1jTnCG.=VOA@dtsm7P82KiU"BGF)h=,cWX3Fo69Fm[r7!E95YYW!R*p)Jh?_B" %T/\hf7?5HMb&JoUKl^Ql;Bpk:'.6"S8u?.M*&1tdDkdH4oU^044u@tM=I;OJ8I\AAi2"Kj8KE5@$;YO^h'teL;2#d77I6S$GJV"U>^\bnQ %No$=D?&`=^W9.hf"a.c$5 %)RZ(9?s'mR*Ri+IXc-JhD&om!XiF_W+H+5TY0.PM#HVk3Fi-Q"`a&dWY\nl1CMMJrTFjJn&W3X!"I2hm'cs&VYIH/`L1*d1%RH2Y]#K(XVVImi3i\)[dihlUL6j56oi*BlU:_g+sfUqC'LGJ#[dpqc_tdg/8]UVCE;]-NK;F) %B*3iA/l3K%0TP;:M*FGENQX'Nb2+Xb=Y3T)7WKb1W\`9/-IbNe(Gs]uG5jf4%IX1PC]lOKW#H>W33Wba'(7(RZ4cjCn"_!$Yt]tG4M+t2P0NGS5DuuQ,D0k6f!X2)0JN%Z"hcn(m>?[+']h]5]K,>Zr %`Zg3X\F@dhaLrE@g`hG&Wa72jS)0q'9qo:g"&QKP*DY^k_E.2n++,'\\nmWDe0%n6U_)aM#Fcpb"uH076#d,9@Ad&)dl3 %>I4/iGUZMN[8\6\&]%11\%MjFjq'@-$_:V(eQW+2$%dmjQ7ie+@NX_YL.89'':"V_BRJrUnV*(G1ak-0ong-'H$*Jon4SK3e@R`k %qP>?u$e8Uar(GRiBrIh@!1d0lVLV$J^gn?=RC^=-BSc8"PGo776RUc5_gbb!iQIs/h$HVO_2[E2N;?Oi6a?Cg1A$nQ:01#3+3(M[ %E2p`8$*]Q+)M3tKr*)0"DsYX)pb;7lEKmED7tIDujK?3PEHp['$-t-4]=mZ_pu!-)KAYPB5Q:3;00aOW0;taH(Ba+eUfP:@=E@u? %=pK\bc2R/ujE2$th/X,Cc-4pLt]n>.;=fK=hV++tsPmCKp,jtt.-G;!f!e%lL&1KkU!0mOViVo8> %B)tl>IUZEgBJ8!LYE+&\Mgd5L+^<)?.C#YbMD?^.dMt"2_R_PP:7NZ2m;7e\m,K(uS+JIq8^HC,+6p.7AVqS)<2^'_'M'1SQ$JFY]N7op#3Mj"[E$pW`F>%;+jAPU'TWdjL56Enn,*%3krP`LN!(%X70Ms %RTCn-/$r.hs6.*0W@O!Z83O8rc:>R$\/1-Q=b;OH[4YXa)O\87=qfJ(Lkd@/*<0GA8UJt0%`_,2`);[BFadF09b%_7'c %5ReOkONGJ_pBf2<^uEut$<=#*o^!]tgJrs%Om\Kg[TYf:TE %7=Paofac<3)U>N-cE7I`5UlE5,sB2$!^1aq@qt$`BUUUX(g.qVVQC\>CN!gU..P9JE&f3sRZ8L"M=ptt:(Ek7m"@`=Ur\oD*B(;J %#]./mT2-7e>PMa&Es3470S7)2Bbt@fFj>cRe:'(*Z_KASTQ=GT\,%3,4eV9HD[;(^6&bYdE0gVWXG'`ep%V-t%5d7%?S&WZM\6]F %"%:_^=jWQRb]_S`V-]C$Xgn&-hJaB%WUkfhbS5S>f_F4-`1*nfV4X6HGhDFV31tN;I&fp<&o3#D60Ma%Yk0 %J6(i![9AE`IRVmo#*(a'Wb\hT[i+O8F5X%hO>@bLE]Hm>K[r[p-77L[d8H.(^na5u#a09RFTrf.G@]DQT4B^=UNq7I*Kng78\,9W %rl@WGG3"QDP-Gtj$i;)C@VKV:Z3!USij>Y]!XZqu%LjFU&#:5GW'FQ)jf&nN"VM1g'hg$C9JDT*&eP_3KB,<"lO+gJDo1Y+YSh5+ %]dqqdnhQ5HV);jB,jXD:[3CX5LP%*$[iS:4e9#?,#$D>]8haA90"VV[8siXqLim`Sb/*7cS8(qNMh52h,&S;C/Rj<5(i;b;U.I-k4E93bAI5ADQj3Q:Ynf);)>q'Kl\=gP>o.0Y?XO)6`] %hQqT1AhZPH\X5L67Y]Vdm,+jo,Ycl!(4#9)AW/H88EC&7R_cim]\(pQg_0<6.^cqk`bc["G(iBM((cdoJ6<`C>C<6("XNi9CUm3[ %P8P8)`Eu^1FgfGfQ_*4Np%@/$a3V4*=22Fqs"bJ)6Ph86D5Ml[)FV_cadT]/SWaaB=n1Atnc4oGQTUq1qM@?LX?<[q:$^SGZo43Rh %@fiC]"#[(9e=%EMX9[H[_9'(ic?"^BA-Ual861DkYdL_lM$4leJoN5s,>lRf5/Bo/5kQTL %N$XS3!m]AfK+i3UDN&Q4%57eb*)de9$[6R*FE$/O7q+UN/=a3Ok97B*@bh;3 %'&JYmJ8,4'#j6F7h+#:hj'_`)Epg'Y4jhTXQQ7Afk_2j+CQ*jL"s^iUJoHKq5eR:t=:%=b81$>B%tqT>5.158_1EY>/ro*iI*ZZ6 %GqRTpLdKoF+6jmKh,t7c3!kn4*/(4TSM0J4FtpLae[[7U_MqXM!GtERB9XI?kV]Z$QU\`D/NG4mjCq92IihqR<^#m9N!Um)U9IZf %YqS6=LHn[>%C8@+a@(*Y5)@%\KFq/HM#1K@$EF8+gt3#+*:pQ^/\$j%h`30c/F>o2IiLD]rNRWUORIg[j\q'd46 %fSG`Yqd9e&!CntPlPP1^=BGt@4<\)@kOZ1ZI'Xe,h?b1_0b3usS5ml(^c;lAH!ocbeQ8dF]WXK"I4>`i5oS8L\Oi'c9D*#ONHXJT %0g>/]:,h'-DkS01;ZsP!E%#Vg+qS3P:A0o&W^p9S.Y^'mC*)Ro:7%KkK^dXfZ8U[gZ+ppX8E83kEP[!`l,m2Ca!L6d.^N]W'"4_Adt7r666BJu-P %DTA>`p.UAq`s6`J7]Va,Mj$hY['`QXn6Tr!a]WuGb7$R-Nm#,[i03_:6fD?do#\]$]FL<'BTRQ]0Um%.\CUbf#tKu%a]XfjC8.q$ %X(MP0k^Zd:\ihsE#LTBeQ&,2.Rn"*CK(k7'3.9*39gP.YI7B-^Ir9JmUAI_PiH*$Y`DlOY_B(fFZ[Gb5h)UHSe&#eGa %8Qa@Rj0-K/lrs;MJXSV1I1i-H!%3T*[._XNH,pW$4n@q9,]l4bWp."5Q!,LsWW<]io7=^G=hFOukN`!#]$eLi" %NgVG?N/OrN330".%L(CE"\kOgeAq4`0bjoJRGN:pg4eNge]'==Bi#Zd:`X2'"U9\n=0/ukKs6*6CQr*2#4/kN*U!=K)Vc'-KeMCc %[4?C3"C.d@\-R(sYHXO,UkH!fQoS3b"=9#S_$"FHP#,GQJL$.Fgb6Xs2-JJK_Q)aMIl/f/ZJWschn=mPR#AND[mpB4)]dF+];Y'@ %?"/pIk"5Wl<2g]M[E"e[ZEP]pVoW!jl3%rM%=/tZ]1^M[dr\"_I7]86n(GrXZ'e*L$+i/$g%@_jU+IAsG;E_?%Da$=^^gJUK+n1= %@'s.k!tk[W*D9m6QR0r#]'OamD&5PIS">t70'>89&t2_-?)H6%B/B>q0)048nX^clIOl[kTsAl2':$HVNI`12+[].`NIu*\(;Rr1 %Si[UY(HXRS2VnXhq&IbD:-O8.G+rbUC7A8s9lD'L47$("9-4$4nRDmmX1GQi"IYAq#C!s%F$N78Ebe4boOA4$$0LV!ZO)ce>\XpI %'.@.jGY/::P(>\(R(*+6P"L'f(LA<`LP/PL_<1hhVn=.Uq;KcIV!+p%^QjdeR`BBThH*Sd,^-+"O\53^-0n&o/p-;4:Y2p@g,+h- %h8Z(.F*3iE\aJ?9p\3H'Ij4YMadaZf0W>U?V4T)(mR;#-,pdRh`eb;K<223BRLo1%e[*FGo4-'#;rcH\W&`ush5k(j>BRC:P`u#q %/sH5)7I"Npe3Eh@THV4HaW%KB!tpdW2&SQ)5`h8KJ2$IQ.2f$/j+Ls+Nm-Z1j%O"9&nNV]WM!lb %O'HoXR,\T4Zs2ghnbcj%e18O4TOQ$F,WWD;1$\2n3',33ZG!>t"b %f!C$47ui*=*@_Fs+`PDjC>OEkK5K %G6rNl`2j8@QR4!f%c0+5figQqS&qc(c9Z!eBM@'8%liOq;Y>?4YnrQRg:E_W@)dU)%Git#eP0%U]F@3@4,Uu0^-IZ4?BstaW %[P0W;G_/9.nd/QeN'h$f>L$EHgg`p5W?t+g8>oWjtcI27@1) %Gl4b9rpGs0s/dU#Q3:FfE@^jRSU>-#O)3otXqe@3#M-lb6Qp&=O&-q!kb;!43_.Ft)GX5V?/A26N)$,hj^9)@l>.^.lf9j7bejAH %4:Y"-fijaXXFqa-QE]n<+VcA]>aS16_@MQ3)dfI2fnSL>_)tnG8Y+irOEVVa\.)Q\JLPU\0R6]o1oi'@OtnAZbN<"$R8u2sW_'Uu %8cM,AChKJQHfr:W6`@)4AV=IEJEIcrf580:fmE_97A7%k/5OmnKpf[Q!je*ZqJf@j\\`a@EW:kQ%uR1o7H?m7S"N+69/2n2SP:`m %IDkR(b3lgY9-j:h`5./6jo)hj\kShJ/TFA?F2+)>4.CCW=6@s,l!0^upA>fPc<,5o:NIl)\s"Zc8g9UbI(n4j=q[>ueD>#0BHMN^ %N,-fr)fo+MHVYk9moYg68p):"eZ9`@+tOO')M8s:54Msjmp!-YLM1*iag.!;D@G`?]Hld<[(4%[+%Q8m<]rX44s,p=L\5W4V3G![ %&+op:/INYbUc.Dl&6\m^]rt]"?";/[q,X-5FK*HR,HI;6(A=a^N9kJIB[DSFX7VfPpJJ@sS$>r9`oJ`m]q%fRoBAq9l#Pma%^ %D$L%&1MH8rHH@cSiS<&r3CK_(&&o4o=_r\jj9rQ1Q\8B!Fl0[9WAaaGnS'[RVg41X/?Ebpc@XaJlU0sOnVdf.8uh#:cP(?2%Og+E8T;-E&/\\",2d\_6kg:I %"[tsIKEd/6HED/g9!ncgmS),\Q6?rThtSt0QHc\o9%LBjYg3b/FtsZ5o"p[`ZB^]UJnM?2bdF10$;i96WK2&f`(N&al.0Ca(11@Q %R&S-r9U7JXfq%=p)i`l1HTcMs\083Y;_:Ka:KAXq]^3tVPB1h=AWj@h?FZcUND<[??#j0>p/R12g+S4:39_>Oh7]s@k<4!$Zi!.+ %:0=;MQmIEq/>-k^g.Zk/X*4kSPNH\;I!q&oi:CVM%)R)p2LZB4,fUI:SGVMDkTq<,?r09;C;BY\!ZBt)C)9p` %/=]6Ol!JH@!'q9n/c`]o5huEL7fZN4&BqNX"/psO;PsMuWF^`%Z+ %q0>R9f_'EbaH)cg/gQR[I@Ll8F(]NbKoBZ>hshjhW1fbhMdV20]TB@k4[#;!_!HFW&SWgbK/%]3d#3p:2'*2Q5%e>u=D\e!VG!T0 %NY1$SUe9<:`"=6'1$N@CpJTI;7HB(,aPN,A5X:Qe!"j51LgG#PPOZ-sA9=%`'UL4(X[;W0=u2qs'Y.&Z`'ul8C_ %WJtId2S]&d)[[VZGDCGIJMpPU7ZmgQYD,5 %%'2N-_1,^,Z3]GOP5`?GkiEKT/Fi58DM``[KJj.lbMlS;LuUSTk$$CKQ1!iEm9CsB_.qPC,oM5r4\l.8dOXhr"o %N7!uECc0Ep"PVR+>8qCj#e+(DgocFf/i/pjX?^Yd/^T=3PLietEB8WDaS6e6Vsr2*>n;se:<*j;jQLU*l'$bQcJa%%Z$iFXjF]>NUQ`k^Gg^np++C#>LJ&-* %.,jNQUS$^>Dmd*__*+i(3eiu)btd2ZcETGO.;Li@Mp$VA$?Ol]cRpXp]TuBP,9ot==F'P=_Z(<[.S?bY-O1Ap-RW= %O>2PH"M$sahN5=lVX5:LDB(Rl.*.mn]Eu[6DOt.bI+*qo<_YMaK_76M43a/.]f"QuC$M4M4NH2b9_KHu*C:?1pQ>8p$pO:.]?LSb %dbm*WGFc4r)<1Q&*>Pi.>l6)@8\XU %b%:Zm:hW+$':j+]cGuhfJ4ACb5ebBS;u";+;pT`CMM6BmeI&Vt.Dq6Z;Tm(i).(."$S^&T%%CfCEd@-%b]btBc(iAb_!Ip.dBBg4 %.TK8I\ElX`RknL"$^DEc$%Y.;7lS38PeGDQFGEOG+iif4EiSp\M(Z&VDg"$gl59I_/j?Vug#?]4<&#PfdrBgoakoSPgGTB"NY%OeJ6.d"3'[n-Y-`Y4p'^V'[@MA[d9`073.!6X@&'f %o`sZ"6uB*#3;+h^^8eN[a@LV@3(BE&7?S354@RP:P<(GSP0;rkH:bCgpKDh!H>pG_R>j2o]+`6!e@@5Umjl@J[Egi.7;dQ,nad\5 %$X_3,0=L%4`]WlMYZAEh_(U!!3sCmJ`*;'NZ0AA=+ojun`4#5XQCg5F/t^,Y1FVlIe5>0^osKLM?pW'fkH0>bq.bn9=//"5bRbkp %Z"[\%)Xo!/N[6N;k)69:M;JZU(95ZH87el`[s4/h0=&loAOV[pH(1p_G$2#P3_Fm;;rFqkRu`@Wb837TF/L9@E,0,kP_:jc"B2h. %(`>LJ(H7.m.CLb'Bp*hO-`J\3?H_7s',/<1SID(sZFM[[@6c/#(#RF`%p=!I@Xam;^FA[!12>=u:g0LRNR+Nr)N!-0B2.l\02/js %4EY,f\C+Vm=K8F)\F03j]Ll(ejXuW7>U(R8.kka>EBU[nV$lmI2)X+si.FH2Z#afGJ5aV86KllehFrpimjP%XlbKha]c[iFT/BqZ_CE%.n]IjR^R>hb)9SE'jgf!a[0rD3F(N3-AUWq*UCrL$0q(k.;7Da^=]#^AlH-S6.deP]p;p`iPu*B$q6L61RXI8Q^_RQ;B3NKVMjRgXR2ociSTNG*@nMT6$XYoCpteH0:*6Pb^-iXAK2oH0-/lsXAWh_DIH1.So0Z2Z02$/+XVSoj'\kOQ!N*ec@G"Z\\k/? %,t_7BE".hC5r+t#_7`JR)cORl*QZsUL2S@(a0+p0b;>bZI4f-#%b$aX]PWs6B#Uh%X'l[n=r7"bY#-1.[k@6R?,RZq`Si5tfL]L` %=qdfS,P>8X/.o!dEe;o[kQlOPU,FLh`6p,D>SkI.@=_e=G6%u.1I.Js]J-H1^pY;)gS;P2"GMYg,ANs;7-OuPOqSQBrDFk:m\AAb %TcZT_p>g_-*G:a0F?4(UGoTK`b!UU^mF5'hPF6e+)PC=[3>:afB^<3uLNt[oaZ-#$Pj1Ub#*ChWp/^-VKg1D$s$jZpR4"W`F@rC1 %/NiFM4iFkb(;@`t^/]Eol_igFG-Pu=g"p=V^1F3(Li)/2dLV9Y_cdof'_6IhPqH9]0gn`@>i[[!>pkZJ0H@l2E4c&L=G8UHL"MSW %$UsX!Og(HYKs0K`HB:Bsk%]4jIQhQA0"7oD\lXr0c"&oIBK*)2_jj0*h+LE2ZIjL_e"&Id %WNW:5't/W9l4rCY:0e&[,TqhDW/puk&mU1Va:EF>Ofq*h`AsLCX;q!q.^Pp*l"P%VMd'#E*,Q"HV=m+=Y7c_0k<]$?sAHI\<8\!/3IF6)uS/lRn5%&XUUlAcip8qI;:#SID/J1;+#GD,[B3'ZVJ %=2LJDMbPo??."0FBmeg(Xu&M\mR9M#YlbG\Sm8`A7XI;(LIr^B,K2X(DbAJj*DFbnojnU[D:ZP!r>@bL*)$\hB57a5C)RRdmoBPX %@?e.+:!P7 %R4,@,.9IRI_a,?ILVe4)$X4g8o;ER,LXo2bbj)j-I,`E9*TWPae$3\9E/Z0S/XO<.MUAku=Bc0//ISoH#\T&l8NHB='1IrUd^U"= %Fn0d['-2j4YC,(2WiKO=3hOArEPt0FF*?29ACjkd>mHGXWq``\`HQG/i`/6M"mE@iZTS<%"uJ)np<$L@#4L*)#':Z4^6[h(>gf$@ %)p#=N30.AMPe^W0qiqDPcsIfq=Zicl+nO]NLYqE^ijC:aobK/1A6(YX_K<@n;c;CbQQ>RP=u^)`E7)6ieZu^FQR!m->:7CX5Ar`aOM6h,3%umG5*"P_(O21'u9n!j8,']'*,#U5,7tk#" %E(PI@es4?.s,ou66Te`1<..E!-:ANCI.40Q&C>G4QnNZ,!b:\ %a2g:?,!Gg]$kZ="obL,oW,2sN@P2t;AsNO)2MSe%WB_/lfm8Ln4sH,ALl4A`XFEdd42guMeD_=>DB\?`il*8;#qb_;(ouL1QYQ(%*ggi-D)Ir$FTJS2 %GW'l_7^iB^N?NV''k#!\?+r+DdA>fZ"%PIuk.NpXaWFWKh[*Z?/G/_DYk5sYb=^+Oc7VR<1E-0Ylu&o5g27rMW0D2Z/G %n[C+L[`..4ptHSaqq^84d4@.)./F$hkAZ!]>+]a#IY"XFRmaD/@eeD^W/F;*lu[!:ljBmtAgYb_8?CcNVU/!C9&t9J*5Hek='n/! %h&9\`JI:!l5r.h=eR:-s029gE!0%nPO,'rc*co,a3+hCNYAaXBLUY0>^O]*]c`p(3t>.4%8.8OJYJ>RjiF%a)"ZnusX2Q1K'M/M`(ZUO*F`]!\3Cf.`n& %$bCZo'=uGLj\AL1FTRRf>M.^3".M6Q-.XrYXg)eDQaMb7/E:Oc:TWU5hcD'4(VBUJ48l>Yr$GabmYKMX(Hee.DrU$IN7agjLD;gK %i/#'mXe_XgL.E0,M\e[h:r,IE$BZXI%&&d;Z"]E(JgFPHYCX_3NK(0Gcu.HV:4Ym9PW#ATg%VdlPOfBFC]PM%__Yk55YL$#sd<0Q(bJPt;O`HTae)9lbO4le`YHKRl9!_mrA(L/>%)]IXi\0qd(c>--@m_YVTiN,pj %'1.rj@6=+QTV?C_jO?N40FZ0gS>%Sdpk+f!96\Gl@6SqeMKFUn!&$EsIq892>kbgnMH4t^PMN5;HJIF[F'E.FRM1I>=$(ChW_+>k %OD<%M-e\EJEH]-Z:M='")N]()1,eF=6LE%H$GbDcbX*YWU27QdL$VT,)Tc&4k>rLIJ;^CO00BmOkM.nOZcpF5N4 %!nD+dWOlW'F]Susn$Z!`\oFg:C3QH6;Q7PXcQ3+Bf3bmPhcQ[fsP$pVV< %FDqYPgJ_I;^Jf8\21;lWnjSgGf.A*FGfr?78l@rBrOFQ-0e:0%>Zehc31VVI:.7E054jS+T&QKT"VpVX7=A&>\W-Nh3Y!W7d,oN! %%2NqY5[20\-2b_n8;kYrPUdd_Kcm=1dEaoRgPMYZ$L]hd51mY:nqK-IW/O%>n7g'mA;l"to2%('-6)[bO?q-)\b`JemQ<*$snFX\`[ilH5L(Kg4PV&S=/Sp^b1\D?9I8.cE#,i4WPa*Jd %q>o59"K3uq6`VTLX;R%_P!$!q3\b6pQ_L3r,U]WU3EL?V5ej.*K"QLPks62:^(f\l5c;I/=OTlII7M_rc#aZLF:\k\h^%c"PG`=0 %,^scuN>GlK/+N3a9(1`Z^3m!rB8ui/7o7Hm^FflrV>j-H)?4k,FE/sX1ST1&f-!0kW@Q&S#`jI[0cY=:1>l?2"b\HeeqVsJAOmkO6.e"W&]\*ZKgD.<2%?m^fYZa'5^/B7Lsq&k1>79p,HSVAe'R*S_GrE!\Rt/m`_I^J^N2fM.o3SU %43t@8F\>`:iBmAZr9Er0Qa4%=:F"?h`9MC>"971ht9JnmKr?LWGWYNaT!U/?M4HNKdL %jf&sWs/\Ur;mB%)CKSK`qPf5HgTpp(NUnXPfdgO:Cj[+qdYG]>V:+C[=p:)K?nbP.15sHOD2?f]-e+q/5T:oI"\'\G*?neqG49*> %L:Jmi&`KZ4[AcP4Rn6sDdVK;NJXN#WFD-o\_F]@D_6>'m)H(ZQG)*?P7,;3"d7Pq+k9(0UZL!Bc2V^"?KFMlK#'K,S+44 %g^)Y9#`CY#``Emp.uIc#]lkTH+nO7m_c@G3-JDeg:Y+M:ndu-fmsrfc1nBI]#XM"XVfT6P0'EbPg\<$d]3%5V@dHZ8KDB;r"K*:= %MkUeEj0p6_FHKUV]G`'H@$lauDEQP4%(d7AXM@gb-P^kRmhisrZrbYKKud'aRW^2DUYT_>XXI3JCYGmsR"(Q!9pQ&C/#?N#R?s+N %r`g=Ab.(?*ZoauN[S5A[>q'i"S3[_-Q`MZ]eK:]2M@>]5C8DagSlN.3*9f@Ye-=2gd:X2@2@^rHe6-G4Uhu&BA@GC^"pQGPc]W>" %ZT?CmIh0m=Ja62%Sn8SRBJV=H5G,qA&WBY-pJH\f@H^ntV][Z#MOu#_5NM_%]I1q]_%Jk,Jp2:8]lORn!O3kGUlXh%i37a*%P`J8 %hd!Fh9i?b5GU977+0QX9l9RZ-P"J842K1cdbsjh'g9H71i]A(dS*Vh.:rG=Z:;nr"+b;OV+91e2&nhb=A0/D1DE(V6C[3`j@jJ?TG]kq/V[:ATWJPu25@]k-p23N7ES-0RTnpF@f %>a*n2Krd@u]a@-V2^WQ.jdC/e;JTPNS;I-6N5f%D@_m[U\F'R!h4P$P2-B;kF*a=ckSh+76rfFPctq8eXj1q29b]I!9$WX*X4A88 %cS^=rIJpSFfr8u$,Ykf\/>FB*0iDrm$Z3+1O:7;71M>,+,[-YsEs?o:!)64Bm#5"[N,"TgQ!0m931%4t^XRRs"s;=PAZEdG@iS3q %X!o&NcD1)4hWIkO:=U,hG47?JCr!NeM] %@n/ndghIt`Ta)SdDRD&D[jfcmesWBpI[V*JP+bGS1ei?U8ZoCm=&mMFkZ2\u#M#h(=+b6&Q)g*m33eq&>eoef%5(7d=HNlW:M6W+ %<]3gn6,+c5f6KGX$uMo9^ENAr$#>lM&p$]!Z,?taJF0oNgeL4h9*91CGK(seM`0#;V6"7.pGh$#GOsE!gT/t'1gX->CW3E*FgP[$ %F_M4bM`]^FPqoHXkDuFH1*7p4QOE!1M4'K`_o:#a(IP^t/+7G*^7$7H8pHU:^/B[J\`H>$Zh?`b7'#ATo2"`P@pOW7 %$-9^[nV]L^cqW6(_8POBO'?0n6OQ]KISXGg1Llg9 %UFj!g_TQ0XaR):Sfc1m.dD*(H,Tqf.Ns50`81>M>5Mq(JK?RnT@P1IB4[1k\=peA7cNY@cS9t3QN':8Jg\3l8iQhX:bK6WPk[2JIu! %QXmY3LINsiV/Aq]Aal)R"=K]C-Wg^7&t_F)A2-a;QVYjV,#4l40)MH^*a/fp-[4WZa;G?r*e,WmbSp+Gr4iDqF4ihi'b0NI\W!O, %Q&1,ECnTWBD3^$0hEsQ?GIB>Zm(&?\+b3iBf3K#tY(>mXZZcql'l&]=`2C6$L8WWe^a+T2#=,@% %QQ@@!)p1WVKfF/jKqWFD2k:l26hIoJPU@gK?lV+M3BZ^W0p.&4ep+>=W>-QNB(TM2YTTU1C8 %Occ"BA3lR`@J0ekO5P*88-B>kB>["SgoZ^h@,:jCJmgu(S!!8@1Oi8JO6^+3$g&sqX)PpT-qgfRV[V?KN#4AW6N3!!nuG^aLH(ZA %AgP&;g(g>oTl,(KDeG'AC>Dn]NbXc-'#q`q=$m[S\qiWEi`-YGSd+_")oSejZ'5CC//>7<:_a*_S)Pc^9c?d(BAn_+`d/9a-c(BI %gJEJ:AJ8I->X0J'QBXGI+&_a)%E7'@CPm([$p7L)J=D`.\PqiD[C<'pX=7AFA*5Em75VEgM@s_R_1(B\-\cORIaO"SK^C%"+5(sm %aH)`Aqi-C/]Ak%t#:]0M\l=%)Hj$7H/^[k@O^OG"m4?6TCJQHjB3'eh%n@!Q\7R5m`XkHSNii]D %9T'YU3WW04m!3h-jAnUY%O0sVkMmJt)MhH[R/aAlP,oXlQ\,9SA0pGCke(6C`[:\PCr<*QKpjh7Tb6nqX>@g?!uD_'9CPrF^9f7O %7+N/A#&gC?iIj1g,cK&3^)i\N-OA(L_V">"W/kW5gDWadeSpVr%GU2T9B;6lkF'+]sWP=$G %7q6#=b5#O+GUn`9=b4k&@N12k\VXkG*4jWkjGYpNX`Y0%nTF43C@X1je-aX"HX.X+kMsa;9jXcPlCE^kfLDBA]`Q@h@np4G8]eoh %:)K6AA*Sl$(lN..k_EJA0Y+l27@sAcqo?Nhr-SZH9IZ0l.S5slm>%!U&'*C084E\NO6RRrdQK_O;&a\c28#K-o %Ct3l^ibTjt-*^SXX0m__VDF$RPQ4h!+Vp<[lSO`G!3R[4',Mga9L_L4/_M;LoFY<9Mi?=L8NiE3Xm]b%lWWCmb*O!b;9%8>E:/j> %b!M@a5QQM*$,@A]'U7tMT1'tdq&U#,.*EbZ6:,OfGH7^GlLl,E97>@af0DtIo9==a&g.ki.$:T0rf0kqe@*tKP,d!6kIN\'TqcD6 %+kOu(!3S0gN'_As536Mh$DGNQj/9g_2dR_?2K9fD07/gC;UuedM6WRXq=ReIlW/8(`B%MD5$1PhYK-`WA4fTGH`4"_h@=k2*O@Vp %CD]1QTdqS06C$=Fe__::G>dC6/VtQ*AkEgnD(>*J[b$^pS)R*k[SV-;$EH\kd^VWSS42k#.Xk&IHXJdafg;cA7aIKGY`WAN#$U?S=A%Eb![hmmFq!'R2uD0SV?OFGr+(j4:#;PiBX?]T>tTdX+T?4H6JZ25tEf7`'W@$@`bIF!eeHS7t%"*3Bg6i1Q@5-\qMT%,#U# %oHVC8"Mt`a&48QbR7Z+))GkdR#6Mbsf0DYWOB"/]%+E'C[F^n?SX %@%C$TRV9PnSlh$&@&a?.%9REkj\2f-6J>7JZtPgnb?o&Dbi%@_o8a*EI-cAPB&,ncKoX$WDpWuW$2`een.Mf([".E %D5e;IN(O^@AnT;Dq+Y49&=X,PZa"NGfKN&Fs$jkSqA'!p#fN/.h4;.I:Yb1>Eho84Rnq*=F(in&DcPGYDRi9^!9U`OE]Z`pdHNiZ %FNqMOco[l-Y`W%*[ASdZ7>84Pn)\9EJuF]8PF1%Rcor:VHUmL4C2df&>+<2jXmYe(lQXgTkY:NNl^)hl'(c"ec0srg,rq\3__r@h %e"C]'&6'I^6b/r)2uFU$qlq[L;(0]n]YtcoEK%K1&sY;uirN>H+X@pLOrjD/,CW3F;R0_eL.`sb67sgo8-q^Y,_SQFj81i*j<8d- %=C7S_0\E0J)btTh8V6WT.2Ku:-KlHP1nAlLFaEgbAr %CqEtK5ffDVJAZ8$U!2e4Ru7Icaln8dK99Rgnch#HT(L()4l-:'5gAi=_-hEW"N/ai?/*5HeNdE\S@dcbPf$^i_qS(6>7,:AiGC#S %A"T(b;hS;Fq_!O!QZ,LkDdUEbof3ed2u/^cbLq!VtP/N/e"%8Fu+qu<>OY;qR'7U%< %O)SkNA0!\;q;#NST9=D/8H9S5c_f*9adhf%m`ZRcCOJ+.GtkU!E;#AuW6Hk9:*g'!.J.A2[$`ep"3j(X$2!2Hb_]r&M7q'qH@(HE %aak[?GXN\SRM>'AKftsX--ke5$.aO75VY0.Pan#f6VuXflDb`).p/cJqLjcEQ`LYY:(Fm9P[fl_G.4_G. %a6<'mVB;7sKR*R10;>U:)&r_<4'/)B;>a/3Y:VA_;;K;Jt#mpF'_`Bk4V9b1@FHY'V&!)i",MZ0H6L42"^NjAgR#W\PS6&I+&jI\*j\UbjESY_.?Ft?H9a7XY %jQTBAHsPgo*B$r^naR[F"q+bnhD5haJZ<\:#&CN6/23?WoN9sARsct>A*n(j=r!P\$&)t9cIfu%Z=PSU[p="$l\o+hM1\kVFQ,^g %kOh!uZ@N\0_B]UN3P2qRF*&,Xp.[N0>/,">?udSJ,,ER[2Gb(o@H/J^A$bTqN=/=#C"Onb::7HW(_@Fa[Bgu70fDAi[O^2a4T7D5 %GW_$$S/an=$p/FE<&u67.lqH^b$!l6VX=09fG3?k^.ChC%o5.[YBUPfKn2IlM+E&H;EMV%i&bkk\#0&\knDh;HNT6>Ou%=j4a,4a %n1b-p8-O)DZ,R_UI*bP5\g`un9ZQsJ<@an,.FL_K2N!dI"(a]51k2m-m,+<6Z(^g;QiV3$Cu/i#Y9a/,L2nAV^??RabPlFm/+iLt %h6C2='pn9JBSX*VF'[Z$-)[*Qlk(%PP(efeE,I\C9=h?#SD>UMNFb$$e;s'^SV8\;OV7DM%4B`b#5tZ=Dcq-C45)6aPTEIYnQkeb8qkt%S9HYoc4*\`7Z`J4A'.F"&S>/og,6n[l^].>6V< %Mp>V%qd.)P$A>:J1*VpR]BVG)=-C`%]&k9_9iICSLTWDDl3m'1RD+!glDo]m*dB0$S_#DE38un.6H6M!OAf(IRHanO4-SqletR%^ %65:a(&J_11jQ)DmT##(WS=,.7+d[geW"r"n<70M^R\N'pG&P#g+b2-1]d"GbF]5DI %-TZst)-DSaFl;HI#u20:h29YQ'X9!oV$E%f4hP3(O^m]Z,"$YDLlD#rn`noVs4:`'F6)M#Z"A2]%eVq %bb#mg@<;@#kI>"c_K$0(ABq>f<]W!M>GHAW?]B&3ql59,>9Lk0b@[F<8\O#X-k%nn-AZ/lsf$RdB9@a-7fDq;D/n8"u\]Pmc2d-O1MqQU)E.U6l-@gsI%d"'[^% %OTQ9:gj8-7i+^dCC=aSCCIrh!Shn[(c7EC=/Pp9m;nE144U>&G@'tlkIgT;[5h4;WMYJp6CYOM@f'F/-6g&ENLeDdn",6]d!Xq]B %6at7s,;hY0*RB;\JhrEp4u(kK3LqEF$2NgBJO4F8,RdNi%C*1S&3La].lIe_-Girn%XK$kLL92&]6"g0-T^;MJ`eh\ %\F(ZG\?RD6FZ:IbAXjG*"-OT9)AKXtQ-?b>GU[9\`arG@o-9Lio]KkJM83I;o0UuJsnS#ji %gZ`a8LI2:,OSYalYM55qrDn"gi;uoQe?F0op!Hk)&dZ+P$\!^K.7\^8MY%2LbQ,c8Fk-+a:o:mu%VeAWDM\f)1Y;Q-@`QTRiMJ,G %S@l"o0r)t'nG[Kn3WJF5]A'CNiUbmOSG=*.e>I1S*%]E=q+,@tTaRRJS5*\e.:mZ7gcGSOG6kRY.:`KQ]&]$S,Xe/[:JHJ_nNG\i %!M<#0nVe6Er#/1oH[%A2+O)*mVP@fBg!;5/f8)Ys8%Lc4hp*Z%cahFnEG>nVN%t1]ZHa`7./hF##r,n@m*1o_ho:"Q^te_0CiofaLmlj" %\5X+T"On^]Kk.j#@V\FnZ=_"s?lGG\[dr2K91'Ftl&[='69^NH7Bo;%LE(+e))OhA:rL4t[uf't$R6atLP8'oN,ldDd4J-28pf7? %C:iWjpO4WJVB:G>n=UoSqGV?7S:;3>KY7rn]ruO_9rWO.ge*]A@^cFj4fl1+(0h(l9!N(M9[j:/Xah(0Jt5.d>#h1:/&.!u7f. %JhXDV([?aFV`C^r4&+>D"ATHF-0,&gs2,D>4fm`d)EpMu@>2KTQp?m&4H0h/bnfHJJis91#hE#[]5I\8XFNGp^fab?,;:9a7KZRG %"6jN.mEL6S0u+^3kS^b\q9\0nEqVMP[mJ.o5*p7_ZBbV$-J'R9C.YPi"fYZ<.RU)g.[;ImM*deT9i6)5T$/ds`g6k14-QN2P^rGm %!P\6KS1lM"kt*&Io[gUkCs_D991a'=\Z;k`F#A_Y9%8407_o'(rmUhD]DqbCHFSCtkF]<[/_0Y$F7]ZXoEKC/#&*R^D=b1MhNl%5;Rd.tk^3m&&pU41\=TbY_T0+r$1jpa^-Oor %nN_"Tcl9B>48@Zu_V7t5>7,kp:lk+^2nsWar9F+Wn^IHsGc1Mr`PDQeNdq)_uKr1Dc%K)T*n %^rq,qc"Cd^[d7@MTXXq7rqufS_nTG6=22KG_uD0ghUCZfnltA%#Pa;O'N`&4^SF>FT>'?d*q@CRc1gkXQ_\*nV@#2+XAg.d<=?LM %-r+YfnF0=P_pQls?Z(9r.r?do!Zk?>POreJLb`[RGTYdjlN`$2GE#j*5Ue(b4D5*aS`./uHV(E_fQ5QJEk#k>lY`X_*hN?LQ=/rA %.m"12C[q@"TQ[`VDYk$[+/-mIZn:-eTCr+b!VOnGg]!`F4_`l[9;P0BrL8q%5;4j?K %HL0U93\9dW%uZBO"-Y]R[5,&[^97Ctj838doeA9j:e`tD1b:J9%E %;8YO[1pQJP>G,e6YV)SIFkr,RV\r3V#JKOD\5r%^Wp9jiP-;?I/e$06IoC";oum>\!:im'bH]%\4ie`;^?TK>aWOj8at"%jO]E.0DdbL4 %/#t+)EQNfk-^Y]gsL5C+Q]%,6"\@,V8:Mg3ceDlhf)4ibsr1:p)X^F(EV;=RLLS'-.GbmbX;\eHBX9Mu2*1kq$Z2f^W %C`u"*91'%V`<]ZZqV"t(+X91Jmg&Y]+#mRaj#%,;jcs']m6]Hi/#a$tgpaSTTV`3c1JlF.[Nd>s %)TSej`ge5U6CQ?V,qWHkYQj:sN`6,2_37M51aI>AHJdoM8ppXo"R^`Ck]*&;B4lt3J-:.JfR-<[pfUhe/mgKdlL6do9!3Dm4An^q(@duJHA.gYIiH_A,@%*'j*t]B^1<;\-QH:L3`otdk>q^tga/*F)&nM7A6Ogpfr.,,f;DAPj;C+ODj?)8m8erCa %SZ^3H=>H[\XJ:N)p;d.&mI6SuP2;0`l%S#*Q%+m4&at?cQRoRA_]O#G@RFu9f7l\-Phrls#g!Os,1&g^hb`jt*e_JUT)2QbRrH`& %0EP#q.]/qN:>D(\b(buW%bDl3PD,kD794fnFoEH?@/7-l`Ro'mVBC:lT#$aL7#A,iL74O*Lm03=0"[I=WeG`:ZYH0#g#9;=Z;flq %hC?s$;C!C.jZ>l3=A;]k=Q'Ui@'EjG,J$d]3%u*rLp\6U"CO<1rurB`.m.,h(\8P:kAW6j;N_T[m*5ZHE5XdejFBjo2f]PX8+iN&!PRl>.-Rk.(Z'th'@[5&A$+euhU\nTFf3_HPCIZ'6Yf4g6g^nCY.okJ%/h&G!?js] %V4:@n3MA3I6GbJ!-d#5Y,,-^0X)oK%Zs:H`8OJmtWXD]#nBR53 %K\M$kCn>Ce`/mCZpH` %*I9e87#*sXc3Vgk\$5)US`VpVUX/Ta;6'4^Fi$eZ0iCu^UanSsV>!F?.4RJMNZ.#JkVFO7neaImF/nA(@u6SM64_G8?Va:):f*!S %:kcj$1s-B!c5*@Y:J1juuB`XjH`r",5Tm7H)-56_tceI4A>*!IP@8Ck!iO]D(h\7>.Uo<'/"UBi\6%X3K\.Jj0UYOsQd(<_.g[3UR %`m:GO0(!Q.>'544:4:PsP/oq0EcV/<\]HLn[uDoRoog[QMML8]FNm0oUn;[pjKB034+/Vbs7pZmhBf%Dd@HtRI7OT8ee2A-=>.=, %]rsOoE1V-c\J0oo,K)`g3T6&ljaXn,^LoLUn^B?O[Uq3c5-9LWW*OVlnuZE6p:nP?8E&bYg-\S>HXB4SF8bReV,9KODFa:\#`rDr686IbN4]ri,(/$9f`J752oeo#$c%*]Xs`E %^?0gMPC9tun8mK$<+f8?['k9E)[0hncn\l1;m&_7>aukfX-'$6SWun_LPkVrfgWT-j;C@\#'SY9mGtegT*eWNKP$!iXs*M>(XXg-O6/ %BsWZmaeCUrS=V(c0M1G!r%ueEi%[-V*+)K&[]]XNf'NkCW_[+^O68LX%hms=3&d8nVPEbUM'H<*DXAn>K_UAZPHWL6\,??@Y;o(Ss,9FLgBpSOKofTokok0]eYui#!:UJ_sIEg[\I^j(j:d&3)5Y. %7Ber^hl0+ELF4pgKM_^87j(ndr>B)(C$#?FodS7sj;@";+=s-1BeO(2)>pf6d3A6]4[P'80DqtC1%=e)GaLG'H,0YC57AX7V@iRR %#p&D%nrOBsYj %Z$-"VL?9,n[,r"GI'C@[VX52;pG\E.0V-OUp$Yr.Yfc*.ppo[O8)2Vk7`pbZD$%"#>>n&78" %F_X:;m<#C-0J,P8r5S@h)+Q('kqop!nA!2-V\s%0^VN*3N[F_.B!$3(?@8@pVUdVam:bNt2p'u\5gRmU!1+h2gqOcXpV[XLa@#e.(T@.B %Mt1c"F4_RT?;-q?@@IDpXt,aBqBe=6qGu4(B\Ukj^`9-@K$"d:0a1s4f:.!Hc-jZ/2Q'^p/`]!W1[`f^?khPN3)F@+^CCh-bQe\C %7(r=):6(FX:%C>ba,0c>HI.Uc9cpt=SU9grTT?/RrLouQIB@.o=/"FkpS"?K7p=2(LZ?'i2P2\,Y9S&^KWEY)cM(;se!)qO&b$Mf %c[)r(phD+bO6DNk"k5kG,q%aG^5N3aNhMArUhnlY3g1ZE)=s@/XJa6(,.RV2cU?e@r(!f9KZ %H1-F5e?0eWGqZ>.V^'2?B^BmOUY,3grA"IO0`PlEV"kMBBqC>qaiut %rjFAt"Z*'qVU%!eDNHFGg9i>$HSP*AU6%D2CXM9I(*2:>OOH %B\][1p+uFs@OV?Za&3%.SJ#mFI;/!4HL%b%lC4r@U#oukmb32=G96SY@6QVX?bQjmB99ll9tgINQ#Eji(u[@JChZB3:,An;RSgcI %N8g6r1\t_2c#RE\6VdeJ:Ps&(e3>;/C2W<<#=7ZNf@(UhL=`SWPJ`<-Xq"(/)a1>c %QW1TYE!*1s54mrA?K35K%sF5(C#XZAQ>3^c\L(G=Lms32/r7cY[M4O==6O<>NfSNk:+`1'gfm7q3OXEU3CRT.G3`BW:a^@!7C]FC %&FTprlI'X!X'2?WoW9"eH`V\2Y&k_5GR(CA"E]B][,)6uLB_sZ@T2alIaDNbg(baluQE %?b<`Eq]h1a.#?O^<"IsW]qHTj)^"sNH0agT'+9#&BVH*Po5Y^(e,J[mOlt34=*M(l:*B-7jn01;+MPpii:UD["pM2k0g_Ua"u._. %B+WP_FR]*#Rq&))oWktK(B9SKJ-9[#.,?aJ:_j/hVf2A&g[[;mSUJ0b3(BV4a!GLeIolr>cO'PY3LYNaKG'F$J7'Snfe(C/G_Cp^ %!MAFq.VDGN*^-[B=WkukF-J^P!TlI.oV3e;IR/N"dMiLK`mo"$[q@5Sf?ohO%X@+0=1G!h?udY7teK4^.aLCV,,>&Q*d.Z %1j6D3UeQQsj3u28(NK%sche9aDFlFGa"=?'H$0+A;V+gdehP1>]:S]4QBc(4Z6cVupZPoKTgV>p %*h%PU<(Tj %qtdQ5en9Rd6ZPXe2r=u,(?l]t0YTg.NPYZef9`b/?(9Q:rTCN(VDZcnRVCRdlfqp3mH^<<@3O.k.4g.,#RROp/OZ>FArO[G;Z3^2 %jaAmVM"4)+#uMbc-r1&3-_J6A;g@jpp_bojs%Em@o6ACZq'/kD)tL3#gt2bg/1@,,*Hd',E!flZ]2e]E<9ZK:=Qk%E'l%-$`Oq?+ %4sFoUOcL.p=\HgRkKqsuf\FrZmblYacn(o_f,qDgaMS@%B"PI=D:E1/be"2((Si-Bir^'f].'G9G7Auba)bTXGr1DZ:71uBIdaW@ %qIu+?Gtuf3D.dH/9]=6DY[gM;c50FCo4"?R_2Gsn(2bNh&ZK<3WJ)gM-dj?H2i,FECHem).\LC6r#FobKft3DV:7uUbR#UaUNO^ %6S\rVLV*=@(oDI9k,49]5)q@8?+;tp]cVo3pNS'+\+?lu:A!\#SR&V[\8a5OOeEG@()].fY^Pk_%"@7c`8#;M6;;lg_L[fJCZG,j %_\#h0U[X)rN:8Y(cXSfCP:(SGjGj.5Vmp.ekh9NGbF#kmlT(6)ehPdYSjP!_UX<__*5>[C^GA'_1p>#MrS96o=5(:uT,&s:qT@N4 %1l+W^B_5-W1\RC/h0D&0F`O3N\Tpe2:"on?h/(/Z1%l@FHoW4I,!^;6jHp`t'+dK4m1"5Q)h7jcHqhW)d %[Abl61U%>24C/P.3_Q5N>(,/3GBD?',1cao>IM8bPo8QT]]^mjER":G=LJgs*>>F`2@`/#dOVD47/"T0lrY1TDaL-+1LYJjl^X)6 %(0YK!S1.O4kpE+C[6IlRMfcBtX.m)I8D&bN2%k(A<;ZI_=Nus3&W>I9ho"rgNp8HOY>1he`t/_ZAY)IhZd#4';CFm(2D"\=MPd(j#$G8&$:9QO84!#_LrX3`:Qq]U1`ts#nrAubfGdZ5.WeHCojrPFu!fcJf%WsTccgjfCqb\%lDd" %c"si$9SSs*gWeNnW=Mf'Hs8n-=+$B:jumD1TO)fF[^D+LhC+=i,k[';8NaaYmr+;TdJrJAf1f9VJp`_dZWN].hQ$e#@gZJ>Ibu?XN2kbu-XPu\B*rB9T_@c1;8N8*gCmGk&af(U["F'dET8f)7LF@eA38H3?GE+'<^(l[ON[T/[kBVpXH&,NeQ^4Bm %JanFh"7*\Om0gZn#O42-HcG,N4Mu^^9q%RKe>##p@h.Mn-4KaQ3VD;CR9aU'kH4@!rB(R&eAR4iY3;ml\A!)6]JCZ@boXXN2e3he %kNUfg`l$f(SO0[3ke!G"'(2[5GE@'*r++DAW07:r%W!1#j7JIl`U&[m,(Z=PI_Cmcq$ch&aMBMuD?M7'YGR %ZL=fGPW/U52LSh3amlT^"ApSu,VS[@r%85$YQ%_YA"_Dahj7"5:cF`ObL7!55`bB>IkFLKPncO?2 %DMGd[P9TX)&?3p9o`bl94I]?:5S4HTO54W[l\.q0h\,Jn_e>;uDn5:Pt@-r=O;%g`FaD5PNQF])MckkTn*VpDp4t_\;F.5,'BlA*d7.`/?T,E.kqX3kD7SSooZS,+X^=tFn:WUe4CDLa4ZaaoK<)K]t$%R8^ %2)ho6NOFIfI&e!to!=CXE#(F>5^HH_`_d]\H(4p(7l&?6eQ+MLC$b?qa,JOWjYP\e@%$*$+I"40ocha(i-o&)KWhXT^eE;oqP3eo8Q'R(Pr$'0]dS %=h[6=TFHaUd>]5h!RWh-U"cJu;tjR6;Ek=V4gR;B+SlN!"R1G\f?gc5GMcRcg*6H-6_C8ZrY;o>FYKq\QXs\3H(=Uf7i$7VDiEei %K-tufH#oPP"$mb_Xh3J83c#bp=.Cl>ZfK#,oBAC6;V<6"n#?]`>^nP]2SuN"3gjN&5U50kD"BjhLW)<80Rl[h%-$VBfY+Aa80_3O %]$!*5rp-.&EXR0(mUBDcW.&XH+'KqY\S#sd.MT-nnXI,t%cmI?8)@9:VLErISEM6?Mk#&q=AIddr\c=-5FgD$bC#f![9pC%=;-Rq %35<6&\*nh2,+:BspK:H(_W7"UK;q[YQ!#a.MDY`j]n;G1]r"6bO=&89'u6nVO#,0X@B@tq&X>'aO]?XF:fN23]Qb[Jn;gc)cX'^ %,R'*%2O8s62QReYBh1H3,TWF@(OISW&V=n)BrHI+Xt^Z*PG&>j%0Y`SLm>u+l[ %QC8!q<*V\e/(*8[+G4!%*dTSGI_U8K[LSALj&b8]gfm1JLseL2@'a(m$eXf@KrWX0[mCq\ETRe\KsLOPg'0rHh"B\MMEF7Aah7a\ %H:>KBf[*:@pst3!['X.-g*#lk-).G$H#G+fkHm/VNW2NaCAlQHloWfuLZ5>l.r(p%Vg%2H$YIVDk,JL3C6d%N*LbTDnG&[5!oeem %*t>DP'M=3A+[reW.K9"@2.X]5`?-*JjDaoHi]Y^1ENqsN@'Jfa`1SkW*h?:CTB_rKYQuO:6akMD8\7DZ^\V8okZHn^Q/&N(lW^t9 %V8^mHja49X.E#(_n1J8U,5!hY;B;JQ?Mi/!?"uRHeI_L(=4.7UUCKH_? %@:R$jOKVX&(`j+Eo>I+Gk;7CTVtXjs*A+Cg)>=#2f_M$T*E9U21MUu-l0?KlVka(BJ[!Vt3TJ.irI!m[qY]-eQS,rEo'J-`H&[;rl_iK^`dOiD[&+j\h!HF3r$%hInbocA**g'994i6a %WS`Q1mjo>]3RMJt:s$,-F1b&LpVUD%l0hndh?\jA@_cCdR^5D1hWCa8FBon@E+!5WjM8omho!>0blcF)DnT_HKVP1[VE$p[B3%QiZ"k6_D@aTBF/A]Ql.=p2O>M"=J_,AmjrAgq*doAe@%]cu2D>4h\5P=JN6F7%"ae=/KlgntMVV\rQF*1f!Zf]f,H6 %a8P#=_fkT]juSN"j#N,sj([+4eu(>GCSF=4Ut/L<*>I0\1:BD;feMg/O+Ce<=@iitM7Q<'ePqeGl'?(@@Pnpm-&U4Yk^+"-dn6OZ %\?Gn_*BZ7bf0.bJPdB18L+1s5h?L--[e>>X[S^r&]$1^jgge:gOE;eXRP0nIhnV0;4H7/*+AEGuF+iI=&E.OkIGYcgA!hWo7PP$J %1pp])dF?NLU8aj"4IOqNbP#K@*fT*B`Vd]Jrj7KYFZ(njVko9UO>a'8@b1nn*,7.ghcEM7M=dWa)^jB"(E_[+T1jL9M/PGs(Y?8+ %D5t,&$aLW;a6k %l0@H8f'"P>GDt;D6[Hddh`2DG0jsZ3dU8)%q9d&X3_)I$$:@pc=ee'j&o4!]oo5UCNT?_`QB!%*)U[:5o)SGC1(Re,;d@9UFn+7R %G:$%"TA1;)R%7l,Ek0G4#N_+;s0F4l4nSbkaBuW2P:'o3PIsZHEGE7k"TE(Nq:M`\=5H#I*hc*UoQ'#kAeKnE7l?Jq:XU69m,@cB %-7KDMD`(+#p-29\(P2)+N0t7`?d4rUiS:j$BT*pp1*3S,, %1BQj:0+h#&IGNKUMLcSOe.t7c8.Otj7QW^=0d>R'L_8sUB2=+m2JVh/OCmN(bpn'^//43m`H#;Np*5a!9$>j9?+ucVYC%k+Zf9Zb %YP\(7))'\T'//'!c-?-io(rN%7)./^pni@5qji=Z[#O,0G)fTC9JbaCO/^]6qjjlD'=sW/A.FWuo\)$KG3*H3%8lf/'t#8RlJnsl %Rd3%46@kRG=+.37';Zr %:g9M\5>4gan?#5nLh@g-c-+W%B&,QiA"J]:Gb!XZa2N:!48V@3hN4$&IbkE7bO%@ZgodN;Xb0@acWUK4],]fQQmEnkMqkD2CRo&% %/95Bcr4KgY&#c?3N\$[R;Gh1q_rbd2pnP0Xin;A+0lM('(-(]C%[;bj3]b1WTCD:Z+`HIFeNWeNkk5B6:WUf?CDLj7F'3(X9T0sJ %7C=EWe^qa63[PW]Z]AmA(-Aj)H<)`>;4Ii9QiZZ2)9pEKG2(ZGWQ,)\'36b_5/TO@QVmUue4?Ds/n@Z(!DJMm[[Mn?^F*?sn0F="`DHT9]247V:rMT\F8&PFZ'pk#J5k8oY\o@BQYRE2j8 %;3cifksXjc;/#=`neYT'0"`*Z>:OT3r0YmFa6KcHXRiTj)OWh(PtojofU;Wc2n!M6e&5Fn`r;!+]HB'4q&S*qc>p>-aN#F^]WR[: %+;4DA$r`^AY@Tc*;g4;7(&q<4PUp'%uDVp"koF]dg0^=Rg-c='EGlN3:5K1JXaRLUjc@HQOMj*P!6>=M%= %8UJBMe;aoQ.-Rn;,Q$5X*m[?k`WCIuPC6X%"D02"T/Z$@LpAHd[jI)"EjUV_r%Vs9;`g5J)74t&t[Zf0N %8c(QmMldK\,0Ef"W?iXWY*/Ujra#cf-nl`RWfpRV^RpRQbp*#_r+#$:7s[q^\]AgVqjLMq,3P9bUuDjF],F=jWAX6QX6*MW\5C8@l*m_#P"YkC-&#?bE)jqX:V^(mV>dL.8@TO4%JBG%O"1+bgn4c\eio$bMoqDEE,LLVEG %>=[K"V?o\':"R^$I22DSXQJ'$#$R>C?sA,g]g(35aV\fqWP=0g'la8f:l0R_ %OMHuS&e]Gp=Srpb]3DftI;NB:K %nCZR!dY30%[(YrDFo,9Ok/9G!\N-pU4cIJZ\RJJr^I#I&!pEWL]rjP3@YGFnDC)0=8k^+fQla-_hM39L+FPf%_PV,,h"GZ^b92?s %iA<$>-,1MXhTI&]B1D@3$md<,R@tlhfGu?WjD;8pXI:ddjAj,\L`qL#L&TQ>C9WFdK$>"^_/q3-@X1:tE8(n"ai?0n^uZ_HRuX"4+(oWf^L9UMS*Xq1fLp?*h`QH"lW %_&"!>PkHhCLn+MBSOZ1>kVJ5T/kpDG.74=9*edh?GUI6Mo!X",\&':M3c#ms(/E24Om+:Z`=).EnU>F%59V?R;\3D]`'q![(!S9" %>t9>F$+7BIrX$:Nictg$L1tZYcYhnNcZJ63h=W4+S9uiAZ'I+UP(_8d%hie+&c],E[aoLjR2db>Z^H%r,EGmffa.OcUjcK=dpefu %imW3rJjqW8GpI&[/E1%2S:)c,GFhN9J%APsgPd(!jC9=LP;-r5ekKf-8;*uq!"au`eHH#g[AZf#\(N8*1qro&QtOLh/9:._C!tIV %e"W9D %#5`!3An'FL$IdVq[V'LnWD3+4r/0fB\.NuS[977ZE$KBbroh/c=L\rTgeXrkX9_NF1+tuCGRPY"5qnPC %0F3D2=mDZ$7NP^fM``e2XDuoF]C(uBMB/H%pLHU4Y"if$OfK-WC':F._]%bMZ(M2DQ)@3K.!T)jAJ.6eSoN$$6rH:W+i;:sG",q: %oYlYMdFCa^di:@D)H]$B,j#ZR3CNAKR4ujNGr@h1\D0^@:]M/h&AUG0JU1s9[L$G#5L8KVDbSesV\PA.AgeqPb^TN1iX)S&CWh<. %'t+giA=D=k4-o*GH+&eg.7J7;>\U7NPdc9p="\>0N$+BbSS`_ %'>-Y!N!-?2&>(L%T-p"K.dCPnRB88@hk/ZudYEU,^9+-TX1_:]lLplZM]2;mBlb4P^.BBl_KqO/Y7#4g2u6'4niQMdZUr]ERRL[^ %W+Jg\m=Q+ZBaqub1%,FdL=,$8MU,G654A8VJ%DeC(mI>4MaXItho"aXm8%r#@GdgrJN1fq;4,Xlp/MYg8H?I`PeU+cni-5U*;"MO %4O'?S;p:iQF[gcW0r8lFh\XVI[$b\F.*h4I]B9h5Xh(EJ\-sPtRpM)S)nn %jJGAY?Z8+(P$?n%/?#XE+4>o[TDtSH4b5GtX!\3;71u,rU,[1T"QPX8:U]R%rS3ML^LG@f6PU@JF`L_EP"X9p(ZP3c1bFPaGu<=Z %P0>UGFp=JdD$@WAJ])b+n\VJGStFV4V*-0CM9"_h0'-!uIbH\3=oS"P^Zj1rPcFE'&)eg=;#B\W54j(J52uHqJr;-;_Xbt;?^JSN %1&mH-bP1XIe(iEEs$`Thn,M&frr?@9>>0GBB8sjrIb+O%k'Z'VT";PPHoF!-EHB8ed@=g4\o\\gB/?#u>F97sp&Yfu(K%D_nF]Wace:17b^UJY&+j]W^_i7m-D+&F-s3G"O_tW&(FWJNb7-mo#ib %]KuRX5?tK)N>l@B:C$'R[s3,L*ZMWh1Ca9B7/YD$$[LQ$K;b]l<[8l#I>DtQHiKu[pAF%'KKW\gsDf&/JhU&ZQ&OLsVWPRmC97JUG@4hEQ;'^6^cOi`Jl#u?e[638 %CVY:JQ[nf3YCLWl5P/Z'M`6;+5Apc]o=Z6a`#koUJ#c/%,gYQjo<*['EnL-1+9J-X8etn@WeY2*U9bF%F[Br`Zg2TPmEb_!IQu-8 %DQPrT*Kg=m]K'<70C/DUFr`#*,5bUA3>rb/V`s`J]0BabdP.#+?'*[i+[,;81-L*?EtQ=]&f#K*!6-,JBAB3p3Zd@9hqC3BHCKDk %b;?-+QCGE653Z9dSfd=Rg,Zc/?iTrg+h4\BYmg+RMtUc&ft/5Hs2_\CouDKYDs"djmtpeID#0oh)"EdJN^@"o %GiB4r)IG:<(#:[/h_<#<8$#A]ne)`.KK[Y?Y2N1JO1=8/NqS.?0Fe'6aSb3K/`iVCIsgS6I4%5JMe,r`Z/M:(rl`Ks@`4QSrsSAA %EXt0Vgp,pum%eec'FCLU2H0,?Ofr]Wo$HIL[QHDA[r*s(79C8/Z8-1t6BU$!rkC5e!0\R3J6kCuM!shDD'ZWdYMQP9f*6kXA_QA$ %THZNVIeLt`ZUH=q^HB.;p\r!gB,O8?SPsL@ZlC@(lRfkn5NBd"I5kS+?!a.P'ebK+i2L!9L6WQNXd9q=hhj<9V]^H_9D:Mtm$H>5 %[?%h1qQlS-N"b%\NJuc6f7)*kAHC26T>g-U1Y7lk'-h)Q&OP#^`(ua),+ZAH'.C\@P=Zit)\,?CVdgn!,sUt,*]`g]<_%/Xj\+?3 %a:`kL>@\RGAQ@]2`0:1:Z%-Mqogl)b?<\u4Yodb.G2%K"pR:aBKAk-l]K9tir0pG=ZuMFMaLV?W1>8.!>kaGi[F:H8p3CmWb4-]A %E!/2WD?PM^TG?X?!!,BX];kZ^5h(@X90!=&R;b'bk"gpUf=kkGR5:cJ9sTnCnYX)baubW7LNu%m %fii1NIeVUjr:Jb$#tf$0"NR^+HN?mS)XF6WM"04LZZNr@^Q/[LQab2D?Pi$-=9S-]"9tt_rb^HZIiH^XMsNLr;DHqP1I;Im>5#:u %:j]=>b#Y&nXetH6RYdkj42$pZFZ+BTQN-?n>p16F/uZ %?PoI2FNY3oo(oNR'-6dqep"^7q*3]Y*q%fZrI`+E?gD%UDV`W.bF`\.n!i/kJ"d/sT'e\pmWC4kB:nTd5`H$G2eDP'm1t%kEbH[A %P7UN9Xo&:TnF`91KpYmeijLH#[Rse!X8CcPA&UD+6Fbi"t"pgeh<3Sd/%d %g=dpiEE+4naN_6U>1n*XK?_'cPecb72SuN8CN;h)HeX!Z1tk$M=T=pgqJ`<>BnphjNJ61Y5\k)l^iND>*;E)UYoQq2aObEBaUPq! %IYjOS5#Gkfq,r"id(?+EO\"dbRS&s3Y9QHN#J0gh)gCr;*GrWS_\O)+`LK37GMetb)gAj+_hb\ge@V6j?V!`Ua&ZAnkrlYI0A\6HEX`Nmq>aW4L53S:BC2oS2We@O';-?,I_P%<&>/Hk %iF"Du!Q?TW&m#'jg)3H"$=+hQ+06]"5kTk^1VOIWr)p=rm0G,qb!;uk/rLpoJ!O8gY/X6G7!jKil9?KQhLD*pmbdrmJ,%?>p%SLW %:[ns9aVU'9,JJ#+RP$Q8EW.E_:N'D%g.V393RkX$r6jEJB67qcp2mW(g7.CDKa4(0BpYeV49Z'%Y+eP.05.Kb1Ule]JF'jgFdYnOg^B"X`*[JVe/`qM)cI/(sH5\OJ*IYZ5`XkIp2`55ABGX9A6A271' %aA8Kt5_k>uGQ$o^l_NA77C(s:iRaQ1K:D+``\]B/F%!Nf<*`-]cGo[WCK3DldE&JPcSTEm,7@[pWP7F(Z4V?JN/9M$!Q>]AB<1e>Z+W6UsclTK@*XaHg[Y1@T72XcVBA %G.U7b^b-"C@$e9p8f]^PAfR"%1-H>9D2Z9\V@XKZ[k1&"<1XZ,AmZn)VQic %3Hh69,L.!(#SP,#ID'X,%r^&Xf045`bG8)a]RZ9'B1Z<+&J>L%MGY/K\oTfY6?JK2.tX(Pc!]8XW\?1!_p7[MDVYYa\DVmf??;;^nH3(sWcR*pr8%)Y*3lhnS?J-cMsHhsTgMCH:..UOTe=;NTQ]BJ!D&bP#2l/mDY(#W">8VN?[$K\n*4&qd(OP/ %6V\-9:!&'Z\],b(KiN52(HMQThBuj5)ZnY!FZ627so#HWn^1%FdqF_psqXNu?Hqd@PBq %A-Icl1@jK9.& %krLa!H#Rf>=M=@76VaYMQimr%TqJbtR&]/F]o./Wt4dhN\fl]MkE!K^d/!GCWnF<]\nFsJNAj;u*C>gN6J>s2ghDDV"g\oJf32B]u?m?PuAP;gM,G0gQ%`0GW3 %/03-(?GcOL:M5Z&q`H^WF0bCIK;uV!U7]%[eWh1PL^4;J"ciAHd(tQq1;NE!\2i#]?_>UD_+Mpa'9CREZ6gY5cg.=NOc][V8u\#I %K,CJ.E'g^n`_TrQ^9$dmVY%QPuN5("@X=<-"*1_WgO^fsZD#A0+Rc5/bm>,2[H$=Tcej2'l@1,.7e-BGk,c4"V.7ET(L`DW\j7m8D8eoJQhRlB(`QA%OhDC2-r %Dj*^UA2[jckCrt+5#HMYBnDXM207c.,PfnW5cYVZOQIOAC#6s$G23`juj9Wk;PF %08k^_?GP'5-.OIs0bKb2UE5$dEJ=KN33uTj^6P3D<#Wgr;B%SH7:'%aH;o$Te2;s[je:p*k=@N>d=J%pi"7@+[UBZ>o^X:H?TRhI %D<#r5;gn!)e'cmDf)o7KKhm8qb1IGo&'V^;#BB8W,Db;=2Sm;i"["c:eh"]m)1Cg'P*IU(.l`.u/m-$uLcB/=-rp'd^o+PrZ]DXG %`eQ%b!h7h./pUDW>>`]B"BiRrUX'>c"ZB"eIFef--H8PqC.hFf$DOoDg'E39ihJ9YmS(K9DD)2BW.]G\.Gd?DJER^L?P)qA$Jh`5 %!!laC8lCNF@O8/+l&'=H>OT+YeN9'A@YK>.?:3Ar!M&.t!jRKL %^u7!Qi#J"=&_r\g_bAd?S`q]FL1aC`91,f5KaQ)&Q,d(t'T$9"Re>Mcs(215i1X8o6W+ME]rp##GEPN,4o3AB;-&Ma$6$FB%+'S' %(][NY'ZnUoX9V#fN^SqD-DZ>EnRT'`5]g4:!)`Om5\;@:jTaN!,h'qo[]^eUu2BY0$.OTR$e&j(Xa4J8jZ8t %e*@8Pd8^d"L.GqQ$R1>^OdcV[m/dg,M4o\gI:-Hu]9%o'G;5WX93])],BiV=?h!-(C5Gg(L+keZ++>bfj%\R4T39!k"fut#6e1.8 %THK"^>`1rFn4Z+N=XI;1`fsmkN)u8]hi^p2k#Z:Dp(01qiUlaU?*ak5\R(N!QblHO& %VSpNX9oAWQftW&snsa_BfA*$Ju\t']K+K:po@:dKbfgKf0/&O?:-,.KfTlAE&$gEaCD8U@n\X84t#,rB3$Q>n3Pk,l"G*. %$\[PFGcg1rnVL4VmYb$F2bGCa5gTXWP0g"hgEY5RSI>lnfu;L<>ta[G*W"ui?]oS7]^=-&CVW=%%!]33(*Af%3>Kf!eV%LcJdua\ %s(]@RrT\K^$1NKb%r0<#Cq-N"rhC@sr7;*AB[>NinV*Jd;pT.A>5-DXu5P:QG#mfmBu?b1-4Wg#M!K %\Z*kk2M(=Nc[j#UX.qW[W\CD''W#mrT.J+3GhO!dImM*'mUQAcW'rP<,'d]6u!/TkVB9AM_!:cIWgHAR=Ft>@?)V!2W %AH`!"DffDEXK,e79q:R1&6Xe\0^]]Q@^"BEkV?@PT6o&gjAWX(#_C;4ZBfR$f,rI99:KH;_(<^RpO`er>WB&E;4n>)\59L[MprL" %EmG[?9t=ZRLY!9(TIuX)N%t?4m\;>hE&;$.mS]f??e8h"S!ob6nABn1QR#:RlS'Pn74S>/nJHd$Hf<-$$ %T($oEC["nJHhi_P,#O'^Xg7'<,2RQLdu]d^B>V%]`_QL;8!h/B5I!gTk"e7n\]NJ;&J*6R[_:@Tc:oso1A+&ICchLDNL'[FZ<=P> %Fh`J3+Fa3q=bh6Jj)6i)"^PLt:RSbD&t_0_pWVp[m-Q_6P(3:afa2S\=TT6[lmhAV_/7<79C.UnPU"7/MK*/Zd/p`_2JM.G7BV9n %HPZAj:MSCebrf\';C_3tPI%X-JeBFjFpIF:=KpZNW2GbWSZ-M2Cr$:+1=6dZDVYJ=#u`hN/nE"W1gRLt8AXo$W5[aY"B/\VNYisd %.-hYt%(;d#\RYfoWcLMANT78W_LjX\eZVGM]5`/7re1kfHFd0/gn&AUGj;t'-S\Iu^mpr_K";2EKFMT/&\sWr.>m(52>kqT6TfB$ %o-0ZioJ..Ej+_o'OerYn!TPN`^^%#.`mheUKYn)hUKo3L'lg0rD0;BBgF-oq7t%p;E"p:R+2;9SG1ko9#5<.1dtF-CXuCGk`00Eq+p^1ap88m/=n30;^o6i3a"aq(2:f0L-KnL5e6@rdPVG/S!XX"AGWFO`+A:T:he]JA)%\;K?9M,tPI`Sgs %MmD96\0_Y(:]IZlG5C$^@Tr.A)B3KcVV5I5qO8q#OYA8+SbsX?&*ol,el^M_M.`4NAM;>E>dhM]&9- %>)XoC`[K5eFZmlh8B@*eA8?iI/Xco:>!t$O;51HQM-ber9p]$kL_E0;HttT`.q:lhJ/ElP+bC;c(:P!1IEI+i3n>GYM;A;-22:V6 %i-Gp^4(_(PTjU@-3A$9'lsX4:QV?BmeLk@cEfSQhCZS_gLo!-K0j8WbJcGf1CYL\,9A=,2_g%Keb/>>+d0VZO.g-&7,jf(7DP#mM %JQZ^kBtUb(+Wsc>_4uG/[-9;=N)$@H'1#uGPlZe-P!IXXLlfV1-(;4O$'f5?u40TG(7 %Wg8(U65RPHT=1pN3+L8!+BaLdI+@8Q\0&FpQ'a&P)6P#$(q=)u(_q;!$lXqqSKn5k'CR5`sk.0N1P"`F4SKM %[]Upap/PnbD4D`55S[#^"F:/sF8SWsI4lM&-GEURJ]\DMMMX<_.G$hM(]l;h>qVs3^9I%c9Z1&');/o#I]n;U:Y1^k>_RNPM]efm41A.[bJ]%VF?kT^+d58=sB %'$\*j8,S88S>@C/W3Kff<_s"KJ7Fe68pO`9TMtZO,iE^%,8(,S;=l!8R0>p<'_R+(S7sD"EOC>f5MZV(a)-tU,Vm1NqP!K %*a%a-T!V4haG']Pl>h1`d`T']p^NNW9MajZQ\Ku#YBYOI"'K8E]5d]GQ>/6K$fR=]]FC'bZ^]8i4tO)Zl!^T$`[`s1CnZ7nf`3aE %8$^7?fJnPh4"3> %b-'HX;:Xec7mAphA3W?:g$c2BAII%!iG3qZ[2Yt"D_5Uk@)L"=8nW6:d`sc>SS-l1+c8fXN`,DMPOf_bRq0=;<>d %N2-mm1m--f&Un\D`j.#8Y'9eY7E(:R6m*>P%eC1t&8iF&=r>Q;K-^=(4\)!&SkD&MD*uE3)?N;"0gT8 %rI1XF6,pd^gQqFp/J2Y7I6Amhh/!.Rirkipetcg^30;t09/jJKVUd*8N;4dKjPI*0p0g+9^4n7rKkDP5KOml7?R8gRbHU?n-p*u)Re8>23=D39Mg_hUimj&]m0Xe)lDN[\e(O'O&V_).-XEp]M4n\ %.:ao"#'l!FMQ!ksP`,;e1,:_nG16O8/0Qlu3/#S'EC+ERf/=1mYR_1_0t:?#IW4L60k888&-[ %EVt]gS9Bic>'?R@UY@CZ>jAg]/d?5pa-\mkF-XE<'<^.6^#;68KYf_MYKOt7=B]gD[>IHLC;@BNF.B9.XYS%k*%/&J %F6t)+%J0&8;_=gXK=Nti.d`B3dL)(!YA50PMmO0_<"T[t7C>d`PLV"L_V4CpQF!9l(L-qY?:!4?+JFR)51 %*U.8#d;V;-D%SLVIoF5bj-CP#GlTpIBNo?s"!G#hTZ(ip5ZC(#<`B#(f@jK8_ANDjR1tOKG?f!1=278qnmam=5@K8%a&I=/Z4J?n %48[i<%OJ_kn;jI$XXS."Xb/L$7*BcMB]Q:0f %j1dl//$a::QU@(jB*On2=a\rcFlhNj+=/0V*4D?V/ZV//WC" %gA2;<\""_oXQ*]Z/#N.E)AQO[fA4!0CNZYsE^k)bicHU8/W5\09TNFHgs;ODM+^$S;(Hr8G>-V&;7[/o$lD(?/6&H#XdaSHOKW#sr#cVX&Gt"YK]1sBL'%re42lgLJaWC"%*YG\+06&7j1u`_n=[U^";SP*'GQ).Q:QFm %g>Ba:]+)\hM"$:pSe,:3BYp.tD"LZ_L[)Z0X9dsm_,M+r&=VD(TJ:An?urOccN:LB72$tSO(_MS%0RT;%RAf$VFM3FDGWg9p7R&u %*G[4W5>H24Q_u9o.5GV>=uSmC1:Nk.=[;%e,'l=mj>GFfkS6[H0f'6*"BV]:;H3:=:_&G7*7DD#&1,H#%\b<.<);,I8?pbVFF_#4 %p.XU0UWeCupp*.n?iUk8P39`GGL$g-';RQk?NDMB9Ad)g?O)L(,Eb2G8=6"`lQpX%(/ja6>[6#]@(@M%C3#j=0VYDD#-g,`tLCH)K6b2JEj2_33Xp&VdWXH3`cBe9fHkQu$_XWAU2H$OZ8!3Nli1S+'GFbqMRoe\9]E[DmrE\dE %)s08=-[cVNOkS0d`(m$ciKh;>+ZdTBE^f4$;c$e\I5V=7)n%$RTf,i3NZnpm`;HAJ%>J6q)Y3T2Bb[UgEe\Q5=0mY4G?DLd1W*Cl %N7.4,VUSJ+]=(%7O/9@`o?oC#&qX2S/N0%EMPIDn\Ce^i(eq=oU^+/`hkQgLdm\!/^R>beS:!VF-^ND=@(P:"io8H%ZfLc %SAJSJ1?+,\,uc@p@]W\@Ld,+DVFI\g`ggK31;'6U_/Z7risb=+baUb3=%nR_n).U0[h4XkW`s!3i`N9JGn.4'iW3N)@SVrgr>cgF %=Fj"Z^1pZ0%IqW*L9=4iR*k'@XQ`a6UXC?D7BkL*:6+pQh61CV+gMC]3\?2MKMLI1/$+!6@h[&&0>=u_1KMBOo_bfOc@ZJF9D^e5 %I\4F[/!Ju/`U(?KUD.:9\6(^7S&<'9[ST;W-o.S'%U6@hBj$u"m?eRne$O#aVIC3 %k1cmJOebD`oGfAd,KcJ,]:R,g(-W6f:esBQ%47fAa_r0S_f))sYc.)?C[2iGYOMJmC2)XpApsVTRrPP-3lL:cK+\7'u %5nU.4)AhD2:C2$4(N]"L0!%9g,W#bNL]qNd),''sj%)W9#q)O5t`Ec5_SV#GSod6<.%X0UKn$"reB]5k/Obd4&nMWGLe_a@B.41Tr[uKJui[$jCa$1@HY>\7\%q1fYa0,2tOB6)dj$-d'6+sKl)aKWORZm*@$rAAQ+.M,4C1n %>&0T4%JiQ19Nl8[LE9b?*Mkf!(4eMg)Nc`8p7O,cq8gGFV4!s0Z6[nO?Z./@GSm[K!@Su8N9*L6TM1rXgr`mZCY3Ec9Y8rnWu:0e %lC:M=T;'f8/20Gh2c22\)h>aZ6t/0+]JT5T^_#.&'!ileX!Oi&i]&@[=edbaT1hL6jO`JGVjM0Y\kFlK;+r.TGE<9dYNqtFfr;b4!bDs=9iU_HHP&WkisA+KP%;U+ZnXkdi/Fl/0;g4WlCe_X.8"So.9t'?\6r9DW:@HDH]sfQ:FcUm0_HDMV>o[A-QR71%CYN9FV#;D*M)_i`=P\@jYh6TgX@K-I\4\#E+(i2q#86$kmG+ %[Q.=L`I[s!.-kEGEX^')ru9;a$ulR))\0n^f>$$JT(gZPQSfVl)c't4m].lEMmi?.#Ng_K"m8/;[=pbMI\VfsS^LojlcU6.Ea&_JR>H_g]cPCeIdRi(!M0+fWJ+S^`0(Vmqu-#R5'6gG>g?XOt!`,\ZgnZpj[ %S&KBn6WXh^_DGY=.^qE,)cBIbb-Y9/FNou&@27bJ=<)9\'ZUK;n\K4<%rfDNZe1*[M23RYTWWDpCkf6h+S\_[-aSt4&VePd@?X`^ %@d?Ckd)pF-JkH\S"'lXZ_kYA!9de5i&g"t,QqIb=`9JQlCsg?6P"OnL'-/7BF`jJjIHJ(Ql4;N9^5VXgS_26PK.e7CQUSr[0SL-_ %\nhe%'nbA[pk5]kPsP>LofODj6'-is)paOoF>o/G]$O,]`hrr-6E.<63r2dVmtJ6\M.&l:-cpq/0]H\Wc*S%S4_d=%SD8_%hfUpf %]<(:RVc#MK[9ONmIND$FXi4_I<+?>`=ql"S4:8(b]S:B-A:oHVafsBo/1f"h7jngF'uV8;3j31JlEbF@jM#8Igi4PSmUX/MoS'.+ %g>:G8T+_`uEr-Fef-99%/f=#tR->ab]J,U8=N2B/Ud]%Sk+`oXk7k@B9bTXL[7(#J6Y0Upn1GA.Qp(T00?rOoSc"0KV@20nR&AI8 %P2-:sWjYs%c:AA=DH7Lic7mN.?o`/0LFaAX<1cUN,giqPfQ01WG8u3c/D7I2iYJ;L\?&%DfqG1B:3*A7jhS)n`gpFFH;UV4K7_:M%Z)6+,K&=QL/f3Zq.mQe"["f+&B/JHX+E^8)`q-".`H0) %l[p=H,`ro%b^r"!Z"Ri',3V>&0im[MEj7*;$Gi=E$!2u4e"m509=\k,SW%FX=>#^XPsis9bj&+AY_l&S,ir;2V=daWmQrHJ>Peh; %"Fe+;9iWffQt($hGmIiK%T_EY(DjdDl!iVTR2QjNYKC$FBr"2N.#VhsXU=.*Ta7Nc-c0PEkK6P^B`E)hV+*+F,' %P+)1jWqqcU%_/<1(%>AaHXh"$ZqL5o_@NLQMQi$1,4&AjhX3nLc9\?!Rg`:&d?NOF-i>MQKY!.QGrB;+^bp43q=]--haO40kS'W` %"XpR`R__bE?+CH5NK%,[,>K>7lti[u0)D=Uql7*HdhRn]JVVR&9^oXI9`5^m[oDo9p^,pC=K\J(^kpf;$"OY$Z`8I5BcFRs/l?a> %WIjhN8l&nj'l4&@p8/>]0[Nl=A!J!]9X5B?:.WAe.E2q>1W17cqD!q>JbpegP&N@T8VD4U=T>,ac!Yr6*#,Yd0uu\Q,r$8("q\,> %Ldh"CE`@s""Fi!MQ+LSh@3/i<6`[n1,U5\SX\Md:*c5Uc%ZpkkjF?XphZ_eT+9+c3m8`c\Qu`N3q7_b!=0!L+6YKIK8%Gq.:j_`!nIDS^4Z3W6+!b*Po*]*O:VL#_kRE">M]6 %ZYT-%b#or4#h2>JTp]hS.c=ZKmp=_N]0i\](jZggqn#M)0OVN/:F@cU>RXS4H8(*[\q/cM]A)nOV%XbqnW0?]o`\CEYU`_0PCE`% %U[Y0U_0V>lBo.7]?Ong("D`ep'*'S#MP28hUnc8]7@GW;$kfQNQ]f4e_A6?#s/WMI+_P6r6:-e1oHWElFjh5eA1'?Npq3R7j14-/jVSiKEgF_^X %l7=%Z`-\H67GES7bnL@I)$f`@Ko\$",)5BI0[W^Rm%B75GDTj;+-a%RY%?riEn&7kJsj_Zg%;ul_Oj99D,(S$'Z-f)<.e]hPn%m5 %7QcM-1.@V\`ThaD"e6Us8uuW3j9.SVUT^u]U[fk%=B1)0q>'&_Rb[7$0FMD*Ajq[^;uj4jT[p`>CQs$#`TZ`FZZPhF>G8<#Gri6*?k]]#4$-_$N"KPkr2dlrQ`uq=/5TVe#>qg-7/fj_:P\4j*%*84&\qDHn8X>"!X8:Z %;c9W]4A+@;[g#FJ@J\]k"J9b#OPFkY<#:nTT)/DR+Wf/:`Gh.lZ](Who,S4%+r5jK.;%N+!`r2YRF5&'".Zd.Z;@p,3_3aH&.t%M %OGA^E2+S)T)\.ac5g5uQ*c/XB4Fota)+)@mF$;'J_+7a)O]1YZCi#cAX(G_l9,*S0VYc\>BXd3,3qrM-X#b#OZ@k9kk3fkLuCNX3?PNGi'1Vccc"IG:AI/:&p&1A[+-I"[&.77`qRC'oC+,`g"o.^e=r+jVTSTg-J6\,5r.WeN %*PGjW!`pc2mK2P6YUO&-eo58U8>bh'-l3tN6<]!,a>/V=%TFQdZSc]$Lnnl,C-)?FZCZimM!6.5l1J\4Au=S6`H[Dg45/+FCAcY*2qpgq0<\M1/Q^ZM>m-67U"NPTIH^672IW*fC(b7%H-a=lC,cMiX&Ki)6XI[dg'ebj:B)/J<%7UOc)I=O?itp&hP$9'&:r)NkL?B3M/$g6)G-jsh'WI-9c0'+p5!hk,$]WP6+(E7^KhN+hQRcl-F1XRjp845?6<^#+dFh7f,6b4#T1qZk-_V"Bjp[oEnC>js0iP %at3\XL7ke0J?5&fb(QiaIOO'LB)-dUn+_"h,C?A>+B?Ee[[PGE4cW.G:6[C`=ln;Y?+=4K]pof=%9i[0'Vf`15I8UPU1B&>&nZ%! %-TsettD8-Ggp!X[nk_2BX>J.##`IE= %=b&pE_2X=Eb@Zd&,l%3WIq0e[`#+3TU-7=R%u(fY*ao/+:o\+2"T+q@ %j*h%^1[6#M"uAZXQe1G^%,u7A^9+nku$`;c#9pMU%[&r3k!-<4!ie[X\i/5DWH=]!DZhAIJj^l*;abdq %IH+(Nm#=j!d-2CO89IWdMf)F&W1g>6bJL7l]V9q:`t;N^[_f_VlKlcip:CUWZV^^i<@!#Ueo?mMd5=u!SiZe;&J4%h1!^BLY:q@X %C[)usq.6#u&"31_ikKBU]XOMj-:2,nei%`*DG_bJB'UOR<2R9t2/d@0C-:G(gYK;!e>.0JmI!5J<"hE^J#`ee_.<(=4V0oA.+hL! %LDI?1S.:n-61*^QeE`0SJP8>NI+MZ?1/!S"J0\$T*9_Q*Bfn%$eYN>p1jUfY/r,("E%F_q,.itBLB@#tgK;DGdUX'?6X7/OSj<\l %Pd5_h#*QSROF%R:#R2/T8Jrmape6jF.?Pq`N=Z%@VKg-00j,f"i$1Am]f97?D[,,UX'f)k)Fh!_i'P8Y6Y3t#@jjXO#NH@!,2bR_ %(p:Cbo-+tN)MRG[q^*g5X'&_;G@0H"@TcN_SRKQl#o@j'E?3A9Zh!Ne`KtW\SWF<,>k1E/7-$#j`9*LID,3j4gGoI[&/e8idn3M3 %!-49c/:K.q&t0s`JAOh\t0atPtjk1&VLVr %4=P/F0mGXl0BOenZgQ\Qie^*OaOkZD2ot/VW4#b2Ps@1C.[+<'5*UPWJEL`=0^@&@L5>\e&5:'O8YjRAc;,(bbQ*#u;l!0^=4)V7 %/?':(X%k6,.FnOP-?E]_28a)C#E@h6hH^md^-qDWj)h8p*Sbi`k=YHVSOV.$3X+4n.jI(K6Pd[hhQ5+O<@fm:JE@@r+]EI5Te\]1 %)>2Z!X6:+SI-oLblLMX83=l3K6h$Ll0cui:a!)C15e/sSSqp3qmLoqC/?)X)"HMZm!2ZNQ8Y"D>^f#kA<=WRXm[6gBi43aM)+%[B %n3kPhHc0fUrD;EfEA7k(8js.$'dQCsV6^Z>[Me(g\[=^\L^5)g]*7lIG0=J#2\bh&rL!K^Og-VW`U.#"`BtQ[g_=;Ai+fn,_Q.jcBplcghB4JpOqm@'@LDHbJWlB%90[E`-Z2(4=X/W>mfIAMVQ411=[.6<%,Hhc*bH1Ca%(FJir4 %>/AF:]b9'FHu:_C+.41VAJpY#%i;1hUuTD)"o:KJTd>NoaeZOWp-"KF.h+BafHsER*>):NAcTfD@!%oJ=O"EshI$%i;@q5Tdsf%] %j*@gX>q[6#"%aODJ,uE1?ENc?MS78]e&h&55.&?QjnR>aAis"s\UNuD7n-$)`b^$*Ei$L_8Mc!FMsMc1g8aG^Z:kRdA=,CjMUmM- %cb@@EWA(7.P:Aa86Nf`%!m?D^hM\0ad*1;j8^p\N107Qa[Xj9!TdDWRM\5W,dZ:I=# %ls%PlL?/X21_m^QikX0OJ64!'&\G* %'h\SM%!hsO,363RFBJ>m,,(5VbaA9!Bir;L[%EqD=k)':RY16YRQ0qmF3$$COl$8CYhid9'c,oNo.$SkLO)KboC%i6a@@0f[9Cd`9)JH,ko"jd4&!`dS!Jis5*R3e%3>'F.E<`@kkK^Due*HRq/#^Ek:!LAP0!-j/jZk("\JMY_$q]^K? %C)"^c&C;uOAc%1qE*/@k[ir$m'dhSp^5feZRso@g>gIRXE4V3`Dma?2gSX;Ub2>p] %$"BK?apZ;N(J/mWea2;".oi3ZCJgAR4\>Z9*FK&#,VgM$6q47Z>T7d&6#hrGZVUu?USdT]LFb<0&[.`jG6O[fo4"pAQtEFF1u\(p %eb!t6*@P)WXpDM^U1%)Rmp1!dg;(+$N`0S/W)BjcrN'^,-GJKhp*9TJRiIWMM0]aA;S_^-,]2Ub<=&5:;m4e?=d$CT"J!d9fhU4j %Z2C$cj_KLoGoZQXR%c:Uf+d45OUsLJ[%[t"l9N^BZkp-DG$5/0PgQ%!qS9Uu=!:7rlGK1qE#L^7TrGOW[9W'?d'r9a+Gu"KJ(>TO %A."?[E)Bs*P*[=.A^D%ZS,UeSJUWcLePm22f3k2meuhh"R]>hR<3oNQL[],j"tQ,%igPC1ES&/m->Xt):*Ymh,C9n"=ehQ2#ZI(l %[2)cn:A%_J-kf4F).67la,6g7A'PcgHkD5\8ghO8KTd2S(1Xk?#(d+/h2ODlY+;_$6%V7?F %XAB)qq\[V_b(K`sX8,;I6b\lBW<<9Ihf=u=a$;tcNHMaEN0"Vk6#B9YTajdo3VLDMq5aA<<^Vu38W(WJ'R)J<`/7%c%U)@lVrXFA_7Ad((pZ,">2ZiI:j'lE3)SYa %s&j<(.0cTQYV4M6FWtN%`Thu*(4aF92Pn&oT8'e)P@q/^VRl1:XE9CDF`A5;M(?RR&bU-Xa>loQSuQW[PjY9L!S7YA'sVOB0a/2G %*77rS6\esDln+E/_!!#D0j[>L_-IgM"3uNkES1qY9GKTk@$@Uj'F&>?0juI%N?YtcHWR=Bk9B;bJ3.Tq!0Q?.]R1^K(S/o&$#+m`0aS=;h@aLlnhCnd;sc%tu().=JlWZf@C9G#'F %70u_hV4WGh]Z2_`Y%$Oka2Eis&`9E,"QJqb-gB3V=r)pOf^(akrXH2,%L%1]EGh$gg]fN&/jEYU]hN6Bqi.Em%TMS0@735:Xtda' %ocmWJ5"o79_.d7#Wr2'FZ8B`Pe4i\Nn*>"DJu"569Ma_E*9, %!o$[nLfPUA@D]/_.2W^Z[N^L]mYH.Cn#nLf6SCPrPIHA-KttL?E@M%PB0iNQYW*UZJsWd5=,*3B^OSCpinXFr %LiR_OKD`PCo2hI13J?;.8_<960DM-c?3dsb5k4%$DDn,^n4'lCK5;bU3@oO_kNW\31R'&4Kmn!^iXROXX`_d3a.6Z%?)9\Z(D %MLM=I&5m(@L^[30IeGcA(*]7di:b$$5]qg^#SrhXo_AXqfK_KHZH6I3D%YrN#"XCb&1d7=-r:M>"N_u\G3$CnK/'Ng1fn2aN=/Q* %r>-0N]6J*.rGbnG?PS^>[_b-5^G[W18-bM:qTKl7lH*j(D88Frc]!K.WU^j^7'>DimEO^7I"mlg61s=KDKX?50^J5^212b0>afAp %AW:(p.R-A('O(5R7D%Pg7AKhQT5LfAjD\N^ZiUA4&\H`Sac6L`%8jc32=VqqVsA,t/EM0? %'p0MijkpWH#.),*%P!64Q$tIe7j3l2GpAVD>N&1H4:!"^:)aqTP14fr?sJ;[0+c<%V(laSOhffn\G"@ocOA:`_F',*&kO48>@C0l %'"0YiX!H@<:td^B,1oGXA`k+fTqKdUO%Iu<5S==L[CP\'=GHWjaG88aKAGc'3sL6<7;5c16S$e5TeQSgD'a_mTkpbbW":?/pYg^$ %C#u=lT@oN5O-fZt8Oo&E&\qm?;JjCt\]I_Qi(/Ab6LD]:o/4T=8T`&MN/,^FAG)XrFie=F!,#:sXu?@mX.9:8WemdV?WA)p[4Y?u+8_\[IEu\?=]Ddgll*Wp!^`/nXe[C39"t;:=#*#M'FC6u#,(KZs?=a?$RQ2pt0_:G;%0=^:q3Qk_>!Q>X!j %OlCK98RMas1=8Itl=Y-(%$9F-;"!T^e.oDZ_.'37"hDL9bpkbuo.q%5.iUchXu^2`AHnaP\&F%f;8O:K!)R_;_qd,.-0PNdDDKBb %E*,2[L.?;rZC7L);cE0$'/in'j!+#B^g_LrU:m8$o-u=V[E;CgA;>oR'bb%X.]i+WD6BDWp[^0K/X`)"\?j=9(U10uCMLM3RA]"E %`#CgNSnj>PNS'Bq\[k+,OY!Im?`F;PRF\R0S*H!%0K/)1:a9]Eb)DLqYb'&X")=^R$.tWWZ"4,fF6J`:M(Kj^2Y,5D&'V,N^0[%B %`Mg.E'4Z)YG8/hE@@i^8/1WZi7RrhRb;tKR#*1b?H9PKq+m'I7t`%@?QRHTtOCFLi+fJ,\l=6k,o@%dX[MSOdLRXB?cGt"%[eBSlgd2@s]-Am;QFR(8sqZbpN %>(D%-*MX?U!HBD@f-o]6#ImVV*Mm+hqiT]uo`eHn1,:&]eKk6A.2Z(+,`E`V3]XE+%i'!s]'E'qN`egUs3nAs+c]?u/ZSYjf2CIi %$e_ao+jE_R4_n<.da+d#2XGT*Sd+DKl4D>4],R]hbj%t7%[,.?qNpUD-_)DaOpnu %1O]u[/3Gc9TZ!:2KPMP`]+aO92(SQ:VZY-L?0F %kslLXQC`\(%%5ne+cDiA_Ipa`.2oqAN_`MuiKS0]a.14?TN!i<'&=F1/gQkB"-]hR(*q@;k%7n+W+e4i;F%N5/[U`d9%4'9m5+oe %ufpB-ShdKZFeIahqLr.K.7R88]/F=#BUA]!7q2]=QJkLga$R+e2AN7NX,oa#_pu'Mc-6J''F3G*`qI"]aeY,j2a0$ %Rgs1Tf;^__eA(%`ZKOQjZVh<>3b:?heVp`-i[2EY(DI]$WfS/\qjFFE4$&V6U'0)H$n]=ULQT'OLFGbT+Yl1GaS>D.(HH8g-HE<0 %e[)87r2Nah*L2gUd\%`g%2Nt^iX"g$Ls##m-'>76g()2(Km3cA4K.9%jq;C()`ttV3VW2PZQ467\GcT6(,%]?"#Hrp+EHP;*Puld,eoA[TDm>+@"UaV\2dI3g>c[U$$f%6e[7fG;A! %GbS?k#'a!J#ITuT(j^nGQ"tkQPhQ7Vgbl^$j"$>=[W^gU,,+*MN"2u@-lS%4%G`br0Ns&"eeud5j%l;#YT@9GMh35r)cG:lAeO;e %2tQ,r2-f>rNQGCo*tC2KM$b4W&A%qe/NMbH"2k8$3*bFXNEF.ejq+DnE/7Op,hi/YgmIT.JQ6JeHA&HQ4*!RRXfe)$:@^J))ETORE?__s\YF@]aln`H2<#]7G8Uo^u&R6uG7BkAjb2`fU>nla57WgDCPGk_:R=$i?1Uq\@R.RB7FP %G,L(Pa6og,\rL+`V+H)57Grd/_q7W'6Tto",-MO7Ni` %Ndf2>d1,I227XouP19qBbZrme9>bVJ?H8i=BbCYM9*!,EVVOJo:(`R,MdI6FqPVI3BuWV9P\ml\,aqmb"cL/BF442ms"88a&=b(" %_mgog)9*8EH6dF![*CC;ocLU:+9T'k0f[.`GZJ!f]3Z2emD[+m3%pdOCT:'Y9n"T"7caVJ@32/]>WC#!fitu5Aht)NPp)8G$ikt`9:Zg\O\k9R,6P7nCB7MU=]45(inO.AdXdZW %&:em"9iPIn1AjYE5!#Pqg.4PHR`Rp0gGq:W$YndtXUCQi;AZl2kN&i+.2D<'2L=q#-]m8Bn=+m^(\1NHXG;Yrl6&jF1j*,![41N#AQ].qMu*mPB!L:^H*2.N?\ %Tq%'e=A#]OF""C`NE[O*$B461Y;;IP]^lE_O\g.=bN1A@DIo(,co(GJjhd57c;=:E8J]a+e>BgrH]Z>&iu,$`bU3sq'e?"puh+hC7>T/Sh5j8d&) %+HV6".SDVe0-0ep1][mUO="=iXUTe=L0mW.%p0D%a@0kX]LWP2Y%G-*_STc_R1baTBm+Le!-nZ7&BqA@:X'LK/t&XtPO]D2=$/ %`DuS8n2sbUc(I,Xa?;F9Q8363^$-*Ok>eoW@-Ed5"_2-@3`T`h0a)_rf3G=>`=h"ABgrbN?X*^nXGFpl\:t-9@m/!%XOK;^'ipcc[Vi#&:LA`dp@^@bTH5MnKF2)o %4c256l%16`N9=!eP?&F3J6/#4pKDc;@#5c\T.qKk#jaAF>uRP&0AZ8s*"?'$BlQmL;%S"EAOW^MgIa7/dd/68,VuCt\4I"18&'G5 %aE+u6an2s;!hi]t6FIr>7M`s\Yq)OOWQNa8*J&R-GqT_Tj&oJk\M[U=lSir.="$%,,`k[-&esVgT.i*:3YPW9\pe05Z;pJ!a]F+m\4,rpR89?d+="/j:K\HO8q.,8eg"bTj\mb"-c+KPJ6;QR>^WPo.Zq6OKfBe9iT$+ofl92rr %RVMT4c8UtF@A5#rA%rnYlT*"s\B)eA3LT%V`=853M;^Ep4pbgMLhHODVW>tnimXb!3j(FDb"N*XGA0e":,4tN<*J3eD@i*d`X,!I %#$\?_QYuXl.W7cP/8GE/kq1:`<.F645aUH&2IXp7=[Ui(6V?!cLpWZBT@(a\hYla<(nQ@b&!"l$J\C$7_h0S![_)WIuKp.(lQTQ#p]S2[=Q'"DWtoRbmD;hXV6U4Os.MT4E<6hqEK287 %ChP=#LZk"G73e/B:Z@>9niObo=GQD$9:sJ"Y-("in7gjc@lYFF?[5F*U_LP%ZmM"=^""WA5p@RbMH>%Z;&Yu9*_k99;p"a.%0Dt; %,41^nhC+.jFD&eYO5@lnYs?cC6EInA_5;coWZ.7R@RQRoSA99&ER2r['@M<,A)3rI[LNT2Oo%kq>KD^l@MntK",khhEJV0CEi*k% %8:!25IRtaFmac-S]#JkdD(U8R6M4,5#SC!T7MG/E4N8Y$K;Y5KJ' %!D,dAZ78!DC;psj)a/nX6rk-CQr))hU70=Mi#$!9pg:9lnVSfY_(97OCcK@9PG! %=^PZ;M,\L+o/p0IL5?stgUs8;@jc%$L\nHdap?Y;ptNJ,Oof(dN^ud$3Y.*78XNgel'O*KX!0hLp4Gfk?-nCGK`sJmD^tW76U;F,'nHu` %me#'4:!-,<%RB\(a:+4\Og^Y*Z9jd(bpEuu\?m?H3fJ/UJHVEM<_H;RQZfup&3c6GBdS476HctSBLHe15WWEZ82/IW+HdkmftX9u %W`C.2;lp79Fd`AhL-d:M@:!8MVPL^2[d>O_`qYuqa=Gag?,Wq"\khUq'3-khd?2LVLYsoLn%C*7dO4dl=CNOIJlX]r3UeVh:T.U_ %Z]u:'TufY+_5V5R$00(bV:+ZUXL\gdtX"%V#Z!S/gR4_bUdi(Lr#b+]cZT2-$]hGQB[=`V,u#`R7N(]XIs+Kn@gSeTFX:pf.>QM%G[A0G=i$ds.Gh:":$R?GM;UVI;2E1H_^oQ5hd"Yb8%A7=T"1 %FNf"Elq4sEK5/P?H"jfB,nsMph&DHU@3]>9[D%!q^_Z7D(98_&^DFZ@$!!G]5,Uu!o?liVeKer)W_Ya_\RD\:91QSi+mO/c,T-': %4#kRDb1pU.)2'&F(/Br[3SXZQIHN+\/e_?WV)Car:(fW$KHU/@hTr6C!r#H*9&#_M#$)*ILS?=;2?c([pROT)=AbWc_"GT)Z(E%.qn9^+aZY$DHBW^7TorjXWLYGJ+N;(Q/] %oFG1l$_-Ve#\,AE:5uk4ml8EpB7],ihYE_=^%YYmIE(r^#5fg;H%HB']$JM_IrEIiDT,stW:4PcorMRsE#i(X5Ar!ZOY9f%.k/=acbUh.7"fcU:Y_8J+"
W'reC1ItOEfO5EiQg@lkS`fgO'edG-)>IK:>m6]/\>H"Q4OPZ]sjd]-;Pn:Hu %UuW\I/MNW_Op=4aVo+N*0$tr:)$7X4'!o%OrCZjTN]ODL>%ueOX@aIQ@.A*>kd^X[<"Heus,>7FkO2\3%SP6hPsj7?WcZR']fIhW %cuZ?`-Z[;6iiEdbDW_o0L1#4RigU5X6K3'&'X'7&O8S&u.4>n_!pUqi%bXtM9d56KA+&aHmC+4lG%&BtL %898>pH89^5Xnf@QG7>E(H+!"-ATE+ge4pJN.Be8:SE]rnf1.[0*%5d9s=9quc%&>*@M!l=/'s/r>4X34.llCihufpGVtd9H%i;R1l3qP/Rc/ %3"R@"3Al5_Lu0<1E'Q#RR+pCnif($:eeJ".D=O2Jdd/AmL!;WR=TB4Y/4s1tcBoG&-JO6O7IMQUq4tQ %G!iP6]st-Z'ZHlQF^,P)_;UCnUR%]P6HNg'Iujl2RA:m5+#$]pem>g#?'Y2:0&A"'dF=J&,U0F2%-!d<(ITp^\g$FU*TE,I8r>K-=CVbfq$o:50OG'TnQJ($E#6,Mphr9'X6GmZp=F[V/89e.!A"?.RWq5WK9E%WX&%,qdg %11>I099_rnV8W41:"hSn$,a9*#OOE:/24d>1p4&e&5V<4Ieh,jS,I46962J29X\7pD!]!6)e%uK0_QD_S\q95fGUroOskoK90U;?l&=N'Vr$R84VR<(N,YDFP3X!$D!LfQepaK(boB%c.u3r:fS;R]R?T)g-:%.2W!!IF^g>Dmo!Vm6RT6#-(57N"o% %)"C/G3o=*P$a3Ulp?G,G;l*7A[rVmMVNdk'.Vn^d(iZ..#J[?Bj0Q?Y#p)gpf=D+bM>k*-:hS2(<[ct@'t$?"Nc^NNLfBA[5bfK[ %%=8OeK@eG^2;0G=.-ja\DTLtRhaRk&gXB]W0b(9-YoV327*S,.es^6I9/rZXRKXoHSpE=`)l>25ZA)ZYE_/Sb;&UBmSLS(VAQ+Q! %,,#@(*;4%/MV@6RDs1K%$$Q%S&bOO2hAY_oHBV?4G=[/"LNS5(3^SRRU4fkoA_?19U7KDc7\OJamSVi;BqrT:VTk)EX`4/a3T!Wk?C68!;o089Y+\US<\J/j=4-nXhV.>j %CILue_[ni\%PGU@B8R1]\BaofKNGid":)8orqgocECNs+Y`E^/'Hq$d]kFsE2qt)WLjmVu!joS-2-p[Lm*JJ0Rbjl3G#X%*Wh?kk %R:Zf-T[$Rag't0ELuIZ$IP'c=Kn(%F/>A.Voa!$!@Fie;>a)V[@^%3,T(:dpAs@@(A-7rY[i&[=[EGcGE0UIjr6@;-Q"552RrRtZ %#S_D#CJir98CruF[Es/NW`R %]l_F]V!&kghk<0F_b3)J6C,AN'F18(cmLqd3gckO4@rquODQ?]3[%k<$Bu6N]C$E&q\ZfD,IX+ZDfY0$H"o=1,.Ui&086g+.Hmk+ %4dFaq(99e[JUUqQ2[$>bEktAt.PMS'LS5n.6B`TLk3h371%)(nF_5-FA=,OVLS86D%KVotB!nYA`C+*qZ%s_^$$QuP!kO8SgRmKW %>BMVQ%=@E::P8_K",TS4L$O\V#4m[?86cQF+G5M&c!p.[8+[atP$I41)BAus.Umj$`\DZR\:-mDnKH%-Q7I]/:L%"nG;'$nqg?2DF_T>[Jh/[QVl %h:*.U7(et8V@2@]PtnAjb]%GE5AZT\P]Ch[3)G$Y;aE>Kc:a,O^e/4JW?:C>s15nd00Jo:[q6@H'@E!3"S(mdXTdPPJ: %=06r%L"#R&k06<9G7/K!%uDGJB0jC&,@32%;OG^!A&5juUhRMXr,L2G\u&:07>%99CDL>a-Z+FH\oo+bt9loks]CufPG!0@4=HO/ebcp %;fbj2]^gCaYfW')@UO7HFfRs'W='u[CS@oiWeng5X2X@K/IW#3(]?QSmB@'':`MQZsUaiAZMu_ue.'7_>$lW(W#K'MHOMS]h/s.U4`lae@]m/4#>5(3JH\Z:1e+U6FjP?gM!>JfYp4Q/Yu>C<8GZ&?B`30s6s79K*%L"oomU %/iq76_r-=+D(Ar:[5q+lbam2k]Jo/YK0BmN-M5&lB4Hpl4,HNNWlPo-hS`tZW6\Vb"hPYni'(V0;R;`"!P1#dMWm0(e&'J-]84'^ %VIJ)MV1A0#/]>nGZ\qA$l7SgCI^7+%-7,ON-Vs6E*?E)j!uA>J*hiPY %1t(gL?5rRg7DD&J%Yk9OB#I^4*'L)h>K#&3GU*8Jno?S!ZAV>>^BZ0rGlT(VM\/Lb\o]e=$B]X.,fjOR)C!Da_(5T)rD=9Q"\U+hTn0_\$.\ %J5J8'd)@C4,rVCL%>gNd6P?&7WNNG7&uc`XMO%]1J'!1a+#.L?#Rmam*@]$2=P@(PVZ:GQ3K>ggq%dG*F'#81_41ueCYSKNj]h-u %Kh8_-2A5fOP,ZCYY<#0V<3$?[mOQ'oq+c'Se5eB-&lS>j_jY?h0Xnh*NdWLFl!h1LbT1L2`RNBS%n/C(_-RX60$q;]G+&PWa;pb)+1^U^U1EXtb8$'rA/e\iN5D%^^ths) %KnQLf#+gEJ03[.Dj^l/Hqc"IR2Q-qqA-\55P3hC-se_lI=TVXOocK%4uL?Jt!uR>hZbSCOsX.L8qV#A]prEB@`>W %C2oH+.Nrlfd>c#m.7W4"S%AYI`?jguUoIML(?%<87K`;u':D1SMooMOcYkF_jsBQ]AAk)q1>?@>;#m&3/OYqAsJ:QBS1$=;N#+5E(I@SIC'MM;>s/A*XhUa %@&VF?#kEb4mJt*(7NLoI`>!s6$.$Sd'mGC[MMh&e,ZBGbY=sS=)pb_U7UX!h)Spm'/eWh=[7ke*%@:Jbg;+X>!F-49D[Aq(0B/"?a7d1i"!2J;=q8PZ](_[:-!7MCo/ %!=,&L,=Hs;*VJZD^6Y\tK`Ej(6i&$D*@$Mt8\^U>impUt*,6o.5H8Em]!ReT& %.aNsOj_&Hc4='j*msQ+oTh70Nnb9^cJN6/fQ+@!)cP$q>kq8eF2:'&.# %;].0!)q)j'?,Sq[FV(;TXTd8upajkuL.Q@#/8-TYOK1FE@pDD("D0rQ4P;=qcad?J$`[WkigmsB=Fn(G?M*eK?OQcU)Q3(]V!/UeY^tlj^ %I/]Wh"^EYFN/F#;VG0t!"8:_Kg&g!OUKN9&$7HdM %'j]=CVa*^1dF$.HHmuik%$`b.HVl,lrd,H[jc&Xlq$SO'k\@Kd&K_9 %_m5)<,(p_#\(#7n\.Nj47&FXC6:8SuIefKe72Dd$i<.ApmCb(f$Ulb-1jZ5-**l\oMCq0]V5`V>LFbimTJ]`#CcQOb`n."X4A1nJ %G!]_J%8t#G9*FGiafC(Zd(D?__QQqFcrC07)e#0fSjppDPdnARb*V+4;^&_L6oXkC-$aRI0`BuLf\m0So-QVLQK1AcCi(Japd%MH %Q,=`$`muq>j%iX,RmlDPehYEHjIEZm'6c5p`9h#_[C6FIA/uk6Le:@HROL-bZQ8VmPl3mH!MCc8(fLkb`65W>-.Xu=Xh`r&D#,]N %)8dFM?"f/D[9RUc18uuc64EChM,t2&C8raY:$:hsOS;kJ[^VRBA07c=iHQa7(m@_(E-rtB-Z6u!p3j8q=8Cq\Jt@R$g7V%:(4N^U %@WD"T4[qiPHS$sTjNGSn/L!(e0nAp%8C>%t-BK%RB"s87B`NER^'D6RoaORc)0.D^HSAm@NHMYhA"OqgFj`>aEAd.* %,j"`n;fOoU7@&]prsA^7''-7Ge?8.9;fel!qF_?)FO`f;T%S9[;'\kQc"olEjNVQW&lAtanYpdt)qW6,l%U2r?;(sD"K".&2[\LQ %Mi=<(P*Tg\NDARk\6-V!:ka9-`^sIk[J0U'8'-b20eVNP,c"G7l5g<;E.dQj&pJ!!;MfWsaF%MnHc9oh!`A4oRW2i;GV.l]N)$oN %R]JBM+Z*l%8-l>e5a.$,PJK!O%&tqT/mrU@H9="rd<*n(H>^j.KsLPp=+_K#=fkWTjD:bMJhVmKKc9A,>SN=(._'O&Pc?@'ro61' %-!d6@Ls.$_To_$t.^6_QbRt[c'E,Kn)@@*b26.u>6?$R/(?#!=^(eNNo'iW2dCWH^efM\N,YNSQip8$`%0fu&CHW] %FN3,K!Tujc+YIO]K/MB46#bn!&r@k7O@=s=U_ZP"c=?)fnnUmZ68qU(53<5`9HN4.)FlIZWO8dN8W %:0i38`.=?`6k+kc&JVBubo!n"*;n5=jE=fO`NF"X#o#Zg`;V,A%m2qERa7RH.;9NZ[82W[330e?3VW)!?]/2M9gW4.&URFd6X.6: %Z1a>&*uGT/QPXXNQ@:-AF("43W$+9im7H,XKgb %53?W8X/:JnX;VA.WF]89$?VK.2//(9FMeG3XR;kYajF9NU:r$o\ch],U35m!?Q#`:L*;1rN?"rPf>KsrkN9<8_@Jp'/dkqml7!gd %$BPHeA<9?pI*:.$Xb^[#]aNg %%]K@sNn`Ws6(+_8:\d>aWX2X^3-H.:>os<:?-KmVO>_1tWktgV?km2"WFT)!MUYKKZ+n-:A,qG^ngRoQ;cOR@B2`3QCF-uLOI`&3lE0D]ZfL/?&0O:L>=<*PNZVe6@h9W>"S9'i=0`+g %Wb*F,)_`ihN\NE0:H)gp'L=D-.Rd")>(pE^_>RZ(.R?r@kbjbcq,3T[dQ5MtGZ;#*8ppl:.fCa[cGJa%qfn=cRhDsBhJEG1JRKCm %&-2.:0Y#=pSm!F:QVsAPo9#_DG)K+kVF %WAE+^&R'qQZtXQHZHVtQm2X,]!8lH@FGR9pfI=ZeEN7ciMfD)N,4X+%,H/=,U.T_Yckq$,QEPt2MRQoPE]&^W8%/2L/VAl5,"P^"GMZKr2:K?KQZVho<5V.WPFZR.6OW\/?/<+a1bKuubAs&,*_f,SXA@D@.']\`,[sU*Zj(`NaUk(0L %VQ!8;hKG0+;W=dWMS#8C^^_G,!Dl]QH>`4pMsKS$EEcZ[V>qB)#_3k/99eY7Y#;po5agCQ_"kN,jG?RPH"B&]92Od6&%UDhbHN'H %ghc@nemqQ&XZHhl6Nt08<9s6Yb*>/"<`hP`W]_'t6s\!(2_h1:@V[?n$?U(PN!LW-_(&"Yg8WgTa\#f7WQ899kCUllR"9 %G-5_qHVTnW'e4$MVqXkd)3V*I.^bIR)u,177:;"qn4?!KQr"3^i[^-jqd]VC!e1Klhu.$-ljfJ$"2t$C^eIZ^0k8hg1-=qD^E8\* %&ff!\W&:GrZ:rpg##BC$-$dn((gJib1"%JSK(([Zqr5rCPAQ*+\9LZSEG+ARVF/WjC"&N:Mci]a;a8dDjD]_#)R1DhRYRrb07rgH %jqQlP"3smu6"\Xf>QX>ph3C*N*AB5GOAJDL^hO4(^?^d#[g(A3MqN-a?d0S$6Wfsl82;-]OJt3[Q:87F'!LH$l6FGrm4Nd*?g>C; %ap&DF_4ZPT$R'W1F*kmDs9c=YeGo_#P)JZ80\e3gYdl.!4."=BQD&*$F&mcH1'hA)7eRJc6G5]iAn\I4H.Fu+A=*XCLM$,Bet@@]i+ %93SGg\:`'6JJboAaI4pjD=^1E;(]X^bR.O5@&F'=+^CHJ(9e>Q0fq7iP3F;KR3MtHTE=9gHH%QVo`5=-@sI+<@rRJT+iBE9YB&=< %db-Ghi<878GR(KqK(&T#oEC.U8XulFfchhB;mb\`T;29Z;]!n8dq?TsJHu?M02R*ndaYB$"=!nZ_-AIOpFRJ8TI%q'#>GIm6-Cf; %#F;nDe<7oZOiFl&hVa@:XqGZSnJlKPrR-`i#>o>hEue$>Lj#@U,r %,&!.s:`O"1:%D54S;nKh-7j65]PCXn>SSdb;LeeaKPcECP42lfS\d,[cu$98@U#RuDR#2-f:bYV:i!7mBHP]^3oDXd=#O4b9k"J\j=6Eb;k-7L3PQ`V/\HNMB:..J9)-K_=#E`-IdFP?ZdUr=gW0*sW %C=%=N7Z3JGAoLQ/=*%uh-3_U*M['mok\aLg*9^<%;9Hb9&`.&D``\B;\UTOJJ8WkJhZ %!*nJ\<Pm)+W[\&82"n,\%VhNV,)q2U*A1_j;ll_C'38(3`DE2`UpM[VgYAaL`P_iM %@:R9W``/mL/Lrk"PeVj@f!U0X$%1$6>Wtl'XI@i@hG_2/]%`4#8Ff0#J/j3ARq9#u]fgtoP;FL<,XCf26?V*7&lr#\6;<'-a;T\I %Wp,RQTpeOaR:=XndN(iX('o.e.7g9X0au)-\a3A`m:43F]WXj:Ho!Ql>0i9\2ju'q'nA=M2!Doh(27gF(aHrZo$9oliH>?&9\pamSEN4!ADNNe!:-0pH`MJpA[HFmKd(&0*;<3ZB9d!$Tbs&-Tb`q$,J2Wg<-H %99YccKi?kaS5k,%"dM-sDS,uj2'AgGMa]h\O=<"p]Gm1Tr( %A8-29NZ4ONj*AgoP=4HB7TE2fnBDStIorF5InY@RVkFZ3A2bt)1k..Z*F!JLN)a!?'I5?ke!=hnaIgF_APhEuLS*H5?1_oYM",TeoKf-%O\$9.]8r'a0G>Y)S %R101h$B',?XM`XD15Oim&HkP^d!KHd6V&FVF./r]cAT!Mr:c %?=lf?,Pme-o9_Nn,cNk`R[2qZQr=$/i;'%AOHBcD-oDlMR/iHs@nS-jiiJ.olm\PL)]OP#"/c["(4,O*K_Vp0[=@JS>45daDFa %XU,U'A/o?)E2%te5`I,li:JdLc_+mi!VYEt!.OZ.C$MR)h'+tEEHeoMOSPc+T=j[^/N8q(Lm$^[7&Gk-Qt"%Ogq>Q&1?+b\<\Z%T %Vu\UOCPFH_'huYNWaO7&%Y5I`)6YbQLbRRd3dj5@bkXkCE0Ai^Wj1oYX?].A59)P"m_)kEG%@W.ncV]G\uIrKS_T'A@%\nMN7E*m %,GcJTl2igAlf52C1'ROd<^D_[Q,YH!-.0Dlr$Q"SkG:-Ym]Vk]%*Z3X(OP(OAgoDY"OVREo)4P+9S?GGYRoh<@Am*WdG\ac(r-sh %PfRd7r\Vcu=^3]#Z'!RUiR1\PP^W16ED3#?1YQ6.'98X.bCe3JZ4K)T<;ghqbdpG4&n?PR33t>S^/P!>L3b2,YoAPPip'L^gU+/_ %]+BBbfG%VVSO$AO/mG0^NL&Rkmii5pjb0Kk<3[F,b]5F3,lY[F4?@03Lj[CgOmsprVaI7rj;#e48/0<,^lL^&cM)9T<(Kj!M?rJr %9$nmk&YshH[7[aXJ3ks@=g"6M6T@_4R<_mB7bNVRIDNIa&WJCV_0AgQY8TJ %J'bT,H2KONF^8X\JBi7m0[kp[@Zr^c0\,Bu4MbiXM;/Mu-7TTdbcIu$VID2F=X^$#dJi0qF')Oa&Qii>aU"n0J7I;q7BDMH;8=Wr0uh=d10$Mg0>A9W=%a-RgAh&0!t3u:(s2odS;1Lnee?cp1Qotb7G:A!GlBILW3?'6WFk$\JK?'GnserB)5M" %6!1T8!$S-MmfBG'euj+MGD71V8Q1mD1O/BM]oL&^05ib;(_5g9>+6^GMIE^tK",h9XFUdq"uM@48#`)U+[&h1bN-'6c4RC=q34n1 %#MYPqW$:c/DMo^";tPNRS:]::Lg[bu@O9rM-l,l'j.^L9?>V#Zb_d#c;bEW`;;dAK<0-s8[@ETEfUOV6IYj/@1$l,8@PLG(e\iWI %+=\r(Ab86^*RGJ/_7jJ38JL/g#0QB,3t($gCqsQA#l1&2CAa] %8h*?)d=II\F\>rcWhRb-'dLn4J^0,rZW,@)7CIg=Ch>8c(r#e_9<)unO*57hfgbTa6\3W=2cFe%->8lAW+\Ksmg$i#Y-bJCn*Xb(pq=2+Xm9j?Yb>.n'\ESh]/NSd+TT]R6gW9n>&(94Pb;GhrI9TI5iH5@7n %.a:NX1U=p71GJ2]aWVA.P(t?t,@Z92ror@l_4AG'mZ&rh+e.%s#T[>Wd/G?;j'urY;\:8![].Ida#=DTi'4uWV,^YsN8T1a;_h9B %H^GOT=:[EU:#5Nl#9VE+l!Y`uAIW:Tr_R+gU:+&ieo##;j7RODq#\24@h5ptj088CX^MU99,j46o;8t=MHGG:O\>&*.Fu]L%($m] %.mZ\dLX6*RB:4UH$f*%IT-b-r+`Xs.K&D"[h%MMS(-O'ML,Ca;Ui0"0RQkOZ/-6j&b3GX9Z(`hk;qK!C6&Qf<$;C%1!Y?Pr=II:_ %DcqEnj/=C!VF7KA9n'&;NTTp_1?/(HQ>!?pOA]&_M_q&*m++X;RpD9FbYu!J]N5Q^]O2S+)$'C^,4j7tC,IFsM)`jKI#DI&=Re+G %@nU]V$&94D3B5':R`.[-r!ZM0Fpu`lFVqpF.9\8c_\%#NGXleDi0kiR)C4"8-s6HkejRWgeT8<%P%sUG-+>jci/cj`\o(cNq[dcS %0S&i?KmM'^&hS5/GQ#ffh3g4Ph<(GNl]X$cfG(9M&8l\YPN&rDP-^G]R4$7U%paYr'r-6J6eaepdmjR$.mY7<$4rr5QV_Y1pj)2' %Zat3:8;:P,`L;cf.Ps),K64QCB_n^sm\-"'c>S/?W2fG4nJ0=3)#T$A[]bg2`9D4*MG-cF^..c%m*BoeC"Hu+D`o/-n6-j]J? %I/e5o1NbnUD*WmOIT[niDgt-fIJ(7\oa"h@hEI6.14\Z^fO%4`L!1F:MKV6pEJ$1Jr26glS&\Vefn/B*4j9"[#0k4H@,.t+@em[L %A.bQ@IIfu@QLhQPW`@='Yu>FP#W1lC!AQI%p,*(.V^>7%3$H6g`8t7=.hE`e(F-!dB&^FM) %[Nl>@En5'fn-fn"V%q7G;N^SlB#+>^3$J&J$QLKW=eBE(DgOlWU2g7"ko5:h3Lq.U"9l[W_jGl,?pJu1$8_g_SR/%O`VIpf5<@*B %'PigE<>5jQMYQ@1^\l3R;HeGm2GgeQ&iqg]'+W*!WCm)M.%G:,<[e%`3`EuIP2cD\rkUb8]AStK2GCObcUp"[&5b-QkKs%+LbN97 %S6<&7U;>IU]R%!._*TV!=l5T@!Mn#d@WZ2>(f^Rjg#?\fm;Vj6H2"m,ak>1*9R.Ug+V*@fn6uBR:hfjV$QOds0HM-eanmA#l:a5r %nnkV<$BX>g7TLpt_9s!G/Rf")R%eT:Z=%cccDN:gG_fX,9k2)&dm<=-jsl:JM\Sk1_pHgDp`#qR68'hQ+2=:DLdjYd7o<7mUdG!h %Z-Wc`@?Cgh\pPSJ4oFei$<"fL85MBiKZ$iBQ_HqAg4i\`P.q1V)Z[/p15oLuUqLP9?2_`O %;DCh[_Zr\=cKPCeU$p'EaKSJ8,"\cZl-O&o9?CN7F/C33E"HH3Z`n+";cnQV8h"b`5.&jeQhNq;j1J>Nj6iHDeg.-oW^rZRVTr$m:9%WE/tKO=9K&^abN[4!eTna7/_S"!ejYJeFl55dM$lHD#.K.54s %DTU]/EPn7^SQ)dX7%*>!6inEeFjI76S5.DgSIZF1RSI%2B-%2[D5h!=]'dQu %rt)YE=$8uC2_L4%`s#ZQL6o*"O8BG)TATXe?[Vb:s-:@uoe6@$^\XSfs8K5%r]gA-J,738r0r32Hi+<&?h>F>Omt:5'dp5l548&=g1pH(o^/j^+9)0sO+7.9O+[K*>Pn,4^\r*N0n&pp4TUeaK0ET4\TJ %n;_fr"OsC0SK_WD9JSjG@1M[H8/J':-8\7*(E9bL=?rOuVAP&!bE0hlP_*LeB/6iM=3o*q5C`[#:dr>H.$Y3k7ZI?1<8mm_iBE%5 %0bba,s(l#Ss/`OYRe]4#+OHSmKd!`,du_rFWNZ%YA?#gI2K40t6S3,2aZE&SCKq,o#V!+I\uMN@3grnYj2_;3TYdFVLe+cjN^;[S %0Qh`KQMRE]cGE\JoK[)1Rl_i;+[A3+TdaRU"RH,"TWhD[\KN#!NbFqtS#>PR&VO[PX.KXgRNSO0'B3]SWYWTHAD-"%`S$0#9GYte %G@Rj".kW=A)q8-1F%@#aU*a$nmKdq?.$B1*Yhi@>;&j%UD,I0srmV(u1dLHFFQN5)AB(u%j:L*QC?0oi-Z+:ob$+\]4AHHZ6Zj9b %l0rt[i5.WZ`82dPd$!U.:kTSF(C4-h!iM]UdgaHJG1;%eT$lDh3.=5:9;%\e7(%gKie:B`7A-0Cir-o[_-5l\0jJgcT7<1iJK %Bgh*hdVVLOK3I]k]O]s)gH$0<92e,1C;H"QpTBHc#H8(&:1,%i81X$3t1#c'L5V8`,<]<$YLT; %4ph:P61.A\<])[:G-bbJOk;ZTU?+(A=JF_+^mr9m5!7.G5uKr!LkKm^>QR%V&=iq+0sBIVm=OmOg]EBp'#laX#8uZ^b9m/r1PEIML:/f(ga!nI>hSMIA\lk3#6^pN.p\A %6Pe2Xeh,dE+?1ZS/n^&`gFWaXYi$8t-Da08A=9 %dTFZDn.6TnjOQl8,E&h"7WJ$G,`TO]fOFKoOPRB*e7tNH-?$+>?H@(+/[9>""q(rN>Y1V9=qFtQ=\!F=+DpfnYCj91DJ!e8&1mhG %,a,ODkq0\,[3Y.nb01_<\De_T!'`V&oam:'A]B0V_AF-B%<;LCdN=jEdWj!n'b3)uVeK %9.'lEWf0*A1itVY?e\a4e.gMhaa%Fn'J4JfdTIiL-,g]]1,UE+[,K*U\lrdmYdou]E+e-c&VHrL'eCVd?.WM@JmA\p0,lfP<'PGN %(WL(Z/?H3@"KMWi9"2.FP]=AJX;W#K[r\F7X0,5'7uSYSf3-kUPj?:QSgsJ%02q;BAEB"T\YqWX(\,D3>Et$!5n:C`\tk<+iX&%M8\^>=cb7;r)%E %\?'+>Vb=c2jdDd6Zn=$CCe@0hUj](O,7tm(u\`4eEnOF,'?*;>5P]W"aGOIf_GBV0ht--)#j6SFj?Uk^65DjBD*,^nOi6( %Z:DOB\LT*dje'<0$A@7#,CnKGCI4N/b;OAlq^ui&ZhDq]T#:tXTP=>HqHCDnnIYoYN;1"($$YX"*/h8$Cgcmd>5[VAFj&6:mQ4+H %mP,\ebS&%q1_=W[MS\NHJlcle4:NgtDEj1GcuD.SW=dk,`NBuo(_Mkkd2/RkH0.7aMPTITII?%+et/cV_X$af0b^$DKt$l8a)3QT %0rnLnU>]hWh]M/1ined^OPTc^W7oKjp*h3_qgTe4Zi4:$d("V^5)sC\i#9e4K#GF)5+%@e5$Eh5Yp*m6S":hnLO^qtgbS;/dVe@66TB %B+kBPZojB]JjJPl2lm_;F\L7ogSi>b8_L_h'?=F/s*D#AmSc7W/3G\cB05H<2oAgG3j"0>/CQM$_scisHIV0)M@aC\ds5'F$,I%i %8XlKB,2lGRBYAnH/R'ie*mqEFmr)eOq7ZuO=(iq"k!Y<%I=E[bj0t>QV`akKR'$5^ZqX=h/33ApIGjRg[fH1KOPnKu"WHeeR"@rFci%7`fm\#?cC%U$DK--&VWfjj`q5.Mt@-2u"cmS$\5?gg4ePJ';imA;W> %?,lYRT/'SV/h!:9GVOQ@0bi-RrgU,RoOm1oiU+W'*"Hsns$J$"54c>)/p.Rp45KX(_Cas#'>BoA4+E<`Vrlh8kHQoEr%rUC!QQm+ %,1(kl+\AnJ&bqn;%ZPE2`kFaUN+U"/p>J>^?OT)85?^I3?J\l`VHC>FJ8(>%HX325H4mTrLb'[C4D<_s`IZBU&Pl8oW0u3`Jk/R= %Pks>Cj_.W*YoS>(pF3+rk!BnJ@U@G]&J^5[gdkSa5c^-t,-H[H6#88BDtk@;Ta8r;aT3BNV;Yc*]:Qe9O2$&dQ5U.]+KFu1G/S^!2L(W_#,mj2@7N%G2IdX& %qe:@2V!G6#ah4EI['at0g%(Ld@d6G4``&j:k(f2`X"I+7KcMBk-?',FfW"?K<_p(a16^67R,;HjNT*A"DF2j2s%p948YKdi>!pXD %fcq^?;>I?BDH)fu_>;Qk^M;XQ.0+B.8RcnDX<3l=>i)$CnB7]XJA5hp;?O\U18&RA<;"4eampMVg8=9one!1p+3(J&hZB4!U\ojFG#J;ScZCnd6r!`\%Qot52b'tPEIfB*M#jq.nl[MsL>Q98HJ"E77XCh?8U(m(TsG>M,DJaYWYZ,u&:C=B %h:5+59qqkWII[V"WL4]D`AQK=N*5s/glTt0(^XE#lFu0#*NQ-J>/K3(LmW'=-4mOl^522T`gi"*`J40 %bbQ&_1"IJk.]c>m1*T')5]Vr7"UePZ,..Kb%$M+W:fSN)^SDe/J86?-L<:A>bRp#(<7_?B9oc,XV0Uo!a)/OAK/FXSmdTFYkDpoE=5p,JJW1#O_ %aL0I+a`.L(I_W#-9UM8M2PU/[H&%Ilr&5*0Gk)f %[L@9o3"Ibi'%_hk)o+]q4NS6lWuPiecDs]]-BXb?fnG'?C1&]NWPCBaEWM3;C.cD)HsD0SiJ3i5!"aZRVMb$q5Z^Z]o(oUG-N<+V %GVr5\iPGf9TrDf%At`hCUtTT"E[$tV2#pNG:`s_@@mX,LH)UBYH=*dSLJ;tqAIlSc`s[KB4\rpF06d][D3%&bMq;eWIp(nghJY0) %5-ek/]Nt5s%bMh,U[2>nRL7@]?0](a1(hdfT$ %m;:E8hQ"aUf-FP7KOlc1>sM1O]TjaRLmk_`djRX.(MR?A10s#SVsG%7TJS=qN/5J7g(_CU$uNg=`*W"9ZJLGC%c;pcfL^62$jPiA %]5h!Z-R25@e@4@3=Yf5oOS5"*CGTO6I+S\'bjb?l+:+2n9VJa-\Hrf!H*c+kZI_gmNWt/0g88k_a:3+Gn;qUQc %P>h9dn[?5VIT[.W9XuB2D!.jm+U((1kZh %8(UbJJ/lbX_Kck6ZE5?YJ;]9q5E=n/EIkN#7q;GLs?^K%4PTp@Es0\f"pNdlueSbO$dLfbs4Su+]3Ks6m3>PLV"03 %NfWqfK&%Jr;O%ce4hB2jggl=$*+E8"fomkJ"L,O=I5?9Q9/GQ8%d)R+'4e(E6fN_bU^Oj1j()n';/<Ic^mM/M`2id$%Q %cJsa50i%o?fj*aE8't!Z"g8IXk$KVK`2=!NUFJJf"X"#VC2a[Rin=>K[1dilI>sGj\I/E6*/2 %IPQ?qgbf3_g`fL0%+$!UN+TA5N/fN!<2)n)N6d4Ql0DhUJ>7BD&2BqT#HA*$Z%e7[d"7F6l<)80M@;6hNb\c.;K7'i %-uSk26^X0VGAauQmA;_G]orugYnC1%\+?=o2Q',jh->%8[PKR@8KVVXZ5!9:NA2PF[6?nlDkR3X>fBRA7InJJjJkkb8FX=&$6L=t %@8h_WiF(eP_DsD:[E1*jY6bBII9f*0MD*ZE#D5e19P/H3X5u'UJs:I=[$j_$5b@E):sGfoq:!APs$B^+2EA;k/a3ZN,T:b6CjI3R %Vq.&)1k&@ZBG4$R%WK#\?Cjk*_6Q-?3m.SuE/?jY[B1LB"[_+P]oaKd'@*Fn2AumO[e+?#\qE&]en7*OO^@KWnCq@#6JEpph$LCY %S:CL]-fb&q>G1ZXrprARm>jL1h7#WJhuE>HJ,#N5ne](=F@9\E6duVnC&6q,F^%0)A5hK:kGMg>[W2b9Nt`b:FD//"nsQY.m2"/e %qmEluF,uZ%naGSA=BZ;PhiSUK+[sM/\s5;u"*T(hl[OTlr6[QdSaG0]PqaNbRjQM[qs>5"N?Z&k.DFD(LZ6Ic&+50^s$cCUGHo(l %23gt=F#HaJE#,A<53O;&AI$3Of5u74:!YY %+!'sLDa3.HHF9iuLhhOI]ME;h/jm

Zl7u@d[(IS`Kb?`h!Jl5]NY*#j]ppgO(6Pq__HdHrp6o"p?dM0 %I;*7DB$]i)"]IB:Ne).(4s.aC'Xd_o=Btas_>S7)pR1EfASEj!@O@UTj#67f#1J@Jau'Z9mh]nDjIk5[H,#Am$qtBPOqT2=h6\TTb]^R*OCMUM@Y`d[;R/Bn,Hn@@Yh5K;o&<[5?=u8:b9BWiN[JDhCnUbRoU224!c%RW#^`4rS^(J7NJ1n %5999*Da3.Ddu=jd%C\gt9^h!DF^B9H/]XBu#KM/'jO9[62FLu[AHklWgbPkg:aM39*BObakDtZ!d^Pk;p9e$4PDq=#i2'-qLWO$k %]rn=6WIOnL>l462rP#]SDLFX8KgYR^a3)%Y+3#:(jJ-4)mFqm?qq=5&EnAOfnJV:FN'a;[hBUqV:IjEU[h%mADt_M"IfK28s7RQ6 %oQLh[8[8st)meLO$s7X8l0*bW$C=9%u8unr*cI1k#Wps319[pZNmGIiY[->J9Oq>qJ %Nk3V,7iil$:]R([[*t0@@MW)rL"jP_J,G#AUR>(T5X_T^73C@,FcGoE.KXLA*K`Yk0B\W4;p`i!ZY$YI#SIDq=-lN&!lia-/qecf %JA>tYpq&=RIn0nN#XL$F5qUH9i0j_[a10:%L2sbC#e\bXXH&7(j(kq'RNcY_AGW]1!iRcJ,Eb]`O %!7;cA[&X4db]7p%'UYSkY4c=FL^C&*4K1I7FWG=2ies2HAS(u@Sh1PS7)9p@_%Hp<0A9ZSJ<0pKpB2)nX>tO3JN(DD>ZhGLr$74e %A*e9d6C80K?:]W%Bso9j`LPNkEuZT&&4<>kAZ)gUP7<$BFTJ>"U`VGRa[k^T2_5K]G;(9K.35(8!5@*O6ic`jrXICQ^^s$gg7+I/ %J.M&,/#]=G43^kq]1C>V>s`aW(KHqq[1sUW/rkM.Bee($9\U./027sUbaJX^!l1#lAZ1s434Ka4C=0P7@7hr?MJ,RV"?MZu&%oLW %n74u@cbS:->CpqCjpJL#aI,"6rJeYIR1_IE%&e8>3sVAH(!)aPXs&pR;@XW9L=h;JW^gAIgmH3i;H.6"6RI4SUqBq>[5=$g$rLAp %&N-e)!1PS3"qG0>QAfnl8!Vp"LS(PTIP5u9gqCnsLV-j/Gm!Ycbs2R)o,BUdD)2Q"E#rna,hDGnDHOuT^gM)_\P'o7(2_%iIZ9Lsd'XXZN_$#T/se"%$.NB%8M$d(HY %Xm@KE4js8l[40'uNXJf-U=9>$KHV)KVk[:d9OUWhp=!/UV2X!,;Y4]qHCK=K?k`Sf.H0W!bO`NiP`$hPk#'j=S2DCH6G^Tt(i,o1,&c_PJ\GpF2GdX=k*>&Y4C"HD\/Y@ThCp;T-gWUeeC:mp'pD:$<52l'8U8aFiFO5akl+fC[' %8S3$rWTqMM$l0<$&d)PJiZ\)C5[-Tm$N@tfS&_id@dDD1"58r3CtC'P`]]D3-\lH8?JF$S&aLcH)7;4Cg5/Sj#):C!(-=*HZk1$G %T_[S(H/BlE\&ngD[6%BhdQ]'%,ZnaG31P;,#6gbKg4"4Nah;V&s'_`>`Zg001'9^JM&\$Om#PfM,2:kjAkKRjQJem%JgG9_g&oo1 %Q*17l&2U_./W>+A#>KKjOfD4lUfK#*j?6X-"pFNd)Y-j!jCQ^Bu6!4]0.++3*o^S*kf44VbJNd55 %&lP3iHSP%VgqE77Q-PXs_#Y)l6ESH#aQ*LEgdFnk+dU$$)9^iY1bAKr+@ko(o/MTV>rG69?$^ZRekK*MMqE?MS8Fmo3[-pIQHG_t %.M'IF^XAp);?AKL7t5SLm[od2p<>?#S#R`d.Wp2j:I2k5ns(=MmWf/#DULtggVY,0<*BSbc^qFm+?2clp[*=(5VoVmIhS@!6F7-; %]9l:]XP*,W%^[\gR\>fPJTao50gZt*e76Q,epoG2Zlt*s2kZJ%U%FOO=Sk7)Pm!oi(TF'k+8[K]%cW(*KhH.V,[c]U20.[bA*(R> %!0u"(6n:X"#j*[3eG1j[7dk.s^@rp6f;N2U)QcE_IGm3qX;W@LJ\Quj1VInGP"`+'dSUg-9FRp.ZIT17bmB9[=iDN(N9_Q2dX;3)#p\M]6iEYF-_*e$rII&lq7m_e[iln)Tk_sI2oXf?rp>9R1k_< %'a)Yo?gQd7BCl:9\9AL$^e73heWKIRb1stl-UZW%! %"JGQb[s^'.@T/t[-J8mma5YYr9AEOCL2\i0gd';jSJYQ>SLdY!p\W<'liX\X7SX9GHOh+kDU%:=0lG?-n-ELU+=5"Ck>;<`[Mf&Y %K;P8-5ED4AVTL]*CjOu/9KH[;Z?_LB1klA&WPUl[OTH(OLc&\8TN8E*'!-'sjjErRH0c&bJiH4rU(+FSL'T<$T#M"GP#,s8_h3.2 %mapZ)U-X2G**lPJ0fG*tWpP`R,c"[&(?;,[;`.#J,%dd/M*V.jH60OA%:d)f#fd(b4S5X\p6OCX3:Y!44JdNngCfJCA17]WnSX:^ %UqMmTH7EqWMe$al8miKr3[BnnB2^ek'iS.s<[4``j/O?jVbp*9Y]mYHXj*^=MFu%6LYG4W"A+[WKj?),aV=^@?G46j/`LcYgY_U3 %,;$jt(8YG.1CkF4'cHYe9J#)IIXhD=,1eG%eu++^'9[Lr9.%^P67pg+/!1('+'LW8:.[Cp0RdHK>SCUES+FSP+UDM;o74,CES>@2)d6Lqikd\BL*]!+?a%=pYZ,A?iuSW.=GRkOq\^c,eX(/#Z!"3>!#NAl04\l %;U4&Y+&+S+:`g!MMua&87pXNT&KRL)"XdK_=RQ*%>i5^1HZ%T8(&G<(&@s))(Af]?rmn;=<[5Nr%9o]$Y"uqReGIaul"X',/'N8W %[*$S+2eSn+q`_-=!:%-S--15k;bM.o@8mW>4:tW$6$N1h,$tE,U&uCT"6ia_iZN<[6l9a!Y %L1,Wa+KKbo'?8aVe+YTs]OW4<%=0Ad%61IA47VV6ei= %S9MdB2Fii:B4e:4b:6*Rj6Se27'"$+GB+I3s#.4OncqoEr3H,p;[6$Z12ikT1`>pSF0\?uh:DJ@"-h/q.*=7n3+0ic%R`(?S7I?1 %"#VnWOqj3a,Hhq%&pP1tOYf5--HkgX;YY$DO0(Q_:%*9nUm,"[])*N;@:Do?\)[Knifll&H_4[^Nd:NW57 %@R-:io;)H%!'2bAN^19%ito*!c,*22BAt.5D(m(#K@2d(GRt0-KkEd#'^u-B^;;%Ha%2*_8P0dC=GJYH.+P=-qGJNA;^dj_4I0tE %WDnKaK)1Cr^0$AhksAqrCB'Ct)O[mkFHgQ5nJ0]'&G9?>.Lr3Y&2p5B*Q8Y'2B7?>,8J%-A/3]b-DKK_(!1*nDm=KG/It.?3C6VL %]5D8m=>ca&2_L-%3(:[(+;$'5D.Y1m1QV2Q1),kFr[C,6[qemZQrT+M8*6A`.^$WuP%HQ)KM=M-%m,REc/W?,cpS^Up*@nd%ko$WIgfp@X5Hlus[VtJckFqf]RLO4eB._*rSTMo&7a;$C4_1)qeqNe7\KEu@ %%M!:.m?Ge#[u`hNc-_YHEN$+_*]qN=K)B@]?tY*X?BTFV+SnWs;sDd^l[Dj2#oolWnu%8c_*VU.9$>$V)i;7Z@01`L"mVMmTWk9+;f?jd0[A!'k#r/=ci %R86Ubf#9#UE/ast8l %E]CCs<%RWW0='3@EcnY'A6+KJH$1$LO;g"=Fc2q@R?9bGp&tbfX;"uk,F*Kn<#D`B-R,>j\Cc^2_L<"j'onge#OeH?h^I+j:m*uP]B*)-:PGm*[*K,48F\I)i[g#Va06pl,;/@i.'p9"]lc-ca1"?S7EX_r"Z2 %IRKSIe=;`qhAP4.KK>`;S=/s4XWF,QZfu6GUtJ[%4eQDGW>&c^Lc+5A@Ef&0%/2'Si+ZSoc&bgNr:(7UbM5o/.jWt_=Zc;*M. %+Vh&>7cOD4cBu'o3A>)AC&7n$EEL"K2s:[pafJ%+cIA8YrB-jbPh](XS5=_b_*($S#t(*iY3?'lT!Z]4Cnb%dm+5bLZt%si&h0;V %k#"'SfpP=YN?,>2O")Kp3O=Obl0`0s4aJ$d7nb(F&N!QL`tUT)XI)8,5ajKW&4^*n*b'99(6RtOb^+qY8`tdUM^,Z8a]>qR[Tf`J %&)sunC(6*SiS(F4nI!i.TFdf#^d>1?44q%;0P(@^#H^115o]Sh/j2_K"8@R=#^JH5p/aX[F\mQTmf3#g0$k]tX<5M* %;Pr)3!qB@#OBaa!P(5b.,ihVC9mFG2SMf2@B_+Dq^S[9K/p(M"[anbpr?sIu)FkBG/7RCPYsV6pHa!X0B8SQ,<`32:F[@CA0=6+5 %,:3@p=Si'g:]KpFJ+kmOnOre4q"^m5Kh#(W00kaB<:CF1p6SS*@R&\reZ'C)3[F8L%l+4o0DsbTmD=4Hp;>BM,t1_.[KU$4o&If] %TZ@2jX@hm[VD%[n7"_IL!_GV*rlMWbfBM&=-AY1%cV@i+\DR8;n?ob&!M&l4 %H/6MY@qt)fnbG5#J,A>@XY#\[("MbP#t&9iM5]9%s0#=l5O%hgnE9c;DR\b_\&i:<].1C;h7Cbd7<@l8+u)O/D)r(9_g.^],E8%`?dFi%\*9Pqj#W5,VUV3/9@0s3[r0YdGI(kX"CHDC]h187> %#UX1M\>'*E`rL,JQM5s,KfEG3[q!PCq-'C\(,aHc,&-QoMaYV>mF]ead,gt'F,lN`o>I]eT'jX)'LD,-D=MoKFtEN]M\7FeBB15I %'5R-\3/!*"W+7`7ft'>Sqel;qQs)0!>/4K"mSEM`UY`^#;EpG>Q0UOW7]&^RrH:N:593WmgrgH=q>ufSG;R %oai:iO.1KCLg7u`ch"R=Tg\aWHFgJ/Lg8!3LJ<54aR(-kDt**:EZGNJLANYU54@?ZamWJP_`I[clg:8'He7Xjg,X?VNpT,Qs,QBI %\F/PK)#*4i>/[IZcWKVhqc"i!n+2>XE:Wg$LQFBIQ8Ua5TC2@;#Vc_;[S>uO9VGNtm3/;;\IDor_3Z1+>YX[74g(A'q^uX`C%AOkI].`;^C$7t`S[\r*u/@9(>9C,L^,_5A[OcAgc4SB6bVV=qj+mas3>io/oFZAM]b4sJ_''B.;s#ASaaBG)+lHrP?(OB`( %/.X((D`jBAMS5r>!aZ![FY*pNV+L45(;Nt=+)=[,'#u#_U>>I/qgeG_"(Gq/+@1i83GPsP@&A%^9,Cn:U8i6ZQnL]lTPdB*<`'2, %ZN,4\c@A0t_@4-fLg#1bXSc3D3kQaukhsj@[uZC5)LXk34W\(ZAu+!`_*Nd*>c)JQke?L(*omarq_2Fk^XnI^r;RcYA<<^,0RHRq %,(cc@Ma?pVP2;uO%'oc\3(HrO4cPJI'rI'a;:de,-\iSc!SA)O"rk?/5kX?&Iah2N!W5K;?I=p(&B%a-o\J%W#I-IW/5NgH8?EJn %?FchM=W+0-Qn(W2@_jbI0J3sOKbtL.$i9M'40QKjM)YTT#+k;99k%o]R#5BB8il$n*(eR/ALipoSka/A*-5-S8jdn:>ai.M_,u:l %%#YJ\\mg5ii(HQ841_pYD#e*f(>fBBHej@ZhmJ(C/RGfKP4#,IAYp=-ND?Rs/g,)Z=hn/(;Cd%Xc>.6'I12nj3KJ9$0@(M;uD3-2LG@XT$GF7#Q("NV%#f(3l%A*I=Z[rVKs;&-c4RI/s"ck"ss9t:UC"u'9^ %UYsMO>[0l_1(`04Mm>KJZTq/W>HqCh6T*ULN;%9G>p"88I.1S\Ss'W$"V6II*\UK#M.6s\VurktNk;ojG53R(N)[$M@_md0hF5`1 %obW8@DH_;#0F??jXU:.%)JKJ!1c['ZNg6CH^)^uG!^tc8#mYeDi9t%-Au9/QQ3h1lW\^h\h*sSEB`NqJ&>_hbFke(Wc-QIWJIPlI %=klS&b%$)"+&X<#R9E"*'"?5iN8fu*J[TkEj.\r'Nrh!;((Ki#8F&Yqnol%V-pV4W_D;6P'^HfRQ>@ %U'/0LqdA+/Ci!-BMIUT]YTaYQPZ25&30q'QHE8f+`/pJ,/Ei>(Pcq([!!'9AGQu1qZXmBGTZ_ou#7u=>RGMPu']:@UhJ5h*o'D<, %oUcuJ6M(2eJ'VNud(3R41?n[M;^'`qNg3&e&"*euS5^&s_:7gM>l*mb!HN0")j3\D7PLQq#2![2T1fUhnqn`p'q@al?m-hfP`;B0 %&G`.&Sr?Oh*oIlO3cI^l[b,Gj,H6SX %2kia]p"LsiN+;dfC\[d.SMU"h3eoh3h3?-fBUp`MiSbBE]%E]ra=Q[Y5*'e<6d$$2d'_`+.5-7VCd9kN'\#jHN-cae8@Yg&)CRc$ %#k*?'!p#SUL>uhJ?2G%Oj80f`YCY"*[m3(h9N=[fDUlpV"DQS8c"J$N4nOT,2MR9\euQX,7iYEle^l>`fV`P-7W>Vs9E@4;IXi!M %`7u7pKToqgBf&GR])6=JlTCC1)F*(j*lhm^KgL;4nZD&0!Gk"eltIhgHf#j,+o#J)K"U&0&-5*.0U#b!>tdB+h_m+5OE-U7/.?]M %Q-99r??uf$eFmS7`.r7k-$m\=??Tg[FVHFU9lnI0KkgbnTCDCG?u.g0Ih@0H@?@8!j.8kGCR1/QlBI^4JiC+/_D\Ki8b:$E9oDYT&ZJH\WId*-CJra!FR$<5&@6p_b:(kf,t1)d2CYkkj2l[V:gQXb]5kZW_e*&m#i7<#uTU`cmUjDr4Xq$bI@7dS`9SV/ggUu".1=.IksEU)K8T)O5=H^A#2 %P3"ao$gJgS,!['N&H;A?8dPsTu"Ih=V0u[r,]A_J2qo+6gKns# %3GR1'QF+Z_]ToqfI&2M+f_!tl0#_-G/)Bd-Y.+aHl,L(/]oZ,p[rM]=VTN+DCT6lrl+0jiHq0IJ2l\uX+q@VEn#18R8!Vu-G@6Be %;<@D/.duYNWH*?\b$-EG;s""=.\H1.o(e:q[l:cKpDW,gSRD6,`in#5f)UWV?>fMoDr;<;(P %03)oYW:KaRPrHDQ8ZOsYkuP7Q!O,7#S]02;Je%$EdU7nK/3N^^+K,.3qo*^0=OIsf2cY_#&/'is%6J+a:`@rZlV!3KF$+XpH@`HO %)QWkl9l6#/nT@K)RbrDjgD45ac,qsbOcH.3%H"?(^[nt:/Q-#)m^ZWd=AhlJFicDpX>dS;b3HLt]^@_el^[[cWHT;>moZjsk$]8( %?)ZF>fp<(uIV68Ge*]6-aY.29h66Qqh>0%\$9uWH70/_3V*eD"$rjeheR6EO5YgU@)E3q/_,-2SE7^S-f@qcEH!E.[()4E!Ch$%D %Ip3)j%L[FV;RN=fU11Mu,c,tG&#CK$j>@S]BqEWhg<5ZK2*2tE[V=7eNcoV^kcWalZdWgGF'+VZN=QIXD]2f]ak([,=h'JbCLbn5 %;LIrdVNXZdhhgY. %_^56DOgV)GO64)AYn%S^11l1k!=<5*q*lWNM\6("XV4="T3.PC6d?eN,u'V!E>ns"qMO^F8hE-V"-X^8ZSe]cjWoY/eMuKh%W,Oj %;cHt.Ya]*SF&JWi-f]=lO8$GP5e0UoN(1Q.E(n3elYWhusm)#/;3XO"f1Fm1U$dNF=Mb=Vp_CblF6*UA'iA"Q&0nb:,8?.&]#+,:\.qs'l_'Rs6 %.-:4\RkJCJVtr&UKl+E;]$,DJ %2]66lSEp[\/USrtrin4tlrSt4EYl&@@u-(p276lGBCE7\kZ"TX3d$oA+O#KT?raea,`pYT>DB4"Q'!Fi!3>uWFq>Y3-m8Mb:T^21 %5@OIFJ0#sLJ=5=-)%[XnHRs&IZeK<1Q?G8)(m\i#'2FW,T?P]( %*=Z.kMO,ebdjb"sR3i,.$PSDQU:&PgdDgNl.?n*02d(2.p5&"@Z\.0]Q!l%@d7=bFgW@$sqe@0K,2J-\KFNMu!`P(s_k%IY<]t@o %A_9^Tur1$j#r[$9qoc94jI@"e?4VO4d3tM6:OBa$2.V7$Or/rC]U!_<@)&V=HQ'n`hoOUh@85S&>sb9!#r7B&9V#IAjK3g[YI,;ifG"++(Zl)!JdJpCT#nH9e$oO[=Rj5*A=d1[*T/'?bdt@JXg0o.5#[3 %IuL*S%*UdpH-::BCeHZI/ZXPkY=OUKVeijGS:"fk=rqi:3SBhVjAmn>Gallf8)A7TV^j>/jafP],Y=pH%EV5b(Un79'G9IT\](g- %1i1h^A(Y<_e8e0=bd6/q..X"QVk_HG^t?ur=!Lb %Om4A7oJ2L>(rN6qnOghL)=PtsO>N+FFfA$,9Jc %dcpD_+LC3/$XSO]5D9\78q*_f3W6"VaaDY+9N\%.jcZ;T4@PlO="^Y[-kXkBD1]TsN6i%1AfT",L'4sOrL%=f3h\o>+pXoXM0iF%,)@'4=g?CJ+"edbiaVLC>BhU5<5#+W0P.U8Ln%*tHN/^u3Jo?\0QaRhTk0-='QA^)fEj@3,q!pWqH(+P,f.6;MY %aOc.,-,hTk.mo\r_XhEW#on^bqL`H.I"?t31%NftlN3^kBAcJ,?rK/YRV?Wn6!)Rs"V_d"/i-=B1X1S5`o,C@j:"""7(MP"2drng %/`ANEdtAHE*UDhCJqF'=*I(dN6`"?UY/#B1[>)tGGbo$=7CAg_$Z$u5F6X[HpnY$ZVYY"E5(N;hQY*iK+j!43SK;?297b;DcJ!83 %(2t(&#s++2N??1D'78&tJl%(J]Sjd"=USjV&=Z&9MgSc;#01/a$](Q`o^+6F^8/?b4jaiF6B'-1FKG1'2uoAd`<;H_q\o;Y]CSddpkXd#(5EB#(UFN2J"Tes_kr5!j%J$oltQYI(N@N>J4ph*bd(-&#p[(ed&F\g,e4E4Zm6VOqCq(& %&/MGgfV)>3I'9F'#O*Gji!mW$1_)CKg*Ln_7"/T\_;ob\W&V8P55Xlg+cDEq>V>EAALJ %-`,+Kj?t6_%*K1-@YJ7$(:4oiAVIDGKOsrRXY]Ru=lVGTXQ!OZ>p;4]=:/8bS;%n+_XFg>5CEMe9Gu@A6j4UbHW]G]M6FdH?<7i! %=>ds3:P-a!R[sY-:!-6kJui80qrG=*)VIRa5h4g+%d[+*3&lOQ.:%(]`3/ZDf?_JFH;9j:XtI3%*:W_W'Uf,2"RY*V5k3Zg%h%Qr %qD'Kr9SE""-_KjK6BiEm/F^$+=D+OS@W;R0/TOOAc;`'M%n"GB-m@1-&#P"4/Z-J"e7bC'4(St(\;Tl%_`L9J8mpga&jHtq#@&,- %Rj:oaPW0[+[S0ZEq4)V;E"+="MCL$C]1J9uIS6Bt'rYQa1N'`Q)!i$I[iChAjTgYp8fr5sVh4-g3t0%8(."bCJ`ara3f:XZbe/bb %3t./NhM[U#Pmql9!S;.5a1j']fgDi(3'iEFOYqO4;L?Y"%0PchTjYW^>$NPooa6L"+#jrGq'M4;;i5:?C*Se#bluPtcf;(0@>q=e %cN=t^(HWo?R[#]'1rZgIq9"@EZV-.\?=]p&-J(OO\kJ\4*ACoS:A-Kj0r:o&OW!+7(#,tiJ8:WNBQ>ZU4$s;qWT* %a+pg>L)&nR"=%07Zq1[`r1PRH,:js&&oj.ISu=hfejoR'DPYh@r8(N6q)Y+?aLb%Z))pgjerW!nYif;RY6>0#FcIUS]CUR<4((Lj %`&&bTc&jm+S0pglT0s*2_ndTQ&&oAe?[^1mF>ESmTZggbMnm]a[^b!Qt9`=:]%Zn)nC*%R"O+30#hj_>]8iDNnf8V8Xd93]FWQmAH/P&q*Vc1 %>f2@3@+^9AK9d?_'pN\.[k",#PDkF?80un_HO>pa\@CG_[cHJ/.K`rV<[Sbr]7:2NYII3^!#E'4qsoo%iX%Gr=n+e!]]f_kac8?V %<#%:F,a@?Ml&`H4Aj5a$0JW\+in:-AI^U2Pdmc51#In+XG;tgK_dgc#fB4>OB9EC-DMprYMr+L81e3#bq_3".!+?/i!$@&$WTN7Y %nWI)CA>i0cds8qGr-/dW@3^$\-\Ef2=r#8KU>'Oiqghm]+,<3/S*1Vf$8me)%T,B#C1_]Zbf3!P)_&C!99,%USS\hX4c@d3eGSl^ %Tl.tgamqPohd5!3=Y?o=1;2"V>Ykp#KchL@!m>O;5io%gK-c5!$(&QFG9Z>5XD3!Y]I',CGiHd.L-e6ZWZ)GbUs[-N`:P3hOlCUC %F:\i@:iF>8"Ch(KR3qgWBt5k2)o`(_P:I[AskKnu.W$Ep:1K";c`8Y,F7QE\%,)G<&RXWg^ %g4?oVg9j?aTpaR!ZT6T,Hh_h_miWXO'-MP\MOPQ)Jh%,I@n]*f2b6A<=o_6_[NgYaj@h&Cb_j(:7Gt$P^40CLnGfNf]`K$RtmRd&UPa&*97-$JMn/F8;I`F93mrMZ* %RpJ+(HQ&=08dkuM)B"D44$!0"4Zl?jf^-mHTQc#tq$n"._]323BtD^Q7,bDE0-_7"`F+YmB)3[)$ThZi(F&,ud7l`DB^bXnGciWm %Z@/=HQacK9/6=Xr2Z6@-Z_/ID&uT,']Id6k;P52,jW*`*0`Q%XVlr %.Zp3^JNHI:cZ45u+Xa,7@qO[3S)kXQH+'#7@cKfE#Zs.C,5@X9fAX='/D:+E2l@@Xm0^I>;oXM#k`3iK]Y=!IcJU5]#s3&kQ7M)b %oThc0-6r-"TLGAq8Ub?@0pUOMBq2IqKtoHQ.^;f4Pm@\*-4>H1h/MCa %WppZG=X8Eu2'u4*H<-='W>n*N,/'!=*$Q3PmS(hLf<@DuDYbR$FdD"b9.'6!_1Ll)S626k*XGts-5k1L9Tk+1BrfEH[%]8aU+=o5 %6(UZf1iiBh5FBQbh0rp:VP7)J'cG@^AS;HHmp$-rBQ:c?L_g(SR.%imj8S5?QQl[=#XpBNg'74/37_j-JX0kA.L>-rRbdG3Kh9JC %+[>;SKL*cG*e]+K\kQ>;$I%r!A.pu]aef";_[kZ1!^MO"6**tiFC\I9G]F*9aj0Hd3KXH;.hLI).aG@X5ITFMV-D^]6eh2H3hj6` %1iF?J]m@;^@LI[WNt9fUl&-I!j^S=1_Xr%nj<;6\OCFC=P-]1B3UF,'88pboi]cF"/K+Z+`H5N,qk!:pWb8!Aj'dd9YM8qZg#en0 %b:?'>XqEMLbmFVI<(/Yg'gbU1m8V^RQKa#e>l`-sh4>o:lAGFL['g!J9[WaM?t=m`*M$or%H?=C>J %RjVYm@%tI2FB*\TTD'8$#EiF+78"!,:^id=Jd3@[57%?c?]\/OE(=%r2hiE9K46G-Zmm6.Q'29aj*VIS[tVGma:a&cd@pF3NqLeJ %BHAq\h1-R,!U*j6P6qCb*"gUET_A)%04GdJ!OCH##S)4H_^;_#R %@?"%p>o(b'bp\tLX/njKS0IZ65h,:@,S$m;qs0!9h*:?c4A_A_f_]8e%bYKR2.`Vi6jHpel:3n(LO47Jl>Xf\A`S=(5dAHl9qJSI %WAD#1QP.2RW;Sdmc5GRDXVqGP#Nt*RS5-bp?q"CS4YWd0TD%j8H^5?)gfQq.'>raepd#=E9X4UtikNe/i?[ueqa\P+F\!F+[M_tf %P\3SL=e8&N?3Pj3Os"mX_s/SUaA[k(Y,nm9[#C%N[]1i$N?\teQ_aB9 %E+@.d"a-.I_0`f$*#BSdPt11a+iTm@+;=7I.G978,eRVnY,`WjP*A+Kn5#[c#n(TqEJi'Rf$LVu38B##ioe&>!`\I<&sgl$]4R1: %&S"Lp7?hsDE_Z&MaWiq,-H7Hr7LMd<1hm4a$-A0hA9\-&7FH#hf:\1MQWAh#K-E_r[P9#Kd(q//Y;iHB^-K9cL^F.G!TK'a_9ViR %GqW;NHG_uX9I4pWSj3XSmYiZ25tQbl>pIk\_"W,f!7Tb\;/`a@Z6ZELCHPi4,`]IQVTcVL^/]Cur!eZrjBCiUr^RRpP,PqIP:^`` %=IP=j'"3`bq[o(S9qgKP(4oc%an6cj*Q3H6GKIgoe(`B`+aaf-j %>onesp==d)!U38cO/EPFJfq@r*%\,9F1"J)'@4l$!1JcB/B4,.:Df$W*lq^urLO(S>("2DR"Z"e-AW>*$NP[, %%LRdZOge?;<4WEFAYi[8MZqoV=ue,YgI7^`KPqMa4rRuV_ZaqZr('g$Vm80t8&L"m?5Nh;&+PGh:HD5s\ccBtjnN$1]Jq4p?)/#W %/V"E6%k1J]J/G2O@6#cg9C?D(&6*X!%*dpK?)J+B\n=\^HC%NW9#rf%!:^]kOk$4BT9QG< %d69!NDXGM[;oQV7bXip:':-`Z$F>kd'V'5(-OT[32QE6`pfWV!Xb.uNSg\5[BOQ_8(on-Z"bu@S]$Au6.l@l4d;K%'o8S6RXcUpN %mi`H"H3-"%;KZ>q\LQahKKbhX&S=MpD'=ihG69Mn,\SGhO$[j:pMuRA+XDE'Bp*47nse3>$%)5+rEb. %jd<''Gc1-n4KMql.QWm?r.*\X.=Ob*d)f-@Kg6Q._!FFg&s.>1#@0,ErAi9r:5)9sA0Y5,Sdr %5mJL41_k/8r2`?4mYe&ZVZu2oMdr3Fc]s%-T:e_E,\g5Ra>d:S0%\WU;,WY%'kF>iP6&CJoibA^aqc$bK\e'R[MYb"T_W[m@0S1f=]Hn'Kb %h?Q;o=-U-@BG'jgOts1U(:gN/ %EM%,9!5&r0AMB4`1,p-*,7`1$-fbuc&a,Sbehlo@e*fP;B1(-nNRFqfLh$m]QSCipd"<#`[9OWX>cm+uM?d@.\tNj#VjAP*a>(lfsCC %45]\(OdDqY^PEM@Uh750+dgFQ.&N&J,#nT36a],*]E56=0qSI@.A.9(GL+:C_u.)pPgM"'B82iOVYu1YJ^hX!lba<[]&Aj_g_:nK %(kl^h3f`Sm%jlK60#lXT_)GqEX7lp:4';_0m'W&$FnO:*#21hd6eU"5"7s^1j0$O(]Q)-W-:VtL`"^%3"HTg[`'Tn[HJ"G7npD=[JjLc3WRlq0iVf8VkZEVr$(Z$F8"icI"$5hdMN"-n2G/t^S1/quu-8_=K %k#I1o$j3'1g-dNEFkVK]k&6,A8!P)g_C;CK8/K'9%W+2'a>[dDpG3G_n"i8TmGViC\k?%-]Tn1VNGsCcWkMpL"T!gmCb(F*_6NC> %rs_pdWDS4)VKV@"(o*Z\Z[R,kmRkbgOBaE(aLR`Y<+MH&nR+;2+*=ME2VSaBT=TSg1X/[;:BMf:1-H8YXFLDg.eBPEIbARl_2W>W %ee\+*ftH]Kg>.J?jDbK>U(rD(-as-l%l(\LR9a]%;cH5nf&h3?uCFd@*,2W1$!V-jmZgfMe:Mj,$qWV8A!X#'o#pt9q2oG`NWG:M6-LO6Aoq0=P %:%^/PU_^\P2dD(YA^8aspR'A&0rAI+TeukX%a=G6*5Cp+'OeSUK?eGS2sG"tAkAE399k/XSb;Lfa-oVG*N*qN:^?o^#"Fh_n#OZs %b#ln`,VW.q8<`bB-HUV;9(uGp!Ld%[X%g0!JC-0oFfaFIfa5;Z@MF]RmioKe9o %OF:@b;t+9P?]J-?YRUpKVkK;9:Jn[)K:`*r%NpJ"MF.TTYnZGc<2F3TGnkGN,,bjMctMF^."lZHA(:)aJ)HImO_EGSk-7:*^n7il %7Bh\0:_bUu]X]M*P"rG,5Il)B_D>Xl@"Q92@@(U4oF2kU!LO2HW'#D^*uEGGgfB.\NCcfHAXbS%qr_mCXZhLT88qG;@Ue6n"Km!E %&*\*ugh`O'fe2SFF.dQ4/SE4EF^("u-i[+Dc)5!aoI/M20jrnR&pDnB[]CDF9emI4RI1(A+*gT::$5Kiedro)Y[#bXCfIl1%$euf %QQ$!lIb'Gh.lL?cd^/GZLgWErQZH1aIi)XcGE`G'=dfUkPM*VY!qW.Zn]0/4DeI#>@NZ.4[FJ.@-N&cDiFg;B&VfnoFmcUs;*YMO %e8GJPku1H>Di-uF;$XM^#E*oE"UPq1!r!]BIDO=7/3[Mp]d$qKjWP %7,i+V_j)IsP;r-Q,#RQ!?VK"U"""<@6:>H-CI-sF&O:rm7?cIn^H32PYW@R5aE=4H*Gm>6:+b7JQr2q[/I(>jYu1n2^!F\UZThCL %Djoh]ncTc"=Gs#ua5Fc`s4gjp(K1bhO,0TKY:G0u>6\.Hu0]S_9OJ@BQB0E[!^]I)'mMcJ?7JZ929JfbO<%U!pN+!;>DPc&9 %WmT6^4j9Z&j"QZ"_O,,nKPW>76uD9#;['t %4\a(fiHHeA8S-)<<=kKre[sgk5gX>q+nt'Bq7IDBQ_du\Ma5\N:mh7J0,Ffd&3gc==p#TeLeUiUZY,1WJRZ@G-WVUHAkoG9/^H'C %0_5[Hf*``#o'5`EP)Et5aQUn^.<_S8I)g=``1]m#`YcW@]EqY=ofZi`)9gO5/],q1R^m/M/I@IMRVA-Z-0FJNDct1(X9)::9JRmO %8-Q3Kk]EBagiiL=11%;tD$:IG`h$>Y-k?2p;aC/>1Z26m1f*N!Fr0U%3>7,I94-e:0\A/A.APl3M5@I3@lOATm`Kl4'7k%9OSo0p %dC,cj#^m)bbL+8VLuXp,@4IPLe)ZX`'[Y5^d&!5OYN-E"[FXZ%Nb>%Z&Uu2@3IWf:s2Jj*%;s8t6:T9!uc>iK@"or&rSG19W,3fT6!-ss68R7)To),a,[JrgY-.5Wi2Br^GC.7!([9?6VhL'R(E;I(!ptQdS2W]btQ!5 %35WP)jq9?0ds.=4(E&s9TU708DLU(!-tSjHAo?erhS<8M2!('Pf#GilY/@5Th+4kFSsl$/GU,=01rD#&U4J[pODt\/&BJN31M\k%$^S" %j@p-sf1!R_E"YUap-&\"kL!4eO^2Z#Y#C%hqfapo[X]@6p\XO,Md2nI1Bi?S?s0g/IFeRg3pH?4I%$U'M[Zf2$l%\URt<$U@r-S` %=E`(m"!j*-B)tTTDBIcb!X$p3md?GI>i1L8pX@lGcqjg`C7H5nD)t#n]1f7Clht,5q,9XrO-VDMTUmX"fZ3Je50W%5OK$&(@i+eT %'c3fHegm,dakuno^1dUiqCW3mZ3&0Kj/Z7]bi0+W'U=Z/YrFn^["E-SVR`o"cP!`),C@SO`B]Smr:/k@JO?SG0+-'@8:T9q %X'gn,?-e`2/_ih*C[W&,koF*"=u5NNdaV+7.\_[mj8+e!q-omkp8PJ92u+02C%+%47F/1B2lDP(ld?>185N9EU*8drnh7b_NhP"E@%"#'Na]SBa\i33o %G;CpA$jI"(0Pkj2=U7EIZB$FCj_fa#D7;Z2ABHf6(S!Mnhk.X@:Dk5f1`,u7hed$dU.PIbEo"Mi=q@K,O0CS04gTSd[]HMerOs5G %L%hMDn^>&ZL[LEie!ngU1%f]ec^CT1a#>R^e.s*6=GZl^^T`_/EQ<=\=Mh'6meao)(]OeW1(4Oe.pThJnQkQAUQkPIW!UiTJu9Yu %/^R)NfWE7=^YcWAeA&%TDU2LirDjBaH`;s5UrRos$h.!,:Dua-8%\sbM[htD.j%?)Se%)\7"sLHbIj.!6XA5qX@YjZpH9jUS>,u/ %XIGq'<=4dMjP\G*D11Ddao-@[+MGU)$<6S/=g7%/O*5W>D4I.fV3#bU/#hUFZb.s_pm$PB\;9,T??L@@XW@VTO\VjA-"L-f+#sqp&7rul1ZPU"D]Bcb-+'C;;<,frlB>2\2!JFdnm9eF2Hpp1fhR!ZR2YeBi1*C %c#9("ZWh5@@8/sZt[@Z5.k:n1>O;JJa`o=21moX29(;&%sj;AZ#cleOgS@WHh%i@"sdSW3qa5)BF_Fq0BZ?@f>*=Cqke %>V3T6n065n`PT>HJ7>cOrTQb&@,`iGGDP]]3qI^67:YF?Aos4Y+9o2q3Rud56'H6aAq9,B#?IFgpnAYM3p>T1c-S0^)j7+!&anT5 %J[t!aY]oga6!h#Q&iCL37M^$.A"9&p[c)n]7iiT=;'%1tjVF=YZ%j<]eh3OOQ:d#1Sn".6Y][,+'\<6g'NWUi3YiO%D`/rmJX@T] %HnHK>r;$@+lmV%f$(iM:-!V:rjD"Rgm'tc!+A52&h4b`[p'rOWDjkknEG)55%m+9!:.hfdnmP_6g/$[%ODU&`@Ae)C[P7Tb0[nQQ %GQ=@hZhYat55D;*`V06*+'I$.eq?AkjV$(jKHH;!^u0_G(I/r[//SiK+cXoQ3[lOK5(JQ?5@#;DYRXbZ^CZJ'u,5un^(`9jPI-&uRoP5qZQ-m:ha+mjeaZ]gMs;Y4dJo_H.V/*3Vke>q`G %,!Nd(a/:I@=^VEQCmT4)"TJH])oiTf4,8.3l]Y(V!%JB7+%<4__%H%9jit$-ZfSMfA1><.aVuW.U@<9k!TCiuN %3I'=Jbr5\g#>aP`&;gQ3cKOX3D`s/r:$,YP/lubG7Q@S>GdftH83,c\]QscS(N`WD('#>G"T+?..)o,AlW2,eFsNWL!QUDl7-1`V %g6=ESe5.Hi*=h18,eeP;UF)(i>O@j.b2O'%,T!5c5f'Rp(f"bp?dGK)L1!kaX[)S7`(EH7j^Q)Z:S)@EfI$bYBK/R127u7!I5W/( %P\I).KQn>!JW/3Nc,Ze@k);9fY"DQT2CJ$m-I+_?F/.5BAW>(u`nH;#X\FCiQh(H:/@e]T9B,'/W!?^=J%qdR)8Gg#";6;Rnuuo" %A?dimPJLGRs!'"KT^AF"j!CL&jrB'"sM^YqDpi<^B,#iP]oI$-48nJ.b %K6+^+BnpkS/2ae4\Vd'UrJ.=`%>?q^;8[d.6A\>#ii>O[E@(1F4XO$7E^%;%$;i4Cm^SW2ESX$b%ld7/FbJ627u/PU6IJc %p+nM"<*k\e>u9p!6G9_;D$7]4(7@5EWh\&GEOpdH``IX!.5C)S,m9f8]F#(D4$>B[Lg[U4ACD)1NMV<2]"!=pZKf>Z?lJ?lDI<+8 %4C!Z(!Dn\$mG4!l8bm@CBulGBf&=,m_3/!ElpFQ-I;Olm%SeliV52C8-Vj>u<*@]1I4'o>Bur<[=M6cOq^'.Ie2aC&/l"f1IDhEjltl6g.&_MS(W1#[;'GcU4u*c8?M)')]];m`*eG24G5l0nEhnSjr8OEQ`n,rl+@KR %U5@G'Qm6F]9njSek2:r,Z=)Lj0QFZV.Q,$sR%e/+ee2iW6q^Eo%jd?,nE[<:h`pf:A+$M%c"tp6fC9RaK4H\gP"4=rh-$:F;5SoX %U?Q4G&![c,itdgIeXtr:,tW]aAkDu0e3"a\;n%TE,gmS8T.r=6n0FFkm<)cG`-)_Bi9\PHptJrg@#Rtq!gMNOd%jbo'-CCj/.U(p %*pVSI6mG`2L)Mfu'Dq7',!#WX2E^2=##>KjY/N[@#%L8tSmM*m^iFI.8Jg)PdkunZD#['u\8Dcm6U890qVIn(ZZ^P75*WJuH4CuG %"$kn&/M'4mCcP_Ak#M,Ablt_h7i%m_O[2*X_H#t$5+2#U8EcS9*!)+QLZr8S+87UjWnB@BjqqJ+@V,H$-"bT%WBSa+id@`K`W%]u %3Q$HLaU.U6/@\X%e`(b4PJG:L%P%4FeSOc3;N2MH8glVcJML0Yc&bMFePQ"J6Dc+&D*r_aV7XpjcH?>WB5.`W6+efaD=W%BVOGTF %6[uj@(Xs=Qf\>+.clsOV$>mgaa$`Ud3b8U5Fb1T#*,WaOiMi1PNa4/ZH6:]2r6@'o1K(=,FWnQN@9fLEtR)*e.E( %oaVupnVfkY;*k6X=A8Wtd`UT_W)jU7",=npTc=7$n5K.VI$,fWgoE#;_*U8m,3:"XE"^BJ+F![^^H#;RcY>b28j^os)"X?UX*3>' %&kR4Q2&Q?7^p@!)$GCDYJ!GWK'E+gD$@\=j!W^8+k3&./M2G.Vlkdh*I0r91e %^_QW^7gsP<+jR:C1gGebYD$57f%Z6]O[Gj;kl`+kR>t<"Tf0'sol#Bok!A"s7`V]-g+CA>0 %Vs]8kY+`9l8)@%d$h'4=92;C>d])LR>pX2+,456tFrFgVJ!;;FWWGKZ'nQNM7Cta%?8S`K(6D'gon4;'Ee`3Rs@dg^R5S!ip_I`Uu=U,hG?pA0%m%i?5`-_hO=3T63;d/TkKF %=`2.R?.QE+2P4[+L[&JlBZd.C1[9P'3Jk9Q\#Vr4ckL\(La0<,afNihG*+*L`CU\0;p^'i8KP!Sq`TP2(hjG&=Y4B]#7;?G"e'o1 %J=YDZfd5mm=;XO_p&$06fD;&/B)8W]nUB)W#U_,'J>iC&H-HOD@gpCa:^D_0Tk^N^[5+SZ&e!)l=8oM:/O:>SB:01V*B4DMY-&&g#m_=&$G%e$B9S>J\tLk-m3.I83XO0uM\*Fg4`cDk$ftic9J0;Ua=gXdKnFEj7Jf)jQ#NH(P*hE9\q@PrAmQgMC>L)lAWq4`f'>@u?.``Qej7],0qFdo`m.PBdrbB'N>/L?^ng0ki? %77J3s&@1).:DHH`L(Rh]?o=/KTsi@q0*f3]*a5I$#TFZWWQ70*3/N"jFSl33UEOb@oQJ?>hn8nG[b]8;'@c+7mB](X'X#s,:i^T#a=hI=l*W['$4k/IBW%)MFfcN^B^YAiGB7h8'F8Y6)'](_]7nR;+/"eN\8H-R,r\W]d$/9797!t; %=5$o#gZ2g_5>Q0K5)k.Y6s_d:&FnhE?>p,?_G0kKZpo/T8gq`"'jn4:,n$-BBb>,adFePL,Dd.dkuKp1Vlh.11m(=Lf>HT..k2EX %Ls/4KN$Y_V3Ff@DX[_m7XfFlu<=Dh?BX4"'n%r4-P07u'[?,&mCj/%j8@3E+&lq:U\b/<$f;=B$:mGnt27ABN5dmV5`%'UYQ^N"[ %Q4G\*Z9ujNVrEo(s8#^\SUF/A:Vhs%,]3@QVUujB3"+8lr$B3R$*caS(n:d6^dWmgTaRcCAjle7M_n"RCNSp;qXQoMutps %8FSKVZ.bjM7Hfe_KadIk,$saWmMJ:mZs?7tb(hl:8rOJb6`Je]Gq&i`r'Ano'-K6=Z-_S1`\nFRLSThXiD`fMFo-Dl>D;2RA&"\Gd8;Z`a25aMe-L""AG8qJ`OJV<&DshID3A_P\l'5Nhc\gg,Ck %oNaFd+4jW-bB;XOT"Eji)?da'D`.JW)Qq&k<0KJN(8ItsRbrDjgQ:qhVR]6a%)D])0tn#5R($l1pr.*?#/+jX$U?g-)?_DRC)#l; %c%#"YHa9ZYNHYFtEdb;T8,#pS@Q)$VOBsACaa^U-lSY$-d=NpP>@UTn]=n'uos=PB\V.1630YhUnl_)^.dBHT9m/Y0Vns=$B3aJcqD9T#T%p--JD#/>.;ud+mXG7T-b/iK@:<7t,4MSmOgdf#HaqiZOb9+.AWOqH/R-b^55Ge(i8%lTP?IM*IQ0?7D %=7Q2,0@?hcYtPhHBBJ]KATAkF^DCtd3.%gSN;'is/&<1+@pg3t^\\a)qUCP %*)gh\;=fmqAP*:?M:jn-QX+FRUb:b2d@[:U6$KZ[hdhJ-8">(Jb&/0I,n0-g"R^(nQN&;u@e%Z8ZCtUr\iobPH69.i6XsmbcB;]Zu$/?T3J)D.l]K8pta8c[)V];fllR;5L7d#=+dqkdpP?pGil*D)KXe]C[:en/R4F:OhTI);]9jtA&nH#CTOW'pA8Q?>V4iR %?13rX_:0N6XZ$\QXjs=,=B]&'S^u`m,Y'[MH!94hGb`d-96)+(Tbb7;iDo%efeB4+)Q'$5NV]Ng*ib\6HSX?Q_7\C&S#7]q\(*0c %`bV&5h=YZ"n2kU5TkFWA#PD=YeRhZbIRCJE7dWib>Li:^)iP#iEgDbdSU(0B/G'0S?ZXJ%g^gnKk:blm!S:M))V&Ngg6KP1@k=pL %7`(<6YhlHNle?l38oZR$.E*`,5TB(8^"eHQjP@60WEF,qG&8m0!8$5(*bs`D3GPX&QZZ)>h`UA-Vs^0R<3t$,-'0g^q\]/d'n(L0 %H=cPpW")3:Z`CM;V3fI)o]R$9q!NT=Cjr%#A4H[T&s*i0s&F"MQA`-nLKFp;]+&VK6adD=]:F@LI<]R'YRe]iQ[eQ/UI+R2+anLH %"D/k]L!4='09S73HJ8IX,j)c9RmI338rFBK=a2H!cL!H6oDVLVt[2tn7^d]6$s6+WG8gQghka96U9&"^JmEr?9ra^3dJ1lEWm=F=>3dm/R1gf,!)=ncknP!Wu %(hC3o$4eC-=$80c*`2WV`g:n@]/pQgf5\]oiJKLOAahUaV9A.8gr>f[V:Fb7.@3[LQD!-bnFk3>FqB`fA,?9i&!"GKE`=11YP:BT %\g_.<`)EA;LSg=m]"NDo07+\4kX^9)cP86TQepKqP%OI^[teE(AWMU?5NCPc:/L)>>O4_2ATNcXfXC4oh8ZV`*:6)eV.D.4=p#q" %/RG2rhIMll<[`==`P+gp.fZfrXjPrX(0\9J[VkR.k$>jPgo_Sg/*>)>>I&!&R;.3:(;n8nNk3J\?A3ZkM_Kk8J882U4GpXDk`GI& %o=M![Z`^uV*kIVJTqg4mn!!(1(j>It#qptE],0uLQU2`dn9m+AnVl:o^JXjjpU`VtU9?C.b`-4fs(K+*S$P::,sqVBoKe;:A]3[' %[8R@ogXj$;maKc#[!_mUQBg9]:s8A#WMD_(qG_(YSj)i@Ej*aRBpnk!^@-,![KURjmF&I4k%&?I=0sO\S&N4hkPK`sK`9I-fsXa? %lJ!3bq\FUN]M?t2Vehi9d=(TU'Q2-2f5poXiVTJ=D]PIiF_bidX$,AeK"9`L"VbDna^%?KiB$jSb8+`Nr_s7u[Rrm4bsmsUG.gOl %-/Z8h0DS._DF_UC5qBG1%c?`$k9,`;K-fu.3T%!O`pUO'rQ8knZ(4ccI=""--G*%5[UlE(DJ\;OApPP6ne#?fqfc8gn_EHAe+`1; %-Ku*I>0jl@qt:4SrRAApXs&\gsEPFqZ95/H4D)Oj]HA^UfdW1e-m3*e)c5CsSq,]/U1#C=9YL]ns9(TsjSU[SLDJ`4>8g %LFLqHB#2?buS3:FmKJ[ %(>*5kDA!6T,_#cu^Md@uY.qLcb8qIngfd*[rE\4pQL*&4D\KaA?M-ZPFg!-NQZ1E>F;pg)9C;ThmXAHCakOa9`VC)XUQ=;^fGj># %l^CJKQI+PDgIOu&TAG"^a7O"srP6As;fi<(bV,2l,h %UJ5m^48h$3>\-).WD-C(^>o$;0>mF[e\?AJ`j'YV4%s(-MC-0sY"*@H";V[%H_1mt!QX=\k0u6WYgj4Z9.QjQIAm3-\"VaKhkbLC %j7Vs2!'!FNgDAsq0!V34oL2,`gigU\=[fcJkpS#Lrg1)Qh6pa9U\"Jc*Od=qB4_=&ThgUb3>j+g[CT]DBr/Rtl6Ck:0e<3KOn6q"n@WIgPq(1oF"5bE)UTc#6Qt1lW#aicRFd`ST+UfBNkGQ+fDOX/i %Za6j%^OP*U3,Zm0\96A7LCRbI"CFEd@Wei,VZP(<=`r9<\\20Q9G@Vs;cYLp8L"#oTo+1;)j]P#ddbqP(#uf?@Nkm_J]Q5^9],:/ %g+6Dcs#!G;ZM7h+:?I`61U1/+X'&.PPq%#_dAVR-clBQIlm)kP[*:#RZV\%AQoeJDg$p+j1 %LQc`jmB]tj5bs67RlMD+3f0C2CPA:/KPBiSIK-`WQeslF.Y,f&'tm*aX0NV%`Dk4d,RG"f+`s$WYL[7;aao57d,baXOe/mZ*<$u` %D[D;$rNV\&]mGQU"ELR0G712'(p,0JPb^SYZLj[B(i* %KoK$;&]>geR8]M;2Q#uaZ>sBqMVX7%OUP2JmS.N,#.^tp=b#)SVU/Rp>^%Qbd<=3ArPLNB!@H'$'q_4-H-An$Jt&D2&$"5--.kq? %k`QS5'GgrWX&)dXEjh37Q^'u+!'C`pN?#e8J(59*A/]t+5cTSdX6D"T8WNRJAk+F!=C18VZWbo?m7DuNi/.[*25u6%B=X$`DEV=0 %;E3h?MFBOn:S7BeNB50$8,KT">K7V@8s#`g_#n#8U@O0.&;D<[I&^gS_-%urb^*IeU;YBt.FXllmaO`L+NXk47!OOlKBTrK'saqD %o7FQ?;aV'mo`]jY1`D,h#FVPec8%S3h9/ja,SHO=,ch]jbL@:=faQg#_/.`i,YW&*(/:AEmkEM',t4cr+V,>QBr-m2DPgdmd_hqA %js;Hs9-7?fe\/$p@EGR>TcZTQqp26*LH5UT9S#4/W6"/do00,.j-:WuDY@jM.auI!i3j75AYN=+o1b!%HLq]/!.i:L'M?$c^KKI5 %I,)R*$GWN/n=6X7aMDHuYUn_rLI;ff<*$o\VcS;op,5p$]5EkjNEd/b":VZr,EKRQWnuNq/G"'6E/GHs':0;,j,;p)l",=P82L39 %2L^%"p]mW4!U`*'m[@CV%/Va^?I4iqnD\6`Hb)K_3/;am;@aeoIi*TI\"Q'Fkp7=P[pZB!)3/I]A#h5+QaJc87P1SE[85@B?h!Hb6sg>sFj\_ZT^9LDJ>$gWfGur_KV_Y"$l1(l@*NK-8C')B'W"%cgYRFGpWYe"O+ukM*\QGP]eI/f_)56;uqGR@kNt!f5pe_:Ffs?2[C: %"#nmS#f9;#0U$gk?CV4>m0d:b=s:[gcF2B9c>"OO'/)1LWIJ4*_(16H`&LK1$o2`64q5SId'`t_kp*%-"gp6Z`$u9'&\?8&$5a:9/[O9':DNu#h$/:&SF'Z3Q&=jl7D`6I$ %dF3ZduJPFsukIsq2TrB^&D_'.:EnR)^,eo_(iJf\+4S/G6euo3OGG:AM]5*A5KI@5\``*&R-!q6U4iL0Dh/>nli,T8o' %&Q>fTr&Y`sq"aa^r"2WQgN3rZO5+YP_/D]4AXeRH$sqUk:#H@T61jep3#*YGnJLU2(^6`g1dLMcuB,]oBS\-!8NB^M7Umdo^)ubOlnGIXC<:7\ZJt^2A""h+.$5e^n;VY2.^Jfh\WcRD;ldC9No+%&/l,W/YSO5:_/B*K.H %#%j8sS\u$'8%:VH?Al>^Ee03Y_.n=C`&5]6\= %1pG"UA0?b9^P!94.eB5qD*&[BfW?*B&)m,Beki)LDe]`W`1(FunnGe7QAJFo*.[mJ$1u`?G@CTMd6 %\n^:C\k".#RH`[OoR-UTimuf_A:+6DVf2%J&_hJ_h3GF/8;`Bn>ehaRahjY'%S0QT+YC45`3M3HeBehW!'/b.:SJ %FZW-:$PUJ#(qG0ENTlXN!_rQ?\)9RH`4PGNnJ*Uh=.?WEA$Wi+![[>`gnB'jIta^Y6iIX;";fD8nI[%=e3QtU/qQ-Nleges&!F=I1!'RYU,RLckMa&t^BpS=+Yl^"e)95?/> %$WFc&(K(VC"Io<6WVIGMR]#V%N]BPH]kfr^G^LT.S\ScSD&0>>T6?EU)lY5"&i;/PQf>uSblU+.KVNPQFX/>&M`MmgKJ:umd7uFY5TKW#)-<'_WJLVIN4fc+^0H&4joaZ\`s,+VgqFV %r1W;uUiJn?[6N8Cf8668',#_Vg.VE=o8t=>=$8T"V&!Rs6lW=DFa>Z>3pH,Eu$TL0Gt %_nh"c0>STJ-E6aCPq>tSlqR0o:sLtqH4>.3.s><'7^Rn5f`VqbJ%B%q$3:CDl'd/%p2[>s8OF?6`gRE@=*$8b*QF:sEnEeHD`PefuG@c;1Jd %ne;]EaEZX^;0iBIA)mqooQ0@#HLmR-[ADrtD.`0`'%UiXqln,@-ZV')"E>&R"CZG]UHg%[285c19OVH#Eeg,$%^BK)1Q/?*'dXb$ %_N6F/J:A,^JDsYkR\Iu"MDu,hT.^7(&&J)D40#i#:JV6;0f&r6!30R:jPuIPOfA'@S*S0/H>A&goB*-4;JMdL`hi#]h^9`F9Mk33Q_HV-hS%D&$l9HeotbZEG4,RP?dcK=1)36Yfs8dQjllmRTQ"@ %QA7MlN.l5B-Q^8IV*=PeW`J,k^+5:l#BAGKrs;RWp`@X(6^^o'';+ %+-bMUp!dQ&V_]0g#ouO1Y`Y$M:72qCd_#?60YaPgaSR)DQFOjH4Y#DHD$KoK3f9+`@(+#/$=RQA3ilmeDi.6<#Cam(a1UHYo0RqNMi7'2AV"kX\T^A*[8l)NF, %,eD8mP'q)F#gOJ8fcdUBlXT'Vo]WN)FGUE:1>,NT46GQUK>_NAC/4F&0R7um9T[F7$4^plb`Fn-5j*EqH1=jro:CrSJ/1>unUgk2 %_;A8hNJO$9MEV[0Wk0r+H;WL*pm]2j5We6lTGp\dql!([%igim2o=Q!Y5%NNTTI/t@6,W9?AXEsL %PV`=P]/u=t<^$)CUPK-ohi:TBA.POuP-)*USSI/V:DjW5<0@p*iDE,oqsGfg-(1`_qGbPCC7kBs%KsYAdS84%3'A %Lq]9BQ_IL;&HD0^r*ElfA_V.S;7+g-*lnA4TH&Y%K1u.f\,rY[_b@tu"BTn,o8Q.,q[N4[g9Qg6\q/p=Rh.uFH4?HcG-A]!L?Q'I %1J(SMn.:2)!:>U7!*7+48fkGC]dNH']eQL4U]i-*Q&]+gV'd/g7^km;h3j>J*@a^D#@:POJ<6aZYb %o(tuFMb:11"8-CI9hu.-DC^UHDMj7X*6oBkW>%2;6,r!R][.IrD3CVc.tb&,jqW.EjQB[,PiP;C^Vf&]9>MV).6$3E\JYk$?f5CF %TcCRr.NAJ1mGG;H0cLTNH4QYGl.]O&)'l]K/P.6Fp:7F\5VA$R?uHBGJsbAtEYX\0"H*V6NiK3E&.gOS:&XAH+l->V`/B3a'2g8X %=`LOj5n8&`N=Y\gPR]H#!sf#k%XG7530@$RU(=-^*q0)tFG62Jra]tcA@#(H$c8ot'7d4W2fFgQnXVJZZ(MIA9Eh*+@HU$K>4!66 %;ZrL^nNB=/h1`[8Qak(`+Y#G,QPn0aKn>],`K[+rWjk5JMtFMil*q0;.m((Bf&nK..6PIb$9/)-^D`RDL'n^(M.UZSm8_*Kr)N_j %&l+MHYcbVmb/fU]T.j6Gf^%KcU::i06]gMa/$\R^_r2pQC\Lh\0PQh7^':s:YqeF.kE=V1L_Er0JC$N^J!69p^^&`[o8_0)gE%4$S.N.M2"M$dS]CFBN':>'K4TdTn-j7h'*;QRa1Y2uS#I&Lfj'Sj]o?k4tB$;96S0N3'6kWk9d)A72o %7nB"4*0G&!fRa0*P/[)i;tXUlF\lP=]_dQYl%p4mhOt!''(=a$EMt;3'E?^.3Gn$qrn(ML[\o(ZAtE<,E#3U4RW]PY'gQKu:FHSi %;q1B[%Com:%`0VJTD35e`G+I;Jsm,[3E9o(/1BZG'\Rt23_+5QPF=9p3T0=u9akb`aa(%lK^>OqIufB'e?afM3_F=]?64P2c+$r# %U8g38h1nlLZ(XUnmFsB>6T)DXYW]sF'j1XTG#`mL6fM7P=@1Ee=5N83V:oF?^Wg$MkO95=LA$_Zq(=ma]R#@-;4&oeDAM"F_%e$3 %WSTI1$k/ZP.4aR.QU0:1f/BZ^(t&ZK:YC\]J=AYlDVEc'J>DQ=T*rn8LX#pf50X"=\P-#/AZ$lqkHJ^6IuAn+aY:\M-!`m)QNQKI%LV\rbV<7If+G#!t,-rHRq1nff%34hm[mQTg`tD;qZo`5b%=TH9E#CY1iCS*#If!`I$dTBGL9Ad__(BNKqXA&3HM %fkUgc$tgusiG#C,4emu(nP2?lTSj%KgM(,S;^J6.Glm1"N7KrKA-"[`+9O_n=\se"j5@rc+UU=8"rIA&6o,cLJfX4_M9NC"E-+I& %<+n,rcnP37VC(%U2'E;=;aW8cVnOYjT&!>(-fC:C"Zt7NL[48.`s3K4XHH.+9Uo.FE)W!^_%c5fj1P*c@WDJ&BUF!\8_r#nm"1%1 %6A.dEQ>\SLd>;AuOHA:Nc'*=T=a0L8;oA]XaR=GD6Z6t'ARLBlY@]+F@$QLF\6.W9s+pY,N`q(<2bgpS'jSuN(C*9h:2de]>PQD( %ON1'h)N3_ur1`!tCmrCsFEZljP=TR.#/eW`_c#[?UCV^_H9/+R07ncEl:L0B--hsug_<8UO!sC")ZdEogK5T_n2hWX3n>i/`6E@F %$%882(D$'$lBUOtYN5jBB`,t*2:\;*U!DPA9EU$#E5_1.N2R4BQ/?k$tR3dDDO?*p@!>HTm.f?IV`G,%e:V2X`:6sgQ[eT"&NMEiD5X/^C?a%-?N %^Fn,P3U#BaD^_\0SYl=OlVT(e'!\;4]m=efT# %AGH4ZIT_=4bme.6-QWOV-K]N@]1lnVWh5mT)A>D(CBuGs![?0Dj;CNCC1s&YHseYI_d\P-]+34mPZAj3ZFT0[Me8lW12\(jd!s?K.5@c/Me+KbEX!R$,Yt(aS4]n.Wpj/-`:K %0Z%g(mU?]NROWG:[@cua?Hnfj[Ck?u"n_,?'2eHG1;Xt?E[@\=j4tO&[ck0R%r\N=9hok'>u,c\m9uW#b?EgU0)LCpFWq"<9FB%l %+\=07aG5?t8HIHT0t`pMj;SRpHL-0hY"*o5`(uiqh/\$O>q^?E"jMNe1&oEM(IX#$^RS0uHlFjcRjh-9;%-sF0>.(^5(6]WL@*%]TOS/4&7PE=]D$r&.dlN4^7[e^.`+I^)(je\P[o%M+[\ETNrl.?,4 %MN8TAYrMI&ot0Os*52u[(/heN>c^U(i[=:X=98O# %!"/h>$Y:>/>?e[)qNPWL]]KB>;7b`KRs5n#>qlO#4`FB1p5lda4lP960/Gt4.W,AEfHERa2S=_t+5bhh!>pr2A.XbNCLl@^i[PdTpmhCYR4NZ/$)QKioF5h001Z'IB*5MG-o>efmT*$7M@Z"A)Z!$VLT\*iXO9( %W_+g?nflfCp"RaEpV-_O(RZPsR)ker#AdF=L[Dt*3Z.DM,KI0Q;&,r4=_/AdUAZQ.gP8l)S@<43qNH>2WW3.r.kjp\^Fbk"LLP8J %ggMH<53ZJ3,k]QaV$]UJgmGq'(\Is1@2@p/oG9Nr(&$=,m/O$="#p=bJ19pk+FM]s`:?`ITpKXPhd!)`2*kigNCuDgEEJrFm:a_g %XP[$R6MmC*Ylj."SOt$A'&ho,=$omJ#BN8m2&%LXm9#;*>S['E96@pFq2)ll@R9/.DE@5GFU#?:=nAmPm&YSeoT0^0Vt\P %Z8ljCk(?9=NQjngZbNiLCY/NZ('j8?\c,Xbj_]VW)ikE03%d2r2,DrgL^_7HX2dQ5IE74`p`sci[pKdCGmA'[!Lq[Mf<=GFY(mpt %If%X'e850_:pZXk:eXI?>ke!]5BP/_>n]mqGr*2iI:[b39L6@Oh7i,B++7Ca6H:CGON+,a7^.OBBKIeZOmEDkP0>ig#sUeCENEsN %gCgNqCL!8Fp\C[>f=UHCmkUWl4$7r?Wt><#)LUGE8DRqC02'Rg2nZXI`fW(#C4S0EZ6c1K?>SN@YEpJ?r"OZ6ko*-q63&gn@^p4@g$&6$+gu%S>i!o*[]+17X#aR1UD13a; %&G-_!^0+a=JH?$59(g"*dKUXo<=>SU'KT0/JI26TY9Xs3:2+Je2C?%A0Im2HRlk(%eO?;tP5T:;g=;H$3!$gU5b_h_1Xj41Mj_fL %^5KBKJieo?!AOVr?+W015DeC-#ZFWu%"!R\kGQ[Qh">XIo:ao %TplYh9f_GKLC-/fLl%].V:qdhLHK9.;Zp=)bb/E\L+-SD[E52Fl88Yu5c?rB"L8J0[i=[)#h77Eedsg!M:Pt26J^O1b+oad(9j0j %"V9ndj7"=O(Ce3[d-A[gQO-R3X;u:\`f#(lGc0Gm<__mjZt?9GNd*J %ee$X"'`&DNLrKjb1D86j@A1tSR0$9a)%DktE&IAj$sYM^>0WlU#U9FIcENhSoo+i?eH&cmA6Qo[9PJ-kT!@r2KkA2m6u@6&LfI&*!4>9aV7OKrXg1;G1)P&juh4U_g0/KciTmd"_JLbo,Q6nq7@'&J/E %_=^DVrfl'CY^=)X5lbgi*[CNZRn.d0#<0]U3J59gD2+Mfh;u2k7\U08kfX7%ScZsOL;[[ak>_mbDRY6%bB.Tq-5/fjN!@s09.jXd %4)DU2C^hWn+hL=!)tj<"kqD#N@%K[HBJfP&H/Vtin"HoMLuACu'O$H?N!*Da,5kn._T%u\0b_?9j?##!2XfJ9GIWY?c4"Xm?f'1e %Qd,S5M3ZrsaoF;m[\+hpq+G1$:9$jH`4ugLS.JC%0U-];_[HlQJE_hY$o+idLb>=V,'2gRmP(B'sM-*Q;`3b?POELb+.Z>@q %O^D'V0_XVQ#JF4Dd$9%E/.N$OE(+0SZS`u8a=XVjP9hrkK@4b"V$FIO>S_="_#o4rl:*Wr&9.F6)dAbb6YHbFcklf+,YAZ?%P=.W %S/8(S0?kXOVVAcFOWNQLprU+>l.A95N5G++Daf]meQE],=k?&TCPeddqg!B(C,WCe:MYQl_`MPlp5ckVjUZa"G?, %=\cr^gXfV5pV(5lB)(#MpJ`AeRs!35GV$Sn]&/\U^:$ig;k"n?7=aC0^2j:E>h^Sq0/HEFg"Qs!?p3EiegP\mHKYCa.4$uLO5CU+ %rUu/1b_/bd!>YO+\,#F^BNQUJeof1h`H/f,rQH&eJ!E5#8qk_c&.$P(G](]"',o+PhA=mL4=!p#b8a@# %^4i3U]I"U`Xa)]UmgC_M>G__=<)G[Nl5cGL>-QV$:Z(u1GEAVXj3[^nS.1D=LKWXbTiW]1KM9IHs` %e^ZU`Xr5T!(A6apb*/k8*p;ZP$K>8i!:J*_Tu4NSOE+O\L+cTc;p,m6[hQ!oPhSZVPAh%/o[^U`SaFkahda!)Th9_$P:F5ZegQfl %@Do4&?U6aVD0hN'+1apL1mP@V)'JNE)Q&]JeW.#tqO=%7U&%0s5N!0aK.$*Vc.89$Y>+[j?hQ_Ug$-[A0E%>nDWVN%p::V2O+,]Q %GS/8i>hs42>5O298Pj:1jEC40$2+8**+Y-gSRm*@\*&6O"cs:24@n)EFqKG5#XT`(\9qSg540CaY\0mA)>^Qk?!W!4d[0nLNEtgo %5)$#q-TI6ckP^>__JumWA_P[?41e%5HP-AS#T>8Oo(EqS*toL0_OMe-s'7O6OQ\YWSEa.Dbe!gShTS4o!WhJ7qF-NslRZ1X+:.j! %(K-fG+46^c(mCZ!Ofu?JVZc[G_hM>,(00#g%P$h/3*$>gf;)AW^PUEMf0,_+?oSYq!I'pg,^gG0_2Doe\H43d=2/]P0fJtjUEa&$ %&=r/FfjL*hE2i^gEf5P:)-u>U-d0WR@[%>KJgP^)l>m+&S<*^1mQ9=pO3`R9GL;WMG`#Z:uSR5^c"o9 %81u:,)VE#GUA9di'!1IVPk+@F$1L!LCla*[03VHVRjoc7G.;B"30Z3&R/I`Gbn!!<.0Jr&sZ0ke%CQfO(SI[D9d7`-B %.c9Tho<6f#2-A%#h<(oNK(e`V.\j,Q]1OIefCbr?U'a2M%WK2[5jZ2A`'$W=igmbR3Do<-T"u3DLM7a>H %oM_;Cbl@Qm3J7;(X9Nsj+@R!iApA$nFXNO]f:8@ZQWd,O&tUekgJh467A4ghAr>a8LWV1,:)$QBgh,Nagmsp9+G*OD!tfYbPQ!&H %3uJsYW*`!^ZZKU:'CL*CQuM,.7"$t@LfNZLf>!u:]RR*k;=\#74ngcN^Woe6@AQ[e]^s0qQkrq5`0oC)\M5Q&FV %rr)<\h=1;+rS.A8pODr3J+rh)r8$iAorn?e_uK"?s6JS@^Aj*urp>3U^R=pWs6B(Qnc/Q+EM%a[f>%@bT>1EnhgA&rig8]S^O5tB %5Q$RLrpfN.Ru[JtO!"E!j+%!aO.lUDWIF\&TDl%oG^T?,>l3u>q5\hg"+,j3P,%dK07WZIlW?1(YL,#4d>%je6&#DCJ,eBXp"-tU %V>cN_hCKf)(d?RPe_j;(3NBQ%gAKsmJ %9V]<76#[>)ck89F]qb"bW$B"[lo'V@eo\"P!g56$m]lZQgPU58FUJ=DD\I48&kR>\DGVo>S.liWnh<-rn@jTr@h"q!mgqNVJiN&, %0u*pX%\R/)qbcnsDaO><%TdmU*Y^9cJ^\h#S*&c>o=:_'6.]:&N.nBLH"P[k6MS,IA#H^]&-M#5N,Ik]R,Y@q-K8IqbGTP!m.2n1 %`HO:]ro,FVWJXPh7.\plj7FD`!]mjfe%,Ms*hM;%Ulr)1KgIY4MAbi22u&(QVNN;nNNriTrl'qff'1N%%u'T4XOB,Qk>JVE4I_YT %aF;_#S.r8s`FFiG;Y;,DF^-S-ku>3+ERQpq/Q+eOm&Bg`Qii9`@+7KbT@ZIn%aDn0F\Uq,ok$FgVm[qXY-0u#;":495dY0)KU.rr %+9eZucOf'q3-P.%B)4AE>25e';Nr:>o'i'K#:&uIGjtX/23^UW%JCC7M&g+([A_p0TZAF]GZ3=PNfprSqhB\?J1E('*[G;FXGVH$ %8tpeeEShDk_@tB/?cEYoHcgacrK@f-a)G<&!uu!U]Q4PUrpb#Vs4ZkCfAQ6Wo$p39>!#`Bq;&s;;OHeA#*9nNf9.9)KA^M'+D6A2 %`f`BN25^+bE!L_nq\E]WeDU!0A6dR@_dFi$VAX.c*H739_GIaFKBJ14UfQKoNn4+7j>W)VD'QA.OmL*V5IIIepNUC$#1\!3$aCC17i/j]obCU1nK8(]JpnRQIh>-$Je`.cnb4*$J7?0ia-?&6*KV6cO@rLrX2_HReWE0p/ %7"+bV"-'o!g5*1&Eag7?38Zl9o/Nf\l&=eX0U6,@8$_qK*-+YhtZ %0b`p-?@CdB$S(K2COqhk(RW-0C03Z55>Np5hdR!'Z5=I3;e_63"n:j`M&E;V6i:%Sd6KM?f>YNpn'5&>tas2MMFmO#7UoJ$CcjJrkPi\4GA %CadsUBK:COgsb<#e/W)G+-FtZ>N7jX=n&X&7gPa?>ks$d80HaH:juWW-DAfaeF;][p)"d%P%P9([ia6@^DFHD0q-[Y8Y.8,<*7GW %ViOn=+ceF]kN@Jl@INE&m?GeF0J38rGGVY`OIpfdb!(t1hs_O*=4egs[1U(#D!c4_&d3UL)*&qU]Th&>@n;D)h4hBO^FfO/C)c`?hmH%rc:(>%+oXi(!`i(+Z6bAYsQm'TR+eq^Yo[0A %O9o?dIKb$o*J^kM:E"qhSp"CFnE[m'-6ZV?m^+"2XjB>trOP_U^rdT*3-IN>,8O9?MSh8Q&k(+HVE'VOhqhb13MI0Vaj,T)VDi>q %#W!Cg1+TlZUiK]72;qT/?cAo"2*sP,p:=;DaO!2_c6(b<#b:nsMpL]\q,90:7Mqh"gNsPCcX["WW5#1lC68md^]9?Q(j6>bi$L%LILg0#5s7?Ib0;NN4`Ad;8-6/jD#hl55*`^dpCC"G=\SuU,SeP%X6dN$nhkqNlVo3kY %?hTBbjH+A@%-Mhjl$o5^G^nc&EEfsM!c"D`rlV.db0M`14gm*C6`u*>I#R)0h=T@.;>4gOQn7>p$CdhU(#2F)bB"FPp<*L\@dna[dS6Qq6#Mb!k?a=aGm])b\SM/aMfnNhf]HC8-k$O`J3t8`>_Qo_E %Z=aFb(-lN5Rl6@(07.Yc5eXujZ>'L.!Ng;S-=0P$80)[>LG%\hGH_*oZ?Ee/*Wn&_*E?>niFPT`0?nQbXNgjMS&SBYIs4Bd_@Ykd+a!3)(T7G6I<%eP1^G"iZY*ZN=E*K8l#;m=ZOSH"8p@#MZ_^Lk4L\cP %a/&O;t*F %A?u>se>FLD%1VM4QNJs]&''7W.iM\2?JZ)dPaefj#Bc>i>$1XZRiCiG]F1<']GJk#=pQE>=s$LX:OK_`obHm/7ju3_hh!fh4eSm8 %NAn8T@&_OkoZN5Y&G?D#HHHPLI73_P*XlLelD$0Wb2CSr'EPOuEgJIi<(h)k)JuTaYR8(](5U3Pa.#Yq;VRGfuQQY(s^?@inO&#tt0ih/) %'rdM>H@lCZW)Ba-$T;o[TO%3>u?O.6XL^#[D*\F1;IDF^cYnae5NcV1"3nWVkZKsCJ+!.dF;ta[`nlX+k&L)l^m67(L %)]2dF"ODH1e>f.=)7Dg@KHo>1>@gSXK*c6NN)i#AS5UGIBo9qk %,)c=cU%E&a;u!`;q-jm8_T5ju_:V%IqpF[I#%41KcECFX]elF,$Z7A&(r:O'&YMm9BC'A5ED;X^,726E(J_,SoB:"GscNt %]L**'[Crs:"?-CDOo'nIBXkmK.;SeDO3RO?nN1N>7=UbY.;Y]l;k+\kIS?A`HrL+ln7![M_d/RifD?Qt*'ack&nS[>+ZH!>!%$9C %QNeX;a(oHY6I=M/QsahDn'H:m(*o?IX\fpJa`G(A@DY`I@>JaOO2%nBH4aQk;d&#IZkj6@F96;,5YJ0bp[c(7(G@U]G9Z5s>8`WK %MIG6^*VYP&.%!HXfAY['cPA';'97Ylh5:2NqK:/%=q\Kbo7Wgl1p9sbA-(K7c,uf$Qp(@=6,a>9USn$dl20_+&4+eJ#QCE`i*6Y&"7O-$FT-7IuD0Ce%`gD1rIt-9,`kn\SqF"^F.j/Jh+$e(Zb2+8)L#G3'Z*4HN5=E,CZKW&Jeel-H)qQVKPi>_#[d(p5eIX4.9HD3nk[E&GqLRFRdI""E\JgA*WaDC$`Gqr1+;k>r`Ou&UQqg:Ub]p^9Y'1QR6ns)a-4E6m3EE^XaDJ[:+mV3.Lgo2L42i-U&GD(YI-SOlD1Med %m,l@I(.c:Iql"o>)PXc^fp,D"Lk]0uL#';/BM*0sEj0OV@HnISEGU/]\"])3Z(htF1TY-aI2(@0Ai_W3ksTq?LX1Po0KXJ32?4a( %q)Fg`KWWo-!=ChH8("BFE=_[U/i;l[,G\u9::-@qNpECSoZQtpl'317!#.`:^sCKFh?dQTb#Q[0.)\IK?&fT$&*g'HYW/fq6Y/(d %]=AeVpT&+N(+5c.,uT'="*iEj&8T#MKEII>-nOCi>m>sF/nD0E=>@9j?6udQ8jN07hpmkp9*NC6;n`rqZKU]Pon0_=q7@d2:jta@ %-i5UiEOQ.JSK[@8b@cLM3J.#[Lj7N;NAuW;V\!TE49?k_FpB&_YEId>Q %/u\=Id*?Ad!#7FY&rP/0%7cJ9&`0i`QZ.[U8Y8(`Xe$%b7Z1nTGcN:['r!L<\@ZA:+Q)^) %Wh^bt1L\%;I@U&_K9eIm2&YgscCmNYPX0(BU7l;BY\tcW?2[ar$]I5V]--$f5p1WP.k]2-"ss-""'!,AVLC`)+SOKE5DATSWJ4\.pSY8XK6)N2=mtS%i&C0Vn%L@d!d<2`35_ZU#G'>O9**P:(EF]/T:sT3-1s]Y_huIGl`?Fb5/J' %['Bg)84-Lfp]H!R#B:#3SOr9Wt#W%Yn#d?%P5j7eta9h$bM(rj'$J;#&?Ici% %X.O]W;?[84p@6C$'d2jf`lT5fK\8K$@51MIm<`$jp2SB-&i5bLi:WWgSi#9mi$9t.3mV'*-B+VdSNDsdIK`9k94s$-kg3HprY.E:r],24gGS=p_]]s3@[,/+>!WhMZ=C]KX+8#*5G9.$%)[TO]O4Wrr*Kp.fnq]l[XsX&PkKG %kA1]MGDr3<@CqZAiaQ._ZWr-i<`P.t>[J`8nnd6hJ(E)TLg)0i3ao'.bA-J:@3#ZC_^O14-ikbt8P]Y?9M?=I*4HRqdKhmJ01!Z]r0"Q?7JimiDX"njKJ2V:c6@(0iD;R-ES++p=G!4+R'*J19VZA&cRKo]=J+q5^"+u\4E^7SWP5\AL\:\mMK&X3da %[H3LkDbqmm0h2r6<2'T(i[NcB-#*i$&Ygnj7;@@tujAK8T2XjKK25kQRPem]q:UN_Tipbs#Q$M'4I_E.n=_@mW.b/P(\A%7^(dQn&DX$=l-X %>?j@i]_)9L?RIpb7M`iO1se=25\I/)E?KUYE);A00qqu!Hbb$k&QOodBN`Q9>7VeBLN"GDKq]X9J;)AEIBDI2>uft:=:=bGri.`b %)JCmTWAX+>/TM&-VlL'c!o-q[&sHSE+_Jcl>dQRuf95IsBX^fhnX@.-QYZN_2;:se\qC)dYo[;2N%=.6Rn&%M>!q#:-*,L\/%?D( %j4pV).`l*,B:6>iKq4LT4TqI_-c(.]q@IIN9+tm"eMc:j3t&rG/*k6@87'NPaJ^+idY8TI\FBspqd$&jNh@3u+ld4q$Ma"7q[\E" %Gb"K?"Q?cFuq-Q#qf%q/L"l'1uXim$5kmY46rfU1X'$6ef<1Be;/d@i[T2C=*j;8Jc1 %"K4_(?B-$%n)[3P.LQo+q'?L'nc]jIY/FjnfphEgqQ`'li]0h %cA[$<14W/#@2`$2F#=?+@`dhV^qNUc=qS2^L1UiS^pF64G&),oAk%u';$85_M+!f-@?;. %?ZH*/K]\oBA\DT\5pPK?QW!K\l3\cG$%d.H")['>/>m:(MkI?J:f-sAOoHFc3u?>WLBA1+`D[H/B8GN5lG$4cOXN5Qf<%a.[C$S%(O+VOFS'/WaFF(g+a/guqiULetHuT_RO6Rj$8boKe9Fp27D6+2T03W\Zb]#Tj9fHGt8/hq;"WA,?2@<#n'1 %@KkDa_a:CjB"5>RB&'8[Ff\U_A!e8O6mH\^/[@IW%I^4Yc=A]'a3:.Z0\GF,i>d#\c`7k>2>ff:0jS774A) %#TK"](?u%WSQj8V:elAjk,(fLetgj,qWS`3l5"n4$j!RGfWlIsm]B.(Gp9UTN#8,O#QXIE<9kG`@gG.)k^%$@P:8AZY;_9`1cUO8 %KRfeCr_Ts?F\b>+:91bWC-XAE&dcoMh!7-_?Y-I+l4\^eCJL".5_1JKR)9NgKRX7ZC(9)b.u+jM+rK`7,cg8\%CcOH3'Vru6P8,t %FM3D<`c!PR3b&`JeY>:%1f?0qYQG;"*Xq`0Y2h:gH(p.b3RBc@5H:aVb!Xp,1l/gRkmtB3c[k'\+_1R51NsQ`5IR\nIGsiT91XRP %k'HBik\lKLM$>N?YX$DhZCQ*['k';-eO]+JMF,ut.F`up=eR:1)IZRgDL,E@,Qfj8%ajTHajCT5#N=$q%U?o\!c)h6P#9ncp+1T= %LW@`bd6qF\mb>.GK,FdHZ!7TU2lY#3P5F@)VR\?<`mR;aYf17B_qQ%<**0QZ?e1[^]baHo5KUj/==cl0bu;t\G=R>'!qUGS5`+i+ %1gik'TNbo=,%TfBR_LB`20)lu1n[/@F8VL!Po3VYqc2p8XTC)-&UT06.dK)@0k %N<@VA3h:LIZ#Xs*m;!8^j4ANnJjSki:lT"%rk**BF<.1I#&tH9+UW.9qr!feCgE]==1sD,;f*c\)+q\@L7e"OjFgC0AWgn_f47'm %/Tu=jNcf5pM4eVk_N9W`;_9ms'[5kL^$=I-dm0lpp(k;ST))'HAHSor$'AD*+V,WUej(P.2j:+W1EFhr6`J4b$Z#(\Sm0Rk*!XMD %GOfciB*IhL+Uh\QI'G,&nnf\tg&_qG4hLTh4`Ji<;n95%c/79`\LJOX.2691G)L&kQA.9"XQ2^P.5R1Mbml0XNi@sXFGjIR''GuA %."_](0al%&pkFk[:d0_W6cu6HMDLNu.gV=`boqaWE-]g(lf9'0b#t-CLI=%)W"]bkI^eEk'"RJS/Uf6#*CkW)Z+LDoSc[R?c/V;^ %=QV<@(foW?b!C4n6H5S";(Y)U'h=QLCb9*$+D$j+h?)d?qp*V\/,>%;O$WFC^>'\3eISk@lC[qBTo#%1].\I.lK!peQkl2,U@u76 %YB^#[Gq.Z71C148Prt)%.%N7p?oXU\4o"n>Y8V@RM8`&1;*)lEKs00`q/OF?"*gH8!(:d6WgN7>_?21kYVL %p2cD99k3J7i3ID[nQR:)H+-ILlTU_pfcru3kgqal59gEfWW0R#5m%MS:KAc8'@n8iAf$S>O@J=dj!/Es3n?_' %_MCNV;JlU$>V*dVX3Y`Mnj:35&-XeV5=sjkXppT3fu-6.@EYH+J_hNH3L0c%_'>8sIStF8c979">)Fm++9i=$?eeRjXrargT=Qq` %OdSr)3`I]4HnEf!N(OEe;G/j`%6VMENgF<49SL(=ZEd4nfF'IRY1_jj*HA4X1/^q1`ZX3VqUjd48*%<3FhSfRT0Ui=phV3mqn%*I %2E@jV\e8Srqm5@>lEI:Z)tP?9%p*X5mH+lp[>TS[[Ac_kgoJLPYj\m5P7+QjF1[RaLZ0ed=sM!6O8H"&_44T4`Zu^um'J)8=tFk-.6suj.Lm%P'LufJbRr/$&DFI$REEl\ %I0f6lGB[crmq5WaII6#hA\ru6c+0".bg[RKqjJHn>>,LGX>(;$O5!1[YnLiRlgii!=?A`7 %A[.Rr*OUDMN,V(Ln.I0>FLn3,.$)M%k`h9b]%4K82AMp,"+Gi:4 %iU_-p?>X\u1,[oHY/:Gs7Zq8seeUs+HmO?uU7_)2Ghrok"b906a=TSU%34JT/B&7.#fphli<74G+LClI9Iudp#CL.\-*;+\FlTdC %%NG!"+8')F?.=S=D4(sRd=He.Mi"I\kgt"7^t"L`&t#3L5%FfehU986<$De %=h6",a+7m!_^ApPEcOhJCVRPXSq>TXW1dttpH]O8X5>/f:u<)3*MMGdgq>fI%_ML^dhf;2I0/+rBVpAi1kQ\q]W_a>p(%jOj367(%LFg3)q70;8e)k^)% %"4?iPLXntM]51@q+3a0jm<[GGI"HXdAak6'eh(,s54p#,DQ#GS,SnK+?*ORs) %L3hh5#,*M^eKL3OVpYl/P0,s/A%%T[2!/dV31Q-kR.rHUkZ5ohreh4Tl+V]-FX%$:FFD5f=cmA;%03AWe@\=1m,sR=S&MEYe?Nc.,"SH6_,D+D1Ff&u:R.i'p@_0q5h4seQ-V"0OHUE\'T.)AaRGNN)m*d$AmFn@g$o.L*A5Ym8^dNqE %9cq),YR:uGCM4'Q#E&1a%)[c3jt*o!'?g.tiL)DE:&5![jPRQ^5SC*lKTnA.i^RjRaPr_WTij<25b0`ck7S.'3Uc2^l.$#A\>_$c %dDjQd6`k\2aJXTs`&R&Z`$RO5P1$@Q(srUM-`FDIBC*8.QcQ-4*K9.j>\:mcFCCQ0J=*R(12eB_I_#Y_[jh(S-C#Q##?Zs %T7(&&^ejV`h>c3R&AgR9dtXtGN@lS6cRdnh>&H\a:atmFr&s:7'>X3f8h!PnkJ&4DJTsfr.[iQ?:6e9-AO=@d]\9b_jhqpC(A*e4 %R*W$Y)CU^``s,-ZT)uq.*FQ8jTrkQ&qbX.Z\X;m`:qZ"r@b=>8A]`pKo$#g;-'+[M`sD":6G*:=%OQt#bL#R#GqQAN&R3U^)DNHH.EB@&B\etr,:c'j-!!IMtn#GqB(YXjoL7iCc+>gV>^"K:(GB0EY'Za-Anb"&h_7F^NF'j9b*O5%Cs[lJSik#4^`JC*0oU3u(MRZF-dC6]H2NqV)KWn'#(\GP26RL,tZ6S&3%2L6dm.;*jdP0@_8%PuINbkeDt@gUVBa'j1It/@!0rJfXicQIG,AdQ,l6g+=dU %Gp7SeK1@/E7+Ks+@'d^dP9^G!80UQ %$A[T[4+&#%0IPA_kWLu'cBLrm\-5=\j<7GW[>Yl41p$3Y5E%2)f:R>jRmXuG/ %BMfu#@lE6MCZUEs\d+b?:SO<=HtS`(a8q,,+g[R-`ESR\RE`Lf@VVpKkU;gS0%eV="OE3J[O"dffg30ob#/Q&Ka<#uXISFhY:L<& %JKI\CW,@n!.,g55UNiEpeJo3CpLd?MeZJErX4#Zg=?_dRh7#-erMFJ`FJ((\GJPk>g0!LL(2UDni#$Q$(bN4Wpc<$*-`MQ;Z[Q@U %&L<(7colK0PId"PP6.?I"u3<_'[]1'Hfe%WCU^HcW8Ipl"@]`R&;JI@f%PR[B\$Na[8UC,J3Ig`Fg)rsG^PRj2V'0Z %#a0e6JU%E:(n[r%%V6m+oJM9X_:?q-B+0+W`skF2C^>\3on7R=_Vp\)`KTsq%,`"k0IG"Sfe8V6\i$-8?t"l&=B-qa3ML?4Wt=>i %#O5d+Wp>nS!LKDh:fLd)FAAA>/+o"7U9o4PR2V7+:De_,!mF#Cg.f5un7nW_2i%bB/>6!7/.-)%+B(sVU.3CbQjC!I'GYB_MT!)5 %bef<s5Q\&Q.$IQXJfV9Kbs)N`\^7= %)VGh4arbg(QYMA]6AQ5UX+s,"95ge$)/(IT+JOE4KfP:T]I"paAt^j#4K\=)FmV=Q[Tkgi0lk6]%-^(gn4JQfNt:L[Zuk-b*?bI>#/rX#>XAB13],%PWqn;P0acC!T:cVA( %S5M#-gco?Wc!8+[bg33$;+O?m&E!'nT92nOW4cnJ8p19N6-Q[/KJuojbrd-[PeLQ,1f/!gRJ:?L);Z@gkZSc?8f$B=5?ebC9G)Yr %V4Aq^F2`/E)g)NK%NaVa@uF3edT*+6e<2Lok5F[8Cjc91,mgl]B.'ffYF1:rPuHd,Z$cS7MT>XLEoKqrqk>UbL4!P2Sp7]-=Qk`" %>/;b`dU`S#PU;q%+E:E/8R/ioK8UkU-Ie$`BeYhg%OTt#Ma_.lE,QJ](G9M2"!*-/LR?Gr&ge_OehAJN$:\3f6=^6;q[XhW'1W/) %6r7Hp>uc\1.m83-H%]p*^%.s/a)%_Y7Ai=O3VQPi:\45_2d3OEEH,6=8W.uR=XmDDO:tHPJq=lt&LX*?SCo/c6'LbgJm.T7R=k7PW@9=E5RQ=BT0._^\)C@9LYCMm?,%^(T:1*']*%kqKPq %>\HR[6b/;\0E!M+=;';W%*#5=8&llSJPkq0e&%4MJXco+WaILWt834[EVC$UW8%?`"QG@2u.,f %eItqcPCT*@'=4`^('@JM4g:Xm\Cd9&\MLI^C?puRT>Z4R)"usD5?J\kiB1]qMq,dPXDE*8bSP1g%cMLYWqSjgZsTZt9"Gr6H7U4% %bd:'e)jQB*7^&t)=/[u]/B00Op'bSg;6(Cb5),7pfGFb=rH"?*)1 %O!qrFRg)L.,faAn(XkDN4AE9[U3[6?H?51bRq(bT1hCC\Wt*@$L6Bjn./JHcc%>jio99BW8rZs3\`Z^ID!"K`N&t`nXI/laZ7Mes %Z1XG7"TO6'HddR]>a$ctA):Cj.7VQk?JHFM&N>>6o/\$A1E];UOY\4OaA+JoY1.J%a6'F^hJ2kl^G7ZdZl^8[iO?+DhXHu._KJ`>d=j?WI:+89eisIo?Q(9-nFppN^l%n %i%@3EZ'&XpGS3qF#8g_LW=\]_f+l*bS_la"#@V'nF9dn;>qE^ib%4Q_C#i=B-eJ,q2k?P.;FKucn+-icA"s:Mpbpr)Y<$N4f)@_G %`N+CNAC-s3hL[ZpUS %Vg"hAfNkAdb&,SF76hr0ZB$N^*2cXB52C+k_]iSCqq?1nT0d_e]'CRShn,BTnV"9n0o/Xo;=saMWo=Io0FZ1b0&nEs,p2)R %X=WOuW*n&l1>c5+k@H?^`gX#OdE*2W;99 %5%_$8pmaTFfM$J7GHA5"<^P$Q5HGXr/t:&IGg<(ES%W$".lFC"\e`Gf$t14\6JPaH'@T"=I*l-#79*4S`9MW$G:OaPBpJ)3g.^iu %ckq!43_Or&XDQi%-)D@]iU'9l9!NCXK:@jWrVl0;gf4sP[1:-+O]/'aZ3,Wb=9BcLgK(L%\@?UCB;fEKZ8l2.jn:GMYjUY%K/df+ %P/m'nT[MT']P8]>V_/CAbRL%WB:+$-@41&d1$(7Kn8<`X$'(KfH[;PDB^HNNs_k,1G %mWOYK@LS>An>^`r[grMY/`2`)J$QomR0;i?HYm&a#o5a2]FYeQUVCTFKVCp+:s<@'?S['o=H"r-oK(^U%'V4D`VeYb>J]R>%lihq %oCD3FZpUg]F^M/+]0I6%miY.h&_7W%=4u4>VDL5*ULjS&\8D@0mF5[GU;S;`gGf6q[rc=2XrY5B$q*HH-(15MStfSbJK;d^NqLYq %8'?J-MTr2T^ijSpTCMY@XBPYY!Jqp2_SB"upf*O@@Gu7bG:Kq^10FCe"b\NILXE6eP<]+TfnVt.R+`!3_i*(1S3l$&Oij^Am)J['Q9.DMtrh/BW-2BajoMYaPK %2:W'b3nUHFU.T$"/gIbfKYZBsU(dL%jW/#qOs7c_#*Ia_!=JI7o(5+0E,-TdTO^I6dV9?lg2[U(hS\8p3rH@NCQiFIF_2?=LcrVV %<'!S3600)^r&lR-mr74)aL',XjV?J=%@NKc'<:Rbf:1h`*)D0L4nDZ)DSffMWiYK=lR-g#ZLmS56errl1sK19Yu`qUf#K$]igFFi %Z0K6f#M="HRW-X)nf7H:ZB4O44H])Gi0<'aA[)9e8b%2_^jT$uC4V*!&4VZr9lEs7mZBuEH_SbDT1i,NE%aS6R^9)RiZ&/Gf$J]LH?eg*V5# %k-3i`kmZ`!QHRc?ZY=i[k[X\Q0H!i,L:,Xeo%dTekb++\[576qcDVJ]"EWEK4 %>ELP;;$lRY-!SIC:a]P)N.r2:7,q]"31\OACoS\E$l9r0rGsJL\W$01*Do2hR,:MD2/+UoP*5l>O!h8HM>O2gnS0q>M:6T8QD,`# %6410J&E]a`h&IB+WelWDUJr7(M]ZpjN[B(A.6j0Ni75nV^iLJ'@dc9<'McSTTu08;EusbC9iZUtkg#dm<1R-a=Zf3Y_Qmj2j%6g1 %/G?scJd9+K=Or6JSb[,?Ht.erEE)EWGHsU=6[&0s%NW$VMe2:q?p._c;/BY(k[=D4?s6D!G*<\V@F?BcQbS<^c4?m.)QhE0h`GZt %\r'gj.o8&*g'PrqiE7:irBSO'X=!i>Uh(Gt'rhH(83[b@j7/+LG:G^R;!(> %>V9A-RDrY$,c;#S1ZfD_@)YogR>qP)cM7joP/E0"?_?cAI=iAQ_tfPlFtu&D:Qbo6YW[^g6"Ano9,b*d>n8;Bhm%o)>I6pcS;ZtD %oCid/'os!.CI6gT-%\og67FX!#/RGmJqm^NhNf&G,E_WNQeomFlsgXab3Zd!Q?UEr1JcDU&W"XBZC]j]H0[A*DQc?#n=K:rA:qiU %PH$2C+l?oF!-^mb%i]A`@bG0i,kPu3ZJ'O-oGLjtqCZ":oJ8D7JW8DW?PR:='L%<)@q/K/IIap0TiakEE'SEP`! %4'd7YUh+.le52Bsdm=ea?NaiX=,cd3/mOKt@:MkOZAP%]23iK`s*dH?"?L]TU,.]8%qRC;>b"m95\n%aQLI(#'[IM_.4-b?<)%ET %@MQU.HJPJ+[U+;>9)EF*4S6PiS`6dZ^oZ:umK0O$$OgI@E1dO]T>__>H;\PCC@^ENX^I=3T-k2D,u>4`EV'G4"M/WPo8A<8*TC^@[ZQfR5Q\k+gWe#[h'tj,70nq<*j\eplslI*/ijA=Cd\3 %2g?7](.b@$rnAVaDprDY^MVR`/#87PKJ$0789*mb>.Tf2^&JF/@_BL5`^g;SC/sC*([`g3HF7-.s-k"s69F %8#KZ&:e94/F3"F/A1;n!!W=VeNG)dr:QnlLVp;&tpR%rmMPK+#.RW6aG'GG%BuIrF*"So:<0lktNPq2V!Z2NTe=E/AV?g+;V$C.K %\fi6h\RXuGdoiuNj`cNKpL99862nVFO:\B/X6QgH+E?WRSdZg&/360-H:hYI-MXoT?h\5?i#pHTbS'q:* %TR?76g2;!<]4n,$b=^0e#*=]t6?`lI4f-C6eKNs$o;4rmb07H3J2bq<`dF,>H%rFHRh=CfM$q3>!:Tsa7=G"hK@etg\G5,=Gl7<, %@MLYre'Y"^a`4^8Nd&9D(K3"dB&a[Aq1r-Nqan-k7@AUZuZ,W4?`Lj>Pk9mL"R(.ZROC,1e@ %'`=Fm)B2b[jAR\P*gL@uX%3IgVL%lj3J&g:Z!qC2;:rBRR0tsL*@rZ<%j@N*`Rl`\&4cF,QGNh/JFaL9nKER`_E'Qtkrk(V7nUK] %QrWgF&C9&ra:\*7is;e^#qRn%%GuZ0QjCI&G]V?I:<%p430LDV.XupY@gM0_C&e:BY+-,W5D07g:neiR %k"5?JG)Lnn(^AZa%,<^ISr2iFBs;hFZh`t0Z\m@gG>>=oj8q'lYGe^^;Q(hAo8Z$*N`=Xh@D6<5n?`O_n'p?jkK"3bd*(Stij %iYi\,s/9)]F$nWMf,0n!"J4#W]bi<$EBt9c+YV'TaiJ_@@A)"j@6Mlj %Yq>MV0i=?Em.pSk:RI:!aL>.]Hc(Ml3e'i+EHqtO)6<#+,RrD;S/sL\g<&e'&LM"T^F?H[0#tCg3Bi+*2=Q,O'i\,]I\5hJne?KA %RsB]3hiNM6=<1YP)@%-2N\!1M%T,Vj#fK0!2@[$Re:<>MIcdB26/1Rr*K.>Zq#cnYQqI;B9Sd-"L!& %?kmGbl+g\a4hCrj-(*=(?o1!B_,!$m,SbR28$NQ4i.q#/![ABOCeqp9mZoc,W:%hGEcIglRW+oCMMD;knlH.JY$-.k=bY6"EO`JV$?+BIDNeWC5/;Va %kNQ7n#S&"DPNISWBQ[:)hr0+]n^E7Z9S_T_Cjo0Qd;e=D"b+&/"^&,b'W %-5uL69g&2bIWeO]I<49#=LlV>>D^-RB$hKa1tgBt\>=j"I"]dV-HpbU)TH28FDaKOEoW$lX^4gl*GU=]iJ_WaZeNSIR*#nPR`ZKdS$f>UqYq %YT?ZN%?ED.F[Zu,Q!<>?bFk_%8u01hp3Es,lW,(c#^$s#:*K5?Wrq8KWZip_btJ?-(BAr-M^%$)0S8=_cTR(Y %_l,J.3'=k>+YO_,4G<4659-!0#"Z*U'F_1cj_kHfST%]:TCg=[IR.UnKVcc]ZkSk#dU4.YR-2Z:h0-rIpW/BUb`I.La#YTfF]LQ3 %)7DHS0_g"+.S.%MjI,_biNg\M)/QSpN3j!TWVo0Sd3@VJ>t*+D62?$::=)e`NZR#h;?0K6;VnjlQm>-4WC=jM/0]aWQll@/)Rt.0'$V2MbG:P":_f+! %)aYbGqF,_iA$Bn-SfE_8am#g[C!XYmD?Z=o*k;kmC_nYIqE)*(WN[=s"I<._r%;#ZWcP %\:/6@KYT//Da)EX>SfF:KdKO9#Oj-9nE>He"'s_/1d=HVJIs?^+f#YE!,q^3 %4mI%7"S=9LLMOkG'raBs;r>1+&[uBT"kq03XgABLhdhJM$0eT.BhI9'=-)R<@)Daml:B_FaoSgkF+*N#Y&W@T%l?O4q@hPg6;oYM %#ZJ$t<]Ho;jbWA3k80GO++NY0EU>I5_ZJtaq>X'#fIDun]Jtq+X3oe7b3P-t7-.p^27kL1DDuX)YI^`J9U57S4Z:srOM]%tjV[L, %pRc"N'YY70J0,@in?L6@)u+#lYk+eJGXgkF5ChB4"2Kq7n4h-=P"tlcCRe)U3$C:p6AR"1OpbM'Y*Pe9WQZf$Vd7@eXG^%](#U_ %DAfJ7YC=O5Via5\U2Bg@mgt[gX5+ahD,KD(@7at6J.JYcG*n,Ulj?-oN.iHM.X`/ak8lmHaSW8p9RgNt]"h-;?-H&CW)=T6IH0MC %eF3rFRX\Lm(38WUTAK:=?KB$-+\t4XDChb";\4'rs3Mr1LCrSeCC^4=NWtkd[ckoW9-IAXM\NI-jhe^?47CD+Ji'jFNt-oI2;600 %P$;%PGq.cb?'4_@"oXpAZ\XqgDP0$:r%nOhL76Q*4HY\'6n0>7*ZSTD,40>$:jcNSXsgrrDAs6eN[kn[X2IO?V4;Wm)"&Q0d(`Rh %,`nqa/H=c"K6$NZ.NjB\*H`Opi_p'f9hb-B6kQgl-D#fV+J4('E=p,>"WZU6%X9MFZ]]Y:$/12h:K\jtg=;."A6)ec`47Au_*B_M %D`,utGjZDqC`/D**r^'.h8YmZ&`orl?,)E@nP9&Q:g %Pa_l3RG?`g,ltgt2+US&j)ZGXTl>pl(d'F8j$%Ze:f*9KI]8=NTR'6!fc`W6Ve33!>.KN.\-MaKi`WcFRtq'6@jMI`eg7J.I)MYJANd_Z9^7-[S\AP,o"I-Gur/5W'1cX=(0l1BGdaC9]);&J"nEZ;Y(jm&M>4bT>,oKp=/Xp`%8"%P`aNm"e2RT %EC*Ym(S4O(#t4,4VI(VM()!I=;I1IaDV=WL27JFulNR2!/hHWpZ5ToJ %I)BS9ru6G?O$U_kl=HZ?;LErn&'`0.X/\5_3@"+PM=okYLdXXN&dA+8A[?$IOB'Z9I$.(9lC$K^?[pk5Qg/?k:3>jYeJ::_s8$#DMNg8)T=(uO0MD7)pOHPU'M\/.*cP'/J^W?SIY3lmCO8Inhd/52VV-Lsgm459UcBAZ!?N#VSs:%^:A*3aYP\lG %p:GXuBY5&/*DH[ej\op1c@\_1JYJ#Sg7XRrf_DU=W!Wok4Fb-aZqh-Pe=uA:nBpJsao]C5D!sJ5bfe:(Msd"pC9rdS.gn$Y&I#rG %Y^_7CLqo5m#g7G'S-Jq0?>:EG5q"]$?i#hOh_aF10VK+!)u%Z2#L"c0RVpEah6071HBrO#@;k?,Z`!;'XBNde+ %S5o6[ZT;5#4_/Q+/@'a7!FVP3HP2Z361V?tniOi?I10_Vk`_bBDjR2"8dEL:qs'SUTd8erlgkgR_hWu'sqoLIbfrH17N4fpOp %Q;h3XS3FF*l0Ct%T7LKtLR=aM)$nb(;I>rolXmk%R/kOQU54s$hO\k?Q[OhP]^,[>U**Unb9e)NNCeQ7=;'^:$e&>g3p0s@aIaF1 %I%.1B4l(7Pc&sf6a7-RO_ci*8,6PpEK(]kJ3S-r^:s"Y9MR1h;)T7oR6`-5H5 %(Y^dM-jRD8,&C"Nd%PFNL4HfNIS"H-:H\A(!^Ik4A3em9?Dj`:ot^R1nbcQ]"$WEOej4uJhi/ap#C41<5K@a %4#f-Yp"pFPUk[@_]5(C96cBSt\O4)/rZS_4;1p3M=Xpf_%o_SWDRS[eC@B@jo!D:YV.s*leU'1!aG4/lHc0,12%+dfk %T6<`sCP%Ja=#-N9C[-:3`RtI;.MITa.mCqT'uXl^("l2g;-EL2KMittDI!F4R8RMR--meQKrb=*6t=o+ %2&4a9)Y7cQL^a@^7j2[uq)f7=>of5^+-L/h2,GH(/r"mD_6LSE#jFr1=^@H\:r?a[g/SEh!4fA9GS>u91*LkLWrh)8q,mr- %Q%NJ$mH2AK;'XT.u-<.BF9ghn'4H%_*\i!KoYFH8W/XXi %pWL:\M>mm;7AQ6h]iEFe0?t-hUZIV"61)?$2P:FToBNkh%YZnZ#*]b(O;1,;Y%roN^Y)IO3f^;W$U9;S/9tED2U$_E*-"6,@b&DT %LHiZ4F!,Ncr,5"j3mCB]*q$&79rDI?6?U87N#t-ZV[]c0D,:14eHndA44J"PK2 %=)[7P(K,i^Z^mAsC-[R$/U(fYNS!b1XRA@KKdlTW^7t]iV37?l4-Rb1)q\ub;\.0N:7IrK6d;ko4:?/X"]<[9+PH:s[ds9*H#rud %eVXFoD?nICB0uC8UMP7AFD^:HmeNR/l,;V86u73%Palf'h\'ff^:-#]g/ifh4@+m:F5)YrO-Gm!]$#kt`["]q7ClU"h9hs8BK$PTI!R-G+73bi5`O;*YnS-tXTe9=DPs&FJ=V;)4'aYTem\`0'>DE?);c)18fS!5cH81@ %_B82E$ml:cqJ&s&\!aL%#56VK7SJ]@U6hA<^bW4>kqRf".)_\=ZpOlN.%UQNKcLaleJ4*ZLTfFN1#Umkl=hVd@rm/;AJ,rm2a_DR %_)qA@O4W2trNL(BfF)9C,6?%9Z;@6/KqRu!cLG.FJ3j&FYRBM4iXH>_kEP9>6GNSa\aR\Y#NoW)/P$7"9j%RTLGa?.rMjLap#D.C %"u`N:]=*ZbJ`eC0qp`*R%Bd1T?W%SFK%C7?TNC/"fYSJKK'C=30Q_cGR.H*.uAQJq/tTFO&+f[IRL34 %0*X,]QuLHQTH1O*IuNQdFT8O.jMeM*'alKB7o+"^ee7:QXTSXmB`OU=uCt7695fOuFD_IUB.k>E'3RKD.YLP8884Q7J,sJ6- %S:]FtMO1lcRas*Lei4iEKF&#uGUJ.RN;IZ,W$-^kiUJR9?tA+;->@m*&9P=i:0e,JZ?ZLD8.M'.l:b8;1//HCH3g!4iJbH!9Oa>P %1e&er5BB[J2JXFE8(K/KGUmpLd"/'W$Js6l8(5C&e]=_$`1Q[923tJjcA9lu^^11efPWOhSc(J0?_87@$KZ?Og:4?k$Ei6.PNQQ* %pT>YdkU_h0H#6BBWjqqeZJ^J?EL-1im!fI_0]j:sV2G*HGl_kq1r]nZl5aomNY6uk`0k4T(R1.NMBI[i60Q-/%5$.DohC$-;*_(N %q0qT/HaX[E2X@UgXhJG6A`T]"b\tM>#'plI].1nPWbhIpnhdunLhkX\Rq!E\nU-ZWe_%pA!Y+Pa1HhBW''YRE5IfJq\XRs_C0W2/(/RH\c %9a2n'l"ZB^g`V>^-:eq=@nK.U4hlpEg/IaZ#I]NrAJBL^<'_^jbJfrqW&C//9Z4E]1A3bMj0TL^ %2KfI%A?Q1bi#<2D.aWJZ&k11dJG3aE1BGKERi+`/%UlkQ2?R%Fc-,Um+F. %T,o"HS]S\U5`p(eL?MPS?p.W5V4'4`C^VBoPLVq$Z@Q10)K6O##a&HSg3I>Z1L(A9m.I/RZ:9TanRY\>8k5H#VutNiWg%=3JRE9kH?*n8k4]WMYVVm_`(tp7-&ISu;XHs_:pcH:`)*S`!k2[(s"%-`^S6O8VXq[ %EbHgnJ'#1K])7)u1s>ZnPDBfRN`rr!?B.-]%58T2^10j3Re\YUO*iXZ-pZf4+e-!XUr9,_ %GGqd_A$>^'q1tqY8MMS-LTJ@(qrP!`*neRmg3]:HWo\hWf?qn1)M0MJSZaPLE %:hi]bLguqZ8/Z$tNk,C96Y/lng0$t@SWr51``bI?-b5u+@5PHf5MTi9e6!WY3Mek&N4E:B'^/$"cY %(]kAW-J$\/2D>&*UP,hP#FJ+rD'%17p<9%**2nt"gO0`OTJUgmos7Wfc&Vs3fi)FOXT,MV_"5IC1$6HY_(4$'m^[&&*?Z7[G.&Cq %.P*r=d*<>lYlkWC'r(N1Cue?c)0T=$GYAhR\V1i4Ra?@UBlU2J'.#>rdEJkGjd6<,Kr0Z?GoR3cd#G,u19o+4WTg1N#,N=;"lE-7 %,dq:k#HA*2i((;l6PVBu]h.qMdX1CQ6%e\2&"4.sprWVPps21(8,]#O+B %+UB`Yl4M7(\*Brj#?-As-XS&i$hg %Fd4?WpFSXZ5e*KhbW!QJYa12q0U&pccc83.<+ZR:PK0C%7u."kPI:Wj/dpjGL:,o^@Q6S$Qi#ti4'`LfO8$p9r>*K%G7dhX\TDj" %KEIlZ3sLi>.XLk:Ia6bOs2t-`efDc3>W<+Zndf"VOjRp&*C:,bFkfQLO$!oR+f7Q?!lGo:LCWaJ(!`IkI3]&G:&1/YVeX2K]$0nQ %>TI.]d9fg_Ho$fOSSF^^T3'MK+aHA(_23b2D3@S$XpuhG;>^d^j84`5_F^V[WWnV9^"$#SH`K0fRRAZmrMR2RBf;'=@mu.5t3+-Xf9YQmiR'aj&13 %I<"Q*PMAA-8jk[Q#UUNOE.Z+Rk_Uk71P-^a`Mh2E$Om4&Af`MCa4YpG"\^UrSEZOd9+pHm>)O3]G2Z8jaXN39#XUnbnPtOogtJ7f %RkU30H/Leu1JZ>nm')H4!+0_[190sN0h_E]kkb_UN`29MF9('qAWZMm2OkhS#L9?\'6aBKe!iD+nEN*YMm'g>(HoF.G!k:5L##1Z*Ga(Hbn)-h\Z'ieYe^22lM(S@>=K!`uYb\;?UV\:57)SBM4[ %JC<+ukEto^=Xs.Bh!"kAS^QQm"&QX>`lufu9A,$F2Bm^_kjljLfS&")%8=bb/Pd_@Z-sP":-@.PXTF,jV3lNr(Y?4ndie"<*5%$: %`6=O2b0W]>mQoIE,`fgK`o^['.c;Y'Q&Qs"19SD.#[LN,Xai64)qq-)W_V>CI]-p6U-R:=Yu%qZ]h7[h'29>_cTXF2J1O],C1/#L %.eTcX#bMhP&:)ZMkij7/GWGi:_9OG]p4hA3G,Tq`?H8;u/IgN=$4rR`nk9>FbiJ)Y3(_l\Mbd'[a^M_0Bc!6Zl#^#;1I_6"\HC`CQ_f_6S>l?*$J[2NeZBbBk09\r@*TO@-W@rX$Yk2hRlB>rr&r.DOu17XpO=2@qlbpCNDHgbs3bQuO5(L0 %M%>Z8^O>3:r:WE=%),r:Pj3/I$h;c'L!BV^cV>DAdBgmmGoSppV.uU(a1ehnqM.)ud+Am/+42rE_D[/qP0r@D;0D50E*\G, %+[PTuChcgkXJ]IR/S<.o$"5ofX=1,XV/fLEP*N_Lf;_Zl,=dtYAj\t)T+f^ND*+EHlD91(I\u93TfLNaYZ-SRHBR$<9"B;J&,:`Q %Ut5"qoW$UlX,/9'DPCa]XHh*aeH0^G1g1oqVo^,!o%Qr7V\ZSfW_/<+-q\mFWF1@o<'>/Y@pJ4Dr1-+*ZbE5ttR.WRB'KU.;4PoFDlXXR\QV.Zps(Zjg9(MeU6ZE@5K,iSHJsc";^1!HXp:2=];OZ[6?A %e%tjHVh`f](K[(1)',N*?EnIit[Wc3oCCd6UN_Dc8r,$V=l,4Wi[N)@<1@g]E[RZKWH/=qqCn\_O7g\4l.)?73/OJ\r"XX7JFNQ)uVLMSX9th[42L"KaHXI/+!RkO+;$[b=Z;qK-Km6is$O]jMNgNCN8U\Gg/J/(j(A/A&(m6pQ81]/0(D6EMpqme1?2%Ld)WF/ %0noK9e^^G_>E0Uhgc9SU_6O8j\MS9O<_78OFdOmFWWnU-of+X0RkGJtXK %]],?9P:2:%M#^44meC31^J'JE9]6#Y^pA0I,ibTd0U3L\S5DRjVDF#0_0Fq$A\KDbp_t'Na;?XY<@t=EUq-f_@fXaG6&VJD/?%6j %#oe_mdk\^?()bgg@ImBscVb3Fo5UWE_5]9sHmNC%LoSuuLIC^Eq)1Q6n,koG %5g?AXEn6aq.C,U-N\g^5Q.($rpcn9rXXMRr?)'gci$5-/*rCb>/PrJ^@]Ed`"S*D7?TYZP.\N>dUmrf`KCjtanE^^6A4)_rV'_1/%ZI.cpi\7GT:_0Li\eI*N%p-&O-QQhZJoLr@FhoO7hbFQMa^,aoKuI>;ee[>=apAc;F[F*orO"4G^S:lm %k^74U2;4XCr'LZs_7MJ>hICL5XTOOQgT_]tNT\i(C_O.Y13tW-n%!e#9h:c+KI*qEF6Gmk.ZB$4oru@QSdbsf$(:e`]T.HAUBp/i %3U#soKo7u@UG)S?1/_]AgR-%F4I^eS=Ra@#JbK(Qcln-5Cc\",rP!%*`p(X3?T[\LYZH`b8eQ_oiC*cGC_#:o>JqXF&#L"t)A^ri %'^)Ee3B"`1-7"Z@fEga;+\%6>9078I>2305,h214^\`W"WB(MAX6H9#WOpW]A\;5i9hs\A$MEHbit9+#BY,%(S/$7L)di5NBfp8/ %0h?1m[F:7cT7l1/.dpd*84**>&4neEni-BN>SI@s$Cd'MEhs^V/"e.(4D%<@cY)YuGO$5q+)2gdApY@D&LY?:Eh'mn6 %/+FPr!-\^&Le\d/"$)(gSnhlDr'1@d1Jh2n,+tne4@d%-e'bC;"2i^WA;Vf),(_bcEC+?BT47dj4D5> %ndqd%/QN,KDObSGHk0%4q:UL\hd#-H6G#@b/AjN84XaWI_%!K5X/?7$]5Z_JF=8^=.ZoGQ7l*05$bUq.i)<[m;J2Xk]$,!V[)kgH %4kg[$)oX5o1?#)%ef_#\X!m#F0KZK[0IDj)LX&2,fFrG:*scl5,QO9<5=M)^)?FsIR$a[o.@'Ggf#eM:_EAVb:2.QqOFm"LZKF&P %p3dO?0.4De"2"iPLiDT>F[O%"/m6gDAlc>oJpm^,j)0J\Rum[Uh^ESaj[A<1Ji.LPcOQ0H^SPl1eWnQB6LCAk+$HhE[9GcEC,!LH %N;"oefsn6D6Sb4)l5uaSk(%.bNa$;+14\]0RR7oOT]!is]T1qk#+<5>E-bS!1l2NEfcaXsN3r[mo__2RbKh/-"E8SuBsa,#@Q6k=I$F-S6B7M@g^r;;42<$Rtp9*MgI#.=Hu7e'AQTmg]C`rp^L`NSc5Lr&Np;09/$<7Hf_VNK'UgmZBN=,=h'^^3u5, %Xa[WsrWS52*7iE],U(s74g]_u=+?)rn6fe,Sr?!>]?t]\Q.t]=hfGk1)Q3T&Li$dtAh32BR]+.2VhX'LK9r6F_V:f#8]" %N!?kB54GQpcgqriMuC5kQ_8\EYm3[&lu;A(%Q!ufkc?3d5N$i6c%U'0O8f^9%TYa2R^kO@!ahb`H[jS5FE_ll$cR %.>6@ClQL-]l#G\XN,8.7&DC,'h\K`_nb^B!5<\;p@0h3lUS%D_)idXLW-lhO*#P,bof+p??!-J&Ldta_F.kDHL9QECio8GK+?b]b %/PH@WAud"8n=._8F'ciN1J^-rI4!6ZE:nn2MK)$CnsN5=C"kgpFaOu$lAZ[Qm90-teifo<)lhgf:KS*p(T-tR@-uW(3!j0#o.=BY %UH6T!YQ+W4A3CID`NsPl*u?X-[a$?]:#'=7X*/INE>V_64VkYGid4r3q#C3>/DFS/l#%YqU(-(dI$Zm,&;8"=P//K7KV$`Y6KWVt %[J"!NF7DU9@q0`h5:u5-Scn`u[U9o,0YCmJp)'6VK9,FDll=*3cM;>qqs7@aR7I)26nB%10qed0k)*467]U^(PlY=g0;Dh1/M?`P %bMFan+hGm=/$Yp`_\]Q4C!9ba.ln6PBlWT,eC3RY:b7hD_`C,$H&,NI#):d_"[e(j`Ip<3#`HN$@T8FSlJhF7S9/`s1\De^Bh&W` %\;Q:OeR^r.\dP99K?eXG(^[P1ju!D%iu7d=QKJ[,"89XT]');4'PFb-@pE'ZW-QICci3IrJ+&Gec[5.t2dfAdk8GGOfefi`@n;SK %!M+7NbIsZV-P:Nq8*!qMfXhO.D"`VlgIRh?UP]<\J8)nE`9;f$*t9KEm>`Y1nSH$eB1`)JicCU.[Ts_)/KZUkRuSki5.c&)"^2MO %LTp?Fg9%fU?]Ol!3g[Ph@iS<'e\r&_#NP-(6UU'g^(;o>mYe:7]40Ef'Qj\$*BB9___^=p"_<](1]o2%J^#7#Y&;3UEGN6[70AiX&ds(Ud0t]Q6=t#1mR)rA9%7hd0p1 %JIl]V&8ne/ci?%b>)))'H1PT]5ZeHV&Aj#6'&kP6g3RopDW,\j>Gk;# %EL@ZZ0QTB=A0ZbM(A,''dt=WRMk&)VmGKE-[1)*-#+qH[?.fd^:M[,8f;I=V1I4bW:!UusA=S^,rVDg@O#@OH\ZkgZ#(j&/KsN=Z %.2X(1]GDNc)9]6kHS"qF>j1XqHDMs%cPA.9]G_%@bkEF]U'FtV("jr/Y!#!$_ljkBdPBU908$-D0>ht8j4(A`5Ik+iTuZr^T%rrL %RMUsLKYrB#[j+**g^G?J2H_ke8JuLLSQkFc)VA*11qkE6T9c5/nF]X@h1Yt.@RceYoY;<:fn85TE1mMD6g/W7/p&3Me7*j?Z#Q%L %[0pE@.dsf%KOrJ&1KFUfnf8ffU(rf8FD?",4"iKN']S;aSiO)4@C.SEd7nX9]0:n2qL %03c\d"qu<"`H[dS-N=k-D@>T:_W,Sa`ZLLk]WEk*MO-\D:G9o]`4;nBeYQn+lr<;:H"pToWOA2(R"s/`^Xg7[[kWt-#1LhPMqc4; %m#RdQGk>R:?bcV0?XJ94QQs;.?/`FT(kX!lqXnM-O4#gB=*uH:>IiAu7VHnd`A>D3n9"tB9\7IEI9Hcg\mR]RY.t#$X+g=haaUM- %Xkg^qQY>+f"8H\IcTRgbS9,g>'A+\F(m$YY1X!'2lcdG_npk\=\>,M3n/$c&;Y7U&b@cA%4!I:/6::XjZ/#N6r"'bS:5eT+EW17d %IF1*D5e9(2_lDn=EUYr,@;!n#Z)qSdb+8;6'C\%!VpH?Kh[COBLDo-gjWtmn#^uOF+KcJ5O,/h5&*]rk %M&s^Lh2hme0&.Q"r!F[QW.s;!4@:n2.kHHJ2N.!_%5>b27%B_ZDmCdKJ,AG\@pJfqOTLjDpj9Em7pf*+;d!Jc:PaS20ok-LWAhM0 %;6Qi-$N?b;6?aZ#k?ABKr'T7LLYCIM[.52G'Ma=e,NomP:r=fI?* %fjQJBYYrFqNlFA)*PBWuX=&>0U#<;:=-YeHY.#+Z<(ip]3abS@^g,3UfRjpYkH`H]`roRaM]9I47,f^L3.o0GWYhS+dBX_((aKd4 %"hij7J4XLm[epE:c6bA_=QtYi]f#qWPbnYZ?@dUnC2Tr<)M^f0ObXtA8Ea6I0:ran^]FoV7 %i?+MJVTQg+V]VZdIR/un=r4@W#HkB\CqM8=$OXA4%FuNcJF9^3m#_[h#'pQSq4qt\Ks5`H?$=!hUKto5]+??jNsiTQRjY94QT!AF %-],tW;pB=%[2@OA)pYeQFZ(@2_8U84BG,_uHi7(nbV;9KE(ITOPc8=Qqs?*kK^: %`UEXtHl^C-D#*%8?$[-ls!f7MN:m""pI^]d'U!"-_g2P6Z8b %.hVX4W+quJ)I*P[/OqkNBQeSQ4uR&dMVu-db\l9YMpp;\>S((EI>mOMi`V?%a0SSUNkZn)HDQq3u_HpDL+O#&ZKY\/d,T %C-]Aa1()-AY<4:as-n:Lm&W"`$Z%J@2Z7$aFB+PN6b=Ms\.h"\mf@O'&+5n+/*Ah_%!KW>f"l+Np9^#hA#,fH*%M$m&*l;MNu:!3 %5CLE#nac^SiV2u"MRTAr:H$UK6nZ!?g^(!Tm',, %4&@sukZs!p9Q(Lh$)(T%0'ps(9T$W:8Niio3''bQC_4#\>ne^m-EIC=csfM6lR-$:\46eJ.X/#F6*(ZY.#TfoK?`(()%E[_dC=FI %B!^l-qQLBl'VUG?XEM?nq@)a:IW+F=5H0JLI0cn0-%<0=m:u"W=YjPk-JXaKq@FSPj_cOhH49[QLWU4]J%jrUq %QaIfYOtQ+5K[=i=N[L6rq[hAX=R'AJSL2S0=,V'Y["s>o-I)*&/qT"3m]&c,G'<# %pNFP\#+b@ZqNJ(r4bUiE=S7"G*;Q?n0I@cjG]mN5*&Lkn#uB]?$d$=Mqe@-'%*T8l?!H;IerXJ@LO\1(@dg9#*ifsL'i=F6+Lr-W %&43eq[2O\!0%="Ds`BERsl&!H4G5#Bt %5/\\IDhABE1f3FlmTjfk'8G\e1@d^6)-`uX^c[?rA$deY_?QMS/VL7r8\(Kfll0^jj(/h'f3_E06S4C=@NBd2=#>Lk;"JX2(jfcW4?]99>,A8RbLnQ1\.m>EL+VN+3W;3I3"-JB#^+HO*]Zu:-#kg %V>,2CqW<6C(_e^iaI/0EG[YOYKjB.3!34%MF).OjWYl7P+;-)dbmPR!+*o+3EAU(_!Z-;b'Y/#Gug#e3T&T2gbdVF:0Z03ig;RC93p.R_q%m2!;n1JdYOH/"AE`(!$k@qI;*DH?uuNYM[,. %>U"Lr-q>;Z+A';P-24jRm;U%=9TB-_UL';seaZ7Tb#I:CocmQB8u2)P/NsQK*L(U/[RFOZnKIJ-QCO(OTVQZ+V2"oR9J<3)HSLZD %%s=5H?22U16$l\.c5sU'*nPdC\jCj=dJ.J;<`0eUcI*ee>rs(]df$X)KSV0e^u6N@LA<<;Z,KdaXT'ie?"q!KrOsS"8YVUqi9"kh %;9s_C>^9PDT>+[`ENn:Q4%$N9K_s#*Dn>:F_2``m*Q+F)giMNS3S_g<>sCLqDJ)O_lGNp,E0c3UhW9D!:Xegg]q!L/H%%dL6SBU' %-n_+3`CgM#R_\[.pdplR3AG`h<%0[u:!JXOG?)_a)D-$6o4%3\a;0Z[P/\++FT %^Dm`MK-WEs0#uH&-jpNub5iJ%VW"e,3MJ1Iq>8!XE=tTn,'5sL0BN)f=u67ZU`_&-R]5N]]-RJ7,i&=o&,+VaO8Jb-FWuK;jtaWE %R2AZOBPtkK+C6Xd!$hhN/n*!N":E#B0X/+m@:?qeL3?uB(l!uXE#FD(@DEe9[b!2m%%5#k3XisETKN_Pj%R67Bb7'V/uaX\D%S*S %Y/ubZE>#-N_K=!GaAc%r*Qb@!iZ2b:86(K/]:7lV/?;"C]hV#eNSAo;'!e@jh";B=Z>V#B:fEQn($K)PW_LP+8u3@K,K=ZS*GRN0d1p.=aF#ggra_9"$J1GbC\%agfhuZ" %@n23^"r9G%^n5l^P4hSnF_X9*]n*Q,.I&C?`1I)LVTb4'eIM+I[]Ld"_c1%1/8.a$%c\MLBGr.'7NTQY(^)Q'_Mj.\TCE!#F6J6e %?8]IjbmC.Fm5e1Pn=( %cVs)mL8ju(/gS7:+4IJ4:D':*>b8"^BSg,P_/n]BL*E,helcm\$-.ndjk+IR5A"\+M1(A-79l_B9hkai>-T"tDT6EL$AB\2(U(eJ %<"0nlegaW?K/alM-/Sn_:+E10f=Gr@1tpaE76&$r[EXOMm%!o-Af_sJH,3#_^k4=noh%rWgC(P+cL>RWg$WT1#527/492>mAR%5=<2QH$PpN^,GC^LW/LF>)`ps[p %98&kZ[4#Boe>DkAm>1D.>TSQh,?qhd7Xp#Tf-0p>bb-#m,(K"A3XpPW5pg)!R'4^Y\Z=OO1(,'(Q"qMi\d/5S-=T)+:5e9E^,G:n:t#aaY44TR)+3_lD,S*7VCcj\;iIb?p)1(CY/P!K4EOPR0D;r;?mRucgCF.7Q-W0QeOKiQ-mZ3*]_JY_L7]?KO.iHO1'M5Z_YfC-e`!RomcJI&*+p&/A'r/el %_$9gcE]+)$:jE/f\(L.bUF85?E[P>W"!gc_'W^(0Y3\ir-nHB=8YJmk]68H#2'3CKi[9iV15SoNkP35\nLLn.?usHG6,o*MHkqNm %^-F+MT'/rf*4VA4V:bj^.?\V=)!P+DLFHIX-;\\/CO(4?E2o3Lq$[G)gDKs:R&mc"JJOGIIH%,@h&k,P/<(L;dnhQ&I>2^,U(K0)c1%YubFZ0."qcK=*P:/cN2h^]Ku0s)$[o`7U%`"og4VBJ4Y"c8EfT %7tdbD"@U,Rp?/GV967Qg=Ht_`kRQcH`I;Q>.1WZ6P/hQg<9h)Q?p0OuiXe:F.F*q7 %P54m9;m]6_.*c=;![[5im7?T+>!Fo2fadQM9b,VO_8YYtSl5aiFjd^TjNKjQ+eA(._$.jP@\AU-TJ`kH@2LD>t`H7a2o)rePGA>W'*@l,#g %:?^I0:?iJmBe@&B9)c2PSs2FqE+kL$DYZ(B+uoe4(s[$0_SAcM@bOMa.c8.,^_5=2c9qD8-7[CR9Na*LXS[)n@$MediTP#c^htjN %^U(RXBq-rhGuBDCD5msu4f9Jo(%lrs2fg!Ui/@2jCmnAM[7oV?=[1Kdi.'6D;i9:9CBE/kKJBi`=]3hj:mW*QjU %K:_>]!dE+GK)C>hJsH\.itEM(pNC(^cP*9U7FJG@"]g.6=Ps['M#9\O##A7#^;,/i9%N0]-ogk=( %&]FcFkk)kNb3H(6ia$@kY-n,>U)e4n<215^p`["DVlt1>$cu6)FA0:-$qs.alrCJfJK;'1?N&i68;rnH#7rlB2`DOoTj\$49G8<8 %r-e+2!N@oEWh^4mM4k=9/2E@q**S,9CDj3Aak;EH"JWfk]<33WKQ'HgK)#@&qRNcZe<''cX %Xs/\309i_J[V!7;%!Js?:lGCNm,O:Q%2#h&QYMroP4&(-$^Zt=oj_@a0$^Q\3OXE'gVmMhm@e1-7=sTDoY@cV^>31ZYqK]^nqjCT %3p2o4D4!A2<[P=j&,A3Q\neU.i#`>Y_eg6F+u4lt %$ECnsC7-d/G+@\/:ITGo@kY=rWsn36c[5o4mM$(2Rs*kKER5ZWLj6nKR$)$%FsX`JI7q;3LrB.$(N?]*b#mAkD,'aO4iAN5]`eaVTEV@L_b7KiQAB:1iU0s@[iQokt:(FQ;1JlSFdkkQH$k8k*+&VPdF_=J(iIMOdLeTWkHo2+@CrY %K'p1'1'1W$Y`H@X`L%:9Q'Z9Nm^p!jm/`30A[DT!.YstMaI>JT7QSbU&tuh+fq0X?5;deWYGe8("pfopPO&LH6K*`NRROWJc!h<5g0@2U'r,TaIj!8O %L:Wc,TOcJ!DT$fKgu?URlQMoF\HI4#.WkT^on,D?J%Vp%qrA!cmM&8@n]!r=7>MF=Bq"1I?$XF4]2+XK@$hh.SaBrBOlZ^GG**Q9 %XfD.##okHW%bkU/"uO_V,[dG+;tpP,Jl#=Z`+U_gFL%;bAoD6AAu69Plg_\kDsE^Ee"UY7!WsU[E44so!-\!&F^28jG@!E`EG$se6hMmYf`_u_DeNegI)eH40$8[\&[GS("6mn9-#CfrG?,F8'Ok@PHmf$ %,7:uH>."@3naC8I*7P!T`(K+uH2*BX/'J1>OJ%06.HXbs<6_QZVi;hC\1%:[OFl+)jf0mkm*Hd`e?^4/?BglK"8:f#gZ:0b,hsa:pIMeh`JUgqG/hT9c`,P)^+A&=1"N-t9lHrd./*np6m?9e %7Z%dh3!$3$6r?f&$bV/WZCZ)@cr_gZm*uA]A\kMN`Meii8W9sD>+?.,ODD$m'X^EGr]dGMa %`=rOO!hc(l-B0D_8a`"g2HI*0l['n#FJo5[bWN*$p%uZZI9K\//0FOO):eg4F='4YJMocYBBn*iVhP=A8*Q-l!4$,H&,9cR^`CGPQE]DGW8pWW]*cILlL@;N,,!bXItsfE(qUuBKkO=h[D/,LTi`T:f>54?'JEp$AuX$!/]sn %dSG%=mX%D]\>1!>8Up53,jj@]))W0("#kbS*?.rp%Vugh$r8&^;jsB'*5MS*XF0>agQItVXQ"RQ+IsgDMIFocXtMIKXFuK9V5GR\ %[1)u8/Z8EnD=J#J:ne71b337;.Y##fIM7A5.#b*gRH&AqKp2Bk>.a=_,TSRa`:`_lteCOu*u(l%M]M"jUSniqTl"/B\$^Grn6ZH3/d5JVnP=[\-_8`bs-0p6)=JeH!/) %F=?OZQD%72phWbed'T:uTNPaQbiUl7&I"8n`Y+YJ]oc#(.MNtt]'IdL_,p1mL,K\i(6R+P+_T0On(&p9f$#_tMIg6!D-^s;8d3h< %WZ+#i0L$C\epL_^MH@2QK/i$fkXM`dh-[=gg&H)N"9/X2Q-DuH&u?Pi&F`']p5+O_&;<]Y^2oYeM/bQ<.o?!WjKP'qB4cn %1.b5 %24,Ms5dbBuGUtM`M"s/M+k!n]XGE[hd2Z+B/5OSD*Zh.?QIYBAfWERFkRS?u'-fVVrL6Q\H)FWLZ3Y8U$AtR>6NDqXe?<'V-TJA_ %*)N&jOq,3VPl$"d32.#fR0Ng8jDhup#aVkhB6.o0]oPXqTU_IlHc^h/Cn[48TbBC[6o`Q!guH>6@hBS-Kh!LJ$B?>EOFb1a@$9c' %j+'7MJM'W6*.J@aTu/5"4Ghp"A)'rDnnCln1q.faSi5UgkDe:QFfD1l+5MQ)SihV+;1?q0NPE[Y\D,`P$$@6!u;d@IF#'I4#u>W&`>CRXP?i7hc3^ %"#_@8FSEO?el%S[<\0aSp^%mj$Dd]2Y$jGuhH"EZV",>\O&B+mkA$kD3h>3EuP"]%?%6WRQp,b()4PCXRVND7\p!@Q9> %i8bF#Fh?P);Nr,QFhB!9e@1;_5b10D"_&0=j9)(kTiM]"IcJg/M[7PbKag>-'na"7o>04Rm0:JVDfu3miq1hCQ94ri+FI@E<6bQC %h^t,W_\ZAuhM7pT/3&tD!"h[5+dRMAM0s:1Q2*^#>C*)*7EL7j-"-pV;jg(u3D.;J_WdKRO4k/][]q:LBnd)_BKkHFq5nL3Zc0>N %Unn0Hpp.MgK"^'+H$)B5VT5d&0-Q)k3mr!m=]_*QVb//:S*Y9C`1,1\U[nLEGhg^\aq?G83&t3S7mUXefP16gZ\DLF:pY^];IW?i %N!AI-XCF,-AS;QUehUCo`0\"UP^V[6P4"ij6:_n;P67<)^l*[bCJ'$<9)bS&!=+a(&`nZ+AZ^gWdEL?sn07T1*_;aAg\7:Uj/C)X %Ch8_85LFAq%&J3>ku`%FQe>s*iZI`'V_pZd.brs"cXnX,B]Q: %?=4]oLS1n]3)3D&_k.d%E&`SW*mN(Sg+@!S8amc[R869:(8h2uOGZ5VEarR9Ai'U*g?^0]V4K%Go7YI,".Qtp"s6?^6]@QE/M5KK %#TWb=hb=#jld*#EgP=lmALk"^,aaG73\+2raVe"C0BN6@>0XPu?G/%0"dcKsjOc6]JJ2i-Mf2T^l!2V&g6rIeE.-uip^U$bV-G`8HCRM=tVoWj&&UC+YXVJZ%P>!c: %,2[8<"V+[*,?ZHn%\6ui>WK/7',3mI4W.<+YbokD;^n*F\3YZ1^_+/$k"E%5n %+;eHqf#,2MNX(mMI;GEOoe`%k@!gQ4@kgb-^=ZmT5=\;@jB9o %nhaLqSZ#TT]OO"UTWO(MKOpPsnXKq0*@?:8iVe,)HE\g,a0eA;"$n_QOgX7M$g?'8("J0fl7*A0?k" %/STU^E3!d6MK9,HYR#ARa^%AHXFk.7f"XJha)(td"f8j'4:i8h9U)iS'T4[]M[=uJ#=BRGc#Nt*0nP'0)/i.6$X]f1)4B,ROlTWW %P^@IGUiq[oY7'-O9mZVW\K*C+"?4qrZ?3Z-9]&3p=&Ge?!Mk1fG!C'CnK^FC##h/"EBP^?5ibJ6"B30:oM4dUmVJ:mS'17295nkh %Ya8s7/1dE:>M;QPl]`(](K[mUo2L.2<-R%gAgRdlLd+Eja%PlU2Xj5GMW[d(cZF %pEh*`(,*pkVTR&lTji,PbdorfZ>/=JC-CiL0o%0p8>cX.h5'2'q5BJtM;%SK'\+[J*t!>4q(`?$HO>(Ppi/24)r?*`Rglf>5DE_( %b`5S>RYjIG;447Wi$95Y-/V5b#1cbq`F$To/WA/'V;Wr57A?dDAIN*"GYm0Q'<]h\(QL$P#,6c,OdkoGOG/ue!R@E-7].M0d:">h %k@/pj%LkElou_1.E:#2n`!m7(cF+bnp=Fq;/+QTR2MqpH]>P_oho3SLl6[/ghp;+cS_k]C(Y_Jkr,!>;KaC\S-3orOG>Y/QUJ4#h %[hr3LA?.++BjjOBo2Dth)nlI?*HLNq("8%J2p4N2L/SA_Y(`=!--^%LiEqV %X`A<@8V>DFZm@BCJapmCZ(6Tu3Vh7_'Pl:_ci?6TnZQs,1h&:#E=*,W@;S%X9/[,=crBK8CmE#iO$mt!I[/.m'3Z`qP!/>> %oUht0bk-])n2bCd],>TL`-*UKP`[LL6ns"Z:)qt.Kb:cq2Ybn>,ASV;cTd84Q/spV"=BPk+sZJCJ[nHEOLfqe$D;Sfef^DSCi@uqs=g5dF$nT@7rjUA:c-FJ]fcW"j*e(`eo;jiT3ip7iJ)1`ST$K6Vr.>fU!a*oOKapDtb!Q@"(sr2"rX$;F$+j"nsbE.ltQ@A\8n %J)F,EBpYu!P>;\/:$F"e8D`TGjMkcc++ce>:*/=C0R)sS] %4baBeO@slX?%s?DONi&ZIKh(FOA1%2Sq\fHOO&77^'6^QOAU@9hhFh\JCAl(s+X5+J5L]&+,#:2'^W&\8$#ra``@6*]G[4:cII00QL#8$'\(LNK[HS,udVG'NQhi/>6rS,k,j%JpCFW$.WU)8i35V9rMuOdD?34F$bSOM]0o1 %\L5o%$2&fml(CRVkc.)d-l7RU7oqjL83/3H=M!]NL"]8i)G_/,W)u&0e %M^mCK_(=(s]BGuC)JYu"g-qD]<,i<=hQGROJW=!23#@M%`B&19in[TT03$@-2"rX!Hl"BL5kXT?&/_DRL:W!RNP\630[r7uBI&=Y %MNRJh7A]OOkSS&!L,tG5:!$4[1/t7^<@pT:bDTD5W2gdcE*h$T0?Tl;#&((S)JY2M.\mm"dYk\NW.Q![VM=B9Takihfc$m&L,uOT %c.Gj"2A2aD2/>R-gK4dVU7*#Nm"S]V(Z!q?"H>h@=GkfqK(2`%2gFu8@\S)s4F+cUc%V9-5h\s78P1)>@;4Ub\lJYHfrL!$U;*ST %ra!ui^WZVf^\ju1FPh?dT.l%jOcWjh@A:D59UcI %_?Gj2@j/VR_F9ArCE^I[_M,Vkk!Ff.`5W1QHSNS0_?Z$uK-A;)b!ulMM`6r3`/'AoP9J+)A7U#WN2AE&L^[be1GQ1(nkP2XKN4NEJ1>4Tu'VnoZ %4d&XPjJ$Z-$)3*q.2S87:m!&eDRl^Y"F;bqDTkOZ-ISXqQ"t7U2h;lBW1.kYfEq4q!p-#cb"j,c'IX2lPnj4qAMsaoEd2/hY?'P@5Ohadf %n=i$i;(2qH$mY/W:a[tu^f*p=GRhdcCi`MH$J+$U;)q=a/uM;!%7R%eHEbGuNCTof*'@c;<[[Uo1fQlVWi&G$M=q$BYgW5eldM@Gmu%=Q88+lP*8$f)B[BKM=Hhc^kCo/P?PL#0O)o&J07=S7]]<>@%FSZi*^;K"ptY!k^YL?_Q'60c%%J& %KM]rjd1YBPOHj+4NiZtH'@/^O?iC0j7(X35?pnVMFUcj1OR>0_WMM#bNbMJ!DhHP;)&1l1dE7uKM*[8_f:Xe9,=dh\&<*^pa2din0Tq.m.ko'c.0[JRn6'c[@old,@m*fe.AqCi(K4\LMVB!F3r@EV>eOCUU$0b11dd(0D %j[nrIA=a`c6mYJGi86\_jMarF47)DO\P\MX;%*."%p[ANj/?5G(>ZA8j`gImaJPIi&=],-(LBFP$]X7M.A %lP["P9L=G(,['Y)HpuKh;.%UDc%b_IcJYoL]PT03_Soc/""Q$_O,=5o5K-g7\?fAd<&88plM;F_;iKX`3!6k]#N_u]aY0bZ$,Sc> %-<\R`@2M,E]h/g]rda.)Z6s,f!(b;ibodm$fo4&oduU/)mQV8J1&G9>#]@b3:;$Z?de.!BdO@('[0R@@EWAV#mSYB-q7M;ZVZ--H %OT\mDLuA%\[2Mh5NR!jm04)X_G;/$QK0W5h%b/u$B %1p?YI3Nq%:iaY!E6d3!T,X7Dan`4UL1bu3'@;.,tT7>/!+31BlO(XKB>$RUb.u6SrQh2(dAYC6@=>PI($(AuZ=PAEb?$##2A-XJ> %]Q@>*(bVmgfo:%sT@bm!`es_CA"-YIqNQ9iCFrtfG-aR4A^#L29HLk,9'15>&>W^IOM])9kk&;?j%4+$;e?RHQsi+qN0?Xhc8]r% %0e-Z!BN+KKm*ddM>b]0urj&*`LW7c241>>Z6hZaiqVF9XD)+nj#Y]p6W.j@$7Z=cTE$eAJMUPO8,\Fm:+E]bWYOuJU$fVW2``:,?(G"G>;ESHTSEnUS %)I]mB!N^H868;1a>Kc?mfd-nr>40pPij,El\:i=m/XaNfaUNF&EoZXUFu@p/=8\.^W2I)X%?@9g!9U6&NqdVJR2Z\n6h:E>gK:2G9qtO[-+HfuBpfFoX&$Ocm %MG_UE/sL.FVip842[=[9?>a_)m62q3ZO)e[9<$FO=D/6fThHrWK'BAhr->=A#l`s4V6#Vr&e1]%a]D%*bN#F@,P@0E2.h5`8U1#MAs)fSRP[?BrM6giP\!/?QGbGZ,TmZ9;]pd-44L %^pi3NJ#B#SS0D[o,dY/JRr*`D$jl:8raYCCV&k.1L5iCN=#k]0jLZ:UWCR&q&buIPAn?+3h3aRQ0rNmdg(US82`Pa^L]WhTi3nE^ %@6(1YApOmZ[",cFE>['!S8b/B+g*T5?Ig-r;`^Dl9>Fu/@PJLCKL`EhFN;<.!7:D:7ORiD2SJhsr;L7k+D"]l_GWAH-d5Q/lJ.+o %9XBqLUaM]N2eWuE6I7sh#'(CmP(fQ"g-G9c37iZtAZ*hQC9UTR9N^/(bTK/J,\^OTDR:HI@Y8?jA?K0UY>%eG*[ltnrfg&Db#jTn %/uT7cipSP(5"u9A#b??>"OGUT4V]T25sd^1> %Zes:+1VZ57g"BQ.*BdBOPa&Rg40tle>01#7KpKoG.nL8R`m!EOOCB9j=kaXhL01p5WF01lAn(kF9%eJS0F-CWb+q?($qIUHR)rE.;3:Mk;Ugped=gg#T]PKTs>Ck<(Y'j__EM/2mjZal__2?hSBq"Ltg`6g>dZp%K` %Q2sk[Dr38$d0(!`/[oINgckA+YiDhJS'Hf)=W38DW72"b&M(-ejB?6@'fHFuZ.2Q'$R44K'iUA&[H'MGP[Nf2cYPPeb"]m:BkIB4Z.O=T9QER&fF<^,'6Yr[Xjk/u)orYtXX-RW:4hQG]/%I*4r/fG)hEu( %.`95;!K3++%Gk)b,O*`\VDXNY%A[>a,-di7Cb._rOf?#bg4nUrThoVTWZ0+L?DMm#ZG*"4n3no' %9ia6<+218\LU+pn!:iQ0q')m[8h`bUc(VLc/-:53N4t*@LGI@LXdXa/I0TS*/Gh!KDq2*#2kXQF)BSR-["6HM:RZ.o8Q,hoa+t=T %YTZaP2lc %,.UMf3,=N/eC9Wlks4hD*;neKj%u\?[r)8=An5BM2=6jq%)A-mLk%=&0dH;<\oYNmUo]Fm'630'Z8/1J2lkV( %"5QonBhU4TGcO;TKGnrpGm!7R8l.nG?T#.NbK!ncm\WH`YUF$p;Q_mHk02"R=f,78>sfWi&c7.-r%+iBg4DH#TmUVP,tLG)Z/Ok; %JpRFs_,o?]bH1l'nB!dn2Fnb?@)LVQC/CJSZ/J898=A-J%5_O2";m']<(E'F^ZNq\S6MAO]4CJaXC%>:]]KjE]]1;*).k[g3U`7[ %V'[jhd"gqX'(MQVcXi;X"kbOUS,b!:3'Nd$,RB7jS!n@ks$AGG%X`;f1,6(hQRL@a_eKLmSrOAZITWg*ZGC5E\PV$SI[9D1X]-hb %ff@:f?8l"]fo4#*M31u^QV;"YoaLGef3BeFQ'mnJV/FMoA1[9_n#PApWE>!"@l5aQj:gZen4uojUV(@4SYNbQM:<1iRGOUp_9rAY %NoK7oJb,?Y.5ud?X/N`B5(PXRO__cU'JoLe3K[:kgXaMUijrU>!?JH7!=0YnuYeWWfitI\<,QTt7r2 %gR*@"j#1YW)I5?hX12aEn%H6rSU`1elRu*)2P[-!l*XPU07uB%q0"A! %>8f4!Xk9tQRV5Zrf#m1T;J$$*@kj\Ga$f%Se==$%[5t.*=H5Me$\/+F;og:'X;aJFW]/(WNC%u5R\1.UOU/nX-mLj6 %Rb]"kAGH9Th6t-b9FIsLjsKmp4-?orMj%L1<@TJ_9K&;Lkfu"Siu0XIgq`aQD-6+o+.V)Tf7Lgh7\CCdBapi7._qM1OLkj9afF;7 %$cjjJdP//hB/ZQDD/KHtdiSM%PhP*Rb7d,VUF^G-g2Q"00j5JdmV5sfR/upo1MX%';lNZR5WO]3X%[fqTs$$k9pj(rWdZ;H!A^=O)CH8(9:>V2dfC",P9fdGsB)K#$_eu-r^X*C$5c8Rg(f)5gJ1B)oW!TTt %F&V=/BFI?qD$VQ7L]i.l%o@HjbSmLQC*`ucM'I#6;8X3S8ECY$0#(^/41;-!*%e*_,B\L&1#-.V'PL[/96:e4A9^=FTilhHB`rm] %,0UM;PF-%sZahVr4KB58Ehsa`%#,dWH7JuTPD>dG'I.( %BK*.\W3I297cQ!WoL\h/2h3^9JBkJUj=s`,EZh!t<5P#S[uW52!GL"\XF^R1ikPIp+VG&eQi<5uI(W+,?L-n.Lp"]fQ)`")V$99C %%3:u%;\6MN(KVi'90rmGip;>Y;<1&t %cIU5l$QZa8ZhNStL@/Pjd!n<5Ik_t*J`[R,;oOnodu@^1oKT2'*)*K\hlbE^S?UnJp('jaAX[6%k7l*,_\7+!!5qrAq218!1W$d* %!-t[bKn0cR=fg8UbM%2^o\UJSkAStk*c;@(>K@=m+ZRW%6QbV>#Y(.,eGmJG9";\\fVl?u-hW\A.Iui6(qr?+SfN5_bX7Z"q%I?s %5A1:QD(RC!\JFOBaiNHkkHQsC(n?ISF,tBh@Z^Fj+;c:E"Z5C"f^&.'5GKj[Riapq'T`(:J)>2 %[Sq16F@!PAn0BXpPj7nd_XA]\TebIY&VeHtkq^MgF0g` %pn'6Y_`"mi$D`iE1I[FLpe49ok4s*e5c*TCeDg#c%Z3c:&qj#c4!69*XQX+P(rR##_T(gMh)$hI\NAFJ;V0[k+P5ciUe^*ii9&b] %_lc./o.jQNo+i[dO0m7t3-q#o\+;aG*+ikYM6Z)_CG*+:SSl2'$,\PT3aY\ZTb,c2]OQoqE9J[BTN'cI&_fPF@[/YM:oe>! %`iM4uqgAbL"C8D/U[@Z^?Q=^WJU,-`^1/*L']]]W8N_.mo(N^+>#)<38l/Tmn %'K#qskMdMp+7='lZThk.)f5g3\M*?^01&[r*IG"%< %Zd>jT6mE"[M,['_3=TGob\"!0Pf7.i>P==S)*nVal-<=+"KUqr2lHs7f-[l&7=[$WOQdkNH^0RWjRt@!R'#'oFHOGT:PmSm$ZZ+f0X,CTSNIVr:t]Sqq%H-`)mnERu@0u7M+r&Dk?kSb\2=#B(tA+Wf%JF458ce;g@NuGs^0_B##\P%A/+f^Z"3SR*?3!)H.$P#)3#-I:f'(#,fFBn[;[u,WO=AeP?.qK)="?;#C/iZ#XHA[*,iFduDqY6=ZCT-SRMZ,Q9hL8TYPUukB0E8YD*+]h]8>!F"5WLRJ,kWj,m?Pf.Tm@_S,2j3U, %&?8jl9f<)8^%^jp,2JB=mt2I:FSA?.e[J>A($]BrDI?_)GAULU^5.&DOlFEHH-'[uWU=/dBdQ_qY^% %mij7E:BS/(5YG>?h,U$/@8j2ZG%Yu\J^MD/ls_I]8h@T)7$.OpNEI %kS,^Dd7tE=r.-44Q5>YD;[0JSET-JnYj9!;6M-l(FU#H4M*>@FHe06]V90blTKl!gL%(s6kGY9EO,5-sWXQD/2PN0N\$`\ %/q/HGE9'SsdjM)5j1/[7^I%F];fP:rMD\-&#rAk4*S$GdeIAE`[&g7]LFS6]"*Yedr992#"BgTgp-i[!/7L?^?\PMWh*;#kWhhCF %,Y]jrD&00s2P1K.E8pTcbr%Vm8VVY\oX_HL;nXj=RC`mWi%Sl?`;YTR1(klrCs!iV(#]F0U5:hM4*pVO.R%Nq2Dl!+F#FS %;_+#f&4*?4a@Gr`'D+_St\]IeU4*lU+\JdH=Ul(GKHjTOr:)?l^T( %CWkq&5qmGcD2d88hVP#'PF*fpN::(Zef9E^*-1$^uZRmqIr5J?P(K!KsCaW1rp-c['a0)DF8.l/k(_+25FPuH`hDQ^(3IHs*\ %5bs$<-fU<5gn[87^l8=tc5fVs%di*<+sUPk/b(.c]0!kiHAeI8q"e$Ru` %)L]^Y5_>m78m9f>NeQPj#fW$q6\p3JP>1tB7bsH[+J0qr3X,$RId/W!l@=LIs(n1ESegK@VT4Rq3Vf)bj;^8KML(pjPfL\7u/q? %Djg,kR#r>O/G_VLIWDWT6j.$PKXYaQjBH7=M@UF=b.sTjQRn7Agp1U&KXcnM@.Ic9H^'`a`O7%:$D9+"??IZ7*L"LPN:3sU_dOTH %auM-(QaWDKAeu^Ss:ff.s=7Q"VPEk\[-KoZq*UP,.JDHZNVOj(V.#51gDFQG7g>Q`9BT+];t!u/<:SSl!6^O6@ljIatK(Z$RGiV0u8e; %i'jFqPV]sC+,2P-TqGjC^).27L6@Q5q8\CmA1/oFktac@Q(b#8ap^]r"9$J8W\Y@^]]aj";PBe[kXBkU'IjP;9eZ(8eYVN] %l+Ys.T3,FPT(7:HdNAEqC%Ye.>E$]'mi!5":12:21![d0gB1Gm$=>36mS0C1VuN+LdVAMhC6M^2@C$mmfpJ*D]o/_h]6B\g?sVc) %f/bPFB8N%&c'qS$p8ZF)o)LKQC\CM;mZORE[ZV#TDsOlL`b>j)(!JSKKL?Ph?t;FQe0`u-6\>fTj\/;BjQni9SM/a,rB+2DB;H^g2t_0h,i=*%n=1"=5_slJQJi)-4'[^gXWt`l(`-$ %AI/_7j6nEmSW+D>)aI1`UZITiFIekufgrd&G#jQ0#e#q@SK>_/jTF]ZOa`h%9R)3b'UZ._-=Ts'I0(r8$l:u\88O7/'\[;iOX`jk %fNG_rM%rO?+F!u8+fb!@o(-f`f]?N^/gPK/S:GFi;+\XVftOD8VMR+1Mb,rWERrV7WEM-e@pl#qMAjlI9%VeZJYDKAQf':)_8J($ %M%s*%!pW/*e!o7qeUug-!&fe'RfP@C9+-Of.&)"P_sP!m%/+D$q.Fs]h/Vr`m*VjqdtX@U@?m4ZqcQI:(1W)2HZ:$jm!"G:gu1H] %*3R4]$jm>OAf:e]DbCb(7Y&P61^=TEErEXmaPk;D(fWJe\sAY1&NT(cmdbi5W1F)+X!I= %AgF0.CYL-s$^4/f/1Y;l,Y/%_M:%X^E!h+FI'Od[fY0-eb$0\lcr@"..RFu3L$VCe`Snd(m:t#DpW0m;PQbU%V"&<4C;sr!0*0`p %$1k+l^=YnG!!E9mNVWAJdu`QK6O%-@/kX`]"0oe01YL94,VZpZ3LE3,eg>F:^`.oS^gi\==(bX28&O9 %7]u&F"R)>Ob(P$#=r2?APk^TXd[afh"ZE"]u>d9_2kB5VD0sXeK$>!i?4`W-C&D>OA=<\'!K8NGAi<_5F*.b,'4HeGPBOV[pukOpNh$D %=IFd)kUphL8T]a1K/0MTVa=8t)%ehg?^mL5TmZLi$ZQPZX&#*eamS(&Sq8c')9P^O8\L"&bdRaNFu35Q@k;9QX\(G]SN$$RWp9]3 %e)s2?aof?ue6jBD`h6_ieMFg^EIklKnI7.#ADhG3hWae'dS6Q9rjTW@:;4tXqL>nX^AVQo_fq2tQ..$D8.6NL8s=raAte/973$/* %2OtR$fb2s_7[Mm+b(uHYARf(=H'0iCUq=99D?oaE2jt*_/7jS?2(O] %CT4Ud#MOj(S.NF+#5.f(1P>eUN9o;k(siP'dfXIbpu7R@k,"aJ4^]bT3dWen?#4e#FlUf#M+F@OLhP#pn`?,!mk-8J\; %FPR[+?2E4ZZr''Q(tl]uGls%e %[F\AM[9iT#VIGmE!UVgJm\-]lp(ke,Ue8/ZP`&cF)\9E'\V5FW+OR %5d_,"k?qe#RF.7'LYlQ6Joli\Kc\NgC/Z^Zhj>!*nFAjl7_`r:&_+rB*2I9Ql`2+8\Em"T:U"-HVhr/t2ipuoAa$PgCoa#M6o+&B %)36b*.R`S0@W$IL$P[Pk[t#=7FocW?f1aXfI")sW3JUAa0!HS$/lNG=2'EY17-Zt.M73)@5UJR)TQ]2]&i/OUDLg[W>^Sg\Ig7P) %rLV(,^_/OCI"_!1>s<*b%5&=V2S#Q=(eiW+oWGna/bVS00W&]hm/RJH;r]Wm_?orp3T!\tD`cV7DjS-ig(Q.:B=u]J4*%iXkJo[e %"\cV0WE8%C<&jArC4$$XhPg'4H:r!!%\N4FngFf;rYZmD'>o+]=k%]W3C4@0C_$)s>RU*S$oo$+,3bXF?6^f)4O+Jj3SmpNX/MPJL)D3tQ-HZ\,!chnE%)'Lqc"gAOi/^_NF5#S\`aV).gH:'K$D'\0W)MJW^pb-sa/2Ya9NP)giB%V`9!7%sJ7:@FBSW*1 %=_'lc4@Gm]W_=>t3=lMR4LQ0HQH_r0Y\D'=m\S/F!%Qj?.DEI!lV3Fp,eh555:r^Ha2(]iM)6T26!/]:H.-UjGWN2kVWHV5^j-,X %p%)@*\1GY[j+$[`04',J*X&0)(jJHN/2b_^ik0e5rCebX$Z+9S?6.*1C=("M" %Mq_IPO4]l&=KRtoFY8R[$SFc8`(8nc+sU?=qf&<4-C\F&&C:](]*-E;dO%`UAY/4C$>$Z^&5Sd2faTu(^,IEhM1dC$eeZ6ZY3'E1]L.>]]"X;c-%W6Ajff-,(pA"'0n7ZDFT;SY9WBO;VD`UN0pK+Y62+RG3lS %&nijDE,$@NtRmfcR[$]7(ku(9Gu>K %<(_5nHYofqX;fu^.,cP:WE'V#!X5^W3"HI)[d+?*tq]dbX!iJUo%gL[SX %9\-Ok25+Bh(;8sR,LoS&J/)R#`_e]sr3U_IYqK8SEE.+edICeOm^sT:0T#,;bPRhXf-Vo&o25XdUH4hSqL*T)_o.QNb+$m2?NU;= %`(af\'t8;Ba<_6fnk5l^j$X:*9':4M5C+7Q6COq8a[;9o+JKg0IUppCPVsr6Em0:A8Y58PeP9cN4<,b2Le#R>B[ZUiFli&D=`L5+ %!>PprkcS#>3B)-GOQ53<:M'UT,h"91\[':07\bIT]]-RF-NDC2J@tReI*@jm@[[tXr; %KraPL5.0()8RB+PQ4(.3[,eh7Z$h3p[l@>HITJo9"5?@Q'+ep$\]U5'WOuif^0YpV1nWksXZ/s-72&8;c-^O1q2t7fZN&[e*%uRj6Q=PA_H4pT %%_I:"2OOe#E&1K,\Ibr4l!Eg'`lo-80Kc^fke!/*;SiEkWO<%#D@F9GF673-B#hG"$_+\*hl"H",9LLVK3SR3C!`1aiAsZiEomj:mrdM#."4/.c %4\e?^_8:fi6a9?Q;L9q_"g4RkoN/Ssb(ng94NCanUL)[qOJl>q/C$Fk*#7n-]CF>/hE&9jb7=OVXtZX8WU;Ou*ZT:-kRt^11'[*0 %.BBUR^GbVbbG&,+mS$K(O81^\EkB.V[62ep^M'-f)]Z5'^TlPOZ0d#].LYDD;Q2=)`5Ldshc"_"$U92C5FImVe%iN4WJ&,8V9!MGtjLFFgdKqLG_0f+7Fng)oj@dqimB8Vm9RZs)b.,C6QTmDt;H7i,W0dHW` %:FWu(G)1*2pokJN6P4X&h=o]Glp;dd^ASCA+fQZmc!U0Z6!*Z8U#="`[6Ye9/_RgU-9S)E]`.'9,"YPW$i5Bb/hf.&S?JtR_^`h(ORp:B%FlI4puc#K>pnnalF-5%8?Rf"MOhg3Ip@LJGqY*5MuhNKS#SnP %&5XVr.`I*bEMI3#A<%&f,I]IV(9^;2?_dD;=3d20Rl]4bK`(c^;tJ7;"NpL:);8j3Jfq.rUVGZHIkON6R1;KR;g#kE %:)=Q3/gNUSg8@FSfs/`HjCD?#Ij(_FdqYSgWL2(u>PLiK %\b7Y#@Dh^F5(4[S)s@E[MS`Fm<(N[.Vcqnj=kIKm6gR_4I2`?lYm-l)C,-ZY62&%HmBgPF(-e/uDS'SmTO&m0e>*(`Stu@!>)aQe %g#PQefPDtnfurLE)as2b_Q("7ZUfhoi8Krd`GtNU]>OZ',:=0YD`GS<+1P",QV#b!AeWB?nhc[1f>$e(X*p2/rOC&'`e(\)3%dJ> %!rB8&>5D3a9,59K@e1NtJME5.f[T#U:6fe5al'5lojn'g>[Y2m3!X3ETe>u._\hna6Y9lMU@^4(R";E*o<;'GR%hK:ZQR]@IDaK_ %_E94C8u!?#G3JI3&`a33T'`Da5.a!%3_Z$0#==V6$Ch".+l;eu,lEKp;*e,+Ht#7Xr5i:G %fjiBUKT;+Do[Q4kFt!S1]c0g9AG-UT!UL"&D]iR-gt2lKk!Hk'\F:Y.mR5.%#T+HJJu5(m%ZNs_H?BMQ5;CSQG=OB^&>G]PnOk=S %MsP!tOEhGe%#$W-M9[X45GSNP4G&Z80H=rDO&5FmYj[ZLquu=.+@q/bMC\)Rja(mja9SfB4O.F+]s%!'A@]rU8O#JB)Y %hY'F#]/V-['4ha!U1gR?.K"Y3gmiH.#eajM"JMCknn-`=#uV<"OE2Fb+m0mL!TR7ZJN\^VPi:6j6uZ?>c4l@T0s4L.`<[8F92572';FKKD0oqD0-'8%g.g2=,9*7"h/Q?706M)$Vrq2tB(s.iAC40:/!l$FPF4Zo_u;i(l35J1jbe(j"2S`,s'[qK+A7NLA2c5ZV2uQAcd-&^-%D**b3[HHV!0`miUbN"qrSpPI,Z=?X_<:R.K!'MRN)NB;0N*r`9sX`n29n9QhblQoJI:! %"^BPi$H!-Y,B">U1r_Gp7eYmW?M[#4+2.6J%_H_F\3um)'>oU^m#SCa`(?h_-)EYmH_$3IWqoa\-JSa87Oj*bl.@FD(j9AX=J&hV7Y#%O[TAPG7rr,smRY#(I#SEZqCL$G[%bH6KBY$3:Rn %Jg%Af$:5.$M]oIt$A/a0P9IFk#%E=[bo#;_JV&W\d1B\(@o4n`eI^Yb!Q,,WfaqV>J`DL3h%8Xo"U'V=i;dJ;JL"U\b9>4m!_%kE %l548aJRsTJmL\`5!JZ@PndF7N!_j26liH&C!fdeBoHE9Z!-!?kZ!IGZliJn<`!_4fNTk]H6q7*rOD/uMAVLW=R-Js*Kt\3k6`0+%Po;D[,HC'`'cR`u6`07)PoDK'6a#m3 %(E@IO"1I`RR=&EtLOA882`g:>'D.LqqTlg>VNl6?$f:.Q-YD9P67_fVb;/,LMi5`iO\iaUi1$E^F]Op+TN1Jdb94_1U %1qh)"QlDZJ6CQ/C:n;?;D!]S;@]$m%;3"D(\91e?X^i*N?I8\grZGp"gdAN2")]D+$mUW/u^nLo(ec.ZRB3kO(R!Y]pMV*m7W%XAD?A4^+J+2+^IWZ3INT_R\HR%a0YHQVbr868EWmP=\9g(<./Oc]k"+Epi2rIU_6JFg9H%SLqeoLS0 %0c+:mZkQ=1L:ELD')^Jq6sZXH?doPLprS%u:5gn>'FD&no82MqIG$K"otG;0PbQF5pn$2>Q(_tP\IZ-L1#jtO[lcqu%#D[363R21 %Up\94$2'p%9"fIFD(98>g'9;`kS(/`jMjnk1Q)$rRe#1&Mq7`PC!j<`Zq&ocm9m>=fu&]U)-tBd6:sj:\kSh:kVDUF&-5k%mm-2i %VNV?99%VhH2P[Ud=8-a>(nld4H9W)Oe93r)iIK7XmFNl5sQi.:#5jgr6;k9 %0$@[W6h'9#S/6Qs['Q#(' %9QaTCP[dND/^[rp3IL?T)/imJMI?rLXB&XWS+FH8K\Dg!i(8q63:RfDq1r8tZLBN+rNXF.R^Q@uEKUm^=srF(nA+"-D^bcuGr=+# %C]M&)9McXTq/5$b.=t*u'*kq4TmErK'*Pp%IQl+=1I3ob"TeuS-t%.?f:#`^i1qb-E&]eu\PSkM'XA*7n,APOq_#q0ao*jJ(Q/.H %"T_!\^d)N$;YIhhaFdsuAV#Mm.9q&T!Y&1N\E%RSKK&;88EK>8&M_^dqmM:hTFje,>f8HciJg[0,EhZ.8:SQ5+ebRMS+m8-5e<@F %B4@[bD^T*]*Xg&);3:;`!V0i6@!Nt0gf^U4"j?Fd##ogd=Tdj"hpqJ5cU=edj;plGrh'2@cVRn`]IWNR9b*M6i87@N0O4_/#p;W>3[gRGcba#g+ %p3,J.rIGgd*f67]cB2glQ-sq/?Y.&?$*drQMAnZ/UgNI*S.VbK["_j#Q:'-`eOjuRunWV"'m+h`aK`S9XhbQ-q'dsLWY./SJs^N %n"&Xo3-B5RQZR76qVh6"0b-AI:K?Y*4no09=d;@Y=Zf[kZF!!3EB5$)Jm=gHh$q9=^L)_]H*i9lhqHR/jJaOj&N[(qkmRRdJ7a4N %D6R"+=H/"Dd2.-5BqP)k"cV\WfOcr_Hrg;#S!/K41&%D^q]qbV(9&209Jn.\/X5F`5sK#c$5`:CZab@[6LBCpT1ntl1D5ht%mX0^ %$tpSd&AY-6=C:F7X0Pp82(4m?k>a8Eo@5PQ&sO%3A@Jr_4?,D1R^[H](\4'Ve1$Jn[JmSK[M!*N(>?WEY0[UFA,$C %PD_Cm.&:kq>m]RpY'L\BZp^iYnYTCXkD7X#bq:J6KJP?MZ^:K9=UX2g$!5dK4fVY"lg*#oHMPb[*Nk*(eH@BRP1',K(jt=+_4>K# %n3_@>PgC]1!T`36$R4#Y#Se]SC(rtWd]g:HV%IIp1c9*QqUUPs#86IL],DfV>cRElIo_np)..q*S9aeqliu"*n%*#CjKYfXRKP'! %dXQ8.]H'/o)0Isc/n\O98mY9BpX;&`Vf-3oI/JbK6Kh@$r^Z1O"CB?FiGcM\>?57NGnA8gE-?q0J\QC=^,-Z:Rh:EmMrP;fkc/$? %^UuohJR)p-Wj2Yt;K9mk[RAeW&M:nC-nV%(@7Gru`.UO*lI/[qD[8+A@k %Dr9]L.HD/"Q,KsT(C?=&/g\qG\Os)&p2>4&?2i^PU?&B]'6eC3<@A:uJ@icCk?HdC]e[P4+Q6lDh%j8*1o"V%0)=8a&FUiP;t[Bn %qVP>9R>);-]Y_l,8=PQ00D.5T %J'Jr,^&gK,#!H0SJ"pfp-#C'.->%VoLpa[*Vb"/lM(R\FO3e+)eBRf[+HXFqB\h@Ii& %^N>OP*@/7[9L5HEj;/76uQp4&?3t>VP]]/>0%rj8M22e)6/A/'U(^[q?f)8'o!V;8ZbCO %7?Hf3njqQl)/^_^>km+DCo$a:C-A'(e'U4*S$-VqNp2d#YKKp,^t?30<)e=NYDEdQ=PPah=>B_M6)$C(>J8=)G5<2A:]Y/M$j[H< %C@pW\s-?s:r\EYCd,d)=R3f%93sDGjnQYZI5#_mpqi0CH2(k]2Z&a"LclVH_8Qm/!O'"C)j/T?tMXcA:4`P9!YQsbYR4B5-8.dA: %c?b]-ZG4.2V[hH`LFo]n,>YcLPjIG0_PE2!W"=O.R,1)ieVcgap:M2e^I0u)#4V372`th0*qgemoIZ,-S;4f)htj59htPCFWNL4l %+/mo7X((cqMKV)O]aau:Eh1clc!"p\,!-r\4J]Sg24FicVLuC/96Tu\kr2/9BRmTuG5t\O;;@2f?l3Y44:2L(N;(d %JPM,kO0CETB"D>$([2,l:"9S$7?PR4Z5ZaR,?uaEC<^M/HfHXkeRRs4aje=Z?lK9%;S4rG&8&B+:hcqG?U9]=rfhImV^dI!LC0+k %0nKM.:QXc.iCTdsMaBBW-[c/D\=n"No,Q"Z>iou9@1nipF! %r(fV#^V`9q$^j^o@2Y'W#])ifm48VV"dPOg9s7:I@=:__F:A^:\NoE;mAH9k;`E9pH.ACA1"&>894l\mGT!dI[:D2:DC1*:4IB1I %"ri^%TU;NP:,C)nVXP?X]AsG#c=WE6W\KAjguM@#@TK'#:Xqug;lk'#"%Yb2FlNosl#]>A3ZF:kBq"gqDYU.Ppkfu'0#P@;2I;-Z %jo_-A>^ %XlZ_MMBK;=-:*5KPLhPo-g"Rkq*ma]Zn'*$!VY(d_@Nb0ZDs+V,0&5FE8"3!Dn<>Z*nHi0^I!LMjtPGWIBf,7M_SVZ,rs%IJ5HKc %VNr9*FQd.k&YsYGR5F1?&S!gp=[6n%pB/f9(mrNB^QoIZeAgpKNFi[Tg0GWTrV=_/hD%>5b\c!`.-snIp,$e-3Q[bc8tkV^W$=&O %8T0F>&=jd"GV>HA&0Dl9M>nntm3;\M&XrVN#K-N"g4*/pVEY4bo=ak@7QUS?g@$[I%<)*VpAB(LAb411(,*b!Og3l+_fp_YB`Ccmi@8=gIP$^UaJomb,0BWqnMdoHLg,(.C)mnm+:cb2ujm^4dMU-QTWY.85Ein@"%[?sokI])*g[bX3TPNp,g=-1Aa=m\AU17u82(:^a %Gj)u*o39;H=thoc1WjY+\nGT$1PSC`:ur?XZraD($1BdOUSHu&nJiLT4IL=j^!B]X!ua;<%tCr@oB$=B_n.r?#J#::EhNM!(A]fj %Z+F!r^44F:%D?:%Vra-/gM_(Z`$6n(^BOuc2>]bJ>faf^KkN8G.&k563.5./%N`L9>-^U5'u4/d+9-etp3ZQ7*1fam+0YZs)&e;$ %Ia.]WQGm&U0E$e6Z^*g:&t$fUm=Ec!n[M=6X;,a3-M@i! %:_iS8(!Ap:]0GgjflH[n#,,,>re$E.nAjAip;c'Z$S"YVB%).8QWpu+ZZh2(?lUD`(j\sR'\u:":b>of^RP_MpJ08IpEt<5b*Z"o %48haW*KReAF*7*+HZ0:92bf]''(V0[h6rXEo^cYbMcGYjpA?cCota._`EB-U/gPB,#/tBcCjO^T>nN?MANas\4(i)]H!umd._PQj %GH0tV0eD.,A,(XLl9\h*#f:Rm0W8qU1Kq_(?N&b67a3' %iF?Q#n0cGqE6=us2C6AUHndP-huS\$Y=Y[KZK$/P4C]"BJ:,g4/]NaBnh+cG1]^0q&i,DW0NsFM@DP3*u1As1O1`P)Z(p9gBKJDV>Oj %WHUJ&2EGoSC(J.so8Wj>h(?"7RPg6>Plg;B`PdYOan1s3NN8.DA?g0m(4Go.+9(??@rc]!CuK#O%r:sb?Q!I#UPr= %KXn0jGT&Drs+<<_ZdL8=C\\;PUcJMgF*k^5PZU?%'9JFGM(S=Nti %q-'j7mRl^]q'G>$MA_p70r'CM`lt@?N09kl]U0PL`#u02X=u#cLN3`rmFo!YS1jkYd[Z`:h;(q35A*p'^+IZJV_HZICH65EVp/b!/\/=e%>N1?FNm\WhV)bY562j#,(ot5Ql;B- %c,H#3:.]L%=iEYI?tHi+BkLnq5lG`V8ZWO7DW#loA$LZ7`eHP+=dN %:=TS_HGbNS`Egh#qdk.lh#4L3s1f)jn5a%tarA"+m/<<&Ad$lQ\R0)Vlo/4B/M+ %db"+P[&TTBpBY!sCNj:4EGA+=h8rGQeKCP"KpfMS&k[jJSh&Ccf*P.6X@sF.'tsE8-Q"k>Qa%'N/./?`5-)NK4hXso8][W-lm9$W %6FP)ACIPWSl"_*$`EcDW)$uZ#8:bf?rAe*05kbL"RK^C5F\1c\G6AXWrYk'<>6Md*LZT8YF51QmMLU\A'C+=UDnGBd%!mS9UZp9L %P-7Ie(2_FIQZ%)%8i+#ccI64,(QbLAD7YqQWAcg&Mi1=enp$Cl6&HMiX&XeCE:\UCiZi9qh;Yg7N/ZgiJ#l_-eGTTT2o%3.)g$rU %ko6ik:iW*M;=m3!+IHoaeVRY%P@"\;/iCLQcR*/?^XnX2RN'RN&`p3`.BQcm.GDk&CNra>ct&5d+-FCFM7)"F6fnr[4XiNL)m3hC %m'O/pX8?^')]H?WZAa7TPc@_2rcHXU$j])qd/.fGlq6W':/:*2,fG8D!Fi=/VdO_[7EP.MnQ-&2VAqA?-9p\U,M"l_>WlXfUPAl;`#pf4 %3O3%Y@FeA/7XhFcQs5PJ,Y-1P?fP:C50O?M[!Q_;eHTr6=OF9B7)1TK_)8Td3JdB8HXsn-XGi?7C(b0Qorl)Li,-gMH$cnoRM4S\ %AUh*GE+L)U[q&u@8`YU!kOXfbsni*Mq?CZJH"*`k`:*4dVESS%aIBsZ6/UXrPSdshi"g@q*/9lA5\1(B12@`7V9m@ %Mqq*O049^FS'2oD$*&F^;Hg6&"NF)-j%A9#?h$_PtGfY=1q6R6>@d![NjC;_dXb?P7 %0(\-VYI!h5HkUmg4:Lgpc<(+f5'N1.QV\!)I>Mp3eRt_m"$s3I9QaNu]?p)6Y%EM>6$@Fn^e3;Z$dN'r<'8aCa.rYPFM9("/hW*s %8!_@Qo:j>(M9K-PS?4p,Z3u>#%-:bg1-&QBnA9fhGB>-$Cr?Rh61]Dj+O>=cf?qQ6"[IaBJ//?Q!4:hJ2^iiNhLH2F0t,F6>)RW> %%"P/ObiBei<>JPQ_:fNop*Zph$`lT>qV7lQl_?X4 %Z)(]"RemL&DkOlrRpaUX&S%])8-*l#&]Bl25L'eP!^oq./T*akeH(E#=_91uV:I*u!4qa@#f;*P.EtOX=,j %_+PMoacm1bc))1$TN3fN^-]et!Dj!e:DdkV4kk21/p\-$0bnPt5<0,$B^FH:>h7Ccn#LieolQ%ef@0G8%alD=?PY;7bEf %hepG=kTZW7%qcNM&Hm?AF:=eJ1iU.*$`H]r(J%&o[%=215J7E7NLs`0aXVR0FLU4$7Lnk=h-Xq#ZJW`-j4Q %*Xp6#E')Duc]%5!ola^Ai$,jh3Cit_9r,B*@ceEI[E4IU[d716q",j9-sEmiUJMX?rDM`E!iqn/&`'OLnuokmeAX*kD,FG+?:D@cS#Di=4'@*s7n,YoNufCG.Fi6#e.2hU@HY\mAR1'&\h %DR.M+1fY7fo,0q,HGs-W/'i/M9/91u)'R;$3+(!_Nq<"i+Q5'Ym+rsj-XQ@NrW^"+XD3HlHkBZp+WY6DX[qQm3\\/N(!dDtPCU%6 %5;p4&Y1noZ2)WsI[21AD(,'/XJ'W%8II#6Qq6"O_+g$>CF1=*>&8q2@P5q3\PR_"L5J/::Gk:C37A[P]Op:lAJXu<+RY1S1VBDEH %.7EN#>&o7]\G%Va1_DN/"_+To=.nFsU8"):WQsWJj3C:>Y+K#HF`BCjrV$m?.m0-IqXP<>If-4VahCFet]a-dQo/iND27 %H^oGlbiu)IY#8Y&IrGrs[J_W-_D7^kq=Lk1gjQ]f@a%4N:3qV4%tI$Bj9HSkru9&t.hD@g1?OWK%Zf'a+lh.nEY*SRSa&I- %dHCJ+p^BJu[=+hg51nV(Xat-9R]@<%M2(+;+a1nAjB>V %;u)`C]H,Yj^C@@?]j\"%W4eJ\n;96r^(C-ffL4?>k03]&Eb%V>\2(3>e6XQ?$fQr`^9pD@%sY17K=\]Bo[r&0@;;rMZ&#%6mi?3I %^mu;Q/FE'rP&#u&m;5^d*7>":VP@)DoGl;S"tjM*OCuP!l"Q3Big21m!J1)r^>*:bm'-:iAPtBaOYCC2oGeGrMLadF0@(FJC0q)q %7=WRoP:m.CeOOQQZCcrVcJIP1."*um1NINrkV,J(LSVJkf-3%U@Q/6AURn'e_)!f4 %5)OG7Fq5T^I5=)SF#:h8P(d>iM"A5jCNsYSE6hA)Kk3-ZfU8#/^(VjE@MLM"i0\I`&0BW]29B=:/It+3(;K7Noaq16j]>T0>TV*3 %fD[2^W+0JLmCl+5Lhoq[04&-rW`Y_&qeslgWPYR1&*-k(&]?VZC;'#mVnnU"&VRO0"A-h]dhUKfnEfC+ikG0a5_pDW*hRV17U[..'E72 %8(#0pD(U-.BLnI/dLfDqo1$@76b8R^Rnlr7_nCdG?f>Z5]:F,Cmi!VH13X4Ee:^!Ip0N,*Q^6i1jqQd%@j0K:EG %^J64R7#AQ%^YFMV;>O2TBi+&#nEP]153tS0i"$@"=g2Eq35qh.VjSDu@=qI\,C.\K>'5W[?u4_+&lEu7qOD)t0(SUnO/8tKoEN`l %[ec"u:$/el!cn(\%o1M+E\-;Oa54E\f9'>$Jkpo@^`Q"d*eC>mb"J!]k(a(B%SoofE6C:_9MBQ$2plB1!toI4Cl*W;a`[D[Y,i'h %5jVOX8FAH-')`ttJ1qB]_e3XpSKOjfA"tk+[ %"3Pm0d"1<]pDr&B/'/%iC)A0IQ&`,XC)ElDDmO#kH^1Td:H2YBeOf8'NkJ;RAo[(!d/nYk)/2G"?0ImApI+Li90&SA^g]TG;"dfl %,0<>MG:C.;lZSHAV'cE_VhI5DP`KO`4d[@M:dQ14,:BSDkk8fA_T.n'B:T&3\CGCNrHgd#I=U* %7+GR]Nl,UTARR[Q",8-]MjZ'r`mmf/fF`aiGr\050qX4,V`;t?A+'0TjAY$jgl+(5L40YNdV4(E %G6U#b7lmU+MGRu$A0/!_d.`pb\hJ9(,=M%S&+FnahMFNe*1H7#6P+:+7nQ$)R>?[;7F]o,njQIcnT#N/RmtTA1I/EZEQt$GV80SC %eOBs<`!?QK6*mnd!u?#@:WE#ZZgCjIFr=9B'?O(85W`VJm6&8k.A6SiSV6-B"bd+kZVbZ/+Ml$0RO8B-,a_ao:+.eRX1Q`"KC+i& %GXJBV%++UF2g85]TP^*&?$#n`IY7iTm.%-r!+E1KMqFA1*"If\?-&dM%2$(Oji8$\&$qg/P %e*u/TH:QW/t)r3qJIk_kRe\S^!Ph%GM:S'2a%fqSW2?kdNH"X?JJ7XQWfBKqoo %8#MQuFM!L-1kHm$ohPW9oB'_`%"a[cTX$N>eAB3bcpbR79pHdI&O,3Q."rk0b$=-FDnaAhb9BFE[5f3%<7[Ar!]Hd52.5)Y*!;@: %=F;3AG-W[4FrAQk'Mq;O&u/\-#P]PX^G=_YK.eokkq''[_7,nL%%-k#(jD8s-F=1PD&k/Q+]s"#c9#7-If1[#jp^cEcQ]3P$l#i\A`9&8s]\>GfZV<>k6mu[n)D):U6u_22a[`[-0KVLO5NNFhL]_4) %Z!0f!+]UHU8'Bhr7+nC?l(oSM-4_p6SJ&q7,-Z?uTYfcKFjB"$?q"[_,V>/1q&A`_qtdPo;TRd^2OL2-BtO"JiJC@3F+F8BKs^29hL9@"8QL&e#$7mVkB7 %.=?Y2!&GM#>in3"4I)EEVI)$6^F`+,YY;'t\q%%6AcO-%_Xkm-nQ[pXCq:?*V`o#Z+P@n'1(n[H/+o%iPplHR.)n>`!Y9mPC72c> %Sc^mN:sA6l$OVO;=K0F)rB?[MBE*$i*35YcN\]U:@bF2rklC,/XD.1l3P5Y9gZQt)JT>&=ma]nifJD+BjW,$6"q%ZT'Q4-n8$8VC %LO7G@O*O&L(@#'dJ#U*@JsUsQ*bTA+dMT"'F0Ats@:@q5_7:"LMe%GM?tbqc`6j`EoP"BGH#gI,pA<3^QY6fR,fuf,c`^F0Za]:B %Kqh6J)JOZ?[990XV4^TB0rYqO3ScO(@*'/e*]9o.=$9GsHo0uT$GNIu,e9o_V(m3lZoD]'D?[s/+o)Ta,?'4qPX=ie!N!A`.KcrO %-gS)UK,0H1U$Qc66quYEH,^Tai$K#ScV4&#!k;lKYQ]3E4Bi(Vi]$=b$dY(@8J&nt>g1MN3[Z;,(G$$cRa2]X3J<,:VUUOh5,][6 %-'sK%_hVPA?deC_7'abOaq@BOYqoV.PZ+Ai$RgqY7m/e,kU=AM5ckYV7tdXLc%%7\MP`ngQJ3QTeDqM!3eSKPb:Oi=GHEAI*@*(i %5[RE+6>geWdCdDrke`FcfSD3*'Snd5midg(qUM6/!3XFhZ8$V.,2G!H]UpGYMfdmP;T)^n=sc?h(4q[D1Dtb-Uesj%`C!BII8)J1 %bb'gU2>F`!8Stuo'\cdB2PV/nS[R`f*n/ %WpJs/%#Mh0GQhI_]T:"qT#,/eT4#Jm_5,er/QO4tcm,H,V7h/b#0$QJ-n*aJJ>"_"^iRu^oLp!H2-5g,9E"/P#o&^;+ %,-$IGgVj1h?Dt.cC^75t9@9>E99i%edko%BQ+_bWc,b.FJn.?F%PT)3>^/nXI7)&W@1BG+K9$4M1(s_3?9Rn3K6'(VP#T-BkY-^Hd+cFlNC:1 %.o&\1%(!;*F3Tiq(JpjKM!@0i`-"kd&bTO)cu[Y9Crr1HHL^I&g1hLp'J#>Sn;_eYKP,d&)Rn!egB3fLeSTeU\rR18@Vo@`asl41d'H %^!qYc!mUmqd&bN_VjGDu&8J,un[3KP""`#nq[G6N!qjD74=<6GE%%HlEgd@jR3ng_W22KNFQR-h>tT=6.hcZ@6!.RY5rQ?E0id1* %`i_WX,rC8+6=__#BXEPMBZm$UD0)76a_iR2\/5IK`/1Pi)9Zna3#*$fC,iS9l7PCD9G%qB7&NZM8n'MP!7["&-4=]K_C=WpES"64 %:m8i_]HCD&Sij"T'>=36S!(bXc]d`_&lZC>IPMjQ9.J7+"jgo#GXH*.)+I_2AHM.[`6V`9*G7a$J9YMDGWiM+Sc!uY[j:nq[iGQ' %UqYYKeM;Rl1ehi,\nSUhBL2Lle>8:uqU4\hj4FX!*LMPEER5\V'('iPiJ5Ja=XK$.I`B_GA!k1;.3M$7jEr]U#PNP1E;Qefh3V#$ %&bsoFDdND4p?L1tX&jd63tmu;D>QPfr;JeS9,Q_c?iKlYIX?3C]/f\`rU]Yg:4E#*h`_#N8&([MFa6i!S\(>O>1Qu\"!TWQ[nb'lebMV/_rqG0Yf^BlM$mf38`ai,f %mcX6-mE7`\hHYG(XabR^j#1N0#M`,:op`!@]5R^kFoCf7?JU"NNOLX=DW&;)Tr,S,;6TS\Qf$W3^3[+WicXqZTl'Sqk3_R(o&"=< %^AIV=[uJ)2HF5q;=0M$Rq&:Sa4P5,\2r@ot?G*gok\7_[]7/`g[.->!(#JgQ5"r;4[Z)TcUXFYl=@W;aouR/Vp#=5EACa3lL96DA %LYL/6gWQY4Iet8^ZCOQp]IQ&:ih77flJKAQeb+.D?@3Kr0b52m0CBAarq"[jRg5S\S$2kdjmLBsDr/3S;&fK1k.WA<'@3YI_%.0g %?iK3YS's$N!n32_0R\"h;&RI@hqq\P5)N%g&t(XEG^^op.;iWR7u?IY1&W>(l(c1=PaFZ,&f`3p%lO?]`'b6e7\9IO`3_gel/+,M %^4(KQn`n`XDh!Y*^Gh:)S>>]JrM/8'V"%RDM=9ag-F1rX]mfS42j`E/?-r%)''Z,4npBodJ,&/qf9[!g]FA5))]o"i>jII3@Vk4B %n)&p;&$hiI\Hae]=O7ipnt92)qVU3%idU^ZjmMO/`U.a+Rd0*dngF*k2cm3>4F[Kpq*Ct"K1GX:Ek]GqNQls&alt?nB(1/ae,MNi %pi/Ags$&`^_#Cj!lZKnUIs?V#J0gYH,lh#>LS^:dl(q31Dl4T*a"hVN^&Z+KpSGOb1P2WFP^%s^#QIDc'W %n))s6\pX/VA]7.$Zo@EbkB27FlLgX)8'GaQdqZf"FO^/Z&Q.E7,bAP7fV"Pgo.a'hEgQY!lHLD(Y3r.'+3CYa<32*!UJ=V!f,(nt %Dn^XFq.;.HiU=7Jci:OO_9o?'gR%sMF>`^fpnH"B4mWW+qFGip)X(r"%pn4s%"3NL\*S=*[t"?*hQ]/V9)n&i?dOScSZO?nYMRmJ %nWC$T[^_YoXT"pNpd_j8S;gbCEd329G;E`YU74j3b4lKePM+/*%WIX-p#Yq7f3`uRn`&\LgBoV/q#!^4GB%j^:,%aqYc?LYlEb:5Q3D %^"SP;jPIs\HOUeZpNS15afYPd(U_XXG0\>>(b^APoH0--Yn7<8a\U_^0#<_g]oB,lraWbHGYh]`:@\?Zp\l&AIr8bfR+6O\Wh27< %JH,D`X/hT]/)EK=Jc+ZqqYFV0biRsdCde"Aj6cTl)Z?6tr.'.b_pSKKnW[(l?_,s+S!P4R];?"E+9%b!O$rG'"'m@0+!_La:\R`; %T34?qSf=8.'NHi*C$&""u?ip5,ak`d1\C,R_O>eR]ErkZ/#?Et&Q,L`n`n+!8n/oDd)7(WBP %`Oh+7()ug:q+W.l*b:Kn1'\idLC%esH6X^4acaYE\Flaj)B2iVI7`IIZ79^E@JPkg.d].""eVJ-`)`3'8T#+22TC2fK@msd4C)%! %gPBG)=Foat[s92Z^7upk7\>[<4F%mVn"6d.`,6gNQ)F2")a=324$mcGpcl_0@OihhkQl1SZp#d+LlI3,7n8@G^'+`I(E66?4o))3 %$Gh,tUUQAns3Vf0_U/s)WuULr`9o![o;u8_Vd/hQXSq="KfJ5,H:#?BboGRfmInT4m[Gq:IV.o[WN68Yq^_TS*Y4;;r7stOW2J., %fZ;F3QrbhTAa)GR]OL%_2qPh$O;%,g!oG_Bg5l5.IMDUe5G_%6"1_rHJ4t0BsWS[kYGUFnt)L6up$Aj5/nM;@9-g,f0jl %8W7XS-Ci]F;(eAU9]MKHV^H<$9K:'d3-W]b1?%kPn_N%\#s&N\GYLSec"%EJ+=VbJU`D,rEjK?#43dCZL/;bL,Q@IL\^[_ %6l%i!^]45oj``Yen#&o&_4BFk$@d<4OUB-):-#\PC[mW3oM&LRp>XIAC]:hf]0;nTj7Bt:)i:A:E[]f,B[d&AHrJ80POJ7bmF@\E %?XCJ"$E:V_`qb,P1J'DhBc@_YJZa`VhlY_id)4_en+5Vm>&5PqF>`[qYOPYJ5/-3LFppig8hfQ(?I?T-F5>Z'DEq`cq>2oJ=1i;;rEFV19/:U(Ap\@oBq(S#WEZDgGd(qnL.JGg&XV`XMofA+6b(&o&N`OY(gWRcA?dtQjq*f_oDB2WjZWr;F*V'+n//#\DaXiq#/$[5s)[l(m3>L?c5W1-NVCI'0mfG7qa+E_ %D61](l+m%d]Xt-p\3o>*Hi$NcgqS51Q&gnM!q(iV@[:T5cYm,E7K,i*2YKVRa4o+%o;F7paFq,:pQKler3[/6[kXlTaPVnEq!u&? %?0/qAO4ooG=50I\("'Igl@kKU7r?FHgN(fPllEI.[cN-;kGW5X4_e2]g6VJBBHEHGYCBsD+'^!P.Z6J-[Jd>PU=UlhCaPV9;cFUI %O1lof":>qbRXb;5GEiJ]KZ7RRF4qT-h=Gs"->=\]A:NBiW*cPm\rCDEUjMn?U38a=g:XtQI.e_qa,p30ICZPbhYcp'lJ=J^,IuKs %9N:P8]=feKh(]Un47VZ5#fY\W4mjbmpfgno-\UJtAa&S%MZ4!rAK*Z):OUHqocucd*SV^7I=03r6^j$LANl9dCb%k;VNpr`d`QCm %p&$(5b1q3Y5.IIBV0eE:m&u(e9Ia> %Y(hBI=0T6/s8=?=r8V@FX\t-K#Siu]5&Ae&p//tT?[d@lioY1"4JDeb3N[tGUE"B?K8t!!Fp_Otje0k<1nUZc1iHmf:Q^:<1A%=] %FHhElXfcC+GEef&M+XQ$4/Z+mrAU5DFU>*ac,iF[)^(DdWL;St>BmFq3>#g[ %c0j=N?!TZWR9)=SEoV"1W>kX0=tXu&Z+o`47Qm\2fZJ=QNPTu*IlAAgpkU`4Q$#;AXb?M6CFMH0ZJ/T?69*QP%b!n3U*$o5p8/0, %I\*;,VRGCm55dX%L2Z-JrD:aX?0t.Kf&"E*p-sIs8'^/[\o2(Fh2(sM]O-rn^O<_^YkWk9KR]s9o8YBQ@4]tfNl$pr3c\=6rjGi7 %o2F_U4u"AfBBES$rnYi"b=?M#)ke/Hj1]kH*uXg\5O9W8nk!Mu58i1f\VV;SE&X`'rk'lX[`YOug4!H@hsG8p\W54u[o63'$.[rm %_A>D4)Z3W:IG,Y:K3*:J6\"!.nAi"S,B)8DB(;[0CkI]?nL<3$AU;3l[[!ONi@3O(/,Y'uCb%l&hl#gCK^/r,akQKFHckc+]_eqP %kPoLZd!P-:0gWpI&sPMg)k5cch^_FCVj?(W.sA$j5>TC"P`d-[;p*D<^UpG(*f@_d/1l>H6_jZpW:7RP&\S)DZi)5,nA@1oCQHTO %Ee=Vd0Z?0qo`*b?0130&8PJB^DZ6Prn6gUkb[C^Y%8Gmnn"O=OJ%>">!n: %"hF_ag.Zh/Vr$HnEOnq$Ir1IZI_LhGT*@CCDtukSA=n1m[n!Y4@q`b\auUTZddfIj%dAJS]m05HRh(Tmn$d4BmJ^URp[^,.VDf;u %DroN\9@:\qqt$T4)rp0P1s4XAh_2*04\>Zs4@pXk,X!ZU_s'X&EmRtcp$jS]n?Wfm7^DXsA-N!2TBP0=h/6bI5:M0Po@"b:K)+Fk %:>2*GeK,_/$(p.r[RHq]@sPDo5DeLHr71K52V+Q6f,o2g*k$!?Xji;Ss,nV])>b)*Nhie9s,`s"S!h1q77[@/c-;0] %hHk$[=<;0RWPsY?k>m&V:W=?iBudeK=+93![Bi^q[V]ld[NcLQgVFVg\',^3o\Gl^R4OgU@u+?bZO.3q\]qoN1]M %J,.j@g\O-a3#S=,.E']o=UHJ,s-`2&VNMVk:Wc?/TKYA:\3h#ESL&#:@bOZ %Cm[\>XJ@Vp\E9d990P7hbUq#E<5QQq>O'PO1.5h\(P\F1!K8I!V`EAjO\VgE7KUVCH)br=l!S")C>6*HcdFd"*c,[jnkR#0X?Cl>3;L\hui`GclCYdD7O"76XbHXME\-mnUTj*p3=20TPMk876@HgAt%2KmI2[Y''W#9MbR;.4la;1IO"I<\/T!I]N)O#]X %dC-^WcrUDqc>Q@?Nie7(EL=iL=2V`=e6cEa_Q[@;Yi52r@+q&?"05Q2kkJ"VKqHF %:RsebLnVRrFh&hFn,?)Ej]NU4P(Pj\A1RHsUL*S<./N/'oRaZ5d/?P)qq"lN$aTnGpI3DtZ:CCChi\@&ts.0FKik"-5LmBnrdjQBtGd%_>tT.CqoiWm;Qb:F3b31u'D'dR11( %-3dbc+jW3P;NdW6S;LXf:`l\"rOIUR,`4s;,CoJV`CRFKOi?36A)N/*p0l@aHI)bO`c`PmCKN%;_NVH$h5 %acnq:odU>";0T@*K@80fQR:2'_XMI)/>BpH;J4K*,`[lFXB9rfdqT=-H9*F9LgjY-VE7Esd?OhbK9Ob.i+V\t-mVt!4aO#R;Ssi3Qqd7R;[=]2s]UbG3#(\ %/B%'5A)(?#&pNr1-eX8!2dt[CE15n:R?1G@E:O/a+?U[gfD54L-95q2<.LLYJOtOc_FgV;qh4H43/b+_!@4b.+>;nn/:?uDc^tu/ %V7`^"eE[2IM46,(8IDAK&JX@t(nBYF<0.EXJ0]6Zbj*X+LKrcJ5Up>k_bX9\F,HRNH4hS+Va\R_K`hst9pOq?&@7sg=ST/&e#JAA %*#[`KRASTT(e/pRQ;I(NOBaXt@o$s>.-jc$\9_W2>mrQX'`Hp`Z"_+;P,P.oM<\@i)i`Wfu=Y1qVFcc9JNMGt^6W)6CWRSDK %r)#J7M$:/%5@lWd?)BIh+WP*AH8U@_TZIsP %K*moQ$G$J#r#Z?J-AD/o%VQJO.87/1u?eijXM2,3r)=M&jOH#a$!\/*"('kiS:W0k;6S.*ScS %`.C?`iL".i8=-51dsf'`I`",L);$\sEk^j*ZaCh:3U$:AZlfYl(:t]S?(C^CA4QS6V5Xl\GXl.uHpJ#`U+A+<_a(hPe=AEe=APA9"?D4-&m.\1/r-C.6gA=9>[ %=4WSEO/H2k#4%Zm+c5jP8uHfq7"n2[g#S3J'ZDkfMZcCm"F0t\1Lkg9KL6tQ:rK>7_,'"3nEP`_X:.Q0Nk,k.7?eF\IcVGLAu9&p5gg1CKd'r:Xs/h1`!`.Z;E6.`7-sZ`HUFj5 %o-3=B8pUs\PU_rKMh[\?227j,5&ol!#k\9LaB#%VcOhf3=OL;Yn\QIY&NZ(<\R0qE%VR"+/b_=q4EqUDI<-4RC_(g)%#]!uK^t2E %4,MHj+1u;'fk_4.JP?ISMe[r[dL^P3qHWM7!7_S*,%Jqg)+(>KB*P&%.YC,n`HiEd8`/ciNHQ6JQBi$rYBLiLHLn'J1&97\:7Xop %FHTeD0D)c3E(Mg<0Sr3.Lc7:p,nu^q!Wm&K*i(Qa@b:j,(lgM(^7)Ns]lXE8M)!&b)epsjO %P6D_9.>g(tL$N!:aVp-&+=i2j?k@"jRE"Z3/oF\*S-I#%D%5NH[#SJZ/=EuZYlB=1Y %ej7cr`'qc6=:'sWZraM'i(]CI^mrH&jNR@?$#7aqmJ+mF %@!ud`2B&OX;:220Pt!U2c@F6KUh*YOhaJm[5"R;7hK'q.ALf5&8##dTgD78gR7koL"(s"`[\GO5b1Gb]eTrXTgWauD\ei" %7&Z[]]UCXUJV(2.">>t]+tPn`3(/labudZQWd9#hX\1t\W3*e<"Aer_&m,s].&b/BB`R`n+:3r]!g6@am6Gs&8fm9?UP"rWXMU49 %!PA1B!D9B2&Y!,5g9QmiO>=]'iLmqG,%1BgH+s--TSh=fK?r3P`S/+Yu %D$9AQAmeGd/eZL:!1]g9Cbl\8@O=5k;c"/p?6UFY`3I:!'#4=-$D`kWg5-5Y?iXi1)=aZ"(8/]O`LA@)lEqkV@6b(9!7"8$8P,LB %(os=^M>Y0)S %eRSRU+R4T*%gQMH$>D"bd4r^*,cs3cn9!0\j:e@5$`\5Ke1*^QjYA7/WTPQB?>974Q4E+uQ=kRdTn+Ca70V98PY!qZQutq&K5=UC %A#XV1RF'P)'u3?F/"FJ%r!_=:4a(dd+] %_b`Kom-B<"X41HFLqL*Cd%)_bGhBc^qP&^:'se1OPq3U9Gc>a/&PUp)"`"rT%U;[07'E[S^!s<4r_*F4dm?MY\XI"`-FF>-&:u^t %3FXmNhd]PSW.84\'m7G'!sYm]OMn&S$tt96Sp8QS=-,#? %s*=1--U7LnQbU+[dlG)>m&Ro!ht=b`q/-)6Zb%3L?Lf0JFFB<)%WQr\d2h4_L1rcqh+UuojDZG55/cRLB4$'cs$IQE*mVJQPA\Z3 %hFuRk"8p38Mh=.5XG]UEO@ebj]ZBTY"8p5NHd;'MD8O:4jP@a[*9Q.3i>pj?AmIOT.h,cHc7_mq+68iE/e7AFZd%>#QdX,Ab[ZOB %365`)#'#`gRWS(%2`5;tOJA7\bTX-(D-p'Dru.2X03$QSpDNe",3gLP+C9\;S6EgUN/o>hccD %_joKBFrfZ##"!fiK:@tjK3[2jMg`1#S$c[-TLAM+?)9Ja?bi*OSSWUiRWL'JhnD-oE3n-A/#8/`(:c#\AZKE+64+`RsgB&*-ZCi %@iT]4OD>j1E0,.2oPfn=*-ZCiK:I%'OGb*&EL;r[m)MD0EcSAoEg2b?*pY:'Qf53pEp3B#G<*n:!/ET!iZORJUEWKXRKG-'^hjPl %p&U1p?tl8$3X=d2!R>b2)%LVX1JQ6F*U(j=/n2W?@TsF62i1$.\^4'gEkg6XIu%US+7tgDJ%0&diB<6S8D\]rF^WH)EioKpG^t,] %ZmQ)RB#e34j_-%$mNZ$)UmK+_gI^_O]%]gBV`W\Aom;Y@S[:Pukmf2M"5N<3K0eMtLHOh\&=5LAVGDEYK-'h`a#s:Y!7s>q3t?;VP:d>bc]]C[ %ER9mj%W]r1G5WO24`k[oS"=_AOJA]%m]\(&4NM)0\i@K>:HEa3gR^"CP9'DOe\8qH:]>4EjlnCH %d-Vh2j9Nrp!H*@ld,aB!SGrGY:B0n"lG*>o*k/mMF66>K\^MQFS::A#`Tc],0>>%\IE<@9Io0Eu;?'Sps'Pc/qHr#9r:/dkS'0Pf %IsCkQqWcVmDf9O.r9j:5>,u;L/SgH@jf@.g-KYVn1``^&`a[T-hD8!'JFHD_q-$Ti-aKIG4 %*eVR_C^kEgk8L4q-750H*g2D*KuN9PigMX)4s`Q.cS&2/N7pQ-iY#)f5t6*]8b8a82Zd-jm_L/4"%]Cg+1580OqQIU'dYQ6&48%g %O!mDie'c&8#FPp3nc0=WQ;QDR*lBYu`3+"=*)u;!bjdBUAlVH_C3aC"1FTjrOHc?(JU"q%>91MXc4CBgEMd3kT;4YLbT9keOTk`L %=\g$^?qT]V2%*4#k2R$r$^6s0YU8-`lc7(Z#K%bQn2h(sAaPGLZ[@XD:K/,qQ=YMHZQTcAR8t %%4s^[RhaSL/0pE(QX&$a%k"L^=hf`*YAo+p0H75qN/YVb/5?-`@@JRtg5II7djgmQ/36`67<_tP6!`SC3`'_tnmOm$)"L7'QE4pH %aq'MQBZOcRkB.:h*Em9kKC=aYF-\oC\.b`&H\Ctc.j4_,S2THq3U.m1)/1Vnu%,%4%H %AbOtbK5=)qYh5]#?j12Jg^^1fV?+V(NMrV+`m-C$HZB*)1Rs`2<'';P8br0pZsD)_TmD<.SrGuDL8(!a)&=ne#KTb!WrnWYNehVQ %:(Kia^j0&QhDt-\bHa8jM[FJ6Ao\9e&O]c>3ZZ2Z.\c1UC^B^[b;A4sVY5@ZENK14`F^6Af`VLBRu9>ZZ^QHp=Q^Us3/AXVrE`5M %\WqkOXn&gPIqa!R1e`Ac;798jYm3Q7q*?200sU]Jm5:MVE]Q_]Q`ea)EZK:)`-VA"5k/E`-c40o]RRFeLkgl;?4rO.b.(fb0YL]@0r<2:-NY%-<6o$<:&n4])1B]>i"1G0(!5YM\0WU,UOJC&Y %MM"'[BZI%l^r`U4!?Q#1MO[G%KnFeI %!`nM=Wm7H^q_-m+6>V&UQP&6tXP4/s&IU'%ZuNsl&/`?"JOhWN:Nq+bpu!sP-gZdt03#3uH:BoG`8$25RbfZC%!I%Y(+Jk)SV!s- %F&Z:dN[!Wh1+!81_`UfI]D"D&XV=rm0)0-i4WcC,!1=Z8Y`,C[1+eb@^,6>,BTpEogiFu>&;+r\'f,pt8P1a1Cd:p9f[b)$0+\%Y+ %J==90$BGR:"_KGuWttg;KaJbMP@EZT4?,fQ:,)D_b*k([#]hNS]SPIpRiO=FUk4f/"U3+I%%*4ajKa@L9!M(6X!k-K1V):C5!'/M^&(p?:-aWl98#AXL]+05`4&/m(%XFUi/!bilW^'^Q!d1 %/L\&0Os6_)'#Fi4W2O2^kt:N>[e`E4V$LVDY#qdIWtQK^&jf-+Mg@aLd(GS>71kZIn+cr;kWpO4\t^GjjC]HA_KT2=HIN=R?+jBk %aR!Vs%f""C(Edm:;_W9[?(O:iB>3UCUS3m29Ct(k6]g110?/V %PEKmKo(+,5n;OX\oU357AiYTC4OaKB=[#6R?KB5q@+@Y&+c\g:5m:5;r%q%0&sCVG^9YVn1GuM9"f@_&`@^A64X(+#n"Ru_bUk") %/2(p6_Ysqa$j5As3#0p"`W&&_0AW^VR$'5)eAGH[>GZX-cc?0P %e0;iJ:(`0ofg/R?R)F>X5[j!2h;&_)8)dL*#/KfT&4dS[X:]Ymr&A2_+?Pd#+S3tN&@(CT_i(b!oK&R>5R_#T#T1Rkm=:!B+^#)`K3-cSWT>X/ekUI+=L7gjXbVPE-377b"Un_BHk)g@c_PjH^"sVhqKe&3H$&hAt?eg!P %i].0oUYJ5q'@;;]FZ2.'n>),%_loaG<4D46\B,j.3)MT28dt)fI$t2E>tQejiGi&JCeXZ %8-$W3?m%L,YPl1P"5O5$36kG9\])+T)Ze?'=`2jWXO00610'&O,'iql(=lHg6'uZ!g"guhD6NZE*^PAJcEBOCZ4abpRd":U6.RH) %%ZO=76Gjl4_,s6ZA.[E\82-\"*c)'+,FI9umL,*!"aAanI&)LpEPI?m.nUp_P;4COUl>i4eq.T>j*rG[;.G %`i`\>1[LVhc=se>%*9HpR=]368>;nRtYd[0lt^t?kJPM)uA6b %-I5'N5p4no0JP+F6N!Y:L1hd:lQCMm79cu&S2#R=Tb5@#kO21B/k1]flURVV%'Pa9h>B%ka[V2VW?u!'5a,9&?ij@4UY_G9qQ>:!2dRaItMek %q]g&f)_6R(rUiOmh1=u[$"Gn**AlD/+34c1M/po3>4P)L/t7XnMVo^#HU7qjc*' %/s+7m0Eps:+F6c!X.QRJG,``/6HnNfGlcY//IOSH)EZfNp_(6PQS/Iig%8l1DH) %''hA*)>\SuP+hsIcEWB-2R\pX=P%`pHg3"O[X:i06M[-69X*XAUs^?[HSbkqkIc89juVt*VYVa#/RM_Rf8nlY!\=_"lkY]A:qCce*ns2"d*/VUAC(;ZF:;q)Q$[2fM9^Bb5sC!/VHANH2)lSZ1G\m,[i %f@(VZ05\NpNE]Y?gfD3n*'qq%3e6bFI;D`ToKD,I":]N!^rJKTA`o9+@>BSNJ=+=3;/Dg_Nidrob_k.B^MAp$nGQZt/5:I\HVcA* %2r/XsqmHsPF?@iDfD!H.m@upf8oinoNm_&XFu?g=?`<.(_;m(;`C9S+,:C-eRYG2mSNslWoWjR5TMc`[&*2qN%0PoW:<9Gi$+JAM>#DnW[9"_Mn%`FRW+7kl;>8Z+jAN:*]Z,F+A2_JO=Pp!!2TOS %T@$;*]b1d2c$\<:0Kk_<",2b;76<.%&]9>)liZ)#A;baNIK=I/=-$PFeAE.X/Ar_RSo9L=]#;.=3 %.A1%u.\Eta&aLE`rGr_9U8W/k-^.$0>]\i`^cj%cN:?<(4,@?MDBG'W=Hc/*pL>&jfZt>-TQ-1\ %U,$P"R3pgE)5<'FcI)^=AH7-[hGX4*I@%oe@41.muOF_?)^e"S0f-!:60QF&MK7#,) %+*bN*]ML/Xg7HN-!`NMjTiG01Y\J@:'e#!. %Qo^7@/O8BqKRc;SCUDWV"@4@!BPSh]@jjnHF0L:3B %K8ni)qo&S$F6]q:"aNQHQmSHMQ>m+?^H:(aV&iTs.KVX(Z%;,3Y)J+-ipA!!XMg*^Z^S2Ic<<:SgjT^qE(AZ]E<<:EmiEJukKBsk %f?Dn$^o>:F1m$q%l[XEP(oA`;JW.[(-Mb:'@U;X)KE->AE0iF6A@8)!ELDd$)GN_0<:pXPgI9Y5;8^"3Ha@(niE)a;9T]@rpFIWK3:.NKnl+tf#MJjU@Tk&9kSLHkG@[:_"k0Ogl'LYkZ*Mq6[;n1)N %eh=EUaKecj,YT+j&=$V!LfCd)2NWO!B*:hG+<#IN[:NXkF+lJ;2mc!T,)c)(%R&8b_/-2'@\+S<[^N8=P((NeSe7J:et5$?k5BO) %"gl_b-T.8DipSL-0aSd:=suX-CkJKP$)<+WX'1:E>f'gd(7IJnYgl %UkENLj?($ecId2(;KhjQ,P#iA_S!@iR)18f/*A9q(f\%t&okVe0sB-<`AX1Add9@W8eEH`-d=KcTjuJ#8=qDieC]T>Z5>;k#Dc&& %i_i2]Z+19d0]126BK+UsB!-,NB)SiMrpN;u)C\Jo*H5,%(WTHg$.9ZZSUj$4([`gU^9(;8kFOr,6,6F)Nog@A*BcHSe]a\tJ'd#; %(E"A\Y,uEe4b6UM3p83;nHeRuhmU3'NPb#!Z$oIQ8W`-B5Ni"_a0GcY)L3An,1it"pS-L.(Y,+qD!mtiU.Lr>ADU?3,(A25C>FGf %W`o]\Js(06E_n;7gGd_@H`AO)8#>4r[V$B&1,iEbc^BFu^1G*cKR8/tZA,,hj#YQ^V$8[V_UoGChW@ %!I?`g\DNTW+/)fHb=T!JOD()MV,G`V[Cu'?P!6\M=/)GsAS7R$l,:%s69EbmA!9i^n7_ASAAtlMbZ1&Di\TLSgGXns".Spu6,m5/ %^=2n"hbb-/>NHPPX3WEE(=I]CQI1n,]/H1u3inbS`&,l?,b-l7m=h(^.L;pSC"(3mB'7fR3q>7#8=AJD`GP?HK\,k;W9CW2)_>ZN\""!RBl1[]S:^67=]grf/(W*4*V3<$G<6ARugpgLHB)(^(9kM^\!E#oX`4-]s(QZgX

f7DKbBo;JPlG(f"5Sq[377q^E"`;tB!r&5QGA9LT_a0n]*3pIuiiE?B60g.c'nbCrS_>DM %j)K"G5la'!O:&<@$?)5pSJk3^H!5oSA6D!($OoF>JgGHtk]9KYS:VTTV1XA)U&JQ.V"=#j]taf=Y3l^=ctmg\51<2,\hIpS='I=j %cg&;X;/71o0JlRL#%[(ijNt^`JK=j&R,-V>W`P#u;+mU)J7/9"fJk'nW&mOLD*[[dE=to4RT"7M@J'%lB.\73I736JJ@MUSoH %$hP"7k[jMNCh;lh>7Lh`%7()(OBLMeTU<@OB/8ae^oT(pMFh;u@,IYW3L]J-M2$DmL^'e6qbW#1M$*>bC]b>rpfdl`FTD3^?JfJO %/-V"'+?@'[-s>e1(]N_LE1PjJYdL?'aS<2\3+:`o9-K@(+I1J:;J(2^-aBm=V<@L]J0Yp/q %`2FDF)-%qNM$OS!(4/Og'\]lZ150.eG)F0XCLUC_ %:8Epg0NsK:QAK@*$%SCm%T+`#fj.8AWq7O?>X\X:g42@^gcgN(L^mImg7/.E*GDi:@cip0@kajd4O%'tWc^!Wo2l6%#KbL0Rl*QWbP+DC\nSPY %etdtI#K]kR0,_CCM7lu:,R8)b?,*V%3Cu))'im:`aFn+SX-NsJWgP$H5Wa_#KYf`E$-MRU^+K#in?-JS0LW`;^.a34[2^H$"N6rE`i5ZX=2IgU;km*/<]S*DX?nmE %8$d:(JBMBodRci)S$WG/FOj#W/,.[#>@!')Ns;B5qb/IC"[CDfQdkjf$;@[eM`G*9Ua&YC\P"BhaD&]5"1YVrCi(#Ro@AelB3`JY %eUL=hT^p&XSAUG*T`^^ST:`2nGcc*2X-j)iX^i6K:SisOe4ahD>?Z`KVL.=0k&@25Y()A7B9_JQ#]6Gb.c[>=K9k#81bUBpp8!>_ %1m!>XHp9huG6*hIR1L01gTS'_%LdO3./S:]7iX%)b8+*lG+Oji'NVEP!XfWQq85r6k1,19I&T-"qk=olna_$!l8,Fa!!Zd[7kP-2 %ZkZCf\NmiY%N9.@^KUp`3/pRMTWA.T4q+I7J6%YErf1d7>,kl/3F09UKeddBikVFOTsA3*bF,sp-&NSi'mXQ6(X=*IM4U2/M@5Q- %:e@WUM9DU=:e>Y"FF^g"XaA?t<>,I^`YiR":7qh:1'[kC57r-t_sBS]>iBL6.Fj^r\1C5%LoT""F?*Ug@QiZqQM%rohq!tN-7%%B %L^lD(Eo/dM0Mi9phQX]r[33c?=.OB`(%plO^NrWbP[eka6'd8r/rX,S.c;_c]mU-4T3>%,SXP5u4s"OPcum\ndZeu]]1l00[FbP1 %C(rcp=i3.OJ995Rf-%%=k0Ah@TA-N$#20?]kYDP4M`@)r\9;i)#MU)C(CoL]V%)3o#:b`/Jbc`l?ql!n54.Q!9,_f89+cn-V%`$0 %oa9-dlMH-YJ0m,*FmoCPWml+W+K2$IBilAV\d*&kJe!Mneq&k8[SJ?^SjM$E[*B7a8p>sH(Isl4gi^t/,OOQ9Ph0i+h_CUP56ibZ %UH>,$jM;r#Xt:-^dJG!GXhb$T+1&D.Mf*LO_Ei3Xi")!QJT';78EaH4Y:m8LS:c8(C-iNT-]jbd%AEDN*NAu3Vb]r^/)XB)t %)mkr/62\I5i)q)QN,)C,;_:@XbKNHF4:LgfgO8CX"$P,f_)>I-7a2;,5,BA=!A:UjDVIG?kpn-4QjmsJBWR#&`f06QS6na*bFV?` %AdI,bl][?O!k\`2^@+*gU&buQ6-KQ!Z?on< %ESB`Lo%HM6S^[_tZd3i*0sn,C<,Lo+O6Wh7)#_r^0KVJ5RHYMpMA!pY-[lcOKIe.Bk@Sd#&29!(fSQu*bS_];=nk-EkliS[JQRPd %F.K\7;'b?j4eHcD9&'Y\=IS"@r1k:!q\UV5/n`0:'K#b7H``RfgXqj=[*qd)H27jtrF>-5[bKP6$G&PXFfQVjAFW+jV!hS#T!s>$19RC'G#J7DE %SW;HWlrJfMTGqrFfG'Q%lH"rmBW4=F(qLlgN-'4a>?N`KRrYLm#G'>XJ^.Z0 %33qCpN/XZ6/;o^s/Tl$Z^+:\X#4J[\/(c;Zm5DP3.!KGf:%+$'J'QNd[4Nt9^TBj>_;o"U6+^Q#K0;<0\3#];@hEE.*:^QMC`P?) %LGRM6blOq^3Ep+pLYB0c1dVLf79#i*U?T(5Y79hf&Bnb8[8jW!++_*`A4K`e<'&[_TG>(T2mNP=CkWJ.J[n5!kK8"q(<09!#9-N_ %^Md/fp,%A!6Oh]+'NKNqUJ_cg]D`\E_#!obb"9!X:ftM18iu_h:6L2GK-lEYZ0B9dhDY+i/%Aacs1UEuJm>"(YQC#u=T,'-Kj:![k\[H^49iP#0/Lkm[S"p/TV64C7JfO/f?;NqILe_a@'F'I'#4,`]MG.I:(X[kQ&PXtPMeF@)EfLI"OBjOAFhI>j %NGf1hVlj)KC\bF,@qaU+$_L<.]7@i_mUN[5?@$>kaZHkQYR+;NKR8d_]h<3@Z[8AqR87^ag)eGW8a;d4!&^/\]jm>Gp);^,_-6d[R*$f=TSNQn;UIHYT!r]-I;iDVUeaiN1"0R_\!dTT,6%bc"5IEsBt1@C6r'#-&OWlqM9\ %El'aHKu>!]j9)b0J#")Ihh1Ca_G%DXIs-t7Nq7=M?js.*[E1o4%a9%q:m^MFP;Sk7N?8S1"\Q:5XmGlA-sbTIHb*N&\4DI824s1- %ENb"=-iGN:")HiUB@^HYE-.jt+I5m@2$`R9jDAVb&U1jf*;FCt:q(6CmYGX_E),fcJ?sahP,VuCqZM)bZ29GY&FMTPfW]W-mSgVk %9oq*__Ljc&4d"o!Ssh9j5s6,n.a7h"E)kSSTd,&D9,#a:V0"F**X8t"JKWZ!"\Y2sA9)eLJhEo^?3hA%TPk/*KhFhoi[ZS@iBGTl %>t"FY=`t^ADfBl819)2gd/9EG[`iJK?VNS3M\BQ.6.K\,p\QmVFkg.aX\6N" %S>Q'(3X`2#RnuK*=dSGf?(p$)VBI %#N#ot5aSX+(?j2SeH-9\FSV5M5g_m`f+,,/!J>mY)m/d[cP5f;DN!h#D;1Ag7;N:h])1+m_R5aH#J)@/Ydnb`Ibj.JkWQ,q5t`6M %Fl;$Fa.Ap)>MC"KDi&);gY$/Cf.q71fO3d_nDp"#tM:Os;C<`47aIaj3 %<00#@m&3&Qh]kX.`T4Qkl%sZVBZ^GBD>O"(V:8F(HMq/lD%TRATZ$cR;BNJglTkgDj9mra]')??@='"X#arH3[VZ-fW/1tqTVq\@2ULGP4if2Aq8mqjf%.\iS_B__2XJoc%+m#pK+%[bdnhX`q&/[RWtT%V=cXG0$AE]LTsME^O](bHgRls$b:nGHhQ(=lU.lF@C`/Hifs^/E5@:i>u71RL/I4n1n*4%2VdpN2J8s(bO-$K4.F<5c?k-YZY^FdR)jB&f*HAj:q&6%3`+AndQpM8fOM%544W<6@>6.`:iV>:L?+GR(/0a@'Ic_rs=mJDgln:Ok+X)iIAQJKg9.IUdk5Zd` %44at./>Z%P%TGm_4##+3k=RItk`W'Tf4L&_4gp-"%*q,"Y!*YPq[PY`?P%ag\$9l+5idjf^ucWh*WWtr:%fU7Kh5_\fU_XBhD3Zl %Q#.sggbpVfieQlW/SC):,*SMoh9ru--6J=%TIh0m#J/gY!<]jt8XF@2jM^fHE\A32\pE43*_![kH4SS5T9aDnTVQnGFBkPe!&1t"-?%"'!8YR)VJT[kH[^d_KD1iE`?29PpJu+gqN1.P23O3 %UC.?O*ucej[KBe?!(3f!g\7`P!*f`]AJBluD`KgB2:q;-9T\Q_UAB1?6VUgB:"8M_aE#u&=11HcY%;d=e1KDbi-l!?.!N.)a?I&d %`!V.KE"c(;?+4"?KQq9J))Zap3*sK,B(23]b]>Es(#8Qo[QL+pPSm5IHZ^0R48mb>L>DqR-N]M5b43f/:['GA7E6+tDak6u2#?t-/ %9ClEIAd%gD>_&QlmAf4F!H>EoLl8f/FRYi<[0C"L>1Lk=%O6"r+tp!*/u*,2f]\NPqN_RX %1iEV&c-\_/CfYGYA`*-XXX0S*EdG/>Pu_bHkEC!:3EnC6]pn14CY8'P#OQF*?EO-mcU@+*6.3si?2Dg32Rt&eaQD+e38`:8A-6+o %/Di`-MPDCm.^kILRuP"-0HBdfK!a6oK`NlUS`ZG_fKs3PUu$X((Dm87\l)I1RHQG[U>qiL22d"IRI>L7nG %UW+g5a7=O$!`I,8pA?>+"GUIlOI(BaJDc,dq+`O;CTN3$_PoSu`Li7)a"#3=e.8ZK6s.(Qq,'q3Vc(1_ZtCA\4ss"K-65/i3#9"H1c\;[UcSX)KBR+@.?End%QeK:80Pf87/K1hTS>kGbEE$]V!92h5-? %=4-JCE3e!7>mHR)6,kgo&Wer7C1W3dp;M7&7.b:%?CflDS_'VU6)usG71[?F%XWgPjWG2[2r.>r %d.tB>2et;fE]4#ajk&C^RutR?a<9ZQ$2UWdp;_U*9t#3&<#fsd;b6L(dD/4M+,*9'Pib/"T9APrI>2bfjR9CB0D`uaBJk&GNF6:. %lV]!!%Ea2Lfmg67IG8WHS<%EmL^n;ITIH,h+(4!3J!K9:bXontddRi.%?nNK.@X@RM9p+;+]i#RKBlK>Hh3M!V(>1=*W1k %TS!GEXbP7++>X#+#0I(Z!]9Qi`1Y$qjb'jB3THP:[A74".%Nq;1n*:>s,Q/>Ht#h1Cp@I@`30=j*4pO^1H %4V/9.**j783JcCLeon=jg6)f.5k+FqW7--$?%K/"PgRe$XriUEcOrgI@hi.R*s'%bPD9=+]GE=,1Nl>V>4-NL"esZF;OZ;PZ5Q3: %AEBXU&M>.Tme^]L%`\*EVEHVn@C`.cXJ071ChAp=2pAj?7\j"V@;op)5V"]jQTJA7G-3@4@*4e`*Y5B1e+Tg-#jP!"NtV%DZW'M7 %lY=CVQiY#XP)LacZ,t.qj_<.X3@MQ!p_!aMN9]rQBH'BV8&J:UFQjR+a-Ti^B[P&+I(k_-?`Vlg38=BEpH7l:jj,$eDT/glDpA[S %mu\guW]7fW#JD-QT?u<#6P*tjYuJP[!j[&cf5$+M;@8ucn0KFY/_#hq-1W`noc:#6deJha)mcEqM_;)KHdJA1B98c\?E;pOTE,5R2"^pmAe]F>an3/OhPA[6#$MT-KpQ %R]bFVK%1W2+.K(q3TZb>o4h^tq&J0UN)@")aK?Et9hA.=4KLON<&f)gRG2>#P3@L<`ba3fCO?sJYa261kif(N8;_J?3AaOIU'9@G %j4T;FZ^lQQ6_R/$(i0O[\%/YYgY/Mb2eis\)e.F>Y\AmIDE7+TU<,DrqJG-@eC+kKe@^3$CW']_&t.iRa*UX*iG6f>rpOQZa`bA+ %&ObV^N#_-I[Q=)1[9X#;@LA_6V,+MXKI!EN,*Pcij,.'>:[<(rj)5M?kD'FjL9JG0udhWb;SqsFWkhpUr3Y;&7c.1]_;NPV_\0CnWZ\=o:l,!QR7SOLjVli>L"h_\RY&@1O0/*_bGPE:OArN+QECL3 %Z9.5$Q<&@O^I[JqN7Wh=3/uUT._hmjIaf_[X//QWJuMe@4Kn@LIBp55hb1guDED(i(Zt$$Zuq)K(kKDEH;Hf2)!.TMWgLT%5kgb- %Pitfg5u*WHbE18\,sukC6#j,5Fc\F^LrO"TU#jt%$kRI5Rd56;9IIUR3-\QAGu1qa$kji!,]sNH;om?e&8Y@R\pgVA16F^1"I"Es %`n/?$4e,C6r^!c.Q53cgfiSVUn>rUq2/'0I5tJq]B;@*bK"r7T+@232)BTG>MPSnsr4UYQ+UJ63(lQZ&CU0*@-94p"S;":dG#MMjflZQCta:tD37STG#& %4(k*FF)kE43Z,gJ#TRHa?s@cp?jcBabW.Rui3^:'4d\!$).?-/%/\K0nt"C?eW*XF`O`XI.hL79BV!@7c5@VaU%u*Hnk`Pub\'jC %&NI\,lkGf[p#PJ*#3NR9H]!'EoL[ %2FaTpD]YR^.Jm7<,+ur^mZ6:2K-eUM+ZFt#*aTa%oHp7Q\?gfeE/JWGA.+S19Z3`8ZDq2XCP'-BTN2]]RT9bU>7_mIBFogGC//Bj %[?%b/=Rt#:)r/S7b.3clR:2k[rjgi)(e#5IOM]8aHpS*.\\M`9FU9&BI0rif?l:Hhm\"B:=;Gt`:?^51a_"PLnk<0^3^re9_`c/dB3U@:4@eZmBKEd6V#&WCJ`gMkER:>FpNZ.b=e31@,&QB>%,=Pp_X.&G].%4ZX"_]T&?*n&;lf1G^djBc"I'YX1fuPUs,JuI8R/JI=ZYB2@8_r7`=O9ke"RT]j(/F> %!Ke@s4ERpUL?SS)#5HkK>>iW-BN7.Z.-'/&S@]#.\k&n]`nIXH6_m,p-(:9,EGEO9&FG(jK5klakTibKooIMt1JS:oNj^kH;#R88 %bS&EcOf>f1$d(rrVBr29]1b\"VEDXB5^[NT1Qc:B;ZOA1DOg8DIJIIF;/EPKP@mEV'AtJ*dJZCYA %[hWOWZtX\dQ_IZ>!-t'L%'[FQ:qd_8$nskSR^HuPD;-f&@[ZQ;aLjQPO=3iNGec!sJ--p2i*q]\M]-5aCM`tS#\qGH5a'X3_lW0@ %raY6tfO#JiFMm;S.m;DS*Ekkq(B0n`1N,#&BpBZt2mmaY7,l6r78=E0WG7.l0ngOn&X %-4i'3+L+opRtc]DiM^B1FX1Z5<)6'D5:1:tZc(eO3f#4OTa$6Z0Up*r/)r%Pf5`V-=VT0/S,joJ*RCa*]rMm@V>9:ut,mL-p+=GWr'V2!a %K]WAq0SCX84?P-.EOK2@[uaePbV[.fO,(-a@mjS;>=ilb\0tGAlUb7L9GGJV"L/]q';@f^Y\I]D:^H3tid0-oem>NMOfRVpg)X@E %?\;M5het6P:8DlXqNbi8c9a,PF\IeoB\VaMDZc?Q3%)uSl_@dFr=a!1VZ,0VD%X5D4(GQH^UaEfGEkIY==Y+biP^;BK?mXO(kmNP %kB,\#b6WiTKNQ\R((KA%0j*a`O7IAuLhcBHQiuCr!C$ep2gP]pDatle8$C.kl&SE$^M6El7H[\#P8.'c*S[].TY^ASZl-ckf7A_9 %>44YPgNM_:\+^J4VP9tsm2"Mm`V2Seb8HD,GmF*H5IWE: %c-5V#QZ:55EG)?YPmeVEI6S81,02O1\Y%?LW %VRUNo_@]G]j(sFWOV'iDX[;G(E7^8TRQKn%.f_XDmQPe)(b@P/KA.X151Y;[eDQiqmV@/^Y73%Ks17M=`aC[.V.@,``i.`R4%o57 %?n:X#GL/eeRsaN:Y:E)8rOR@iXk+WDHJnkM:Y%54nljFR8ti>h's/$j`sK8:\[Hso2?LXL_0h3sTlY!7m+YA15cnFPXZ9`/AR4fg %-iLU-f$@F"oe8P\CWegZT5p==M=*q]5e83`>>YR[rn9j&??B8UOhqkF@ruo`2E]2IJnko0)@f_][Q@X0#i)Xc@Kjo$@UT60F>Oo> %?UoI<3oTfOh$un,Xq%h:_h0P-7L.]&A+b7sE3Ru](1:s_%l!E:IOTsN:0OoW?WC)mPR1eZ7BrO'E]s2KNVc'S(Xnpu0Z9WC!NZ)# %$k%)i+>pYD5.9+RF6u68)>fLU.t$-W)]iO5IEh+D.!BE0$W!R_CL!:WQngDT0(;^1iLCsrE)$qL4L&+5:sA[SZD2_!Gfu-4Pcj>\ %YWh3g+uJ]jIFprmF9SK6`J+mIgW?\LV.cK.4m+ZRMc=)en)?`LXsp&%V7@)+hg-i9Ea\',qiEYdPfen5JoOL<'ipZW$DS6GS)%J' %ROre9m_dM*f-D.GHRm`_WnFZS*ANlu!*c[8'Lh"dJD=3c.FGhJno<8o#);UE/2?j"j4p7!e`gp]D'`,KXGlb.s_["i_J9 %CJ(GSjWu&W3o\s0s&%o!e=O#H`oALh&$K`Ik<\,u\1R!`2pf;Ao,q(JL/k'F"'?Zn,0/:6'>NZW7LPoh/$LE*/`q#ZcT>SB)k1O% %g,#aQ0V9lX=.Ir)mPP=j.HEuVZU>PIW'EYa(89!KC/ACo*4-)NU85774sB<0l@%Bg/n?l2=pa\++_.arfb9a2g-s\jDT>R@E%3;: %4$S]sgQ#krX>GpCEAj8ogEVSN=oo"S@Z^m94\>$+8J>u;bIMdMW;RA*5L-#PH@uJ$':/M8R!_%YD(X2ddtcAGg"-!n"QW3hp\C`n[OZ/1\l-7P;'sh@>d!$k<$\qmelseKAPg#En(GT>(b'3u=)emb)r%U`obr2_>cj+n#'4>s %nOPX_Ep&>*_`HC:$qpG1nNuT95-#F0W)o4)E:;T%K12_5@X9/9&4]uJA!)JMFSP"<+uUMR_t2_7"fsu-TD5os%THQ[UYjK.'Vc0. %)3Vl^iF%m/;LVmjW`MbKT=]c\I@"3c7B%[`^X#Gk)-TXePi4&1SDRfar^c%XZodO2CfrN-$VO?`+/F+VD)'Y4jp+:k;piQR3WS#< %FF]J&/p&8F@O-:4%;X="+'Rkp2ZhmfJgXaVBJP4*T]dVY*Sa(jE,i?E\-g?^;eRkJ/lN4:1?f&e-X.%<5-fDt/>NMT>R].d;ND[; %0Kn>W)seChMese;o$pTN1:TjsQEoS^FT?YAVh)egs,mNn]+C@joM0*dEiobI!>R">$OMe-C%0k7ne4R#V/r^klY'^+XHB&"@O`N. %WeG%fg>WSOZ^P4,ATKCS+aAT//B(7VO:eJMMr+8=\_p@B&t>B\eIlq;C`&,_%CNJFUDCc.VO1BT1[.$&/:eNu[KP*U<$#kJl=baF %lbX)qF7sP,Y4\\rGWKL'JBsU.e-'1_KTr2$6EoCq":tMgn^C&]&Bci3B_'O&nn33>_Ss[Nctk!]LSqbZnW]6!g9])$8TPt]#,8nc`0>S^VM"8G*]1@MBO5Z6 %'V\6/iAuLI@5if#Q0#ZlU/iaS(k/$`"8+2Q/P=$4b'A>&kRWJ06jif:W^6o&\6gT'g(kckR$>'!RYU9`X>@f!(Fq#1RAWUbTSF$? %Y`)H4;[\4!+V@XWP]uX]@r[@M&Z,;i3KLYgaWY>HVCq: %,]4&lXUrrq(FVl].o4qEGGCIf1kDs'BL_@PMtf?$U"rVl %a:hVuhYj`!8T:)I>E$)<[a[&oG1i1M^;74Z1c^VsE3L2P(1?5]6"pC#,s<[saf$gOFB50IT]:]9Vei8D5tF-ffHTNY8qZDPXP2-6 %HJU&/`=B$R*E)r0,JP3'.(\$me2:pdg#Te6>N4.(Zal5l6HrOB[eH9IYh,C1%l#N?-/7S4mD=r!1,_k?)_*5h@#/B&S0OnJH>q:\H@/k^8aoAJ-8egrQF1,hB!LPNQg %@dHmo=atYOrgDr1'tbD3(m%t[%.YZe3KrfH5:hFu %?WT.[:!ogA^Gr?"Yrh4/0#DL'X%qjC#@Bo,%td>Jd!!/N9Ej1?!,V:-aX/=pghjD<%7*UQ02no3QdeXM?YXCS7TCB_mR>"H"WaO00G9%fm?KX"W"`+1AcZa'e@7Sil171`7f_:)&BXGa9\>ZQ` %[RS__"uF'Ye'=O8@;HIM(3Q.:1"^>&A[G[,>3ei99Q1()m^WXa`8lK6N&)LIkH6F76rclPLu*$.HIKB=4[ES-#qK%XJEWmGKcQj`*).Vmml_^k.U^[E$1s&uE6KLDBcf-&U;WC!id!MBbsGJtXS0b`#// %hhd8tPN,bqW^p%7&6@;@9A1?1iCY3tOmQ+B+nd"0"2_q`$J`rnij=DMTbs2LTO_2,HY@+^OuiIRBY"9rn3oNN;eh9ZPh?_6I)_gW %84!ou!t-YqV8*;tq/MJ&p_p`Wo36!lPA"D*.)&/jEgWqA-Tu&f/es#UPO1]L/4T1%_3X$jkRs0-L1VrOBN]_s+jhJI(u(5;0Zj4! %EU!.L'on;%(mR/rguQP]!eHP9Zb4N-4/dejOOf`/3$tDOMqIreNfI">E+r`!GCil(%3V5d^iPF?L`5U);eVChCUk2&kZQVL.+,R. %a'_dS8m0GO4*"^pl@Y,6(RJHb?4pjK8neoaX2S4hdY:=GG9Nsq6#"caLpPcE>1HE-hfKj>IA5V'l7mXDfk5!<>HDu1MC(qQ(i %Xr=3J1I&I1*A+/8._!jP9R%eg.-$/ILo@eo5M[+E.s%*l2+4bGlf&$8ra4 %_PD'5g_:!odEs*11AeaInh_u;aLBt4/[9p5LF*h]WpEEuZ*,#_&:C2,B& %:gk&R+?$(!n7Y^@-ZrgK\AaTl=^8/8MkcC*>g2l:>n6EaRB;)Gnu(uMn2&p^+D[Ec/s;+HPslQ>T?8M8'KO_Q]5o7c5?'_pb]7P0 %'O:)nf=$70L$^`O>(UHiSN40DLTQSX#GU@%f7A]/#uI[bI1OC>i$:qof'[%S6[&!>PYXjTQ`B?>LTeoK)S2"*=HHB$c`NVJK!^,P4A[+d,@4PX+B"57*5J-HqEC]TO`"7YcEmV_0)*buL92)pAe<@lJ;aEVt)_u-:Ga'F_T%D4Os'p.UC;&OdX %FSI,?k]I83LqU\/0J8$p&&TpA@6KXANWQ(35K?a#+cMH.rP4F`7jp=S,Mg'Eka*+kPNe:)#78flHJMGtHnSZ%\lTmT?b5&jel_^Z %j[NheJrHp6E+_Q@S+c$?J9sK;6RoHW/K0ac.kPqdDgoH4FLF;].r/(+^qtcmU1<6l!:K;Z-0VZZ<3K`&NcT[P7e;`Xa%(*G4$UCJ %L.t`<#3.+RKS'D.g).`^XkgL%#S6 %pB->rM//jmaBQ*,2P%+rcUjdM^juB6)98=G4hJs71So'kTDRIt0OAOO[T@G&rK&E:"3X_D6VDY,8[mG>AYTYp+%'0:kQ-+;qQDQl %;hfW`7=K7jR=2X#Zt3q2(?QKaIg-bB>kB!O"21L&oRDWDBD9='?l\*9;/u@4(tF99eZijOl4lT)(K-GP9\Lg.B7D7hjDOhu)pO,< %7?rZ^Hd6UYO%H4PoXaQ1I\#s6dR;Ku8f4f4#Qd-;/'kOLgUI5_VeTTgVA?+Y/OrlO4\RkZr$cLV-%I*Am.%?VD'g75*^1]Fgik`0 %"n0[EqF@c7j(bq-\'b0bXHYFQE9*Dn?/S,:6(IDBT4u>Y=R7X6+\hs5KB0.k;->:4alMal3.%?j@13k4/8AQ&CP%>i&=R@>*d=5r %p'U5+[s29qZ9;]-OK!40`DTs^)L7t_=r[YYDf&Y#dU5#Mr<++JHM30QKS25L*62=7*Qdhdi0!(: %-5O2IS5GW]^P,D"ioq<\'1)BM!-^&G:F)27SimkMsR8ApH'?ugidZY')nTIZCM0JLn,8I(16*^Q8?Tbr6+mFF[*2lCT4]9 %4dB*C=)_:CN%;4,M97=niiQc1Po@doUf\[a$u+&JnT4L(S`I7OhV4(=r.:et?)bEr`#q0o,*egt=0oD,9fTT[M$]++Tille/o@rI %Vj>$`R1]5\m290)?P)_NFbT>uTfkHI4mJgu6Y/4RHUTm1;=$!36gT]h_-6hPcAP0o:=!F-3tO=aW&+^rf>sL;^_kdHb*.18N-N+J %XISi8\XjI/Q?LZ/A+q8?W@u03Ssjq=4P&RZZ&@U^;(6[!A`!:robul2)]5.:cMJhAb$Zoo8BR$I(c9g_cO\@c:%3obRHaNFTET47 %jr3r0lRi^n*V4r%#Nj)r[3/SA&M3@^i+7IkH[cRQ/;K,3S3*& %j=[^9Qu!ii8p-^u(:U@:E>p=;^+CSWe*_UZ.bjZiejE=[Rj$C;.s76pr2Gq'YI?tD@m#T>oumYZG2@9@7q`tm3&3(8&2)!sOmG;F %533iBl+_Y%e[qlM\2@RWpO(CJ@)`m%)eSm[2XI>s>XG.DkL_Nl!+kg9R0R9Z9C)k)>e7m58!:grB#]fIp,7 %ppaJhp09HQ*=Ps!(=lNs7EF[5a=N7Q<(mEPc_fLE!hFGC^mafPeh]`69a1]*K9hlZ/'b+/e9Wk)`L?#MHCZPK0@kE%Tn9rWGa[kE&=;'4eZT(Fi$9$1S(qg=>YR@=no?)EmFo5pui\(Rn(i+aqamIC/%K>+Xa>fqs]n>pB=ka0K8ZXSLW*2Z? %;"E!a)_I0phH[Gt:9oR*!kp>\1_uoZo/*%0T8?]ioD"2`U$@&j]bV`?mr\XNhRR)S`ooP`,PbuXKQU@q3E)WL*4:fM:cYsC./fjQ %J^Mg#&AU+>T9;:u*5d>b.\XD2mGg[seQMmIilSK%M_e0a2rR73mbA,KIW@1BLr[9?KT'k#K:@if,&GDTnZ[fiZak^ZXE_>Q/?"=A %&_Kc`/OW!@&!ChI!\>l'.XEcn][K5!OG@jNjBe!WN$;#3;'SP&2+:>A7F,<^;iA')Qs!j>RY/R?"K]F')$)aAm,gY+1oITa5kV;d %qpU^:'?-5Z/]lsQ@pl#d0;75r"lpI5YWb%_cTVl@0=c(Qg!2:78E;li-ETsf[I:c`]>+G6't(O4/tJRe"*5Mi1`0OlZKN,cg3R14 %08*WS#cs0Wq1TE"UJ8RA)OZ*o(>NB%26M@P'66#IbkH:'a+&KM??C^`#d\nZM&hhL&3N'rV_!BC4LQ %p\BPoUq(aiIg2[1_^o(uMsCG&p]ro<#=jl.[3]=<9Ip51'*.)S>2kQbD/#L6?"&]XP>;1?4Wo.5A_\%Y@OUDLqSuV`U'm%&^/Z#$tE!7#h(f\^l>eF!t=u3nr,U@)ghQj %RZa:D&Q^tjRs5Y4?(0=Yp[5*;6T0Q@5fEe2ECZ8hF*aPLEE8XAL80WeJ^^uHHfojU^TY1k;Q5i`Gc'Fj)IDrPr2^T9G-I1qKj<("%s#) %pe;(pE4_\(&I"L5\TP(95?+Z!A?8@o.iKL<6l5j;,jJ2-_5*O`M(/'U8&>YO.Ln4r>Ik6\"@+Er/^NF#H!i"d$0iiq%H[f6G07b: %T$QTG@4di21\Q2mTG<$Ofi\h$\oEg4=dI!7V:brb?]g_>&^r/aehW-lNXDDE=bO3M>s4;$`/*0TralXNWY*-C`HZ3T@FHam)/g7*S:m9nM5tgnO*&*Nq-Hn<3E)pL">5^qsGS\%Orrbrsa+\_Oqu+c# %6/7?OG6f<3*[pC<`SZ[a)EkB8Q[=$MD3J/*)XRZYs]=*][iOj2"0Of*`)i8+f@m2$?2Uida2uZcd6>Z0D[s8."?!Ng7VC];2+RjAXuaM+h'7Q0]RBdcEPub.b&FZM:!cr/2&<[ %_R>Y%(S7kr+:EKg[i2`GKbojpSoeVI#FTCir"i?BE.iKCBEAHO,ZB;e&?pdYmrZlM1^,=`;TgNQY%GBK7n+=j^B$@KVpt5Mpo<;Y %'[i1^l+0O!P>4M)kJ@tV49[MhYs9<2V\<5g"=+;X]m#AcfJTt:oh2KoHjoiXR,B=pq"k!( %/M:eOj[Rs%K?i:%)TXtHns0Hp2DrKU[!IGH$D(L0ZHW@HOPdVf]O9WD9nur1XNS>O$YHT8#]"ROW&t1:m6g^!=fn4&**W,LAX]]eeGU(daM=pTk9t$3Ic_-rb %%43P==hTe"/[W8QCBEBL;kfh-/@H&kTmb3ZeNCU+Kk*6\Ee8j"-B*kY0IVQs44iX*QB[(MLYJdg8=d2BH/bEqk]m9^XE,Zh:0]HC %UEjlcDqe#?;MUo]ipEP3.l\ri^eY4qOAXuTUF4b:aVOCUkkS7d+!Za(CgCe-eUoXds3ZIi%"`Z87I=FcV$@dA43o+9!dM66d(bQ\ %(AtcRgq`3%L9E@]?>uc$F:dS.Q$QEGmbS`4b8oAi"Ob*I+E+63?P!3hWnt0LO]nFM^e1^MgE<0)SY'.mY8'p/s\/ljYBE!ScD9MZg;QC%Oh4fFMSN%VgIP6>$_5>-i %KQ3TbgEJ4:_"B)k$]/"O[j5go?rW2Q-VSAY(qM+i\K!2Ae]HYqaolm_T?DMQ1[_E$WB,X5, %P?AAbQQKtU'h=.s8GanteHA$+6I[^cmC])a=R_>)?1@UH&hqc]l7ADn1nljGW)E^@#fnR*=b>_kR43/pO[S\SOSeBM#fgh"R8teX %=/ei(#2F:SU+JOghgtCcOe%^T!j(\27Vaj?)l2kiSlu/]0[9.4$>*P>86mFr-,:a7G0NK@Z"iTq8T08P:o"jT4XbR"8dP!l#1gsY %a;uI?\@8SqaX\JF@=3c\V(JN%E0*Zm_pFA#!Y\H6Vc.oWLWpF#hRb)l8m79.?tF5Wnadl"V?qJT]%!pE&S?c_\NM,Zr15\dgjDNZ %bKNfqKKZ9B//0;AI1$rN-$*0-I"J+a*nJ-*m?o)D:bYfDmtCd1pVco$KjD1.FiEAUAUNqKd]Zk#n-rVXmERnDA3sQ:Q?6>-K5X<4 %(%F^u^_Q!1$:%E:i=Qb)"]j?Jc%p?*B\Sq$._Z#`;)&7^Dt(Y'W<-*2QHU=bnCtDB?GdV-]aUXB/Pr5YfQqLD5tE9)RkmFHD9b(h %gL2O7NO#**&K?d=[7a:-:sL)P)uZ"@k#U=,fZ%'!LPP!_1&&pfW8MGFp8U[\Z._A,"q1tmUI[I<;C/C-M?I/M%hhb94X)oT-*%)0 %TN@MA]kh;6KrGGOe4&e?"!b#%p2fX3VN6hPb9Os>TE^+`^pdg@NSX-5jaMZTEeUNHCRKcK6fa`=,RQD*r2VXA,8LBni2$#?$0ouJ %>O\A&Tujj.I2m3[,/V4""N]**@?'9;N1.kNG7l-_?/XOlX2Vm4[L[+5A.RD%(eG:AYH56"]1!pJ\n`OE_N^ENIiWti"6!%#6TTKAZJ$qW?<.dQ!\6f?.kGa"k-?4AL6+!HCJe:sji44u%%Z6!)8ed6p %6`]GooH/K;d?X?4WE3fC!<+kJG%J&4]rYb5.&C.EK"QS\fM92dIYYQFkSH=\Cm;d\TK`2nT'O/fUo0#G/tAmji;B,X+?I9smdmL* %7%bdu1:M0\9P4=j8\l"CKWSf4R:LgPZ&co1%D%i2YQ/i8@raD2Hl`]Y9]a(2?3ZGIF?l %ohk?bCY*S8UQ@E]lYTHR1t-uki3V!iXZ2TbjIouI6-5\^*j)\IUIt\B`ptXm"EBVXV#cMU:[msE!)5k>Oj;fb[kZ!mYZ/]2P0A6Z(UeI?p^>\3+4:]r26%+RDGdb4K(!<;i+[Y*AE4o"lchXY(ajIB/dBPU8aC2;!I %'3Q/Z<6W5H'3siYI$V'0bKjV1rYdhY[_IPlU%PTtIC@]GMgC1j]SW;p]a6+AI-\``9H[WVpTH;H\^lB7V$@14*kZ3f %".<&&-(lPK6lb7PbE-9cmoA9X"baTR7Mo,&\eH)#gNS7A0*:>K8f^u]1pX$O;7rkYPNn_WogQpI(AgbED %0cA\c>Zd_%#@"n0%=V.Uk01fqD[:;Y6[Ds3Ql1"Ag2KfA#MIAEl!N309S81161mT]d)&sZm*OWtA>`6\A"-Gn]MC.\X)52XDT.:L %!>1Cq!q9u-6*2`T2Q4Ea\3ueHX]@;]@Ome@HQZ]KF<88eKh$g$EZPpR9Y:j]S]8F!8U$KFUumUAW.?t(L=1$c;JnQ-fs,^Fnkh7*"@p4.l\[M/h-I2YTRCah+r,Se %s6#;?-$:9%m0G=6-_pg+1\NP1Qh,:V'P6)aoC$^h_X_O?L+2Ro0FF %T^V2bHe3PaBlH/3hri$>3C$3hVoDbl%%]rU"_cd1VHgMDWR]EEgujTGD7#`?;Rnmio'k`BOgYUoe5T3?/5TFO&@(p'k.$d-@?@fW5Opk"@u^`;)aAoq?5R %frY>GW6^MW\$je9#(<9GH4ds[[lmDM:V\#HOB9!Q^^)oYm+:-C"b*`M'aebaJYhP*[l,Z[UbXs_%!2q@5[uLS5ja'uT-*ZCs67E; %chuN3l$86>%NC')ESnt2O4Aj59ojcY+.eIGsjiY#Ar2r"bt*NTe[ZW]TK]='pT4rcF$*MZV6C7d!DfG'/51ZgFiO3W!%Bn %bup)t\SDF0Eps.#!?MZbD;^:.L+pOt>6&Q(6JlPIZCROUc_?M`gNZC08`%7?3k)\Hf+.R::'s8KX&IEk^du*Dnq6`98a7Ek\mdYWRCp*= %8QcMXUWSMVN\KM@_k?`H%Enn#k#L5'cS^h8_EG,E/=AMss$OT=O)S]STuZXLiA4)rH"?C[TAZ=*^HI[FB*65%[Pb_=Pm]VopZg%7 %nuHP@#3@n\@-!!s;jM?B?Ls,T"fXopoI_P(m%eY>qRWjH(`QPr1bXfg1J54efXK(mE9]K31Eq"1GI5&&7rR=V4s(/s]aq>B:-lV[ %i(hQhYie'WcM2_"nUplZ2]P"2pbg1u46`Rr%L+nDi,T4UJHBH1a)7kR=F)$]SO'OpSN+dgE(2Ei#A>*fL^l(34_bbs,.; %4YC2V<jN,?k3s`I5%t>[sAk0]Vn&=I7IN#sfsXg8>inK+KN,k%SQm.9"uWWRMC2QpXM]5578FB@VT4h3qDGV9ff>RXT^M2/a^\Q'c*P[XSi8h %^a`l-j<+!/WYNHL)Zr0cYf"7%oedS6VR$h^-VGKPP&r4T*cf^2n^()Rn1/B[$;4IU-tZBY.[6r!WK,MXj(DQ#*e %1eJrk.@]Do`b)]n$@")/BWDOGMp%V:6uHULHYp9&@h3a%m\!r-"gDK:>g@q.U(3*q6@ha5AKc,g[MTepWq+kf%I.+!6&oe:g]s7; %;Bt[%`R(KbA4NnMH*[u%3EnW$/`OT3]_.+M\AcZGIO;er(@/e&1)\Xg,\pA\@O,+nN2[h?G:li$)aV5_IIRc@q?5dI@C`qMX9$KG %K,fIm0pi##,46%^T;,H64)cHG7c;3JZsuah %C16^31B:D85^@BG8!d=2+4qBZ]cQD62'K/6,J3>cQ9):6bML%1Tt.L=O$3JG\HIq(a0X>A6i8qFp#QPbpJbLP=)oS.$Wu%rC/d8F %n6T,SaclgB`Dr2SEbM0uR0?hC_7PtI%Pr\C!4.N1WT,="f9C"c'V\V0[jJ.Ub6?jKI=@+56u?FlJt"*?>gLNs?[@mb$T9RclAB]q %[DM813AOIW#)'7IjDhOHXDH(#.1SBb.dJ4t/)28t82rli"7-Gj\p\5@q'$U,\su_GB">ucbk7.PL+$b$W[lpZE>,^XCKUNe;@?`4 %4-D6*_mr3.WqcP4Y@umYYRjq_ouZ.4SX3A%)-n,$4Y:FQ84S'6I3jPO9j(fJoM]'M&('Ga,o,eLYj%:c.R9ruX?I[Jf$'+_URs1@ %^fmKDCEV1e6&ksqmb0fM38+6kNed#d@CCO?:;X:g9pHK##Qh-&BWEl*?^N>%HGZUfCQHHYBLoqBp;AM)$*\%nP]QF9o( %#BM*WdS($dD6]L%7;i47,l!?[UQGV_k@H8T`%C?Zlq%Cn9l`J;o.Z!KK,1nC+jo`=/pE4_Z>9%qZ %A`P@J+=-6jGO:)Ai-]>8l* %`/L%gQr--07196Z/5bJE]a%KMSS_(8K==lC]KHAZE'A1_N8c"(2J2EO7,-`]2FN,AYf_>s'=k2PkV %36ZI\1K5nJX^c$GK#o51"`s3]hi#9,68RI7V-M2Oh0@.iDo(.;i'7kg0=rbDG"l2!Nm(*I6j9[VcB1VJK8UN6W#Fkkr'BRa@$(cW %+J'"dZM8,02'6o@A&H0![TgcaW4m[0@F:I27k-GQ#0Dfm9?tGB?#@S$8c+gdGBGA^oaqAWZ3-M"Zt3!YG&%2. %<9#utF87k.J]&EIJS=FI"3G-qQ(K..>LWZG>cO&lhP_M1VK=RaMLa,+:@_tRQ.sQ5fV;%r3NAUO)l\nN\dm/s7@)T7EKaT4;W.ZO %3?5(]D>^Djm?p@G41CWfcau+-2065_qiA+IF)lg,rI&$A^J#pqn<>c(A_U*:FmORUDDD)[!P%p84_EeM)`L8[QTb'u2XpVH4G'OV %.sOac==[:PoDmJGm6TUN]8j"4.Mh[a!nO+HIX[5c.fq[aU*A23[W?k`FDMh6cYs0Zc>SW!$8hgP4UXI>@CYQD&>e;(+g)(^*?]:8 %$C8[8rG]7qhclqiQHD\95]s,58eO(Majc2BC%nIF^s[OYPIEHX\6;@7g!0t)/m6rG(*:7-[utk!=N1e/BeJI9pfT[EDN_SH["D+> %hp>:ZMMS;YSTAEcaS=g]iQ]El?.MAFFV8:gDZfU$;pa\U"Y^^?b=K[1Fc.g???@t6XLOG,OiePnKo<]CgEt6U#ENoB*iL^Z0_Wa9 %8*e55<&s,8V+1D!B?1/-,[fhU1W^0DfBM33`'RQ#Rs6EH3Uk7kG6:,XCWsX3O(tl%kMVpl7jdWU`Z?OaC(La+p$o\$FK7Q]dd1E> %KQ0TX+J&5ZKC6o!qF/pEPEo+S4(6]>L$)jg!7_r+8DQlAbc!!/\Jh:+W?*>DVtP8"j>H1h*CMhnQe_]VBijGOoG!7%>Km.\4?_B/CV#fksu%giLg]ftf9=jH[Y\n.^SBRI/V\n:;-b]gF0_"/.QoiLohF77,Ru&^hP4RcV2bt;; %=LVRfgl2ZTmqEIiB0r(MC6$Gn'YoaR`Gh4!);C1E+6M>eYgZel:3b&p/_e;GD+6*\bP5HW'Di-q&N"aqf!T$fl'a^9),=hT)1K?] %/Fk7OD0*RqVurQ,gH6h(Q#m.uhn7Q'4sGKp_>=5MmYSN((2B9R=GujbFs^k[Md&I8c?rBP&OQ^,p^Hor!kP5ChN\-d+Kl6*K(HR6i"khs'JLc=G%38hS,YO$;1im#YQ_e!_l1mni4L]gG$Ro3aOm3mEZT4D(n8f.S-cV) %CjFIeW1"MobnM"m0fI-f+_>I,heM347/k%BloR:Z/!2X=])C)j2iL&n&[n^Zj'ND/+`"1u.lY8&VJ4kQ@2`OX^+bV6P9Z:OI4"4G %7Si:9;:V3-169^7[Q$r9K_^97g=mh.:K@O/kLZpPAC>o/7ZGXqfn66bk4"@\eWW\sF+q4DG]J+CVRN6?TuY.e`%V+:;jm=-GG@X=_PS=1:l=&8o4gUQmpM/I.R2e3F1!!Y@qqp>U+YBoV %Qj0/V+h9&4l;:@._!0naEtpU*\nsnA/7!@)L99N+3.*mtms;B),DRCM`o^Iks3)c6i,t2/g-H(u63/e2BR%i3S1Z@\e94gVb[R39 %#!;?GieasC'#6O%*luN6+aub.QQ0h2fj_mpa7^^B!jHH;^lF2P)*;b7NsdD#HY(f6$VsbeJ$TC,6It3!p6=BkD%Y"FHJlu9!nc6g %_O$Z/TBXl3>[:skAF/&bUl$^gcb=KAJcG--YO*,Tq"aH#cM>HU]>Gb:R+Lm-cQ!_4T.3J$!m..8EnX^b\]mi@XTD& %j/-DNk']aULnf@GL&06#c*,p6Z2%/hn*+q-ke!/LYW[L%55jH^;3\e)'*u\^j.YM7GECHP2gMQ8jN3[>'^1ACaa&hgYiX#B'O=gZ/$W^!F, %)dpVN\:k!;)DfP0ej]M;T3#L:WmcQ:`ub$K4u[hID4*cVFSu\C^1l72#lb@T*Xi,*(?XbW,-sYJhR""r1?+UQALSKE[gCEDB\4DoSR`-tmkPOQ4Pm5iY"_?CX""S9,"tHoYND^5()5bMgJ4(HU %H#7:N#BbOJn,`dXTP7ef?.@`Vg22-?PeUN7)@76okhDtQ_=I?DWE*@uNlh-pZ3@7\9"$u&mFhUl*%!%57]:E_=oJ;uh@C`DQtp!F %%Q![PFc/BoFTc(Z5Xi*f!O7+Fh8i9NEfi434^qhh&?pKfO8Q\Hdmqk=e"GaF9m:&2MEM@KhOQc8K9gs,0Z4'AIIr)mJ;7+K)ot`) %_7kX]&2=]XN-ajJjc#2bF*N@9m5k!%K=`O36Ji2;N,7SuRMXo5>Ur;"#r>hKncMk]Hfk`.63qRM`q %\uGZ[cmZ(d*])mc(aWL3J:ii^*!NQRAO=PQ;$-@]bDNK0hf^FeNZ,#.%BO-f"W4lnjPU9$Kl)0.M+YMAg^@>r61&V([]++"P-T8@ %(,8\;U=PY,H%otq[ai6`rPj@f4-;0T0/82)mA0/Y/agTmpr!a/R?koE(^E0Z42!5YS$^7SRI_0t:26R%#Sh<]TTi:HhSp2;R.[C9 %*G2ehFe+Y=?5$Y>oAc9(0kPp<]78n(H>n9r+2`DqIkRSr1a*!Z9.NO5_!q %BVYllXH]f]%VClia$=)Gm@)d[R(R&CpD:brTJ;$39?L4oQip0"i`Lp_&[]S/][OV8oljG->o9<,iS;XTqtb!"hcIfHB\RW+GTOtM %X9,[4aSB6?>eC,u&jGZ;L.Yce__a?5e#V-ZW1jO&ei$>WKd!nfGQ4Aegn'&jar^eOpSA^\.fL*q58@O9?15S736ZQVDH:,*/q2n %s6%GpgOK'5_7.WGrY$t%RM88e;qnX1RFH#TNsu2]d4cUR]:[/g7NdA>^>a*+l4+>QNN2ORhIe.166!=KA&_WFY&q%a(oPQ1es;W7 %_X$=QOl_;J@",XAj?::7_5m,-B-c^n=`2kki5gVa,lblA5crd6%i2H)fbbndHHH(l8aWZa06>(KI,Xfse^E38hVdYb^*)J6AhBlP %?%c1+"#-QVUh]GGPmQW-%u57,B]BJ:Z:=(WeM2a[]FHQ3Q*n(IX]4)&2>>KGUZ`V=iQ-e9hTYmg>Ekt!Xc#W2a\C'f?5S!-@ioOIFl[3%G`0` %EZh4Xqa["-g4El">,!M-#\I9>2ZcU4PZP*:hfUmXqYkUF6p/d$*a*tMUH2Rs)/55k+!2^Y]k,5&=P]u59chm"/u=$gakQHDn01"q %AL^7^'U73DC,0YLGY9'\@c6OLC8oj+!ahZVj]TiIio+,qAQT(G!h!d=9N,^&SYUCn0%`H%:gc%5r!1ree:7LCgP<&C$.aH3l&W$[ %jo*qWV40OU#W^aXlZaB^XS)_M%D`[W)J=k#f'V#BYtaRV6)R+[!1Z03N)Ti*`ZE=tSJ+lP11K6&Mf4Z5?'hPd%N@rYcTt+)7ukbJ@LZVQ1g( %"t5Y5%_=H90_V)b2#-ak*s[#ndiV_ccpW#Dlu_g&#-q_=a,Z_\Nr@_(l3cHd"%6#(r&.:uGd!t2'jc_/4OgK?n5X?$`>ZgYH\ks6 %HB>4%C2J9OB]TlI('FE0_1_\[9/u1Y!XrnL+A5?3_\u"a\r;4tnBNL,!meMQaKF44LmMpG;&+s<4AS9_WqqPnfV2(d+Y)I@`1Qc, %.2CW4?QUNZ9)c"[lY(KKl8!WXl*%='J<+Ac1('ij-E35ET4!0IqWPMjOM]T$2NVG'"QVk6*+%b+m*hf.`\c5n!VF%B!:d,92o=tS %q$u2d)H.YJ%m6VQ;$Lep0Kg9VLH4,D(:\r8Uk-X)-50Wc>`PM03PG;?jHn9Gc0N5N&4=W@;b\oE=ShI[QnJTq!N8*6G"^,oDnmpT %WHXHff?]N/==27=N)R@'FRN=5Lo)S,"2kMFi2P8E[j]EuY>>DskE[upW*d%.jc%%+LYE)NX\T?]#oJEtA5CqfpB&(#Ii(-!I#-,c@%#iB9Xr,]Pqa7uR %PU5/S/c:%5,\.[a%HR*]Nll?IOp##>'i;^m)9LW89beMaOfk0PBe(o7_u9C9IV29:%\F0F2uPCs;(?D_?6YpQjJ:]:OLDR]e`Bdh %]gh?qdh6lYT-%QY;Bj3+DBRP]?A_C:m!ZYn3pn8AW?.Gij@GbUp4.,JJR8&XV;1B:Y/BW_IKtaAT`eVcs&6oTYq2+WV[:FS6JEuW %4]&B"\Gf&blP;u+()%0%H[Aa0=)L&>Uj]5-D,>I+>7ccXbs9354JGO)mQ+AZ&o\5B@S@ar9Dr8bDEUYXS[^aKS!O"eRNT+NjS'%Hpd/(>?L25@6BJYTGl)\huSY-IQ>LhJRqp!:R %V>'YgEcRq"=f23;[e@0-.A1Ml'QA_"J%22QM7^3r#1fs<"_,JW.8%PN[uHOYIG$pKE4]\jqJaV^PlU=;%N>#&oX_UPA[lNq?q2,? %FS`ZbbR\%"9 %+6n#bOW`b@'(rBDkX6d5E#BK*+WrYeMI@1sZ'Zo4BZAA^:9q8$@Z,r3s21UTm];+>2iL15:(Ns#1@YA5eh8e^'Ju4[e6;7uiABEB?BMsVR9`a#lg?R[N072(3Y/0qV\QtLmGqW<@2$_kde5q`eeQ6E,6;1O8LP&]JmTU%0*VHUsZ"(/Ob%GN55IoKF)k7kI %K"b+^oeJAGR9O%"Jc&tl.VlXTrR%qDXfN#^_5n9G`sU,(3.QU>:i0dcA]W>`B2os\e[!K3GNYtgrnS/kkGKBOKr%YKpu9Jo<+5$k %;e-^SFf"k]#2N*Wq>$bcLpDZSHAPOuBV_d_.85Y[#\[VF:r:>V!g4/m)g1I`&$i9FJ(P^>)/%L&M@J^MY^rN';DfG?&.DdfWg,'3 %#*=q!0?mSJ=UWNUU4RYj<)B`TWAU`6Sj$nskYr;=G&u#nRArIX,"VP't@5&f=D#:=sTF+D,!9em1lNTsf9#Se:Q%SJ@rhA0Z=J$drH5^l/qF(:1XDbe4Y %73^h(!_BNlR2T-%S/*#eCj%3UuH5V=eHeKcD85UlVuMijWo!g$R'if>>*ku%m>*iNTqB($+C8T&"cGfG.5ai`5.&@>MC"J-:]qg:b5]d%a[)Lio@YU^*9TR*i5WiFs:D4Cd"3Xq%Y94?S:]qJ7?CE3"$VL=A %kbepDE.'Gi18i]KWC'Rm+tXcs+XA6l)XP1]fB'**Q@G$K&nU$Q8:,@.`72`QKK*Q!e^7XU3I"b*mD5 %<0Na*UpB!j3m&@7T9r=+#4bUkBl&I);idF560XS:Z/&DZrZ67)<\^/kc_Qj"S1RGQ\S\\Y@>AR3/hE2r)Tl]j8[Mp>O+Wj#D[[qt %DN4cbAc2HCgZDA*RjJ:Ip,UYaX0(T`\J_k;CR7%Z2=Kh] %'!q[l"^:?J9`QisdKA]lj"!C4r&(#1U=#;;n."Sc39=&sYVJd/Pp:Nj*[MbjaVBJl:$nt7;B[\fN,&?!h+gQSTjbh5\_@U?&&qPH %mU*m#ceke,cp\lX1[TfTSaYM2C/(B,ZKVXd$k@.W:u3LsL>)GC;ni2iM<*r!o4:P6A6B/)"4Y-ocL4r;Goi1_:s3eLW;kZ79Bcb+N?cXq]ephgC24(2L)12\nr$[VnIBqir*>D1@"nI:29Ih#>HAQ:gYUYJnkXgbm#AVc]3fWV^?J5O:&fpQMY/\d2ln_:n\[d<;'-J=%mi]#h?k=J&c_Oa_eu %fLXh+:5\Si-?DUAR;<&<6^RqbCWUmJ4'Q&mf'@X-,9T/"g/8p^.Q?2C]"6@oY'YoueTH4P!n]F0hgH`30usuaq?gAM;5Ile7eYaR %!`77+n0fDd6!JZN61c:FAL\`l.U?)*L(N!NR[Fl*e@&NZZ:cL"DQsEMb:B58Aq,O1g5_N'TWe5^5ThWcFCDUB_@T$@u5eO^ejOfWSTH3/qnCU$V5%Donq051?)KgB\=G<4f`!:G6nJ1EgqX!V^rCbc'5%ibq4&m=$;Gd0NGUs %UKsbsR`6+[=kIi`"#ah>'Um$bm8<]g[I[kEf,%.oS.:CgI>t3CA`U1^"^XEN.$6ME\m\3>R'1':WjPGbq1(K3e;0sWK!@nZ,u+Tn %DDMBjK;gZY(gpak.9&?Pc*mJ)3nW[#l#;*id_iQOV7o)f8i=oX#>["ZIdk"-kVNq7(7hXSEU%CL5Yt9?pi777Me:Z.j@Yku%WCul %BhePOW@N*&HIXEoD<:Jpi5lA6Jk%j)9=*C/0&*SPqjE,8ASp+[6T&YKVnHY+Zb0/g7S\TQ'u_hWB6@4RuO %U>$EoWC19_T``W=1Cc.aq];!&,KN3'@T3C'"a;(OIh,4.k40nf7rfB3kE,g0iK-H8QjQCIjhAcu$kU-KJeV%#^-_\.T,n9s.X\7E %%+;hm!\ur)?_Q8i!#b8+a*)B*i/.S1%GZuEJd+hbp,64B*JA4!+Q9"4J_.5T7XC&l2;lQ*TmZjHFCV'miC='>KUNYdqX\KRt-7 %OO7,M6pf*"SlOMnP@k_@nacqXekLgPBI#_]_0FjpU6E$N*;3Vu\"+Z5^^q[Z#m!^CW^B5/QBe_+A_(a<`h6>?;?O`M5`e-:>DYWr %WT>gqpO[_ap!JOa64muEOiGqcDW`jW3'>R\NqB`Z4i$:0R`k.6R,&mUS,@\DU]YEfT* %n@hSHaX\kC1.+AMKpM'+qH@-1#-KFfZ+Y\^\fEP-i^0M]dPD\a(2a"DRSG2kK`HhC@:\;FSZkj4"0I44Ooi(' %;t?CXUC+3#";r%VGZtY_LXS:qFP.IHKP@lAmPrr??bo?LF"o;4I8,Yc>?HK\Q=97+h8,_b/u(8!]a%ckOMY^!86]K@Rja)'CDn7& %?fMI_o:oI@e@:WA::\3=H**Q@dV>Ygo`F4Z?A8cC2=GLm/YcALBuF]:rA%*S[^9nICs2*Mo!2K-(c5s5T<(je#QNa5=+3L\4 %g=!&H7Yq1d&KBNo]ZpCOLNXKLXu6;pmnmR^JKd@q,muiYlUh]1/gF9;#h3bLg,6,E7*r9<4>7_gM(5N=8BJtr- %c0S$j0!QE+AA#u!*7J9!i\9A&/*cB)fEbTV)(UieH?3PcJ_D&D)!'7l&8Cq+f&oPAgIsd:i9i`n^*.jmC.Lqtosuoaos+R3aa2^- %VC[)uk%E+W-5N*I3+;t="'qD(ouUcoI08&jEFU$neGMoX?WdlR!F$)Wb*Ygi5oMhRku$#+XbO:V\O\#gWg5I-!aL!*V:rZ,0[:*` %dO7p`B':q3)&/TDTWWnb.G!n/a0a=J9Y5ISeJ4>,+[)",gg-6=9X`>Y:,8r_D4_%03gK<6Y&9%23)b87;,'Y9BRU@>(6Srrr]p"8 %Q0XQ-Hmr&^:G(Cm"fGj6_=W;FDV+gJrm7+eF"Cq*6iji].f0#NZ@D?tQ\Pfpj$D0[FnO/4d=H2Y=;25u&Pqs1#"XM#D; %:;t+ei,VP:s!5_;+\7W/*NOne`,/>3=NKR*k#gYE=EN+@@%P]@KCW#qj'(OkRC-@Y76o\.d4O4i$O4e_hR+;);Nn!TgU;%:iDI8) %_ZJ3>Y-Ds];0[$DPs%SU5ZUVD,E"#o3f$ZZ7P8qC+c'u9eBhgjIq]G'4B#IEj#:)45<\4ZhbEGJe:H/=0c@n54_DrY7t\1O-;5c4hal94W\]_CAn=_Ch`L:rP* %#.50o0ib/S4sJaATIA7rbJQs;)\@Fo/rG`Plf's(Qc7ak2qI5YaL9jIEDm%R_'!AfcU"5&D3#%haf%LW['CiWR[(Mf7!)\ar)OAo %`$q?eM+]H.l7:nf#b;Q0%F7uK;F_"#`8W;4ffS!EP@*,IFn0EfQr(K:/tVe92Pq&W\f@eRV-RX+B]>+PaWbBBbf1X %nblltL%iE4fg4ENm^j*Y,nA,"PKPjp*d!KlI4h;l;$-Ns]8YVCYd4BIlVT:m'0"CMKH=_JA=+[HWs2n^_SEjhT,qUgRpUdF%^U(j %h>V]0q17,6YWj1H^=XoP1+rIChMb?"]q\t(01"-WeEk(U^9h,cZ.dhIp`(U/TLU@ZbMS>9T#D)RKq^15llJp+W# %)!o4/e`<-8eSLC#?NhG$dBhL89L?5jbo]$6%5@.JKudFtlY7Vd8us'W[k2BosA0FV4nFt7+GmGQ)YUh)_GE%PQVA'#-kEKRWD %)0h7DO1)X;X4&Aa8@8X!2ddIm'-)AGg(lZC]L`5(E_6?YpE50ap0aNdqrJPt5W>J[VtR'Y\%piTBcHSahguXtZJQ!3\5_!h"kWRM$Hlj$[dgq*_'t3pCg]gf8^$6M%r#6B\1^[Hh6e3/PsD7%_+FN, %>L+AVVseYq3`9ag[,dIq=Gb\a_i;ep_o'BpR(G;ds)=VWgs:Xl^agoP1Zk%fLrmsNoW$5E0@*GZ!M3X&Y34@2%'jG/!"GNe42;N: %SR4KF%G\G)r0;8V-eo7+>8%?11WZ>SH44p;3Nc#=&hP<23`L"\$Drh33_sIRhghk$;3reXA%cE$R"0-/'-+[m/M8,srO)"YtY3aU#fA[B@G[XLb??JZ\YNq*LmFSq=([Qh1+m.DBV:hR %`\B=%hKHUqlj^IY.<0`h19ei$p]biX&P]$VK@72>`3]e6`.sTaZh9ucQ8Uc44.&n\/;mPm97rJ]@5iqDlY&k$nD73)Qu=OFC5"7? %T$2-;6&f]K"l!uh&%(_(YJ?9K.f^pR;*Z!GAL`V[[jF7A`ZQ=*8R2bS):_9)HuGEajnft>g5lYR0i%*OS.FY#%cZj3fa?8/RG3/' %oO"2SiKs1Mr6Ts`Jg!B(5i8p`cU5)Cpk^%SF)19/#Uk.b)V/N[=#dsg$4rY?4Sk75_C.$D>(6`_3rY %)6V\#6/h0I"mejj8h.X&O0NQ)U^WXXh;Y3ZADQ2!39jo]]T-KIfoBg_AacZ@aF70N6;.%1fs>VMlFR$V'^B61EX^g5/MN53(#\"0 %Lh.\.B2D@SHJ"p2f6lCm^p:O1!D)V)YE5W+RE;-jN,)9pEc,&W`]d)V_"\'nj3qPqY``+U55SX/rg^]4l*E1%>$BL;V\jVI(kQD%Y"*!&\oJ5bNE3/5W'045I/o+mpLYE'8bL5^^YC4X\]^\WcfpYL?$\m'mjW#rC%;NW!blXN?/l[E\Sk>n.,F\_-l>3Abrnc_9u73511g,P!5LQ6hU(77Q$k= %RQ?)0/<"MCfU4JY%>OWSdE9agHWbfp/aQU"3OSh"X=jfFje'X!=&RG/!quR42n3e%(?fi1[0O8!'@b",rB?XsXJ-%g^nV*0)na*: %&d'3%P;PgKS%1.iEa:2#^?5anfO6KT$06R(q!mqP\R.$GdILi"cIE_gH.IVUdD4kR!:C_)EEQ3("d@RCgXcN[;1$ZA0!7"-`>$/& %%\E%"Jp/'R"FI!N<+a+,@Df7BPFE_5,JaojT7u&h^8]`u)jk2!%GpMeH"fpU4P:M+([7q&7ff`/JTPfhY^G\0Y.+?9G`EJJ0q^Fd %B%[h=o5[QtE9]851n1>CC8BEcMrD>2@sEY6F:\'NqVn<$I>SnbO_X^_/AuDp9$/nI[XVoq^2_Y`IHTT3Kr5Zh`9A="gLO0-(>AMZ9[>-L4:-_Id$]X0I\+SIo"\f"$_a.kliT2TpOKT&]<u"?8B[N\U=*R4KSfO,8Xc!oQ#CI^p@*c=07&.FK;/n^akq6lMOU@]r/QQ/$)YNMZ;8cAN9ao0_$"kJCAQJ4kM!r6=%sheeu!o %lPB8c/21?J[JT7ggW>&XYs^0:dt/kc4^pc6R]S`MiQXLE#[L/n]B;*P.,A=\dE-;WkfX>X0gNi4mnVmT\msOhuPq`Nfka"q`QOFL@Ml^p,9!6=$8jIA>Z!i2^b\E\8+%p1,/' %@306*ACk7TiF1:mC/QkMCco]TM5G%'Hd$VjcU!PhVPS:Fa-Ps`-)=@\;^GJrlf#ri._fj^d(*CK]S%\&d&a069jSaZi:RF7hQi!H %(adYIT$p4t!;Z5-<8do,TSV+I')]\/YB/T.1r)oq^JuG`Le)I-W/?h.i#bHk<+A+n?j;3@E%S&_`$Y6c!S>$p %;5^-X2l3.hH+O4p/=.1Wo+Ih-L8:LtfX%*&$d\_MXtX8Z)7oG*,9%[i[*oQj/%6T-aM&\6o$c@U'sBlU=[:PZk@1HrPSdXrb>75Cai'IrJ6XT;gMUcbVj3unnHmc0`qt&p(NL#lE[kYDTgkQtA%*mMqDsRH1cGgs1s&Lj6Hog"_'TT_M %pD@JB+0j6Z^_!+%:Ue4Rg5i%.[7d8n4:,CR %S9UE^l4;3PU17Z5X!PE<1KUPs-9u#% %68m2#4beAS6U9pF&m/$`f-1i)\It>$%>_?Fi39=u+APW?M%?UPCe;_G?BR!J92L5+'_DC'EOf,u\3.MMZt`R)LFh=T)8l"Nes-,< %ekGN3lj'SSc7-8hJ-,]+4K(Y7pf.].Giq_`-?[5!Z3irb.42o!2],%94BEjY#I0Cr#u]Pb6]Q?cbV %V%?d8cNc.AnspRjg(j]kg:3CJ/<'kU%8JGNJVqXiif^_QTlDXt7hWhg8eU7tW=,e*Ma8mg*6Csn*mF4@3\+*db-ikVaa(cFCn38m %9L?_WGOP)(')*TV;HL"81*D7doPoI<+P^p_?]e-.+e;=>@c[L@OYH&st9 %&ulAfaZi53r<45tYY+j*_nC#.Q/On3-=1,12RmUO(bOeN3'=4qh$mrrW4YG!_ee1#]ZRsh]hK07YZg9B'WUI/KDhc#Mu"/,-khHd %\>E[dJg?29ALOtMA8N(/[5P-e5a,ZPd[(]PX%7_i+GN7T,dk$k`=(e"4=j((oMKH?dtQE-.[X*ACd#iK:1#0Rbq9!cVUcRP0Gq;] %.,?AP>K4)$^=rgq>7I\.b)1^t]X"Z"Q]liY@"m`#\L>t+4+FMeaa!rLY>B1Ug^<0Z?\]&MK?rLE.gE9Ao\66g)\*`hD2/4MG2%27 %V*S+B6s=G]qY+(#8.U>[Te.4i>#`lPVUjbJJRFO>W>P3DQloI]flaWW;7O'tXaSb;kRfU7(n9@7=7^8\F1M@\ %jj?,U=kX(i\m`]aSp>hBStGiXnXS-!LA>%]dh4AM2fk*'Cc?k-K0>EDVHN\F=23o:]Yco*J?q=b$s[X)s7+N\`>igSZ:qI[ABNp[ %"<]2UR30[:V"9i8`LD:K'C5`-COL;lG59Bp %cAYkh'/?KrgbraiDDq/g`,@3W2NkB1Zo[H3sd9a+`-*r%JT]r\:F1mjHKJRK)9Ym7uZXNru:BHPq+N\,4" %[NZ7\cJ]66cY;Jqg"Q-#QRU$&:epG(e?f/IgQ&K,>W;6JP6Y:7/?1n9i>3L2DgQJo5k;(d^Ma7'69BF)W9e]IPkJ6R2lI",e4JjN %osNs7[f+O5Z)tnET'fmh-*ruR[^Y$S4@THEOe"1r_(98H7Q&Q$rO.1A)p,+q-rZO>$OI`]_5q"<&/R;^fNYfaD?B+gcVY120G97.Gh %m3$(D]huR_>2Q]2odG%17u]i`c'#V>K[;j\\eM:PgH@'F/HbT@aPkaW\N[IY;DHO22_=2Zp,O<_*M"BLoYK\M)#C#po[t9jg4'"a %)4pCIn,6XL'.U4lr;&E&Nb.uRqf>`lrDPlkphe_th]F0+S;2C]J>]_HWqO!l+>iOCG=Q>hfTopI8[C0Ia#Y]2%H^Q*@&^a)@9aDJ %*=Hi>Q^;8-Ye4i[Gig^8%(fXJejX3=f\;kA],`ErhgoRo&q588BR;bcGkT_(dr(kj"o3U[-;FAbrH4`,E8&@9T\st?7fC-KgtQ0W %$U7M`?i-G0cc)C\K7(jOmGg4\76/@8sT@4qU6>^`@Xdg6;k@!4N]RalR-4;Y)I+pVUa($g,drh'5if5L]D"8uU,qt#=;p-52LZi<2'?Cr25I.1l])o\HBLP6+S?%Z>(5p")7 %$R'cS0O.la9*sb^Xi/I0!Bu:tcLi0@f"gUeoQn)Y@nRDB$F)f88Ae\N(bL-d+_+9_4"_0\@AJn3.=VWmNdQs!T)4@i#7=IgGZnUV %h_;i_(@8R`C^n'c$@>9nLYEnVKO<@JmbuLIqC+C"T^rXtLP'r*B7W0^^a)l'pA7SM/O^W_f#+r.M.hBHpEm+*fcQ4f[m?M`':)/F %n=DX'`U'0@[[".XR``s$>;pWZre$'o!>"t&2KRJZboCUuTR^[1k6aQbgiH2]Q4&,^)qIWV"C"cWR:am'K*obEduLK7K=I3$IE)XH %%CG[A'X=,VC5Y;m(R3Jq[=Z/#O!3NMlMY=c%su0+MAZ&KTCmtT`IQf8lb.W%-dBT)h&S![E?Zdacas:JeB(BI %gNVRqfiN%js]Od!G;snHJ=^%T/WSHCpG\dL3[MpCUZ$Zs1'-9"(mWH]8[g@hijntSJ/qnauCZ: %iPp[<^qO:95,[&Wo\9/or4FKc*]G0j5VfC:oh?r`HtRiD]khrPAZg+1>&P3f0:WZ@nT;#.Z89R2SbX>5/5b`PFWH]0E?pis_9ajZ %mX(.*8J09AF,[9KeK45467mP-l$FW\\narBN$=B-_Lfc)Ho$B,':q]ET"?05d)2K)S8ZhD+l^,0d^SJ=6`MV)]M33jRf`I6>rG*P %TT66@OLrfT)jZCMZZHu;4qPBD7SC<5,2uI!fQs9-^(P_#j1J\`A.;JSgDo64fuS(eQUA\MH-U=aU5/+"'#CZsk:&L)I.*K##2c;/ %du9%I]'*oL48Q0\rg5@VaF=:XUA*UJBs3du!KRmLh1I\rfA,'9bL9,e7,/,FWG\)hQX*jp9/H`t=Y)7e^U-oDP(dHpUi@SibpKS\F %Da_6Hj7msb&;k^D%sc>So#aN*&cY+(nrDqPo;MHU\*PP^Gue62=X`8K#+%8R0*<^5"h`rXH-8!"E70u$[gY05\8GVtE@>9h]'(RW %NW>D+fVYRN8HFdC`!_gj,so-#.f&8[GApt:]@%'O:l0G?-?PdpEfs6T_8qSDLa"Z2UJlKDZ[egY/k2YrD-HD(idB^0U540-q$4EV %%:%5M,&W:JpDjdne'%c1bjl"?4!TZ@%Er@(^)l>XJ&YtbL8D.YD:(LN;fcj&:3UhgS234f8B*(4(pdIq-qk+W.`%HJ&c.^gZImjn@ONFGLEa0Z,1^D`%_/PipU=R1<$CKGZ]o\,fFc<#$lLQ0/0 %VgB-bi5<=RXR\^QI90\*)j8DZ&c!'jqHDSK3A]%DQ)Q?:jCQ$h7)scmIWM&7`qI!>=I:de^/0>X`8;2^^82T>*^GTB;#p-3;bf;. %(0bUWddQ^]0qmb:gd9mZLbcR(,8k/Q5mWOg2SBPVCpnC$W]_T*>f;na;U?2(2F!C_B7(u]<769DK*UGZu`[D-8\G7%A&2^Wi0=@s=mEg^-_YC4"i@Au:M5G,IDbI_m_Flc,Y[cRoX2[,Y7_S1,uE*7l>Xf$4.T?34d"o9Mna,+*Lm?L%)X`1&5? %DsI;6W=9Oc^8WCM[cYq0I3ORDVOGoe\jo,I,`6i4*23aLJ+UpA&6l#Bh8p0#C2toB-_PAd.=D4KI=?"PX>SW)Ak$KmM(R0ZF#"n %h,,YtrS`L7"Fp4BncrBR;$_"rZTe=Z9;k#-nr1MlQgS"D@$F/ckMZ2XpC:*QADk%SaSrEH@B88K&%:/6>Uu=#G@]Pp$IQ0t0Of0q %CQ'#:-TL#b3D^0%1#(!*3*K@.>E5)d^*^cdNnH5>$/qVdfqUSHo)raK

&W<&YZX./O]f+0]F(]:`YGWg%D#pdu5rXpjLh/^mgJ@bJT/#-RHB,.2>3`gpE,+^:b<$s>_'Z5e( %mW[M_@c4SOm(+k;Msq78h&(V'+/&&n%fdgbHN;[X'_#$h>d2:Uf?Bg#2q\dl8Up+jlk;rBQ@j;91tD"6%MHN&2,-c&M`b5Jk_@kHl%[HrK6t/a]uM0`iECN^eU6SY'1>fH=NqRc,4qUU6.u$#J._C?i#gW5'H6uRC<.;QT=hTp$3EKmf)YP=))P6XB%G.)d35hBHR@VO#oc@"!1PaTu68g&j4oU]cc3*%6ImI^(E+8JQ'J$V:YDN2UiqMsp %CVE?=FD'o&a9i?G$bS-Yc7t$Q6nJ'@&aQmf`d+aA(r.YIRr4K%o12-l$$0b*OI`<[.NDUSuaH`6bk=oi0FD[To1GD!.Z0<@;rX-90>3^7MWi$Jie>7!aaTe8HM#jR0I9%P!B[g;FVPfY,W\4 %qY+g>/Jh##,0%[>q.ArHrqk#YVS.ZsO=Y^b&/e\2(7UNj'Q>f(pa*I+)i]nHAnXk)jX736&X$(@dG!2h^F %bVSqH2o$^Pb!Fpe`n %^V.^8i!#5ts7=JWAmUnf(`CZ<7l77g'&i@i._cC5(j+)hOg6o_0EIooIYSFlJ2K7GRP1g8qB(aZi8fd/a5WA9XA3D5:_/?^i+@mW %>;t._UXni.(:,c2eQYEZGbNi3E/@A"AGITK@J?LeF4gG6[On<:3Yp"ul#[Za(n[t$`?7`!pc;jH0TV29_J^-Y8Er;I`5Fni=cUH0 %+j5V))'o?BXGFgm`!/NM:$]-%'Ppp-^73'!Tq]_)3Y#s$BFos+PYL@H_30pa#)eU*7n1Fa(cYJK7n<&['^:R- %:2o@V2!9>ZUr6\7_OHljfOBDuR:l43qPZjW66ETKht(IhI!!J'P_mHnp"&(J\33Xa"R%sY!BOYaKC_"#52tGCkT4UuVL#c>fKa\+ %rF&2-c-5-L-kSFnq$0#9\@'$QcX7*QM#)bYL7o?Q,a8C*S[JZ(;>"Cha^59f,6OR-9IHGghYOA&ri]XW\ek,EE):?_rS8c-E<;QE7YOF]*83#KkYG;Hf*+#]3Y*Ci827aDRIdK\!k/2?ADs'RQ85>10_DcMm7 %i=tj%05!&P_K'MOS=FH;)l)c_&,Qig[,lh\nUP7MWSJk0EYXT3C*d>LU2p,sk-j"a.p&rb4WpV[?-[%i*^rF&;t?G!OmF+H\t*M_ %*0fad;1XQUI`?(u*.C-D%njj]O9dPU8-BLH^tT:>'<1rcP""PnrdiWQ"2iDl?t=gJ\?6YT4o)0Cbabu6WlmO[\D'8-L'8EN'&L#= %>RM,["6YUb0ALOOi\kDUI;dtPq,:)nKuil?rS!+Y%Nmr7+AB(&js`f>!`3[-jKp4r,bfH4^1#H*2c!r(74D1QOoYct5A86HQL %'d`nL_gQNK"Dr98&eku>47!Q\1Uc.*/%MseGAl^gHgtpC@4\aLBj@dbr!m=1&b53sgOpC5ipS/pEFP6)D3+[aY"!VVefT@N@hR>m %"eRr%M:Vr&flJEBYpTAt+@*=FR5fc9S5[&Sg0T?FG@g,e]fs)_Sp/$R!Z*D/#KN7NM/&J:(L_]kcIpa6p7=PS5 %CION\PLa2>So4";df)qn-&0(?L^@R`hY9/%g=244j+I#qN3j^d<3GKnY66YA2\[6:2>&)eqr0 %bJI`\L_tTV*Q]91FH)#!p5#i193B_mLN2+4JED9qQBm==(..TU+Uuo\\dr,((dsZ%i6Qc:a/#EfW3p&P%3cs!b?T^":-]\$A'Vc2 %'`j)Y[3c!gGu/n7YmV9@Utj0O&X3P;$]t[bnsS1KAh/`Ufk7I,=]_!iZ,O\S+tNcLPric@PbQ?;(NFB#'\R"HFAGKP_5Sd4'UuYn %a^kF%`sN,U!7quGTB3=#+sL!=A#g,Lp5IWY[Z!2W=@CE%3.otI,3C\aG8?ipOW;aX+gIWAKCmrt8n9lL]94T0%hX54XRljEil/Rh %ro8)qhrIo!p_"0;qZ@nLo7?]]ca:7)M<^c>'N''to;:ncp8k=J\uas3I2#V9cm\/M %@O/O'P!MHS>0mI>6i_Jsd!^M`F^'l!6p1@`NB_rYg;*N=Lb*@d=m\'4h]?<8+ssCGR5P6Sh3T,m]U8,/T*JPWS_!fPWg3Nk41"U. %Z8H*]9eLUu3PqsmKX4t\*uO'f)F`/GXQP^-DRid1Kd06.k?\H&(]4T+$-0o)Gp(6<+S\?aC:lOY^.9%#&R,M>J8'-o'Fhj93J,ngp*eds&oKU6Vegb* %$RL=Q3QoE(=J<&KZ`CG*:\hmhb:aLXGPS-Q4AYH>?ZQuF36lmfB\EZVOeSd+S@&*HSUrsUV^9j>Qp?YTdO:HM`7`nF,+c+C,/E#F %72O5WNL?hUi-qDoQZ=V`Za3gqKD;aV[C\@;4KC4\fVlEVD)cER=(USZg;t,WR`*g,.s9'96N2G/I?Ek,!H-AFb5uCHKKm+V=bHhr %M]tF7YTLu9+I)7r2]tL&_0k:P-U%E7=hDY+hsXlSdaS0qQ[tGpW9]oe9#*lV,4lDU5BdYtiXTj'p6Qu#d.HTE)Wb86o*+2!kTUMc %$79"h)>.mtM^)6BDT'bXY:9lP<5bJdWhZe3nR&(IFc`K(3F$+#H^[/CPG)q1nDZF+\(91G3F+\ku5")ah;c:?a5B4BhZi"GiX#ua"um!`Q` %\n>9s-`gR^CTU^!c4@cUm*rZ&7N)*.J]j"8+jBF7m^A&3;5Qn]K.CbTSI1`%1:H!-^2a%JJ.5BS=mmKK3@nNX,Mt;a@CS1a+m.\d %3PWS&g/67:ljGD8_l^KLiCF$XA_NfCo"_R\IBn<]-6V%b[cuor@Zr](dWAS/-U9S=2#f&jN;FYPhd*S.J'>]KnGGGsQfOlJ@?_/5Ng0!O,<]l_DsYON#Q>*EpKW$65>fn6"-i"7pB %^!Ufc(1k4taYe!V#jmq+E!"h'fQd.IR6KG&k,cMQI``a%E(:Mg#&Y0dTmm'c8q=9#_-lfNq4WC_W.QMB8J.n&@XknR %C`O!#%pYLtEbG/X:@)U>iX5YLbfD@@2ckYqUuET^'e.->%N0UB$W(30_EaX_.r9nJ(b$'i#Hm\'ZFlQTLet(maXE!\^#Ojp8e0>j %XEnn$&c?BIqon8/_u".,qs^,sP%M'r/_$GGe9V8+[6tc[_10dJQJl0Z,jH3`IZ0XON'%@_Wbo=dcGBe^($&_?>4]VrS?FKrk8*dl %pM4C!ePQWjHNrMVl`f9ud;ieFMh6"`/73Z`eK;Gm?otZq=OdIoK:>-hA0<'p %AN-t-]rdIrCP.dZO'AIAQ8,Lb(=o/>Zp3f[2r9;L-NA+;cEe1WVZU4-*4/*VG"J]g2NX_?!^c>,IoHR:Xu=Xpk?^SfQ.H94^u6g0 %cskRE#KgR&_*i3ikNAn36i-1^\'W2LXWm6=3j56@K7JEp5ighKOl9@cgu8r\Z7nXjCDQbiNedZO:o#4(\jYIg.0\9+/979c[ZdYt %7ZO6r2`!5OP)FqD[otPq['Q5DNE%6?PM_c^MA>]4@70aAd4"BL&U#$X)CS8h#!tT#bT2o%1I-((=4U:F;lo-"''V5G0M8)$m!4Bh %\^L2AQl_Ze1f)L[HleTZ7#DXlfdXYD%4A8*314LCkK=s-)UH[OFI<)qVpcE80e:_Z":#B(k(]10rW7aa5Fa5FKi8>eD2`KAb:3m7<.+ %K$X*ZN_Y*D7@Z/LR7MY9M0;O#eA8ZA(2S#W&f<>c:)(>n+Qq3Ak1,^;*M'gZ1%/M3lcooo1fE6?(i'3)t8f^M'f] %AEKG(\$A^>i=$7`DnPoeS3NYB,"PS.+3MD5Us:L!f[mD'XFW-.bO)3qB7W4hiQlPgC^14Lk$t.C_37)'Up>@lBf604D%Qbdi0lj77iP+jl?m2^>F4-UjL/BBIb29*eb@'5k.t&IefU49om6VASLf!iUU5Rp %BJV>O69OJ)r&,lGU/NUl!LH_8D7rF0i0(f/02aq8/A#]5)qnndmta_MJ)@tnHlXG'fQ_:?24Y/"ROlKVHd+f/nEODd/@Y_;fV]'! %:8/5F>0nTuGq.0*N*WCMH(Icuol(<71\"j7p7"TBi]S!6.o#GRE6Mr/cZKtBENore9OUrbsOe`h"guTpT?TH**7=I'D+B9:Ju@5EPELRr*]8D$lLpg+aMdX(kt9jgCfLK %-:*dDiS1![H`sh%^.XUE+b %Vk,\^(XUe?pX\Vna1AbRQj_YAAp0_k[));a=!(nD:`"$9_n$E$ETsaW]oa"*d[3Sk[rYfG>YosR259_2$D*S'Hc;E8,7=.^e:U$> %o&(=c3`(*!i7UWL%h`c5g=Tr:=.)tGf\Hq^;97Qalq=7F8PiiPke3QN_Y/AD6*1C,:r^9oUR2KI)Wk3AOdfQh&N8LL0$GY%]t6b" %'Jc;*4g_ZD_\:tJRYM/M.8V9dWjl$bm>B^a"(=Lo>RNY5[#6mL5d8)PWX/"c'cXKN&e>>)$qPF/A1Mqp$oV"]+Y@i-oCL %Phj!HH*WBQn2aC0qVg.^%K0fs!(in"7o_^(,W[QFS@jH9U3G!7m`lBbpc2;Rn@G"rW<[,?W$XRWqrN`PDoojEFZ-XsLFX/Rk\[0) %.Lc*Vbdoo3?E_kJ]K;4o+)2hUFH8^'s2SXMVN]nIqiqX`#H9n01,%Nos,\$ZljrDKFk68>`K6X9`L4H+`4@l[3(4>a(LZ-'^H&X* %;:YXQ)a%Dm$ZaPu^L1ke7XtidTb7#`(P(gaWD]i*I"k(`fi\ojNJ0Km="$Q"&V'J0gf(`Yol&]:J+LeF^PUq3rgS[]^\Qje=#YX) %o9\@K]m9D8X1+_Xr;5NWs)@o1pqJK]h=3^UDu?eGIfKB$n)"*Os7Gg$Dr0IQ %J,/e6?b]+S-SKsopuAuq2_X(&rU`Ogq>C$V(Opl'_sre;3<&g0(\K-3"T?X_59145q=pCAu7@FD#&gPJ;W&l6cZ;)(Wn@J&,l>\``4j]YHGTWh3KXCem\eotp(GGg`,%(CXrqT!qq;e1*^]3l`m8l5rqX2P1^4(EVs"Q^&bFd=S)/^^rIk3k<:usB27.6*> %mV_)bPUW+pR>1@Xno%eJ94%V^`N;rE#VWEDHO6rr;UbI:I_lo^j(iUL*T)&3nj+P3DdL/j5!_CFpi/>QO\]nErVQ&uqtg8`DE`@A %o(2bU+!,n*c1bSfXZ#?k^&R'9YQ)d)huE-3rAWV^FhFSVrn+^Up$^adqWfn%\neh:k/qkLgV>39KTE?_)V-oRUAfiEI/6J9`K'Vg[-A0('Ko@I@SkfI._?>'Rs[Ok7'W"??Qt+\q3p7jN11$-'@B66;AWkn(1or)IWgA9 %g[pg\,4enfBU6kI1_o,K?Qu:SR@K-1;u-XVK/M+-)Co?diM%b_,#D\a6gu8j^Z^d:b._pYAcHK?fZrS#i[0IYEW#EMjrA!QiQ4WE %7-YL0`8gHF.3^iFPj/_b#74Q)c)jC//:MsAK)]T/i$7.ADnQX`J!?Qi%6je-^&#>8njU&n,Vudn7.Vh89"O!L%HL2sRd!hOeNm\C %>7t(o4#-q3[1^;@c6>3sLu:3D*.m4eTCgrAt1TQ)uGpkh55Cq`I4iAd[0G3@MT %<3]TPXMa^BHA2"tP%XRLiIt`TgPql9O!hR1(BY;aqGNL=IsM(!5P+H4In"Hf0s]k1lB>T3F5MfKliQ9%%1"hj"b/LKPp/9i3!GU] %P^/0f6&Pe@:l@ftr[We5pNpOb%V+gI2<"XQoCc36X$e:WFXk&i/B.,a@&%nL*d5/>^"1d#`<^k4mN9=O(nLJ)eAi&ZIft8TfXGj' %>C!`i'h/(dPKcW@"Vl,?MPks>RaUpeUa^F?RXlP42U@9F@&gGc*L3A'=#_n?'s[3J$rTej&L@STKPrP%i/(GXeNSp&PJRai,Y)AZtCb5PLh$/ln/A'n]U^"R5oehI[Z+NNhTaIaf,?4*=>ZnEQk]_PG %;a5?#R:t[$H7so*PlYA\_4'J.X7[\G![fYl<`!l`%4=C-1`^+$2%RTBgfbSS&gj[C(.5IVF``:2"r0^d1JTbJ/:i>TMgg;",=nMj %;$;f9<*l5Oe$&7^oAJ97;N(P[kCIO2k8s$Jp[kBNl07B$W*AeY"oP=$q"Ksm*C$qioQ+),l7B%78L,)uh>VBIXK&'Sfc!gVmHj9> %FBHn%T-YAMeUFDGL"3L^=!N3dJI45pA=BbNSZ:C@c_nP(7P7b)l?;WBWNFdq!I0X&-2s_=V;XBDX'"R_O&I2kU9cJ_u91mL>8 %6l`MJJBlGL55-_?59)!@(_G*)>qC7\rZ,gdkkA`C6FB=0S]*j79'@7[`Z.a!NTJ\XkYN[nW/<3&kOFN4Aq"a;_ojKQ9"JUG+QlNkM5fm&i %/_#QDRSV#"b[<@/hcI[iLVo=naJ*>Q1rliSbQmp-e`a]b!<>AWke0m0F@W30B[3u$hWimnZHaC6*9,$P8cgAK=Q5+dl;!kQnr8bo"2H4h5.DR:l`?nEHXicN@jMTPcQ@gS6!>>rk<01)R7P;a>=b5W_Ypk7nch"[lfb,GDPb<"G;P\cOXKiV[ %C(nP#ZG9+I`=uG5Z($/rU0i19.$!=.s7I/Fa1fpXHndb@;JII@8:5r*f#2jEon)B.a[E)hM\)5PM$ %.EP[FBD4]a>\0-ST-q/V7D7DG0U:.*-GZX %HRiY]&j[(;,_=D#$Ea$lGQJ#"3-@R]<@*h3,5B-0r1-.(Z7pMoDWUHX"C*P%,8Z8s]tAjKcb97\T9F@rBD&?>3NNMRUWYA6e)ZjJ %#ajpjd6sH<%%Ri;Cj7pTY"/tZ*auAl=YfTaG*$H`/iMQ2C=KAsGJ?(%"`3LG2"UOh4j*qVV.,8g%A?EeQ561HO\NJVbpd.ZQ'0^@PU*R)C6T4B3pIhFJAJNpd;PS>?mmbBmX7I-2-(o$S3c)Q0sK&! %h#\nOC@(`ei5D28&XPi"$.r7%[J%cEG]_n:-:Md+8Jo5t/kTG]-BGt+KT=+;(;f2OHp@"ifK7b2o[.#_'`:4%M/1tWJA %kP,bf+,9NWWT[C?O)FmjUKsncOA>/K(=2P'F;Anh2R_;)8V4L$)[R+#EQhQ`n9l)9Ob9=,Gmq**%#/13qEcO$i#+Qh7$.>GsP9J5rhn#]bVC3,V?k"jg.(X7'))>!uneJsgDck[GU&`k0;rJ7hL: %PGBT!mmSJ=aM=%>\PW]$WI@]5H+9D'2O0cQF;eK5=9'GTF.Zo-BDL9nWKmdJ(_L^H:Ser?+f')6E931m]oscVgK+ %<-T9:Z5e2m]=-nd`]C6"(`W_p39_tW9T\fZq2QHJ#S186\r9L"KLP$qa_<(Kn472/eV[u-?^s$`7"!f]RbWBQSFR=eA.+<8Ceoi\.Q5^Nf>mfG1KVKqV:\lIm\UP.qhMSVj)hUWjUu*@[TREqV]],\W&ih9 %IS8*Le(\gFVlHhfEY&OSOj*9eRO<,2*_3l9dbC$O5'EMs/93h`.!A7d=m,PnIeDfXri1G!=T^RS+kY@Q2@]o?R-V)<_iOa#Il$uk %K=AW0Ha00]F&YYo'A7g4ip(SDi8"f0NV&*-rJZYko3qio??DEcaYXi)iN4Nr%9_9DfPr1`;(CY)&)11hj,SD,HleDD;^L^=/KM#? %""2s-BN"NVI#Qh<(tT9hQ`5f1SBpqp55L9crd>gb9@P&? %XI2qr8;9*g[05!s2@<<&:kcHLS1<"[SSVW1C24q@dAJRIC?51+!'l*cYTFh3Y=9'gXu.Y"Xi(`P!L441nKpq<:K@JN%L2s6"&6b& %8]X@,Td:]@[[9'D"G?'/=uq-*je%_kc;BMM>-J.qagB0F+*;`m!nqI"AVi,oCI%V1a\do*.:G%V9p^!A]Kl3mm+X&i.kZI8I%S1H %.erdlnV)Oi1XH[A&u)h2+C;9^,\"NFU>gA*UJPVN6e4mEWdkU]GZt-]!r`?sU238L$8,8*-Cb"` %[.Gos!Ij")dc$5`Bs(:mLjY3Mg[oEVeQQA1,Z\2O$]=9J;p>9De^[?g1@F9)gnRbU7"r0B385.AQ(1giX%a62:*Qj'/A5!H3,JgU %(3Re-kg#^%6"aJRUiO'eKBH?+onku."6/d?9N&,SB@Wm^-'99*qkq4_4nqI=pdhdP+YK(!tg4I<4`mYZOm2Ohtb]s'N3JhAB-:OdAmIg[k9qV %IZ:MLfWQp,?*7)@>bd5)D_%(RWoYVg"15i.Be*FfM982lS>r7KltQ'oTrD0p'a%GUWGAk.DbgX&OR'SXYeX8nCF7,4h7_HrTgl$7 %PX+I7("%<7VJ,8)&6Li1OdKG's2-VNcBJ.T>e"qGS8:/lmmH[)Hp(HURepuEqjJ9hRC&b&F5^^I"oOkZ+OGA^Deam\K$6.W`2b17 %cEE`NlAq4t("angHpgR37YX5;Xb7TZA#*d%\H)#JXeo'JraVHUZHdOa^imsV>%9&3o:]!7(.%7;dN[m$YahK>-'BZ1Yol;Q6cWTQhY$Y';rL\W\G,!QAW.lALL(/jQ$V\TiX)F$h6W=._A$3[lF.HrI)(C.\%=S]tM" %@66>-kc<'D@`LuKh5"QA!Z7)q';]B17R0[n"n4@:4L)=D:H*WVcC,^]JRFH`l-!qf^sdK=@5mD./77Y`SfP%MKsOMHP[#mhKDq#gHDHID#f&IY(t*QJ$1mQo.1%0.gpaYd@@kgi'B:c8[QD$tPnH %XLLH[]\X%1:-6:hJ[2-1].ZlO@72fI6TH/"Pn$lD3V7d(e7%*g]@sl.\'\l.!tcH+EhQ$@fn(0OO==DlZFhq6KDL,OFfq[+H7cru %6KcO6`++X^M^6@&KN@XUPXa %R*h$gNE[Xt3X1L&\T_pU2Mo7QNPPU)7%/[e7c@!sbl1k'U2q6O;Lgns$7h'Cg,q+>Bo*(/b+"!*K;u/YA@!m7(Lb0_K!lnE;4KpF %+T$f0IQ=`K".Lj_RWNJ=?XVbal"tN>\cX*K%q_)k!?FM1bU-JEi*#,tEb %YRTV4[`9k-6BAGlc(FW3F+j0N:B_+tI]QYd4DgTh&'2uuc+SMEC>/dV-`p+` %1.h\T*TsV\ia2_'M%U1&[C]4*/\s\%H7eFl&_Tu`MqCV4'd#ap"K5WED&Ncr/.(!@6(2]fp@fbBB#5gYH9V3f#rF3pZc6\4JS4F) %^^BEG,5[ZV9L4O<:T;YY@r&FiO/"YW;/Gt:f=bM(DN4N,m*4(E*+Fl*d[-Ca0p-d;haOmf7s7cXU)>-u^@GC-MFbTiEo+=n,^XTV %V($C[9[uH,8E`ssC3X6pIp+5%'SKGmRAR;%Jsu&JN2@D0]tstP]_.6_$HZ3:&p'hE5"UkZp0+W['["40U616-1_QnghW(k7J:9?< %.@$pe5u++O-@5nHJ8)/=f^&-m6I_72N?u=TA1rDL$`h`T<%MCpUB9oG/m1X)TrP% %:V]GHK&Lj3gV)&Ci%3X08:nt+?NG1hctX#]ZO3##r2BZV08\Ss:'4&E";<:-q\:k'?sSs/-tm&`9L/lNRWP/I3ZcGpkcbV_L95eK %Vq!#s[NM!q)F^]]a+nNTsYPZ_f"G#Fj4 %G.#tVTt+]Ic4JBu(nrGX.GL7_7N+Y.1adP"C)d=$1Bn:pMNLD]9KPVnT:)\"po@rp*M;[T[ %XdJ)WVRK^qE_^2/(/OPpD5.W`DS>tU4*qB)'4H.)mP]g&DHhaB$Ou1PiRodM-f<@oKH!_WWKJ+n]jq(:$$)I:&`WE>H@q?YVDVhc %MhLrJn#2,!`Q28;5Q/]T[oCm4NCc?iEGM+\OXhZ7/,'.keg %Se2[\NIR\RaJ[R-K!NUf8SKFVjqVF%gned$'sOW)2'bEtTN.>V!o-WS7TJ9HcttPN_E"JAIEQH4>WYbb;Ii+hZXfX;J5&ut1/')e %>X".^Z4>D!@%'pfa$PO!N,OI!SMrM9NQ=k"8bLC$;]ZTA$^&s)Wt?XL(I.WucVaX>+;XX#$3?p6d;bB!o*tp1]dCB$82)o?*Gc', %4n&Z0%W-B"BbUH`Mk@n.W/FkoL<-(YJEC8*5^"n=n>,b*:UER6b"R[lW[4PKf0#TE1$Va`+PB4Gl`\m(7FBAaO(EF,8nIF#uJ`3 %N?pP5)9t1mJ*ub&3BH<\flLuDja%CS9DN=8cQPc@R@i5TW21Bsq/c&gn/O_VW=DQkXaA@FgrFVEdkbVPf4JM'[]>2al5#k'ggqT\]*j*5YOq%uY4-6EcXR9\6G)Ng)D %kVaA-A%#iDRfotg$RmP[qGEq&._2s`^ogeh5pA8@b3O^1(,gb$B+q\B:6$Ui+G:pArS=!(^RP.i-8>"2\9,Wr*$5#nSem]XMja7( %B4CpW5a,2)FdnTL8tpY8\?_FE&5!I$B8'!gA'QP1JAHQN]olrI#;79j*3DWuBs(h@R`q%(rNkN&UAd[Y;m'rn%kk)'ollH;$Wm&o %f8o>!=+9Qpb_-@NJBA=6#Q!7$8,oEJnG6"7ipt,r@s>Ic+cih(bGF_)`RTZ`pZNg5r&Hm[jg"Q!rK+[=oRsh=%:;>GEaJ_bT&Xhr %I=/N;gaZ[G\@Ss%4h:r,rnPJ6n3hR"oXR(FcCTXnc.R/SLXP;1@?t5gc4AONm(5cIj9p>2oqSDYj_o.<>ke[kj',c5#53]4DT(Ak %*ll7'i@uso,Vh$DD.%RKlucAfWn3;LC6u^/g(2!*;tT%r'DM'>IXV/G^O,Sq^AL!:NaDp6?1er!Rb]d*6gWg3q&uVTT:@(1%\Aq1 %,$sFM=X*1k=a;4Wi`VZn5/f>&m)']@t5!s'WQUg6)1De'kN-Z`f]4=bVtp(1$A*naXZ.t;Y!Xe(\A %ihUfg%`u;,j+u/,S;*moNW"@MGMPF=V&j$-_677;_grJ6p2cm6o<@KZ:Y7C4X7/K(O8J^agu^tDM^%;N9h0OJnO/2 %-t)q2%$O'.Ssj/0WSj[eR1(22X?7.nDGSP7Z%H]MR]ME[AXeL?HUb`k\p-uiamJ;ik+"Ds:X;)T90=`nUg76I4Lp:EdrS=7V7m?? %5HOQ+#D-Y(5!9P(JrZbL_V)hjUZ"C)f-6N(iE7m.pAKrKV02=0\0[40B1+A=`q`'kMAk[(a^4MEI!YdLcg^Q@*fl\$,k:kN^g>p4 %I=Lka\)4n]f3/.@APDkDd&p8U!nmBZ+_7V?MTW31YA;qC9GO&-9kSi$MlfusW2El+m$ON?XS&#Z!83d;UC-^/SPO,M>];O*0QS/[ %]6-rP;Ga,#XR)HDS#jF,jmnlt"]Z(Xh+8Ym%5qm@lBV;9lBl>QK*A/NVlF_cg%A3T?mZ6#i&Dm?:@-hJCMUWOBqsV?=kp\J-fYb+ %@qqg[7^9nn=('U1;/T5G/X(0@=c?Bq,lXAt]*7C$m;L8&Sce#2/rFdkX+C5UIYmXW]n?&o/0nn;Ks7+Ylse"5C]iO`5/BB[4m9AW %%O@ccN?'6lg,QMg=O#hOn]0_Wn(?@H_>5V65<.l4@A6o5m?T^9dpD"#6(%gR[RP`Mj!89.n)S]s^@'RN`.9:[;JcuKYhrZuKAoZ\ %MJYY[On7`+?7EcX@p=qL+'_j$5=,$0SEumlGQM"GQ)FT)+ReG(k:FfVsk[8hY@kk5inWCJ(lbJiXXo>[o7uG=#3k/:OZM]D24\sW` %I&?g762jsH5bX]E>i0+]p[E]EN;>1N,b<%;aNY0X&0Z8dj*L$I)pVD3W%jgi[(>nME:Ki_>$N#C3/djGDR'7O='@RLec71],Vku? %;+$?)2D"!!n[:SM5fa %?)=QX(hFSQgb]pqbdA#+a,rl.%>@f<]W/=?1hr4Fgf&\(/F8iuXVAp$IK$"f$HkR8@i%'MDNYupDY0ck&j1)s+iE2/=.sVq5C5ldL\MuA8.lAd].'RL5T@E$<$jF1S1?2M;J9Pe=O*kda&,$76NNni*^01l4gPWoKhDf8hmSoCorr*bmMKDqiinI9RE`GrD\&DHq8*Tsh#,RNch""OffBY_a %D2q0VY&Pg;:he>4IRPDBS3QuE/3&lZL(%#;=AEE#$TYC.OpO0")&o(SG%"qSiX=tj4"lBLpfdn`@Y8iG,V,hEk(.'6(3l=l)XWcn %Lj2rH4u(t8:+Z4F;0;ZUKLb:;qU1r]JJ?TgG@R_]2-l>V.$^oOE@kSU4(`]b^%aRFhZma9,g^K7YpUiQB %AGr>M0CT6>Eu;3VPWO42*fE+@GW^7$Rn\B`T>pU %Y2@r,/)$$o,a9d(V8pM2e_ee\%r21*JiiE%$d^)AD+:U2G'V?]>+)USMcShAMI+#?Fe)&XW?P$TBsAoi[+O7HiHdA)q>cO3Mgc2: %6j?:d=GS4KV?+[L\ANVo)=^k=7Y5sPTF&p-1q>rBYq93d",)+FVrR")[We<0P1DB(U[8XMYXcBlL=$cnGiOXIbbWm7AeoTcGHkZq %"RX1.SMoHh%bZ@V+[6!g0r%#i>GHHPfaJ_&C)KaL%!GY3ZFs^71\>.lP\&MuH^!PWiKK-0&5?\a=Q8oe>Q`(U_DORa\qpobaid(a %rW^V>2;.csH?,I^=^dLeLl'Nc)$:8[cT],p#D(2B_(MPrk6KYnW7':B?fc1*4:mgLYqVRsg51K-g'U\V28(`i[ZL*B+],)`%PF$& %DkMkWI_s"q"dG?cJ:B+=%fIk.Vg'L)[.d2Uik*1/As1 %J9Agj.!@4hP/>Rb$GTt6S+!5_O*o/Q05-THDU$:4SsIT63:)&G9-h3t!iTEfh$j=VVknkafgdS(EQn=0Qnbh<\"8lW#%S'21XtEY %eE\H4V74mnfH-\f/sY"SD)9QVYS[0h]eAtHR1`fB*8`EYS:eR/; %?T.$>cblm:j4FP4:bS-JPV"u+;p:plR[UG6=u*%`?:3YG13@a,(-6rQdHTBe^h30:EfP.s;/R6l^RAE'ctksk*cHT;j^+ZDLcj8A %*#=c1Gc[9seRedCqD>qXq5qlQ)J=+448#8.9j/W?+?('O']L+P)B2FV0cIH47QYs)>`osHV3nO-jE+%4aXI2:TQf?BOk?4])sB,e %&2bgupNh5Fd`ILsJQ7#c*6PfC,XIgOkrO5g;I_I,,F7.T5+5t%aE$OC[RPkGVKi>'LdUj[%&M^j`sbWq;9"Ol`o9,K?;)2^g.m$2<&*4I4p?(4],Nmf1,f2$P$1S^_"1C[HCk=T3"H4`28=!@ehhcV2Q!?i^GF.P>SS^2O1R)hIOjfp*59WuGP]-t@![JB1ZZJ@*&"*DO&V_@[LI/3r %4i.%D+-.u!T##3/pf7)<+r()q&"gFZ/6"G'[cY_GIK<^M;>) %1OuVG9/_4%RTf#A+O %1d=O]85iSMhtk9h?Ggg %Zl2BFZQ^q4%"uV+KH\+3(2ok'd8(8nLr,n3%d6TL&`aX3fh1*=,>^-a]AB`uai#&08-@\kT#oda,g8r-56G7e[gC(QMUWG"r:%6J#_f5R"WuM8T6-VOA'f0;L?_X\J+k\F\dJgkKa([gq;GnR%Jj7`"j%J2nKHm;Q %a3iK5%tZ#S6^PWpPbB#:@^+bKme$uL48 %*(RfXLTf-r,6XjfJf)`$U)MN82*j9J>kH;[An;7.(n%KdQkP$?oD6C;8Y)LdgLkVfL#Mo[!Sf52E"(aCFDnf'%>sR/H!M.ntaNej^TmDm_#Z,ZHCH&Yn\IuMFbRe'b=jl0ZX#A;:@#UN(YN8;5&dEPMa!&sgaH]1Me3?#=kT`GS*lT(rlK:K8D %B&"i6FVmo3C2/:)41)XFN!u.arctrhc'VbR9YfB8l9Wm>_`FDgmYjgk8kLCQAE$O2LKc"r(4'K-ZSD!cpEqA(2:K7&%U@SO`nP/2 %KO0Y^Np=`+HLF&MhWr,s\A/#OBY$l7nj5B'K1uki[XDC*:EsmU0\r=tLmYK"D$;X+Q#63<&3qFp_4(ZfEXg\)`*9*L.qub#admT! %$.!gU?U;'.;b %@bq1W!j]a\b"KT[qWH,sZr\XCrUp!1Xf6jY6Vgkd`d83G!7R+Uapts@4[I9^A51NsE5M6m1_&CPrY8EZR[\gD2l@P[(.G@dP1AR\0c`_ObZtLgXeuEdS)F_g:lS7bj[D,//m$O;Ld1U%*Li[c&nPFBX4`^ebt*!(`)r`=1TP;@k8Y3.nGVT %T>n`N)LrVqL-%<2@d8+ZZ+6W%UR@&=Nd"-hf;9Bfn=,'gNh7qp]_5T[h)nX`cP67grT1gZ)'i1LuGeQYf-/J9)qBa60%JSMQ90FLHeLN-M#*^+'2'U@%46"-B@qH!$FK6h:`eDAC,<[>h>8fhULLtEm.(YON(6s=$ %pckk-8m/ii':R79Tf<$fm$$U;i#])%.mn*cBNLbp74JGWjf:-Q&/b[@[\Cl!hBa?X3hYQDlKs"V/MiOj1erSdJ?V^%$&VEr!rT` %A:u6PKIp0_H+Fbj5,FXc-0ZDrk9N\EE!t4d\)us1Gsa,Uj$6d8)D0IrOU:Lee9IoqNnj//!Det^_IE!_Pjk;9Lj;P?d.5U:F_lqN %Y9XpeO?#B,hnVY"Cq/,f/[]SRk(lg!c)A1jf)a!K6kMkrj5"9f+ur`o`(pX3!Tu"SF-WB!3!mthP?o>*P.EQj7OJ%8AuL:4AV0^d %m0kOe`2nSd0bqqIkK/&[jt/6&5(PPRjWlM>>FO*RhFCY5Nd;r993tn"pXb`LCrqNs#9=_:C-Jj>T2*!*G2eqaQF\ %EWbX"lsnf+nqGb5gdG&nU9<-16mBjtTHcPA+?*] %H:8']6f9B?=T[!=DOG_4=5iOm[r00/\=5QTh56!Yq@"c(OYlo?S_o#^gC(3WbHe-.%R!d5i1=et(XH&D7(+XBWI5"FT^6(bmD,E9s*\n%u=F,T:)G-"uiXJ=:I`3N=Iu1']%_QZGLID86+80E$K=qb&TbY7(We&FOD]O,C=^'Wa2oqAW:q=.8M6j4$"EA=cFF`W1ZXS %Nae>Eqd*/4+KP-)g^?"CBnd8UogXdk)U]KH"!A$oB]E-Ck[D>X^fc,%Oi?*QCC%X2,h.b'-W)3mNXGd-Ls*nH&TciuYua1=&m\W" %l?@kj#&[-*Ddn(+6NEQ^DKmUsKQJBDVBh%d\k4)VN*j<2CM0HO"FZ^[e]+X97A&,?#'sYt:i"f?.2C<$$R->RfY\u:kCa%PK#?Us[]RCY0ma!9t]f@l-jt-T;2d;QYR[fTosI0(sRe[bm,FA%ZKVX#H`c$H1'< %T3>u!l;]87\b:3o1;ptc]nFi\Ql1.0,REP,$:Ye38F\\-bC#[XfVqnRF)OE]Zmp`RHOVn,;o:]ol/V^^WhuP1@IsXt'==*BAa2*b"G=PsV0bc?ZX! %Jk"n$dNZB4."Kk19(a>Oq]5p>)YNF>NT\F2rTs`HfF#d4>sEGX0,SYckl1.hqKd`b]3Z_8!09qM]%WH>R$6 %A$fO'0iK,W/%Hdi+kr&@g%@nEtFP,`AW\e_m8Ho!T[j"9kYLS4fe\=9UBG/4')I>(N9)&V1N %0p`ReZrQc+M!Bbg!7eUfS\RSB]UE%Dk8D!'0^Qj5NlY;dEg<6A&X"%+'%U@kR`$V_r?dfp')V %#6;$041,[$9('Waq%bCE-FR\_a)YY3->A0_1+hVCp^qK[ZFfrNToh5@RV9>_;.G:PI)oAE.^tjt#_0;+c?X.G;UD_0W'qH!6XhLu>(Hki7k_WHC4,!8W,n>d0J3c;^+3lQ> %!u)0Qi,YAjGd0S\9kdtN?.Z&2$hbb2-)QsJkY?d$3LL2M)cEOMc)`KZ %pi/pX`MuNjp'0i2oN8Ng'2FY+CI.p\NCr?KUMO`&Eg<&T1YKQX%qN\38)4W3^"JVG(TmC0E,:c3@NqbUrE/a&MVO--['%M]P2tC7 %h>CEZ:`S+1!onNUZ5J$_$XE+ra-D`V3pV<.$@[jQ>Wi><)W+Y76P>`o^!e8S/\[[o(=F8ZCp7%K?ESH,+\+>2Fr*\@^2,PQ-U-0> %)Ikk2S$umF3c'[3[QpHc;b*(1Vd>`cZQ,Mmf$se;-)kIV/"_RD'cnc*qP;'[L\1d8i^=>l))3+A"n?`f'FUfNQ9/]ia %/8,:=RSmQ%U[H^Q5K3nTE[O*$8V^qcn,E/RI=6_[>)7FFh/0m3uYFo'/pG/rEPi6 %_>G2:TF0tH*^39HVV&Ut[OhRr%@C77>W-pB9&U-#dNd5oj&hM>*N*DO]S7/?B(LjXG1scI/3X7cc!Gn6pDTBrG4lZr4Y8MNWsP#u %0`KcgXBoQ%TmC'8YgS+4Aq26a:a_7pofR> %ETAlephAdA*4WaI2<^TD+N>\^Umnc;kE,mAc&#jT?3S3NL30Bu1]U5dm]uN.dn5jigB6Dfj53>o%+R&9KXr\pgc!*_Bb9MeL4`]= %n/VNu1BrLZceqX5DXhTnbtBsTEbZ7+%k\i9.to,BofG&#FKNG&[C]p#W:6d-7bH)q_qHfN>2H\*8VK,Y4^V3t6=]:ft8H<)#F*.J9Q)iDBFS&jprN:$1?">]?b\6FA9""p32RlOt*8OI%o'j=Sg^-q0s>,(0qWI0j24.C-Jeha"!j+7+f %8\fjMPD&d`YW_=Tn9[e2!u3+d!,S+.M.5"F!J5\!n8J)*,[Qf5<\rTLcWX:/gI0eucRZ8I5RrK[)V#P]"0rD.P(7%M8(9!2,TuE) %U%Mn+7^+iZ7W(/TVep-r$gjW/+&;_q]Pp238CFlNbqS.Adm?S0qXF>c1gDs)7ErY7?>Q-nsI.t7*4 %OsOb`+MP.,?>jBd"I#7DC85_UT64^"]?fR%ahD7^JY#MENVN)OYb?fn$k"C?_8pSJ=R7;1"3Y\D^?#mDmmghTSCc[K=pWDq03Pei %o_6u3!neiJOaZ3oX\8Vpr9q[,I=CEKh^Nf,V$u=8?\gEHg&T6IT98d1/6NRR_)c!iB_[3*idjBS*fMPm)LLD@N"Tel<9JXj %ENK\)#ad(6)m!.Hol$*lCo6@_VEIg[e6-',.tJ[J/ps0?@KXkD>qh0BrRdgD_&W-p\m#U-,CmL(2kG-mS<@p/,]G:n>#<7"Z%%WU %'\V,m'4m@hBn99V1GT$3(gh.@NZsHCTFbmcDeV^U5k&N6kr*,f6BQ/'!_0&H<C#h"ERuPE'#/dLYZ\(uEeRZ6VmpdkUGGNL>P5bQ2l!1#909hkICM`5^(_V-uT11$5nu %;PD4I4[lB-O%+5V1bt;$XA[Z^B\p`#Q'bam1/Om!?"A>9>2mXk,WqW`DlrJd0Uc5XbPKLc@k!$V<#5&1SZ@rJ-[-'@d>1AjjHrRT %J1%[*'SWU^0]6(J\HrD0IR8KjfbM20IShZ[Ho4$*[+YD^>mMjF-EDt[VHDO8+f;RL\X&GN_RRCC=pU+).(Ibrn$[*1aMlWaiH<3c %j=+bG9b6Ok.X\97%G"Wn@[."24L+2;A3n)r<.!'q=)a?g4%mb$@qV&.MNl)amgXL+Sqa-Z[R'DS*+&ujotiRGioCd-F:^Y+cKJco %`-UL]`]+)S9b%8+p"?>%*$IBq9Lrla!,;ae2K=TB[L4\X_J"Ba92k?slH`XDbVUd^\c"$(7=h>(":.QqC8IA %mW'tFc0MVZb";7'TZ^)N%Y[G0B$boMmRBsYLlZ.C"M2(Zd`2@@L1)rA[i0]U$oK0p%@9H%+dVgYVl:>-npn"I3`Z<(W!.sF0VWe& %*F=h6+E)FY)llEt16ukVm_5]B\"i!UK$:gL$c3276CE#DkQup1YKCPm\r'Z"[-3=+DiD<9#^>rI)jQD)AatAJrM. %fd)Fe[jL+eRp`1V`bUlN2RAR5qnX7HS4@R'gZ`1L%O\ %_C5\B4)p=$T0[F/WoY"ud5F'\5_sR&n"7;!):Xgn(Dh`bTp>"3L]MT"'/Hpu1`XRf)Pa>XJ1(QBE=Q*FoVB&@G('cRcZnN90%qc? %1TX;Z5H9Xh.baa)0nC=7BF]8NKb-3i%Sq%2'EYb8,.:,CMWsUo+;)QfNep)6C0&tF@LVWDMdmPm/Sf$`9:H5-.P#58BW#s[Q>aG, %*bGhj=XB9_0[H.KGH'^oWrOZ0DI*Z=nfVT@iSR-FetJ13K-MjrZ>G157h6Jj8>RbA))Y6i(XoE$h?;"_XVjpob\2m5el]`-)9jm^ %]k1e_WD`fkUf+BlctS_]`oNLh3tn0(8A8mo;U8A.-Wu^&)?X"O&J\o#2Jk'E\fr[A%i.ifo!`?OK!\ZsF+ufM]DH$dc23Xh)g5iuDHuZ&\45Y"%ZPS3&%f %Jl7tIO$;"#=`794$FETO %"]sD,D:(c:Kg_uB's;E%6oG."Za#LlE@lG+*1bkJ4FTU((YplLSJmHoJR+43noeI!)FC.:PkI6b2`IRFg=hT4&Png(?sPkZ;KTfu %/a]kffmGcQ<,W?j>a_Z8eqG!W$tBGTCeo%Kp;C'[BYB-lH5Ms=MUY.kF#G.Q-E8&%_S+:h.HGrZ>E`>V'%!sH'3\NT_3#l@$d%U4o'Pe&s)Ejl/k!+4)/1^J`8KSoc0es*$@]F()IotCCTa+W1RYbOTIuP.B?H7)J4rJD(-U^@cs/t %!I*"N;IA4u`9`)ganuS?XjWLd2D4OM[FJq3VVjcd>.LqBJKMnO"1]B4``iqd>c;2N=4l82O=$Z82TaSn>PA<-1eqo30F)3C*Z"'n %DL$E!`r/5@AG]cU8qmLLn!dQ\,o$T7BjK"-Mb-J6P?rUlISC#h8>ZU1raHn9Kii\lQUjrLq_LFdE>Y#Lusr[T'Yhp"!KIC %M\i`#/"g+CRR\3`o6Y?/LQHlmp8#eA?KfmEf4ro/Q89-u/P]2([":MQ?e_0BE__W_3:CG=Rrrp&X0f]u[&oDi9sr3t=P/6cM6Bqj %M67r*68a+sAFYFj@mp_1]GKEn@?+,pNR1cP:[>ak4[%P6/j0j&l\g$`Qo(i7VlCum4"k-JZ&4KMq2t3Gb8*B9=/P&H>Q3-s[lj;<,h$VHm2ilo#9i`G;Y;oiP(H&XBiY<503>I93; %ZE\MW,h]XTY"X))T9'kXcYc=jf,;muho3X5=hC2T@:3+\!Y`'bku2,b(^` %N+fZSfWF@6)p\-+HJ:&k7Wfs!Yf9d,8>H`:`nAC?`Ck/p/`C3!QL$/!/r.BDatR-RCXa5>mimPSMQABfI--$W&Wu^j]+VL.V^]-W %V`6k=[0qg%bV8haXD18EY;V8ibBeg_.gVKQh^`q:gH*5!pC\['Oqg3h`V*3Hh'taB09oIGUJ&K?;[/I=_VD6j1(&=uqp_XVTasl8 %XUBlEf$9a(d$UO'M]NEN"!TUXH^;,)S.e=(*P0^iO9QmQDg_@_+euklj/jBBc\=qK/+NtjIuNR]Y_G\A*,c]l4O %YCn/7GA?'peMc/R*Toh9a4c'c51f$IhoFnND#6iY6lX9V(JR_)Gik<0Y.ht1(d'i=,bXJGk-it_BGm)VKoOCIjB/0-UO.!SLFYN0 %hg"lW=01WFiQeMFYsZBk*#T,`dTQLJ9Y8$TB`'nGn3-o-`?Bo4aTr]2mXb;53h;\5'g+5t+'eURI&ZTE1'M@R.'SV\FL3[="X2\S %P7Z/uBRFk^l01OC'A+G)H%e@0d=Ecu9L*,[9M?9q@u@9(,5J^VFBS*QJ(qNJG9,PD!>OTct:8?J7/NKt+VPTE7Uh00k;!;PeH20iZ7>,DXE@ %pE4a;L7=::OtJduY*@"D4a%-s*qq(1aTPmF+oU_+I1FnIRVfUY;J\=f71IN+<:RMgW<-448>OR0cf15aT]$a"+Q*F;TM3RaDTIqk %NIg(\<3R7)$IlPf.hStJETG);(0PEh2>Frj2V-H@:/6)6CCD>5on%($lgYCL+;`OW5iN5-?9!", %E,RFNVAC"#@"9ZQg,FtQ4FJSs>aFI:G%$Kt23/IiO:*k^TMqDs49&&en2R>_P?'D!ruuE?eD`?6N3Fnb%&gfM1[;-#I<'LQ;5&%_ %BJi^mMnt?WmMIIG3m='_FdOmt.bA-?>9^a$C58mGADoNX@3Lf/)\Fj[ToaurCgXq:Yn\YF:^Rf9H<;![oF(=cqO3DI\%>5Waj"%8Y(\h`:?"_8a+a#eFUiC]m8QS#P%:UiSkKRr*`l]8ole.!A&_jNm2O^BEYGOA,.gfPrLOc@rs`)'GL=s5`dmU\$BrLJVB*V %7`ds"#tTK#[^=g()2i?cR/>>oNLB^,\%Z'6U64b2];<'1.[*61.!H\OUF[4Z;ZRin%W+8Qo5c>V5Z!bn\%SdWRCupL?>CB1Q1LfoCZ4oE:e@4[JF\HZ!dV)CYpUR[/NJ(I8rP9[B?.^+f&G2LTogq83a_&YU!P5-$SWdobYk[e3c!ub\P21k#,es\P]W?P %'$4E%;$5V;Kt*0C:"/1Q/s$IG_>D`)BT7e:S9^c?,5ng.1aip:$_bnL#bE``V1i%eN1*A!.?-rJ2E@sB\4SB(&[s<'<(UuGat==G %NM*F&NIJNtrJ&Et:Hkjb-q#i?[K=it&aNK8D:u3$KA$\F&MaZ-V9IsZD[fF %\Des1boq:?O/mHf/c4497oZNYBo7m$a*29'F5\N:-g %;eNp"/>/;Da#/OA'X"]H(_[K<'dL?^^S")^59md6FmgU)B"IQ&*tf))?;Xt/(?rXgA.ViX@joAUCirj;U/U4V-M:KL[/2@H):1IS$8(P##0r%dt,K'Y_WrOa1\AAJmU5LI!8u@trO2N![.M1Pae`Zu&W1 %^#8Dd/Op`!&LW\u*6n^k(!6k1feR^O)bitdW9OI9:3*D'LG4S8!%>_#Y@ %7$=j\MaT#7MdU')A97"Z"`u/SXY((c#i(s2nVE14Djo\i*D6L)9F2L#HAG&81MN12J@fqIAanj-3PA^]HPsfgWY,CPMI;BE6Z9Dq %ZQOiT>K+)\,MD0/)3G9%-cHT;pj%O8C %GfTZ.>&I7mU8j9cAiQ%.79ZR8,ZQE_PGOMFSfO:mr/?e?0LK>Qg%=V:WN:fP'F_JO)Y$Cf/\rG7OUbHAZ[A22WNfJORm;mO5<,X/ %`E\\;2(!NiUbK\/=t5^YJ?#m8bs*)37-r$HN"S,gNM?i99*-B,,bS?6[^o"aRdM\#V_O]rC$liZ!8Km-(Xpq= %^f)^-2uNuP)P_Z#8<6&@Y&fi0LL\jnm`'Wg?uf7n[ka=n[V]A5.F[LJ4)2:r#a4]V`1'X-G_.MQa.&/9@5^anGA]9+dg<)h,Lu(% %,Tg,kW,FS$X(GZ<4[gJOo?,mTL"?^_mr%!k7K8U2*,M_/QW=C?Je`UJa9^>"6DMn%Vs6J`I5E'k]n]m;EQ]P7[K4!s]=s_?[FM\]fd&,*GY/.Y %HV=(WT(9\S=NYVAnf[H"W1:@REHd=UNoKqbD%A7d?rHG>X;ICkb(kRXKAfD,+iBB&h7KpYq95TjC7W?#Z$n"]9Hh."In["5H %8jre.9Ae#QN=Q:.L_X?`'Ea+&o3@-JS3qi[$Y11cT;<3Vl=,VHcb5Dn8:c%D@^sPbWXrL(J]47E6eRK:!q2b5bNB$9#*cWm12:FA %?>n5R"EFUjB'gF>7rPm3')nN4)+>8k4k^'"Z"ZT %Akq:?\cB^'AiALh@8ug<:u$#q=Ml!;F`.P?"1+sM\X\pH9pAJ1SuKs@jAE'Zi3F\mIGXPpH3R7)6",q#l[hED[7m6Sm/kQd`@joJ %6-Msm2_IctNRin2jMJhLWs_!A`%?Dd273ZUG7A'M\CqGT0/)OP6rdeCfK'fSROY?ju!V06HGP$c+O9r0A]I:m:#Ep=;ui@,6U>LLBrdiP.#'N3-1joQ",Zs2aRNe498=]n!so(ZMGmUBF%mjiO]CHgEd[, %">@H,V%E\RBiHQdCK/3$#lXW-3bn&OnOa.IZe)mVp^ %q/1Cml.SAch+itS8UZ9)!/Z>F+QSNJ3ACS667O_lKDK+)//P-\CKP7SRKqG1\Kfi=UP2T%dU9[fP5t;1[Ip["8`Ws30`LBqY%Nnf %af2Q(9r2C>&H,Qh6t80VAts#j(1G_4/;Xm\;97lL:D[&ki!Hagf`f94q#tOjosU((l@LnnieXcsR_NOno9==])k1ai<8I>gb^+%=(` %?cf=)'^)rA&g`(qe8ZZ.Z`oTn[]3dPKnRd@mj6`<:bj+%.LdNcIFk;`"Y,9[mFFo9AY.arWOtNA`'=i`i69VtL %Ji?!q/>FB?^gjrmehL4!?0JG^l_h***PC&HKK:k<.^F@d"f7)`e@f$Kc@dLLl#O_f7]iqjh:MHF-LB\se,b"ZK\+SlYl\<;]1?$[ %OO3\R)B[;+ME?YcFLW^'dV%Hf5)F^T&HZ9GnX'i96)TTGJEEXIItKshVFq.;^="RlYU39>\).Ia.XWeP>c/g/-Zq5PS8EDb'b.p% %=WY`=XV^]::BgHP5soo88?)a,fqN1k1a_W]).Z"!6h4t2Y@tQa3p.5WcLGpqU^g8nm@YF5+Nl/jP^g)7\(2Y2"T%XB#R/N1p=a2o %e8YL,fa_1:CRkCjTeTGj;W][8^-jWQ\@=D^P`SND@#RtR0W$%Y;fHl!]E2D-(GE?R!m&+J.6I$\[Y+;0O?Bs-gN#6B3l/:hMTP3* %)#]hR/3AP1:lhYQS,28<*iB7u:9D(dS>mE7=#]QeP&cK';#m[rU$hU7V(j]'OQt?aQ!ksgV_H>aB)J%D %G*^[tC_`flZHjH6TT0&PQ))e7Xi6Ok@?%L1f[+_>Z12BcU!cIG$8u'0\Y*LXHGQC.rPHVeL/<-K\hF]jg1H>EI_a%J<)HPNJc[_X %Y,Z?L]1j7mV)[GOY_=8Z-s*?HNE!R;g^-2u@[SM7bZLojr#C+H5dlCRD'VX^0)K_`V:s;9c"5oiK3iIa.c %-p.8B),5Z%k/,prgd(_[h&B-sA37.D*Z$'pnfWFeP--4=;g+KnSE8_C@LWe_Tuai#!'Ia"']^P;jlsu%>8!Z/=7OF3HpGDJ*)`7B %Xd\:28!eNj46kO-9rH&6ou_Lc2E%7!:cu=Lp]_D4eYQZhTRaY6g-^soLf2f=BH?bko^ofj/p`@nL/RUb7'KPd.hGn6B)@TGp/NVM %`R5XfU;B?_MT?@M+-37(?1h,CK;e5-77>pX\r,AjK*J7u7*Gr0Xh)1mnB4/)QmX6Ee6;J'2cWZ*RGA":$r!t.7inG=/Ktu#]l!/Y %M2<]Wg$s)Pb`YYA,]PKkX<^#tqDDJLZNHohU.(qCXDMh#uTK&1:(D(GH.[Wi_ %J@R1eW_r-/,fB:`,eL;kp0iE"T8VSm1oUP>020$875l[""PG#COc\%]*dgug&ta^i;r&!Mg:3o5A<#Z)Y']*OI8]X"F`.n%,^XhO %JGfAS_$/a#2J^Z):mr)mAH?YE%?IDTWYlb5]'#PO>,X*QR?f_sTdF*-1sL3;:h1tJ2i5>I-kg8?i3X?H>[a1+E=WA-,>g,(?CR7- %9-Y[@Y2?+4NI1\@.U3+^Y\4tE!m)+OOiNJEQpru?qrG=^E>btK!KP2l@]NA3l %8CS7;+L#5hY+fGPgH@W2k2k[:SpQE#E/)Z[Zm+.Xm=L$>jsmh+e5W.pi76(sgInYr9.nR_3,'XD[mXtN.*\m^nD`CZbKN473Xd7> %&;$?jZ%E0!O-?u@\ejJ4d#AA/IG["#`url+$E%#Pf9ouG;YWM]I=RPPYnaYMMD;Br"=\RQqOt0.#0E45eHD;/"mjfD5"sL%"3UmF %)Bl384sQ;gfVDOU5qN@Hl%a/+f96NI2PCp9"L?qu"i2I#r$mJ_WX*57oOd#50>T81UoS %GM5d5Tj*YOm!](M-5uE;$D3HgHm'6C0\,^.pUQF_JO5>&Xiak59=Cg*kJ&4%V;'L/qi8((+EL)P`30Jd5#; %HNqA#FTGN=fBFU0l55(-0WW0,He@%4D7&*cDS*\cd"(35Q(C%R/_^7a'A5XG8K+OF?/E;"kNn3-Kak\5NK`:A%P8T+XU[+M9J4:@ %8/++H;7=J&IIbWD[qBBF\Z60`Y;3Q;\0f_q.G9E.dFM5=Trs-toP\:u`G@B"F`"8.jGM&SKJTOn6c1eBY0Fjkp?+lJ;/9"8,E6P&X:'laapfU2 %SXi6X`LtKn_3876ZpaISTVUJ[4@itSO%"ENS!ac9FE1I?DG6>O^65/Y;H6hAo3qK0VC %Lr_s92A+A\!QcUdV):nLd$CAn#,:m1PB**0SBBKc(?SQ)0tlLSP)\>/f#%Xm %6+KEY9,?r_:JJ"uLbN`hiq*:Y^Ur,FPX43.]2RVnf!4u/P-W.=>@Zof/Wi"kj1nUDe0Q00$DW@S<;S/K^?&b+SWc(Pm=HN>m)%0g %SAst\"q0"XYoD2:5`FlTI@O%9kLfj(Y9$j1YZ8(?Ribmd=5F&3=r"n;fRqVtRUm2GMk&T'&k?YY@HCXhJ3:k1Fm+.[cVS]2;"8A1 %+R3$nScXmsgIMMV3%,SXNQjkmL0Gj]qhAdljXQOP*F$ZeX?oJO\;j1G5W#)-"0$+KGaso`QL<,e[o,!n'pZ3T%k-%C %1iIMfSDsU9`S0c<)J\Ea;E6I#<#S?tjRq00dM9#uN,D/s)2!a%Bfr//jDlfJLcGPdE]]EcVHbK@>#\%P%Q9]UaVqg^-If6YoE?pP %,Fr/??M+dWV*YnPR^#,dap%9T.tN!N>;8d\Ke1kAcRK/SQ6h^Y\1GUDi3LjX&%ePH\G=+lg0sEk/a%/S09f`>\Yus# %fuV5&i^$O(g_u4gMm60q3I`hG%'_4!g8^=e\$PP0$rb-A=\)@OI_#rMU1j_'`n,\6nBFjVkZ9\F)Hh237qlBEmiGdG4q&a1NFadA %*e=u.GF5N$^qR4bHR!/>a",P%"^VTATNX3=L$UP69?T6MD'BC_!Dp %8L"ng?qt;Mfb'^F^/UYK$`E8D_=3I]"ju@C76]eLa?4/:A<97de;BH!iRBBLb;jlA$Wfr0TL^9B%t5\GaU9uXh0q_[\Z]jQC$Bn& %A3\iOEfb^ij^%]0<>Va&R=S:I%9+\lfkP>Fc^:d>!].Q\r3]A<4M0d[=f_55CAuir00l1:aH[PWW'ZJMb:\Xn;:=$u)*)QcSSe6+ %/!ndg:TE0'&e_'pB%GN:'Z.qF]fN&&!&&!KS5b*$&<[u]g'/(DMEapAgs!4`r5*Q.Qk<'=J#eQBkW.29MdnP%F"jY80(W6!>Th9a %>=BJ9Cu-a;BdH^E)sA;"cqGX"bVbINZo23B#=0GFk)44.Kdl[5p6W->0bEUOoiPHGan9Tn[$p&KHcGK+Ad5B_qNQ>ZFq %,a4p"/@_8rcL"*9B"Fmp%=7=R*U$\ON[ft"J&Xs=WMWKkWp1SE>ENE4dVNEaMj8q86`LbUk?4VkN^ %'8P]9"n,7D5+>0F3(6.9Yhjir[7Z3EZaaH0)l9,bSiL%'(M;\PU'sdJZImoHUmY8lG7:2'+EM`W;719mXbP$NGdYf`83n1TNgeE^ %WNg'/.`D3lfbl5o7U@Qs]-s55NHp*Jo`Eo&'gUg(ppj/LOZ"CGNdA?L0Z"3`1QBt[Q"e^BUl/RQ2$H+$>hJrbWL:t$cmt0elkP\/;Y-U@-05Ol=$i)EZ0\_H6m.b,+aYc`6uAa$U:=LJ8o:P-X1MZV$^FCV %)Gr:f!%n)A.mXpPCKlqDiIrhqf1/SCS`++Y'?Ci'KbYGj$c1iSXaAVSJE4"6TaNd\&d\<8op[r,Nq[Ns[n%W1",cO3Zoq2fb2=BMd%e;-nj<\/M?Y\c %l"R`,=rc1.9X^6A"#Z^WN(GGaG,3:-BNPEec4#QZ3QAY6NOb?l#&5ldIF)Q32:K%`C;7PS4+!*,j3fSD0pW_MkPB>bRX>QbI %B_8SeOaUp<7LFY'jt&O=JhOg6>/thh+C'.io/n6E(UbZkZOn@(m_;4`aZe,Ctt\4Ofk%&j9XPA+OjCM!&Ze- %d"&M_MQo+>/W'PVNO!5sj[*M+Fg(8!$])J$bu_+BR%1M"651mr)@=MaarY>'35&GR_J;$O?4#54oXg+>Q]/21];inL@\4/A/A^/lsT*gYeGS)LrO%`ld;Xi4b/W.uX#'a=&<]VaH3-gRV72%X?Sb$-12kSf(r\LnQ]PK,1=AY'd5[Y^/]oWDi1\ %M4,L`_K#!3]:a+5gc_UV(es`r:G1\,:$T+W>6tq]nt"&E-B6(QbdU"IeCP %#3)YeYZ-C\&#MjQfb]0/L!ciJs+6%JHU!/3a9RNc'4UW2lEq]i"Y55KY4CCLbT,+?+F;R.X/s5DDjIaf0,*/@D=p3ui3pI`f5?Gu %e[$>.J`B^3L:>-#r*[R&\Q*LKl/`;eGd:Ba5j"27ACf]._C[#r>Is#W>,]2gl^0Y3M.bWm8p,_?^.PE8,7IM%'Wfd-<87"bh*2.e %5*RFdHZ-='Z^"p^M)(5#hNS[[$^k%_Diikio-q;I'/dpbq/$CTWC*;1C\V`!>je+Kj_Zqi7K(,=2Fj2#V<`)j&2)Sqee/3PSg@sd %lA=U:AMD:@duh[E"ho5Q^kuYV)[gG;phhj,#ok7^K<,,.A;pbUOaEL$NR$!(P3A/Kj:72T&L];rcd::[XgE9UCDk:?+t+C_mh*oG %P8q?NXZEh)mt^G2KQDBk-1%3Tj^VX+85%(YokbQVVC^fFnF%Z5[lQj*82cpj,:)>5[V%p_CV %.eB--7:n@d^>-g=]"X`=Xdg#k>)C=g>CLQW&'q2n.N$++9SAJ5ob`f25=o't!ZdT(JPK"oetr2-4R %7GGt\@#]T=jI7k'>.j./RSG1G^p&oVH:bn/W%An;b`M&u/K59?d)3EU':bgKLShg6)f0``Rppr+[PG9pj_BnQH6W/5n6XUflkM?L %@Zjit5[Aq7f2kpFHRI941L,VR#?'?!mp-dJXZbHMjTU`ch"f?OED=R#5?:5B!3%YY]^Tee,e!&%]>J4N>.2euHG;Qi0'k]$_K9Nl %0JWrBe;?h7F028b;EsQBZr&?\3<>]NW:mo&:LR?$L.A_;9h$^'57,m+KhB(u[`E\^-+P_^6(Sih8M1$D50AJ"FbZK#+]Te-,Xaii %U)Ud2*Yad)S__!#2!dQd0!I(.CQ3\UpKJG8mp>t$CJW@3k#Ue:!u/qtYejj^(Js'7NMQ?u*9ZYTBa1;%3X+sLJo1d9@7*CQ6-GWD %QV?)A8K\+(NI)E?on_:tgV`1MQ@ug:.]qOdin?qWA[0C(_6h\+XT4,>QBcPZ,fQQ]0>t %)VX2\2K+P2"X.SM&a^D*%f_W2ZA.^,_+:SB&.#@l7BD>N@b6liCjS9tWghUi88oIaK'(4)dYKsU1PqR:i`I5'YUWm="LJk4g/q-A %*HG]2DG`MfWK7cZl=luZ:]YDYSkBVJY0=Ks7:!]Aom4JZ&<(_p@-L;F@5uWtKR#n*@ZN%5^:'@30=BLhpYt(HH#hFW+"-0eZ2"R, %.0]CQ2Qfs=f&P>khH\IHFs4GU)ra9>aC>L[6ttEJ7.krmm"+FoO_hPe%%u`fCD#bFX4dGdXfP-);'Yj[gBH=-RVu@G:qJC#-nCH) %)/!JoN(b_8R3p^,4dsuDUD?K[-CdFV6>#pNmGKUYqo)8"bu9i7`b1sDU;X[oQn^7"I#g]geEoqle01V=KK[^R_W:5`5:4#QQ:b3M %=^R[R*AXb*pfF_E[q[[65@[Enl4A(Bh(bS8kIbCGlS%24^iWUfg[nFKb'9/4C<%S@S.'$LkCjSJ.^.VE[H@Kt %^G*,)[j]gNhln`p;lmBCTTOB0E@5(!LU%m(GR[D*.?0Nd90&sR#=IH#l9Gn`0BAZFG.QqW %GACNGM8"&/JHGH0r9,ERS.8Ut_?>B+X3&<8P-4n%I95s(K.LS=(go^1;CU@k4*+Tjl5=>XJn1/_5\(@+`Gbn%S&66D6,Sph5qF>0 %(&lNZ+[.\.`;BiRnN<7glI89-ab3WQL9d/_GFT89XIm(`gJG6lo+tpP9`e&Bhtq(6,:/G7[HJmXVN[bg-Ji"`d]V>l^QSWkf:RQe %rBG[Xs8CR,]`7Q7T5OWMh&CkBS+kgtI.I)O@^ruKk2G'5IX(nCc!UJJjgT/H*kqI8mADoCrj_8Y*r/]\Rs6H$/J,f5S %pV6[cqfcD^J,>;aI/!MV^\dT"\)2&sqi=D-J,#)n^\[n)%tFN>*WH)*Du]J&HaJ1k?bZNgT>//:rV'ZJpZ[TToD.q60E1h-LZ9Jk %4oT6sM>Eht^3l&\KM99%^\I''rh#7$q;96#^YY3EJ%GFts"iGX,oupOL"XKr]/5O2a./0F(Ur=-+@8I"1@CsK2Cg5\k^HEI`7)?! %rS-."hr=TV"(%Gh^"O$tGFNUBk4(YfVhU(:V5MrhJ;HMEX_AC]_nNXZ'Tr(7duUk %IfKDjLUUXHr;Z,s#IIN#Q8\Yihh:Eh)5jCE=1>;K^jn0iG]HW1,Y:`@6'0il"Zag3]">7)5I/lX_3_$/P=!+(uAI %#c5UK;cC7N+o6anh$h50"r`(1&rAq\6sM5A)59//9#\e5$Nc5BIT#S)%I(hf&7DTF+GC'i8(5R(,gnGY."Vq]q,&8$$-Yl(KnIE" %!/rXDV-Q.,2t<[]';Jj-Ko%O-'*F)pEEbiO@=mSZ_]Sr"7S16V>)3Yb)6c\C.APA1ri^KRQq.I>_BZ(f<.p]85eB*L1*pV4fdXb% %Ro/,bLCaFP5gP#^SmVs^YGJ39Rj$7QRaV6$TX;)KK#3E6i')h*4sFd):C5ZcMiFN %.0(eqh)d1[)penp=OB5>qA3jDjhR)FjWW%mW>p<8AV)$qdq:[t0_?e%?h4(*L&sn1'FfofP?'-.LQ_$AJUJU/J63"s!MS'3)2(IX %RgkOVA-*3"YAjWd6r>Jg<,K8ZjXZRgR9IjGNG:um %*R7_?Q&A&8(3:6,0mSQs8A3b%Z;!l"hA^FiRJ\\^Wsr#OSk&#r'+rl#Xs)EAVAL>9k\G4^5[E;ErKsAg\1E<,+=o'B&Lp@d9ekt4 %/9&?G]:7W+QuBco#VB0b.olp7#l&!Y\he08=b*%l%1aoW9cALfL@#*CJ8tAZKWU0(CK71s4[_l\M?+@;Ss,kl*YGjE(%h,RF%a#N %dT[UC%IO_N`8)KRai1uL7ghc>f7Or62B73^T-C@G]`Qai,=<8Vor(uB@k($_@j61s=s"6qV&/IfI"E:V)Z^tFVi;tQP)>72&CeOZ %,8B;+oYDL[=[d/f_nfa=a6/8?_d/E]M@c3F_%2.ONQOWhh%(bB8D"T-h>hs`b5)+-2/n/WSH:c'0+?:2)Xp-@1F %M1IBSVk"_Kgh"%!KS:dqJ6d>oP!'YnN&8p57l^OTq34`41IHcS`,*juE_L6!CIk0&BsoYLis%g)15=Qm2Qk_fL'(0RbU4rZ\/S#8 %`SD56jtSPZjD2h7Qu&7-.Y=#kS)T_W=kD;Ul,ng,BIZEWLW$[D=njPp-[$!$Ce]k8+pZl6F$8H!:YoqDjW:[\W==^Z>T>r2mp=oS %;_+o^%OcEDoL[dP62qL;9l^ajH4qBfo8X4TSe37'=$Y)%^6/1c%_Ak!3qBuq3kB+4]7>'\Jmuu9c]Y`/%!=dgXn0Ld`6noS(0Dr] %<3`LS3TTgS:%orq#QJOW$q&gT'"_g'4dt#E_^&[U]CV,_S7B2+:^A`Xl$ND+80OGl'7u=u4C`"1JsF^)E:Nub*I.b;3<^\Vl(^;e %#\De1iqrn-5^893=ou%4`YJHBBb_=;'cMFtO)lo0c.nUKZQTZi/Pe!OY0.`:Tc[W49e$=9F$_FK@NCh8 %=4dVRYY!91RT8@PQmNL?-F+ae2:$S>)ogKcC8J<_e5]e_G.!MjGXY`X)j`:NiJPDj]IBm3)Cg1gAo %U]?Z\$6LMAiapD34-qfKPAL=PB;W.C`\o/q!WF8hAJU+EPb]>sn6G&E^dJR(GsZ['P[n5]CU_=m@@J,kI)unNgEVcJ<-I@cgKAEk %`8)ch/+?(3N\*U1\(P!n-(h>Wl$NZ7;gUaUcJhDnDA;&OgN.K%h.TER*t2B1/_Kt&U%r=nIt5,g%VJk4"a2e1:mdadhD7G*$=&3^OKEfE#W9LM\5t"`=$J %:*^`-g]EmcrP[N``)p6LX$J21\9G4maF,E'-KtRkF`\TuAFHeQS?s!5^Mmf#"odF6J#@NX@Bosn&)n[IQ\.;mNL5nXM9R:\a`?&3[+=9tj4N18clDp4[PT\d8jO`SqA8>TmTAB)ZgkDo`$afSIV3Bjar\\1oZ?N;@8L&'jufs"$DEM/$b7Tn_p=W7 %DV@&R]VN`qUQ9/R#scHL+IsM_M:u0+W?l*'+;Qe6NZUS4CGQQKK3/,-jt,aY9$VC)^s[G\I#'s8-U)TDf2cp`Q&[P8;H=8`D>%#e %[DZ=R>]JK?TU^2Z)PUJaM%rN@"L[noGn.6I9_fP>l^lJA%,CV-$2OFK_s=;@=g3;d?[[@^V2]-H16nVH[g7b'84J@oYg=Ae(f_[e %)X2;hVLFXr@,8@;d_-QK+ZX!h6V4h*'8DZjnnNoBEX-B7P&_Tk$mk"MedNkdPh$6hjhac[Y0,_*)7msJXpa;_5C2+6<_dk`fN.Ec %bq)rAr.GZc>lU87Y&mGYG5gVDc$E9\8[1obeYq]0*#]1NYn.OH0:SB9OgI$\6co.'p[@qG!'uhK'8QCQXD+gq6,MM9] %+\`SRAVEe)2V&5OU2@^]hBfF8=6LK\!g64%@]*"#K;*JZ'8;<%(5@?n3p(56L$B/0eq %PrQS,!)r0m7tXjAK-:\['gG19?sDpC8HX4:j!b9=M]ZME^bKuuV*W>T!LX-_dgjiKn,M&#$Jc_ZP;/6j)^u;.H/]*R+Qq4nR!t<<%gQU$1[c.(_4dck@'Vj-XQDNjuEb?6n0m0qU5.F0,3ut5S %ao^STf_/mco-@=&&FXc`A\epudI""h/IRb%p %V\nANJbUcJ#LDEMj=K?DI$UCT(YV(5%L"!Y-))L`6lV;Z?[=lpQ1U/c)tp7n0r7mua5C%(eb(r'ct.52Bf8Pj'G9i1aZ[L?at[9j %8K]pV$>57501oRYTQKk+e<3p(=\7U2Q7/$`6V%/T&UW_3#`#'e=mtYWp?B90KBmH5.a<-@>\pb9&J`*/XPINeK=WOCD?\#c>>EF$PFlg0@%?dhOTn*[fCe=Ic:C\qOQQ%Yq_WXKbM! %"@4J-Ae%5fZZA';rBe/REWo,7CEkY]S<=:o8aDcI?WtmS*J8*iemf-;R:o:-V/=f@(%,t9km+"6psP7;D1!XDi$j&JNU28Xm+*?t %@i.sTdZQRM*-*hRq@9IJN#5=Dh#bdP",iG\ZMNkb,)%,WNff)0bf%0DIB\R+Y%jB]ZYfF'q:rJ[S3Su-)B5DVtXDepu:Mk+hLb-Cs/INT3C?Z:5%I2*4 %J8T2!K45K/dkMPB^a0k&IK&d'U\Ol8GHAQ/!`>0sQ0$#lnKQ12%&`mfY,]dZsm*A9AFujB45GO'X5' %6BfsHX!iuV64Nde@TkA"`L6eEU>#mG:UWB[&-SMtKT;`rS"hEb=TgM6ggE[lM1N?oOPg>tVE1!XbI!_;JH+m"5^56JGXT_""?armk$H4V+o;7U0l+)Hn/TJ.CW=CafUE3*_2HC>.\VrUYi,'McY1 %M+P'3?mU=Z(h_!H4Sm<9/+Ltg7_>3Ii&j*'P&/'\\D&KKHoFAlT2#^#Gr*oF5RsL23EYerR,1g!cA"Y %:Gf%H,_3/>:KBid@XnPTos<6?#<\i?ou`B5U1:kq,c2pT,RSr=&m\M=7tjA!*:%rkoW5)LPUM@"m+29V=1a;$OB)moOpPg'Vab/e %6Gti@&B.1r)qk!3&eb"hg6;\Bp_'c+DGO[;\%VQ)QB&,mP/YV#`Qa4m9iONaV+ED`ai,KUZ8ELYX5e1OaeTnHmj=TUqDoetLlP`) %Ur%-RHE4S#WM)/ADB.**!J'!"MhbaULCKWelS-O6)L[+VFVOnQ_W5jI'C39'V"7j""uL"_NA`hc%Z\b!=U7X^8`>"r75JG)EPJ09 %#O.N3H7DKkl%3RFOmADIBIdBcEC_]&e>ECn#rW?Mq\$\0Zdf[B@V+4!2n@tgm6KWZ2e?6W<'q>haOo2bb$V[P9sTia:H-f64\JU5 %`gfZBJ8PX*r'>>r]kjTYV0Un"@,rj&u]S",RRV&YpVDO<3PamE[u'#BNN:Q(qa>pkV,AAcHS=09Mu920.[4G.1:bA'D4m<=9?KS %Ba5YNCt`kHTIZih!q#V)A1@(7MZ,X"o#fCd@#>_l=@M7`@,#(6*%;=)qB6;I)Bcb]:0&Vj\mXg$1Dkm5HAROC4ZA,pKbGtr\=F+j %i-.f31bo+IbV#,jpn#FKLd['>UrcA+4RfUH'JT_mKi92-H)ruM!Yk*bC%1]?@!tl6A(3QNMZd#tlOg=r:\$k2&p(*G %H58$Z4%/okAfU^Q[K8/#r+%U(gb\V37gEY1O&b-O)C""'>s2!]3e8lu"j<)C$"m,.CW9PrX(W`])3bY4"]\!LKV0m4hTlR"5Y;.& %?8%T^PPj)\^hqRn87^h$aC,4;beds^[r!H-M0*u)CCo]`?mS/J7346c6a'uB"\jQI:n:eKb"dg/_W<"Z!(%"5kpDKGedu==<@,W3 %.U`[#94f=JoieF/,pgA$A/Y_AJM]BC[LEcOXG`9C%D@eME+7UU1&sZe:DC;F)%Ird'>dI'.\[9TjC.s:q!8OcBaJCX&Fi45TFtg\ %>$r$9h6Su>#S'uX!oXBm/TWTtcGAGW6p3;oo7WGJQ$X?hUIf][nml>Z]VC&OD^AKG4Q-+ZZ$Q3@tAaNpPa^FM1BK(Pd<_H:]S)3 %4drH@VS`lMk0kTplqi;'/VYd2/f0s*Vo;42A+k"b+?Yl2oUnks4\s";![$fS1(/f3p(ZpUGlrEq9ViSep%u&!R`K]dBQ&%76,N2_[3hS0r:UW=uLRh>8$CuVQ=\\9KYtk_6^[fHgH8;A0MNc %i>W+TmV=j7PP*lSe/_-(+cs?snQP-6P\r_j#V$'bA/@lu+b9c$B(?YO6]ZeR%VifcR%e&o(0MJ0FMj=_2hjthNq?dO:LYLb,Q.l/ %.j]s]*3)Q4b;cFT;8\@,kkL+;I(n=IQ5AI6:O&[D"VM,U160a@4J'Xsjj,j&oJp-#^DYd>.IX`A0P7^.UfAWLC`mX[iuXK#h*Cp] %/`96o"@>`Acn'a^PcKZWa9>]LT`eOK4suEN`l+Gf'1i62(/?(YNUV<*-5.)tS4+j4+REB;4lV_PkXE?N&3p]8#a=\2Doq6*5_2*K %U*Y_2><6J`KuT"k_IX^b@2dS\0UUCKjXEN\=&g*&!)I\?TloS@/3IWiR%0PB80!@O(`50:'F`$)(nkY82DJcf)866&qSX*j`Z:uj %ZQjMKRgIjp9K,ImBKWS3A$`Tm-%$.nMLsb+4"Z@Y2:B;S'%"Q4ZU.[V5Z4/7NU1gM,T\`i-rWd-^8FoYd?6#cORQ/dh$?AQTh@b0 %/ral*d52NWUAB+Z)[!2hC6<\nVr2:%q,%mWd&LV>$IkP4DtYKYH"SK)DS.P/rAY)66C)q%_FI$1 %.L91>pj'tES:\hM;Ti,m-)uua8lV9d%DN1\A!uI&_U?9l10$So7`KIV&7lh=91VKg,>:<5Y#0&*&HV-9(+(CUlCh3U_g?.],W[j5 %j%@aEIB`9%Ra/&a`glQZjR@7#ZcFN-_2@i\ViQ.H`nE-Dhgj.tNJEX/8Vj84Z-WF48*Y*\fec(lR(j1/RW]C4p?Ag8[uGV=KIbae3!58i'7P0;KslASn1;kP,BJC]"Q0CoV&_\A_@UBkgC;MWN:-B.\1_=rU:gOn+F*-6 %d$.&*@43^&b+<>:]HfP$CQbS(_g/Hl6AZ(7/g"qN!FY#Fp:ujP`TQ_1Ot"H4J:^S\1LR@"p(;F8Ncr^TZtO?!UtZgW1FO@ %$4\.8m?!!`7J)(8#002r:insEp;d@'ZYp8r<`<_,=\KsrROVR*&kuUh@YniC&/l_Uh#L#Q %?\SVO)_5>=)]/g;mg&']7BukD*XbIB%YqSe#[o-HIV:(O,%>MX*4)f*q)6kZ(7I!(T]2[N$pEa=:E9Y8b8c0G3K!jg-Skh<+\NSb %M'5>`0pOZnB0DjUdIMK8=.ZceLB3BZ%gh[bVZi2U=E$Y/"S3old]O/popWkU"fu+m(g!b++JdXM7"i=@I"/f:@-tE)g3.JsM!(HE %S_3\aH6O&/;9]m;CLPplADikX6G3Sc_($ZKTL5C$h8c7,`(%a?qtB^-'f2fk=h#'50Ip*],['&M#'<[O'2QLb_bu.<17))G %?,oJ/bebbQCPd>P0bd?p3&j:]HRiXSg'e`nQ[B@US)Ji4J?5O9@A'>*MFiL*@/704AH8#lkb->I>sO)AQ"Y>J@A+uI!6uSbdc:`l %?!%)%]U85Qi'b#/8noYUhtQ#*JP)0JClpobs1?)HIC:?ha"6W'E-7- %OqnbN/Cc/\d0g-%W3?'leLL;HWP;eZrNBT!?,,IDrqA)@Y(-;g?g_5"o#p^]IsLO7?iO$mqK&?^J,"_(p%rFiIcqSo=2AA-MA?&# %?M/:-RWYV7J,CU!og$pY/Qt%\o@P_dY30T/mFnC:eO%$dWu`nNWqW08frmR5$bXZ"fQ6iOTq,ut!q1H2Eq\6$-4K?Bj6<39>u"GX %n^Ai"h7if>5PqK&WgGqYg<9j*Ref8GX&;COoc?>=qWP)s7+V_%(d*)`U]GCur:uiO_+G_<:7XF$Vt/k4qllEtNYT_SJ2hY@[Hfd+;Hi0.]I'e8+FH,Hp`jE]oQr`"gNhk)ZrUT45H %W7IVjV8@N7q@C@;M(a5*H,4lYrdO(ml?0,?r0KP7rTE)9UmKnU'ef!rM>cZo2/?96nhf&!nR$d'q0g"\Z.=,!Rg,7%,M0Zt:ds9iDkA[[rY>1!bb%,QAZKHi<@V"Uh5GTl(/5n#U0-Q=p2oPf[B"uTn?UpjV=Sq3b%a+dn_,Ws0,ge,-eDWJsKbCSHrQ@HS3s`J,eYbIFE7hX5_lL %TF#P/rG#B6[!DZ(L')$?q.O29XZVZbI\g7P-/Mh2o@U$p)eO<8e8([l1M=`=;73PK?8X^E0SO97T"8IcdQ)Lg>lSo;VCYlTI.;m8Q1MBJIe.pBc78A8dVgbn\p8c)MD=?Od'KieD`\p2N8(6h]Mg-HgLIF1 %Sb0b`[EI_C]#.[eIJ$#ljASQL+@,4f26GM+//m3!!g7eRKP2^D[&^jT[A&#]iK'NMF[C`p<;2otKmC %Z$65Ne^Lj3Nj4>s5R-[r-"EdeT1(?pn+q3F2e?L5+6O'"8[Y8gY)XH&goMt[),Ru@'AB\qArqb:pVNI'*_;qJ1Eg,u@CQIBd#G"D?jqbf.Q1U3Nf %3pDQ4f>VA_Zi/]4ncYu8aL+OAe%9Y(*F1n+Q0L@_p\Nh6$b(&HffYt>K7a=H!qCaoA%D3dk9s1Jdq3M?dEaSBCNu,!#ZXTS$n>@1b?2M>1@^jX#TA/KGfnB5Ta]IT\^@9t4krIk)e!1ueo)#B2(\c-f %W:Ko[_bWFu:D7\1;Z*pl%:0KCa1"+RgOAi3I^/!N<)e2@ZbtDF]5hgq4Leg+^\ZT,ap_R(oEKE*O7b-sOPrBmfH:(^Kj)`*AQ@/> %:rUMa'tsLqWQeA2$a]g%`\b\8bq:*o\p.>CGZi_/f,T_*FmJ\^l-7^opon,2\iJQjBcO %dl3o1PoI?<*oHe@W$V_^$OUq9]+orKWsGW,KWmVPFu.S'Q$_oJY)c(0Ng><<_q%'l %7NZm%p>IBmW;)rp2&j$"7]qY&UEe!n/b1KmKOiqSMO]5k*Ig@aFO\cl$ohu6;O4Ls((Dj]CMD>?DL %[9BqOqJ)-p`LQd1#7c0_X`bcRFF8#I3U#E7`/c]p_GtF5@:`kkF?/G@S>l?26mIH %*7h`I.FkjAL9;.b?eN3$B/)q*d=)"S1W`+FIQt>c@f*0bL@S;h)ckW=1p]>N[8"t2SK!j@kM$6b[-B_fbhHE02e:@CDn`jL4FI/2 %bPI+\oVnpsT,kG+p9&(H=ZEWOeK,*AT;R?`HTrBKs!c+"AadJ64)3Hcf;RGP`:u%h>*W#]ek./NLMhM,d.70LEu+((GLNgtBeR*. %n)M6L;mjVnaWofbl(*b/cmn?moboa$DsO]T?[SfhRmY1k*7:mK1,5Bh\7jc2Gh7fRpoYuZ%pH:!eFhn7XrL\1Ug0ofhbSL1ah;k6 %j,DRAMM:A<+#.AP\(WS&hJ(VhoYe$5>A1X"RZk1M(%n;FV2@)Lp<``B)%opmm5&oq>B!G(M %,F(:u)*_P3a%E?D?Q$.1)P>FOl;rj@MDQ3aCsSM;a1leB`LtYY<9<)>E"7l1S#%IMnX!NE\?'&EX*LDfI-r#Z#co"G:J=,no0JSPR$>>N^4TCS_n.G-cCYJ+Ja/VLVs_8k'%DLiZm!bO.AIK@+2VOp1T_]@H0lPW\5==m.'?7f5LVlD5t=O7s!4j)Z[6i3I[\\LljA;V@?Xi %^:=ALNDE[ETC+pfjmorlhsT=,s2[3hE,H_$e%]SUcB=q]EXbE$porp3Yg;0K %NZ7]7G>!9j %V_2/drVMM^@s?_BG2_eAX'4619k.mb2i8rr_6%(%="F,N),(id>s5T.p&#\k-dS1[nRl3Hhgb9RCROWif9qWei:nq]I`.J-K:9Eo %C414cA$qsI0-&`]C]!grh]+V%lMQo(__(hEl6XIEPBHasSipXMUU]?Wr4pN`MlrON/tP:Wnaa(,\+@=(];"a?VOuiga1o)kg06EV %d9lQ?b8FOTXOb'ld=.nmjI.t(rD,VIhCX0t=1\DZhd*sC*r3LP*r_s21eoOmIJ:'jb@boLVSDDqVGfCE]Qs66npB1V]7"jtBR%j- %[se/=R2oeqLmP\+rqbX1ZcN/hrV2]3IE$]Fot0\eYFk>sX;b1[[e`O`iV/P=CFAjoqWcg6(X=,U0X[=/m!H&6Zn5q]f>I0-h7Rk/ %_UY-bYFjT00t'jPS`]COE;.D&b]WgLQAO/aG89GMo\7,i08s3"[nWiq^T1?6:Cj=@$N<*1[FnOK8g%rPS %.g(77GMP6aWk[t(.,"'Q"$]j+hEJVeXEKm3Vb-c7Y.g$FEUWgAmGQXAIW*iaeE=Zn=Zrj\HIihTVJ\Tr=!#QU\p\J:]J`s^W&3kI %_`q5caLun5je)t7gtQ\nT(>0J`GV7BCA+-3s7K@BKQTe6[Ye?SY^AiEA^He1\T$`"^Z#;1]5F.k1qQBaH(Ip8/l1;KchXtEM+\T9B*&"pLiUH!K,?gD_c0QV`LG%8+o[7*WkMO"" %k1^=Q8$l%,JI4=S\mXQl$\-^`Gl3EEY$\karlg5:qmNV23dXhuQBdBjKgaCnB?S!Yete!M^>/8' %GDD5!4)sQP&!sG(^N&Sjm>PtsIJ_TTCR:g*I"1(D*LkW^=>FZ&c^%RhjZ?A6;!B<4#EP@fki:d*eu\ah9=1IIGFh!H-N,uRqW*L. %L\C,jZu4DAMs!bR3O&B"*DM!Z?I4>>h]i&f)_!(.7F)$9C8Ot]4-HshVjk('D_4\t@+GTLU/#[.K\h")LbfL;eFSWlE2qB9hs %]h>4'I?d*\pS:$0Zo;%$/-.Xa[L'qgT2`io+Nn0,=2BfW6];"7^k0mq;*a\Q_d>T*SOJ$SaRLLTALL:VgC\fu&E_:iAj@Rd83!?L %0^"@W:'OT"Uocth"t#RU;=k$M77?SeY_8MsWW5V6nk&T$Li^ %%O;/[i/W,-nANn4X]Lc%Pocj5LG?JoLhJ28Q*$3FaaL_;j[C&eGr'V:?]1Poeb$Ob*(!OJPo@M,j?0`@`-SbIqZbq_;3kT<*@$p4(rOl43d/Rh+'![M4_,_%&Ch09]J_"j^T %P>kJe8]f..@ZtQ7/='q1E!k(&Gckm.AVlO#JS,%*ODKQr'J_`c:fNgJCdd=n"F_'5Ad*o,&+u*[D@5AU3F#Z*AWD/2TQT8IC,H9I %f`JQcJ3D=8,pLhQpbhJqfQ(6cc0+$e[C]Yd]'cKore!GF;O(((-_>-MeiP;W4Sfkj9[\"<:sq %-#uZ<4L8uu10BJKMFCc"9[&dC^]bS\08N]G[:tYQ9Pr9e"_DQ*jT8P[(C&>+a^>.3)KFQ5'a7.[R)FnTPsoO64*DN;0JLJ>KPX[P^D:)(n %G_q+'5k]q+KXQs>@m>r5-f9XT4-r_A\9,4tcliiBoE;/&H3IQK5\MY4S@icnFM&Lks/nIE:,K``%FF26l`]aLM0(03h6))7TmQR+ %aXHM=pUXogrd(!Eh\<[PFD,fXJ`/K_%=J-2Su=G[C6-"PAhlP)mep!?mXCXKeTba+Uk\X9`/D=l://UmmrC9*5HF7dL$TWE2g&fa %]AKJmbsi5HG5&W@X6uXPj\ik>hlQVRRJL6F%C#"#Pcb:>+LYJ32p %]q\1M^it$7meDqm[:R^'`* %cPF!A59>d(%^]BMrJOOLnaEptkMG(FSq$";k3L@359;HhH_SBaD>QNbJ:Dds))^kZqP!V<8'Yre"YQ_X]a-7T\`*GAdkKdTk,N4- %5CA"K;gulu[r1Ts$@b7ANii<]@IS1d5B1MY)ji>B@d]6#pjLkFIO-ps]6Hgh[F"j^+r&2G5C.cOBCYFoCZ2>g&q:FNRek#.0kikM %q(KZa4RB]'Hh"]Wgm=M"HZTDg=o#rWEcZ_4.%Kq7eiqB\[c)/Uf+n8,@54W2u&b;Th`<,?di`;8p:%D[/g,,I75tD%So^)=A.$)D[*%9m5EhF %^&:qi\EhZtDt;[p2/WF*pY$(c*Vs._A#YM_/"5b62`LKT+rJX7a*t-.h=PH[p!=aQVd,eHVPWu?,#$C\"eLOon,&W#HiO42Uu9]Q/#=0@9TZgHHpJ!lSa$$<&>tEhk%d,STqVae3turh^^l[ %MeR80&ifN[[q:T$PH+-/WPelt2Kq`hZ9C9tH8Y>VUA`u?hND2BA(80=2,^R.?Vf2Kke(Tg/MhJBY$',,CQ=fIJZDXdm-MG>V7Q") %[se"u%W^?3q,2Tn?E;]ML]99"0Xj9eb11iOI+6,^m"*nd://.QN_^X`+\$=hAY4f,)GQUGJj4LN&Yl(tlLd$iFtahYMhG=3i[5SW %QG%=^HpGq5T/]Fe_-\-B]E(7eh8!"7254%:m>J`&q;UTJBWJ:WhNl>hjT38gVaPHZHI^AAG>Z!lX3#nXZfh1Mm8HN8q/":eg!aA`m`C7P>7!]^JDV)3XT8u@jfPB=eL@pRZ2U)7]Pi&nH"]R4H(o(9l7geNRondkh65$)l4I6+5\L>l:2-D@jL6V01U/a %T#t5_>(3)lK@CQ5k![\JQkP<_p&2%YocMr"]C?_+pKR(Bpdq*6bkp3;))^=/Ki'\AAK+'U8k5]g"PF3k0HdNCU&cQ1a2jh+?R1-?\*=SC*1laQ98 %ZY-JOp?^YDD>g3f4ko+1l/S7:GOF=o(L929GA`r[AGJZs^H8Q-:?rfiXd=u!D*$C[?.(\EkJ+.@2dchRWC!nZM,D2"%2)P1Hmo6e %U#;/shD-)8>B!:0!X.Rq!s)@/rPBZt9UZ\[Hhef3o@Z._iR'Z322S=SoIWa96SkQt*dB5l<8K/=RUKJI$o((=XZUg %/PBt]E,pZ6=t0B&HseVG$d`fZ$5Z%`'0i6n,kN)%eX#39inLb+c:>Dc!6D"qqUMaJ]$quH)mQ]Tp+sTsO %[A9%eM3S/0(_Sa4Mb#"J!/)C)Z=$+Lf4Q:W+`WIp2'bHu6<@ToF@/Z`$f![W(33ua0_ie,!dtmqb'jHo\pmPCfjSFoBPPj9eSX_@ %+S8D7Of5.c)cEgkHAVff&`j?Z-tEnq:^*+k\;sHg.DQu-8ma!4"DTFU;+o@/2G/?))-=O0E("&aeg9Fh^u[9N+&\+"OST<=/M&69,Z8LDbrDSi9b.SJr>M`rToi@]Q0%&p+qA-%KAW_Lk5]j]NSWe]229Z=/;Q9<)5c %M4Wg-.n+lU)@oef8[P#[WUZ05W%&"(__`3djTH'QJ'3`gX<_Am3qGN`,>mc2kQcjqJNtN#UVkeeJQ,YNobrVXFaR(q&TYr@jQA!Q %9CV$^<%%dO@Eb'0oa2CFbidgJQ`=VNTV<@'9W?f8iOi>Dt^ubC71U)'9Eki;N%MHI:Ko:Z!Q[f&B*2Dl@VH=*i_T.e=b/2>^+<]QRfj<+X"Uuc7 %FS+ZW*naFh^=5IX)Hh,&K+GqqML_PEAs&(o2HkV=/1-f6;sh];:%T61*I'3F]Bi"c2%,+L5[UmIqOD6*-83.K7@.WeM]<%:8jLk2 %e99=P$2b$,W`8$g&a82*j_C1)$s[0)qh^=K;PNEu6jq6Lf>FG8#I>_`!!-n&[LhYsU7NR%FUX!bOsT6OROJh+&LfJ(ahHp_<\se6 %>'`C>8jW`:a`(D;O;W^&-0Q4+r%O6592W$OZ"FhI=]K,8qhg*Cu1XW1T/T=C_N<[Ta$k$:(a%#857YUn-]NILG=%?c//?^>.?A/fTSOB?IqW %lPFt;"DC.1LMTl(2m>aFN3Y3P,e,+8-Uo]V$Tom1)\lNn!1\TR#l>ua,;8[,3Gu[;N"Si69+Hf=OR72+OCS)V84Ji7'-M$,F1'@! %L_:p7d%k.(BWa>k`\+9PB8TRHK!$d#6T6-Y7jnC^jJ[qo@cX7OJ]5P9?tU[t%9.n;=D2Y0TIP2"fgf(i,iG %]S*m8!R+!LTk*X?"le42;'[=W=^^GgW-c=(>sZs@j@M)<>Ia9P);Z4jRQi9:hiOEPXr-=:chHfq16*)mU1@C) %*,.q&,%JMS%)2dm`=]r/M7bdAYH?Ugj51K=.pIk7_CeMa]XEIU;JjRcK>T,J)?UcGG*=ctgO012hjR %/]*Td@At%cUaj54Tht73&X)C?$7dCV+j'=:Cd?W*D0Q%Cl3VSmBNEk3#VSM26V8[?>=^#]F-J))9aGEiIF&2>WC5YK%,$ZP"t?m1 %^p"snl6=WN>/BV6mgO3.`Ym,0"Xki)W5i_\($X@!)N'BCJ2ResBO2*814GX)CsYXFLc".Xcgc:-#`0^BqP3!rpW&pJD$C1RcHnu, %@FO7BK54%dYt.:Y9cKemQiZE`#1BNV3!5N0&XSP#Ki#0'Fr5+IQ3(Y0o`upQ_[MN/5UY!]k?>O=&VAH)pp/<.,9nKWfgIQAQp*WX %0:17&JRA1-Zq(3#fU!m<837CL-/,*rZIa2O,otf7V?Z6hdqKZ#-d^YS&b0G:?5S,VeI;qVN[bpNCr-ZL_W_.mIRW!&]aG6_6@qSG %"+G5c.W@]T=;WplM?[RS6!P>Q5gYujH!6?oo+>jsTnZMu([Ynn_S&E1b(*6:K_VV6(=35a"mXj=:M]NX&t#)kg2J"b(0c/u8=^MC %7#S*!Lkgk9qn//bfF(>nQ?8@hb,O4WP5)T>IDW %+lq\@^dgEQ3jI_.Yo/3?iX5M0q(6E"EK_:J*H8)Ha8n+a.NKm2rLaph.5iWDmHL\c?:N+p0-`g% %XQT15=WG:k0u+hG2do2)W;t.hoX3Xh%^iCYp/bem$E-)5VN*+F2lQ^rn3iKk*nZ*Z9J\u!84GEU3jDNb?dVADF.Wt976#lLVJ(bs %#Ye'XK"42fQRb>Y(1M.A`:).MVh=#`DFBCR*8l.slgXs-+R]Vj3&h*_TTGQB92o;<"H^GI*>"k&\9*07of$,:_*;GY@4uKeK[WJ> %(%E>A/0=o5q,K6GSK#)>F[HkQR9I,#UfDnYD,1ZY2I*+b_P'G3DgJMVX3>,-&B%RrQeqQLa^?m?("&*B[_i/tLI)TAS`) %$=CfB_8:'CW!"-H6oH==$I95UqB3%1t="NAr+Z@=/UJ;>t<^Ni.QNf,GU:&YWG9k4`#]4-+g/5YBV^812_:K=m %hPadO'1VV$LcTWcDMXb;VN=3uhHQfLCqsZO-`T>eX]YVPYq#Q_ftY4.Q%3@@%+&dM\i)t1A-u@oh[&Ik1]ID[K2l1*&,lcbsQ$3*Ts/JJ4?4T8a+,,k>?OrPnns$Q(p[tV/[tojP?V_A1W^Y_?oiS3!)/+-[nZjX0a]iF3^R&$`V0hZHaTo[a?V:9# %@,(,n/5+SLpiVg2d9=Lf.8UBIO*cs%e!3?e5G1tVePlkekmunU6iToBX1A8f;lNB[HdjPL'Q]=!T=<`cr;+keRMsu[:I[@q*&_Z$ %:_A$#8ltH&F?NU-TO)U=Z#;>hUUcphL`:U,/qIKaRK['&oFaAc'LFXFcX4Gc>u[iE;JpfYqlN%Pr"2m?MGu2E.r`o$&.mB#`U9!3 %UJb*jqH>Z$les45D3EDd&#E1M^"sqK'31ho.mUu]gid/]^f7=1URX7VOW]^=DN+S'i+(0Dbiu1O@AHM<):1.(C:=&aX])9hV,FFN %oh$@n]Q0VCc\Lndc)M8D3(/f`X#+q7q]j+^"u,/,25>rNR6@3j/fsJ8E %p>W#$^JM\J*jbIZ9.rI1&T8%hF=s#.YRjR.O/;\jerZ*YGL("H+8FklgXL!7p/n218RdoW3!sEN/H_@*^?Z2.b/SfTe?r$mu#OJIa6H^Z^OC6e=O!lo.3&q`eG"1Do/Sl@!@&; %-GsSn(=$_/Q%qW>QW>qqSS?( %.s@JoSU5KHNg@eNj^0O)lG)T6ID98H#1Kq^FlLo: %n\Z^@_)AjDIXZ-\pJco"EM2fp)3n)gMAK#F?E3!"0(#)Q %OhFOjHbD7Xd^Sr?Y=HU0`lDQsY&3Nq8sQ=rVV82:;!a+PVPnaeTQUc!Mt`L)h"Z'Wr\uh4gu>?5,5%Fj89N_3,s8YRZ8N@1COf@H %M!q`1a'1FqkW>bCfoDUQUcL5$s5_n=HJE8'Q6L.AnRq+G+S%QR/sUa2d"#jdP98BJN6U),hAXcfVq]SKa\X)X]!D0l<2WmZlD/I/ %@U-s);5r2JGOi%>CU`)QHh6$IfrMl4[rJGH7YJbLknDC=g&%95*5!eWpU,)*%P;O\C.=KFd64'mp4jSGD#Gt.+So"Y2,':5!2o3i %@sM#l]401'\),e`GP(lkfqhWH+U9c5o&aW9409PVo/[cc\*WRUG+mug,($%sVta.SZ&IJ$:&/j47FnGJ$? %.hqJef[O1d*qg)q%>%'A^^P0DE %mCWATr.n#V%o36!hm<>K7Wt]3q1pdu?AG8Ym8d00Us3!>#fbRsP>^L(G#T%]j^oLDqa8Kkf'\ra(/Lk^1J-\Ep^sU2\'CPspL,#+ %8VPhfn,?.+Bj!3HkuQ9>@XsInqZ7d?X\+?/o_KY3Xtne@1g:8XV:pmP%4F0#sQom1V5HeYhOW4@m)% %m%'G;,SB-Y0APJE&&"HhN8h80Hh(O=)b3*:cB(OqQH@nM)@MAQ[Bk4G%P!_uP>nrpCCadbbJJ8bd`MGL2gdHb9ceRJh^EjJWpPSe %?d1C%X%0V%IWnVtp'j[XQ%d;V7F0Yf2K%9SF6>0eG(4r0oVH=^Dr/-Ujii9uF8E8DGc0a#.c7U]:?O6OFm[nbfALqtf5KHWH,J@4 %;qgE6,(%qB9TsYQ3?>H\B&+tsaoB0LpZSPDFB$8[(+T!L"eMo`h**;]JO7nU2epsO7'!2%RT+-gh7QuAHu#D_H/;H49=q'87Cue% %'tAI(;n+/jVatJ_Sqt,_^F-iAli8&:m(=/&AB.$G:C]DRI:PQa4N>BqVU(W:5TeYl6:$DR(S%T1";8Fi_^EG\%#4htWFmsiVs.;) %]7,SSe1>'3Su5iV'Kfn^JeHp/;(@,?*GoR/%63"*r$2$E0b^q#"B=4G&dH,kQV@?r(sO %$*H?e1!`5"L`md,;%$K^AVOGAh]c<\!9?^Q4[O]&C`1Xli$>XrO-"W`6NEoaKIdQ6NHC1)e%>Z*70qh(k]K.;]-al8;-A& %HYQq2$2V/:$@aca3.h'U`:R_Q<#.R;":$!Zn5XQOH/nt=%RDO(*6EuP0&Vj"D20-nIcr,m!lTkeV\*DCfr+-\jqaW09F;*Y15%/; %87ot$nB$)-!9NR3A5An[.$IcP!16@)1%c-gW13l9CGC0d$OA.3$pYpq'XAPM5!'*gLI:c/"G))-Q2R%%A'_\hgp3G%N=bL_-36+D %]FGO>5%@LoeRP>^JAC?o.%_7\3>ej2fRT))pP0C>ld)ROA(W,)iOO-F4#!an+ %l,$F3bimN&Y"-`*i%74,)FSBjBa-`9;_Yp%_B+,VAeqnoO;UD;N'dUsORFm)c_Ji_qD'%uBHkbUO8N*(E!c[e.OT\K(>9nF\7I!V %Zc-V?BTn'=eM,A5-uMp`JQE!m@u!kV'VYciKEu&j8@TPYkD:5oEoM=/ %:PM&"KWr](`g1"Z0g&Pn9&sEWHj4Ggn`:)=RZ;bLhY2KjH*QHfi-pc<,W#6hU!u]Mp+EGJ^?tg2uX`YhKUUSaO=)W,rGaK\knf/C,-rsgYX`PP>joBLiMeM5a_=&@R,TqR+ %*cH"+"oMYh27mn6Fp#_c$d:jQT/^7sa)DCuB4>hI*sEnb(<0p,R%EI``O\]M4c%Omm!pE").!t3B\0/DeE$3%ii''][UBbq8DoqX %')25V1"*1q(K/&(LlZ$iEA-CLP'N`%i9'hLdD\s=&8UG6Y-uP.b1[.gMN?r:+`auI1n;HS<+,g3C!-nd%P;c;r5P(#+967([V7>5 %M9I@XI3QsSTU&[,?_1ucRW2;/KIBup!"ner_@-h]m#TCE&Boc?2^f'.o`Xl5kj)aZN)]OOYT&QTF`2(bLiW)@9F50Q>`R:-G]D&B %+JaP!gUqn_(2PX3ZkN)[i%Yt'8HIDHjq5Tm:m"NhKf@%( %A-g#F-n,GJV-"Xoj51utQ.qC&(_BJY0_;n(6bEGX$),4RLSffS1(`UE:7E^PWXOqUr&JR4?[,i?L %Q#Rnn!iC8[$!]GX=W\SU*7.S#V,&d!*.iqkR>_EP'NTa76H\O8E"!)%iSYpcb9B8s"S*T]`/k'=Cf=Jg2M0Q"_$b"KIT`0#6U$r"e.cXOADQPk'1cWuPcfo_nSpE($k\QZqh7>JmTP>!>3d#Y*"Sbd=B$R/LG0fO,FGs@29iq(G %+NCtj>aU?cgjH6C)m@r"PS3oH1G=tA=]k^gq=:]p]]u-gW+B2G9%>[Ma)4OQ^>BBj=;DeXeb5r5f&ZlN@3d(DN,@t13aU+fUNu=+3:U&io3U[H[QKn,1 %I%XRm?GTH$p8OMRU[S4lK0!92b(HRPWL'2Fcu\.^CR7_#\T>gN*\HZdD"*%2GqE)ZqU`&r>1]K';.8DLaj&;@82Zq/eWJmI:RjWJ %ou^FT]/L4o:I'HTVdHP4be[:?Zm)uTf4Ff[hAM^Y?QQ`VjOMGop4%SMWt0un"r,NR">gRgQ\DVH0rIr%66k)#5%eD\R\Zt.gNWA, %Wp--n>W\deGf?FK%:?V;7F;Dlk2b$H:A3"1lPVL*^Q]n&hOj7Aeh,Eenm"Mn!6?^&QE=3rb?VH-?sEU@(dN6$R5_igL+GA=>qr3c %lD;e$HR$&dn]+O]b-gTkY.Eh27\dDq(PtW1PCT6PWV;p,#GuiP9+:^e]lqeQ96TXY>+eidH9>35_m&+e&bK5/FlTDts'fKaXZL^r %m'kE5SP<^E4AE1[US.d@"8EJ;@&hAb)&)Q3^!NViJ@1to)&_"4m(;3)pNe)q$!nL_I)YSan5k7I;n>C2Jb-arWG9?.T+*)>3GmXp %!;ShQ$@U,&PF&JAg,,Bi];=7_F:j![S$D1J3[ptIdHE(hT"fLQOMP!?5"B_9a!t')rq00m7A#&j.`N-/F7.XMefemFVo>:RkgAV= %2_.@gW;Vqkh]3R4d:1tT\`00Vl;u!iU[3fulTaU.IG(pdG$Vbd%G4t0G'JN(VdnG&)`@iGVT!l,>IJ'E[8c&0G:aI.SC5[J&X]K\ %h"@:8f+=oAlRq8\*gcpucrJgZcY8\[1j8!eJ01-F^\H"&$R`,BIVtW_5H`_?+i/2WpKP6!j$==5pY%q6p"\1]Fi*]t-]bDN<%:mh %go$d)D\Baf_W6^R^Yeno3nr)Rgk(LPBoioV`>pBeV5c-n$njlW.:&@MJ[_q+2-3"r=LA_"8F%j&P@?W-ao%lVO>$QL5jM-1pk^2`GukTrs$0-4=Z %eURI]ge2u1pCKJ8VhSF>Oe7SbqgMP8f6D7sM:If9sf@Cu9U7_W?DCY]mc9 %qabVkg-6$s6>msgc2DNrWli\.h4WHUI_9n%9E5a?d,Y(ahk.Xn\?9mW-R'FWf!5G>jiZ.\;=^A.M]7#?^H6qH(ceE^0E49kf@HM! %Q_5I6^7*Um!RRt"-8?k1fClD5RL4#D)oq]T*fT"UNG2<[`ETH4(s'+;/)R=lH;YRjXk0EN9J1e#qYgAKW'%DVOKR-k*\4P;hB2f_ %(O+0h=>'7_1isXaE^#m[DXLNMp<5j4PRVu"*s="acJ,!0ITZVF[$3]>ikkW9]G'O4aQq=imKRURR+E_^VVQI4)ntM:&W@S[A3U/QoK:p)\Te32s3`'cbjncgu84h*K.fo %07X#H[S=rb80ahgg3\Ttjk`29pVS'#jsrOeSj?Z0Flpm+g(aPOjPBP3`Y-n"*TLZ)T:46D^.7,rg0XPJj[Z-/Uc6<%mUR(:0G2_0">IQD&CgT3: %HgRn0G@R0rft>q<:Y0I;ZLo_fD2YRa4*L'SHN*K3F5O-IpT7J-mui738nYQTcKWiHY:a+loc,r0jl`6hSXBB9O^tFjq'N4J2n1(L %mXP644,3)l0l-r*MO;M%U:Y)5N#`+b8kE=p;sGDc5UYW^"RpH!`WTt_!Wn!7bZtLU=GA1@;Z_a"J-1ie:Sq"-[=jmcQ1tq^$qPd" %P\^H;,N>J&cUD_0k2n?o]2CW^88C%i9F./$Rj;`#(cRO-ap#:WaJ,3C!Wd0]a(Bu?E>AWs%\,70^e-^9?uD.s61L>Q;J=OD8+&Vs %&1SS%*u26\]GD(#R8QkCWNZ^[Z.jjTLj2#_=bH>R^ur\$$%VA.au'taM'_=OB[]g@ %_&NU.3jJql%9[@#eN+9s3'Is7Es>$A"fLc0agaCUH0/4lXV(UqLs0jDCe[+M-MAQi>9[\[0T%*oSSLb4"n6(HUs:7i?-L)mi0Ogn %b^i8sksX:Bm=cq8[`;-M?g0*H&dW"\7g>amU#5tQ:DbUZKCZ82&QgAjn=@CO]VnrV>6:upmMa\BY,2, %3mB!dAWoW@TcSf1#mi7JPHcHpmCB7$Ko$Aq+[2PqEbk_c[;#G?T.>jOdHG$EO/`9.6m%$m9;//0"/0B0*;Km'Ti.SbbTjW1%`(I7 %M@I[^==kIG/:eHfCt/2a*+%-)q->7Bms<(CO5L0[T>GS>gr[>V,B_cVICR*m/&7Fg9^N#s,U+h(,\.CuJ;AXUM$;A"&("[9J-8Um %qNMsdOI57o&CgQZ8O*M:A<\&qI0'9HkNJWD"L]a0Sq0PA3nsj#n]8ubUsCsl35o2*(_k^M='=If4F8p%4<0ro2ss=>FiQdf!etHC %kTY4UFL`/&QjD:[">C>%oEl7S*nGl*Xr'4X,Q!!^HGgU_:s[`XW!\/g;mSkmYoP"@#jk(=3^ch?SQ;$%Pu."1DD1u;^:^4Qat=FNNH&#Vudn2 %L-]-]9.$+-@8+["!@Oi\:Nf)p[q_HA+BScIoFTatcTkQ5LM#^9,Xam%5W@EGa4BkK]TeQj* %$Ee9!k;^kCn6I`-)t^f]9*N8Q`'=a<&F^,3 %nbC1m/b'p=Wf3eqVK`/'B.3oM.Qdg\BY0uLEVVa&-[A&t)doOs$k3inQ8u7R?Ds->4u\'3*=llSnP77mhH7a<-]f[K(rhSm&-O$X %U:]*0i]m,SWa^=D*X'j.U\"e1L5#tk"GV>6gpCUdRZ))f!sbllcF,$'F!16%6d1h+=@"28TpE.,(CU %5A1$=^"A>g1qSBRTUB2c6I,mI>4hX^7KX?ZDAu;@((B_H%PqB-ZrR2M;T;!&Q3o6!;*e&ErVo;;5e\3-D[?g0Z&:j3M=h3De#F'n %`WF(s'Nb?T1]ID[YlM4Ds'LZfaO;N/n*;?"8_jcT:ZguZL[KUe`p7!Ap5f,Bp*Wl6!fu1%^:Z+!p;`f:dsZLH^XC=)Ujqj?:VZiq %.eT/s?Tdn"d@_o'.%d]H;A6$6@]s_>^cm]p6i^][,01q]aSc#ml`=U1VS<;>]/KCqRqTq*aLs %"/-)sL\-^TJu)e3Eqg/176KU&PX3Rn6eY&P"\g!no8$a%b!C7p?g9XXWQ:ENmA6`_nE_rgSZKZGKp*HGaY+C\\>.8Wd0D'FEi:p/ %ehELZq79"i*Wj8UO-V9?YACple(Tr';^o>!H24EHQ7X,bb;4[?-pMh14@u/EZ=SqBLSoN5rKjQ=-D\PJmggtpMOphEY<&^?$_jhh %;mUXJ]@$(#R?EOOTL?*e_qIRkJ$oL8pI]9,+Um"GjjM.Td2tgB[&APi=/4.^i)=s)g#2*&Q[LX$&"^X>*P$F6]ASp#m$4Ll6'EY7 %0Sailo'XcrjjH@!f:9++H6Yqa.9%42HPhrY$D7W\\VLR"!r'0lofP2;'7\B%]8^OX.Lm@k.h#RS'`oA4WRg@qqm8p:UabNM@\3E`q"?$CYN\')"OW7,MniXLDKXJ %d]IAgniYX3LiV_3fMnmDe4E3ddr>o]#O9eUn1U^?)am'<1q'F/4P %>if>mR<%MqOdmUS:)uE;C^nMrQ^mj$h?AMuVoteq=3a)N*M3QWU>ZI$c#IH7e'uQDom!Vh %1J;Ak\?Vkh,kfooq.QsKko[=s!q!EPX+lZTe>=WqU\@O>UO.00eFZJ$`TW^BI5Q;el=7EkkhOHpj',':obP_\\"?tiIaZpQaij?U %-PJ,C'5IhGe5Y^uZ$TqddOV4jhVJ)&rPE0H9;QC9J'WKaB0RJ7;Tg5KZi3^[l1F;mkq>QFh,`XXCsAmKZ+)D-=?\S+pT$&pH[[!P %2pNLM5GMLFImWhUWt&IG4$h/obMRfW@VWsSC!X[%kqHVlSeIIYi9QGMeqr4Tc0YBu\P1>-^=heR\LTW-3HF(^7OF%_(QNl'YA]=\ %MW70o1s>,B02?#^C\QHsYZ/;^J+ku&^Z0CZA(N3=V9a5t7B@^4^KL4/Gbcej %0*F)C$,K69jU\0e!L3$uAH_2j[YV+$RX=71&l(dCd6i'+Ot$^g+=;?ROX`,CpXP5jrbpLIRfuFCZg"2Y<34:V:Dq-E1'gBY(7A.! %nrTV=PHMbBi(V1h+>O^8!f8.\mK5(^&laQ`>000M"eI.^#Fl#'ZT=1'0a*Q4'+SOWUi)5\U0>qPCg5)u[K@.h+J+'F=UU$QK>X]E %Kj\=H1'VQ+%.-Xp"61s'#T?>=R+7LHB+.;R(q>ljET8%e"6)(jcD?clq4:gTY6GR2o,-&ojirAIa:9@=@3o %(ZCD[@qIj %#tb4_LIpb=6H=he!3-$"?m&`\8.08MWrW-fRe#U>Kh*rME]E,5FDQu7jD)aB73#86SKse(.(hY:s4M`4n@5Xe0\GA[M4c8ZU,GX_ %;I%!@i7f'N!^VDW8<6l=GMg+.gjAJ"kFNF_LG&d!+t&JNTV!JM,f133=J= %Bu2-l+1d!C&mg7>l=ptEVp&c9EJ5WY0M8oQbIOC;V1@1`N6=P1dd^)$f1io+n&m^m"]. %Q>86TMS(qB3"/TdBd/Kea\*T]'amIm9"BD!9gfh6P,^JgduAr^&CrT?.%8[cA!)#P#?GMY9KK7CJo,4CG!Z?WOJ!]sVIk]Z(1+E` %!H_a2+XAK/!&gok0U>\"!Tm>n^JjbXArS<%q4U*7_MfB]*ZXH-:"%T0FY6MSR&ouunfd&saT4DapR3h0oTf=&YT0@6hT!d_UnBk\ %CAW+3dY#VSb(7LN>c;:N+sYWZF%YYtjO)IBG,jdB8ZA0l=DJSJbYTqGZL6K_^n7[r#';NDgNDtG-4kS,8Rt+K]YRtFm,8-#g"!e"mBHl=P3VFb*6f@_ZktK,;kVV$kG@Eo#0t"4f:$WSB[YP2n,1\Xg)@'^(L;:bF%3pa/Z^bo73H8m:1Fpm^!O(aMKj\l( %Ypa1UB&Z!O]H#8!56dQcQ5<[(aWV^\bboF$S>;7uK8p["2FI[hmBAc=:'Ilm\.7J_.ll*R+WUo7;X,ZXaX2d>X,0es]8ZGs5Sp`6 %6B%n;9e%^OjWhrWlU\/rE29=$gtR1>-KGej&5)(293Hn'Jn3rJPA+_5P338Pau/o-nue#B0sMW`_E(l*e0Gt;*1[_CgE?029&UJd-K %f0Cp,b])F^m8<3Nd&^2!4bTE8>*)p(3IlinBSA01U7L& %A16Lc0J0d\[s,t(cU7:`a(PTalSYX&>BIq`VJOBFR)DqAeE\IWUrF[$3<#O.JbW!63?qU":br/WShmdnm#(5* %XsUsnfo@u]9YTB %-#09^h&P9$Uj-Y0">X2?8Y/Dj6O4tX(tph36oZNc]'H0S(baiQmdY!e'BFEOP#R%uA`aZV=j+lMR[L(\3Mi^l+9I<`N^ecUMIX&' %$[DDTC0Fu^rDC#u?8IelSRgWfTXdKI$QiO?rP^P;O0mSP,nMj@K=d2p"#d3)-f/($fhA85BS`aG:&i %J]G")84+E"h%80`U%XK]M2dbAmYFa>*3kUr+u):%j4"ld/4((_023E>Hsk^Q?;c<\QjX"np(Sk#[knR&nuCCH#*\SrVMf+X(E))H %FbC*1Ilk)Fm@#6;n3%'e*Fn?\>erQ\bJllpdb&>9(l5.-RHs-<$g:"FL_1LIR&@E<9=%LEa@2f@:C$XGG4535ggdJfFBtrD1l[-q,eu4)H#o?2lej,Co %GDZ95W.`_tAEnWt]dM_\_e9):]@);,0ddi_W'U;/Y#VJ*Rl1aRHf*I73uX"-M>=STTtkJ-=]0Hj6r]2mMGXRFZZ/<9!sP`B/p]J\ %_A>i&5=IL;6AAo0?/HdT(E*`,F`+W"er1U0q2[ahWd"A0Q2$:3mIFiK_Q/8K))X4)=Q+ %^c/,nOfO&:7C[l%$+#?s@`ObDPn`YI\KSM@Opb5?AWEN"021RVPfq;fM3.L:`Q%j4iBskHjSaYb;gV4PP-6rTpD#<=*O!"Vn[QG& %'ETSD(+XQ^^/8![J_=%H>4+p!bL)f\SscY!2V-*4Y;V@;*LgVW7XKXLmlCQ.!7E:3*hAc?\PY/ %c(LLT&ZssnQ[N_-f+cOd@^fL3a&W*qhejMNnt*N6Wj.(j&%RYWP-Qf40M'MfCAUlEDqK?$s-1a*m'HZ3+Wj3V-p'?* %!RYXN0di#QRYW.l*i),d9-F&_2Pc<>D:,DI#WC'6_*DQG-9?FR%cs'N6:.P./P(KC.#AZ[%4N[qT4=i*QAD!s5\8J4CP^1Ahe!a( %(tI;4`5lkrG$HC0O7LpR%#(so^%Ok/RB(CGuh*s6`#o1A` %N\:o:%MJ-Tej;ALRZjBiqPT::.8B&,cC#0]@hroD>Vj$CjT?o.PS5FaF%.N09W:*M.cQ9[Lsc.=gbl9j/uT%J:q[U>KV]Gs;Fm9= %@%eA@HHOr2"4D]b8djqOj,A[mZZXKZGl[2]=HFrF?uQB3f`QASD@W,kCKg5[BqEa;Lg`nSIqgsO\V8:tQ.pJ\4FsAR[Zr@B`l9Nm %jl'GRo/5XL.`94d.mUN>4(R*do+MeVC3a3-U,6%+p/S,Fj44(8@Pq0CbQkS9L5:K6`eGrhq+Y0r;UTi&#*rX2Z'JJn!=u?I*0q7(3:IFCGrt6pg>dn\'o&`8*t?-IT^#?4D/=W@`_a]l56P!89f=]$N5nW\?cEM/M^,Cdpe\4")QEa'pE^M\iCO#`Dqlh;sKbo3"DS_+8fKE\#*p7He^6 %#nJG<#S?EQ$._n_,1qh0A*f.K_?4n8BBS1)Yr/=>`ohP[QcR,?`&<=@Wh=?DARa!r7&COIJ>X?0H88^-B/GJoG4E*qV-FVGLdrA- %>]/1u!^Xa`IOpL96j&lOLn*3g`]B>9/RJ4spsP(\T1Eam*,6d:LG9=YJf`kri$0mRkds/ja:MuX@RE,8Wp1m?#Uon(5N:]dL-$2f %h6-HUh9hcV@,@E':'FN)M_3"G:IrZJ][*f+EXuU`X#CR"\S$3N\F@9BI)F=J"k;e#K7HlF&1`#^dMWS^b'l %BH:SeeI2M^+/?9ON#5r1o1b!R*J;?0&gAN?"3LJ7!?*A''hcLi20QdC"Kk$ESt\.88i._RE^U'\pO:Of"gM/JP3!W!H<)$:*@tGH %o%FetU&loG1j1b8SW/@`Eh37r)P8B6`BNTlj_0kF'kdY+10ph3A/B,V"Tta8BIoRA8WK^m3a6">YhULTJA^V>0KXra!]a^fqADm/Kq9!iR\u+%RIJ0jP'%?/1i,2aW#CFYAU-K.Ek6\k8lR@rM*0X@Td?QJ^03#% %eh_UH**F121uVVo`U`eej-Z2Z0e-SJW.+2)'Tsr/ZR[eVD1#8@+[:FWPQiEjdXW-R.$9>1?QD;[6;nlq&^GJUd#0X3ed3t_W_bu9 %Q*tIr_A!Ll_,6oF5sgiuD7AV=+r2#X)lbk,Kp%Ja&=8RF8T?$,3iIM'gC[]uR>"+YU(sIq*5qD)(YP64Z[nN.I7KDUF;2>e?]W0dCR"T;3Z$/MW_&@J1FU<^lH+^LNQ"pM\:f7SN_uJh-uqASBA>I89FPqS-cuK[%%6(Z\W,Ve %NtFbUa?7:qKY&MI;I'C842r`ta5ul$2)3SPL.NQJ]8:j#PU8-JYg@ED$^7Z.e5=:9BfL/[ZZ[2]`RZ#mlt1f68NKLD3jUTE-#;XV %h@d3l$lnB)/M@Z<3kNe$-&*.L7P`jUi!3_oA8B%-68urN4?$]a7Q36Id\MtV'57Q_D),YlAI8ecgR"R+'!hjV-"_XWOR,D]&)j(G %f/<&W$4]Es`IS/E;We_>cjM[D8Mf1+m&.sh8aqNkJJ<5="tNAmb\KSHWPTe.5Z/R`#+(NSj"OsG>0%pBUJ995i(CE249l#s0Opu]N\e`@Hj5bS %#;5OK*W7h66IRpN(E.g\Nm^BEU.>^.-la8rIe8e"f-c>JFV+tS<+1mDD=qj+Ka$0/^)+t&,?>>:+:cZH&4Rg7 %r`K&NAaD]Ja_\qL%3#aY3uOn>F+5jNWJn&dY:ph[ed]X/h.R1q)KFdM'D&NF9nS[PburK2a_;$G6[jNAqWW %7_`$u^"iM\V]5Hc(^-9Iq1-lKk1*1+s(me_M`!*!AZ's\uoI8*/>DGf7`m6(0kSfBX_K* %gieLRO8e3k)]7HeG-/SIa"g?&;pPcHmRc3PFkr9a%s:$Z-+,CtrG>+XAZt..`(aL_NN(e(Guf,9C'"o_n^t+r+!#R%"KEg;LsR+c %X>2#[R^)WX(4g=qe7_%chd^,!d8[C?JL1m&\LlI-T1icK-Z_;fc#:1/fMFq<--H)l1@Waf!1uMIa4:E!/ %7R5jQ%FujmE=RdM<@'LQY.9Aa^4HCKVDOdNF@1>fD(?D8o1![D_[;.Ml %"'g"8lXG^$98;MLTk7kmCRhCqSS]q"nB_fA*GP$ajk\"\R85TKk[pArgk;GZ4#-K;ChX-f8Ms.!jUN(*f5ee9_=NioYq\9q7pp;d %.$lj,D2WUi\iF?Wba%*la5(!iR/QF-kbMj@5Goj6DWq#V]'eeEE-!#(HmMQ%.25j-f9fiP+bqmEp;BaCnsZs=1;)r%]%B`<%Ig39cAM5=3mXH[&/D0/>l)*W^i2S.tar$#]*_)d7k&D"H@5-.[(SCi[7['8%CI& %il#duB%t628l-"Cdi_8']`Sq(FWFtPh7OK*P-Q'%-8D7!mF,*g\\fGD9Hf/97Qn?#O)4__gCO$oZ3-BV-SKbX7'_(Y,6N5-PIJ&> %*Fp0WFs31Ppk1UJg(;\eY^KQ/1$6?ENu4?C":0_rpO$u$^_"3Q+5N\j;BXuM9QI4P6K1IGt %d,^1Q$_9_#6fT1`6T(R^4[j;(s'M1[-+\=Vua*\!8N)PON@XP!XaZ)>Fu->M@,Zd+\3L]A347\VI4CQRCY^qHX%S. %;N/(=c*2dDs3QliS&H_K*+_Yt_iY[JdLi7M,#^fL/cO3DlV:DFq'HZ1'EUou(fA@Zn^7m$r9363qY1'X1Mh(iOU7$SS%M:'9Xr7c %$M6_:KI02jL#b;]2\.tsQ1]^aQND78't5iT['Wj1r#V4a.?r7rEuiA1,6Z]Q5_Q45m_7p %"$7T>Fp:(uq=4sq?/DcjKeQDr:K>)icS++W&^t::2oHMRYh2gj!%;1` %=k)]1.=Vt7Zo.A%N&ZGZ8Z %7@LFU752XihSm(I86"'0=W5Qh%D2D.6n_l\_e;@KHD6IVlLFY*#`U[c7[#-U71kaG<$Z:!BSoQ?_E+XZ %h.KOG=Q0FfbQHL4;.$8Q.*>G$8Pli13g'LO)rRJI4THkHn5+-SV3Sbu2q1)[_m-3f<5VqcD2*Y%a9bjdX&kWE=?'*X'sUUOBSfdm %REJq&i!G>fXI"V*iJER??6I7sZ6NL;N!AO&r4te<0oLWsB8i`FP>'VG$U^#%*UQ#OD7m8l=9B3@#CTl%$#>W5`BrnhaHZ`+)P\fP %,oq?k>AA,EJZOPEF`?B2"8er]_/Sqa7(jp:GB(FD>J+X,SN]Osd,hZOaA'&bDW9?=*B`1oe %N3<2P-%Z[h+W2##3K0pi];uQqZY32DH)rF#[%dE.0S27kU\YS\V+L15:K!=;r%8IZD\mGmUsogG"PHdlAAcX"GPEAL2Q,G!+%u6c %)A=gD%?9.X6qiGPDbJZ<*1dS+i`Qra]p]>f%+.q`NiFW^2j=;8DX8Z!DNC"l]]Z'kP\7O)-s?"P-i9T>:f-pAe^&uT0=#mq5=Go_ %d9(..*sZ^:JI::E='rF),t#Wq77:Vp6(aC7Sg)KL.B&JEW23g^.$u"`MBkU5&OT:u?1&`$JQVMV+^>#5'Pd[idT;+]>9Gi@%a$Jq %:tI!M=KV2[WsXg(`.9LM<=<0H3='mtN(O[a$!.^KGa"6TU;):3?4_9&G%sbH_Ai_g%%mI-?RYoF\l*e>P9%7TN0Hi+;)6Ot?1QJ' %Lb0+b'0D&63&%pn40eKEjDZA0WKG-W1qKbd^lZ[C_&4isPVj]NcQ[g;T"A$j1r'='j@(UlH*l"09L'H4:e'cO'79)A[F]/@l^5&W %Uc&QK@q.9;,.MSHE28&@&VT;i;r^+t\S7X>VTB>*`^P\$+:CJ%9JdJR.ijjn\u>DM?qS:,.>BFRjMlk:Ad4`FVV*$lYm4.]7FmG!7cb2*'(U]'AM`H&<'_g6cVm*8^ea_loHWBX25W,t[n;r/ %KVE"P$[h;Te3M#nA1Tm#",e'E09VecjlYSR)q5-"1+1KZ'i>[I= %U?#KP#7>'lP?U0b&kP>e,DdZ!]Xj%V*em3B(df.'6Qlm7X^FP-r6#k7@BBit4q_.HCsXpag]Y7HU7teQ";`ku`#,&J>?=85'1iIJ %f7[Xj4UnW[e`(.ZQu@MDbsV6fVEcQr%;[2J'"sRi %la[q2;Za@ME@3+Em9,_[?ZmV,`'EpV,'R!YSICt(ts+S=fgZ0n81.CcMbZpPTa/5OZY9-B,:ia!UF$^:B'T5XiPP1-f %$HZ@c,jM)-]-oNI34*D!W/YpgJZlY+$52UGl:B?/Ehs//nNdqi+BP^B\AqDdmZCq$m6I25;,OBa %3QhdjAuhc;.)!a@)D?nuW47il%?8V7,nqtie`82Jd8Fe32:._CZ$6AU=.c\<[P?WG,QWl^>aUMfe4?](6F"0mnNI1R%T;I2$ACJi %X(A"k:r[Pl4CLj((=nV1#n'@A;&pE5qXcb'W4"?fQe=[L;$.MsIu5'S79UBC&.,G`,7/6N0B-3MaC6kP/%L;mF^D5\W>!-9*_W=J %k[GM$)<%"!?.>,m!e$'+NV8Wb6D3CEh'JdlcL-5X8I%h99OgZ^;MFo;<4cR^QCqU4gZsbDX[RqoLl%8Y75B=sXi4(M?6IUHP87Hj.D%*MjL)#O0U@,?B*`lklR66D9&5jQl^-de^:J!*a %K4W9-Lr;X&I1\4j6D8MA6orFRX0dDO[`#408JH-CajNeH'H@DuMrg+_\Ai.gknAI,P"=Tl#rA<1Z4WSi\6N%^K95[R$&UH08>I.I %p>Vr4kaJ"^(oWmTqZ.'3FMPgqDh(J_!B^tHDq,Xf!7-OY\4AB[al4496;;h`H8_M*#]ku#\,%4_`%;<,l`88qTRqC;61?Kq\grAV %,SP2KUNEh_&kdIFBp7-d`FFH9=^1#6Ko;0bTCn1t[,ia?j_4'e6j_G4Ok1eg.Pg^C5qLX^j:"DmD8d,5?3q5Jl=VEKce-QC8e-)= %_AO+eZU-,Y$0d#/.5XU^,l@s;[A$;?C.aSg.qDFuWD1Oo1!`Fae(H`._AAb(7mhJF3>'FQKE81VP]B1O8a7625Wh5!gm-ro7m>$m %%jt`16tH&p-PjH^h:i]3[%sM.Ef1GE9%Ni-LAT(0Zc9oL/F!mJQ'QVZ63%C$@:#R@Pk)m/AeuC#M2E9fddDp9UqON_-6:o.2 %DUo[4GE%X5XIda=j>SOdPG#,(-3DHS"A-PW-JO/H,D@\$#+g-G-r7?T2t3%>[:6FQGC;C*j:0:0cu:J9<%8o1o4(mm,l@nZl&/\u %f[XR@7G*2I;hU;[ChX[/-m2Z%S7[nEk+E$F=bIf'e=UEa:5hAS_n]dtZ'679*+Su*%t*pT_ZqLm=0+-d:roI6Zsm(];^#j%$YJD> %L7!^3H*LskOGbnMAWk%[9D51q(UF&C._$d*(/eK4WZYjkLNJG@_t6P\9jPIEc!DUN-"7QaF"]P)@`D1r$pqZPH5sU\M79E,@?M$c %`3T3%(mig5lBtYKHUm@#[dX4H"s0bkTdK4Xd/%$B\$/JZtJV\RrtBeqBK3SR\A'.MU'H!G[U"m&M@'Y_=D!?OO*^@qDD/0S#U:pj0&mQg!2H;3q`.QuJ'WrC9WgG. %EE2d)`l[#='fft:jN.1+`)6.D?Y+sHE3CZ+ZRc/7H>nW;5c1ADP>sp^d]LWG+[^sb+l+mgc5EYH9Jn_h3E,15V&aL*UN6N?3qjE4 %q$'9:+X)'<-Eg5\%@[9BZ$HY22iEAm?NSJc&Xad7PYN>2,[k-r)S(a#_<)`Ba2kgrCM5aV@S*u(@:CVr?K=aO\hK3o`M'*813@IR\fD_r0akd-9;&cdp\!\^P.-06'kD%(h@>e6A`nOF$N(och@O$=es_P0q<4"n=ktlib>7#!-B9AdRcRr %^_FhU39E>^$-OT^btou=An=]fNlnCZ)+XBZ5"M)>Af=re)//S+Hk?OFd=;Qtd6ZG%Oq.9Q8td7b&mgfc[jip]bL$$UbRpV+RC54$ %rim[l%Z=VP+q@(PR@*F[:fs7oHVIC#ko$\J?Anidp6Tc/NqK:SQqAiZLhPY %9W$Al9>?dhMrW!E#1LKR]-1*Fb"YIe3m*'4)O"\d0_'i'8FBiJd)T',mL6FI)02U6I?Q[?;E;X>5L[h@_^0i&aE/QGE](pY9@-=3 %V@u>']0hm""HObUOWr99,/ZR]KIh"]k/_ZAaDql$8O/S/8Z(fFPF%06OR:KEjSj2&WHR8PPNc,3jaq!:&2stDa34X/-R&cPEg>(+ %S-kM+U0-g#3@mB0#"e`"e9o='kj*$Z1MVN%-$"9kD3G^^U:V;Cb4&H(OIoYIa<@GiiBkRt>sK$94VAE9('rp%+_egaI$#jm\5Wn2 %C8i^@`Au56Mpt-P>.](O60-F\6&8Eh(FLu'T(E2nB"$S6(pe5a^/@coE %>.>k9;L9&o$KKp%np7`(PWPk+9BpT!a)YIX\'4bkX,!5DR0=^S'?hC*a=Vu57ZHnto_4BCn'-XX %`WMABs1IE>a8C4*hg+hSdBo"L_%9'L6M\o2Zp;>5nLN]/;SY&TR?686ct3T?6Etdo]I!&Q1iMWQq00]"nbg#_HRF:Z %c!S$f.kG:o%AYRVo7fMsL1Q[$d,=@r14Ru?"W^5P/76!HmQ25d\81%dr*:XV."QD`g9G/hJ.+(t$O4gK(p=0*!uJ=l]mqY/'47jbm0s^%hRAM]]S:^.TNq0DpJiGQ`0W-"*(s\R=)'CllHVL2hC`0k^ku#gOFpQ5K_o!!2Z;]h9DQ\`BkCQThuql!sH> %?*jCQ[J/SSp]G`BUG)NIIR52?H=YRSL[QNs("0]#9YfBc`p3UY%2uSX.g9lH:k1>-$NP2pi`-qJgrY'H#jWSFCZ@e*3Zh\:l)btq %+6hGSBLg[LbI\k6DqALB[m%BNb9J%sBZ!3\eiei)k#UC8-p\3b65M=u!$_mS`WH=TUE3-B!1e#11ha*kYTmNo!MWR7!XiBMoKG,T %>6kc?_9+@=fA>nr^"[s_KthLoY^T$NrZ:uT^d!Ho/%io(^sd/2])c#cFP4ZhQ`VW++2)SAoEB;\pRM\#,VUJj33rT==h$;%LjKNu %m9$mpmR=?FDdBmV&WC+Kg"r*"OYe2P!-1lBiLY;S]fS:Dh7;e,?(4p`hQh1JQBb6^=[c]qp[69Vi=XQ?8Dt;.f3WO-[H?oFgi?NV %c#;tL0Q[!E$!VQ6Z:-In0IdoGRonBpR1()eguA01]8o40XT?TJp]IQ!PDnG$+]rDH#`sBM&/[2iZ#s&Bf;cO?X7g\n[t!.\IF`fO %m+(Sg8DaO#lFq-0_5"\LX.]%/9e3\k]JngRV6<\<$GaO.Xab:-i!Ed$9Ju1$Keirf%38+Sm'Yp`g2^=RfFb>u!#fqab641:@t89+ %E1Y^n'#rVZ]ug %JA[gnb:NreKu!a`"1m;8J.9+$?@`oDMc`a$_0!M""41GB!GGblORD.Q"#b&SGRm.H#hEW3B`/(cn_aG&j])0)C%uRqkY17L5Ggij:l8/XMRX3`9!JDb6Og[0GE"ITS#hrj[H!se4!&h`^,tims$!dL@434V+$)^2#H3R-FWmaC[0$CH3`oE9X[.5!MBHu9L&S0bm#p"cAW!00Z<&t!"gW6NWCu`dFt^WI1#EurA4s=FF^iN%g.IqcmGCje:[i&$GeV'4pSM,YY7_]B*9?= %(EcO>VP"Ng.j4fk*Lp&_.PaTQV;L+?=]4Q6=:4@"3-gh*1sdCXH8si2gAig,6*i7T1iM"M1^3)hOJ"D.%MJp0#joOJ_DVNH#8@\D %r+8:L#(Ur2!J!$r<&&QCrqu?o5cK(+Jq\:j1+g_52%UKB>7l6lE%0(k/ %TH#GsCr;P>2@0\(HRD6#_"e2MAIG1DBF:+6QsT.2\dtYY(?c;M?2t6F!`1RAR!`3AhNa&)N+5P&@+?+C@,u%c?nZkKbVB+7/A\qp %!VS9B%O8b,,"dBdcmd0d:iE#f^cT@<@@T%*#(CDm!.fg?!6#HNA9[Fs%0/^J-Zp6C(BFpcJ9%#?+JcdR_FXbQKQ/W55B$O;!JEq8.@;iW2cr9!'I!0#";!!92I&WMLPH3EiSh=CaOZ3[M6XXQ %%Xn,9%m,;i([en^QmHeq:Tr^!\eQJO$ss#(2us;%6=jO\kU7\Vq'_a:J/ndN_olEf0Q/Z:4KITM"*gLNO/G]a/QSYX&YZIW=Du0r %3k]C>G8bp(Q6(=+9e1KB,^WA6:"[+o&ae8W$;_Y$6=:Va5O1*+>ZhR[bT?*PVAN"i)Z[g7^cK"KUtJTlZfs-khG)(4o$MtmR@ZKp %D4j=nj+aM]TE5BpTaP8@q5f>jk]HsZaT-3%!!,R-_YJ=R0(ETcW9l4Z5W3MZ&qN'Q9VN7*qTr3!pTmR %$YMWXbO0/8R;$:;&*V"Te3/Tm)=U^3"Q-Y+9HF(ZK>>=,1fkj2*t`F*GVE[G/7Cqn#Z@hSgP#cfaVq6$oZ.%'=V#oZR[GicD#n=P %k4rQOB_b]p"c(]fNr-!?:;g+c%o*$)rS0n^$#0'o-%J3gjQ>6C_SF6/"7kgi%/A&j+Fntc1M(0q"jg(C^>nk7=[pTDMV-sS]/M*) %8tQLRd,ZicSQ>KsE]BY`krPt'`biR,mF`tj;6R8pco#/k#XDp4*hN`p1Goniefl%8CV_%]8G:L5Yt$`JO&?1P"?b6Wi/nNQEc0kB %q(bK:%6)QHP.):"XgY;Uj/QPf@-nFa6IW@VJA<d7WqoE%mTUBDJ9q*4UQ^ %Y"lKP%?.uPo@P_S$(r^($6O#PR:ui__^gG)JOs>]5CHWYF=F68d)#Kkn1o$q+3>Ob^Ln7`F7A!Sh\;$(Iul9\FK)`RmN"Q'RK8qI %R:uu#n=n+":$+-VM,l;N+DSY(8cWp?jr$41,rPJ,T%f[gQ->R:4dGjNN-pVZMZupdlM0;OR)+CW(PiK)-l>3XAnD0RMV3`-TJ^DH %\S-%cij/W8"l]W*c9/mVL2PV\:XtLjoDA/tq*)2*J+AJh&f?nl&2T@p.@mK_^&l^:nn?'I,VlZW1BfI\>lnaLV^aC27`!c#@\aV3TAd5K(R:4a,4$36RT.j5lYCZKS %9Z,DK+`q<.@<)?;+ZOMlb.cUrbS'D!E:j3i!o!Y+I7n!nZN?5,oI@.KK`W.M**?a6^*Y+001+*L+Qr(kh#5fFAi"=Sa=p87*\fSZ %"+Xe[7_ou4Le(#PkW(U)!*LYtZ"*m>l`^g!).4&g+'T5](X]&uBR#-!+^Va60Ugh,Hfe[=5pf3]W93N_$HVIT!YB;Wn.R\i:j?BV %@230%g-5i"U/Kmf6qC`*!ZF0n;Dm+Yp=d-0SJJ1krJef.J,/N$q3PILPD.:YF6hoI&%3V]#kJJ2SQh)Zhg7GHfks<1T*CD=U'hd1 %$45ggb88!'!Aap]0UP#5$3>ZB^_o?AlD6,2\)2]h?]IZ.r4k%Q]FgG2J0#^BE&@.jK_^E#i8U.,#l)VG$F_T3)VsloSel@8#&4>n %W"BXtpGIJ.'I^4+HT#5ZSXN0HqKuW95nq!K%LA^3E,rX7jjO#s4d";M9qgsV"&Q6:L"dla#>tXdgZ&h"$.hind>Xnt-jR^B[!qJY %&RWu-fI/N2cO,R^.+8A5C?=B(=9dslPQC!#Cqp2l>/D]g9X%Jf9j59E`K8Q.Hn=POX<%&oU>)-]I$3g9Ho9?A+`s+P^*R?G %NJ?<3q"SPlGVuB?j>rsacn^"Sh]m7G1+1*s7][mJ;\'4#9YJUhJgl]FZc(^%Vk.t[or\:JI[#+VMk:]rI %E7(E!M\e$MZuA<`P'/0H/7,u"l(*#MlWAW1h01O@\81><3&IOIl])`TgqD^=Q)dqMnBbA]H/u(<5B,LC*PbsfBIfL.q$SsIGVo0i %qTd>kgMGX0g9g:1eLU&m1r$#hL-]?-Z),WaK6ZRNhS)?INZ?cf-eQ$(JJCfo2@KuITf,RPpTD04Z&'iS%idcViq/ENo#6aS7PEqs %m,)Z^?/;cqITUg<>Hhe_S]d=g5s2S&Z\d)7k9o-[*WPd/rn0BinU9:t/Y(J,ooXTe/k@pDa8CWam^dc,r=;&S>DlEg3F8*+`&liO %mXP0[msjRh+8Lap?[g=Zf*_Jm])1o!3%mFlKhIc9H>(RC0&)G+.LmO@3-k@*7MA#VnXog1Vgn^'s3bOEqof$.CEIr/p>hf>%s0?p %2)Tj+mXAiup7iEShsp[6r)Z,R5%rn&5OmIRA2WnTJ+hF-pri^/=+C0- %J,L15oNT*$Z$23Ko,bMVao#+:GHP32*kXK."$c_+:O_ll_GpHUJ,J)Ob&$'U^\5(=J)_JtpNM,]NPG:q0E'U#^\dhj_4#^L:!HnP %m/Du+:KN#'"/#A4YC-n]eiKqnN.9r-\*O?i=@3j_+S;jQZI0PppO:\#A>\Xh>PNT-+'N0E:leIJNVV.fSe\Z^^a) %o_HOWr4i:$NPGDi?iTbFY+==Xrk:Hg;g6GiI.pjS*PD$X0E8Xks3`"ffjEZ:r9`+MT%SdGTW.t_#QOJ[hu=]5p2mIOCS>HWro1O3 %EVc=)_2nML0E0p;r6<_Lfhhu;""Pm_FeDL8Ymh!(.8Fe=$6?N7XNrqsKU&42G!d&onA\`ZZqO@?HQTtN!miF]Ug,ZKVMK[:[WhjpbXIEt]3OR;':)&^u/9kPXidJ*G"gG."4dY5S1bHI$B!rJf_2 %h(O2e^!E$okDR+S++F&V^@U%V>mT@/b'lIWC!W!N(LMLU1S=Wrci%$oT?mK$B0Z"A#CiuSjR*]m3H*k^pT4i"$eJ^4cRU+9peQ=4 %^!BK&r1gGPEOL'B=F/:<>Bc\fe@t8G?[V%KgAS\U`U@a)[p]S)o@@f"X("^idnLAK0B)B+$DO/mcW6M %_p*Ch46A3-F2dE7enkKFhfW5.SIa4k12]d0baZ@\Bu`C24n.qcO2C(bG!9$uOc4W,['alCd_"GW\7-eWbhFH!i8F;upXkm]ZL>Hg %8i_OM<&a>/B)9VcjT^+`YMW\.`jdH<:EJR60(<'h\/ah=F$8QR\@jp#q8j*uikM,Pkt(c_#4EUfT?lVhg^>,80e8msBc,JQOp(.d %4?K;$@9VGn=,\:j?&Yo;0A0^kXt'5S]EnMeTA=[?`t4!4):*299_/m*fQ%=W(-<:dLteEt@'!AcHR0%j6rA%\0V'"nI8$7[ajHbc %N#sG7#FH3o+s_mY#Nm"h\FZs?9!Tl^"3G$NWPW4*b^Z4A9WT:l_1f-L0iGCcToHn[>`*[eRm>M-/*s^W\q23k[2*/Q%B8;aaCG_T %D>A@>H7cF$Y6DXp9W+82?'MVF%4SOu`gh'/TbD$ETI>i_l0b07;<1,S-+Q>+,n#U?23%;cf+b %P308AJ0q7/A\Q1PQ"]2U&t'T4(k:6Rof!XTS>8O\@L\Kj'I9BV9i:j^'\t^L01\.uPG8&r,&KgWCt"Cn8D4B5UAi.VD+=pQduGkJ %g51uerl-.!7g`MMN[Z[L"E%,ehcM/kBS!)K,R$b$@8bV4=JW-SN=CY;isG[]nK^OAptBWd1b%omJ^YP#-Rk4%[(o@_;NO9Y[@>_t %.?\k]^`YsA?ioOq<[7U>MRXZY0Sl,H8D8o`UAi.VD'u@@'rcd1TPnRp8NRbA3RgGmZ*,#g--W;t]5ajh&.gE!0fh@NQ?=1hf7OYg %!tP;P'5#%?8Ac&f7K&37$B#TDYaC"FpT10h^Tg=,c)P\AMmi[gg$-sV`\_&:ea.!PgAMZEA8baCtXt$a#(l2ln,iPF&T%%Vg+l6\d:p,@?Bo#)kIHl-?] %_(M3*LP70R>nc`=s4ht3UW^Xs=EmqXUf-IX0f91M_6Q%%J-UEYnqctKWnfLfYu*<=btjEQ)/-Ua!@cQ64li`-XUD#]`QV"]YgaMa'GGQ'1,WGY`gF,PoeC$EK`T[*b[ljqb1a4&_H#,Vi10RL_G5A"SurK\)437Ds@=7,e80;3gt3[fAB5j^#-J_VX=>*f5G37 %[sPmGq^q(/QM:B5%H)Eu[sNVb#B7LNB_?/P)KgU?ipq#T94hIf"0gV(qdhV2c3L5^D$".7o3g[BHGen-l'Rl/8F4I/;=LI4UU.J,M_>moA,OQ%sk]POBoLhkr^;?&(@W3WJZnkLZn>Z>fKPHf$o(hjk^XHhE.)R@:1:IINI# %UqcEAb>5@db7:Y?JU?s3]NqoLg8grSW'/R3`eFT7$TmhYi@>GdV_gr:dl'qY-R'3&YaTH[bDG^?<@en,Cg0"02iRb^]-Ka8YZ`I-*Bf %i4o6WbHJSX`S^[6=5X&7pV-S%J,eu@^[fOnIK%-`Re'Q#J+X2aoVl3sm4Z3Qb0*$Hhr'3T>1Hl'j9*0l_=)X=k?n7\o4MjBIc2*W %kNN#rq^4UqWX0gHi2>I2\FS[`>hJ[^:j_T[B0%,ZT_$#NY?]_mq8,en4-lnWAE/r9^0E-Tc-D*R%_ql#(K*+TptetE!9s,+T6f*" %^&I9bIej3>$@`Kn4T4*?XO-i?^$"n-UaE2+H[k#;?3f81l$Y3.M?$':pFEG]rg=ib91/jkhtFccIf(QWo(!%Bs+-M$Jq)2*YPe1A %rn>\_bS&;g+5?P^cqpDiKHL!tr[sFbkk,IY0h]GBH_8Fk21:5"hu3DpWC_]1bO!Xf^%ZsJTD*inQ&kn1YPdRMNi]=cH0&E2-p<(K %H[h8EmsY4ks4qVmiW&o%+7Qe2Z>@DpaG4]Rbup$V.cL\dpajWr!k2a'SZA %Dg^U>55OX+MsIGFQ`N@IcTgu!YIm#)0qYG_MV[b!NPG=FJ,$)%pua`*f@=-?iuOcharc-'K84$abe*VjLVE?(J,I?*IcWpemS-W` %H2$rZi6,L-]0H9dSWf?sU!4Cgn<[M^b(k+7mT@1:$GQ>\^\ks,%D=E$F70!6q"!Ts:lDmB.pr+hqp2CDKBOoYQ?AdGnH85>D2P=N %_]O?t>j&Bs,!1;-RN6fM5]& %NAZksgb1-OgU8D(\EE[(@g1A%pNI%GU5R51WD-&.Ce6\?#kQl4km2[ZnuKb)Eor!/l-'VpM7JTPCUJW?.)o6Ln+_nk %4Yc1]%usFUf0]YLf.D:t/A(DAa]3a+#1CoF%GEl5mN[8A3+:LfH,u.:U/M#r^5g0s %*B]SYP2)pO;-)Q#mLGu4fD[L5XqV)S1]V$F.3gAYH\!#C=Uq@"`)[%lVAVX)Co.5--Yg1D4*9IV\SZs4e^j'tW4b^c6fZD9( %*p]\^@UrJL(mMcp8/=jU@#e7_d' %'=^rB2U)7tnC/enJ@1hh[D>mU/BNi7.r-"\GIoJ#+Z3o&SZRtlP$NjNclBC]U''Q;YtFgOm"bu#'O=un1PPIRNaq,E,iU2l99\l. %^sI/A_F-n,cZ!HoR+ki9XGu;$k0*SD`G0:`3XgY.qH8h#p?##EdEC)! %<;!4?9QEVWe5V>BI!fA\C;)NAgCCYd:!m@GXm]GQ9`o\MP[S,nW#dgmQ$Z$lV(9*m6?RB`W)Uui60mq@gcAEKP,^oEA^lBi\3,@[ %).#T(iNW[$fGY)8amN?%=S'PPV9jP6_.N/ %#2?Xt+2>ODhgiGb%c$i>$$Q\mWFP_f0VlLVGeQLl_n1%1-9C=ZBo_+)*6(^Al"'B7^`L<3=u,F;9`d>lLf%S(1Rr6ei8I.4UOk7m`(9.dg(M)WaR0h)`o)5A@lh^gc'C1E\>_$cnH0:-`fqcHXk+H %F9"0,/uhD_cZ+@\T&HV[]FAA"!^e$!\5k*13jp%X='d<(&)[[SpD(lH@rPF.#"&MgDHdW*McYi5G[%@*Ljc$E!"N?9,T&,r\,r"7 %U1%u(.U05c)P"mdP0NKm2L=)HYDj<\2gBTP/mYIl-P-YT2US$A06Gc.gW$RX7^E/F_t-g%'.'*41_1%:3h$'jFbTl?7Dp4I"rhqfdDD;<0Tr?454BXos]/0-"`;cI92\ri=$/_!l<4BY(ZP/.Z+.*74T),`kr& %)rMu\,RqCP2!2e2]&hs-q^D&(QPnPQ9o(D?h6n=.\EG?R7K*tOIL[q*n\LgUr&FVa3)0VK@dcje[u&TiJ%m.8,I?TN7]!PLY-0*n %3'WNI*.9n7Zu,91f'XFB4`i+CpV@fdf\$$Xn6$^#lgNa%p>42&DC>G?!l)4J:O]M%EG97fRH4+rn(\B$6!_]XAkl)88kh*_Z8aRB %Qn4)53+Gu-.br5cR67kH;?Ci>YkrP:k2c9pY0PJkrmV?^s,?SNCX'n?Dm`KDNT;Gofr&lHj-r"I\GdihBK,9+Sp$\us+]]Ham[9J %SW3?'[Mqn.gER1a,,$%3p:m_3R)C/p"+%H@DGcgTMgTQ?T*)Q]2kI!`QSQmS24>CI2!7Xt4,T-]Zoqc.X?30VmC@2OnNGl22.*Ll %W`7$-aH_%6gRiCMZ;N?b8$U9O-hL,qCXd=M?'2Zr<3?uI?9CD;`HA"iZrD03jiOdab4oYT-C5>p*$6aSYDebpl=L=5*3P5,RC'?K %.Om-1(\\Xt=,Y%$et\H)iR'cKd+1^p6G\E^,g"Nf2d+U %_!3"SoN>8Sc,c.Hb`o_]$0I-P?'0QJPMN*5fegjrUf$Ps)KhN=]ub!<9dVg0LA5b,1i<%H\=6GJj5Vhf_3&2LW&To$F_uLY?;RLq %3&U,=W)WLN@rIhdWQdTDqi-lGh6R7+obcIN:5k[LBs":s&iV9b'/R7KUUBVirYkg4JK5REHL<&cZTZq4es'+F/Zj5L;09s^&WN\% %W>G5TUMi*`,"7L^,pe1YC,PqEA8Pd,+nsF%A/Q,:V+T^h$_t+=qUt-'g?CU^-)rs>H-')Pih&/Yi55*"$1YkKjt$R1GcnE %=ZhL&C$Y8nF_4*g&I`iC<2sl7&p"B[NM!/Er;!$iV)120e0Dr>PqNqCl3]#)oVQ=P5Jj=ER%,o,X)&*o`erR@ZXQBo_qlQhnP-if %Eiul#QWX:=`FGX(6T8@_HsimpZ3a58j:b)XFu.[Sf>62p7A4%HED[3mgr(gf$m@j%*DhXHn4JT^@g02 %KDEo0DJpSajuLHiI!Z2NDt_0,F(;l'#HUlnRA1ilUgrEtg[RiELEVrG(+9*9?hN1_hTlY"DL2#8ms.i:o,8_$ht$-5mWeGA/pZ-6 %N4/u#.cc@80/QCCh6W&CY(,c)>rH&CB8t?R7OB/YoXfiQ%8ZIV.4"utOA0$0#W+UfEptEgO*>q]5_:@d(jY`a4QKr^V:r0h %7kHCYmOYO)A?jqP`LQ7CRp="sTTJ"Mi,qB_]$f>"qAUr\^YtNn+;G1W``pR0!Kkg#3b052nURl#%iBX/#L!XlhZW.[T9p3O*&CcY %KC0<"]th&=(>Wlj5'bALNa9-9j-t`bg2@07i`drm2`F/M2E+9W'?'$)j0qj"hAnU/a`Y6mmEHUoM.G5S/Fbce; %%Y;q3ms;F+e&`a%/b3TF,;T-bTmSB[]f.hD]_\5$^&ajsIJglIn3aR:D?^"j9IAmAp]6qsh%o6]&:?\blUiSGVs)sd$N/i:)uT2" %%6bI);"d*?!Fi\&_"7#p%]T7K`p=n]G:53Ri6Z8kS;bad`6gU(AejDNZGLhHEqBFf'.Y-\7L-\35TCHmg]1+jaTBCE-1U5$gI)UfJ]<&mDO/gFg]j/WC`b%Xis6Jc1Ct-6?CCcA?5?/:C?!DMf@l5/ %El7QqSpTLSs/fi3k+5l;\,JAtZl]Aq$rulKiONolho<5H`6[6&UFW6SJG*5gA=\?*N3*Lm'4:ZL%n:WYAU#oK%F8YI_3j_Qha253 %[o"m'`-MWTR"Z5k?t+@DR-9"",2t1r^s,?e^<*k!rEAW&^=I[kUIY27>q-U:1(gdpR%G3E[d>5L(*!cX->U)UeZb=ZY>m(c@!+8HAl^SNODhV>V[SZ(I(/p/0aKTY_O/#0u+0gWI#'MNcd39imF7P(:/!-M+u.c",1_9K5cH?4$' %gW.*hgA>qhQTYSVo&'9[P#J-`g*DdHr&BYG[o+^;07TH0dbYc3[cn0Q6/mkEP1Ue3Nm_LXV1tF/F3HWepLoR&?)er^NYY>\]^`ELhepB,gEP#nk#fV2Z$80_jV+,0Mmip/8)<;WDA.n35Ua(e3C4>56pgE5_@bV( %q_&;M`[[=UUR+GAZh\H_o-M-Yp*Oh4)DYb8pc;&m^%d_J8X=FO`qf/,l^(Q,La22Edd1DD-odbCdhX$8136pN.rd45b#[,-^EMsH"5.s/\[id.u$D^hr0HIR\b^,s^tA]X3WT=l\*SD6.Ko[?X*T.E]A8=dMJULW=1 %F8iVp#.,(rbGMk`%]AY]Ibr3OA5Q:9!BPDMDOsqa\\KPcgfojn\Md_j;`.?umLZT$dgO'Dnf)R!p5t!#=ohS7Dp#lJ'EI=,K-*&NM;4hN.OMNI!0UTJd'!ko]Oss*u(XuSm_O0<*4iPT5=II!7^2@!$\%FnmWc38*K,c'k81VS,dJ/jXVX*B?E&UYS#i1W"f@)9'@6p7gs&T)/GWC8.YN"%1eR-2"%<&RuRXH5bbFnIX;FM8>U8/,Bq#Q %?)c`S/eK$l*C^);=2=:lSKp#U"-[5;DfW-e$$`2g[Z$ZQ.-+APA>"ZHCX8JaF9qV(nbM1`ZC1KD[`ZMWFV6oi\&Y=S%QMg?J$C\: %[O@oI`;:=_Z^DHJ0;2$,R%t?+XSbE.2B"*8 %<_gN:3J=bWb1B@iV"\?/2@5lcOn5Qa:=h>k*uu@Cch>a:Z0iR_c"k2mSl9 %5XjMoZ6n^ZJY*'H:jq)aUaG8p!0 %]KqO9+P.)>-b'Dhfgu&\fZ<%LjiMnXXgb\f^':'V[&o\),/,fd4u!DRL>:_Ycq9c0DL)O,)B9j$.QU>r^/i5):/+b:9#\5Fe#H,Y %C?L.GAI-:ca:sa0.if(qm;nut+u>l$XeiSVBfSP/8\)`n[^):?QAhHMUg.7o>BS.t]6p$\/::>_PA0H!3\Et\&P?3XNY9=!0Lk21 %jGFl11Rq?Yc9pn)f?qr3RMVBHj3^rV>Yd_&9aoEkI#!!0(llVe&h[^,>_G"cS4$;V>Zf#^nEhJFreL&eXJ$?PgGLU!\Nt %0UmV<[BjAT"9mie'A(ZX]][qHJReRu[dE7(V7j?\1_rH2a7l$\YhjSf)fYXggi7/#0@BD[YL3;f!D$Si>?IIS$09R/6lOinPfZ\^ %6th^G0-)=D_5Fh(_0A"j:QmVc^mJ.FO>.ca`K(5,N=Al[fZ[RhYfdOlM+PZj4\Q&b[DtcephOmY#IV$sNt)0*^l"G.d*[d`)9DK0 %O4.bQNK"%^8+q>;L6HUno^^'+S:GmR]P;#Z3&+=$XPj\!h;PqI*QHhJY\0_4@IF"BV,$ta+pL/.KR?>`_'g@d"QtBPmGO&emHXMIPjp6$s&0Q_O,d76/b0#l*d0u %24l`-R'o-Cq6"oAp?W!&'tk[i#mQ;95J?`B\E.4F,A1FT;Pu3(=rOOZb>P5=D,V2@'@?R%j9/,3n^5p"=LQiP.+cBhnHLX_gE>%- %(uLa1R%52=[>P22:mJ:+/Lm[i'>mEuBOliaDT7LS)d`ub=;=IH6+ML3a`@nsm?5NHV/%ajrHD!Mq/mi4XdCRGFa[*WoK;<%=. %7mM?((+:hT]TR!EYlcq*k8603BV^n_O@G+PGFKAFf^8D"GciW]%_`g^TumN-L&Jt`'*He4b8AuR\j]P=LcGN?1B;LWP_DKh9n(1? %\/oktpf:O^'T$1Ook)NN_-c[0&QX2XX730h,@d[4cD%jJY_kRn@U!_gsJM%ja#A!82P4pm>7 %`02)i%89NU:JTtGk#&'(8KW"$M\JY3]OUh]mi3q17](IKW`stlV;*KK6LFR[>"hLYY*P?VP_#1)^u-hH.uIE!:EM.uO%t_33kUR. %q,uq7=$fPC%'`E<30?j>HlBGH>2dBU`h$p.(QUYa"7:R$,MPf(Z"HMZ#PP[nsTj5q)7)dBA$m\LXe[BLbb_WOBE>(([o:Q*_A]$^Nd.g %P,8mt^!^ucGeOM8LW1KiP=)R9I2-Gd,i[^DijF(f;mPc$=A`VX/IoM+ %ZY-LE3gcN>/FK\Ung26*)ElD-VbGo6QuM`F^3%Wlj&7?d`*W,a[Zp`#_giZM,H#s[m([DZ8[>HZ>ci.T/?J@Odo7o+/lr[%jV;^6 %Uo7:oeLt?P`Q<./ROZ*=^k<-*79%\g^MUVFiB=JMLC0=:iQ5d`e>@#T:oQYr)CjZ_S@V%`N5<>UCJWWMe4bu4^I:jVg?OMW*@4iG %X,StGI;>c`8's(nI[o#u@^S1U7Q+E0CACc4qeDVn7]&F4XAu6+f!_HW,OK"RGcWZ,WJsZ4A695jHX*m] %['!E.@P!1HE4/TCFrQH_LeXtJWiIN/G\(?A9nP;:;O8E`:.6&"g'd:J98hK#BP^ib!gSp+$_qMf%$a<&J.1!L"b%G8LY6="WLl3ZCo9@WNG$j$B0d/k4M,'C&;5&H %Wp(B.>.9lfU+@4:gHq8I*>J9B$&\\;A_;VkPZ4i#I'5&7B,&col[ %,61+"WZJ:495\4f>ch2s:$aDq1!NIs!\7BT'sY/]COi[%mE4WPd)tpEl0''A"(E8(%,^$dclld2;U?_1/Z?&VpgA!45o*9ENKQ?d %?'srW:2lpRPSd.EO-6s^BW)nuM^;?)/g9?tQGRH"_r?rpXi)_FF:jk0aqUr,f,8BN;W2B(-qrk?@EQ^!0Vofc(:GoN:s2VW!6^Wq %r/r!NP6;+dj=C=6],,h$.'63c_d@:?*h18o)suu!E7[t9k%S2+4"d#e;n?_X1]`B92l#*pN+tF1Wbo6%Hg/\J[DLQRfiDE+b?NT* %-7+3.>rKf"f=k395!Om5?s39mE4Ru_MrgB><_YMp37Z%_rJ9&pm1cC:VlO\RSo,C^TX?+Sl,n\C;\?:1ZI9/+eREs\pY3\oB1CP> %bQrQd).B^)'C9l,Y]or0;okGVqaf=V5[O*7SaMa\@'A%0]i1]$dtk\?ZB`#dD.9\IO1j"BScSQ=Xg$FP(JidNgZWG!#^u(XTs$U( %oX/=`^31Ong/5?BHL\B0-V/'oduL:3M6ei;O*'Wt'bL]A(B9E7;3Z!t;W,0fYQ/K,J($eUYJ7USQFr_:(=!-3d[1_-edQ_,G;9L!!rZW7.+i5?,@[6`6%>mrtmF\i`(M^J)%9L)`g>M:&hk%kKUF2pi[9qo-8 %^7UA=nUi:I4]'C1_mff$`^VG9qb>[;9obSR)0Bcj^!tu1WD4:XXnAK%)c1([4-Sp1J$[sf,ZJ]3-E6iXVR#a2G3>%FS*a`E]KDD2 %Cj7fTmZ^4U^,Bu>3-8c>pqlZgYnm8nB/FX-/hRKPo+T)A,ElV)jnrek%4\+M?&F>(gKItqHLp05@6:K%ldT_,AB4rp/NaUdbcmZ7 %XGMp2il0G04(Aff"HD3353nl5dj:EELUZU[]%0#Ag[sR\I7Fb`0(j'BiUf8&r8)'I]fcJDmLIRSf'n^Zp@W"n\`6MgEiJN\rlL.J %CUQ?B23_t2F(T*Tjch+iSs-)K:-Gk1r'DAc'oY<+Z$p?;Z[ER'%/S^UlPZ3Th:Y^J\m1GCG!A4BF6)]ea/dbH]7tX?NZU@\`0%R< %4$'nCCj07J.IYL[lM7?ZI8BO,44Ug@B30ZP9gCdq-hh`oN9mWH %]mZZA'hg486[_9LXBYI>o+lgM %n"p,(kDAT([d6$mN)VH4[@Q,kJ*_")I:1;(YNF>p_J%4_h:l\l!AAbJa)g`kdjAjT_mt3gSY!#k3-tQ,FUC*/h^uJ[53$QGoLsI@ %VpDG+f&*d^@._Nkoi[i/XKPZKY3E%=m.Jm[=DQ<(qn0DhftKJIe(VgD8%VrHlAp&L2kJc"ST_)QK%8UtI`kLLnqkRcfChAf4aq^i %e=Y9je[P:o'3UlLXG:\Ga4HTWRs&@=Y]oLQX7+KA_kD3iAoTh##>0lo_]Qe?dZ(]t%chJ,QC=Xe)0?h2e,p %BO905F7.L'HP:MT[5I1_H=9e<\+U01a#@mPkg]@I/rmF+oO@L18GQ5C:7oT4@Ga,X@sp_RJkCB]4kZjaKBq=<*Tsajh7;Z7mI?=u %abEQ!mrPrcH8k5?Ibe+UV3E&->45c7Xa,Gb965F$0NP^3pD %_^iWQca;tZT658IKfH]n6dK\#ijn?ZPF7sOmaqg.9++jSa,^o>?)pDl:Noro(H8J@me"T:ij#DRg=2O4$E%"Sm?rsqb:Pdr*'*QU %:od>%=D*YQn'(4,hd,-ErcI$24*P8QH=m*Eg6S;$\ThNL7&s&fXL!86lq9Ci':HHq\pOIrB22^AOP/lPJLcLZG5BuSJ%2[NVrrV7 %=r1qMY&%4$4Z>P_&4^VMV\]kdQ!8E8>sGi1:!tJ^@d1RPg=ebPFuZhWkZHHj2,@K@g(0ach2Rn9nmZi@3ZW6"2/J_Aop_QW@q>A^2\C8.SlpLHAE,2k)HM8?EoSI1KFJqhUh.\fi$8]-N %i7`^nh.%PkC*847a1Yu1d^\VOpE$=&FBu;C+"CcuHQ+-1p(T$7]mBe#)NSq=\a=Fcm+Hi^]4$c %4f&hF5BuUL4a*^QgEY:cB@fqfHjZZnhhDnB@0JQ*l!5sldOIF^$lbcCOaU*I?+U_,4tVhsIBqt`nG&^EkEDf]k(0rdnBDgm>Xns%M+k05!?5jbEZi5Zt[P* %XrlB+VWHQFcgTW&X6%fCf09qObBbWop>!UVnUiFN^2h6HH_TWNYq(77T5!s4VWu)0hoo3oo>?TtqoP."$g"(ur`2fRXP%RVr8uQd %[UF[3d@k$m[gtQIf%R6tp8i8bD;*ie`r(J?r`u!.lKt-()YWfh;^Z=,jCrQ,cA&6pSisM`<2hW!6jHh*"LQVJ(bS3"(W/a-laQ5_e;bhd*UEb*k.YmuGo0c_$FkkX)L=-O,[b+U %:M?F*6ALXfkhhp+F0>J*A:olHYmb0F1UAG"@Z8".-11\JJZJ/c,j-ACOS0Cq1T`MVok<`7V4kY^-p %*<0,G-P-(%>o_jGbePZd.`de9B(E3iLcif4n@d7u.H6GrjN231YfKHV!]\,pbPJZ_k*I;GPW;r!ZFjsQXqM&k(d#./q)T"2WRO"N %iZ1`om6:gEJ9&gjf151?0_4,OV]G0#rkH@>*nVo30799:"I-DQS`D%Q=S*$^?I6Yb,>1tcY73M*.Tj(DaK0o&1O>(/!E%GDBsrE %S?'O"iG-ld`foi1V8C@8MqLXfQ>dG^i'u/$;C@PnAo'X&&l-3.JELGuU-HRpgms.PA5A79+!k8C!,he/_7TaQWK'!R(lN=oYu9J< %gCs:1-bP9o[,=X?;s^7@*eRTQ)NTAq)a$OGe\*r/r49Gni*R;L2jV\G+0i132 %D]/@m?omVO=4N29C<.48a",o %NgY,j7(@S@n<2,29'kI#'oQTn>c<&3h2CDaErRmmL6]]qo=!0SH6S\X4+!R=a?D=r:?AaHP!I,'_pU:Q%$q^QVd5'o(t$&RLSRYZ %1KFla`'@BLY&b3CQa8Qc%itRoT0<^4bs+9h1HcK%[EeO!m@cQsg0&^1oBC_rB(2bn/ME;I>q)V6Y+)\/:MfI%lSsmoC`t:_=[/Un %HFlm]]_]Ra@k>H[(AeDRZ(C_Q[.=)ZC=U1jdkJA*'?apKB#emEcK=4]s6+_7)]h-q@4^k:c+J_\#+skE@gX2k<*C%kP8GE[7lEg+ZnpUoV814@`P;<;XbbD?D/`[8o^/ %S)KVZ$;7O0OOW#omJV):>L'1=AL*6"/+Km.LL#n2Vk,BYa2Z>6n5JWu]"9,Nr!I&eF?[I5Nl`43ddO8ar%pqfAn[.u:,o6hP$$#c %h62D1ckn'RP*+_FG?3g1*Jr5^A]WUVQmJd)T#OKbX5'oGG-FB"VQa?N")R%[4+eD;PDo.r6p\1!UG#q@`,%\["lu%k&.N,nK-r_! %\s\')`U#,CmU].A#Yg%`kUJ]0DEXAnZ$.1,D;r1--CR1#`[$uYXHS:d8]_Z0-U^90oRu>(+VCLn\B"W#gR1fR1j!Q[Ln\qf*iqW?WbE*IjR]dccAQ.:1>;lRAOa9E4L'7+ni.Qtrjf13]^b`P=Ys3q[Zh8u>>.QpaDoGp.#pm_iEI(@ %r8$SMbjaI5!!#U>VbL*UUpEah,#R@UL.d]NhhOU*W*@qgXL(-=2Leo0N0WbPP)af\\GC1?kbB=0SX3j2WpJ^+(,2VZ:Z-?`YNkj]A41$muU3X9/4-,JR&ROA5\6BeFU4mgHmTsCh %Mo<-DMU:9r?h?bA/=lQ]Ji+m;qtSrOa^(aD^UT.rA&M2dE;")AUmk+CnemF%7&EgIAA[M\rM#Z0<-k9,:]oF@(>dWi(sf=T,`i9D %b\_o.514gK:(!0+p[4$sO+MmDX^p52#S7"$%3@JE)3YV!gcWSt(KC?B:0$5N$Tj!VO0Quocb'52J(YPpj=d+ak"1fTA^GP6eXWd`ZM?4;d<@NPK*SBB]nM,4kYGV>[e7--/07U3Y"cS5Z4bH1+\VSmmf?Z7m;=nQbO(C5]PMW6"nchB]/qk)-8<%eGo0 %[!K@Be#A+g\6B`RRqWnXjdsHtdF0PHKCjV-*P$-H"&A$r/Zo!;]*A9piACg+Xcj$/%6I,!aQh)lRMn'C,<1u'6tEai61q(i=LK(a %?8Xi"oT)?E;:IeT;IT[XV+e"UZL,:O?IK+M^PCLld#kg@gV1ZXr*3O+KE_+l+_A-pLi"O'6R!'%oejJkVu?g>5i.mR/.:Nm3=EZfGaoW3fj`??7?P*1?G?dd3dg'q*bYD% %LXBEX!6D^r?uBRVX'q*&.SRRraroo)LcF>+9&eT4b=rE%\Po[RBoIo!(sda4o&Q'S#"Dgh?%OqgNQ6cem&6qNC5dc7_gXt^][URC %YqP3*$\`1:_AELt%;[XGn@PhM@e5!]55RKSZ]:U;Rj[%mf+'j(M01*b_]6+Br]]c\aBS.3:/f9APUjYEH)j]6EnH6UUV+AuT)3tg %9VO4L.ZYL]#cHSl-g)e@9G^r7#-,`W#:7Rt@-aBcITjVIU;b3"'eWSP#of4`LZ,qR$.UVmo*fQZ[)/jU#=8q3Z>9]cC^d(k;Gn'G+7WWV %'AKrXhZr`)@S$R[R7tV?0@&_A;Z#640?R">eBSGnaf9)^n2^TL5q@lB<\Qj\:NIk=<`V[FC"-ue9]K-sb#>cpWD/#s'*s5US.,Rg %WL/*5b=Wi)F<-RDg+hL"Co#Q'O*_Tl_V6+&rJObeb,qq4BeR.0Af4T$a/PuZ`U%4k"e;-k0)EM!?Zp3+3@<]Khs-3USE&k!F06t+1P%$Gk-mJ>[(pTS3APVC4$;)0@:*trqP8o8Is;u&=1h;(b0`1Sh7 %45j7+d$W:X_1]pJ`*T>ki^B4/n*K^F;mka8GTJ\_E;RL$iO=X2hCRPm5sD/f"f&1h\1#m26dYBU&KhDKL6]m6lN0EOE(*f`A9T.& %]m'^dN1nQSQWs@p!u88S/-@1MfW5cJ;^DOlC_)Mf6YC^c4#nZNGBX>[**MaP?aq2eiHd6TO/^jhi/,T"1^uW0dh)&*>9)FOIKCKf %J7RU`U%.G$P[MPa.#dI4%NuJ]&\p#Y&JB59%4Xl29F+?][hI`ujURWZ&?aNoSZ6OllEm#N8ONgj`Ph=T`UECW(,H=K,*!dL%o.6[ %Ft87@Hgp9of8ZeZU*=-NCb#X2=J#GMFj*`'1Fe,d0p0h\\k,pOCnE##e7'&8+^rG#TMA;91(Ja\KCJ7`@"H>1` %(HrEQjt!FUfsb2fID7.Afom$'A(*4DNfR7`-bV1fJ`(mLfJ$G_h8EKF-Q3;=]K-![=o:=*SdW/KO6dOVnEX:->NaMl'LGbP@TZO_ %!ip\OW_#L6paBQg_\qJmElQC`5dY=cE215oK+u&D[G2&Uoff6g'*9go`&FMCm;_(-at98sa2^c#*kn]]8@iqb->@&W0#MA&o?%s0tYRQ7'F^4m>NN*6F%o09+= %)?MR!;9P]ZP=k?+)qFLJKET,X+/G2-]&J?':EjC"WFb;^X$_=;[]*`HE@l_R2X/f9c_L5b3 %A8p]CkL:]']cT!+*ier8Kg,cH]oUS6)e-N]i'&kXn_P%rLfXfTai4)f^)n0eM2hC[HEMUZpV)MWf<_F5E$pP>STtt^b5/oP+$hd) %#K<:P.SE>Vn6;bqJKQ`mhlo@X=U]Ma@7BPNF0.rVT,[4&_hpG@$Bi]-MS5&U=]XAI6@P*J[O]/GqdB*SgTH(UqlJN2A]%06lm40_ %`.2"oo?3NG9n,cOMpjuBQUarC.u#079NNBH\tCcm*?dpKnt9`5N==Te:poMSC5qaZ%]U9b;iN(&it&.*h5<",a[+IOA#c_njlV2q %A]OAgK;qA\<5VHFIrJ5le4JPY5H%aRSAt=O#?BO(O/W=nq29m*7Dtl:m,JVV(*->%`4eD0TPDk/eZ_[5%h>TT]bI&url+dOn;f]1>&EB*!fU%>TPBP^<;IRUtI8DdZ %AGQ.jCAE0Kgr1Xd:pk>Wjh1n!9/'39Ui2O;j4>.SHZigTbJ`3Z/#b=JU_%_%%(lk:.!5hmE-ne)ZQG_!Ob%.T`9f0PP^[$?4V[[J&7$p=D&N]j7NXpCPG:KE^4B0W'S"p:HV5n]WQqr$-Ei+tE6h&*M^!GlQp`Yg>Mb*3nO,h4U6HEl$-sBg=ugQqQ$L3"B<.Dd45eeT>Wg=i4Chb4X^@@,3i1cf %pRf6]"4+t@2Y^4=DMDe6J?ss_KR8jLo&/lh\sf8-ZS>Q2=.cPqSrEcr$?PMPY1SELnmtWmHsE099LRc`p\Xp@j:7-KP)ffZ\>gInB %j)T`CKX&B?:Z^9_8sT+23BN_^aO6u!h`MijB4k^ud_"i3Cafbc)NG`ZUq%>Fm+LT0G`>rBJ`mA.NVr_oid0n8cBdhIM9S:J6O(uBa#VQ*I`/`cU#>,hM"/XqU;&sP %T+H1/@,ja#`d+A!&9FW&Jf)8q$p/Tfn`q0A'Oi'5-f9rT_F9EAF0C(G(mdYA59)DBmB7)]f4metLDX+CHgkXc\Emp4 %%n2FA#+)-=(fu8]\cX[9;$6hGM"(_O0au"1#%.8aJJ^UU@t04M=*&!Pm>,bM1[?-A(e5T4$/Fn4Xb^tVnaaoqa!8SGKX"E?N!iZA %4u>tk@[rGNadG8P6B&8%L%eG\PC]U1a1^>](mg7g4;osR\2AGtbJ*=+W"PPr&i$Odjgs6)(`2r5qA[A_I&GOc,0!FBV^Lj1CH!BU %n*+tINu6JV*-cnHZ#Mn6*!=W0#@^s+>b8t[`ilSN9>n>DI.pcf+NZtm*etSfB5gMo90X1HcCU/EJ*kg0`4/pdr!d+?UE#,8!+PjO %@6?U+;b:mmN8N8,8gRsE8qc8VLiHg^T.o;TJf%!m=oJPc7j>tK'0oNe's&<.Nfb3)!+K3M9DSb/31>7u3:WaTir[^dDSZ)Q`;"0i %%b2cCS1-=&9KlBdfq`1T:s/JD"plBquo-O;[JB8!H@DOd,DWGaE^@,Je"-JJU]43>\(bM&NX/: %;u.GTpu>,l]7pg1!AATu3rJ:-V%5qKo@WOOC3m/4iV[X@:X++q3aADRq[_'4Oqld`7u-\MC^$?J?0H5V0Blp/n0@CtE#*qR%d07R %;-Zo+UVe`=]RPMCa/^;o(EG-0,5=6Apa,5$0@8\0C(qbF%TggB4pE%QA&B%]D=9fHbAlmRk\qR^N`.rc.lpq4HLSc7E*h.;BT>;C %L\$H?oi65b3h.imdMVuo*A0F>LSD\3Cq+_L4VAbfDo4[M*A42-*fgfG1+s/+L\'0fa3KUudG9#jMi+:AYBYG=UVloLQE^0,L(tY`D%u4Khc$r %`Hu(Hr,AGJ[6&?'B/[MXV_2oPa3F6WcI1$rr:ITs@8:+Dct"Cd*np@cTVHPe"!AmB2YM"7B>37"?B-HUpkKZpK]qY*SWT@.(#Sc;N?aT39"h&^[:8Vs1!XlIZH$F"j@Zd%Gl>qJ;,>Te %1U*?-?JEpadA;!b`[Yok(p#[qDA.5E\Rh3Rj^V=jWMU/,]gp69g['ZTds:TihQe,J[QK4Qg*9qjc9geY7M-NpM4]m.HYU.r3PN.* %Cu[uKM6]V&ZTLiL5(p*!PQP[M"#SS1$5Oa@C0rFGbPNsVM*pVeVP8=Jq?pl?9H4WKHE^f4b)24OrT^S&4!aK5Ce17b(R^/h %V;R2N0G!foX)2u97Z8]e@T0D)s&fg7o.Cn(\NL`9e0Kcpe)9R%5W=>%pgaL$QT,?q!-!;2?t!!9AEgDoi(`^GqW^+"oka=:U"Ip8 %h'Z&^)6Jq>?rJ6HqA:cs=2.#M\hZ/YGE#$E$7*PUUh^\N12#02aA$Xs"9U&r8rP!_I-f;_ph5pE(A4Ia8K=X).cqj6QFa(s=]Z3S %QtP#[B?bu(qp6C.O@FBk*otX;Y,giSFWH9d7+^;OUd;.O8^cWt53`!2k_%C\Pn:ER;q!9HAkDYmPR%9$c7LXD' %\=?`?n7l)l>%4MncK;]A)O %Wn(9LZ,)'O>/#/lrSuDu777`b?1"q4):8/=j-khSZM[gnb%_e5\]T7j]re#;F`HQS14Gd=[,17g+0>2]B&&+R`j8@u'mT]GSZN[R %4n5D,5R@*P=l,"P+;h%iAMjealcOPr#Ji#)p$6=q7K%sm6O9iAicn>8C!s!=,JlF!9Zr-*OQJ3mL(!/Z\/L/lRd&C5)c0#p_k%=\8rIpH%!X%aJ^]4d\7[!/tDD/(/CB,NAns0/Qcpon2]U\SuhD7@HDe66M>ZHH[8R(SEQTC`A) %].a$nQD!(M[ckf! %E&6qWMjp\'(jB/uH?H.C?GEr'H/,YfI(]Mo\U1Ws%\;HC3'p[=ZR5l!#NP-G*5R6k3u!W$o@+sd[NY,u(hp)VBUF`q&e,X&l5\HE %e/0K9/m.7oYh$8PQ/V&Br6gQ9K;W`6k/d="5l25&WCZW*iUKp_h@5rki]$A,0kqZ2H(+kn%Xk7kC,.\?L/;tX),&M%m\sR<55W3o %@K;@EdZQK>0Fe-S2;>I\d=B.='@H72K$Ke/EHF6bX6`U3K>$YiQIEI&E^rn0]86ib?,i@jaLW<(7Il"g/8eAZd*'rqLm>i %U36C"4'LC[)2@MIPEf>7.5c3;KBV")L=\S#dFdqqdE-%c$V900)2q00KAD&6dk3f:DXHM*gWbN!d7 %qBgO`epA]1p@ZRoW])`lk,&X$cdmmHk5"fRq0N5P>bqJ9D=0 %L#mDn)7(RqF# %Ye1r9-<7.&(p1Kg0KmTL5-SCDQG?bEm=5bM3PFm`LL=(U+-amXK.RZeBOP7RX.IW"1I:MG^%:SNk,78#cs&L!*L!!Q4t*''f7K=( %EDE"CjHh)QcKLh=+@"1kn+X.L5lB&?U[?&'A'SVkO]Ui;6"dM1'7VSp,?d<2k5BMA^5k7\*+4k<*Ig_T*VfUV/(<1;T"5?De4BTQ %8$;u(!!:\A1/hCgeuK,a*$:P\Pjgg>=6m6>@7&'O_##N.;q%^A!i\;Qas3 %ULt?I!G[gE_1;+DoL_CH1UG2r=T_Un;,1phr&_LUBA^iu2[B1;`7PI?qqE#A;GL'PR@@o*L5bbMLqUOt9 %[2)"a2>?1$a&1qFB=ojlq;lLfL6G#RqVD2Ror13A>A6$*#$PM80S>X%Q0Dfd'Cm_H[HN!=>JKrBk0B[72U>uj]cUo&h@n,r`6b6b %$em0p?#hB$\r;C%eBHQ%3,n0V(JA93'$Y-<)SBZ4\5]SR^ %.1;tArk9[\?PLrd/d^+P+#WP#3FH=\O#.JXkZ[%i(7dtXT:m_/_IM^d+a?/sQ';%/,Ag+u^/MpQb0N9\a>)sCclB.QBTh>;XO<<0 %`I%I6VgaG7m88":Sm]I&0stt$Cr#f0>/7P1$*82T$LE34,i0hK?Z/ujmlGp:R)e)+WPQ@lIL0YD(!G*9VZ)e3VO,)5Y^DaNB4XK( %DpL$6,a?U1jQrqu"84SI/8;pi"Mo%"RWLff^)I^4eZqWWm0C8e,\$[_rN>L)2.cW<_\6Ar^gH+Klt*LkdP@X(k$mg/@Tj@-?k.,? %$.n$+gVSDH%/_WA9=T023;B58UCW"*)u/DQc@cXlEO)(_B9672X?1MmVI).`\PV=TFRiWLg+Yd[!Kr:,C=I]%.^9%s=)87gjW)Q3 %(0CQC08`lII9o8[/JoKc*MWF/2JZ\=GlAar;aX&!MscKu1G7lo]XcW_\Brm/J]#nt"EV;V+c?KriW=JAR7(XP^,1,36u %5-s6qG(md`f9EB)f*=(g[Y3#e6e>^a>BmRHqpS+G:"EY5\`7P27e#0P183t9gPBZ$3Yhe'ZkQ(*-: %To<"pfX#J-b6'*h"["8.!*s<.ch'JK5NT]YDG7inBpm)c@Vni!IPtVBpQ,m[W][TYf[p#!`9_?P_pJ+-9qXB(PurYnS%A4@YaRePC]u2Fhblp):\DD/=*DT<)4GtueH5q>F:V(&2f3fUN&oA.]4F`s2S(If0+>E4 %@k#YI[[1AbPO/E+u>I_%s81Ide_pQ-W35>/% %YW-5"L0R47a.>f-\+;'N29s(d.;hJ'3sJ#[]#>3ad"4Ce"4*0\9Dq7g%b=(9-VYg<;FoGg"RT=SgppJ)m59.k8_8>LaEd+US)7^O %c4WPF24l&'HZR8<2a8ZPUIlZPrsZn!ebnnrQ433cnlldP60'ofl2\FJjuV23)'E@"[dFL)jJ,DWlBCgFbno0G?nYR2]H6oS5; %+.,\=b$QgjU"&5lW.uIQ^P/j9$RV5NZ`Xu]!TWS7nZE$`$8[F>`![Q2&a9+LNj5r`_":8\?BC.@erk_Zb2.PhTq.(tr+o\-k@!Dq[##6k:Va6fa(*WM$q3O"cdgNI %?n;[,7fJQ)&5j9c)"U53%,eUu^-q&T-VV4=eoNOdLl(dTF\,JdW.oVjV0c5Y]oP_8L;%.WDWDfml--[)[%%fEEoOZ,*eWIR=N>->*C96.Pf(*X3/k9Xd\%EP&h<>craAtXMO&]udY8F@'&dLRAdG;q?GJFBP %S]651+6l#pH2B]_T@jp(-ZGm`#0u\AL+:9@jM(]G,?:TB7Rfn]R]>hsDqj68mHoa$Ce#'N6SWN\*2GAhjPC'3+,<%BS[nPA7JNuKHXf6$WtfA`'7"^RH@\H&gCT@jqQU4D@Vbr48\PopD:)"mY)Et!nnI!p %!aiR;:IKAd($r8iK5@[30qDmUj@b1Y;s>=f#KsE<^14$%1/(tR&Q?MM,hS!oa0YI0V9]97d[o\'$Z<=5qpt!kOYfF`$V:8TjX!@F %[O+)b[MZ]Y?6^oX7FG)".$%:p'HTi$G7dEn;Gog1N?k3BZ7Jl/hR+)L09.=P^:Z_B/rnkh4W16,c]1G.otrA:2Pf,$]Bk8'>GE4] %:HRinBo"Cu?NmuA_1mVs1cG/X%^oVLG+[Mud:J$&rU`955qbBjrTNIIaN]/d4HQ(?8^RX`#3=ArfQsd]mU;S\G+HNC(#bl*4[u-d %Y2ie&'l['#MAQC0pgR.mRqPpU;L/]HjD":-Ra]>%8M,1gg_R]Y!%1s4*"tO0RT=.1Y<=e:2cQ*]V\H6O,;*orW#]8^=!7>#2QYDO %of*@_O3DbVkg<&"[O^8QZ3E)'ek#A:Bkh.?I4X_%?BUQTDPimh.<$&h'X3a#IIi6>=dG[r["hAl*Jt>]bTo5[Abu(Ao$6-#'BorR %^u6lMP`>`=9YPKfZ>24D;f'82`0pU'I`)C"\YrkkAeM/uZ(Br!ed)-32(YFO^cDInM9=??V9AdE@7!m]P8QLol?uUc`RXu:e;qtc=j1uJ;[YlP$tY9`DWX:Z^!_5M,;a0b^0:%i!^?YidB9\JQg"])TAe\YKLQCa8]>%MBj %pe=jDN7&-QplGta3iF48Q:/)\fj0T5,bs$u^CK%aq!tNG%P^Vkd8n_36S*0p28dH`fGJZkBH;[bO*'II64rZp/Z'>-49."#nL"pN %mRrchoPK>FL+ZULY,=tQ4Y^UGiGP"V.o?e?c;Q0)C3Wj)iE47\M/GIN]fL#I\iob8:HH#*b^DgP)#.&BCtVVmA#u7?q(l3oE\I[L %"Js.5:Qf)1nfBskP1KGhNAuK).uc8dpoTar7WD^FZVFR.opWPPS82FLclW)N3TRG8hSNU)hgIr$E9u;'fk:V$f_)T6$_>RF@G>a\H/^c=kfWG><:'GZ_"79l]QOYB\kGed$&O*/Ct`BYE#+' %#)[+&f7r?Y2PB^"Ohm$R+=E0T3CVkW;!;([;1J?k1j;m4"6F]%p'I*0Z<,+^HMe6Ge_:9t'qA;a05XAQ.qh)]l)lc'cr^[iGL#/W %_k'OG#'S+?3H.Q!kH9-D-*_uOZQ*)Thfa7MK:QLoVJ`.+Q@S?BNR$Xt/f16G#.QIs`Or(Y+UImS^;d6T?"tUX%'P1a2R;WJrdgca %CA<.VrBLC/2K6aXME]pP;g?JI/)WPces>:sGAe;?!;6&,&2Vr)>1'cBj714W4(tSrqFsC3sZ;XmTHJjp1p$R#gp^ %'&*u;;'QG&nl<0V`X)ge,(050$8akNj!-9h??jhu)%/Zs8-ET--_r:fl0_+-?)1q@*0l\0qq3F.^_3;-4]=q'/F.rMG,''Db^aY,XOZG., %BetKS.7A6Mb2SfqW5!_$``9<8a^7h%j<\)&BMd5YWR4U\Dt]H4QL?[I&fA1TG*JKXJ[)!NZZ(A$_GBgN0_3goars]4tdE %"n`Q%Qqo"c^u.g]4sd*[B`g&ZbZVC.U6aQc;'LQ#dN=C"2Uq(I86DnMCqe([tgkUbdZ&\L[M#\1to6A]8/a;IGum-M%uC %c/X7?V+b.R9jB2+W2#kOIZNdQO`bZTM$DDOBp5]D)B3j-)idk(X'#pi8.)Kd`/a\fT>$=`oqqne4o6WFbF%=h1th7SP2>BU$cb0@ %K\4aknd4WQ5,k0KbokMe=`"U>(im$5Y4@iRa/Z+6*SN`(3;SK=B'5p2+&TUHOq@3`I$5[/[KQ %ZVu38XCA3&/@;'*&QVV'MbZ6P&n1!-=/ss\^A\+\Z#sSJeroK#C(\mL:-28WG?Z4&?+qSF[7a1$jgpKpQ[Mi;^fA/R>UJs)Cl-Z[!:\9m!.R?L)7u@kV %_>3#fQ<.AUK[O,MZ=U^92(KU*n]pRb<9<^n<9doOFR=i8iF-N#pFdIfjV&:A'G9H,P=^.NCeloY;4n15Nl95KFD-VI,NG>iLG;u5 %ZgIHE,$pX?S$9=&?f:QG/K&<.*:&-+&iZ>Hf8b8Z2VU(8;\uhaX7jC;n4EMr_57=`+S70Yeu>;j.-H)'*/fE#/.s[B1<$+u!I`8= %mLq\$XHa^Q`o$+LmmLa7;.BPlrTdSEK_;2?C#!jGq-1:O:(QC %6''*OW&_s+>\Jb"0^Vh/FSKOd)VPL_WEHsF$IrYbOn4+1jsa3JZ0HU#8.:jfk+ghmW.0r:cgh'@jV6XN0r34'jYr %aeUijT09k+*L;oHrP-E<(KjH6[16`sBJlB3gX.o5J2Ps.!K1hPjZtjg,!]^Jicuha40WN'kmo.oK,4Zb3s^9jK6a*VpgQ^qPjr>S %RpOUZLK)#j-;"*K=Mr7^g-22,;qk(%9t-NkNHlkC7oK!KJO3I- %s/j#J'C(%<'WLXuhp=fgLeIkMFsa*T[9#*skeCSlGNODK.&6jL]q@4u2?W>#O28]:Vs*1j:kJ05J/1ZV3!;l9/2&Yr2_!H/*KXQ-I*8>k2BFE[K\r %,#[KK_.Yb\EI2(\39OP.g0oH8">E*mAWl'ueUZQW@k2aoE6Q]dfW>g?$H:i9FNSeKD(L[j*8X_!I1c %\HHUhYG(>WG<=qXE`+L&?[=>V18Lq7l_?1(MM#r>'eoPZ2i?oP,6$B:JAn5PNUYZMr5=sm*Tp#83>oEXFpUc.U&1Ye;mq(.\<02f %RK_Y+4""d9()+iS=MCo\>Qr]c(`%?q_"]jn:e@?L>%dtQd/AS>.ZjH74C-CM$:2I(!*UN6^bsp#'AN?(E[6_$dTNHL4+<,KOddsC %ctHo_Y77*6Nqa9lkcGCEUGsN'#n7!MloDkoBu4Fs(mCeD.S$RIQ6a-DPJPu*0]m2XEgiLBLVe'WZH/bk^*R8/*.<^unAGgWd8eTf %OJ98g>bEaA1F[t:X!*&Sa;o\iNq3Z2=J,rlgJ2'N/2_bj?O6IUQlmQD/A+83@*%*\H^ %KD/G8d9LGFS6uWu2HR.D`BFdEFNQfN?0'5GC0$++kWN[(>p;t2:k=tZHYeoGe[0)[D7 %H]N'tRtBJ$)?#:WDa6_2&\Mc8+=a#ZP]2O*XN]8PL>_#!-c4fLaiZOD]<\meIg^jI-g+5C"bM+4@*eE*_P's^e`B":%41s#SC8`7 %`WS1.paY,'X#ddRiB_uq+0]&SP)`o1:<1_aN_CGng[1cEJSM6.8i*X^DpO\`W=d-3J<"/4Fbrf[K+o*)/Y/!@3 %kh1\1hO)tdqT%jp`+D(-4n^"f2tp(U&^oGUYsIhn7Fi@]CT8IYO3jFQ?(h<(=C)K&9]-j.:0Z#23rNJC4i[nkO)\oJ*"U$:)=WJH %!i7J\Trn'!:Hpe8aTN%I:K*S*_$oP\g+LpIlqL!P3Ai-mrpMkK:j5$E!Bnb+a=Qlfq+pr50nZV0MhLRH,(m`cp!k^=W&`PqVhX'INk(L3oRuN>ipU,h$EM;'>MZb %]XHE/Oej3-)?>fQNj5t"#a"r,`0df"Cs]N&JZpdpL7W)1G.<;sM0^a8?rE]hK^D1FSA(H.3HkQ%618I`_1[Vg#5J,-.ZY`\o3C8V %N3CS$0irEnYg,_@^Jhi"^72Fq]V^ku]3:3.9?://+)B7o-D<(>!\B.blJ_!tjf:iJFUYb]=%/q8m`iB07JuRia6pls.ia;?NG,+A %1FaI3b0#eMrTEr6?]Ad46oCe!0X:)C,,p!Vi>#gn""1,>\/Wk!jRrB]`l)QM9a+2/Rp?=b.<\'A/ga %Jh"fcS:,Koht@rY_kRD3KDL7H$bkfP3GeS-8Z8Y@:_>kl^EiHa+LHC,)Z+iGlHQ0Of;hmJYIIs?Hmh8'%WnOpH!mjcH+d)sR#dLX %CCUT\FB2@H$+j!i"9pirI7]A9?0G@i!s:)(lKdl!BOOkY0#^k9Ph=fWf3g/-g?\VdYN)50:5YiW?11eG#KqtqEc=lIlZJAD*OLMg %78!FrOXO*QSQDa?&g^JM?=3YOOEGl5%ohVb50h[+M,fb5%3h %S5;S^/?5ksA,G8N.Q&"7TWS4O3j!'H2c)#cr/a84M9oM5\@qn6*mOhTHjquZ.mai>X7N/\0@tjm@GNu`G_QA;$`.3H47eP&Rssm_j[_/FB`$^!ZY-V]6/>H/bX9%Ksk302r2n!1"pNu %mVTOhC<2XnDVa,j$YJ92gMYj'FA#`l-/3V/m\X1o25J`FlgCWS)C%`+Xh1'?Tfs&\D7=?U2Vit10u^S*G;n=ZDc(DaA\5G9(@8&r %fQQTKVH[FZb&+WUDRp#MIC6c&e2Q2E7XM;Hh7C5_()VsMgTA)WDXe_dYI*,s]BL*tW5Z1#P#/9ur!?b4&2gl./?OT(."o/c>NUHj %A:lNaj!nqT>c<#+\LL3a8'gk7i\S$=/4/"IG4Z:S^-lL7Z0;n#ah@$UE-TKilapT1Jh%qrp-[&`SnCWs4.d.),r>)o/Fok%M0k8GVcmS0W22!u0j4Z!8 %BZKtVR+jjL__*)M8!5PgpM&f!f*'g*FuXs0[Ze+`BoXZ.l02-dbF%lHDaIP>Od$5E:hgh[D9F-G2B4LjNFAP^#pDQDR@E)D!edTf %LD`UrkO%Pa?SkWaN3XL#0D.CBerja+7W>1bfcOUG^oY:@9kO"S_Vi1mXiBsnrN@1<(]Fkus*9FI5Jljo+8kVC@AkoUHiJ@BrsHLZ %A'[S]p[ij?Tfu(d2]sfaYikfS&Xh("l4`Z1[q(>5555o %i4lY=bu"]5r\+&."98/qkPt:KJ%gEOIihtj^G1+4772=Tc@F78cPm?R_dT2#hstm27sLOf0,P2XFZY!P0=^$53!heM`oqJuh#[!/ %_FR#s_bRBg']AEdLRk":i=S37GieQMn?^TO+Q;ti6@aR+#C69$1[$-4r7atJ_!d:/nTPuYEdEglA0]>q@m*'u6H1%D%C'RHf_HTu %YWSF>?\e\H!IP%M^BBl/V^Lg8:C6ifi/-EBn>:.Ylj$V-RscH!+U$X(B&IoBV"&a:B>EmJguIpU#DU&Z69_;+9RrTqIV6Fuh\?JX %I3(eJf*ebWdEB%8"`V`P%#,%4T?$29"#bH35N&FM4R6,D %cbQ#;5(E'ocOIn[%=-@&q:n9qT+CG?'*t89K`#5j`s[E:,31$)ILgphcg;-qI:;W4J.ll(;!e]Qr>n`G]o35Ng$o9;*Y73mn[ia# %"UiQhbRG"`)KP]1m+Md@!e**f*^CSc_[YJapO^e>K$LS:b`!&r,Y3H0H+`'@DD-JC]G+%@fF=4lhZ\`Qo(dn$8Kgk0nD=\0(dM&> %DbFCL!%G7gZRZC0k6&WmqnqWE^F+sGoRYWk+BqSdqKuZ\d*'7o#5gnkJro%)3ut4mY(_RcgCBpK3&6_rX.]qpfZ5g'&3XMis%"O4:'.pYk>dH%iDk_NWOUi$c$?@on*:!4h`_X %_!2=(]YrL2Mco+RR/l,iHpP[Xpimo0$+CQZ#Smn9!89g0$%sr;p)TBMn%l:LEJe^2ZR4D`"V($DL_HZRNLs9-l0U_qhSVk?3B3*/,C\+EF?f!B@fO]Kj9_h?=pR=4at-VaAFW8p`I[D=*;9K %s3CV3.>f)G9j7La%(63p"rR3>$3)ecr\YW[Pl^%Dgjhes:0AY6_4h"c#=ooN0-&uT'nk2SFptn>>(WIu(kh0Go%N?;E1_f8_6D-@ %+K7`B+M)`j7Htfc>CL!G)&E_V"8r%_)RtuYI\5e^-BH1b#^AIFo*(2Q;+c>ua6JGAm8c[H8*!KA=F:%6mNVkgJ8A)fQGNg+X,X9ihR`IZA9:epclY]H,!&01]6F$X;b[J[@O\,"roEe>NU %\",])DaX:#B^:4lm/H1"-j#2O!/hP>)D!8hhsocWf[&+HCAZoKkGUjD=b1%a6XVDg)pSJsG[kGAS(+m!q#]3+ZeaR>:&YTNIolN` %I^018r0qON47LbA^YRN,5O]$TamjO^FhTT!rsoOub:E~> %AI9_PrivateDataEnd \ No newline at end of file diff --git a/docs/xen-api/xenapi-coversheet.tex b/docs/xen-api/xenapi-coversheet.tex new file mode 100644 index 0000000..a8ed57a --- /dev/null +++ b/docs/xen-api/xenapi-coversheet.tex @@ -0,0 +1,38 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +%% Document title +\newcommand{\doctitle}{Xen Management API} + +\newcommand{\coversheetlogo}{xen.eps} + +%% Document date +\newcommand{\datestring}{24th July 2008} + +\newcommand{\releasestatement}{Stable Release} + +%% Document revision +\newcommand{\revstring}{API Revision 1.0.6} + +%% Document authors +\newcommand{\docauthors}{ +Ewan Mellor: & {\tt ewan@xensource.com} \\ +Richard Sharp: & {\tt richard.sharp@xensource.com} \\ +David Scott: & {\tt david.scott@xensource.com}} +\newcommand{\legalnotice}{Copyright \copyright{} 2006-2007 XenSource, Inc.\\ \\ +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU Free Documentation License, Version 1.2 or any later +version published by the Free Software Foundation; with no Invariant Sections, +no Front-Cover Texts and no Back-Cover Texts. A copy of the license is +included in the section entitled "GNU Free Documentation License". +} diff --git a/docs/xen-api/xenapi-datamodel-graph.dot b/docs/xen-api/xenapi-datamodel-graph.dot new file mode 100644 index 0000000..62590b4 --- /dev/null +++ b/docs/xen-api/xenapi-datamodel-graph.dot @@ -0,0 +1,44 @@ +# +# Copyright (c) 2006-2007 XenSource, Inc. +# +# Permission is granted to copy, distribute and/or modify this document under +# the terms of the GNU Free Documentation License, Version 1.2 or any later +# version published by the Free Software Foundation; with no Invariant +# Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +# license is included in the section entitled +# "GNU Free Documentation License" or the file fdl.tex. +# + +digraph "Xen-API Class Diagram" { +fontname="Verdana"; + +node [ shape=box ]; session VM host network VIF PIF SR VDI VBD PBD user XSPolicy ACMPolicy; +node [shape=ellipse]; PIF_metrics VIF_metrics VM_metrics VBD_metrics PBD_metrics VM_guest_metrics host_metrics; +node [shape=box]; DPCI PPCI host_cpu console VTPM +session -> host [ arrowhead="none" ] +session -> user [ arrowhead="none" ] +VM -> VM_metrics [ arrowhead="none" ] +VM -> VM_guest_metrics [ arrowhead="none" ] +VM -> console [ arrowhead="crow" ] +host -> PBD [ arrowhead="crow", arrowtail="none" ] +host -> host_metrics [ arrowhead="none" ] +host -> host_cpu [ arrowhead="crow", arrowtail="none" ] +VIF -> VM [ arrowhead="none", arrowtail="crow" ] +VIF -> network [ arrowhead="none", arrowtail="crow" ] +VIF -> VIF_metrics [ arrowhead="none" ] +PIF -> host [ arrowhead="none", arrowtail="crow" ] +PIF -> network [ arrowhead="none", arrowtail="crow" ] +PIF -> PIF_metrics [ arrowhead="none" ] +SR -> PBD [ arrowhead="crow", arrowtail="none" ] +PBD -> PBD_metrics [ arrowhead="none" ] +SR -> VDI [ arrowhead="crow", arrowtail="none" ] +VDI -> VBD [ arrowhead="crow", arrowtail="none" ] +VBD -> VM [ arrowhead="none", arrowtail="crow" ] +VTPM -> VM [ arrowhead="none", arrowtail="crow" ] +VBD -> VBD_metrics [ arrowhead="none" ] +XSPolicy -> host [ arrowhead="none" ] +XSPolicy -> ACMPolicy [ arrowhead="none" ] +DPCI -> VM [ arrowhead="none", arrowtail="crow" ] +DPCI -> PPCI [ arrowhead="none" ] +PPCI -> host [ arrowhead="none", arrowtail="crow" ] +} diff --git a/docs/xen-api/xenapi-datamodel.tex b/docs/xen-api/xenapi-datamodel.tex new file mode 100644 index 0000000..7589489 --- /dev/null +++ b/docs/xen-api/xenapi-datamodel.tex @@ -0,0 +1,16911 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +\chapter{API Reference} +\label{api-reference} + + +\section{Classes} +The following classes are defined: + +\begin{center}\begin{tabular}{|lp{10cm}|} +\hline +Name & Description \\ +\hline +{\tt session} & A session \\ +{\tt task} & A long-running asynchronous task \\ +{\tt event} & Asynchronous event registration and handling \\ +{\tt VM} & A virtual machine (or 'guest') \\ +{\tt VM\_metrics} & The metrics associated with a VM \\ +{\tt VM\_guest\_metrics} & The metrics reported by the guest (as opposed to inferred from outside) \\ +{\tt host} & A physical host \\ +{\tt host\_metrics} & The metrics associated with a host \\ +{\tt host\_cpu} & A physical CPU \\ +{\tt network} & A virtual network \\ +{\tt VIF} & A virtual network interface \\ +{\tt VIF\_metrics} & The metrics associated with a virtual network device \\ +{\tt PIF} & A physical network interface (note separate VLANs are represented as several PIFs) \\ +{\tt PIF\_metrics} & The metrics associated with a physical network interface \\ +{\tt SR} & A storage repository \\ +{\tt VDI} & A virtual disk image \\ +{\tt VBD} & A virtual block device \\ +{\tt VBD\_metrics} & The metrics associated with a virtual block device \\ +{\tt PBD} & The physical block devices through which hosts access SRs \\ +{\tt crashdump} & A VM crashdump \\ +{\tt VTPM} & A virtual TPM device \\ +{\tt console} & A console \\ +{\tt DPCI} & A pass-through PCI device \\ +{\tt PPCI} & A physical PCI device \\ +{\tt user} & A user of the system \\ +{\tt debug} & A basic class for testing \\ +{\tt XSPolicy} & A class for handling Xen Security Policies \\ +{\tt ACMPolicy} & A class for handling ACM-type policies \\ +\hline +\end{tabular}\end{center} +\section{Relationships Between Classes} +Fields that are bound together are shown in the following table: +\begin{center}\begin{tabular}{|ll|l|} +\hline +{\em object.field} & {\em object.field} & {\em relationship} \\ + +\hline +host.PBDs & PBD.host & many-to-one\\ +SR.PBDs & PBD.SR & many-to-one\\ +VDI.VBDs & VBD.VDI & many-to-one\\ +VDI.crash\_dumps & crashdump.VDI & many-to-one\\ +VBD.VM & VM.VBDs & one-to-many\\ +crashdump.VM & VM.crash\_dumps & one-to-many\\ +VIF.VM & VM.VIFs & one-to-many\\ +VIF.network & network.VIFs & one-to-many\\ +PIF.host & host.PIFs & one-to-many\\ +PIF.network & network.PIFs & one-to-many\\ +SR.VDIs & VDI.SR & many-to-one\\ +VTPM.VM & VM.VTPMs & one-to-many\\ +console.VM & VM.consoles & one-to-many\\ +DPCI.VM & VM.DPCIs & one-to-many\\ +PPCI.host & host.PPCIs & one-to-many\\ +host.resident\_VMs & VM.resident\_on & many-to-one\\ +host.host\_CPUs & host\_cpu.host & many-to-one\\ +\hline +\end{tabular}\end{center} + +The following represents bound fields (as specified above) diagrammatically, using crows-foot notation to specify one-to-one, one-to-many or many-to-many + relationships: + +\begin{center}\resizebox{0.8\textwidth}{!}{ +\includegraphics{xenapi-datamodel-graph} +}\end{center} +\ +\subsection{List of bound fields} +\section{Types} +\subsection{Primitives} +The following primitive types are used to specify methods and fields in the API Reference: + +\begin{center}\begin{tabular}{|ll|} +\hline +Type & Description \\ +\hline +String & text strings \\ +Int & 64-bit integers \\ +Float & IEEE double-precision floating-point numbers \\ +Bool & boolean \\ +DateTime & date and timestamp \\ +Ref (object name) & reference to an object of class name \\ +\hline +\end{tabular}\end{center} +\subsection{Higher order types} +The following type constructors are used: + +\begin{center}\begin{tabular}{|ll|} +\hline +Type & Description \\ +\hline +List (t) & an arbitrary-length list of elements of type t \\ +Map (a $\rightarrow$ b) & a table mapping values of type a to values of type b \\ +\hline +\end{tabular}\end{center} +\subsection{Enumeration types} +The following enumeration types are used: + +\begin{longtable}{|ll|} +\hline +{\tt enum event\_operation} & \\ +\hline +\hspace{0.5cm}{\tt add} & An object has been created \\ +\hspace{0.5cm}{\tt del} & An object has been deleted \\ +\hspace{0.5cm}{\tt mod} & An object has been modified \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum console\_protocol} & \\ +\hline +\hspace{0.5cm}{\tt vt100} & VT100 terminal \\ +\hspace{0.5cm}{\tt rfb} & Remote FrameBuffer protocol (as used in VNC) \\ +\hspace{0.5cm}{\tt rdp} & Remote Desktop Protocol \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum vdi\_type} & \\ +\hline +\hspace{0.5cm}{\tt system} & a disk that may be replaced on upgrade \\ +\hspace{0.5cm}{\tt user} & a disk that is always preserved on upgrade \\ +\hspace{0.5cm}{\tt ephemeral} & a disk that may be reformatted on upgrade \\ +\hspace{0.5cm}{\tt suspend} & a disk that stores a suspend image \\ +\hspace{0.5cm}{\tt crashdump} & a disk that stores VM crashdump information \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum vm\_power\_state} & \\ +\hline +\hspace{0.5cm}{\tt Halted} & Halted \\ +\hspace{0.5cm}{\tt Paused} & Paused \\ +\hspace{0.5cm}{\tt Running} & Running \\ +\hspace{0.5cm}{\tt Suspended} & Suspended \\ +\hspace{0.5cm}{\tt Crashed} & Crashed \\ +\hspace{0.5cm}{\tt Unknown} & Some other unknown state \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum task\_allowed\_operations} & \\ +\hline +\hspace{0.5cm}{\tt Cancel} & Cancel \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum task\_status\_type} & \\ +\hline +\hspace{0.5cm}{\tt pending} & task is in progress \\ +\hspace{0.5cm}{\tt success} & task was completed successfully \\ +\hspace{0.5cm}{\tt failure} & task has failed \\ +\hspace{0.5cm}{\tt cancelling} & task is being cancelled \\ +\hspace{0.5cm}{\tt cancelled} & task has been cancelled \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum on\_normal\_exit} & \\ +\hline +\hspace{0.5cm}{\tt destroy} & destroy the VM state \\ +\hspace{0.5cm}{\tt restart} & restart the VM \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum on\_crash\_behaviour} & \\ +\hline +\hspace{0.5cm}{\tt destroy} & destroy the VM state \\ +\hspace{0.5cm}{\tt coredump\_and\_destroy} & record a coredump and then destroy the VM state \\ +\hspace{0.5cm}{\tt restart} & restart the VM \\ +\hspace{0.5cm}{\tt coredump\_and\_restart} & record a coredump and then restart the VM \\ +\hspace{0.5cm}{\tt preserve} & leave the crashed VM as-is \\ +\hspace{0.5cm}{\tt rename\_restart} & rename the crashed VM and start a new copy \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum vbd\_mode} & \\ +\hline +\hspace{0.5cm}{\tt RO} & disk is mounted read-only \\ +\hspace{0.5cm}{\tt RW} & disk is mounted read-write \\ +\hline +\end{longtable} + +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum vbd\_type} & \\ +\hline +\hspace{0.5cm}{\tt CD} & VBD will appear to guest as CD \\ +\hspace{0.5cm}{\tt Disk} & VBD will appear to guest as disk \\ +\hline +\end{longtable} + +\vspace{1cm} +\newpage + +\section{Error Handling} +When a low-level transport error occurs, or a request is malformed at the HTTP +or XML-RPC level, the server may send an XML-RPC Fault response, or the client +may simulate the same. The client must be prepared to handle these errors, +though they may be treated as fatal. On the wire, these are transmitted in a +form similar to this: + +\begin{verbatim} + + + + + + faultCode + -1 + + + faultString + Malformed request + + + + + +\end{verbatim} + +All other failures are reported with a more structured error response, to +allow better automatic response to failures, proper internationalisation of +any error message, and easier debugging. On the wire, these are transmitted +like this: + +\begin{verbatim} + + + Status + Failure + + + ErrorDescription + + + + MAP_DUPLICATE_KEY + Customer + eSpeil Inc. + eSpeil Incorporated + + + + + +\end{verbatim} + +Note that {\tt ErrorDescription} value is an array of string values. The +first element of the array is an error code; the remainder of the array are +strings representing error parameters relating to that code. In this case, +the client has attempted to add the mapping {\tt Customer $\rightarrow$ +eSpiel Incorporated} to a Map, but it already contains the mapping +{\tt Customer $\rightarrow$ eSpiel Inc.}, and so the request has failed. + +The reference below lists each possible error returned by each method. +As well as the errors explicitly listed, any method may return low-level +errors as described above, or any of the following generic errors: + +\begin{itemize} +\item HANDLE\_INVALID +\item INTERNAL\_ERROR +\item MAP\_DUPLICATE\_KEY +\item MESSAGE\_METHOD\_UNKNOWN +\item MESSAGE\_PARAMETER\_COUNT\_MISMATCH +\item OPERATION\_NOT\_ALLOWED +\item PERMISSION\_DENIED +\item SESSION\_INVALID +\end{itemize} + +Each possible error code is documented in the following section. + +\subsection{Error Codes} + +\subsubsection{HANDLE\_INVALID} + +You gave an invalid handle. The object may have recently been deleted. +The class parameter gives the type of reference given, and the handle +parameter echoes the bad value given. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}HANDLE_INVALID(class, handle)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{INTERNAL\_ERROR} + +The server failed to handle your request, due to an internal error. The +given message may give details useful for debugging the problem. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}INTERNAL_ERROR(message)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{MAP\_DUPLICATE\_KEY} + +You tried to add a key-value pair to a map, but that key is already there. +The key, current value, and the new value that you tried to set are all +echoed. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}MAP_DUPLICATE_KEY(key, current value, new value)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{MESSAGE\_METHOD\_UNKNOWN} + +You tried to call a method that does not exist. The method name that you +used is echoed. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}MESSAGE_METHOD_UNKNOWN(method)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{MESSAGE\_PARAMETER\_COUNT\_MISMATCH} + +You tried to call a method with the incorrect number of parameters. The +fully-qualified method name that you used, and the number of received and +expected parameters are returned. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}MESSAGE_PARAMETER_COUNT_MISMATCH(method, expected, received)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{NETWORK\_ALREADY\_CONNECTED} + +You tried to create a PIF, but the network you tried to attach it to is +already attached to some other PIF, and so the creation failed. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}NETWORK_ALREADY_CONNECTED(network, connected PIF)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{OPERATION\_NOT\_ALLOWED} + +You attempted an operation that was not allowed. + +\vspace{0.3cm} +No parameters. +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{PERMISSION\_DENIED} + +You do not have the required permissions to perform the operation. + +\vspace{0.3cm} +No parameters. +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{PIF\_IS\_PHYSICAL} + +You tried to destroy a PIF, but it represents an aspect of the physical +host configuration, and so cannot be destroyed. The parameter echoes the +PIF handle you gave. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}PIF_IS_PHYSICAL(PIF)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{SESSION\_AUTHENTICATION\_FAILED} + +The credentials given by the user are incorrect, so access has been denied, +and you have not been issued a session handle. + +\vspace{0.3cm} +No parameters. +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{SESSION\_INVALID} + +You gave an invalid session handle. It may have been invalidated by a +server restart, or timed out. You should get a new session handle, using +one of the session.login\_ calls. This error does not invalidate the +current connection. The handle parameter echoes the bad value given. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}SESSION_INVALID(handle)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{SESSION\_NOT\_REGISTERED} + +This session is not registered to receive events. You must call +event.register before event.next. The session handle you are using is +echoed. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}SESSION_NOT_REGISTERED(handle)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{VALUE\_NOT\_SUPPORTED} + +You attempted to set a value that is not supported by this implementation. +The fully-qualified field name and the value that you tried to set are +returned. Also returned is a developer-only diagnostic reason. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}VALUE_NOT_SUPPORTED(field, value, reason)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{VLAN\_TAG\_INVALID} + +You tried to create a VLAN, but the tag you gave was invalid -- it must be +between 0 and 4095. The parameter echoes the VLAN tag you gave. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}VLAN_TAG_INVALID(VLAN)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{VM\_BAD\_POWER\_STATE} + +You attempted an operation on a VM that was not in an appropriate power +state at the time; for example, you attempted to start a VM that was +already running. The parameters returned are the VM's handle, and the +expected and actual VM state at the time of the call. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}VM_BAD_POWER_STATE(vm, expected, actual)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{VM\_HVM\_REQUIRED} + +HVM is required for this operation + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}VM_HVM_REQUIRED(vm)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + +\subsubsection{SECURITY\_ERROR} + +A security error occurred. The parameter provides the xen security +error code and a message describing the error. + +\vspace{0.3cm} +{\bf Signature:} +\begin{verbatim}SECURITY_ERROR(xserr, message)\end{verbatim} +\begin{center}\rule{10em}{0.1pt}\end{center} + + +\newpage +\section{Class: session} +\subsection{Fields for class: session} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf session} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +session.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt this\_host} & host ref & Currently connected host \\ +$\mathit{RO}_\mathit{run}$ & {\tt this\_user} & user ref & Currently connected user \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_active} & int & Timestamp for last time session was active \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: session} +\subsubsection{RPC name:~login\_with\_password} + +{\bf Overview:} +Attempt to authenticate the user, returning a session\_id if successful. + + \noindent {\bf Signature:} +\begin{verbatim} (session ref) login_with_password (string uname, string pwd)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uname & Username for login. \\ \hline + +{\tt string } & pwd & Password for login. \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +session ref +} + + +ID of newly created session + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SESSION\_AUTHENTICATION\_FAILED} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~logout} + +{\bf Overview:} +Log out of a session. + + \noindent {\bf Signature:} +\begin{verbatim} void logout (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given session. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, session ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt session ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_this\_host} + +{\bf Overview:} +Get the this\_host field of the given session. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_this_host (session_id s, session ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt session ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_this\_user} + +{\bf Overview:} +Get the this\_user field of the given session. + + \noindent {\bf Signature:} +\begin{verbatim} (user ref) get_this_user (session_id s, session ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt session ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +user ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_active} + +{\bf Overview:} +Get the last\_active field of the given session. + + \noindent {\bf Signature:} +\begin{verbatim} int get_last_active (session_id s, session ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt session ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the session instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (session ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +session ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given session. + + \noindent {\bf Signature:} +\begin{verbatim} (session record) get_record (session_id s, session ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt session ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +session record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: task} +\subsection{Fields for class: task} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf task} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +long-running asynchronous task.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt name/label} & string & a human-readable name \\ +$\mathit{RO}_\mathit{run}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RO}_\mathit{run}$ & {\tt status} & task\_status\_type & current status of the task \\ +$\mathit{RO}_\mathit{run}$ & {\tt session} & session ref & the session that created the task \\ +$\mathit{RO}_\mathit{run}$ & {\tt progress} & int & if the task is still pending, this field contains the estimated percentage complete (0-100). If task has completed (successfully or unsuccessfully) this should be 100. \\ +$\mathit{RO}_\mathit{run}$ & {\tt type} & string & if the task has completed successfully, this field contains the type of the encoded result (i.e. name of the class whose reference is in the result field). Undefined otherwise. \\ +$\mathit{RO}_\mathit{run}$ & {\tt result} & string & if the task has completed successfully, this field contains the result value (either Void or an object reference). Undefined otherwise. \\ +$\mathit{RO}_\mathit{run}$ & {\tt error\_info} & string Set & if the task has failed, this field contains the set of associated error strings. Undefined otherwise. \\ +$\mathit{RO}_\mathit{run}$ & {\tt allowed\_operations} & (task\_allowed\_operations) Set & Operations allowed on this task \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: task} +\subsubsection{RPC name:~cancel} + +{\bf Overview:} +Cancel this task. If task.allowed\_operations does not contain Cancel, +then this will fail with OPERATION\_NOT\_ALLOWED. The task will show the +status 'cancelling', and you should continue to check its status until it +shows 'cancelled'. There is no guarantee as to the time within which this +task will be cancelled. + + \noindent {\bf Signature:} +\begin{verbatim} void cancel (session_id s, task ref task)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & task & The task \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt OPERATION\_NOT\_ALLOWED} + +\vspace{0.6cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the tasks known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((task ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(task ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_label} + +{\bf Overview:} +Get the name/label field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_label (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_description} + +{\bf Overview:} +Get the name/description field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_description (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_status} + +{\bf Overview:} +Get the status field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} (task_status_type) get_status (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +task\_status\_type +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_session} + +{\bf Overview:} +Get the session field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} (session ref) get_session (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +session ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_progress} + +{\bf Overview:} +Get the progress field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} int get_progress (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_type} + +{\bf Overview:} +Get the type field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_type (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_result} + +{\bf Overview:} +Get the result field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_result (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_error\_info} + +{\bf Overview:} +Get the error\_info field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_error_info (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_allowed\_operations} + +{\bf Overview:} +Get the allowed\_operations field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} ((task_allowed_operations) Set) get_allowed_operations (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(task\_allowed\_operations) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the task instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (task ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +task ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} (task record) get_record (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +task record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_name\_label} + +{\bf Overview:} +Get all the task instances with the given label. + + \noindent {\bf Signature:} +\begin{verbatim} ((task ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & label & label of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(task ref) Set +} + + +references to objects with match names +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: event} +\subsection{Fields for class: event} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf event} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +Asynchronous event registration and handling.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{ins}$ & {\tt id} & int & An ID, monotonically increasing, and local to the current session \\ +$\mathit{RO}_\mathit{ins}$ & {\tt timestamp} & datetime & The time at which the event occurred \\ +$\mathit{RO}_\mathit{ins}$ & {\tt class} & string & The name of the class of the object that changed \\ +$\mathit{RO}_\mathit{ins}$ & {\tt operation} & event\_operation & The operation that was performed \\ +$\mathit{RO}_\mathit{ins}$ & {\tt ref} & string & A reference to the object that changed \\ +$\mathit{RO}_\mathit{ins}$ & {\tt obj\_uuid} & string & The uuid of the object that changed \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: event} +\subsubsection{RPC name:~register} + +{\bf Overview:} +Registers this session with the event system. Specifying the empty list +will register for all classes. + + \noindent {\bf Signature:} +\begin{verbatim} void register (session_id s, string Set classes)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string Set } & classes & register for events for the indicated classes \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~unregister} + +{\bf Overview:} +Unregisters this session with the event system. + + \noindent {\bf Signature:} +\begin{verbatim} void unregister (session_id s, string Set classes)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string Set } & classes & remove this session's registration for the indicated classes \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~next} + +{\bf Overview:} +Blocking call which returns a (possibly empty) batch of events. + + \noindent {\bf Signature:} +\begin{verbatim} ((event record) Set) next (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(event record) Set +} + + +the batch of events +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SESSION\_NOT\_REGISTERED} + +\vspace{0.6cm} + +\vspace{1cm} +\newpage +\section{Class: VM} +\subsection{Fields for class: VM} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VM} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +virtual machine (or 'guest').}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt power\_state} & vm\_power\_state & Current power state of the machine \\ +$\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ +$\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RW}$ & {\tt user\_version} & int & a user version number for this machine \\ +$\mathit{RW}$ & {\tt is\_a\_template} & bool & true if this is a template. Template VMs can never be started, they are used only for cloning other VMs \\ +$\mathit{RW}$ & {\tt auto\_power\_on} & bool & true if this VM should be started automatically after host boot \\ +$\mathit{RO}_\mathit{run}$ & {\tt suspend\_VDI} & VDI ref & The VDI that a suspend image is stored on. (Only has meaning if VM is currently suspended) \\ +$\mathit{RO}_\mathit{run}$ & {\tt resident\_on} & host ref & the host the VM is currently resident on \\ +$\mathit{RW}$ & {\tt memory/static\_max} & int & Statically-set (i.e. absolute) maximum (bytes) \\ +$\mathit{RW}$ & {\tt memory/dynamic\_max} & int & Dynamic maximum (bytes) \\ +$\mathit{RW}$ & {\tt memory/dynamic\_min} & int & Dynamic minimum (bytes) \\ +$\mathit{RW}$ & {\tt memory/static\_min} & int & Statically-set (i.e. absolute) minimum (bytes) \\ +$\mathit{RW}$ & {\tt VCPUs/params} & (string $\rightarrow$ string) Map & configuration parameters for the selected VCPU policy \\ +$\mathit{RW}$ & {\tt VCPUs/max} & int & Max number of VCPUs \\ +$\mathit{RW}$ & {\tt VCPUs/at\_startup} & int & Boot number of VCPUs \\ +$\mathit{RW}$ & {\tt actions/after\_shutdown} & on\_normal\_exit & action to take after the guest has shutdown itself \\ +$\mathit{RW}$ & {\tt actions/after\_reboot} & on\_normal\_exit & action to take after the guest has rebooted itself \\ +$\mathit{RW}$ & {\tt actions/after\_crash} & on\_crash\_behaviour & action to take if the guest crashes \\ +$\mathit{RO}_\mathit{run}$ & {\tt consoles} & (console ref) Set & virtual console devices \\ +$\mathit{RO}_\mathit{run}$ & {\tt VIFs} & (VIF ref) Set & virtual network interfaces \\ +$\mathit{RO}_\mathit{run}$ & {\tt VBDs} & (VBD ref) Set & virtual block devices \\ +$\mathit{RO}_\mathit{run}$ & {\tt crash\_dumps} & (crashdump ref) Set & crash dumps associated with this VM \\ +$\mathit{RO}_\mathit{run}$ & {\tt VTPMs} & (VTPM ref) Set & virtual TPMs \\ +$\mathit{RO}_\mathit{run}$ & {\tt DPCIs} & (DPCI ref) Set & pass-through PCI devices \\ +$\mathit{RW}$ & {\tt PV/bootloader} & string & name of or path to bootloader \\ +$\mathit{RW}$ & {\tt PV/kernel} & string & path to the kernel \\ +$\mathit{RW}$ & {\tt PV/ramdisk} & string & path to the initrd \\ +$\mathit{RW}$ & {\tt PV/args} & string & kernel command-line arguments \\ +$\mathit{RW}$ & {\tt PV/bootloader\_args} & string & miscellaneous arguments for the bootloader \\ +$\mathit{RW}$ & {\tt HVM/boot\_policy} & string & HVM boot policy \\ +$\mathit{RW}$ & {\tt HVM/boot\_params} & (string $\rightarrow$ string) Map & HVM boot params \\ +$\mathit{RW}$ & {\tt platform} & (string $\rightarrow$ string) Map & platform-specific configuration \\ +$\mathit{RW}$ & {\tt PCI\_bus} & string & PCI bus path for pass-through devices \\ +$\mathit{RW}$ & {\tt other\_config} & (string $\rightarrow$ string) Map & additional configuration \\ +$\mathit{RO}_\mathit{run}$ & {\tt domid} & int & domain ID (if available, -1 otherwise) \\ +$\mathit{RO}_\mathit{run}$ & {\tt is\_control\_domain} & bool & true if this is a control domain (domain 0 or a driver domain) \\ +$\mathit{RO}_\mathit{run}$ & {\tt metrics} & VM\_metrics ref & metrics associated with this VM \\ +$\mathit{RO}_\mathit{run}$ & {\tt guest\_metrics} & VM\_guest\_metrics ref & metrics associated with the running guest \\ +$\mathit{RO}_\mathit{run}$ & {\tt security/label} & string & the VM's security label \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VM} +\subsubsection{RPC name:~clone} + +{\bf Overview:} +Clones the specified VM, making a new VM. Clone automatically exploits the +capabilities of the underlying storage repository in which the VM's disk +images are stored (e.g. Copy on Write). This function can only be called +when the VM is in the Halted State. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) clone (session_id s, VM ref vm, string new_name)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to be cloned \\ \hline + +{\tt string } & new\_name & The name of the cloned VM \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +The ID of the newly created VM. +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~start} + +{\bf Overview:} +Start the specified VM. This function can only be called with the VM is in +the Halted State. + + \noindent {\bf Signature:} +\begin{verbatim} void start (session_id s, VM ref vm, bool start_paused)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to start \\ \hline + +{\tt bool } & start\_paused & Instantiate VM in paused state if set to true. \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE}, {\tt +VM\_HVM\_REQUIRED} + +\vspace{0.6cm} +\subsubsection{RPC name:~pause} + +{\bf Overview:} +Pause the specified VM. This can only be called when the specified VM is in +the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void pause (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to pause \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~unpause} + +{\bf Overview:} +Resume the specified VM. This can only be called when the specified VM is +in the Paused state. + + \noindent {\bf Signature:} +\begin{verbatim} void unpause (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to unpause \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~clean\_shutdown} + +{\bf Overview:} +Attempt to cleanly shutdown the specified VM. (Note: this may not be +supported---e.g. if a guest agent is not installed). + +Once shutdown has been completed perform poweroff action specified in guest +configuration. + +This can only be called when the specified VM is in the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void clean_shutdown (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to shutdown \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~clean\_reboot} + +{\bf Overview:} +Attempt to cleanly shutdown the specified VM (Note: this may not be +supported---e.g. if a guest agent is not installed). + +Once shutdown has been completed perform reboot action specified in guest +configuration. + +This can only be called when the specified VM is in the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void clean_reboot (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to shutdown \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~hard\_shutdown} + +{\bf Overview:} +Stop executing the specified VM without attempting a clean shutdown. Then +perform poweroff action specified in VM configuration. + + \noindent {\bf Signature:} +\begin{verbatim} void hard_shutdown (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to destroy \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~hard\_reboot} + +{\bf Overview:} +Stop executing the specified VM without attempting a clean shutdown. Then +perform reboot action specified in VM configuration. + + \noindent {\bf Signature:} +\begin{verbatim} void hard_reboot (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to reboot \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~suspend} + +{\bf Overview:} +Suspend the specified VM to disk. This can only be called when the +specified VM is in the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void suspend (session_id s, VM ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to suspend \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~resume} + +{\bf Overview:} +Awaken the specified VM and resume it. This can only be called when the +specified VM is in the Suspended state. + + \noindent {\bf Signature:} +\begin{verbatim} void resume (session_id s, VM ref vm, bool start_paused)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM to resume \\ \hline + +{\tt bool } & start\_paused & Resume VM in paused state if set to true. \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~set\_VCPUs\_number\_live} + +{\bf Overview:} +Set this VM's VCPUs/at\_startup value, and set the same value on the VM, if +running. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_number_live (session_id s, VM ref self, int nvcpu)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & The VM \\ \hline + +{\tt int } & nvcpu & The number of VCPUs \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_VCPUs\_params\_live} + +{\bf Overview:} +Add the given key-value pair to VM.VCPUs\_params, and apply that value on +the running VM. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_VCPUs_params_live (session_id s, VM ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & The VM \\ \hline + +{\tt string } & key & The key \\ \hline + +{\tt string } & value & The value \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_memory\_dynamic\_max\_live} + +{\bf Overview:} +Set memory\_dynamic\_max in database and on running VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_memory_dynamic_max_live (session_id s, VM ref self, int max)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & The VM \\ \hline + +{\tt int } & max & The memory\_dynamic\_max value \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_memory\_dynamic\_min\_live} + +{\bf Overview:} +Set memory\_dynamic\_min in database and on running VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_memory_dynamic_min_live (session_id s, VM ref self, int min)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & The VM \\ \hline + +{\tt int } & min & The memory\_dynamic\_min value \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~send\_sysrq} + +{\bf Overview:} +Send the given key as a sysrq to this VM. The key is specified as a single +character (a String of length 1). This can only be called when the +specified VM is in the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void send_sysrq (session_id s, VM ref vm, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM \\ \hline + +{\tt string } & key & The key to send \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~send\_trigger} + +{\bf Overview:} +Send the named trigger to this VM. This can only be called when the +specified VM is in the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void send_trigger (session_id s, VM ref vm, string trigger)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM \\ \hline + +{\tt string } & trigger & The trigger to send \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~migrate} + +{\bf Overview:} +Migrate the VM to another host. This can only be called when the specified +VM is in the Running state. + + \noindent {\bf Signature:} +\begin{verbatim} void migrate (session_id s, VM ref vm, string dest, bool live, (string -> string) Map options)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & vm & The VM \\ \hline + +{\tt string } & dest & The destination host \\ \hline + +{\tt bool } & live & Live migration \\ \hline + +{\tt (string $\rightarrow$ string) Map } & options & Other parameters \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VM\_BAD\_POWER\_STATE} + +\vspace{0.6cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VMs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VM ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VM ref) Set +} + + +A list of all the IDs of all the VMs +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_power\_state} + +{\bf Overview:} +Get the power\_state field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (vm_power_state) get_power_state (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +vm\_power\_state +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_label} + +{\bf Overview:} +Get the name/label field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_label (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_label} + +{\bf Overview:} +Set the name/label field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_label (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_description} + +{\bf Overview:} +Get the name/description field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_description (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_description} + +{\bf Overview:} +Set the name/description field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_description (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_user\_version} + +{\bf Overview:} +Get the user\_version field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_user_version (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_user\_version} + +{\bf Overview:} +Set the user\_version field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_user_version (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_is\_a\_template} + +{\bf Overview:} +Get the is\_a\_template field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_is_a_template (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_is\_a\_template} + +{\bf Overview:} +Set the is\_a\_template field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_is_a_template (session_id s, VM ref self, bool value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt bool } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_auto\_power\_on} + +{\bf Overview:} +Get the auto\_power\_on field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_auto_power_on (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_auto\_power\_on} + +{\bf Overview:} +Set the auto\_power\_on field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_auto_power_on (session_id s, VM ref self, bool value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt bool } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_suspend\_VDI} + +{\bf Overview:} +Get the suspend\_VDI field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (VDI ref) get_suspend_VDI (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VDI ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_resident\_on} + +{\bf Overview:} +Get the resident\_on field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_resident_on (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_static\_max} + +{\bf Overview:} +Get the memory/static\_max field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_static_max (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_memory\_static\_max} + +{\bf Overview:} +Set the memory/static\_max field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_memory_static_max (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_dynamic\_max} + +{\bf Overview:} +Get the memory/dynamic\_max field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_dynamic_max (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_memory\_dynamic\_max} + +{\bf Overview:} +Set the memory/dynamic\_max field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_memory_dynamic_max (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_dynamic\_min} + +{\bf Overview:} +Get the memory/dynamic\_min field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_dynamic_min (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_memory\_dynamic\_min} + +{\bf Overview:} +Set the memory/dynamic\_min field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_memory_dynamic_min (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_static\_min} + +{\bf Overview:} +Get the memory/static\_min field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_static_min (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_memory\_static\_min} + +{\bf Overview:} +Set the memory/static\_min field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_memory_static_min (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_params} + +{\bf Overview:} +Get the VCPUs/params field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_VCPUs_params (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VCPUs\_params} + +{\bf Overview:} +Set the VCPUs/params field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_params (session_id s, VM ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_VCPUs\_params} + +{\bf Overview:} +Add the given key-value pair to the VCPUs/params field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_VCPUs_params (session_id s, VM ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_VCPUs\_params} + +{\bf Overview:} +Remove the given key and its corresponding value from the VCPUs/params +field of the given VM. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_VCPUs_params (session_id s, VM ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_max} + +{\bf Overview:} +Get the VCPUs/max field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_VCPUs_max (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VCPUs\_max} + +{\bf Overview:} +Set the VCPUs/max field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_max (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_at\_startup} + +{\bf Overview:} +Get the VCPUs/at\_startup field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_VCPUs_at_startup (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VCPUs\_at\_startup} + +{\bf Overview:} +Set the VCPUs/at\_startup field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_at_startup (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_actions\_after\_shutdown} + +{\bf Overview:} +Get the actions/after\_shutdown field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (on_normal_exit) get_actions_after_shutdown (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +on\_normal\_exit +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_actions\_after\_shutdown} + +{\bf Overview:} +Set the actions/after\_shutdown field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_actions_after_shutdown (session_id s, VM ref self, on_normal_exit value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt on\_normal\_exit } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_actions\_after\_reboot} + +{\bf Overview:} +Get the actions/after\_reboot field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (on_normal_exit) get_actions_after_reboot (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +on\_normal\_exit +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_actions\_after\_reboot} + +{\bf Overview:} +Set the actions/after\_reboot field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_actions_after_reboot (session_id s, VM ref self, on_normal_exit value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt on\_normal\_exit } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_actions\_after\_crash} + +{\bf Overview:} +Get the actions/after\_crash field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (on_crash_behaviour) get_actions_after_crash (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +on\_crash\_behaviour +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_actions\_after\_crash} + +{\bf Overview:} +Set the actions/after\_crash field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_actions_after_crash (session_id s, VM ref self, on_crash_behaviour value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt on\_crash\_behaviour } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_consoles} + +{\bf Overview:} +Get the consoles field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((console ref) Set) get_consoles (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(console ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VIFs} + +{\bf Overview:} +Get the VIFs field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((VIF ref) Set) get_VIFs (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VIF ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VBDs} + +{\bf Overview:} +Get the VBDs field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((VBD ref) Set) get_VBDs (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VBD ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_crash\_dumps} + +{\bf Overview:} +Get the crash\_dumps field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((crashdump ref) Set) get_crash_dumps (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(crashdump ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VTPMs} + +{\bf Overview:} +Get the VTPMs field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((VTPM ref) Set) get_VTPMs (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VTPM ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_DPCIs} + +{\bf Overview:} +Get the DPCIs field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((DPCI ref) Set) get_DPCIs (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(DPCI ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PV\_bootloader} + +{\bf Overview:} +Get the PV/bootloader field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_PV_bootloader (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_PV\_bootloader} + +{\bf Overview:} +Set the PV/bootloader field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_PV_bootloader (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PV\_kernel} + +{\bf Overview:} +Get the PV/kernel field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_PV_kernel (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_PV\_kernel} + +{\bf Overview:} +Set the PV/kernel field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_PV_kernel (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PV\_ramdisk} + +{\bf Overview:} +Get the PV/ramdisk field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_PV_ramdisk (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_PV\_ramdisk} + +{\bf Overview:} +Set the PV/ramdisk field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_PV_ramdisk (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PV\_args} + +{\bf Overview:} +Get the PV/args field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_PV_args (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_PV\_args} + +{\bf Overview:} +Set the PV/args field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_PV_args (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PV\_bootloader\_args} + +{\bf Overview:} +Get the PV/bootloader\_args field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_PV_bootloader_args (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_PV\_bootloader\_args} + +{\bf Overview:} +Set the PV/bootloader\_args field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_PV_bootloader_args (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_HVM\_boot\_policy} + +{\bf Overview:} +Get the HVM/boot\_policy field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_HVM_boot_policy (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_HVM\_boot\_policy} + +{\bf Overview:} +Set the HVM/boot\_policy field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_HVM_boot_policy (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_HVM\_boot\_params} + +{\bf Overview:} +Get the HVM/boot\_params field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_HVM_boot_params (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_HVM\_boot\_params} + +{\bf Overview:} +Set the HVM/boot\_params field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_HVM_boot_params (session_id s, VM ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_HVM\_boot\_params} + +{\bf Overview:} +Add the given key-value pair to the HVM/boot\_params field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_HVM_boot_params (session_id s, VM ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_HVM\_boot\_params} + +{\bf Overview:} +Remove the given key and its corresponding value from the HVM/boot\_params +field of the given VM. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_HVM_boot_params (session_id s, VM ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_platform} + +{\bf Overview:} +Get the platform field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_platform (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_platform} + +{\bf Overview:} +Set the platform field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_platform (session_id s, VM ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_platform} + +{\bf Overview:} +Add the given key-value pair to the platform field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_platform (session_id s, VM ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_platform} + +{\bf Overview:} +Remove the given key and its corresponding value from the platform field of +the given VM. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_platform (session_id s, VM ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PCI\_bus} + +{\bf Overview:} +Get the PCI\_bus field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_PCI_bus (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_PCI\_bus} + +{\bf Overview:} +Set the PCI\_bus field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_PCI_bus (session_id s, VM ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other\_config} + +{\bf Overview:} +Get the other\_config field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other_config (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_other\_config} + +{\bf Overview:} +Set the other\_config field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_other_config (session_id s, VM ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_other\_config} + +{\bf Overview:} +Add the given key-value pair to the other\_config field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_other_config (session_id s, VM ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_other\_config} + +{\bf Overview:} +Remove the given key and its corresponding value from the other\_config +field of the given VM. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_other_config (session_id s, VM ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_domid} + +{\bf Overview:} +Get the domid field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} int get_domid (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_is\_control\_domain} + +{\bf Overview:} +Get the is\_control\_domain field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_is_control_domain (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_metrics} + +{\bf Overview:} +Get the metrics field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (VM_metrics ref) get_metrics (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM\_metrics ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_guest\_metrics} + +{\bf Overview:} +Get the guest\_metrics field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (VM_guest_metrics ref) get_guest_metrics (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM\_guest\_metrics ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_security\_label} + +{\bf Overview:} +Get the security label field of the given VM. Refer to the XSPolicy class +for the format of the security label. + + \noindent {\bf Signature:} +\begin{verbatim} string get_security_label (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_security\_label} + +{\bf Overview:} +Set the security label field of the given VM. Refer to the XSPolicy class +for the format of the security label. + + \noindent {\bf Signature:} +\begin{verbatim} int set_security_label (session_id s, VM ref self, string +security_label, string old_label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline +{\tt string } & security\_label & security label for the VM \\ \hline +{\tt string } & old\_label & Label value that the security label \\ +& & must currently have for the change to succeed.\\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +Returns the ssidref in case of an VM that is currently running or +paused, zero in case of a dormant VM (halted, suspended). + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SECURITY\_ERROR} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new VM instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) create (session_id s, VM record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified VM. The VM is completely removed from the system. +This function can only be called when the VM is in the Halted State. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VM instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} (VM record) get_record (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_name\_label} + +{\bf Overview:} +Get all the VM instances with the given label. + + \noindent {\bf Signature:} +\begin{verbatim} ((VM ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & label & label of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VM ref) Set +} + + +references to objects with match names +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VM\_metrics} +\subsection{Fields for class: VM\_metrics} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VM\_metrics} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The metrics associated with a VM.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt memory/actual} & int & Guest's actual memory (bytes) \\ +$\mathit{RO}_\mathit{run}$ & {\tt VCPUs/number} & int & Current number of VCPUs \\ +$\mathit{RO}_\mathit{run}$ & {\tt VCPUs/utilisation} & (int $\rightarrow$ float) Map & Utilisation for all of guest's current VCPUs \\ +$\mathit{RO}_\mathit{run}$ & {\tt VCPUs/CPU} & (int $\rightarrow$ int) Map & VCPU to PCPU map \\ +$\mathit{RO}_\mathit{run}$ & {\tt VCPUs/params} & (string $\rightarrow$ string) Map & The live equivalent to VM.VCPUs\_params \\ +$\mathit{RO}_\mathit{run}$ & {\tt VCPUs/flags} & (int $\rightarrow$ string Set) Map & CPU flags (blocked,online,running) \\ +$\mathit{RO}_\mathit{run}$ & {\tt state} & string Set & The state of the guest, eg blocked, dying etc \\ +$\mathit{RO}_\mathit{run}$ & {\tt start\_time} & datetime & Time at which this VM was last booted \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_updated} & datetime & Time at which this information was last updated \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VM\_metrics} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VM\_metrics instances known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VM_metrics ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VM\_metrics ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_actual} + +{\bf Overview:} +Get the memory/actual field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_actual (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_number} + +{\bf Overview:} +Get the VCPUs/number field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} int get_VCPUs_number (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_utilisation} + +{\bf Overview:} +Get the VCPUs/utilisation field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((int -> float) Map) get_VCPUs_utilisation (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(int $\rightarrow$ float) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_CPU} + +{\bf Overview:} +Get the VCPUs/CPU field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((int -> int) Map) get_VCPUs_CPU (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(int $\rightarrow$ int) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_params} + +{\bf Overview:} +Get the VCPUs/params field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_VCPUs_params (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VCPUs\_flags} + +{\bf Overview:} +Get the VCPUs/flags field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((int -> string Set) Map) get_VCPUs_flags (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(int $\rightarrow$ string Set) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_state} + +{\bf Overview:} +Get the state field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_state (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_start\_time} + +{\bf Overview:} +Get the start\_time field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_start_time (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_updated} + +{\bf Overview:} +Get the last\_updated field of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_last_updated (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VM\_metrics instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VM_metrics ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM\_metrics ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VM\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (VM_metrics record) get_record (session_id s, VM_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM\_metrics record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VM\_guest\_metrics} +\subsection{Fields for class: VM\_guest\_metrics} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VM\_guest\_metrics} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The metrics reported by the guest (as opposed to inferred from outside).}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt os\_version} & (string $\rightarrow$ string) Map & version of the OS \\ +$\mathit{RO}_\mathit{run}$ & {\tt PV\_drivers\_version} & (string $\rightarrow$ string) Map & version of the PV drivers \\ +$\mathit{RO}_\mathit{run}$ & {\tt memory} & (string $\rightarrow$ string) Map & free/used/total memory \\ +$\mathit{RO}_\mathit{run}$ & {\tt disks} & (string $\rightarrow$ string) Map & disk configuration/free space \\ +$\mathit{RO}_\mathit{run}$ & {\tt networks} & (string $\rightarrow$ string) Map & network configuration \\ +$\mathit{RO}_\mathit{run}$ & {\tt other} & (string $\rightarrow$ string) Map & anything else \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_updated} & datetime & Time at which this information was last updated \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VM\_guest\_metrics} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VM\_guest\_metrics instances known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VM_guest_metrics ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VM\_guest\_metrics ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_os\_version} + +{\bf Overview:} +Get the os\_version field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_os_version (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PV\_drivers\_version} + +{\bf Overview:} +Get the PV\_drivers\_version field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_PV_drivers_version (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory} + +{\bf Overview:} +Get the memory field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_memory (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_disks} + +{\bf Overview:} +Get the disks field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_disks (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_networks} + +{\bf Overview:} +Get the networks field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_networks (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other} + +{\bf Overview:} +Get the other field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_updated} + +{\bf Overview:} +Get the last\_updated field of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_last_updated (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VM\_guest\_metrics instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VM_guest_metrics ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM\_guest\_metrics ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VM\_guest\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (VM_guest_metrics record) get_record (session_id s, VM_guest_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM\_guest\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM\_guest\_metrics record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: host} +\subsection{Fields for class: host} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf host} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +physical host.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ +$\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RO}_\mathit{run}$ & {\tt API\_version/major} & int & major version number \\ +$\mathit{RO}_\mathit{run}$ & {\tt API\_version/minor} & int & minor version number \\ +$\mathit{RO}_\mathit{run}$ & {\tt API\_version/vendor} & string & identification of vendor \\ +$\mathit{RO}_\mathit{run}$ & {\tt API\_version/vendor\_implementation} & (string $\rightarrow$ string) Map & details of vendor implementation \\ +$\mathit{RO}_\mathit{run}$ & {\tt enabled} & bool & True if the host is currently enabled \\ +$\mathit{RO}_\mathit{run}$ & {\tt software\_version} & (string $\rightarrow$ string) Map & version strings \\ +$\mathit{RW}$ & {\tt other\_config} & (string $\rightarrow$ string) Map & additional configuration \\ +$\mathit{RO}_\mathit{run}$ & {\tt capabilities} & string Set & Xen capabilities \\ +$\mathit{RO}_\mathit{run}$ & {\tt cpu\_configuration} & (string $\rightarrow$ string) Map & The CPU configuration on this host. May contain keys such as ``nr\_nodes'', ``sockets\_per\_node'', ``cores\_per\_socket'', or ``threads\_per\_core'' \\ +$\mathit{RO}_\mathit{run}$ & {\tt sched\_policy} & string & Scheduler policy currently in force on this host \\ +$\mathit{RO}_\mathit{run}$ & {\tt supported\_bootloaders} & string Set & a list of the bootloaders installed on the machine \\ +$\mathit{RO}_\mathit{run}$ & {\tt resident\_VMs} & (VM ref) Set & list of VMs currently resident on host \\ +$\mathit{RW}$ & {\tt logging} & (string $\rightarrow$ string) Map & logging configuration \\ +$\mathit{RO}_\mathit{run}$ & {\tt PIFs} & (PIF ref) Set & physical network interfaces \\ +$\mathit{RW}$ & {\tt suspend\_image\_sr} & SR ref & The SR in which VDIs for suspend images are created \\ +$\mathit{RW}$ & {\tt crash\_dump\_sr} & SR ref & The SR in which VDIs for crash dumps are created \\ +$\mathit{RO}_\mathit{run}$ & {\tt PBDs} & (PBD ref) Set & physical blockdevices \\ +$\mathit{RO}_\mathit{run}$ & {\tt PPCIs} & (PPCI ref) Set & physical PCI devices \\ +$\mathit{RO}_\mathit{run}$ & {\tt host\_CPUs} & (host\_cpu ref) Set & The physical CPUs on this host \\ +$\mathit{RO}_\mathit{run}$ & {\tt metrics} & host\_metrics ref & metrics associated with this host \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: host} +\subsubsection{RPC name:~disable} + +{\bf Overview:} +Puts the host into a state in which no new VMs can be started. Currently +active VMs on the host continue to execute. + + \noindent {\bf Signature:} +\begin{verbatim} void disable (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to disable \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~enable} + +{\bf Overview:} +Puts the host into a state in which new VMs can be started. + + \noindent {\bf Signature:} +\begin{verbatim} void enable (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to enable \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~shutdown} + +{\bf Overview:} +Shutdown the host. (This function can only be called if there are no +currently running VMs on the host and it is disabled.). + + \noindent {\bf Signature:} +\begin{verbatim} void shutdown (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to shutdown \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~reboot} + +{\bf Overview:} +Reboot the host. (This function can only be called if there are no +currently running VMs on the host and it is disabled.). + + \noindent {\bf Signature:} +\begin{verbatim} void reboot (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to reboot \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~dmesg} + +{\bf Overview:} +Get the host xen dmesg. + + \noindent {\bf Signature:} +\begin{verbatim} string dmesg (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to query \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +dmesg string +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~dmesg\_clear} + +{\bf Overview:} +Get the host xen dmesg, and clear the buffer. + + \noindent {\bf Signature:} +\begin{verbatim} string dmesg_clear (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to query \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +dmesg string +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_log} + +{\bf Overview:} +Get the host's log file. + + \noindent {\bf Signature:} +\begin{verbatim} string get_log (session_id s, host ref host)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The Host to query \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +The contents of the host's primary log file +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~send\_debug\_keys} + +{\bf Overview:} +Inject the given string as debugging keys into Xen. + + \noindent {\bf Signature:} +\begin{verbatim} void send_debug_keys (session_id s, host ref host, string keys)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & host & The host \\ \hline + +{\tt string } & keys & The keys to send \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~list\_methods} + +{\bf Overview:} +List all supported methods. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) list_methods (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +The name of every supported method. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the hosts known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((host ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(host ref) Set +} + + +A list of all the IDs of all the hosts +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_label} + +{\bf Overview:} +Get the name/label field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_label (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_label} + +{\bf Overview:} +Set the name/label field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_label (session_id s, host ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_description} + +{\bf Overview:} +Get the name/description field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_description (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_description} + +{\bf Overview:} +Set the name/description field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_description (session_id s, host ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_API\_version\_major} + +{\bf Overview:} +Get the API\_version/major field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} int get_API_version_major (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_API\_version\_minor} + +{\bf Overview:} +Get the API\_version/minor field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} int get_API_version_minor (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_API\_version\_vendor} + +{\bf Overview:} +Get the API\_version/vendor field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} string get_API_version_vendor (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_API\_version\_vendor\_implementation} + +{\bf Overview:} +Get the API\_version/vendor\_implementation field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_API_version_vendor_implementation (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_enabled} + +{\bf Overview:} +Get the enabled field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_enabled (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_software\_version} + +{\bf Overview:} +Get the software\_version field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_software_version (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other\_config} + +{\bf Overview:} +Get the other\_config field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other_config (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_other\_config} + +{\bf Overview:} +Set the other\_config field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void set_other_config (session_id s, host ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_other\_config} + +{\bf Overview:} +Add the given key-value pair to the other\_config field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_other_config (session_id s, host ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_other\_config} + +{\bf Overview:} +Remove the given key and its corresponding value from the other\_config +field of the given host. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_other_config (session_id s, host ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_capabilities} + +{\bf Overview:} +Get the capabilities field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_capabilities (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_cpu\_configuration} + +{\bf Overview:} +Get the cpu\_configuration field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_cpu_configuration (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_sched\_policy} + +{\bf Overview:} +Get the sched\_policy field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} string get_sched_policy (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_supported\_bootloaders} + +{\bf Overview:} +Get the supported\_bootloaders field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_supported_bootloaders (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_resident\_VMs} + +{\bf Overview:} +Get the resident\_VMs field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((VM ref) Set) get_resident_VMs (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VM ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_logging} + +{\bf Overview:} +Get the logging field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_logging (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_logging} + +{\bf Overview:} +Set the logging field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void set_logging (session_id s, host ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_logging} + +{\bf Overview:} +Add the given key-value pair to the logging field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_logging (session_id s, host ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_logging} + +{\bf Overview:} +Remove the given key and its corresponding value from the logging field of +the given host. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_logging (session_id s, host ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PIFs} + +{\bf Overview:} +Get the PIFs field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((PIF ref) Set) get_PIFs (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PIF ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_suspend\_image\_sr} + +{\bf Overview:} +Get the suspend\_image\_sr field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} (SR ref) get_suspend_image_sr (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +SR ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_suspend\_image\_sr} + +{\bf Overview:} +Set the suspend\_image\_sr field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void set_suspend_image_sr (session_id s, host ref self, SR ref value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt SR ref } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_crash\_dump\_sr} + +{\bf Overview:} +Get the crash\_dump\_sr field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} (SR ref) get_crash_dump_sr (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +SR ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_crash\_dump\_sr} + +{\bf Overview:} +Set the crash\_dump\_sr field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} void set_crash_dump_sr (session_id s, host ref self, SR ref value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +{\tt SR ref } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PBDs} + +{\bf Overview:} +Get the PBDs field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((PBD ref) Set) get_PBDs (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PBD ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PPCIs} + +{\bf Overview:} +Get the PPCIs field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((PPCI ref) Set) get_PPCIs (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PPCI ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_host\_CPUs} + +{\bf Overview:} +Get the host\_CPUs field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} ((host_cpu ref) Set) get_host_CPUs (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(host\_cpu ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_metrics} + +{\bf Overview:} +Get the metrics field of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} (host_metrics ref) get_metrics (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host\_metrics ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the host instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given host. + + \noindent {\bf Signature:} +\begin{verbatim} (host record) get_record (session_id s, host ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_name\_label} + +{\bf Overview:} +Get all the host instances with the given label. + + \noindent {\bf Signature:} +\begin{verbatim} ((host ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & label & label of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(host ref) Set +} + + +references to objects with match names +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: host\_metrics} +\subsection{Fields for class: host\_metrics} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf host\_metrics} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The metrics associated with a host.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt memory/total} & int & Host's total memory (bytes) \\ +$\mathit{RO}_\mathit{run}$ & {\tt memory/free} & int & Host's free memory (bytes) \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_updated} & datetime & Time at which this information was last updated \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: host\_metrics} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the host\_metrics instances known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((host_metrics ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(host\_metrics ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given host\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, host_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_total} + +{\bf Overview:} +Get the memory/total field of the given host\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_total (session_id s, host_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_memory\_free} + +{\bf Overview:} +Get the memory/free field of the given host\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} int get_memory_free (session_id s, host_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_updated} + +{\bf Overview:} +Get the last\_updated field of the given host\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_last_updated (session_id s, host_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the host\_metrics instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (host_metrics ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host\_metrics ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given host\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (host_metrics record) get_record (session_id s, host_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host\_metrics record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: host\_cpu} +\subsection{Fields for class: host\_cpu} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf host\_cpu} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A physical CPU}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt host} & host ref & the host the CPU is in \\ +$\mathit{RO}_\mathit{run}$ & {\tt number} & int & the number of the physical CPU within the host \\ +$\mathit{RO}_\mathit{run}$ & {\tt vendor} & string & the vendor of the physical CPU \\ +$\mathit{RO}_\mathit{run}$ & {\tt speed} & int & the speed of the physical CPU \\ +$\mathit{RO}_\mathit{run}$ & {\tt modelname} & string & the model name of the physical CPU \\ +$\mathit{RO}_\mathit{run}$ & {\tt stepping} & string & the stepping of the physical CPU \\ +$\mathit{RO}_\mathit{run}$ & {\tt flags} & string & the flags of the physical CPU (a decoded version of the features field) \\ +$\mathit{RO}_\mathit{run}$ & {\tt features} & string & the physical CPU feature bitmap \\ +$\mathit{RO}_\mathit{run}$ & {\tt utilisation} & float & the current CPU utilisation \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: host\_cpu} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the host\_cpus known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((host_cpu ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(host\_cpu ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_host} + +{\bf Overview:} +Get the host field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_host (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_number} + +{\bf Overview:} +Get the number field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} int get_number (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_vendor} + +{\bf Overview:} +Get the vendor field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} string get_vendor (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_speed} + +{\bf Overview:} +Get the speed field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} int get_speed (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_modelname} + +{\bf Overview:} +Get the modelname field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} string get_modelname (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_stepping} + +{\bf Overview:} +Get the stepping field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} string get_stepping (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_flags} + +{\bf Overview:} +Get the flags field of the given host\_cpu. As of this version of the +API, the semantics of the returned string are explicitly unspecified, +and may change in the future. + + \noindent {\bf Signature:} +\begin{verbatim} string get_flags (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_features} + +{\bf Overview:} +Get the features field of the given host\_cpu. As of this version of the +API, the semantics of the returned string are explicitly unspecified, +and may change in the future. + + \noindent {\bf Signature:} +\begin{verbatim} string get_features (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_utilisation} + +{\bf Overview:} +Get the utilisation field of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} float get_utilisation (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the host\_cpu instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (host_cpu ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host\_cpu ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given host\_cpu. + + \noindent {\bf Signature:} +\begin{verbatim} (host_cpu record) get_record (session_id s, host_cpu ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host\_cpu record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: network} +\subsection{Fields for class: network} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf network} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +virtual network.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ +$\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RO}_\mathit{run}$ & {\tt VIFs} & (VIF ref) Set & list of connected vifs \\ +$\mathit{RO}_\mathit{run}$ & {\tt PIFs} & (PIF ref) Set & list of connected pifs \\ +$\mathit{RW}$ & {\tt default\_gateway} & string & default gateway \\ +$\mathit{RW}$ & {\tt default\_netmask} & string & default netmask \\ +$\mathit{RW}$ & {\tt other\_config} & (string $\rightarrow$ string) Map & additional configuration \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: network} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the networks known to the system + + \noindent {\bf Signature:} +\begin{verbatim} ((network ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(network ref) Set +} + + +A list of all the IDs of all the networks +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_label} + +{\bf Overview:} +Get the name/label field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_label (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_label} + +{\bf Overview:} +Set the name/label field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_label (session_id s, network ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_description} + +{\bf Overview:} +Get the name/description field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_description (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_description} + +{\bf Overview:} +Set the name/description field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_description (session_id s, network ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VIFs} + +{\bf Overview:} +Get the VIFs field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} ((VIF ref) Set) get_VIFs (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VIF ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PIFs} + +{\bf Overview:} +Get the PIFs field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} ((PIF ref) Set) get_PIFs (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PIF ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_default\_gateway} + +{\bf Overview:} +Get the default\_gateway field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} string get_default_gateway (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_default\_gateway} + +{\bf Overview:} +Set the default\_gateway field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} void set_default_gateway (session_id s, network ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_default\_netmask} + +{\bf Overview:} +Get the default\_netmask field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} string get_default_netmask (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_default\_netmask} + +{\bf Overview:} +Set the default\_netmask field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} void set_default_netmask (session_id s, network ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other\_config} + +{\bf Overview:} +Get the other\_config field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other_config (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_other\_config} + +{\bf Overview:} +Set the other\_config field of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} void set_other_config (session_id s, network ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_other\_config} + +{\bf Overview:} +Add the given key-value pair to the other\_config field of the given +network. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_other_config (session_id s, network ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_other\_config} + +{\bf Overview:} +Remove the given key and its corresponding value from the other\_config +field of the given network. If the key is not in that Map, then do +nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_other_config (session_id s, network ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new network instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (network ref) create (session_id s, network record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +network ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified network instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the network instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (network ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +network ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given network. + + \noindent {\bf Signature:} +\begin{verbatim} (network record) get_record (session_id s, network ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt network ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +network record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_name\_label} + +{\bf Overview:} +Get all the network instances with the given label. + + \noindent {\bf Signature:} +\begin{verbatim} ((network ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & label & label of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(network ref) Set +} + + +references to objects with match names +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VIF} +\subsection{Fields for class: VIF} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VIF} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +virtual network interface.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RW}$ & {\tt device} & string & name of network device as exposed to guest e.g. eth0 \\ +$\mathit{RO}_\mathit{ins}$ & {\tt network} & network ref & virtual network to which this vif is connected \\ +$\mathit{RO}_\mathit{ins}$ & {\tt VM} & VM ref & virtual machine to which this vif is connected \\ +$\mathit{RW}$ & {\tt MAC} & string & ethernet MAC address of virtual interface, as exposed to guest \\ +$\mathit{RW}$ & {\tt MTU} & int & MTU in octets \\ +$\mathit{RO}_\mathit{run}$ & {\tt currently\_attached} & bool & is the device currently attached (erased on reboot) \\ +$\mathit{RO}_\mathit{run}$ & {\tt status\_code} & int & error/success code associated with last attach-operation (erased on reboot) \\ +$\mathit{RO}_\mathit{run}$ & {\tt status\_detail} & string & error/success information associated with last attach-operation status (erased on reboot) \\ +$\mathit{RO}_\mathit{run}$ & {\tt runtime\_properties} & (string $\rightarrow$ string) Map & Device runtime properties \\ +$\mathit{RW}$ & {\tt qos/algorithm\_type} & string & QoS algorithm to use \\ +$\mathit{RW}$ & {\tt qos/algorithm\_params} & (string $\rightarrow$ string) Map & parameters for chosen QoS algorithm \\ +$\mathit{RO}_\mathit{run}$ & {\tt qos/supported\_algorithms} & string Set & supported QoS algorithms for this VIF \\ +$\mathit{RO}_\mathit{run}$ & {\tt metrics} & VIF\_metrics ref & metrics associated with this VIF \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VIF} +\subsubsection{RPC name:~plug} + +{\bf Overview:} +Hotplug the specified VIF, dynamically attaching it to the running VM. + + \noindent {\bf Signature:} +\begin{verbatim} void plug (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & The VIF to hotplug \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~unplug} + +{\bf Overview:} +Hot-unplug the specified VIF, dynamically unattaching it from the running +VM. + + \noindent {\bf Signature:} +\begin{verbatim} void unplug (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & The VIF to hot-unplug \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VIFs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VIF ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VIF ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_device} + +{\bf Overview:} +Get the device field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_device (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_device} + +{\bf Overview:} +Set the device field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_device (session_id s, VIF ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_network} + +{\bf Overview:} +Get the network field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} (network ref) get_network (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +network ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_MAC} + +{\bf Overview:} +Get the MAC field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_MAC (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_MAC} + +{\bf Overview:} +Set the MAC field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_MAC (session_id s, VIF ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_MTU} + +{\bf Overview:} +Get the MTU field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} int get_MTU (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_MTU} + +{\bf Overview:} +Set the MTU field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_MTU (session_id s, VIF ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_currently\_attached} + +{\bf Overview:} +Get the currently\_attached field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_currently_attached (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_status\_code} + +{\bf Overview:} +Get the status\_code field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} int get_status_code (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_status\_detail} + +{\bf Overview:} +Get the status\_detail field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_status_detail (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_runtime\_properties} + +{\bf Overview:} +Get the runtime\_properties field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_runtime_properties (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_qos\_algorithm\_type} + +{\bf Overview:} +Get the qos/algorithm\_type field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_qos_algorithm_type (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_qos\_algorithm\_type} + +{\bf Overview:} +Set the qos/algorithm\_type field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_qos_algorithm_type (session_id s, VIF ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_qos\_algorithm\_params} + +{\bf Overview:} +Get the qos/algorithm\_params field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_qos_algorithm_params (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_qos\_algorithm\_params} + +{\bf Overview:} +Set the qos/algorithm\_params field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_qos_algorithm_params (session_id s, VIF ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_qos\_algorithm\_params} + +{\bf Overview:} +Add the given key-value pair to the qos/algorithm\_params field of the +given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_qos_algorithm_params (session_id s, VIF ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_qos\_algorithm\_params} + +{\bf Overview:} +Remove the given key and its corresponding value from the +qos/algorithm\_params field of the given VIF. If the key is not in that +Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_qos_algorithm_params (session_id s, VIF ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_qos\_supported\_algorithms} + +{\bf Overview:} +Get the qos/supported\_algorithms field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_qos_supported_algorithms (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_metrics} + +{\bf Overview:} +Get the metrics field of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} (VIF_metrics ref) get_metrics (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VIF\_metrics ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_security\_label} + +{\bf Overview:} +Set the security label of the given VIF. Refer to the XSPolicy class +for the format of the security label. + + \noindent {\bf Signature:} +\begin{verbatim} void set_security_label (session_id s, VIF ref self, string +security_label, string old_label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +{\tt string } & security\_label & New value of the security label \\ \hline +{\tt string } & old\_label & Label value that the security label \\ +& & must currently have for the change to succeed.\\ \hline +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SECURITY\_ERROR} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_security\_label} + +{\bf Overview:} +Get the security label of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_security_label (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the given field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new VIF instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (VIF ref) create (session_id s, VIF record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VIF ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified VIF instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VIF instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VIF ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VIF ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VIF. + + \noindent {\bf Signature:} +\begin{verbatim} (VIF record) get_record (session_id s, VIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VIF record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VIF\_metrics} +\subsection{Fields for class: VIF\_metrics} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VIF\_metrics} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The metrics associated with a virtual network device.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt io/read\_kbs} & float & Read bandwidth (KiB/s) \\ +$\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB/s) \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_updated} & datetime & Time at which this information was last updated \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VIF\_metrics} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VIF\_metrics instances known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VIF_metrics ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VIF\_metrics ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_io\_read\_kbs} + +{\bf Overview:} +Get the io/read\_kbs field of the given VIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} float get_io_read_kbs (session_id s, VIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_io\_write\_kbs} + +{\bf Overview:} +Get the io/write\_kbs field of the given VIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} float get_io_write_kbs (session_id s, VIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_updated} + +{\bf Overview:} +Get the last\_updated field of the given VIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_last_updated (session_id s, VIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VIF\_metrics instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VIF_metrics ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VIF\_metrics ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (VIF_metrics record) get_record (session_id s, VIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VIF\_metrics record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: PIF} +\subsection{Fields for class: PIF} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf PIF} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +physical network interface (note separate VLANs are represented as several +PIFs).}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RW}$ & {\tt device} & string & machine-readable name of the interface (e.g. eth0) \\ +$\mathit{RO}_\mathit{ins}$ & {\tt network} & network ref & virtual network to which this pif is connected \\ +$\mathit{RO}_\mathit{ins}$ & {\tt host} & host ref & physical machine to which this pif is connected \\ +$\mathit{RW}$ & {\tt MAC} & string & ethernet MAC address of physical interface \\ +$\mathit{RW}$ & {\tt MTU} & int & MTU in octets \\ +$\mathit{RW}$ & {\tt VLAN} & int & VLAN tag for all traffic passing through this interface \\ +$\mathit{RO}_\mathit{run}$ & {\tt metrics} & PIF\_metrics ref & metrics associated with this PIF \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: PIF} +\subsubsection{RPC name:~create\_VLAN} + +{\bf Overview:} +Create a VLAN interface from an existing physical interface. + + \noindent {\bf Signature:} +\begin{verbatim} (PIF ref) create_VLAN (session_id s, string device, network ref network, host ref host, int VLAN)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & device & physical interface on which to crate the VLAN interface \\ \hline + +{\tt network ref } & network & network to which this interface should be connected \\ \hline + +{\tt host ref } & host & physical machine to which this PIF is connected \\ \hline + +{\tt int } & VLAN & VLAN tag for the new interface \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PIF ref +} + + +The reference of the created PIF object +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt VLAN\_TAG\_INVALID} + +\vspace{0.6cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the interface (provided it is a synthetic interface like a VLAN; +fail if it is a physical interface). + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & the PIF object to destroy \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt PIF\_IS\_PHYSICAL} + +\vspace{0.6cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the PIFs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((PIF ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PIF ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_device} + +{\bf Overview:} +Get the device field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_device (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_device} + +{\bf Overview:} +Set the device field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_device (session_id s, PIF ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_network} + +{\bf Overview:} +Get the network field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} (network ref) get_network (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +network ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_host} + +{\bf Overview:} +Get the host field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_host (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_MAC} + +{\bf Overview:} +Get the MAC field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} string get_MAC (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_MAC} + +{\bf Overview:} +Set the MAC field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_MAC (session_id s, PIF ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_MTU} + +{\bf Overview:} +Get the MTU field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} int get_MTU (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_MTU} + +{\bf Overview:} +Set the MTU field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_MTU (session_id s, PIF ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VLAN} + +{\bf Overview:} +Get the VLAN field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} int get_VLAN (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VLAN} + +{\bf Overview:} +Set the VLAN field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VLAN (session_id s, PIF ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_metrics} + +{\bf Overview:} +Get the metrics field of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} (PIF_metrics ref) get_metrics (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PIF\_metrics ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the PIF instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (PIF ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PIF ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given PIF. + + \noindent {\bf Signature:} +\begin{verbatim} (PIF record) get_record (session_id s, PIF ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PIF record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: PIF\_metrics} +\subsection{Fields for class: PIF\_metrics} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf PIF\_metrics} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The metrics associated with a physical network interface.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt io/read\_kbs} & float & Read bandwidth (KiB/s) \\ +$\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB/s) \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_updated} & datetime & Time at which this information was last updated \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: PIF\_metrics} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the PIF\_metrics instances known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((PIF_metrics ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PIF\_metrics ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given PIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, PIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_io\_read\_kbs} + +{\bf Overview:} +Get the io/read\_kbs field of the given PIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} float get_io_read_kbs (session_id s, PIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_io\_write\_kbs} + +{\bf Overview:} +Get the io/write\_kbs field of the given PIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} float get_io_write_kbs (session_id s, PIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_updated} + +{\bf Overview:} +Get the last\_updated field of the given PIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_last_updated (session_id s, PIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the PIF\_metrics instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (PIF_metrics ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PIF\_metrics ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given PIF\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (PIF_metrics record) get_record (session_id s, PIF_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PIF\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PIF\_metrics record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: SR} +\subsection{Fields for class: SR} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf SR} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +storage repository.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ +$\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RO}_\mathit{run}$ & {\tt VDIs} & (VDI ref) Set & managed virtual disks \\ +$\mathit{RO}_\mathit{run}$ & {\tt PBDs} & (PBD ref) Set & physical blockdevices \\ +$\mathit{RO}_\mathit{run}$ & {\tt virtual\_allocation} & int & sum of virtual\_sizes of all VDIs in this storage repository (in bytes) \\ +$\mathit{RO}_\mathit{run}$ & {\tt physical\_utilisation} & int & physical space currently utilised on this storage repository (in bytes). Note that for sparse disk formats, physical\_utilisation may be less than virtual\_allocation \\ +$\mathit{RO}_\mathit{ins}$ & {\tt physical\_size} & int & total physical size of the repository (in bytes) \\ +$\mathit{RO}_\mathit{ins}$ & {\tt type} & string & type of the storage repository \\ +$\mathit{RO}_\mathit{ins}$ & {\tt content\_type} & string & the type of the SR's content, if required (e.g. ISOs) \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: SR} +\subsubsection{RPC name:~get\_supported\_types} + +{\bf Overview:} +Return a set of all the SR types supported by the system. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_supported_types (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +the supported SR types +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the SRs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((SR ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(SR ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_label} + +{\bf Overview:} +Get the name/label field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_label (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_label} + +{\bf Overview:} +Set the name/label field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_label (session_id s, SR ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_description} + +{\bf Overview:} +Get the name/description field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_description (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_description} + +{\bf Overview:} +Set the name/description field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_description (session_id s, SR ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VDIs} + +{\bf Overview:} +Get the VDIs field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} ((VDI ref) Set) get_VDIs (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VDI ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PBDs} + +{\bf Overview:} +Get the PBDs field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} ((PBD ref) Set) get_PBDs (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PBD ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_allocation} + +{\bf Overview:} +Get the virtual\_allocation field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} int get_virtual_allocation (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_physical\_utilisation} + +{\bf Overview:} +Get the physical\_utilisation field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} int get_physical_utilisation (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_physical\_size} + +{\bf Overview:} +Get the physical\_size field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} int get_physical_size (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_type} + +{\bf Overview:} +Get the type field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} string get_type (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_content\_type} + +{\bf Overview:} +Get the content\_type field of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} string get_content_type (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the SR instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (SR ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +SR ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given SR. + + \noindent {\bf Signature:} +\begin{verbatim} (SR record) get_record (session_id s, SR ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +SR record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_name\_label} + +{\bf Overview:} +Get all the SR instances with the given label. + + \noindent {\bf Signature:} +\begin{verbatim} ((SR ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & label & label of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(SR ref) Set +} + + +references to objects with match names +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VDI} +\subsection{Fields for class: VDI} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VDI} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +virtual disk image.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ +$\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RO}_\mathit{ins}$ & {\tt SR} & SR ref & storage repository in which the VDI resides \\ +$\mathit{RO}_\mathit{run}$ & {\tt VBDs} & (VBD ref) Set & list of vbds that refer to this disk \\ +$\mathit{RO}_\mathit{run}$ & {\tt crash\_dumps} & (crashdump ref) Set & list of crash dumps that refer to this disk \\ +$\mathit{RW}$ & {\tt virtual\_size} & int & size of disk as presented to the guest (in bytes). Note that, depending on storage backend type, requested size may not be respected exactly \\ +$\mathit{RO}_\mathit{run}$ & {\tt physical\_utilisation} & int & amount of physical space that the disk image is currently taking up on the storage repository (in bytes) \\ +$\mathit{RO}_\mathit{ins}$ & {\tt type} & vdi\_type & type of the VDI \\ +$\mathit{RW}$ & {\tt sharable} & bool & true if this disk may be shared \\ +$\mathit{RW}$ & {\tt read\_only} & bool & true if this disk may ONLY be mounted read-only \\ +$\mathit{RW}$ & {\tt other\_config} & (string $\rightarrow$ string) Map & additional configuration \\ +$\mathit{RO}_\mathit{run}$ & {\tt security/label} & string & the VM's security label \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VDI} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VDIs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VDI ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VDI ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_label} + +{\bf Overview:} +Get the name/label field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_label (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_label} + +{\bf Overview:} +Set the name/label field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_label (session_id s, VDI ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name\_description} + +{\bf Overview:} +Get the name/description field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name_description (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_name\_description} + +{\bf Overview:} +Set the name/description field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void set_name_description (session_id s, VDI ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_SR} + +{\bf Overview:} +Get the SR field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} (SR ref) get_SR (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +SR ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VBDs} + +{\bf Overview:} +Get the VBDs field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} ((VBD ref) Set) get_VBDs (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VBD ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_crash\_dumps} + +{\bf Overview:} +Get the crash\_dumps field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} ((crashdump ref) Set) get_crash_dumps (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(crashdump ref) Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_size} + +{\bf Overview:} +Get the virtual\_size field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_virtual_size (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_virtual\_size} + +{\bf Overview:} +Set the virtual\_size field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void set_virtual_size (session_id s, VDI ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_physical\_utilisation} + +{\bf Overview:} +Get the physical\_utilisation field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_physical_utilisation (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_type} + +{\bf Overview:} +Get the type field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} (vdi_type) get_type (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +vdi\_type +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_sharable} + +{\bf Overview:} +Get the sharable field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_sharable (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_sharable} + +{\bf Overview:} +Set the sharable field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void set_sharable (session_id s, VDI ref self, bool value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt bool } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_read\_only} + +{\bf Overview:} +Get the read\_only field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_read_only (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_read\_only} + +{\bf Overview:} +Set the read\_only field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void set_read_only (session_id s, VDI ref self, bool value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt bool } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other\_config} + +{\bf Overview:} +Get the other\_config field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other_config (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_other\_config} + +{\bf Overview:} +Set the other\_config field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void set_other_config (session_id s, VDI ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_other\_config} + +{\bf Overview:} +Add the given key-value pair to the other\_config field of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_other_config (session_id s, VDI ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_other\_config} + +{\bf Overview:} +Remove the given key and its corresponding value from the other\_config +field of the given VDI. If the key is not in that Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_other_config (session_id s, VDI ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_security\_label} + +{\bf Overview:} +Set the security label of the given VDI. Refer to the XSPolicy class +for the format of the security label. + + \noindent {\bf Signature:} +\begin{verbatim} void set_security_label (session_id s, VDI ref self, string +security_label, string old_label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +{\tt string } & security\_label & New value of the security label \\ \hline +{\tt string } & old\_label & Label value that the security label \\ +& & must currently have for the change to succeed.\\ \hline +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SECURITY\_ERROR} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_security\_label} + +{\bf Overview:} +Get the security label of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_security_label (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the given field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new VDI instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (VDI ref) create (session_id s, VDI record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VDI ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified VDI instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VDI instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VDI ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VDI ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VDI. + + \noindent {\bf Signature:} +\begin{verbatim} (VDI record) get_record (session_id s, VDI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VDI record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_name\_label} + +{\bf Overview:} +Get all the VDI instances with the given label. + + \noindent {\bf Signature:} +\begin{verbatim} ((VDI ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & label & label of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VDI ref) Set +} + + +references to objects with match names +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VBD} +\subsection{Fields for class: VBD} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VBD} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +virtual block device.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{ins}$ & {\tt VM} & VM ref & the virtual machine \\ +$\mathit{RO}_\mathit{ins}$ & {\tt VDI} & VDI ref & the virtual disk \\ +$\mathit{RW}$ & {\tt device} & string & device seen by the guest e.g. hda1 \\ +$\mathit{RW}$ & {\tt bootable} & bool & true if this VBD is bootable \\ +$\mathit{RW}$ & {\tt mode} & vbd\_mode & the mode the VBD should be mounted with \\ +$\mathit{RW}$ & {\tt type} & vbd\_type & how the VBD will appear to the guest (e.g. disk or CD) \\ +$\mathit{RO}_\mathit{run}$ & {\tt currently\_attached} & bool & is the device currently attached (erased on reboot) \\ +$\mathit{RO}_\mathit{run}$ & {\tt status\_code} & int & error/success code associated with last attach-operation (erased on reboot) \\ +$\mathit{RO}_\mathit{run}$ & {\tt status\_detail} & string & error/success information associated with last attach-operation status (erased on reboot) \\ +$\mathit{RO}_\mathit{run}$ & {\tt runtime\_properties} & (string $\rightarrow$ string) Map & Device runtime properties \\ +$\mathit{RW}$ & {\tt qos/algorithm\_type} & string & QoS algorithm to use \\ +$\mathit{RW}$ & {\tt qos/algorithm\_params} & (string $\rightarrow$ string) Map & parameters for chosen QoS algorithm \\ +$\mathit{RO}_\mathit{run}$ & {\tt qos/supported\_algorithms} & string Set & supported QoS algorithms for this VBD \\ +$\mathit{RO}_\mathit{run}$ & {\tt metrics} & VBD\_metrics ref & metrics associated with this VBD \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VBD} +\subsubsection{RPC name:~media\_change} + +{\bf Overview:} +Change the media in the device for CDROM-like devices only. For other +devices, detach the VBD and attach a new one. + + \noindent {\bf Signature:} +\begin{verbatim} void media_change (session_id s, VBD ref vbd, VDI ref vdi)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & vbd & The vbd representing the CDROM-like device \\ \hline + +{\tt VDI ref } & vdi & The new VDI to 'insert' \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~plug} + +{\bf Overview:} +Hotplug the specified VBD, dynamically attaching it to the running VM. + + \noindent {\bf Signature:} +\begin{verbatim} void plug (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & The VBD to hotplug \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~unplug} + +{\bf Overview:} +Hot-unplug the specified VBD, dynamically unattaching it from the running +VM. + + \noindent {\bf Signature:} +\begin{verbatim} void unplug (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & The VBD to hot-unplug \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VBDs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VBD ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VBD ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VDI} + +{\bf Overview:} +Get the VDI field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (VDI ref) get_VDI (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VDI ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_device} + +{\bf Overview:} +Get the device field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} string get_device (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_device} + +{\bf Overview:} +Set the device field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_device (session_id s, VBD ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_bootable} + +{\bf Overview:} +Get the bootable field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_bootable (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_bootable} + +{\bf Overview:} +Set the bootable field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_bootable (session_id s, VBD ref self, bool value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt bool } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_mode} + +{\bf Overview:} +Get the mode field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (vbd_mode) get_mode (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +vbd\_mode +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_mode} + +{\bf Overview:} +Set the mode field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_mode (session_id s, VBD ref self, vbd_mode value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt vbd\_mode } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_type} + +{\bf Overview:} +Get the type field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (vbd_type) get_type (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +vbd\_type +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_type} + +{\bf Overview:} +Set the type field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_type (session_id s, VBD ref self, vbd_type value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt vbd\_type } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_currently\_attached} + +{\bf Overview:} +Get the currently\_attached field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_currently_attached (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_status\_code} + +{\bf Overview:} +Get the status\_code field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} int get_status_code (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_status\_detail} + +{\bf Overview:} +Get the status\_detail field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} string get_status_detail (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_runtime\_properties} + +{\bf Overview:} +Get the runtime\_properties field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_runtime_properties (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_qos\_algorithm\_type} + +{\bf Overview:} +Get the qos/algorithm\_type field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} string get_qos_algorithm_type (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_qos\_algorithm\_type} + +{\bf Overview:} +Set the qos/algorithm\_type field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_qos_algorithm_type (session_id s, VBD ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_qos\_algorithm\_params} + +{\bf Overview:} +Get the qos/algorithm\_params field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_qos_algorithm_params (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_qos\_algorithm\_params} + +{\bf Overview:} +Set the qos/algorithm\_params field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_qos_algorithm_params (session_id s, VBD ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_qos\_algorithm\_params} + +{\bf Overview:} +Add the given key-value pair to the qos/algorithm\_params field of the +given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_qos_algorithm_params (session_id s, VBD ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_qos\_algorithm\_params} + +{\bf Overview:} +Remove the given key and its corresponding value from the +qos/algorithm\_params field of the given VBD. If the key is not in that +Map, then do nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_qos_algorithm_params (session_id s, VBD ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_qos\_supported\_algorithms} + +{\bf Overview:} +Get the qos/supported\_algorithms field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_qos_supported_algorithms (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_metrics} + +{\bf Overview:} +Get the metrics field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (VBD_metrics ref) get_metrics (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VBD\_metrics ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new VBD instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (VBD ref) create (session_id s, VBD record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VBD ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified VBD instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VBD instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VBD ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VBD ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (VBD record) get_record (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VBD record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VBD\_metrics} +\subsection{Fields for class: VBD\_metrics} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VBD\_metrics} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The metrics associated with a virtual block device.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt io/read\_kbs} & float & Read bandwidth (KiB/s) \\ +$\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB/s) \\ +$\mathit{RO}_\mathit{run}$ & {\tt last\_updated} & datetime & Time at which this information was last updated \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VBD\_metrics} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the VBD\_metrics instances known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((VBD_metrics ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(VBD\_metrics ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VBD\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VBD_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_io\_read\_kbs} + +{\bf Overview:} +Get the io/read\_kbs field of the given VBD\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} float get_io_read_kbs (session_id s, VBD_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_io\_write\_kbs} + +{\bf Overview:} +Get the io/write\_kbs field of the given VBD\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} float get_io_write_kbs (session_id s, VBD_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +float +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_last\_updated} + +{\bf Overview:} +Get the last\_updated field of the given VBD\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_last_updated (session_id s, VBD_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VBD\_metrics instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VBD_metrics ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VBD\_metrics ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VBD\_metrics. + + \noindent {\bf Signature:} +\begin{verbatim} (VBD_metrics record) get_record (session_id s, VBD_metrics ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD\_metrics ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VBD\_metrics record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: PBD} +\subsection{Fields for class: PBD} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf PBD} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em +The physical block devices through which hosts access SRs.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{ins}$ & {\tt host} & host ref & physical machine on which the pbd is available \\ +$\mathit{RO}_\mathit{ins}$ & {\tt SR} & SR ref & the storage repository that the pbd realises \\ +$\mathit{RO}_\mathit{ins}$ & {\tt device\_config} & (string $\rightarrow$ string) Map & a config string to string map that is provided to the host's SR-backend-driver \\ +$\mathit{RO}_\mathit{run}$ & {\tt currently\_attached} & bool & is the SR currently attached on this host? \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: PBD} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the PBDs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((PBD ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PBD ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given PBD. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_host} + +{\bf Overview:} +Get the host field of the given PBD. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_host (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_SR} + +{\bf Overview:} +Get the SR field of the given PBD. + + \noindent {\bf Signature:} +\begin{verbatim} (SR ref) get_SR (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +SR ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_device\_config} + +{\bf Overview:} +Get the device\_config field of the given PBD. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_device_config (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_currently\_attached} + +{\bf Overview:} +Get the currently\_attached field of the given PBD. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_currently_attached (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new PBD instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (PBD ref) create (session_id s, PBD record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PBD ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified PBD instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the PBD instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (PBD ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PBD ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given PBD. + + \noindent {\bf Signature:} +\begin{verbatim} (PBD record) get_record (session_id s, PBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PBD record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: crashdump} +\subsection{Fields for class: crashdump} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf crashdump} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +VM crashdump.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{ins}$ & {\tt VM} & VM ref & the virtual machine \\ +$\mathit{RO}_\mathit{ins}$ & {\tt VDI} & VDI ref & the virtual disk \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: crashdump} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified crashdump. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, crashdump ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt crashdump ref } & self & The crashdump to destroy \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the crashdumps known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((crashdump ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(crashdump ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given crashdump. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, crashdump ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt crashdump ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given crashdump. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, crashdump ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt crashdump ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VDI} + +{\bf Overview:} +Get the VDI field of the given crashdump. + + \noindent {\bf Signature:} +\begin{verbatim} (VDI ref) get_VDI (session_id s, crashdump ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt crashdump ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VDI ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the crashdump instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (crashdump ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +crashdump ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given crashdump. + + \noindent {\bf Signature:} +\begin{verbatim} (crashdump record) get_record (session_id s, crashdump ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt crashdump ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +crashdump record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: VTPM} +\subsection{Fields for class: VTPM} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf VTPM} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +virtual TPM device.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{ins}$ & {\tt VM} & VM ref & the virtual machine \\ +$\mathit{RO}_\mathit{ins}$ & {\tt backend} & VM ref & the domain where the backend is located \\ +$\mathit{RW}$ & {\tt other\_config} & (string $\rightarrow$ string) Map & additional configuration \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: VTPM} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given VTPM. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given VTPM. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_backend} + +{\bf Overview:} +Get the backend field of the given VTPM. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_backend (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other\_config} + +{\bf Overview:} +Get the other\_config field of the given VTPM. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other_config (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_other\_config} + +{\bf Overview:} +Set the other\_config field of the given VTPM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_other_config (session_id s, VTPM ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_runtime\_properties} + +{\bf Overview:} +Get the runtime\_properties field of the given VTPM. + +\noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_runtime_properties (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new VTPM instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (VTPM ref) create (session_id s, VTPM record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VTPM ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified VTPM instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the VTPM instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (VTPM ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VTPM ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given VTPM. + + \noindent {\bf Signature:} +\begin{verbatim} (VTPM record) get_record (session_id s, VTPM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VTPM record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: console} +\subsection{Fields for class: console} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf console} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +console.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt protocol} & console\_protocol & the protocol used by this console \\ +$\mathit{RO}_\mathit{run}$ & {\tt location} & string & URI for the console service \\ +$\mathit{RO}_\mathit{run}$ & {\tt VM} & VM ref & VM to which this console is attached \\ +$\mathit{RW}$ & {\tt other\_config} & (string $\rightarrow$ string) Map & additional configuration \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: console} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the consoles known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((console ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(console ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_protocol} + +{\bf Overview:} +Get the protocol field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} (console_protocol) get_protocol (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console\_protocol +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_location} + +{\bf Overview:} +Get the location field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} string get_location (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_other\_config} + +{\bf Overview:} +Get the other\_config field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_other_config (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_other\_config} + +{\bf Overview:} +Set the other\_config field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} void set_other_config (session_id s, console ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~add\_to\_other\_config} + +{\bf Overview:} +Add the given key-value pair to the other\_config field of the given +console. + + \noindent {\bf Signature:} +\begin{verbatim} void add_to_other_config (session_id s, console ref self, string key, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to add \\ \hline + +{\tt string } & value & Value to add \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~remove\_from\_other\_config} + +{\bf Overview:} +Remove the given key and its corresponding value from the other\_config +field of the given console. If the key is not in that Map, then do +nothing. + + \noindent {\bf Signature:} +\begin{verbatim} void remove_from_other_config (session_id s, console ref self, string key)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +{\tt string } & key & Key to remove \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new console instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (console ref) create (session_id s, console record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified console instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the console instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (console ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} (console record) get_record (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: DPCI} +\subsection{Fields for class: DPCI} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf DPCI} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +pass-through PCI device.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{inst}$ & {\tt VM} & VM ref & the virtual machine \\ +$\mathit{RO}_\mathit{inst}$ & {\tt PPCI} & PPCI ref & the physical PCI device \\ +$\mathit{RO}_\mathit{inst}$ & {\tt hotplug\_slot} & int & the slot number to which this PCI device is inserted \\ +$\mathit{RO}_\mathit{run}$ & {\tt virtual\_domain} & int & the virtual domain number \\ +$\mathit{RO}_\mathit{run}$ & {\tt virtual\_bus} & int & the virtual bus number \\ +$\mathit{RO}_\mathit{run}$ & {\tt virtual\_slot} & int & the virtual slot number \\ +$\mathit{RO}_\mathit{run}$ & {\tt virtual\_func} & int & the virtual func number \\ +$\mathit{RO}_\mathit{run}$ & {\tt virtual\_name} & string & the virtual PCI name \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: DPCI} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the DPCIs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((DPCI ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(DPCI ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_PPCI} + +{\bf Overview:} +Get the PPCI field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} (PPCI ref) get_PPCI (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PPCI ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_hotplug\_slot} + +{\bf Overview:} +Get the hotplug\_slot field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_hotplug_slot (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_domain} + +{\bf Overview:} +Get the virtual\_domain field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_virtual_domain (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_bus} + +{\bf Overview:} +Get the virtual\_bus field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_virtual_bus (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_slot} + +{\bf Overview:} +Get the virtual\_slot field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_virtual_slot (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_func} + +{\bf Overview:} +Get the virtual\_func field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_virtual_func (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_virtual\_name} + +{\bf Overview:} +Get the virtual\_name field of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_virtual_name (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new DPCI instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (DPCI ref) create (session_id s, DPCI record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +DPCI ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified DPCI instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the DPCI instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (DPCI ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +DPCI ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given DPCI. + + \noindent {\bf Signature:} +\begin{verbatim} (DPCI record) get_record (session_id s, DPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt DPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +DPCI record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: PPCI} +\subsection{Fields for class: PPCI} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf PPCI} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +physical PCI device.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt host} & host ref & the physical machine to which this PPCI is connected \\ +$\mathit{RO}_\mathit{run}$ & {\tt domain} & int & the domain number \\ +$\mathit{RO}_\mathit{run}$ & {\tt bus} & int & the bus number \\ +$\mathit{RO}_\mathit{run}$ & {\tt slot} & int & the slot number \\ +$\mathit{RO}_\mathit{run}$ & {\tt func} & int & the func number \\ +$\mathit{RO}_\mathit{run}$ & {\tt name} & string & the PCI name \\ +$\mathit{RO}_\mathit{run}$ & {\tt vendor\_id} & int & the vendor ID \\ +$\mathit{RO}_\mathit{run}$ & {\tt vendor\_name} & string & the vendor name \\ +$\mathit{RO}_\mathit{run}$ & {\tt device\_id} & int & the device ID \\ +$\mathit{RO}_\mathit{run}$ & {\tt device\_name} & string & the device name \\ +$\mathit{RO}_\mathit{run}$ & {\tt revision\_id} & int & the revision ID \\ +$\mathit{RO}_\mathit{run}$ & {\tt class\_code} & int & the class code \\ +$\mathit{RO}_\mathit{run}$ & {\tt class\_name} & string & the class name \\ +$\mathit{RO}_\mathit{run}$ & {\tt subsystem\_vendor\_id} & int & the subsystem vendor ID \\ +$\mathit{RO}_\mathit{run}$ & {\tt subsystem\_vendor\_name} & string & the subsystem vendor name \\ +$\mathit{RO}_\mathit{run}$ & {\tt subsystem\_id} & int & the subsystem ID \\ +$\mathit{RO}_\mathit{run}$ & {\tt subsystem\_name} & string & the subsystem name \\ +$\mathit{RO}_\mathit{run}$ & {\tt driver} & string & the driver name \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: PPCI} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the PPCIs known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((PPCI ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(PPCI ref) Set +} + + +references to all objects +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_host} + +{\bf Overview:} +Get the host field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} (host ref) get_host (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +host ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_domain} + +{\bf Overview:} +Get the domain field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_domain (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_bus} + +{\bf Overview:} +Get the bus field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_bus (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_slot} + +{\bf Overview:} +Get the slot field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_slot (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_func} + +{\bf Overview:} +Get the func field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_func (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_name} + +{\bf Overview:} +Get the name field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_name (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_vendor\_id} + +{\bf Overview:} +Get the vendor\_id field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_vendor_id (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_vendor\_name} + +{\bf Overview:} +Get the vendor\_name field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_vendor_name (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_device\_id} + +{\bf Overview:} +Get the device\_id field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_device_id (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_device\_name} + +{\bf Overview:} +Get the device\_name field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_device_name (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_revision\_id} + +{\bf Overview:} +Get the revision\_id field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_revision_id (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_class\_code} + +{\bf Overview:} +Get the class\_code field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_class_code (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_class\_name} + +{\bf Overview:} +Get the class\_name field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_class_name (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_subsystem\_vendor\_id} + +{\bf Overview:} +Get the subsystem\_vendor\_id field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_subsystem_vendor_id (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_subsystem\_vendor\_name} + +{\bf Overview:} +Get the subsystem\_vendor\_name field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_subsystem_vendor_name (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_subsystem\_id} + +{\bf Overview:} +Get the subsystem\_id field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} int get_subsystem_id (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_subsystem\_name} + +{\bf Overview:} +Get the subsystem\_name field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_subsystem_name (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_driver} + +{\bf Overview:} +Get the driver field of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} string get_driver (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the PPCI instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (PPCI ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PPCI ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given PPCI. + + \noindent {\bf Signature:} +\begin{verbatim} (PPCI record) get_record (session_id s, PPCI ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt PPCI ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +PPCI record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: user} +\subsection{Fields for class: user} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf user} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A +user of the system.}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{ins}$ & {\tt short\_name} & string & short name (e.g. userid) \\ +$\mathit{RW}$ & {\tt fullname} & string & full name \\ +\hline +\end{longtable} +\subsection{RPCs associated with class: user} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given user. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, user ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_short\_name} + +{\bf Overview:} +Get the short\_name field of the given user. + + \noindent {\bf Signature:} +\begin{verbatim} string get_short_name (session_id s, user ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_fullname} + +{\bf Overview:} +Get the fullname field of the given user. + + \noindent {\bf Signature:} +\begin{verbatim} string get_fullname (session_id s, user ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_fullname} + +{\bf Overview:} +Set the fullname field of the given user. + + \noindent {\bf Signature:} +\begin{verbatim} void set_fullname (session_id s, user ref self, string value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user ref } & self & reference to the object \\ \hline + +{\tt string } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new user instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (user ref) create (session_id s, user record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +user ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified user instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, user ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the user instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (user ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +user ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given user. + + \noindent {\bf Signature:} +\begin{verbatim} (user record) get_record (session_id s, user ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt user ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +user record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + +\vspace{1cm} +\newpage +\section{Class: XSPolicy} +\subsection{Fields for class: XSPolicy} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf XSPolicy} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A Xen Security Policy}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier / object reference \\ +$\mathit{RW}$ & {\tt repr} & string & representation of policy, i.e., XML \\ +$\mathit{RO}_\mathit{run}$ & {\tt type} & xs\_type & type of the policy \\ +$\mathit{RO}_\mathit{run}$ & {\tt flags} & xs\_instantiationflags & policy +status flags \\ +\hline +\end{longtable} +\subsection{Semantics of the class: XSPolicy} + +The XSPolicy class is used for administering Xen Security policies. Through +this class a new policy can be uploaded to the system, loaded into the +Xen hypervisor for enforcement and be set as the policy that the +system is automatically loading when the machine is started. + +This class returns information about the currently administered policy, +including a reference to the policy. This reference can then be used with +policy-specific classes, i.e., the ACMPolicy class, to allow retrieval of +information or changes to be made to a particular policy. + +\subsection{Structure and datatypes of class: XSPolicy} + +Format of the security label: + +A security label consist of the three different parts {\it policy type}, +{\it policy name} and {\it label} separated with colons. To specify +the virtual machine label for an ACM-type policy {\it xm-test}, the +security label string would be {\it ACM:xm-test:blue}, where blue +denotes the virtual machine's label. The format of resource labels is +the same.\\[0.5cm] +The following flags are used by this class: + +\begin{longtable}{|l|l|l|} +\hline +{\tt xs\_type} & value & meaning \\ +\hline +\hspace{0.5cm}{\tt XS\_POLICY\_ACM} & (1 $<<$ 0) & ACM-type policy \\ +\hline +\end{longtable} + +\begin{longtable}{|l|l|l|} +\hline +{\tt xs\_instantiationflags} & value & meaning \\ +\hline +\hspace{0.5cm}{\tt XS\_INST\_NONE} & 0 & do nothing \\ +\hspace{0.5cm}{\tt XS\_INST\_BOOT} & (1 $<<$ 0) & make system boot with this policy \\ +\hspace{0.5cm}{\tt XS\_INST\_LOAD} & (1 $<<$ 1) & load policy immediately \\ +\hline +\end{longtable} + +\begin{longtable}{|l|l|l|} +\hline +{\tt xs\_policystate} & type & meaning \\ +\hline +\hspace{0.5cm}{\tt xserr} & int & Error code from operation (if applicable) \\ +\hspace{0.5cm}{\tt xs\_ref} & XSPolicy ref & reference to the XS policy as returned by the API \\ +\hspace{0.5cm}{\tt repr} & string & representation of the policy, i.e., XML \\ +\hspace{0.5cm}{\tt type} & xs\_type & the type of the policy \\ +\hspace{0.5cm}{\tt flags } & xs\_instantiationflags & instantiation flags of the policy \\ +\hspace{0.5cm}{\tt version} & string & version of the policy \\ +\hspace{0.5cm}{\tt errors} & string & Base64-encoded sequence of integer tuples consisting \\ +& & of (error code, detail); will be returned as part \\ +& & of the xs\_setpolicy function. \\ +\hline +\end{longtable} + +\subsection{Additional RPCs associated with class: XSPolicy} +\subsubsection{RPC name:~get\_xstype} + +{\bf Overview:} +Return the Xen Security Policy types supported by this system + + \noindent {\bf Signature:} +\begin{verbatim} xs_type get_xstype (session_id s)\end{verbatim} + + \noindent {\bf Return Type:} +{\tt +xs\_type +} + +flags representing the supported Xen security policy types + \vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_xspolicy} + +{\bf Overview:} +Set the current XSPolicy. This function can also be be used for updating of +an existing policy whose name must be equivalent to the one of the +currently running policy. + +\noindent {\bf Signature:} +\begin{verbatim} xs_policystate set_xspolicy (session_id s, xs_type type, string repr, +xs_instantiationflags flags, bool overwrite)\end{verbatim} + +\noindent{\bf Arguments:} + +\vspace{0.3cm} + +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs\_type } & type & the type of policy \\ \hline +{\tt string} & repr & representation of the policy, i.e., XML \\ \hline +{\tt xs\_instantiationflags} & flags & flags for the setting of the policy \\ \hline +{\tt bool} & overwrite & whether to overwrite an existing policy \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + + \noindent {\bf Return Type:} +{\tt +xs\_policystate +} + + +State information about the policy. In case an error occurred, the 'xs\_err' +field contains the error code. The 'errors' may contain further information +about the error. + \vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~reset\_xspolicy} + +{\bf Overview:} +Attempt to reset the system's policy by installing the default policy. +Since this function is implemented as an update to the current policy, it +underlies the same restrictions. This function may fail if for example +other domains than Domain-0 are running and use a different label than +Domain-0 + +\noindent {\bf Signature:} +\begin{verbatim} xs_policystate reset_xspolicy (session_id s, xs_type type) +\end{verbatim} + +\noindent{\bf Arguments:} + +\vspace{0.3cm} + +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs\_type } & type & the type of policy \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + + \noindent {\bf Return Type:} +{\tt +xs\_policystate +} + + +State information about the policy. In case an error occurred, the 'xs\_err' +field contains the error code. The 'errors' may contain further information +about the error. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_xspolicy} + +{\bf Overview:} +Get information regarding the currently set Xen Security Policy + + \noindent {\bf Signature:} +\begin{verbatim} xs_policystate get_xspolicy (session_id s)\end{verbatim} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +xs\_policystate +} + + +Policy state information. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~rm\_xsbootpolicy} + +{\bf Overview:} +Remove any policy from the default boot configuration. + + \noindent {\bf Signature:} +\begin{verbatim} void rm_xsbootpolicy (session_id s)\end{verbatim} + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SECURITY\_ERROR} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_labeled\_resources} + +{\bf Overview:} +Get a list of resources that have been labeled. + + \noindent {\bf Signature:} +\begin{verbatim} ((string -> string) Map) get_labeled_resources (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(string $\rightarrow$ string) Map +} + + +A map of resources with their labels. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_resource\_label} + +{\bf Overview:} +Label the given resource with the given label. An empty label removes any label +from the resource. + + \noindent {\bf Signature:} +\begin{verbatim} void set_resource_label (session_id s, string resource, string +label, string old_label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & resource & resource to label \\ \hline +{\tt string } & label & label for the resource \\ \hline +{\tt string } & old\_label & Optional label value that the security label \\ +& & must currently have for the change to succeed. \\ \hline + +\end{tabular} + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SECURITY\_ERROR} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_resource\_label} + +{\bf Overview:} +Get the label of the given resource. + + \noindent {\bf Signature:} +\begin{verbatim} string get_resource_label (session_id s, string resource)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & resource & resource to label \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +The label of the given resource. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~can\_run} + +{\bf Overview:} +Check whether a VM with the given security label could run on the system. + + \noindent {\bf Signature:} +\begin{verbatim} int can_run (session_id s, string security_label)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & security\_label & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +Error code indicating whether a VM with the given security label could run. +If zero, it can run. + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} {\tt SECURITY\_ERROR} + +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the XSPolicies known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((XSPolicy ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(XSPolicy ref) Set +} + + +A list of all the IDs of all the XSPolicies +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given XSPolicy. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, XSPolicy ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt XSPolicy ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record of the referenced XSPolicy. + + \noindent {\bf Signature:} +\begin{verbatim} (XSPolicy record) get_record (session_id s, xs_ref xspolicy)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +XSPolicy record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\newpage +\section{Class: ACMPolicy} +\subsection{Fields for class: ACMPolicy} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf ACMPolicy} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em An ACM Security Policy}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier / object reference \\ +$\mathit{RW}$ & {\tt repr} & string & representation of policy, in XML \\ +$\mathit{RO}_\mathit{run}$ & {\tt type} & xs\_type & type of the policy \\ +$\mathit{RO}_\mathit{run}$ & {\tt flags} & xs\_instantiationflags & policy +status flags \\ +\hline +\end{longtable} + +\subsection{Structure and datatypes of class: ACMPolicy} + +\vspace{0.5cm} +The following data structures are used: + +\begin{longtable}{|l|l|l|} +\hline +{\tt RIP acm\_policyheader} & type & meaning \\ +\hline +\hspace{0.5cm}{\tt policyname} & string & name of the policy \\ +\hspace{0.5cm}{\tt policyurl } & string & URL of the policy \\ +\hspace{0.5cm}{\tt date} & string & data of the policy \\ +\hspace{0.5cm}{\tt reference} & string & reference of the policy \\ +\hspace{0.5cm}{\tt namespaceurl} & string & namespaceurl of the policy \\ +\hspace{0.5cm}{\tt version} & string & version of the policy \\ +\hline +\end{longtable} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_header} + +{\bf Overview:} +Get the referenced policy's header information. + + \noindent {\bf Signature:} +\begin{verbatim} acm_policyheader get_header (session_id s, xs ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +acm\_policyheader +} + + +The policy's header information. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_xml} + +{\bf Overview:} +Get the XML representation of the given policy. + + \noindent {\bf Signature:} +\begin{verbatim} string get_XML (session_id s, xs ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +XML representation of the referenced policy +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_map} + +{\bf Overview:} +Get the mapping information of the given policy. + + \noindent {\bf Signature:} +\begin{verbatim} string get_map (session_id s, xs ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +Mapping information of the referenced policy. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_binary} + +{\bf Overview:} +Get the binary policy representation of the referenced policy. + + \noindent {\bf Signature:} +\begin{verbatim} string get_binary (session_id s, xs ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +Base64-encoded representation of the binary policy. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_enforced\_binary} + +{\bf Overview:} +Get the binary policy representation of the currently enforced ACM policy. +In case the default policy is loaded in the hypervisor, a policy may be +managed by xend that is not yet loaded into the hypervisor. + + \noindent {\bf Signature:} +\begin{verbatim} string get_enforced_binary (session_id s, xs ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +Base64-encoded representation of the binary policy. +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM\_ssidref} + +{\bf Overview:} +Get the ACM ssidref of the given virtual machine. + + \noindent {\bf Signature:} +\begin{verbatim} string get_VM_ssidref (session_id s, vm ref vm)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt vm ref } & vm & reference to a valid VM \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +The ssidref of the given virtual machine. + +\vspace{0.3cm} + +\noindent{\bf Possible Error Codes:} + {\tt HANDLE\_INVALID, VM\_BAD\_POWER\_STATE, SECURITY\_ERROR} + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the ACMPolicies known to the system. + + \noindent {\bf Signature:} +\begin{verbatim} ((ACMPolicy ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(ACMPolicy ref) Set +} + + +A list of all the IDs of all the ACMPolicies +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given ACMPolicy. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, ACMPolicy ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt ACMPolicy ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record of the referenced ACMPolicy. + + \noindent {\bf Signature:} +\begin{verbatim} (XSPolicy record) get_record (session_id s, xs_ref xspolicy)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt xs ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +XSPolicy record +} + + +all fields from the object + +\newpage +\section{Class: debug} +\subsection{Fields for class: debug} +{\bf Class debug has no fields.} +\subsection{RPCs associated with class: debug} +\subsubsection{RPC name:~get\_all} + +{\bf Overview:} +Return a list of all the debug records known to the system + + \noindent {\bf Signature:} +\begin{verbatim} ((debug ref) Set) get_all (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(debug ref) Set +} + + +A list of all the IDs of all the debug records +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~return\_failure} + +{\bf Overview:} +Return an API 'successful' failure. + + \noindent {\bf Signature:} +\begin{verbatim} void return_failure (session_id s)\end{verbatim} + + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new debug instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (debug ref) create (session_id s, debug record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt debug record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +debug ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified debug instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, debug ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt debug ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the debug instance with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (debug ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +debug ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get a record containing the current state of the given debug. + + \noindent {\bf Signature:} +\begin{verbatim} (debug record) get_record (session_id s, debug ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt debug ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +debug record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + diff --git a/docs/xen-api/xenapi.tex b/docs/xen-api/xenapi.tex new file mode 100644 index 0000000..902d9b2 --- /dev/null +++ b/docs/xen-api/xenapi.tex @@ -0,0 +1,58 @@ +% +% Copyright (c) 2006-2007 XenSource, Inc. +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.2 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the +% license is included in the section entitled +% "GNU Free Documentation License" or the file fdl.tex. +% +% Authors: Ewan Mellor, Richard Sharp, Dave Scott, Jon Harrop. +% + +\documentclass{report} + +\usepackage{a4wide} +\usepackage{graphics} +\usepackage{longtable} +\usepackage{fancyhdr} +\usepackage{hyperref} + +\setlength\topskip{0cm} +\setlength\topmargin{0cm} +\setlength\oddsidemargin{0cm} +\setlength\evensidemargin{0cm} +\setlength\parindent{0pt} + +%% Parameters for coversheet: +\input{xenapi-coversheet} + +\begin{document} + +% The coversheet itself +\include{coversheet} + +% The revision history +\include{revision-history} + +% Table of contents +\tableofcontents + + +% ... and off we go! + +\chapter{Introduction} + +This document contains a description of the Xen Management API---an interface for +remotely configuring and controlling virtualised guests running on a +Xen-enabled host. + +\input{presentation} + +\include{wire-protocol} +\include{vm-lifecycle} +\include{xenapi-datamodel} +\include{fdl} + +\end{document} diff --git a/extras/mini-os/Config.mk b/extras/mini-os/Config.mk new file mode 100644 index 0000000..a1a2283 --- /dev/null +++ b/extras/mini-os/Config.mk @@ -0,0 +1,57 @@ +# Set mini-os root path, used in mini-os.mk. +MINI-OS_ROOT=$(XEN_ROOT)/extras/mini-os +export MINI-OS_ROOT + +libc = $(stubdom) + +XEN_INTERFACE_VERSION := 0x00030205 +export XEN_INTERFACE_VERSION + +# Try to find out the architecture family TARGET_ARCH_FAM. +# First check whether x86_... is contained (for x86_32, x86_32y, x86_64). +# If not x86 then use $(XEN_TARGET_ARCH) -> for ia64, ... +ifeq ($(findstring x86_,$(XEN_TARGET_ARCH)),x86_) +TARGET_ARCH_FAM = x86 +else +TARGET_ARCH_FAM = $(XEN_TARGET_ARCH) +endif + +# The architecture family directory below mini-os. +TARGET_ARCH_DIR := arch/$(TARGET_ARCH_FAM) + +# Export these variables for possible use in architecture dependent makefiles. +export TARGET_ARCH_DIR +export TARGET_ARCH_FAM + +# This is used for architecture specific links. +# This can be overwritten from arch specific rules. +ARCH_LINKS = + +# The path pointing to the architecture specific header files. +ARCH_INC := $(TARGET_ARCH_FAM) + +# For possible special header directories. +# This can be overwritten from arch specific rules. +EXTRA_INC = $(ARCH_INC) + +# Include the architecture family's special makerules. +# This must be before include minios.mk! +include $(MINI-OS_ROOT)/$(TARGET_ARCH_DIR)/arch.mk + +extra_incl := $(foreach dir,$(EXTRA_INC),-isystem $(CURDIR)/$(MINI-OS_ROOT)/include/$(dir)) + +DEF_CPPFLAGS += -isystem $(CURDIR)/$(MINI-OS_ROOT)/include +DEF_CPPFLAGS += -D__MINIOS__ + +ifeq ($(libc),y) +DEF_CPPFLAGS += -DHAVE_LIBC +DEF_CPPFLAGS += -isystem $(CURDIR)/$(MINI-OS_ROOT)/include/posix +DEF_CPPFLAGS += -isystem $(CURDIR)/$(XEN_ROOT)/tools/xenstore +endif + +ifneq ($(LWIPDIR),) +lwip=y +DEF_CPPFLAGS += -DHAVE_LWIP +DEF_CPPFLAGS += -isystem $(LWIPDIR)/src/include +DEF_CPPFLAGS += -isystem $(LWIPDIR)/src/include/ipv4 +endif diff --git a/extras/mini-os/Makefile b/extras/mini-os/Makefile new file mode 100644 index 0000000..ec24cba --- /dev/null +++ b/extras/mini-os/Makefile @@ -0,0 +1,133 @@ +# Common Makefile for mini-os. +# +# Every architecture directory below mini-os/arch has to have a +# Makefile and a arch.mk. +# + +export XEN_ROOT = ../.. +include $(XEN_ROOT)/Config.mk +OBJ_DIR ?= $(CURDIR) + +ifneq ($(stubdom),y) +include Config.mk +endif + +# Include common mini-os makerules. +include minios.mk + +# Set tester flags +# CFLAGS += -DBLKTEST_WRITE + +# Define some default flags for linking. +LDLIBS := +APP_LDLIBS := +LDARCHLIB := -L$(OBJ_DIR)/$(TARGET_ARCH_DIR) -l$(ARCH_LIB_NAME) +LDFLAGS_FINAL := -T $(TARGET_ARCH_DIR)/minios-$(XEN_TARGET_ARCH).lds + +# Prefix for global API names. All other symbols are localised before +# linking with EXTRA_OBJS. +GLOBAL_PREFIX := xenos_ +EXTRA_OBJS = + +TARGET := mini-os + +# Subdirectories common to mini-os +SUBDIRS := lib xenbus console + +# The common mini-os objects to build. +APP_OBJS := +OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(wildcard *.c)) +OBJS += $(patsubst %.c,$(OBJ_DIR)/%.o,$(wildcard lib/*.c)) +OBJS += $(patsubst %.c,$(OBJ_DIR)/%.o,$(wildcard xenbus/*.c)) +OBJS += $(patsubst %.c,$(OBJ_DIR)/%.o,$(wildcard console/*.c)) + + +.PHONY: default +default: $(OBJ_DIR)/$(TARGET) + +# Create special architecture specific links. The function arch_links +# has to be defined in arch.mk (see include above). +ifneq ($(ARCH_LINKS),) +$(ARCH_LINKS): + $(arch_links) +endif + +.PHONY: links +links: $(ARCH_LINKS) + [ -e include/xen ] || ln -sf ../../../xen/include/public include/xen + [ -e include/mini-os ] || ln -sf . include/mini-os + [ -e include/$(TARGET_ARCH_FAM)/mini-os ] || ln -sf . include/$(TARGET_ARCH_FAM)/mini-os + +.PHONY: arch_lib +arch_lib: + $(MAKE) --directory=$(TARGET_ARCH_DIR) OBJ_DIR=$(OBJ_DIR)/$(TARGET_ARCH_DIR) || exit 1; + +ifeq ($(lwip),y) +# lwIP library +LWC := $(shell find $(LWIPDIR)/ -type f -name '*.c') +LWC := $(filter-out %6.c %ip6_addr.c %ethernetif.c, $(LWC)) +LWO := $(patsubst %.c,%.o,$(LWC)) +LWO += $(addprefix $(OBJ_DIR)/,lwip-arch.o lwip-net.o) + +$(OBJ_DIR)/lwip.a: $(LWO) + $(RM) $@ + $(AR) cqs $@ $^ + +OBJS += $(OBJ_DIR)/lwip.a +endif + +OBJS := $(filter-out $(OBJ_DIR)/lwip%.o $(LWO), $(OBJS)) + +ifeq ($(libc),y) +APP_LDLIBS += -L$(XEN_ROOT)/stubdom/libxc-$(XEN_TARGET_ARCH) -whole-archive -lxenguest -lxenctrl -no-whole-archive +APP_LDLIBS += -lpci +APP_LDLIBS += -lz +APP_LDLIBS += -lm +LDLIBS += -lc +endif + +ifneq ($(APP_OBJS)-$(lwip),-y) +OBJS := $(filter-out $(OBJ_DIR)/daytime.o, $(OBJS)) +endif + +$(OBJ_DIR)/$(TARGET)_app.o: $(APP_OBJS) app.lds + $(LD) -r -d $(LDFLAGS) -\( $^ -\) $(APP_LDLIBS) --undefined main -o $@ + +$(OBJ_DIR)/$(TARGET): links $(OBJS) $(OBJ_DIR)/$(TARGET)_app.o arch_lib + $(LD) -r $(LDFLAGS) $(HEAD_OBJ) $(OBJ_DIR)/$(TARGET)_app.o $(OBJS) $(LDARCHLIB) $(LDLIBS) -o $@.o + $(OBJCOPY) -w -G $(GLOBAL_PREFIX)* -G _start $@.o $@.o + $(LD) $(LDFLAGS) $(LDFLAGS_FINAL) $@.o $(EXTRA_OBJS) -o $@ + gzip -f -9 -c $@ >$@.gz + +.PHONY: clean arch_clean + +arch_clean: + $(MAKE) --directory=$(TARGET_ARCH_DIR) OBJ_DIR=$(OBJ_DIR)/$(TARGET_ARCH_DIR) clean || exit 1; + +clean: arch_clean + for dir in $(addprefix $(OBJ_DIR)/,$(SUBDIRS)); do \ + rm -f $$dir/*.o; \ + done + rm -f $(OBJ_DIR)/*.o *~ $(OBJ_DIR)/core $(OBJ_DIR)/$(TARGET).elf $(OBJ_DIR)/$(TARGET).raw $(OBJ_DIR)/$(TARGET) $(OBJ_DIR)/$(TARGET).gz + find . $(OBJ_DIR) -type l | xargs rm -f + $(RM) $(OBJ_DIR)/lwip.a $(LWO) + rm -f tags TAGS + + +define all_sources + ( find . -follow -name SCCS -prune -o -name '*.[chS]' -print ) +endef + +.PHONY: cscope +cscope: + $(all_sources) > cscope.files + cscope -k -b -q + +.PHONY: tags +tags: + $(all_sources) | xargs ctags + +.PHONY: TAGS +TAGS: + $(all_sources) | xargs etags + diff --git a/extras/mini-os/README b/extras/mini-os/README new file mode 100644 index 0000000..41573aa --- /dev/null +++ b/extras/mini-os/README @@ -0,0 +1,46 @@ + Minimal OS + ---------- + +This shows some of the stuff that any guest OS will have to set up. + +This includes: + + * installing a virtual exception table + * handling virtual exceptions + * handling asynchronous events + * enabling/disabling async events + * parsing start_info struct at start-of-day + * registering virtual interrupt handlers (for timer interrupts) + * a simple page and memory allocator + * minimal libc support + * minimal Copy-on-Write support + * network, block, framebuffer support + * transparent access to FileSystem exports (see tools/fs-back) + +- to build it just type make. + +- to build it with TCP/IP support, download LWIP 1.3 source code and type + + make LWIPDIR=/path/to/lwip/source + +- to build it with much better libc support, see the stubdom/ directory + +- to start it do the following in domain0 (assuming xend is running) + # xm create domain_config + +This starts the kernel and prints out a bunch of stuff and then once every +second the system time. + +If you have setup a disk in the config file (e.g. +disk = [ 'file:/tmp/foo,hda,r' ] ), it will loop reading it. If that disk is +writable (e.g. disk = [ 'file:/tmp/foo,hda,w' ] ), it will write data patterns +and re-read them. + +If you have setup a network in the config file (e.g. vif = [''] ), it will +print incoming packets. + +If you have setup a VFB in the config file (e.g. vfb = ['type=sdl'] ), it will +show a mouse with which you can draw color squares. + +If you have compiled it with TCP/IP support, it will run a daytime server on +TCP port 13. diff --git a/extras/mini-os/app.lds b/extras/mini-os/app.lds new file mode 100644 index 0000000..4a48cc8 --- /dev/null +++ b/extras/mini-os/app.lds @@ -0,0 +1,11 @@ +SECTIONS +{ + .app.bss : { + __app_bss_start = . ; + *(.bss .bss.*) + *(COMMON) + *(.lbss .lbss.*) + *(LARGE_COMMON) + __app_bss_end = . ; + } +} diff --git a/extras/mini-os/arch/ia64/Makefile b/extras/mini-os/arch/ia64/Makefile new file mode 100644 index 0000000..4050662 --- /dev/null +++ b/extras/mini-os/arch/ia64/Makefile @@ -0,0 +1,63 @@ +# +# Special makefile for ia64. +# + +XEN_ROOT = ../../../.. +include $(XEN_ROOT)/Config.mk + +include ../../Config.mk + +include arch.mk +include ../../minios.mk + +ARCH_SRCS := sal.c +ARCH_SRCS += efi.c +ARCH_SRCS += time.c +ARCH_SRCS += ivt.S +ARCH_SRCS += fw.S +ARCH_SRCS += common.c +ARCH_SRCS += time.c +ARCH_SRCS += mm.c +ARCH_SRCS += debug.c +ARCH_SRCS += sched.c +ARCH_SRCS += xencomm.c +ARCH_SRCS += __umoddi3.S +ARCH_SRCS += __udivdi3.S +ARCH_SRCS += __divdi3.S + +ARCH_OBJS := sal.o +ARCH_OBJS += efi.o +ARCH_OBJS += time.o +ARCH_OBJS += ivt.o +ARCH_OBJS += fw.o +ARCH_OBJS += common.o +ARCH_OBJS += time.o +ARCH_OBJS += mm.o +ARCH_OBJS += debug.o +ARCH_OBJS += sched.o +ARCH_OBJS += xencomm.o +ARCH_OBJS += __umoddi3.o +ARCH_OBJS += __udivdi3.o +ARCH_OBJS += __udivsi3.o +ARCH_OBJS += __divdi3.o +ARCH_OBJS := $(addprefix $(OBJ_DIR)/,$(ARCH_OBJS)) + +GEN_OFF_SRC := gen_off.c +GEN_OFF_ASM := gen_off.s +GEN_OFF_H := $(MINI-OS_ROOT)/include/$(ARCH_INC)/offsets.h + +all: $(OBJ_DIR)/$(ARCH_LIB) + +$(GEN_OFF_ASM): $(GEN_OFF_SRC) + $(CC) -S -o $@ $(CPPFLAGS) $< + +$(GEN_OFF_H): $(GEN_OFF_ASM) + sed -ne "/^->/ {s/->/#define /; p}" < $< > $@ + +$(OBJ_DIR)/$(ARCH_LIB): $(GEN_OFF_H) $(ARCH_OBJS) $(OBJ_DIR)/$(HEAD_ARCH_OBJ) + $(AR) rv $(ARCH_LIB) $(ARCH_OBJS) + +clean: + rm -f $(OBJ_DIR)/$(ARCH_LIB) $(ARCH_OBJS) $(OBJ_DIR)/$(HEAD_ARCH_OBJ) + rm -f $(GEN_OFF_ASM) + rm -f $(GEN_OFF_H) diff --git a/extras/mini-os/arch/ia64/__divdi3.S b/extras/mini-os/arch/ia64/__divdi3.S new file mode 100644 index 0000000..514163c --- /dev/null +++ b/extras/mini-os/arch/ia64/__divdi3.S @@ -0,0 +1,141 @@ +.file "__divdi3.s" + +// $FreeBSD: src/sys/libkern/ia64/__divdi3.S,v 1.1 2000/10/04 17:53:03 dfr Exp $ +// +// Copyright (c) 2000, Intel Corporation +// All rights reserved. +// +// Contributed 2/15/2000 by Marius Cornea, John Harrison, Cristina Iordache, +// Ted Kubaska, Bob Norin, and Shane Story of the Computational Software Lab, +// Intel Corporation. +// +// WARRANTY DISCLAIMER +// +// 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 INTEL 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. +// +// Intel Corporation is the author of this code, and requests that all +// problem reports or change requests be submitted to it directly at +// http://developer.intel.com/opensource. +// + +.section .text +.proc __divdi3# +.align 32 +.global __divdi3# +.align 32 + +// 64-bit signed integer divide + +__divdi3: + +{ .mii + alloc r31=ar.pfs,2,0,0,0 + nop.i 0 + nop.i 0;; +} { .mmi + + // 64-BIT SIGNED INTEGER DIVIDE BEGINS HERE + + setf.sig f8=r32 + setf.sig f9=r33 + nop.i 0;; +} { .mfb + nop.m 0 + fcvt.xf f6=f8 + nop.b 0 +} { .mfb + nop.m 0 + fcvt.xf f7=f9 + nop.b 0;; +} { .mfi + nop.m 0 + // Step (1) + // y0 = 1 / b in f8 + frcpa.s1 f8,p6=f6,f7 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (2) + // e0 = 1 - b * y0 in f9 + (p6) fnma.s1 f9=f7,f8,f1 + nop.i 0 +} { .mfi + nop.m 0 + // Step (3) + // q0 = a * y0 in f10 + (p6) fma.s1 f10=f6,f8,f0 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (4) + // e1 = e0 * e0 in f11 + (p6) fma.s1 f11=f9,f9,f0 + nop.i 0 +} { .mfi + nop.m 0 + // Step (5) + // q1 = q0 + e0 * q0 in f10 + (p6) fma.s1 f10=f9,f10,f10 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (6) + // y1 = y0 + e0 * y0 in f8 + (p6) fma.s1 f8=f9,f8,f8 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (7) + // q2 = q1 + e1 * q1 in f9 + (p6) fma.s1 f9=f11,f10,f10 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (8) + // y2 = y1 + e1 * y1 in f8 + (p6) fma.s1 f8=f11,f8,f8 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (9) + // r2 = a - b * q2 in f10 + (p6) fnma.s1 f10=f7,f9,f6 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (10) + // q3 = q2 + r2 * y2 in f8 + (p6) fma.s1 f8=f10,f8,f9 + nop.i 0;; +} { .mfb + nop.m 0 + // Step (11) + // q = trunc (q3) + fcvt.fx.trunc.s1 f8=f8 + nop.b 0;; +} { .mmi + // quotient will be in r8 (if b != 0) + getf.sig r8=f8 + nop.m 0 + nop.i 0;; +} + + // 64-BIT SIGNED INTEGER DIVIDE ENDS HERE + +{ .mmb + nop.m 0 + nop.m 0 + br.ret.sptk b0;; +} + +.endp __divdi3 diff --git a/extras/mini-os/arch/ia64/__udivdi3.S b/extras/mini-os/arch/ia64/__udivdi3.S new file mode 100644 index 0000000..6a1c367 --- /dev/null +++ b/extras/mini-os/arch/ia64/__udivdi3.S @@ -0,0 +1,142 @@ +.file "__udivdi3.s" + +// $FreeBSD: src/sys/libkern/ia64/__udivdi3.S,v 1.1 2000/10/04 17:53:03 dfr Exp $ +// +// Copyright (c) 2000, Intel Corporation +// All rights reserved. +// +// Contributed 2/15/2000 by Marius Cornea, John Harrison, Cristina Iordache, +// Ted Kubaska, Bob Norin, and Shane Story of the Computational Software Lab, +// Intel Corporation. +// +// WARRANTY DISCLAIMER +// +// 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 INTEL 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. +// +// Intel Corporation is the author of this code, and requests that all +// problem reports or change requests be submitted to it directly at +// http://developer.intel.com/opensource. +// + +.section .text +.proc __udivdi3# +.align 32 +.global __udivdi3# +.align 32 + +// 64-bit unsigned integer divide + +__udivdi3: + +{ .mii + alloc r31=ar.pfs,2,0,0,0 + nop.i 0 + nop.i 0;; +} + +{ .mmi + + // 64-BIT UNSIGNED INTEGER DIVIDE BEGINS HERE + + setf.sig f8=r32 + setf.sig f9=r33 + nop.i 0;; +} { .mfb + nop.m 0 + fma.s1 f6=f8,f1,f0 + nop.b 0 +} { .mfb + nop.m 0 + fma.s1 f7=f9,f1,f0 + nop.b 0;; +} { .mfi + nop.m 0 + // Step (1) + // y0 = 1 / b in f8 + frcpa.s1 f8,p6=f6,f7 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (2) + // e0 = 1 - b * y0 in f9 + (p6) fnma.s1 f9=f7,f8,f1 + nop.i 0 +} { .mfi + nop.m 0 + // Step (3) + // q0 = a * y0 in f10 + (p6) fma.s1 f10=f6,f8,f0 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (4) + // e1 = e0 * e0 in f11 + (p6) fma.s1 f11=f9,f9,f0 + nop.i 0 +} { .mfi + nop.m 0 + // Step (5) + // q1 = q0 + e0 * q0 in f10 + (p6) fma.s1 f10=f9,f10,f10 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (6) + // y1 = y0 + e0 * y0 in f8 + (p6) fma.s1 f8=f9,f8,f8 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (7) + // q2 = q1 + e1 * q1 in f9 + (p6) fma.s1 f9=f11,f10,f10 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (8) + // y2 = y1 + e1 * y1 in f8 + (p6) fma.s1 f8=f11,f8,f8 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (9) + // r2 = a - b * q2 in f10 + (p6) fnma.s1 f10=f7,f9,f6 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (10) + // q3 = q2 + r2 * y2 in f8 + (p6) fma.s1 f8=f10,f8,f9 + nop.i 0;; +} { .mfb + nop.m 0 + // (11) q = trunc(q3) + fcvt.fxu.trunc.s1 f8=f8 + nop.b 0;; +} { .mmi + // quotient will be in r8 (if b != 0) + getf.sig r8=f8 + nop.m 0 + nop.i 0;; +} + + // 64-BIT UNSIGNED INTEGER DIVIDE ENDS HERE + +{ .mmb + nop.m 0 + nop.m 0 + br.ret.sptk b0;; +} + +.endp __udivdi3 diff --git a/extras/mini-os/arch/ia64/__udivsi3.S b/extras/mini-os/arch/ia64/__udivsi3.S new file mode 100644 index 0000000..34ff936 --- /dev/null +++ b/extras/mini-os/arch/ia64/__udivsi3.S @@ -0,0 +1,124 @@ +.file "__udivsi3.s" + +// $FreeBSD: src/sys/libkern/ia64/__udivsi3.S,v 1.1.8.1 2005/01/31 23:26:21 imp Exp $ + +//- +// Copyright (c) 2000, Intel Corporation +// All rights reserved. +// +// Contributed 2/15/2000 by Marius Cornea, John Harrison, Cristina Iordache, +// Ted Kubaska, Bob Norin, and Shane Story of the Computational Software Lab, +// Intel Corporation. +// +// WARRANTY DISCLAIMER +// +// 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 INTEL 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. +// +// Intel Corporation is the author of this code, and requests that all +// problem reports or change requests be submitted to it directly at +// http://developer.intel.com/opensource. +// + +.section .text + +// 32-bit unsigned integer divide + +.proc __udivsi3# +.align 32 +.global __udivsi3# +.align 32 + +__udivsi3: + +{ .mii + alloc r31=ar.pfs,2,0,0,0 + nop.i 0 + nop.i 0;; +} { .mii + nop.m 0 + + // 32-BIT UNSIGNED INTEGER DIVIDE BEGINS HERE + + // general register used: + // r32 - 32-bit unsigned integer dividend + // r33 - 32-bit unsigned integer divisor + // r8 - 32-bit unsigned integer result + // r2 - scratch register + // floating-point registers used: f6, f7, f8, f9 + // predicate registers used: p6 + + zxt4 r32=r32 + zxt4 r33=r33;; +} { .mmb + setf.sig f6=r32 + setf.sig f7=r33 + nop.b 0;; +} { .mfi + nop.m 0 + fcvt.xf f6=f6 + nop.i 0 +} { .mfi + nop.m 0 + fcvt.xf f7=f7 + mov r2 = 0x0ffdd;; +} { .mfi + setf.exp f9 = r2 + // (1) y0 + frcpa.s1 f8,p6=f6,f7 + nop.i 0;; +} { .mfi + nop.m 0 + // (2) q0 = a * y0 + (p6) fma.s1 f6=f6,f8,f0 + nop.i 0 +} { .mfi + nop.m 0 + // (3) e0 = 1 - b * y0 + (p6) fnma.s1 f7=f7,f8,f1 + nop.i 0;; +} { .mfi + nop.m 0 + // (4) q1 = q0 + e0 * q0 + (p6) fma.s1 f6=f7,f6,f6 + nop.i 0 +} { .mfi + nop.m 0 + // (5) e1 = e0 * e0 + 2^-34 + (p6) fma.s1 f7=f7,f7,f9 + nop.i 0;; +} { .mfi + nop.m 0 + // (6) q2 = q1 + e1 * q1 + (p6) fma.s1 f8=f7,f6,f6 + nop.i 0;; +} { .mfi + nop.m 0 + // (7) q = trunc(q2) + fcvt.fxu.trunc.s1 f8=f8 + nop.i 0;; +} { .mmi + // quotient will be in the least significant 32 bits of r8 (if b != 0) + getf.sig r8=f8 + nop.m 0 + nop.i 0;; +} + + // 32-BIT UNSIGNED INTEGER DIVIDE ENDS HERE + +{ .mmb + nop.m 0 + nop.m 0 + br.ret.sptk b0;; +} + +.endp __udivsi3 diff --git a/extras/mini-os/arch/ia64/__umoddi3.S b/extras/mini-os/arch/ia64/__umoddi3.S new file mode 100644 index 0000000..790317f --- /dev/null +++ b/extras/mini-os/arch/ia64/__umoddi3.S @@ -0,0 +1,154 @@ +.file "__umoddi3.s" + +// $FreeBSD: src/sys/libkern/ia64/__umoddi3.S,v 1.3 2003/02/11 20:15:11 schweikh Exp $ +// +// Copyright (c) 2000, Intel Corporation +// All rights reserved. +// +// Contributed 2/15/2000 by Marius Cornea, John Harrison, Cristina Iordache, +// Ted Kubaska, Bob Norin, and Shane Story of the Computational Software Lab, +// Intel Corporation. +// +// WARRANTY DISCLAIMER +// +// 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 INTEL 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. +// +// Intel Corporation is the author of this code, and requests that all +// problem reports or change requests be submitted to it directly at +// http://developer.intel.com/opensource. +// + +.section .text + + // 64-bit unsigned integer remainder + +.proc __umoddi3# +.align 32 +.global __umoddi3# +.align 32 + +__umoddi3: + +{ .mii + alloc r31=ar.pfs,3,0,0,0 + nop.i 0 + nop.i 0 +} { .mmb + + // 64-BIT UNSIGNED INTEGER REMAINDER BEGINS HERE + + // general register used: + // r32 - 64-bit unsigned integer dividend, called a below + // r33 - 64-bit unsigned integer divisor, called b below + // r8 - 64-bit unsigned integer result + // floating-point registers used: f6, f7, f8, f9, f10, f11, f12 + // predicate registers used: p6 + + setf.sig f12=r32 // holds a in integer form + setf.sig f7=r33 + nop.b 0;; +} { .mfi + // get 2s complement of b + sub r33=r0,r33 + fcvt.xuf.s1 f6=f12 + nop.i 0 +} { .mfi + nop.m 0 + fcvt.xuf.s1 f7=f7 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (1) + // y0 = 1 / b in f8 + frcpa.s1 f8,p6=f6,f7 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (2) + // q0 = a * y0 in f10 + (p6) fma.s1 f10=f6,f8,f0 + nop.i 0 +} { .mfi + nop.m 0 + // Step (3) + // e0 = 1 - b * y0 in f9 + (p6) fnma.s1 f9=f7,f8,f1 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (4) + // q1 = q0 + e0 * q0 in f10 + (p6) fma.s1 f10=f9,f10,f10 + nop.i 0 +} { .mfi + nop.m 0 + // Step (5) + // e1 = e0 * e0 in f11 + (p6) fma.s1 f11=f9,f9,f0 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (6) + // y1 = y0 + e0 * y0 in f8 + (p6) fma.s1 f8=f9,f8,f8 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (7) + // q2 = q1 + e1 * q1 in f9 + (p6) fma.s1 f9=f11,f10,f10 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (8) + // y2 = y1 + e1 * y1 in f8 + (p6) fma.s1 f8=f11,f8,f8 + nop.i 0;; +} { .mfi + nop.m 0 + // Step (9) + // r2 = a - b * q2 in f10 + (p6) fnma.s1 f10=f7,f9,f6 + nop.i 0;; +} { .mfi + // f7=-b + setf.sig f7=r33 + // Step (10) + // q3 = q2 + r2 * y2 in f8 + (p6) fma.s1 f8=f10,f8,f9 + nop.i 0;; +} { .mfi + nop.m 0 + // (11) q = trunc(q3) + fcvt.fxu.trunc.s1 f8=f8 + nop.i 0;; +} { .mfi + nop.m 0 + // (12) r = a + (-b) * q + xma.l f8=f8,f7,f12 + nop.i 0;; +} { .mib + getf.sig r8=f8 + nop.i 0 + nop.b 0 +} + + // 64-BIT UNSIGNED INTEGER REMAINDER ENDS HERE + +{ .mib + nop.m 0 + nop.i 0 + br.ret.sptk b0;; +} + +.endp __umoddi3 diff --git a/extras/mini-os/arch/ia64/arch.mk b/extras/mini-os/arch/ia64/arch.mk new file mode 100644 index 0000000..1216871 --- /dev/null +++ b/extras/mini-os/arch/ia64/arch.mk @@ -0,0 +1,20 @@ +# Build for Big Endian? +BIGENDIAN ?= n + +ARCH_CFLAGS := -mfixed-range=f2-f5,f12-f15,f32-f127 -mconstant-gp +ARCH_CFLAGS += -O2 +ARCH_ASFLAGS := -x assembler-with-cpp +ARCH_ASFLAGS += -mfixed-range=f2-f5,f12-f15,f32-f127 -fomit-frame-pointer +ARCH_ASFLAGS += -fno-builtin -fno-common -fno-strict-aliasing -mconstant-gp + +ARCH_LDFLAGS = -warn-common + +# Next lines are for big endian code ! +ifeq ($(BIGENDIAN),y) +ARCH_CFLAGS += -mbig-endian -Wa,-mbe -Wa,-mlp64 +ARCH_CFLAGS += -DBIG_ENDIAN +ARCH_ASFLAGS += -Wa,-mbe +ARCH_ASFLAGS += -DBIG_ENDIAN +ARCH_LDFLAGS = -EB -d +endif + diff --git a/extras/mini-os/arch/ia64/common.c b/extras/mini-os/arch/ia64/common.c new file mode 100644 index 0000000..c65f0a0 --- /dev/null +++ b/extras/mini-os/arch/ia64/common.c @@ -0,0 +1,268 @@ +/* + * Done by Dietmar Hahn + * + **************************************************************************** + * + * 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 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 THE 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. + * + **************************************************************************** + * + * Parts are taken from FreeBSD. + * + */ + + +#include "os.h" +#include "types.h" +#include "lib.h" +#include "page.h" +#include "xen/xen.h" +#include "privop.h" +#include "xen/callback.h" +#include "ia64_cpu.h" +#include "hypervisor.h" +#include "events.h" +#include "console.h" +#include "time.h" +#include "xmalloc.h" + + +/* For more console boot messages. */ +int bootverbose; + +/* + * This structure contains start-of-day info, such as pagetable base pointer, + * address of the shared_info structure, and things like that. + */ +union start_info_union start_info_union; + +shared_info_t *HYPERVISOR_shared_info = (shared_info_t *)XSI_BASE; + +struct machine_fw machineFwG; + +/* This pointer is initialized in ia64.S with the address of the boot param + * area passed by the bootloader. */ +struct xen_ia64_boot_param* ia64_boot_paramP; + +struct xen_ia64_boot_param ia64BootParamG; +char boot_cmd_line[COMMAND_LINE_SIZE+1]; + + +void +ia64_write_itr_i(ia64_pte_t* pteP, u32 reg, uint64_t vAddr, + uint64_t ps, uint64_t pk) +{ + /* The virtual address. */ + __asm __volatile("mov cr.ifa=%0" :: "r"(vAddr)); + /* The page size */ + __asm __volatile("mov cr.itir=%0;;" :: "r"((ps << IA64_ITIR_PS)|(pk << IA64_ITIR_KEY))); + /* Put pte into instruction translation register. */ + __asm __volatile("itr.i itr[%0]=%1" :: "r"(reg), "r"(*(uint64_t*)pteP)); + /* Serialization */ + __asm __volatile("srlz.i"); +} + +void +map_pal_code(void) +{ + ia64_pte_t pte; + + xen_set_virtual_psr_ic(0); + memset(&pte, 0, sizeof(pte)); /* Prepare the pte */ + pte.pte_p = 1; /* present bit */ + pte.pte_ma = PTE_MA_WB; /* memory attribute */ + pte.pte_a = 1; /* accessed bit */ + pte.pte_d = 1; /* dirty bit */ + pte.pte_pl = PTE_PL_KERN; /* privilege level */ + pte.pte_ar = PTE_AR_RWX; /* access rights */ + pte.pte_ppn = ((uint64_t) __pa(machineFwG.ia64_pal_base)) >> 14; + pte.pte_ed = 0; /* exception deferral */ + + /* + * Must purge here because a itc/dtc with the same address + * may be in the tlb! + */ + ia64_ptc_l(machineFwG.ia64_pal_base, PTE_PS_16K); + ia64_write_itr_i(&pte, IA64_TR_PAL, + (uint64_t)machineFwG.ia64_pal_base, + PTE_PS_16K, IA64_KEY_REG7); + xen_set_virtual_psr_ic(1); +} + +/* In ivt.S */ +extern char hypervisor_callback; + +static void +registerCallback(void) +{ + struct callback_register event = + { + .type = SWAP(CALLBACKTYPE_event), + .address = SWAP((unsigned long)&hypervisor_callback), + }; + HYPERVISOR_callback_op(CALLBACKOP_register, &event); +} + +static void +init_start_info(start_info_t* xen_start_info) +{ + /* Make a copy of the start_info structure */ + start_info.nr_pages = SWAP(xen_start_info->nr_pages); + start_info.shared_info = SWAP(xen_start_info->shared_info); + start_info.flags = SWAP(xen_start_info->flags); + start_info.store_mfn = SWAP(xen_start_info->store_mfn); + start_info.store_evtchn = SWAP(xen_start_info->store_evtchn); + start_info.console.domU.mfn = SWAP(xen_start_info->console.domU.mfn); + start_info.console.domU.evtchn = + SWAP(xen_start_info->console.domU.evtchn); + start_info.pt_base = SWAP(xen_start_info->pt_base); + start_info.nr_pt_frames = SWAP(xen_start_info->nr_pt_frames); + start_info.mfn_list = SWAP(xen_start_info->mfn_list); + start_info.mod_start = SWAP(xen_start_info->mod_start); + start_info.mod_len = SWAP(xen_start_info->mod_len); +} + +static void +init_boot_params(void) +{ + ia64BootParamG.command_line = SWAP(ia64_boot_paramP->command_line); + ia64BootParamG.efi_systab = SWAP(ia64_boot_paramP->efi_systab); + ia64BootParamG.efi_memmap = SWAP(ia64_boot_paramP->efi_memmap); + ia64BootParamG.efi_memmap_size = + SWAP(ia64_boot_paramP->efi_memmap_size); + ia64BootParamG.efi_memdesc_size = + SWAP(ia64_boot_paramP->efi_memdesc_size); + ia64BootParamG.efi_memdesc_version = + SWAP(ia64_boot_paramP->efi_memdesc_version); + ia64BootParamG.console_info.num_cols = + SWAP(ia64_boot_paramP->console_info.num_cols); + ia64BootParamG.console_info.num_rows = + SWAP(ia64_boot_paramP->console_info.num_rows); + ia64BootParamG.console_info.orig_x = + SWAP(ia64_boot_paramP->console_info.orig_x); + ia64BootParamG.console_info.orig_y = + SWAP(ia64_boot_paramP->console_info.orig_y); + ia64BootParamG.fpswa = SWAP(ia64_boot_paramP->fpswa); + ia64BootParamG.initrd_start = SWAP(ia64_boot_paramP->initrd_start); + ia64BootParamG.initrd_size = SWAP(ia64_boot_paramP->initrd_size); + ia64BootParamG.domain_start = SWAP(ia64_boot_paramP->domain_start); + ia64BootParamG.domain_size = SWAP(ia64_boot_paramP->domain_size); + + /* + * Copy and parse the boot command line. + * Currently only a check of bootverbose is done. + */ + memset(boot_cmd_line, 0, sizeof(boot_cmd_line)); + strncpy(boot_cmd_line, + (char*)__va(ia64BootParamG.command_line), COMMAND_LINE_SIZE); + boot_cmd_line[COMMAND_LINE_SIZE - 1] = '\0'; + + /* Look for bootverbose. */ + if (strstr(boot_cmd_line, "bootverbose")) + bootverbose = 1; +} + +static void +set_opt_feature(void) +{ + struct xen_ia64_opt_feature optf; + + optf.cmd = XEN_IA64_OPTF_IDENT_MAP_REG7; + optf.on = XEN_IA64_OPTF_ON; + optf.pgprot = ((1 << PTE_OFF_P) | (1 << PTE_OFF_A) | (1 << PTE_OFF_D) | + (PTE_MA_WB << PTE_OFF_MA) | + (PTE_PL_KERN << PTE_OFF_PL) | + (PTE_AR_RW << PTE_OFF_AR)); + optf.key = IA64_KEY_REG7; + HYPERVISOR_opt_feature(&optf); +} + +void +arch_init(start_info_t *si) +{ + efi_time_t tm; + static int initialized; + + if (initialized) + return; + + init_start_info(si); + + init_boot_params(); + + init_efi(); + + map_pal_code(); + + ia64_sal_init(machineFwG.ia64_sal_tableP); + + if (efi_get_time(&tm)) { + printk("EFI-SystemTime: %d.%d.%d %d:%d:%d", + tm.Day, tm.Month, tm.Year, + tm.Hour, tm.Minute, tm.Second); + + if (tm.TimeZone == EFI_UNSPECIFIED_TIMEZONE) + printk(" Timezone not specified!\n"); + else + printk(" TimeZone: %d Daylight: 0x%x\n", + tm.TimeZone, tm.Daylight); + } else + printk("efi_get_time() failed\n"); + + registerCallback(); + + set_opt_feature(); + + initialized = 1; +} + +void +arch_fini(void) +{ + /* TODO */ +} + +void +arch_print_info(void) +{ + int major, minor; + + minor = HYPERVISOR_xen_version(XENVER_version, 0); + major = minor >> 16; + minor &= ~0xffffffff; + printk("Running on Xen version: %d.%d\n", major, minor); +#if 0 + printk("machine addr of shared_info_t : 0x%lx\n", + start_info.shared_info); + printk("machine page number of shared page: 0x%lx\n", + start_info.store_mfn); + printk("evtchn for store communication : %d\n", + start_info.store_evtchn); + printk("MACHINE address of console page: 0x%lx\n", + start_info.console.domU.mfn); + printk("evtchn for console messages : %d\n", + start_info.console.domU.evtchn); +#endif + if(strlen(boot_cmd_line) > 0) + printk("xen_guest_cmdline : %s\n", boot_cmd_line); +} + diff --git a/extras/mini-os/arch/ia64/debug.c b/extras/mini-os/arch/ia64/debug.c new file mode 100644 index 0000000..d95252b --- /dev/null +++ b/extras/mini-os/arch/ia64/debug.c @@ -0,0 +1,179 @@ +/* + **************************************************************************** + * Done by Dietmar Hahn trap_num, + ia64_vector_names[tf->trap_num]); + printk(" iip : 0x%.16lx ifa: 0x%.16lx\n", tf->iip, tf->ifa); + printk(" ipsr: 0x%.16lx ifs: 0x%.16lx\n", tf->ipsr, tf->ifs); + printk(" isr : 0x%.16lx\n", tf->isr); + printk(" gp : 0x%.16lx sp : 0x%.16lx\n", tf->gp, tf->sp); + printk(" rp : 0x%.16lx tp : 0x%.16lx\n", tf->b0, tf->tp); + printk(" b6 : 0x%.16lx b7 : 0x%.16lx\n", tf->b6, tf->b7); + printk(" r8 : 0x%.16lx\n", tf->r8); + printk(" bsp : 0x%.16lx rsc: 0x%.16lx\n", tf->bsp, tf->rsc); + printk(" r14 : 0x%.16lx r15: 0x%.16lx\n", tf->r14, tf->r15); + printk(" r16 : 0x%.16lx r17: 0x%.16lx\n", tf->r16, tf->r17); + printk(" r18 : 0x%.16lx r19: 0x%.16lx\n", tf->r18, tf->r19); + printk(" r20 : 0x%.16lx r21: 0x%.16lx\n", tf->r20, tf->r21); + printk(" r22 : 0x%.16lx r23: 0x%.16lx\n", tf->r22, tf->r23); + printk(" r24 : 0x%.16lx r25: 0x%.16lx\n", tf->r24, tf->r25); + printk(" r26 : 0x%.16lx r27: 0x%.16lx\n", tf->r26, tf->r27); + printk(" r28 : 0x%.16lx r29: 0x%.16lx\n", tf->r28, tf->r29); + printk(" r30 : 0x%.16lx r31: 0x%.16lx\n", tf->r30, tf->r31); + + __asm __volatile("flushrs;;"); + curIfs = *((ifs_t*)((void*)(&tf->ifs))); + if (!curIfs.v) + printk(" ifs.v = 0"); + else { + uint64_t* regP; + uint32_t i; + + printk(" cfm.sof: %d cfm.sol: %d\n", curIfs.sof, curIfs.sol); + regP = (uint64_t *)(tf->bsp + tf->ndirty); + for (i = curIfs.sof; i != 0; ) { + if (i <= (((uint64_t)regP & 0x000001f8) >> 3)) { + regP -= i; + i = 0; + break; + } + i -= ((uint64_t)regP & 0x000001f8) >> 3; + regP = (uint64_t *)((uint64_t)regP & ~0x000001ff) - 1; + } + for (i = 0; i < curIfs.sof; i++) { + if (((uint64_t)regP & 0x000001f8) == 0x000001f8) + regP++; + printk(" r%d: 0x%lx\n", i+32, *regP); + regP++; + } + } + HYPERVISOR_shutdown(SHUTDOWN_poweroff); +} diff --git a/extras/mini-os/arch/ia64/efi.c b/extras/mini-os/arch/ia64/efi.c new file mode 100644 index 0000000..498eac4 --- /dev/null +++ b/extras/mini-os/arch/ia64/efi.c @@ -0,0 +1,237 @@ +/* + * Done by Dietmar Hahn + * The code is partly taken from FreeBSD. + * + *************************************************************************** + * + * 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 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 THE 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 "os.h" +#include "efi.h" +#include "page.h" +#include "lib.h" +#include "console.h" + + +/* The implementation is in fw.S. */ +extern uint64_t +ia64_call_efi_func(uint64_t funcP,uint64_t a,uint64_t b,uint64_t c,uint64_t d); + +int +efi_get_time(efi_time_t* tmP) +{ + memset(tmP, 0, sizeof(efi_time_t)); + if (ia64_call_efi_func((uint64_t)machineFwG.efi.getTimeF, + (uint64_t)tmP, + (uint64_t)NULL, 0, 0) != EFI_SUCCESS) { + printk("efi.getTime() failed\n"); + return 0; + } + +#if defined(BIG_ENDIAN) + tmP->Year = SWAP(tmP->Year); + tmP->TimeZone = SWAP(tmP->TimeZone); + tmP->Nanosecond = SWAP(tmP->Nanosecond); +#endif + + return 1; +} + +/* + * The function compares two efi_guid_t and returns 0 on equality, otherwise 1. + */ +static int +efi_guid_cmp(efi_guid_t* a_le, efi_guid_t* b) +{ +#if defined(BIG_ENDIAN) + if(SWAP(a_le->Data1) != b->Data1) + return 1; + if(SWAP(a_le->Data2) != b->Data2) + return 1; + if(SWAP(a_le->Data3) != b->Data3) + return 1; + return memcmp(a_le->Data4, b->Data4, sizeof(uint8_t)*8); +#else + return memcmp(a_le, b, sizeof(efi_guid_t)); +#endif +} + +void +init_efi(void) +{ + efi_system_table_t* efiSysTableP; + int mdcnt, i, numConvMem; + efi_memory_descriptor_t *memdP, *mdP; + efi_status_t status; + char fwVendor[100] = "unknown"; + efi_char16_t* fwP; + efi_runtime_services_t* rsP; + + efi_configuration_table_t* confP = (efi_configuration_table_t*)0; + efi_guid_t sal = SAL_SYSTEM_TABLE_GUID; + efi_guid_t acpi = ACPI_TABLE_GUID; + efi_guid_t acpi20 = ACPI_20_TABLE_GUID; + + memset(&machineFwG, 0, sizeof(machineFwG)); + /* Read the efi_system_table. */ + efiSysTableP = (efi_system_table_t*)__va(ia64BootParamG.efi_systab); + machineFwG.efi.efiSysTableP = efiSysTableP; + PRINT_BV("EfiSystemTable at: %p\n", efiSysTableP); + fwP = (uint16_t*) __va(SWAP(efiSysTableP->FirmwareVendor)); + if (fwP) { + for (i = 0; i < (int)sizeof(fwVendor) - 1 && *fwP; ++i) + fwVendor[i] = SWAP(*fwP++); + fwVendor[i] = '\0'; + } + PRINT_BV(" EFI-FirmwareVendor : %s\n", fwVendor); + PRINT_BV(" EFI-FirmwareRevision : %d\n", + SWAP(efiSysTableP->FirmwareRevision)); + PRINT_BV(" EFI-SystemTable-Revision : %d.%d\n", + SWAP(efiSysTableP->Hdr.Revision)>>16, + SWAP(efiSysTableP->Hdr.Revision)&0xffff); + rsP = (efi_runtime_services_t*) + __va(SWAP(efiSysTableP->RuntimeServices)); + mdcnt = ia64BootParamG.efi_memmap_size / + ia64BootParamG.efi_memdesc_size; + memdP = (efi_memory_descriptor_t*) __va(ia64BootParamG.efi_memmap); + + PRINT_BV("EFI-Memorydescriptors: %d\n", mdcnt); + + for (i = numConvMem = 0, mdP = memdP; i < mdcnt; i++, + mdP = NextMemoryDescriptor(mdP, ia64BootParamG.efi_memdesc_size)) { + /* Relocate runtime memory segments for firmware. */ + PRINT_BV(" %d. Type: %x Attributes: 0x%lx\n", + i, SWAP(mdP->Type), SWAP(mdP->Attribute)); + PRINT_BV(" PhysStart: 0x%lx NumPages: 0x%lx\n", + SWAP(mdP->PhysicalStart), SWAP(mdP->NumberOfPages)); + switch (SWAP(mdP->Type)) { + case EfiRuntimeServicesData: + PRINT_BV(" -> EfiRuntimeServicesData\n"); + break; + case EfiACPIReclaimMemory: + PRINT_BV(" -> EfiACPIReclaimMemory\n"); + break; + case EfiACPIMemoryNVS: + PRINT_BV(" -> EfiACPIMemoryNVS\n"); + break; + case EfiConventionalMemory: + PRINT_BV(" -> EfiConventionalMemory\n"); + PRINT_BV(" start: 0x%lx end: 0x%lx\n", + SWAP(mdP->PhysicalStart), + SWAP(mdP->PhysicalStart)+ + SWAP(mdP->NumberOfPages)*EFI_PAGE_SIZE); + if (numConvMem) { + printk(" Currently only one efi " + "memory chunk supported !!!\n"); + break; + } + machineFwG.mach_mem_start = + SWAP(mdP->PhysicalStart); + machineFwG.mach_mem_size = + SWAP(mdP->NumberOfPages)*EFI_PAGE_SIZE; + numConvMem++; + break; + case EfiMemoryMappedIOPortSpace: + PRINT_BV(" -> EfiMemMappedIOPortSpace\n"); + break; + case EfiPalCode: + machineFwG.ia64_pal_base = + __va(SWAP(mdP->PhysicalStart)); + PRINT_BV(" -> EfiPalCode\n" + " start : %p\n", + machineFwG.ia64_pal_base); + break; + } + /* I have to setup the VirtualStart address of every + * RUNTIME-area in preparing the later call of + * SetVirtualAddressMap() therewidth the efi stuff uses + * virtual addressing and the efi runtime functions + * may be called directly. + */ + if (SWAP(mdP->Attribute) & EFI_MEMORY_RUNTIME) { + if (SWAP(mdP->Attribute) & EFI_MEMORY_WB) + mdP->VirtualStart = + SWAP(__va(mdP->PhysicalStart)); + else { + if (SWAP(mdP->Attribute) & EFI_MEMORY_UC) + printk("efi_init: RuntimeMemory with " + "UC attribute !!!!!!\n"); + /* + mdP->VirtualStart = + IA64_PHYS_TO_RR6(mdP->PhysicalStart); + */ + } + } + } + /* Now switch efi runtime stuff to virtual addressing. */ + status = ia64_call_efi_physical( + (void*)__va(SWAP((uint64_t)rsP->SetVirtualAddressMap)), + ia64BootParamG.efi_memmap_size, + ia64BootParamG.efi_memdesc_size, + ia64BootParamG.efi_memdesc_version, + ia64BootParamG.efi_memmap); + status = EFI_SUCCESS; + if (status != EFI_SUCCESS) { + printk("warning: unable to switch EFI into virtual " + "(status=%lu)\n", status); + return; + } + /* Getting efi function pointer for getEfiTime. */ + machineFwG.efi.getTimeF = + (efi_get_time_t)__va(SWAP((uint64_t)rsP->GetTime)); + /* Getting efi function pointer for resetSystem. */ + machineFwG.efi.resetSystemF = + (efi_reset_system_t)__va(SWAP((uint64_t)rsP->ResetSystem)); + + /* Scanning the Configuration table of the EfiSystemTable. */ + PRINT_BV("NumberOfConfigTableEntries: %ld\n", + SWAP(efiSysTableP->NumberOfTableEntries)); + + confP = (efi_configuration_table_t*) + __va(SWAP(efiSysTableP->ConfigurationTable)); + for (i = 0; i < SWAP(efiSysTableP->NumberOfTableEntries); i++) { + if (!efi_guid_cmp(&confP[i].VendorGuid, &sal)) { + machineFwG.ia64_sal_tableP = (sal_system_table_t*) + __va(SWAP((uint64_t) confP[i].VendorTable)); + PRINT_BV(" Found SalSystemTable at: 0x%lx\n", + (uint64_t) machineFwG.ia64_sal_tableP); + continue; + } + if (!efi_guid_cmp(&confP[i].VendorGuid, &acpi)) { + machineFwG.ia64_efi_acpi_table = + __va(SWAP((uint64_t) confP[i].VendorTable)); + PRINT_BV(" Found AcpiTable at: 0x%lx\n", + (uint64_t) machineFwG.ia64_efi_acpi_table); + continue; + } + if (!efi_guid_cmp(&confP[i].VendorGuid, &acpi20)) { + machineFwG.ia64_efi_acpi20_table = + __va(SWAP((uint64_t) confP[i].VendorTable)); + PRINT_BV(" Found Acpi20Table at: 0x%lx\n", + (uint64_t) machineFwG.ia64_efi_acpi20_table); + continue; + } + } +} diff --git a/extras/mini-os/arch/ia64/fw.S b/extras/mini-os/arch/ia64/fw.S new file mode 100644 index 0000000..48bb851 --- /dev/null +++ b/extras/mini-os/arch/ia64/fw.S @@ -0,0 +1,540 @@ +/* + * Done by Dietmar Hahn + * Parts taken from FreeBSD. + * + *************************************************************************** + * + * 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 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 THE 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 "asm.h" +#include "page.h" +#include "ia64_cpu.h" +#include "ia64_fpu.h" +#include "offsets.h" +#include "xen/xen.h" + + +/* + * ia64_change_mode: change mode to/from physical mode + * + * Arguments: + * r14 psr for desired mode + * + * Modifies: + * r15-r20 scratch + * ar.bsp translated to new mode + * sp translated to new mode + * iip translated to new mode + */ +ENTRY(ia64_change_mode) + rsm psr.i | psr.ic + mov r19=ar.rsc // save rsc while we change mode + tbit.nz p8,p9=r14,17 // Uses psr.dt-physical or virtual ? + // p8 == true: switch to virtual + // p9 == true: switch to physical + ;; + mov ar.rsc=IA64_RSE_LAZY // turn off RSE + mov r16=rp + ;; + flushrs // clean the rse + srlz.i + ;; +1: mov r15=ip + mov r17=ar.bsp + mov r18=ar.rnat + ;; + add r15=2f-1b,r15 // address to rfi to + /* !!! must be the same like in minios-ia64.lds */ +(p8) movl r20=(KERNEL_START - (1< + * + ****************************************************************************** + * + * 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 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 THE 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 "types.h" +#include "sched.h" +#include "xen/xen.h" +#include "xen/arch-ia64.h" + +#define DEFINE(sym, val) \ + asm volatile("\n->" sym " %0 /* " #val " */": : "i" (val)) +#define DEFINE_STR2(sym, pfx, val) \ + asm volatile("\n->" sym " " pfx "%0" : : "i"(val)); + +#define SZ(st,e) sizeof(((st *)0)->e) +#define OFF(st,e,d,o) \ + DEFINE(#d, offsetof(st, e) + o); \ + DEFINE(#d "_sz", SZ(st,e )); \ + DEFINE_STR2(#d "_ld", "ld", SZ(st, e)); \ + DEFINE_STR2(#d "_st", "st", SZ(st, e)); + +#define TFOFF(e,d) OFF(trap_frame_t, e, d, 0) +#define SIZE(st,d) DEFINE(#d, sizeof(st)) + +#define SWOFF(e,d) OFF(struct thread, e, d, 0) + +/* shared_info_t from xen/xen.h */ +#define SI_OFF(e, d) OFF(shared_info_t, e, d,0) +/* mapped_regs_t from xen/arch-ia64.h */ +#define MR_OFF(e, d) OFF(mapped_regs_t, e, d, XMAPPEDREGS_OFS) + +int +main(int argc, char ** argv) +{ + TFOFF(cfm, TF_CFM); + TFOFF(pfs, TF_PFS); + TFOFF(bsp, TF_BSP); + TFOFF(rnat, TF_RNAT); + TFOFF(csd, TF_CSD); + TFOFF(ccv, TF_CCV); + TFOFF(unat, TF_UNAT); + TFOFF(fpsr, TF_FPSR); + TFOFF(pr, TF_PR); + + TFOFF(sp, TF_SP); + TFOFF(gp, TF_GP); + TFOFF(tp, TF_TP); + + TFOFF(r2, TF_GREG2); + TFOFF(r3, TF_GREG3); + TFOFF(r16, TF_GREG16); + TFOFF(r17, TF_GREG17); + + TFOFF(b0, TF_BREG0); + TFOFF(b6, TF_BREG6); + TFOFF(b7, TF_BREG7); + + TFOFF(f6, TF_FREG6); + TFOFF(f7, TF_FREG7); + + TFOFF(rsc, TF_RSC); + TFOFF(ndirty, TF_NDIRTY); + TFOFF(ssd, TF_SSD); + TFOFF(iip, TF_IIP); + TFOFF(ipsr, TF_IPSR); + TFOFF(ifs, TF_IFS); + TFOFF(trap_num, TF_TRAP_NUM); + + TFOFF(ifa, TF_IFA); + TFOFF(isr, TF_ISR); + TFOFF(iim, TF_IIM); + + SIZE(trap_frame_t, TF_SIZE); + + SIZE(struct thread, SW_SIZE); + SWOFF(regs.unat_b, SW_UNATB); + SWOFF(regs.sp, SW_SP); + SWOFF(regs.rp, SW_RP); + SWOFF(regs.pr, SW_PR); + SWOFF(regs.pfs, SW_PFS); + SWOFF(regs.bsp, SW_BSP); + SWOFF(regs.rnat, SW_RNAT); + SWOFF(regs.lc, SW_LC); + //SWOFF(regs.fpsr, SW_FPSR); + //SWOFF(regs.psr, SW_PSR); + //SWOFF(regs.gp, SW_GP); + SWOFF(regs.unat_a, SW_UNATA); + SWOFF(regs.r4, SW_R4); + SWOFF(regs.r5, SW_R5); + SWOFF(regs.r6, SW_R6); + SWOFF(regs.r7, SW_R7); + SWOFF(regs.b1, SW_B1); + SWOFF(regs.b2, SW_B2); + SWOFF(regs.b3, SW_B3); + SWOFF(regs.b4, SW_B4); + SWOFF(regs.b5, SW_B5); + SWOFF(regs.f2, SW_F2); + SWOFF(regs.f3, SW_F3); + SWOFF(regs.f4, SW_F4); + SWOFF(regs.f5, SW_F5); + + SI_OFF(arch.start_info_pfn, START_INFO_PFN); + MR_OFF(interrupt_mask_addr, XSI_PSR_I_ADDR_OFS); + MR_OFF(interrupt_collection_enabled, XSI_PSR_IC_OFS); + MR_OFF(ipsr, XSI_IPSR_OFS); + MR_OFF(iip, XSI_IIP_OFS); + MR_OFF(ifs, XSI_IFS_OFS); + MR_OFF(ifa, XSI_IFA_OFS); + MR_OFF(iim, XSI_IIM_OFS); + MR_OFF(iim, XSI_IIM_OFS); + MR_OFF(iipa, XSI_IIPA_OFS); + MR_OFF(isr, XSI_ISR_OFS); + MR_OFF(banknum, XSI_BANKNUM_OFS); + MR_OFF(bank1_regs[0], XSI_BANK1_R16_OFS); + MR_OFF(precover_ifs, XSI_PRECOVER_IFS_OFS); + + return 0; +} diff --git a/extras/mini-os/arch/ia64/ia64.S b/extras/mini-os/arch/ia64/ia64.S new file mode 100644 index 0000000..7ea9433 --- /dev/null +++ b/extras/mini-os/arch/ia64/ia64.S @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2007 Dietmar Hahn + * + ***************************************************************************** + * 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 "asm.h" +#include "page.h" +#include "ia64_cpu.h" +#include "ia64_fpu.h" +#include "privop.h" +#include "offsets.h" + + + + /* + * Allocate kernel stack area. + * This is used for stack pointer (goes down from kstack+PAGE_SIZE) and + * RSE (goes up from kstack). + */ + .section .data.start,"aw" + .global kstack + .align PAGE_SIZE +kstack: .space KSTACK_PAGES * PAGE_SIZE + + .text + + /* + * Start the kernel. + * r28 points to the address of the boot parameter area, given + * from the bootloader. + * Execution reaches here in physical mode. + */ +ENTRY(_start) + .prologue + .save rp, r0 // terminate unwind chain with a NULL rp + .body + + alloc loc0=ar.pfs,0,1,1,0 + + rsm psr.i | psr.ic + ;; + srlz.i + ;; + + /* + * Initialize mini-os region registers: + * Currently only region registers 5 and 7 are used for addressing. + * rr[5] : virtual kernel address space + * rr[7] : directly mapped physically addresses. + */ + movl r2=0< + * Description: ia64 specific trap handling. + * + **************************************************************************** + * 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 "asm.h" +#include "page.h" +#include "ia64_cpu.h" +#include "privop.h" +#include "offsets.h" + + +/* General register usage in interrupt handling: + * r16, r17, ... are used for input parameters of sub-routines + * r29: used to access memory which may raise nested TLB fault + * r30: b0 save register + * r31: predicates save register + * p30,p31: used for TLB stuff: (0,1)=data, (1,0)=instruction + */ + + +#define FILL_FP_PAIR(f1, f2, b1, b2) \ + ldf.fill f1=[b1],32 ;\ + ldf.fill f2=[b2],32 ;\ + ;; + +#define SPILL_FP_PAIR(f1, f2, b1, b2) \ + stf.spill [b1]=f1,32 ;\ + stf.spill [b2]=f2,32 ;\ + ;; + +#define FILL_REG_PAIR(r1, r2, b1, b2) \ + ld8.fill r1=[b1],16 ;\ + ld8.fill r2=[b2],16 ;\ + ;; + +#define SPILL_REG_PAIR(r1, r2, b1, b2) \ + .mem.offset 0,0 ;\ + st8.spill [b1]=r1,16 ;\ + .mem.offset 8,0 ;\ + st8.spill [b2]=r2,16 ;\ + ;; + + +/** + * The function does a store of the current processor context + * to the given exception frame address. + * These are some special and the scratch registers for calling + * C-functions later. + * The bspstore will be the same. A clean RSE is made with the + * cover instruction. + * + * The return is done through a jump to the next bundle after ip (r16). + * + * Used register: r16, r18, r19, r20, r21, r22 of bank 0 + * + * @param: r16 ip of the bundle with the jump. + * @param: r18 pointer to the trap frame. + * @param: r23 trap number/err val + * + */ + +ENTRY(save_tf_rse_switch) + movl r21=XSI_IPSR // XEN !! + movl r22=XSI_IIP // XEN !! + ;; + ld8 r21=[r21] // XEN.ipsr + ld8 r22=[r22];; // XEN.iip +#if defined(BIG_ENDIAN) + mux1 r21=r21,@rev // swap because mini-os is in BE + mux1 r22=r22,@rev // swap because mini-os is in BE + ;; +#endif + add r19=TF_IPSR,r18 + add r20=TF_IIP,r18 + ;; + st8 [r19]=r21 // store cr.ipsr + st8 [r20]=r22 // store cr.iip + ;; + //// r16 return jump pointer, r18 - trap frame base, + add r19=TF_UNAT,r18 + mov r20=ar.unat + ;; + st8 [r19]=r20 // store scratch unat + ;; + + add r19=TF_GP,r18 + add r20=TF_SP,r18 + ;; + st8 [r19]=gp,TF_TP-TF_GP // store gp + st8 [r20]=sp,TF_PR-TF_SP // store sp + mov r21=pr + ;; + st8 [r19]=r13 // store tp + st8 [r20]=r21 // store pr + ;; + add r19=TF_GREG2,r18 // Now first general regs. + add r20=TF_GREG3,r18 + ;; + SPILL_REG_PAIR( r2, r3,r19,r20) + SPILL_REG_PAIR( r8, r9,r19,r20) + SPILL_REG_PAIR(r10,r11,r19,r20) + SPILL_REG_PAIR(r14,r15,r19,r20) + ;; + mov r14=r18 // move trap frame base for bsw + mov r15=r16 // save return address + ;; + //bsw.1 // switch to bank 1 for saving these registers. + movl r30=XSI_BANKNUM // Switch to bank 1. + mov r31=1;; +#if defined(BIG_ENDIAN) + mux1 r31=r31,@rev // swap because mini-os is in BE + ;; +#endif + st4 [r30]=r31 + ;; + /* + * On XEN the hypervisor has stored the bank 1 registers + * r16-r31. I must reload these registers here to get + * access. + */ + movl r30=XSI_BANK1_R16; + movl r31=XSI_BANK1_R16+8;; + ld8 r16=[r30],16; ld8 r17=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r16=r16,@rev; mux1 r17=r17,@rev;; +#endif + ld8 r18=[r30],16; ld8 r19=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r18=r18,@rev; mux1 r19=r19,@rev;; +#endif + ld8 r20=[r30],16; ld8 r21=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r20=r20,@rev; mux1 r21=r21,@rev;; +#endif + ld8 r22=[r30],16; ld8 r23=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r22=r22,@rev; mux1 r23=r23,@rev;; +#endif + ld8 r24=[r30],16; ld8 r25=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r24=r24,@rev; mux1 r25=r25,@rev;; +#endif + ld8 r26=[r30],16; ld8 r27=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r26=r26,@rev; mux1 r27=r27,@rev;; +#endif + ld8 r28=[r30],16; ld8 r29=[r31],16;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r28=r28,@rev; mux1 r29=r29,@rev;; +#endif + ld8 r30=[r30]; ld8 r31=[r31];; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r30=r30,@rev; mux1 r31=r31,@rev;; +#endif + + add r2=TF_GREG16,r14 + add r3=TF_GREG17,r14 + ;; + SPILL_REG_PAIR(r16,r17,r2,r3) + SPILL_REG_PAIR(r18,r19,r2,r3) + SPILL_REG_PAIR(r20,r21,r2,r3) + SPILL_REG_PAIR(r22,r23,r2,r3) + SPILL_REG_PAIR(r24,r25,r2,r3) + SPILL_REG_PAIR(r26,r27,r2,r3) + SPILL_REG_PAIR(r28,r29,r2,r3) + SPILL_REG_PAIR(r30,r31,r2,r3) + ;; + //bsw.0 // back to interrupt bank 0 + movl r2=XSI_BANKNUM;; + st4 [r2]=r0 + ;; + mov r18=r14 // restore context pointer + mov r16=r15 // restore return address + ;; + //// r16 return jump pointer, r18 - trap frame base, + add r19=TF_CCV,r18 + add r20=TF_CSD,r18 + mov r21=ar.ccv + mov r22=ar.csd + ;; + st8 [r19]=r21 // ar.ccv + st8 [r20]=r22 // ar.csd + ;; + add r19=TF_SSD,r18 + mov r21=ar.ssd + ;; + st8 [r19]=r21 // ar.ssd + ;; + add r19=TF_FREG6,r18 + add r20=TF_FREG7,r18 + ;; + SPILL_FP_PAIR(f6, f7, r19, r20) + SPILL_FP_PAIR(f8, f9, r19, r20) + SPILL_FP_PAIR(f10, f11, r19, r20) + + add r19=TF_BREG0,r18 // b0, b6, b7 + add r20=TF_BREG6,r18 + mov r21=b0 + mov r22=b6 + ;; + st8 [r19]=r21,TF_BREG7-TF_BREG0 // store b0 + st8 [r20]=r22,16 // store b6 + ;; + mov r21=b7 + ;; + st8 [r19]=r21 // store b7 + + //// r16 return jump pointer, r18 - trap frame base, + + // Read and save RSC, PFS + add r19=TF_PFS,r18 + add r20=TF_RSC,r18 + mov r21=ar.pfs + mov r22=ar.rsc + ;; +{ .mmb + st8 [r19]=r21 // store ar.pfs + st8 [r20]=r22 // store ar.rsc + // Issue cover instruction + cover // must be the last instruction in bundle + //XEN_HYPER_COVER + ;; +} + // Read and save IFS + add r19=TF_IFS,r18 + add r20=TF_CFM,r18 + /* xen special handling for possibly lazy cover */ + movl r8=XSI_PRECOVER_IFS; + ;; + ld8 r21=[r8] + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r21=r21,@rev + ;; +#endif + st8 [r19]=r21 // store cr.ifs + dep.z r22=r21,0,38 // copy ifm part from ifs.ifm + ;; + st8 [r20]=r22 // store cfm + // RSE in enforced lazy mode + mov ar.rsc=IA64_RSE_LAZY + ;; + // Read and save BSPSTORE and RNAT + add r19=TF_BSP,r18 + add r20=TF_RNAT,r18 + mov r21=ar.bspstore + mov r22=ar.rnat + ;; + st8 [r19]=r21 // store ar.bspstore + st8 [r20]=r22 // store ar.rnat + ;; + // Write new BSPSTORE + //mov r21=ar.bsp + //;; + mov r22=r21 // new bspstore equal to old + ;; + mov ar.bspstore=r22 // the new bspstore + ;; + // Read and save the new BSP for calculating number of dirty regs. + mov r21=ar.bsp + ;; + sub r21=r21,r22 // r21 -> ndirty + add r19=TF_NDIRTY-TF_BSP,r19 // TF_NDIRTY pos in r19 + ;; + st8 [r19]=r21 // store ndirty + ;; + mov ar.rsc=IA64_RSE_EAGER // RSE on again + ;; + add r19=TF_FPSR,r18 + ;; + mov r21=ar.fpsr + ;; + st8 [r19]=r21 // ar.fpsr + ;; + //// r16 return jump pointer, r18 - trap frame base, + // Load the gp with our module __gp + movl gp=__gp + ;; + add r16=16,r16 // for jump to next bundle + ;; + mov b7=r16 + ;; + +{ .mfb + srlz.d + nop 0 + br.sptk b7 + ;; +} + +END(save_tf_rse_switch) + + +/** + * The function reloads the processor context stored in + * save_tf_rse_switch(). + * + * On calling the function the bank 0 must be activ. + * The return is done through a rfi. + * Used register: b7, r16, r18, r19, r20, r21, r22 of bank 0 + * + * @param: r18 pointer to the exception frame + * + */ +ENTRY(restore_tf_rse_switch) + add r19=TF_IPSR,r18 + add r20=TF_IIP,r18 + ;; + ld8 r21=[r19] // load cr.ipsr + ld8 r22=[r20] // load cr.iip +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + ;; + mux1 r21=r21,@rev + mux1 r22=r22,@rev + ;; +#endif + movl r16=XSI_IPSR // XEN !! + ;; + st8 [r16]=r21,XSI_IIP_OFS-XSI_IPSR_OFS // XEN.ipsr + mov r2=r21 // save for fp stuff below + ;; + st8 [r16]=r22 // XEN.iip + ;; + //// r18 - trap frame base + // Allocate a zero sized frame + alloc r30=ar.pfs,0,0,0,0 // discard current frame + ;; + // calc number of dirty regs and put this into rsc.loardrs + add r19=TF_NDIRTY,r18 + ;; + ld8 r22=[r19] // ndirty + ;; + shl r21=r22,16 // value for ar.rsc + //mov r19=(MOS_IA64_RSC_BE << IA64_RSC_BE) + ;; + or r21=(MOS_IA64_RSC_BE << IA64_RSC_BE),r21 + ;; + mov ar.rsc=r21 // setup for loadrs + ;; + // Issue a loadrs instruction +{ .mmi + loadrs // must be the first instruction + ;; + nop 0x0 + nop 0x0 +} + // Restore BSPSTORE from interrupted context + add r19=TF_BSP,r18 + add r20=TF_RNAT,r18 + ;; + ld8 r21=[r19] // load ar.bspstore + ld8 r22=[r20] // load ar.rnat + ;; + mov ar.bspstore=r21 // set ar.bspstore + ;; + // Restore RNAT + mov ar.rnat=r22 // set ar.rnat + ;; + // Restore PFS and IFS + add r19=TF_PFS,r18 + add r20=TF_IFS,r18 + movl r16=XSI_IFS // XEN !! + ;; + ld8 r21=[r19] // load ar.pfs + ld8 r22=[r20] // load cr.ifs + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r22=r22,@rev + ;; +#endif + add r19=TF_RSC,r18 + mov ar.pfs=r21 + st8 [r16]=r22 // XEN.ifs + ;; + // Restore RSC + ld8 r21=[r19] // load ar.rsc + ;; + mov ar.rsc=r21 // set ar.rsc + //// r18 - trap frame base + add r19=TF_GP,r18 + add r20=TF_SP,r18 + ;; + ld8 gp=[r19],TF_TP-TF_GP // load gp + ld8 sp=[r20],TF_PR-TF_SP // load sp + ;; + ld8 r13=[r19] // load tp + ld8 r21=[r20] // load pr + ;; + mov pr=r21,-1 // set pr + ;; + add r19=TF_BREG0,r18 + add r20=TF_BREG6,r18 + ;; + ld8 r21=[r19],TF_BREG7-TF_BREG0 // load b0 + ld8 r22=[r20],16 // load b6 + ;; + mov b0=r21 + mov b6=r22 + ;; + ld8 r21=[r19] // load b7 + ld8 r22=[r20],16 // load b3 + ;; + mov b7=r21 + //// r18 - trap frame base + mov r14=r18 // Save the context pointer + ;; + // bsw.1 + movl r30=XSI_BANKNUM // Switch to bank 1. + mov r31=1;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r31=r31,@rev + ;; +#endif + st4 [r30]=r31 + ;; + add r2=TF_GREG16,r14 + add r3=TF_GREG17,r14 + ;; + FILL_REG_PAIR(r16,r17,r2,r3) + FILL_REG_PAIR(r18,r19,r2,r3) + FILL_REG_PAIR(r20,r21,r2,r3) + FILL_REG_PAIR(r22,r23,r2,r3) + FILL_REG_PAIR(r24,r25,r2,r3) + FILL_REG_PAIR(r26,r27,r2,r3) + FILL_REG_PAIR(r28,r29,r2,r3) + FILL_REG_PAIR(r30,r31,r2,r3) + + /* + * On XEN I have to store the bank 1 register into the + * global XSI_... area. + */ + // r16-r31 all now hold bank1 values + movl r2=XSI_BANK1_R16 + movl r3=XSI_BANK1_R16+8 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r16=r16,@rev; mux1 r17=r17,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r16,16 + .mem.offset 8,0; st8.spill [r3]=r17,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r18=r18,@rev; mux1 r19=r19,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r18,16 + .mem.offset 8,0; st8.spill [r3]=r19,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r20=r20,@rev; mux1 r21=r21,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r20,16 + .mem.offset 8,0; st8.spill [r3]=r21,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r22=r22,@rev; mux1 r23=r23,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r22,16 + .mem.offset 8,0; st8.spill [r3]=r23,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r24=r24,@rev; mux1 r25=r25,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r24,16 + .mem.offset 8,0; st8.spill [r3]=r25,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r26=r26,@rev; mux1 r27=r27,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r26,16 + .mem.offset 8,0; st8.spill [r3]=r27,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r28=r28,@rev; mux1 r29=r29,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r28,16 + .mem.offset 8,0; st8.spill [r3]=r29,16 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r30=r30,@rev; mux1 r31=r31,@rev;; +#endif + .mem.offset 0,0; st8.spill [r2]=r30,16 + .mem.offset 8,0; st8.spill [r3]=r31,16 + ;; + // bsw.0 + movl r2=XSI_BANKNUM;; + st4 [r2]=r0; + + mov r18=r14 // Move back the context pointer + ;; + add r19=TF_GREG2,r18 + add r20=TF_GREG3,r18 + ;; + FILL_REG_PAIR( r2, r3,r19,r20) + FILL_REG_PAIR( r8, r9,r19,r20) + FILL_REG_PAIR(r10,r11,r19,r20) + FILL_REG_PAIR(r14,r15,r19,r20) + + //// r18 - trap frame base, + + add r19=TF_CCV,r18 + add r20=TF_CSD,r18 + ;; + ld8 r21=[r19] // ar.ccv + ld8 r22=[r20] // ar.csd + ;; + mov ar.ccv=r21 + mov ar.csd=r22 + add r19=TF_SSD,r18 + ;; + ld8 r21=[r19] // ar.ssd + ;; + mov ar.ssd=r21 + add r19=TF_FREG6,r18 + add r20=TF_FREG7,r18 + ;; + FILL_FP_PAIR(f6, f7, r19, r20) + FILL_FP_PAIR(f8, f9, r19, r20) + FILL_FP_PAIR(f10, f11, r19, r20) + add r19=TF_FPSR,r18 + ;; + ld8 r21=[r19] // ar.fpsr + ;; + mov ar.fpsr=r21 + add r19=TF_UNAT,r18 + ;; + ld8 r21=[r19] + ;; + mov ar.unat=r21 + ;; + srlz.i + ;; + //rfi + XEN_HYPER_RFI; + ;; +END(restore_tf_rse_switch) + + +ENTRY(save_special_regs) + alloc loc0=ar.pfs,1,7,0,0 + movl loc1=XSI_IFA // XEN !! + movl loc2=XSI_ISR // XEN !! + ;; + ld8 loc3=[loc1],XSI_IIM_OFS-XSI_IFA_OFS // load XEN.ifa + ld8 loc4=[loc2],XSI_IIPA_OFS-XSI_ISR_OFS // load XEN.isr + add loc5=TF_IFA,in0 + add loc6=TF_ISR,in0 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 loc3=loc3,@rev; mux1 loc4=loc4,@rev;; +#endif + st8 [loc5]=loc3,TF_IIM-TF_IFA // store cr.ifa + st8 [loc6]=loc4 // store cr.isr + ;; + ld8 loc3=[loc1] // load XEN.iim + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 loc3=loc3,@rev;; +#endif + st8 [loc5]=loc3 // store cr.iim + ;; + mov ar.pfs=loc0 + ;; + br.ret.sptk.few rp +END(save_special_regs) + + +ENTRY(hypervisor_callback) + /* + * Use the thread stack here for storing the trap frame. + * It's not wired mapped, so nested data tlb faults may occur! + */ + add r18=-TF_SIZE,sp + ;; +{ .mib + nop 0x02 + mov r16=ip // for jump back from save_tf_rse_switch + br.sptk save_tf_rse_switch + ;; +} + add sp=-16,r18 // the new stack + alloc r15=ar.pfs,0,0,1,0 // 1 out for do_hypervisor_callback + ;; + mov out0=r18 // the trap frame + movl r22=XSI_PSR_IC + mov r23=1;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r23=r23,@rev;; +#endif + st8 [r22]=r23 // ssm psr.ic + ;; + br.call.sptk.few rp = do_hypervisor_callback + + movl r22=XSI_PSR_IC + ;; + st4 [r22]=r0 // rsm psr.ic + add r18=16,sp // load EF-pointer again + ;; + // must have r18-efp, calls rfi at the end. + br.sptk restore_tf_rse_switch + ;; +END(hypervisor_callback) + + /* + * In: r30 - trap number + */ +ENTRY(trap_error) + // Calculate the stack address for storing. + add r18=-TF_SIZE,sp + ;; + add r20=TF_TRAP_NUM,r18 + ;; + st2 [r20]=r30 // save trap number + ;; + +{ .mib + nop 0x02 + mov r16=ip // for jumping back from save_tf_rse_switch + // Used register: r16, r18, r19, r20, r21, r22 of bank 0 + br.sptk save_tf_rse_switch + ;; +} + + alloc r15=ar.pfs,0,0,1,0 // 1 out for do_trap_error + ;; + mov out0=r18 // the trap frame + add sp=-16,r18 // C-call abi + ;; + movl r30=XSI_BANKNUM // bsw.1 + mov r31=1;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r31=r31,@rev;; +#endif + st4 [r30]=r31;; + + /* Save extra interrupt registers to the trap frame. */ + br.call.sptk.few rp = save_special_regs + ;; + + movl r22=XSI_PSR_IC + movl r23=XSI_PSR_I_ADDR + ;; + ld8 r23=[r23] + mov r25=1 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r25=r25,@rev; mux1 r23=r23,@rev;; +#endif + st4 [r22]=r25 // ssm psr.ic + st1 [r23]=r0 // ssm psr.i + ;; + + br.call.sptk.few rp = do_trap_error + ;; + // --> currently not reached!!! + movl r23=XSI_PSR_I_ADDR + movl r22=XSI_PSR_IC + ;; + ld8 r23=[r23] + mov r25=1 + ;; +#if defined(BIG_ENDIAN) // swap because mini-os is in BE + mux1 r25=r25,@rev;; + mux1 r25=r25,@rev; mux1 r23=r23,@rev;; +#endif + st1 [r23]=r25 + st4 [r22]=r0 // note: clears both vpsr.i and vpsr.ic! + ;; + bsw.0 + ;; + add r18=16,sp // load EF-pointer again + ;; + mov sp=r18 + // must have r18-efp, calls rfi at the end. + br.sptk restore_tf_rse_switch + ;; +END(trap_error) + + +/* + * The trap handler stuff. + */ + +#define TRAP_ERR(num) \ + mov r30 = num; \ + ;; ; \ + br.sptk trap_error \ + ;; + +#define IVT_ENTRY(name, offset) \ + .org ia64_trap_table + offset; \ + .global hivt_##name; \ + .proc hivt_##name; \ + .prologue; \ + .body; \ +hivt_##name: + +#define IVT_END(name) \ + .endp hivt_##name; \ + .align 0x100 + +#define IVT_ERR(name, num, offset) \ + IVT_ENTRY(name, offset); \ + TRAP_ERR(num); \ + IVT_END(name) +/* + * The IA64 Interrupt Vector Table (IVT) contains 20 slots with 64 + * bundles per vector and 48 slots with 16 bundles per vector. + */ + + .section .text.hivt,"ax" + .align 32768 + .global ia64_trap_table + .size ia64_trap_table, 32768 +ia64_trap_table: + +IVT_ERR(VHPT_Translation, 0, 0x0) +IVT_ERR(Instruction_TLB, 1, 0x0400) +IVT_ERR(Data_TLB, 2, 0x0800) +IVT_ERR(Alternate_Instruction_TLB, 3, 0x0c00) + + +IVT_ENTRY(Alternate_Data_TLB, 0x1000) + mov r30=4 // trap number +adt_common: + mov r16=cr.ifa // where did it happen + mov r31=pr // save predicates + ;; + extr.u r17=r16,IA64_RR_IDX_POS,3 // get region number + ;; + cmp.eq p14,p15=7,r17 + ;; +//(p14) br.sptk adt_regf_addr // Check for region 7 - phys addresses +// ;; +// br.sptk trap_error +// // No return +// +//adt_regf_addr: +// extr.u r17=r16,60,4 // get region number +// ;; +// cmp.eq p14,p15=0xf,r17 +// ;; +(p14) br.sptk adt_reg7_addr // Check for region 7 - phys addresses + ;; + br.sptk trap_error + +adt_reg7_addr: + /* + * region 7 addresses are only directly mapped physically + * addresses. Currently I don't do a check. + */ + movl r20=~((7 << IA64_RR_IDX_POS) | 0xfff) + movl r18=((PTE_PS_16K< + * + * Description: Special ia64 memory management. + * Parts are taken from FreeBSD. + * + **************************************************************************** + * + * 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 "os.h" +#include "mm.h" + + +#define MAX_MEM_AREA 5 +paddr_t phys_avail[MAX_MEM_AREA * 2]; +int phys_avail_cnt; +uint64_t physmem; + +/* + * These variables are defined in the linker script minios_ia64.lds + * to get the size of the kernel. + */ +extern uint64_t _text[], _etext[], _end[], kstack[], phys_start[]; + +uint64_t kernstart, kernend, kernsize, kernpstart, kernpend; + +#ifdef HAVE_LIBC +uint8_t _heap[512 * 1024]; +unsigned long heap = (unsigned long)_heap, + brk = (unsigned long)_heap, + heap_mapped = (unsigned long)_heap + sizeof(_heap), + heap_end = (unsigned long)_heap + sizeof(_heap); +#endif + +/* Print the available memory chunks. */ +static void +print_phys_avail(void) +{ + int i; + + printk("Physical memory chunk(s):\n"); + for (i = 0; phys_avail[i + 1] != 0; i += 2) { + int size = phys_avail[i + 1] - phys_avail[i]; + printk("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", + phys_avail[i], phys_avail[i + 1] - 1, + size, size / PAGE_SIZE); + } +} + +void +arch_init_mm(unsigned long* start_pfn_p, unsigned long* max_pfn_p) +{ + uint64_t ms, me; + int i, j; + uint64_t m, n; + + kernstart = trunc_page(_text); + kernend = roundup_page(_end); + + kernpstart = trunc_page(ia64_tpa(kernstart)); + kernpend = roundup_page(kernpstart + (kernend - kernstart)); + kernsize = kernpend - kernpstart; + + ms = roundup_page(machineFwG.mach_mem_start); + me = trunc_page(machineFwG.mach_mem_start+machineFwG.mach_mem_size); + memset((void*)phys_avail, 0, sizeof(phys_avail)); + /* 1. Check where the kernel lies in physical memory. */ + physmem = me - ms; + if ((ms <= kernpend) && (kernpstart <= me)) { + if (ms < kernpstart) { /* There is a part before the kernel. */ + PRINT_BV(" Found chunk before kernel: 0x%lx - 0x%lx\n", + ms, kernpstart); + phys_avail[phys_avail_cnt] = ms; + phys_avail[phys_avail_cnt+1] = kernpstart; + phys_avail_cnt += 2; + } + if (kernpend < me) { /* There is a part behind the kernel. */ + PRINT_BV(" Found chunk behind kernel: 0x%lx - 0x%lx\n", + kernpend, me); + phys_avail[phys_avail_cnt] = kernpend; + phys_avail[phys_avail_cnt+1] = me; + phys_avail_cnt += 2; + } + } else { /* One big chunk */ + PRINT_BV(" Found big chunk: 0x%lx - 0x%lx\n", ms, me); + phys_avail[phys_avail_cnt] = ms; + phys_avail[phys_avail_cnt + 1] = me; + phys_avail_cnt += 2; + } + phys_avail[phys_avail_cnt] = 0; + + print_phys_avail(); + /* + * In this first version I only look for the biggest mem area. + */ + for (i = j = m = n = 0; i < phys_avail_cnt; i += 2) { + n = page_to_pfn(phys_avail[i + 1]) - page_to_pfn(phys_avail[i]); + if (n > m) { + m = n; + j = i; + } + } + *start_pfn_p = page_to_pfn(phys_avail[j]); + *max_pfn_p = page_to_pfn(phys_avail[j +1 ]); +} + +/* Currently only a dummy function. */ +void +arch_init_demand_mapping_area(unsigned long max_pfn) +{ + max_pfn = max_pfn; +} + +unsigned long allocate_ondemand(unsigned long n, unsigned long alignment) +{ + return 0; +} + +/* Helper function used in gnttab.c. */ +void do_map_frames(unsigned long addr, + unsigned long *f, unsigned long n, unsigned long stride, + unsigned long increment, domid_t id, int may_fail, unsigned long prot) +{ + /* TODO */ + ASSERT(0); +} + +void* +map_frames_ex(unsigned long* frames, unsigned long n, unsigned long stride, + unsigned long increment, unsigned long alignment, domid_t id, + int may_fail, unsigned long prot) +{ + /* TODO: incomplete! */ + ASSERT(n == 1 || (stride == 0 && increment == 1)); + ASSERT(id == DOMID_SELF); + ASSERT(prot == 0); + return (void*) __va(SWAP(frames[0]) << PAGE_SHIFT); +} + +void arch_init_p2m(unsigned long max_pfn) +{ + printk("Warn: p2m map not implemented.\n"); +} diff --git a/extras/mini-os/arch/ia64/sal.c b/extras/mini-os/arch/ia64/sal.c new file mode 100644 index 0000000..d087053 --- /dev/null +++ b/extras/mini-os/arch/ia64/sal.c @@ -0,0 +1,103 @@ +/* + * Done by Dietmar Hahn + * Mostly taken from FreeBSD. + * + **************************************************************************** + * + * 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 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 THE 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 "os.h" +#include "lib.h" +#include "console.h" +#include "page.h" + + +static struct ia64_fdesc sal_fdesc; +uint64_t ia64_pal_entry; /* PAL_PROC entrypoint */ + + +struct ia64_sal_result +ia64_sal_call(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, + uint64_t a5, uint64_t a6, uint64_t a7, uint64_t a8) +{ + return ia64_sal_entry(a1, a2, a3, a4, a5, a6, a7, a8); +} + +static struct ia64_sal_result +fake_sal(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, + uint64_t a5, uint64_t a6, uint64_t a7, uint64_t a8) +{ + struct ia64_sal_result res; + res.sal_status = -3; + res.sal_result[0] = 0; + res.sal_result[1] = 0; + res.sal_result[2] = 0; + return res; +} + +/* + * Currently only the SAL_DESC_ENTRYPOINT is checked to get + * the entry points the pal and sal functions. + */ +void +ia64_sal_init(struct sal_system_table *saltab) +{ + static int sizes[6] = { 48, 32, 16, 32, 16, 16 }; + uint8_t *p; + int i; + + PRINT_BV("Reading SALtable:\n"); + ia64_sal_entry = fake_sal; + + if (memcmp((void*)(uint64_t)(saltab->sal_signature), SAL_SIGNATURE, 4)) + { + printk("Bad signature for SAL System Table\n"); + return; + } + p = (uint8_t *) (saltab + 1); + for (i = 0; i < SWAP(saltab->sal_entry_count); i++) { + switch (SWAP(*p)) { + case SAL_DESC_ENTRYPOINT: // 0 + { + struct sal_entrypoint_descriptor *dp; + + dp = (struct sal_entrypoint_descriptor*)p; + ia64_pal_entry = + IA64_PHYS_TO_RR7(SWAP(dp->sale_pal_proc)); + PRINT_BV(" PAL Proc at 0x%lx\n", ia64_pal_entry); + sal_fdesc.func = + IA64_PHYS_TO_RR7(SWAP(dp->sale_sal_proc)); + sal_fdesc.gp = IA64_PHYS_TO_RR7(SWAP(dp->sale_sal_gp)); + PRINT_BV(" SAL Proc at 0x%lx, GP at 0x%lx\n", + sal_fdesc.func, sal_fdesc.gp); + ia64_sal_entry = (sal_entry_t *) &sal_fdesc; + break; + } + default: + break; + } + p += sizes[*p]; + } +} + diff --git a/extras/mini-os/arch/ia64/sched.c b/extras/mini-os/arch/ia64/sched.c new file mode 100644 index 0000000..5eea71e --- /dev/null +++ b/extras/mini-os/arch/ia64/sched.c @@ -0,0 +1,79 @@ +/* + * Done by Dietmar Hahn stack = (char *)alloc_pages(STACK_SIZE_PAGE_ORDER); + _thread->name = name; + memset((void*)&(_thread->regs), 0, sizeof(_thread->regs)); + _thread->regs.sp = ((uint64_t)_thread->stack) + STACK_SIZE - 16; + _thread->regs.bsp = ((uint64_t)_thread->stack) + 0x10; + _thread->regs.rp = FDESC_FUNC(thread_starter); + _thread->regs.pfs = 0x82; + _thread->regs.r4 = FDESC_FUNC(function); + _thread->regs.r6 = (uint64_t)data; + return _thread; +} + +extern void restore_context(struct thread*); +extern int switch_context(struct thread*, struct thread*); + +void +arch_switch_threads(struct thread* prev, struct thread* next) +{ + ia64_set_r13((uint64_t)next); + switch_context(prev, next); +} + +/* Everything initialised, start idle thread */ +void +run_idle_thread(void) +{ + //do_busy_loop(); + ia64_set_r13((uint64_t)idle_thread); + restore_context(idle_thread); + printk("%s: restore_context() returned - bad!\n", __func__); +} diff --git a/extras/mini-os/arch/ia64/time.c b/extras/mini-os/arch/ia64/time.c new file mode 100644 index 0000000..baf9096 --- /dev/null +++ b/extras/mini-os/arch/ia64/time.c @@ -0,0 +1,288 @@ +/* + * Done by Dietmar Hahn + * Description: simple ia64 specific time handling + * mktime() is taken from Linux (see copyright below) + * Parts are taken from FreeBSD. + * + **************************************************************************** + * For the copy of the mktime() from linux. + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **************************************************************************** + * + * 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 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 THE 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 "os.h" +#include "console.h" +#include "time.h" +#include "efi.h" +#include "events.h" + +struct timespec os_time; +static uint64_t itc_alt; /* itc on last update. */ +static uint64_t itc_at_boot; /* itc on boot */ +static uint64_t itc_frequency; +static uint64_t processor_frequency; +static uint64_t itm_val; + + +/* + * mktime() is take from Linux. See copyright above. + * Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static unsigned long +_mktime(const unsigned int year0, const unsigned int mon0, + const unsigned int day, const unsigned int hour, + const unsigned int min, const unsigned int sec) +{ + unsigned int mon = mon0, year = year0; + + /* 1..12 -> 11,12,1..10 */ + if (0 >= (int) (mon -= 2)) { + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + + return ( + ( + ((unsigned long) + (year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + ) * 24 + hour /* now have hours */ + ) * 60 + min /* now have minutes */ + ) * 60 + sec; /* finally seconds */ +} + +static inline uint64_t +ns_from_cycles(uint64_t cycles) +{ + return (cycles * (1000000000 / itc_frequency)); +} + +static inline uint64_t +ns_to_cycles(uint64_t ns) +{ + return (ns * (itc_frequency / 1000000000)); +} + +/* + * Block the domain until until(nanoseconds) is over. + * If block is called no timerinterrupts are delivered from xen! + */ +void +block_domain(s_time_t until) +{ + struct ia64_pal_result pal_res; + uint64_t c, new; + + c = ns_to_cycles(until); + new = ia64_get_itc() + c - NOW(); + ia64_set_itm(new); /* Reload cr.itm */ + /* + * PAL_HALT_LIGHT returns on every external interrupt, + * including timer interrupts. + */ + pal_res = ia64_call_pal_static(PAL_HALT_LIGHT, 0, 0, 0); + if (pal_res.pal_status != 0) + printk("%s: PAL_HALT_LIGHT returns an error\n"); + /* Reload the normal timer interrupt match. */ + new = ia64_get_itc() + itm_val; + ia64_set_itm(new); +} + +static void +calculate_time(void) +{ + uint64_t itc_new, new; + + itc_new = ia64_get_itc(); + if (itc_new < itc_alt) + new = ~0 - itc_alt + itc_new; + else + new = itc_new - itc_alt; + itc_alt = itc_new; + new = ns_from_cycles(new); + os_time.tv_nsec += new; + if (os_time.tv_nsec > 1000000000) { /* On overflow. */ + os_time.tv_sec++; + os_time.tv_nsec -= 1000000000; + } +} + +void +timer_interrupt(evtchn_port_t port, struct pt_regs* regsP, void *data) +{ + uint64_t new; + + calculate_time(); + new = ia64_get_itc() + itm_val; + ia64_set_itm(new); +} + +/* + * monotonic_clock(): returns # of nanoseconds passed since time_init() + */ +u64 +monotonic_clock(void) +{ + uint64_t delta; + + delta = ia64_get_itc() - itc_at_boot; + delta = ns_from_cycles(delta); + return delta; +} + +int +gettimeofday(struct timeval *tv, void *tz) +{ + calculate_time(); + tv->tv_sec = os_time.tv_sec; /* seconds */ + tv->tv_usec = NSEC_TO_USEC(os_time.tv_nsec); /* microseconds */ + return 0; +}; + +/* + * Read the clock frequencies from pal and sal for calculating + * the clock interrupt. + */ +static void +calculate_frequencies(void) +{ + struct ia64_sal_result sal_res; + struct ia64_pal_result pal_res; + + pal_res = ia64_call_pal_static(PAL_FREQ_RATIOS, 0, 0, 0); + //sal_res = ia64_sal_call(SAL_FREQ_BASE, 0, 0, 0, 0, 0, 0, 0); +#if defined(BIG_ENDIAN) +//#warning calculate_frequencies TODO + /* + * I have to do an own function with switching psr.be! + * Currently it's running because it's a break into the hypervisor + * behind the call.! + */ +#endif + sal_res = ia64_sal_entry(SAL_FREQ_BASE, 0, 0, 0, 0, 0, 0, 0); + + if (sal_res.sal_status == 0 && pal_res.pal_status == 0) { + processor_frequency = + sal_res.sal_result[0] * (pal_res.pal_result[0] >> 32) + / (pal_res.pal_result[0] & ((1L << 32) - 1)); + itc_frequency = + sal_res.sal_result[0] * (pal_res.pal_result[2] >> 32) + / (pal_res.pal_result[2] & ((1L << 32) - 1)); + PRINT_BV("Reading clock frequencies:\n"); + PRINT_BV(" Platform clock frequency %ld Hz\n", + sal_res.sal_result[0]); + PRINT_BV(" Processor ratio %ld/%ld, Bus ratio %ld/%ld, " + " ITC ratio %ld/%ld\n", + pal_res.pal_result[0] >> 32, + pal_res.pal_result[0] & ((1L << 32) - 1), + pal_res.pal_result[1] >> 32, + pal_res.pal_result[1] & ((1L << 32) - 1), + pal_res.pal_result[2] >> 32, + pal_res.pal_result[2] & ((1L << 32) - 1)); + + printk(" ITC frequency %ld\n", itc_frequency); + } else { + itc_frequency = 1000000000; + processor_frequency = 0; + printk("Reading clock frequencies failed!!! Using: %ld\n", + itc_frequency); + } +} + + +//#define HZ 1 +#define HZ 1000 // 1000 clock ticks per sec +#define IA64_TIMER_VECTOR 0xef + +void +init_time(void) +{ + uint64_t new; + efi_time_t tm; + evtchn_port_t port = 0; + + printk("Initialising time\n"); + calculate_frequencies(); + + itm_val = (itc_frequency + HZ/2) / HZ; + printk(" itm_val: %ld\n", itm_val); + + os_time.tv_sec = 0; + os_time.tv_nsec = 0; + + if (efi_get_time(&tm)) { + printk(" EFI-Time: %d.%d.%d %d:%d:%d\n", tm.Day, + tm.Month, tm.Year, tm.Hour, tm.Minute, tm.Second); + os_time.tv_sec = _mktime(SWAP(tm.Year), SWAP(tm.Month), + SWAP(tm.Day), SWAP(tm.Hour), + SWAP(tm.Minute), SWAP(tm.Second)); + os_time.tv_nsec = tm.Nanosecond; + } else + printk("efi_get_time() failed\n"); + + port = bind_virq(VIRQ_ITC, timer_interrupt, NULL); + if (port == -1) { + printk("XEN timer request chn bind failed %i\n", port); + return; + } + unmask_evtchn(port); + itc_alt = ia64_get_itc(); + itc_at_boot = itc_alt; + new = ia64_get_itc() + itm_val; + ia64_set_itv(IA64_TIMER_VECTOR); + ia64_set_itm(new); + ia64_srlz_d(); +} + +void +fini_time(void) +{ + /* TODO */ +} diff --git a/extras/mini-os/arch/ia64/xencomm.c b/extras/mini-os/arch/ia64/xencomm.c new file mode 100644 index 0000000..03d163c --- /dev/null +++ b/extras/mini-os/arch/ia64/xencomm.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2006 Hollis Blanchard , IBM Corporation + * Tristan Gingold + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This code is mostly taken from ia64-xen files xcom_mini.c and xencomm.c. + * Changes: Dietmar Hahn +#include +#include +#include + + +#define XENCOMM_MINI_ADDRS 3 +struct xencomm_mini +{ + struct xencomm_desc _desc; + uint64_t address[XENCOMM_MINI_ADDRS]; +}; + +#define xen_guest_handle(hnd) ((hnd).p) + + +/* Translate virtual address to physical address. */ +uint64_t +xencomm_vaddr_to_paddr(uint64_t vaddr) +{ + if (IA64_RR_EXTR(vaddr) == 5) + return KERN_VIRT_2_PHYS(vaddr); + + if (IA64_RR_EXTR(vaddr) == 7) + return __pa(vaddr); + + return 0; +} + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +static int +xencomm_init_desc(struct xencomm_desc *desc, void *buffer, unsigned long bytes) +{ + unsigned long recorded = 0; + int i = 0; + + if ((buffer == NULL) && (bytes > 0)) + BUG(); + + /* record the physical pages used */ + if (buffer == NULL) + desc->nr_addrs = 0; + + while ((recorded < bytes) && (i < desc->nr_addrs)) { + unsigned long vaddr = (unsigned long)buffer + recorded; + unsigned long paddr; + int offset; + int chunksz; + + offset = vaddr % PAGE_SIZE; /* handle partial pages */ + chunksz = min(PAGE_SIZE - offset, bytes - recorded); + + paddr = xencomm_vaddr_to_paddr(vaddr); + if (paddr == ~0UL) { + printk("%s: couldn't translate vaddr %lx\n", + __func__, vaddr); + return -EINVAL; + } + + desc->address[i++] = SWAP(paddr); + recorded += chunksz; + } + if (recorded < bytes) { + printk("%s: could only translate %ld of %ld bytes\n", + __func__, recorded, bytes); + return -ENOSPC; + } + + /* mark remaining addresses invalid (just for safety) */ + while (i < desc->nr_addrs) + desc->address[i++] = SWAP(XENCOMM_INVALID); + desc->magic = SWAP(XENCOMM_MAGIC); + return 0; +} + +static void * +xencomm_alloc_mini(struct xencomm_mini *area, int *nbr_area) +{ + unsigned long base; + unsigned int pageoffset; + + while (*nbr_area >= 0) { + /* Allocate an area. */ + (*nbr_area)--; + + base = (unsigned long)(area + *nbr_area); + pageoffset = base % PAGE_SIZE; + + /* If the area does not cross a page, use it. */ + if ((PAGE_SIZE - pageoffset) >= sizeof(struct xencomm_mini)) + return &area[*nbr_area]; + } + /* No more area. */ + return NULL; +} + +int +xencomm_create_mini(struct xencomm_mini *area, int *nbr_area, + void *buffer, unsigned long bytes, + struct xencomm_handle **ret) +{ + struct xencomm_desc *desc; + int rc; + unsigned long res; + + desc = xencomm_alloc_mini(area, nbr_area); + if (!desc) + return -ENOMEM; + desc->nr_addrs = XENCOMM_MINI_ADDRS; + + rc = xencomm_init_desc(desc, buffer, bytes); + if (rc) + return rc; + + res = xencomm_vaddr_to_paddr((unsigned long)desc); + if (res == ~0UL) + return -EINVAL; + + *ret = (struct xencomm_handle*)res; + return 0; +} + +static int +xencommize_mini_grant_table_op(struct xencomm_mini *xc_area, int *nbr_area, + unsigned int cmd, void *op, unsigned int count, + struct xencomm_handle **desc) +{ + struct xencomm_handle *desc1; + unsigned int argsize=0; + int rc; + + switch (cmd) { + case GNTTABOP_map_grant_ref: + argsize = sizeof(struct gnttab_map_grant_ref); + break; + case GNTTABOP_unmap_grant_ref: + argsize = sizeof(struct gnttab_unmap_grant_ref); + break; + case GNTTABOP_setup_table: + { + struct gnttab_setup_table *setup = op; + + argsize = sizeof(*setup); + + if (count != 1) + return -EINVAL; + rc = xencomm_create_mini + (xc_area, nbr_area, + (void*)SWAP((uint64_t) + xen_guest_handle(setup->frame_list)), + SWAP(setup->nr_frames) + * sizeof(*xen_guest_handle(setup->frame_list)), + &desc1); + if (rc) + return rc; + set_xen_guest_handle(setup->frame_list, + (void *)SWAP((uint64_t)desc1)); + break; + } + case GNTTABOP_dump_table: + argsize = sizeof(struct gnttab_dump_table); + break; + case GNTTABOP_transfer: + argsize = sizeof(struct gnttab_transfer); + break; + case GNTTABOP_copy: + argsize = sizeof(struct gnttab_copy); + break; + default: + printk("%s: unknown mini grant table op %d\n", __func__, cmd); + BUG(); + } + + rc = xencomm_create_mini(xc_area, nbr_area, op, count * argsize, desc); + + return rc; +} + +int +xencomm_mini_hypercall_grant_table_op(unsigned int cmd, void *op, + unsigned int count) +{ + int rc; + struct xencomm_handle *desc; + int nbr_area = 2; + struct xencomm_mini xc_area[2]; + + rc = xencommize_mini_grant_table_op(xc_area, &nbr_area, + cmd, op, count, &desc); + if (rc) + return rc; + return xencomm_arch_hypercall_grant_table_op(cmd, desc, count); +} + +static void +gnttab_map_grant_ref_pre(struct gnttab_map_grant_ref *uop) +{ + uint32_t flags; + + flags = uop->flags; + + if (flags & GNTMAP_host_map) { + if (flags & GNTMAP_application_map) { + printk("GNTMAP_application_map is not supported yet: " + "flags 0x%x\n", flags); + BUG(); + } + if (flags & GNTMAP_contains_pte) { + printk("GNTMAP_contains_pte is not supported yet flags " + "0x%x\n", flags); + BUG(); + } + } else if (flags & GNTMAP_device_map) { + printk("GNTMAP_device_map is not supported yet 0x%x\n", flags); + BUG();//XXX not yet. actually this flag is not used. + } else { + BUG(); + } +} + +int +HYPERVISOR_grant_table_op(unsigned int cmd, void *uop, unsigned int count) +{ + if (cmd == GNTTABOP_map_grant_ref) { + unsigned int i; + for (i = 0; i < count; i++) { + gnttab_map_grant_ref_pre( + (struct gnttab_map_grant_ref*)uop + i); + } + } + return xencomm_mini_hypercall_grant_table_op(cmd, uop, count); +} + + /* In fw.S */ +extern int xencomm_arch_hypercall_suspend(struct xencomm_handle *arg); +int +HYPERVISOR_suspend(unsigned long srec) +{ + struct sched_shutdown arg; + + arg.reason = (uint32_t)SWAP((uint32_t)SHUTDOWN_suspend); + + return xencomm_arch_hypercall_suspend(xencomm_create_inline(&arg)); +} + diff --git a/extras/mini-os/arch/x86/Makefile b/extras/mini-os/arch/x86/Makefile new file mode 100644 index 0000000..3c5fb6a --- /dev/null +++ b/extras/mini-os/arch/x86/Makefile @@ -0,0 +1,31 @@ +# +# x86 architecture specific makefiles. +# It's is used for x86_32, x86_32y and x86_64 +# + +XEN_ROOT = ../../../.. +include $(XEN_ROOT)/Config.mk +include ../../Config.mk + +# include arch.mk has to be before mini-os.mk! + +include arch.mk +include ../../minios.mk + +# Sources here are all *.c *.S without $(XEN_TARGET_ARCH).S +# This is handled in $(HEAD_ARCH_OBJ) +ARCH_SRCS := $(wildcard *.c) + +# The objects built from the sources. +ARCH_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(ARCH_SRCS)) + +all: $(OBJ_DIR)/$(ARCH_LIB) + +# $(HEAD_ARCH_OBJ) is only build here, needed on linking +# in ../../Makefile. +$(OBJ_DIR)/$(ARCH_LIB): $(ARCH_OBJS) $(OBJ_DIR)/$(HEAD_ARCH_OBJ) + $(AR) rv $(OBJ_DIR)/$(ARCH_LIB) $(ARCH_OBJS) + +clean: + rm -f $(OBJ_DIR)/$(ARCH_LIB) $(ARCH_OBJS) $(OBJ_DIR)/$(HEAD_ARCH_OBJ) + diff --git a/extras/mini-os/arch/x86/arch.mk b/extras/mini-os/arch/x86/arch.mk new file mode 100644 index 0000000..b27f322 --- /dev/null +++ b/extras/mini-os/arch/x86/arch.mk @@ -0,0 +1,22 @@ +# +# Architecture special makerules for x86 family +# (including x86_32, x86_32y and x86_64). +# + +ifeq ($(XEN_TARGET_ARCH),x86_32) +ARCH_CFLAGS := -m32 -march=i686 +ARCH_LDFLAGS := -m elf_i386 +ARCH_ASFLAGS := -m32 +EXTRA_INC += $(TARGET_ARCH_FAM)/$(XEN_TARGET_ARCH) +EXTRA_SRC += arch/$(EXTRA_INC) +endif + +ifeq ($(XEN_TARGET_ARCH),x86_64) +ARCH_CFLAGS := -m64 -mno-red-zone -fno-reorder-blocks +ARCH_CFLAGS += -fno-asynchronous-unwind-tables +ARCH_ASFLAGS := -m64 +ARCH_LDFLAGS := -m elf_x86_64 +EXTRA_INC += $(TARGET_ARCH_FAM)/$(XEN_TARGET_ARCH) +EXTRA_SRC += arch/$(EXTRA_INC) +endif + diff --git a/extras/mini-os/arch/x86/minios-x86_32.lds b/extras/mini-os/arch/x86/minios-x86_32.lds new file mode 100644 index 0000000..13796db --- /dev/null +++ b/extras/mini-os/arch/x86/minios-x86_32.lds @@ -0,0 +1,76 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0x0; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.gnu.warning) + } = 0x9090 + + _etext = .; /* End of text section */ + + .rodata : { *(.rodata) *(.rodata.*) } + . = ALIGN(4096); + _erodata = .; + + /* newlib initialization functions */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + + .ctors : { + __CTOR_LIST__ = .; + LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) + *(.ctors) + CONSTRUCTORS + LONG(0) + __CTOR_END__ = .; + } + + .dtors : { + __DTOR_LIST__ = .; + LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) + *(.dtors) + LONG(0) + __DTOR_END__ = .; + } + + .data : { /* Data */ + *(.data) + } + + _edata = .; /* End of data section */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + *(.app.bss) + } + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/extras/mini-os/arch/x86/minios-x86_64.lds b/extras/mini-os/arch/x86/minios-x86_64.lds new file mode 100644 index 0000000..6a5c0bb --- /dev/null +++ b/extras/mini-os/arch/x86/minios-x86_64.lds @@ -0,0 +1,76 @@ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SECTIONS +{ + . = 0x0; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.gnu.warning) + } = 0x9090 + + _etext = .; /* End of text section */ + + .rodata : { *(.rodata) *(.rodata.*) } + . = ALIGN(4096); + _erodata = .; + + /* newlib initialization functions */ + . = ALIGN(64 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + + .ctors : { + __CTOR_LIST__ = .; + QUAD((__CTOR_END__ - __CTOR_LIST__) / 8 - 2) + *(.ctors) + CONSTRUCTORS + QUAD(0) + __CTOR_END__ = .; + } + + .dtors : { + __DTOR_LIST__ = .; + QUAD((__DTOR_END__ - __DTOR_LIST__) / 8 - 2) + *(.dtors) + QUAD(0) + __DTOR_END__ = .; + } + + .data : { /* Data */ + *(.data) + } + + _edata = .; /* End of data section */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + *(.app.bss) + } + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/extras/mini-os/arch/x86/mm.c b/extras/mini-os/arch/x86/mm.c new file mode 100644 index 0000000..b1c0084 --- /dev/null +++ b/extras/mini-os/arch/x86/mm.c @@ -0,0 +1,617 @@ +/* + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: mm.c + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos + * + * Date: Aug 2003, chages Aug 2005 + * + * Environment: Xen Minimal OS + * Description: memory management related functions + * contains buddy page allocator from Xen. + * + **************************************************************************** + * 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 + +#ifdef MM_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=mm.c, line=%d) " _f "\n", __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + +unsigned long *phys_to_machine_mapping; +unsigned long mfn_zero; +extern char stack[]; +extern void page_walk(unsigned long virt_addr); + +void new_pt_frame(unsigned long *pt_pfn, unsigned long prev_l_mfn, + unsigned long offset, unsigned long level) +{ + pgentry_t *tab = (pgentry_t *)start_info.pt_base; + unsigned long pt_page = (unsigned long)pfn_to_virt(*pt_pfn); + pgentry_t prot_e, prot_t; + mmu_update_t mmu_updates[1]; + + prot_e = prot_t = 0; + DEBUG("Allocating new L%d pt frame for pt_pfn=%lx, " + "prev_l_mfn=%lx, offset=%lx", + level, *pt_pfn, prev_l_mfn, offset); + + /* We need to clear the page, otherwise we might fail to map it + as a page table page */ + memset((void*) pt_page, 0, PAGE_SIZE); + + switch ( level ) + { + case L1_FRAME: + prot_e = L1_PROT; + prot_t = L2_PROT; + break; + case L2_FRAME: + prot_e = L2_PROT; + prot_t = L3_PROT; + break; +#if defined(__x86_64__) + case L3_FRAME: + prot_e = L3_PROT; + prot_t = L4_PROT; + break; +#endif + default: + printk("new_pt_frame() called with invalid level number %d\n", level); + do_exit(); + break; + } + + /* Update the entry */ +#if defined(__x86_64__) + tab = pte_to_virt(tab[l4_table_offset(pt_page)]); +#endif + tab = pte_to_virt(tab[l3_table_offset(pt_page)]); + + mmu_updates[0].ptr = (tab[l2_table_offset(pt_page)] & PAGE_MASK) + + sizeof(pgentry_t) * l1_table_offset(pt_page); + mmu_updates[0].val = (pgentry_t)pfn_to_mfn(*pt_pfn) << PAGE_SHIFT | + (prot_e & ~_PAGE_RW); + if(HYPERVISOR_mmu_update(mmu_updates, 1, NULL, DOMID_SELF) < 0) + { + printk("PTE for new page table page could not be updated\n"); + do_exit(); + } + + /* Now fill the new page table page with entries. + Update the page directory as well. */ + mmu_updates[0].ptr = ((pgentry_t)prev_l_mfn << PAGE_SHIFT) + sizeof(pgentry_t) * offset; + mmu_updates[0].val = (pgentry_t)pfn_to_mfn(*pt_pfn) << PAGE_SHIFT | prot_t; + if(HYPERVISOR_mmu_update(mmu_updates, 1, NULL, DOMID_SELF) < 0) + { + printk("ERROR: mmu_update failed\n"); + do_exit(); + } + + *pt_pfn += 1; +} + +/* Checks if a pagetable frame is needed (if weren't allocated by Xen) */ +static int need_pt_frame(unsigned long virt_address, int level) +{ + unsigned long hyp_virt_start = HYPERVISOR_VIRT_START; +#if defined(__x86_64__) + unsigned long hyp_virt_end = HYPERVISOR_VIRT_END; +#else + unsigned long hyp_virt_end = 0xffffffff; +#endif + + /* In general frames will _not_ be needed if they were already + allocated to map the hypervisor into our VA space */ +#if defined(__x86_64__) + if(level == L3_FRAME) + { + if(l4_table_offset(virt_address) >= + l4_table_offset(hyp_virt_start) && + l4_table_offset(virt_address) <= + l4_table_offset(hyp_virt_end)) + return 0; + return 1; + } else +#endif + + if(level == L2_FRAME) + { +#if defined(__x86_64__) + if(l4_table_offset(virt_address) >= + l4_table_offset(hyp_virt_start) && + l4_table_offset(virt_address) <= + l4_table_offset(hyp_virt_end)) +#endif + if(l3_table_offset(virt_address) >= + l3_table_offset(hyp_virt_start) && + l3_table_offset(virt_address) <= + l3_table_offset(hyp_virt_end)) + return 0; + + return 1; + } else + + /* Always need l1 frames */ + if(level == L1_FRAME) + return 1; + + printk("ERROR: Unknown frame level %d, hypervisor %llx,%llx\n", + level, hyp_virt_start, hyp_virt_end); + return -1; +} + +void build_pagetable(unsigned long *start_pfn, unsigned long *max_pfn) +{ + unsigned long start_address, end_address; + unsigned long pfn_to_map, pt_pfn = *start_pfn; + static mmu_update_t mmu_updates[L1_PAGETABLE_ENTRIES + 1]; + pgentry_t *tab = (pgentry_t *)start_info.pt_base, page; + unsigned long mfn = pfn_to_mfn(virt_to_pfn(start_info.pt_base)); + unsigned long offset; + int count = 0; + + pfn_to_map = (start_info.nr_pt_frames - NOT_L1_FRAMES) * L1_PAGETABLE_ENTRIES; + + if (*max_pfn >= virt_to_pfn(HYPERVISOR_VIRT_START)) + { + printk("WARNING: Mini-OS trying to use Xen virtual space. " + "Truncating memory from %dMB to ", + ((unsigned long)pfn_to_virt(*max_pfn) - (unsigned long)&_text)>>20); + *max_pfn = virt_to_pfn(HYPERVISOR_VIRT_START - PAGE_SIZE); + printk("%dMB\n", + ((unsigned long)pfn_to_virt(*max_pfn) - (unsigned long)&_text)>>20); + } + + start_address = (unsigned long)pfn_to_virt(pfn_to_map); + end_address = (unsigned long)pfn_to_virt(*max_pfn); + + /* We worked out the virtual memory range to map, now mapping loop */ + printk("Mapping memory range 0x%lx - 0x%lx\n", start_address, end_address); + + while(start_address < end_address) + { + tab = (pgentry_t *)start_info.pt_base; + mfn = pfn_to_mfn(virt_to_pfn(start_info.pt_base)); + +#if defined(__x86_64__) + offset = l4_table_offset(start_address); + /* Need new L3 pt frame */ + if(!(start_address & L3_MASK)) + if(need_pt_frame(start_address, L3_FRAME)) + new_pt_frame(&pt_pfn, mfn, offset, L3_FRAME); + + page = tab[offset]; + mfn = pte_to_mfn(page); + tab = to_virt(mfn_to_pfn(mfn) << PAGE_SHIFT); +#endif + offset = l3_table_offset(start_address); + /* Need new L2 pt frame */ + if(!(start_address & L2_MASK)) + if(need_pt_frame(start_address, L2_FRAME)) + new_pt_frame(&pt_pfn, mfn, offset, L2_FRAME); + + page = tab[offset]; + mfn = pte_to_mfn(page); + tab = to_virt(mfn_to_pfn(mfn) << PAGE_SHIFT); + offset = l2_table_offset(start_address); + /* Need new L1 pt frame */ + if(!(start_address & L1_MASK)) + if(need_pt_frame(start_address, L1_FRAME)) + new_pt_frame(&pt_pfn, mfn, offset, L1_FRAME); + + page = tab[offset]; + mfn = pte_to_mfn(page); + offset = l1_table_offset(start_address); + + mmu_updates[count].ptr = ((pgentry_t)mfn << PAGE_SHIFT) + sizeof(pgentry_t) * offset; + mmu_updates[count].val = (pgentry_t)pfn_to_mfn(pfn_to_map++) << PAGE_SHIFT | L1_PROT; + count++; + if (count == L1_PAGETABLE_ENTRIES || pfn_to_map == *max_pfn) + { + if(HYPERVISOR_mmu_update(mmu_updates, count, NULL, DOMID_SELF) < 0) + { + printk("PTE could not be updated\n"); + do_exit(); + } + count = 0; + } + start_address += PAGE_SIZE; + } + + *start_pfn = pt_pfn; +} + +extern void shared_info; +static void set_readonly(void *text, void *etext) +{ + unsigned long start_address = ((unsigned long) text + PAGE_SIZE - 1) & PAGE_MASK; + unsigned long end_address = (unsigned long) etext; + static mmu_update_t mmu_updates[L1_PAGETABLE_ENTRIES + 1]; + pgentry_t *tab = (pgentry_t *)start_info.pt_base, page; + unsigned long mfn = pfn_to_mfn(virt_to_pfn(start_info.pt_base)); + unsigned long offset; + int count = 0; + + printk("setting %p-%p readonly\n", text, etext); + + while (start_address + PAGE_SIZE <= end_address) { + tab = (pgentry_t *)start_info.pt_base; + mfn = pfn_to_mfn(virt_to_pfn(start_info.pt_base)); + +#if defined(__x86_64__) + offset = l4_table_offset(start_address); + page = tab[offset]; + mfn = pte_to_mfn(page); + tab = to_virt(mfn_to_pfn(mfn) << PAGE_SHIFT); +#endif + offset = l3_table_offset(start_address); + page = tab[offset]; + mfn = pte_to_mfn(page); + tab = to_virt(mfn_to_pfn(mfn) << PAGE_SHIFT); + offset = l2_table_offset(start_address); + page = tab[offset]; + mfn = pte_to_mfn(page); + tab = to_virt(mfn_to_pfn(mfn) << PAGE_SHIFT); + + offset = l1_table_offset(start_address); + + if (start_address != (unsigned long)&shared_info) { + mmu_updates[count].ptr = ((pgentry_t)mfn << PAGE_SHIFT) + sizeof(pgentry_t) * offset; + mmu_updates[count].val = tab[offset] & ~_PAGE_RW; + count++; + } else + printk("skipped %p\n", start_address); + + start_address += PAGE_SIZE; + + if (count == L1_PAGETABLE_ENTRIES || start_address + PAGE_SIZE > end_address) + { + if(HYPERVISOR_mmu_update(mmu_updates, count, NULL, DOMID_SELF) < 0) + { + printk("PTE could not be updated\n"); + do_exit(); + } + count = 0; + } + } + + { + mmuext_op_t op = { + .cmd = MMUEXT_TLB_FLUSH_ALL, + }; + int count; + HYPERVISOR_mmuext_op(&op, 1, &count, DOMID_SELF); + } +} + +void mem_test(unsigned long *start_add, unsigned long *end_add) +{ + unsigned long mask = 0x10000; + unsigned long *pointer; + + for(pointer = start_add; pointer < end_add; pointer++) + { + if(!(((unsigned long)pointer) & 0xfffff)) + { + printk("Writing to %lx\n", pointer); + page_walk((unsigned long)pointer); + } + *pointer = (unsigned long)pointer & ~mask; + } + + for(pointer = start_add; pointer < end_add; pointer++) + { + if(((unsigned long)pointer & ~mask) != *pointer) + printk("Read error at 0x%lx. Read: 0x%lx, should read 0x%lx\n", + (unsigned long)pointer, + *pointer, + ((unsigned long)pointer & ~mask)); + } + +} + +static pgentry_t *get_pgt(unsigned long addr) +{ + unsigned long mfn; + pgentry_t *tab; + unsigned offset; + + tab = (pgentry_t *)start_info.pt_base; + mfn = virt_to_mfn(start_info.pt_base); + +#if defined(__x86_64__) + offset = l4_table_offset(addr); + if (!(tab[offset] & _PAGE_PRESENT)) + return NULL; + mfn = pte_to_mfn(tab[offset]); + tab = mfn_to_virt(mfn); +#endif + offset = l3_table_offset(addr); + if (!(tab[offset] & _PAGE_PRESENT)) + return NULL; + mfn = pte_to_mfn(tab[offset]); + tab = mfn_to_virt(mfn); + offset = l2_table_offset(addr); + if (!(tab[offset] & _PAGE_PRESENT)) + return NULL; + mfn = pte_to_mfn(tab[offset]); + tab = mfn_to_virt(mfn); + offset = l1_table_offset(addr); + return &tab[offset]; +} + +pgentry_t *need_pgt(unsigned long addr) +{ + unsigned long mfn; + pgentry_t *tab; + unsigned long pt_pfn; + unsigned offset; + + tab = (pgentry_t *)start_info.pt_base; + mfn = virt_to_mfn(start_info.pt_base); + +#if defined(__x86_64__) + offset = l4_table_offset(addr); + if (!(tab[offset] & _PAGE_PRESENT)) { + pt_pfn = virt_to_pfn(alloc_page()); + new_pt_frame(&pt_pfn, mfn, offset, L3_FRAME); + } + ASSERT(tab[offset] & _PAGE_PRESENT); + mfn = pte_to_mfn(tab[offset]); + tab = mfn_to_virt(mfn); +#endif + offset = l3_table_offset(addr); + if (!(tab[offset] & _PAGE_PRESENT)) { + pt_pfn = virt_to_pfn(alloc_page()); + new_pt_frame(&pt_pfn, mfn, offset, L2_FRAME); + } + ASSERT(tab[offset] & _PAGE_PRESENT); + mfn = pte_to_mfn(tab[offset]); + tab = mfn_to_virt(mfn); + offset = l2_table_offset(addr); + if (!(tab[offset] & _PAGE_PRESENT)) { + pt_pfn = virt_to_pfn(alloc_page()); + new_pt_frame(&pt_pfn, mfn, offset, L1_FRAME); + } + ASSERT(tab[offset] & _PAGE_PRESENT); + mfn = pte_to_mfn(tab[offset]); + tab = mfn_to_virt(mfn); + + offset = l1_table_offset(addr); + return &tab[offset]; +} + +static unsigned long demand_map_area_start; +#ifdef __x86_64__ +#define DEMAND_MAP_PAGES ((128ULL << 30) / PAGE_SIZE) +#else +#define DEMAND_MAP_PAGES ((2ULL << 30) / PAGE_SIZE) +#endif + +#ifdef HAVE_LIBC +unsigned long heap, brk, heap_mapped, heap_end; +#ifdef __x86_64__ +#define HEAP_PAGES ((128ULL << 30) / PAGE_SIZE) +#else +#define HEAP_PAGES ((1ULL << 30) / PAGE_SIZE) +#endif +#endif + +void arch_init_demand_mapping_area(unsigned long cur_pfn) +{ + cur_pfn++; + + demand_map_area_start = (unsigned long) pfn_to_virt(cur_pfn); + cur_pfn += DEMAND_MAP_PAGES; + printk("Demand map pfns at %lx-%lx.\n", demand_map_area_start, pfn_to_virt(cur_pfn)); + +#ifdef HAVE_LIBC + cur_pfn++; + heap_mapped = brk = heap = (unsigned long) pfn_to_virt(cur_pfn); + cur_pfn += HEAP_PAGES; + heap_end = (unsigned long) pfn_to_virt(cur_pfn); + printk("Heap resides at %lx-%lx.\n", brk, heap_end); +#endif +} + +#define MAP_BATCH ((STACK_SIZE / 2) / sizeof(mmu_update_t)) +void do_map_frames(unsigned long addr, + unsigned long *f, unsigned long n, unsigned long stride, + unsigned long increment, domid_t id, int may_fail, unsigned long prot) +{ + pgentry_t *pgt = NULL; + unsigned long done = 0; + unsigned long i; + int rc; + + while (done < n) { + unsigned long todo; + + if (may_fail) + todo = 1; + else + todo = n - done; + + if (todo > MAP_BATCH) + todo = MAP_BATCH; + + { + mmu_update_t mmu_updates[todo]; + + for (i = 0; i < todo; i++, addr += PAGE_SIZE, pgt++) { + if (!pgt || !(addr & L1_MASK)) + pgt = need_pgt(addr); + mmu_updates[i].ptr = virt_to_mach(pgt); + mmu_updates[i].val = ((pgentry_t)(f[(done + i) * stride] + (done + i) * increment) << PAGE_SHIFT) | prot; + } + + rc = HYPERVISOR_mmu_update(mmu_updates, todo, NULL, id); + if (rc < 0) { + if (may_fail) + f[done * stride] |= 0xF0000000; + else { + printk("Map %ld (%lx, ...) at %p failed: %d.\n", todo, f[done * stride] + done * increment, addr, rc); + do_exit(); + } + } + } + + done += todo; + } +} + +unsigned long allocate_ondemand(unsigned long n, unsigned long alignment) +{ + unsigned long x; + unsigned long y = 0; + + /* Find a properly aligned run of n contiguous frames */ + for (x = 0; x <= DEMAND_MAP_PAGES - n; x = (x + y + 1 + alignment - 1) & ~(alignment - 1)) { + unsigned long addr = demand_map_area_start + x * PAGE_SIZE; + pgentry_t *pgt = get_pgt(addr); + for (y = 0; y < n; y++, addr += PAGE_SIZE) { + if (!(addr & L1_MASK)) + pgt = get_pgt(addr); + if (pgt) { + if (*pgt & _PAGE_PRESENT) + break; + pgt++; + } + } + if (y == n) + break; + } + if (y != n) { + printk("Failed to find %ld frames!\n", n); + return 0; + } + return demand_map_area_start + x * PAGE_SIZE; +} + +void *map_frames_ex(unsigned long *f, unsigned long n, unsigned long stride, + unsigned long increment, unsigned long alignment, domid_t id, + int may_fail, unsigned long prot) +{ + unsigned long addr = allocate_ondemand(n, alignment); + + if (!addr) + return NULL; + + /* Found it at x. Map it in. */ + do_map_frames(addr, f, n, stride, increment, id, may_fail, prot); + + return (void *)addr; +} + +static void clear_bootstrap(void) +{ + pte_t nullpte = { }; + + /* Use first page as the CoW zero page */ + memset(&_text, 0, PAGE_SIZE); + mfn_zero = virt_to_mfn((unsigned long) &_text); + if (HYPERVISOR_update_va_mapping(0, nullpte, UVMF_INVLPG)) + printk("Unable to unmap NULL page\n"); +} + +void arch_init_p2m(unsigned long max_pfn) +{ +#define L1_P2M_SHIFT 9 +#define L2_P2M_SHIFT 18 +#define L3_P2M_SHIFT 27 +#define L1_P2M_ENTRIES (1 << L1_P2M_SHIFT) +#define L2_P2M_ENTRIES (1 << (L2_P2M_SHIFT - L1_P2M_SHIFT)) +#define L3_P2M_ENTRIES (1 << (L3_P2M_SHIFT - L2_P2M_SHIFT)) +#define L1_P2M_MASK (L1_P2M_ENTRIES - 1) +#define L2_P2M_MASK (L2_P2M_ENTRIES - 1) +#define L3_P2M_MASK (L3_P2M_ENTRIES - 1) + + unsigned long *l1_list = NULL, *l2_list = NULL, *l3_list; + unsigned long pfn; + + l3_list = (unsigned long *)alloc_page(); + for(pfn=0; pfn> L3_P2M_SHIFT) > 0) + { + printk("Error: Too many pfns.\n"); + do_exit(); + } + l3_list[(pfn >> L2_P2M_SHIFT)] = virt_to_mfn(l2_list); + } + if(!(pfn % (L1_P2M_ENTRIES))) + { + l1_list = (unsigned long*)alloc_page(); + l2_list[(pfn >> L1_P2M_SHIFT) & L2_P2M_MASK] = + virt_to_mfn(l1_list); + } + + l1_list[pfn & L1_P2M_MASK] = pfn_to_mfn(pfn); + } + HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list = + virt_to_mfn(l3_list); + HYPERVISOR_shared_info->arch.max_pfn = max_pfn; +} + +void arch_init_mm(unsigned long* start_pfn_p, unsigned long* max_pfn_p) +{ + + unsigned long start_pfn, max_pfn; + + printk(" _text: %p\n", &_text); + printk(" _etext: %p\n", &_etext); + printk(" _erodata: %p\n", &_erodata); + printk(" _edata: %p\n", &_edata); + printk(" stack start: %p\n", stack); + printk(" _end: %p\n", &_end); + + /* First page follows page table pages and 3 more pages (store page etc) */ + start_pfn = PFN_UP(to_phys(start_info.pt_base)) + + start_info.nr_pt_frames + 3; + max_pfn = start_info.nr_pages; + + printk(" start_pfn: %lx\n", start_pfn); + printk(" max_pfn: %lx\n", max_pfn); + + build_pagetable(&start_pfn, &max_pfn); + clear_bootstrap(); + set_readonly(&_text, &_erodata); + + *start_pfn_p = start_pfn; + *max_pfn_p = max_pfn; +} diff --git a/extras/mini-os/arch/x86/sched.c b/extras/mini-os/arch/x86/sched.c new file mode 100644 index 0000000..9b98b93 --- /dev/null +++ b/extras/mini-os/arch/x86/sched.c @@ -0,0 +1,136 @@ +/* + **************************************************************************** + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: sched.c + * Author: Grzegorz Milos + * Changes: Robert Kaiser + * + * Date: Aug 2005 + * + * Environment: Xen Minimal OS + * Description: simple scheduler for Mini-Os + * + * The scheduler is non-preemptive (cooperative), and schedules according + * to Round Robin algorithm. + * + **************************************************************************** + * 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 + + +#ifdef SCHED_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=sched.c, line=%d) " _f "\n", __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + + +void dump_stack(struct thread *thread) +{ + unsigned long *bottom = (unsigned long *)(thread->stack + STACK_SIZE); + unsigned long *pointer = (unsigned long *)thread->sp; + int count; + if(thread == current) + { +#ifdef __i386__ + asm("movl %%esp,%0" + : "=r"(pointer)); +#else + asm("movq %%rsp,%0" + : "=r"(pointer)); +#endif + } + printk("The stack for \"%s\"\n", thread->name); + for(count = 0; count < 25 && pointer < bottom; count ++) + { + printk("[0x%lx] 0x%lx\n", pointer, *pointer); + pointer++; + } + + if(pointer < bottom) printk(" ... continues.\n"); +} + +/* Gets run when a new thread is scheduled the first time ever, + defined in x86_[32/64].S */ +extern void thread_starter(void); + +/* Pushes the specified value onto the stack of the specified thread */ +static void stack_push(struct thread *thread, unsigned long value) +{ + thread->sp -= sizeof(unsigned long); + *((unsigned long *)thread->sp) = value; +} + +/* Architecture specific setup of thread creation */ +struct thread* arch_create_thread(char *name, void (*function)(void *), + void *data) +{ + struct thread *thread; + + thread = xmalloc(struct thread); + /* We can't use lazy allocation here since the trap handler runs on the stack */ + thread->stack = (char *)alloc_pages(STACK_SIZE_PAGE_ORDER); + thread->name = name; + printk("Thread \"%s\": pointer: 0x%lx, stack: 0x%lx\n", name, thread, + thread->stack); + + thread->sp = (unsigned long)thread->stack + STACK_SIZE; + /* Save pointer to the thread on the stack, used by current macro */ + *((unsigned long *)thread->stack) = (unsigned long)thread; + + stack_push(thread, (unsigned long) function); + stack_push(thread, (unsigned long) data); + thread->ip = (unsigned long) thread_starter; + return thread; +} + +void run_idle_thread(void) +{ + /* Switch stacks and run the thread */ +#if defined(__i386__) + __asm__ __volatile__("mov %0,%%esp\n\t" + "push %1\n\t" + "ret" + :"=m" (idle_thread->sp) + :"m" (idle_thread->ip)); +#elif defined(__x86_64__) + __asm__ __volatile__("mov %0,%%rsp\n\t" + "push %1\n\t" + "ret" + :"=m" (idle_thread->sp) + :"m" (idle_thread->ip)); +#endif +} + + + diff --git a/extras/mini-os/arch/x86/setup.c b/extras/mini-os/arch/x86/setup.c new file mode 100644 index 0000000..3671fee --- /dev/null +++ b/extras/mini-os/arch/x86/setup.c @@ -0,0 +1,118 @@ +/****************************************************************************** + * common.c + * + * Common stuff special to x86 goes here. + * + * Copyright (c) 2002-2003, K A Fraser & R Neugebauer + * Copyright (c) 2005, Grzegorz Milos, Intel Research Cambridge + * + * 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 + + +/* + * Shared page for communicating with the hypervisor. + * Events flags go here, for example. + */ +shared_info_t *HYPERVISOR_shared_info; + +/* + * This structure contains start-of-day info, such as pagetable base pointer, + * address of the shared_info structure, and things like that. + */ +union start_info_union start_info_union; + +/* + * Just allocate the kernel stack here. SS:ESP is set up to point here + * in head.S. + */ +char stack[2*STACK_SIZE]; + +extern char shared_info[PAGE_SIZE]; + +/* Assembler interface fns in entry.S. */ +void hypervisor_callback(void); +void failsafe_callback(void); + +#if defined(__x86_64__) +#define __pte(x) ((pte_t) { (x) } ) +#else +#define __pte(x) ({ unsigned long long _x = (x); \ + ((pte_t) {(unsigned long)(_x), (unsigned long)(_x>>32)}); }) +#endif + +static +shared_info_t *map_shared_info(unsigned long pa) +{ + if ( HYPERVISOR_update_va_mapping( + (unsigned long)shared_info, __pte(pa | 7), UVMF_INVLPG) ) + { + printk("Failed to map shared_info!!\n"); + do_exit(); + } + return (shared_info_t *)shared_info; +} + +void +arch_init(start_info_t *si) +{ + /* Copy the start_info struct to a globally-accessible area. */ + /* WARN: don't do printk before here, it uses information from + shared_info. Use xprintk instead. */ + memcpy(&start_info, si, sizeof(*si)); + + /* set up minimal memory infos */ + phys_to_machine_mapping = (unsigned long *)start_info.mfn_list; + + /* Grab the shared_info pointer and put it in a safe place. */ + HYPERVISOR_shared_info = map_shared_info(start_info.shared_info); + + /* Set up event and failsafe callback addresses. */ +#ifdef __i386__ + HYPERVISOR_set_callbacks( + __KERNEL_CS, (unsigned long)hypervisor_callback, + __KERNEL_CS, (unsigned long)failsafe_callback); +#else + HYPERVISOR_set_callbacks( + (unsigned long)hypervisor_callback, + (unsigned long)failsafe_callback, 0); +#endif + +} + +void +arch_fini(void) +{ +#ifdef __i386__ + HYPERVISOR_set_callbacks(0, 0, 0, 0); +#else + HYPERVISOR_set_callbacks(0, 0, 0); +#endif +} + +void +arch_print_info(void) +{ + printk(" stack: %p-%p\n", stack, stack + sizeof(stack)); +} + + diff --git a/extras/mini-os/arch/x86/time.c b/extras/mini-os/arch/x86/time.c new file mode 100644 index 0000000..4af0b89 --- /dev/null +++ b/extras/mini-os/arch/x86/time.c @@ -0,0 +1,238 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2002-2003 - Keir Fraser - University of Cambridge + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + * (C) 2006 - Robert Kaiser - FH Wiesbaden + **************************************************************************** + * + * File: time.c + * Author: Rolf Neugebauer and Keir Fraser + * Changes: Grzegorz Milos + * + * Description: Simple time and timer functions + * + * 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 + +/************************************************************************ + * Time functions + *************************************************************************/ + +/* These are peridically updated in shared_info, and then copied here. */ +struct shadow_time_info { + u64 tsc_timestamp; /* TSC at last update of time vals. */ + u64 system_timestamp; /* Time, in nanosecs, since boot. */ + u32 tsc_to_nsec_mul; + u32 tsc_to_usec_mul; + int tsc_shift; + u32 version; +}; +static struct timespec shadow_ts; +static u32 shadow_ts_version; + +static struct shadow_time_info shadow; + + +#ifndef rmb +#define rmb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") +#endif + +#define HANDLE_USEC_OVERFLOW(_tv) \ + do { \ + while ( (_tv)->tv_usec >= 1000000 ) \ + { \ + (_tv)->tv_usec -= 1000000; \ + (_tv)->tv_sec++; \ + } \ + } while ( 0 ) + +static inline int time_values_up_to_date(void) +{ + struct vcpu_time_info *src = &HYPERVISOR_shared_info->vcpu_info[0].time; + + return (shadow.version == src->version); +} + + +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift) +{ + u64 product; +#ifdef __i386__ + u32 tmp1, tmp2; +#endif + + if ( shift < 0 ) + delta >>= -shift; + else + delta <<= shift; + +#ifdef __i386__ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "add %4,%%eax ; " + "xor %5,%5 ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); +#else + __asm__ ( + "mul %%rdx ; shrd $32,%%rdx,%%rax" + : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) ); +#endif + + return product; +} + + +static unsigned long get_nsec_offset(void) +{ + u64 now, delta; + rdtscll(now); + delta = now - shadow.tsc_timestamp; + return scale_delta(delta, shadow.tsc_to_nsec_mul, shadow.tsc_shift); +} + + +static void get_time_values_from_xen(void) +{ + struct vcpu_time_info *src = &HYPERVISOR_shared_info->vcpu_info[0].time; + + do { + shadow.version = src->version; + rmb(); + shadow.tsc_timestamp = src->tsc_timestamp; + shadow.system_timestamp = src->system_time; + shadow.tsc_to_nsec_mul = src->tsc_to_system_mul; + shadow.tsc_shift = src->tsc_shift; + rmb(); + } + while ((src->version & 1) | (shadow.version ^ src->version)); + + shadow.tsc_to_usec_mul = shadow.tsc_to_nsec_mul / 1000; +} + + + + +/* monotonic_clock(): returns # of nanoseconds passed since time_init() + * Note: This function is required to return accurate + * time even in the absence of multiple timer ticks. + */ +u64 monotonic_clock(void) +{ + u64 time; + u32 local_time_version; + + do { + local_time_version = shadow.version; + rmb(); + time = shadow.system_timestamp + get_nsec_offset(); + if (!time_values_up_to_date()) + get_time_values_from_xen(); + rmb(); + } while (local_time_version != shadow.version); + + return time; +} + +static void update_wallclock(void) +{ + shared_info_t *s = HYPERVISOR_shared_info; + + do { + shadow_ts_version = s->wc_version; + rmb(); + shadow_ts.tv_sec = s->wc_sec; + shadow_ts.tv_nsec = s->wc_nsec; + rmb(); + } + while ((s->wc_version & 1) | (shadow_ts_version ^ s->wc_version)); +} + + +int gettimeofday(struct timeval *tv, void *tz) +{ + u64 nsec = monotonic_clock(); + nsec += shadow_ts.tv_nsec; + + + tv->tv_sec = shadow_ts.tv_sec; + tv->tv_sec += NSEC_TO_SEC(nsec); + tv->tv_usec = NSEC_TO_USEC(nsec % 1000000000UL); + + return 0; +} + + +void block_domain(s_time_t until) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + ASSERT(irqs_disabled()); + if(monotonic_clock() < until) + { + HYPERVISOR_set_timer_op(until); + HYPERVISOR_sched_op(SCHEDOP_block, 0); + local_irq_disable(); + } +} + + +/* + * Just a dummy + */ +static void timer_handler(evtchn_port_t ev, struct pt_regs *regs, void *ign) +{ + get_time_values_from_xen(); + update_wallclock(); +} + + + +static evtchn_port_t port; +void init_time(void) +{ + printk("Initialising timer interface\n"); + port = bind_virq(VIRQ_TIMER, &timer_handler, NULL); + unmask_evtchn(port); +} + +void fini_time(void) +{ + /* Clear any pending timer */ + HYPERVISOR_set_timer_op(0); + unbind_evtchn(port); +} diff --git a/extras/mini-os/arch/x86/traps.c b/extras/mini-os/arch/x86/traps.c new file mode 100644 index 0000000..f641d90 --- /dev/null +++ b/extras/mini-os/arch/x86/traps.c @@ -0,0 +1,333 @@ + +#include +#include +#include +#include +#include +#include + +/* + * These are assembler stubs in entry.S. + * They are the actual entry points for virtual exceptions. + */ +void divide_error(void); +void debug(void); +void int3(void); +void overflow(void); +void bounds(void); +void invalid_op(void); +void device_not_available(void); +void coprocessor_segment_overrun(void); +void invalid_TSS(void); +void segment_not_present(void); +void stack_segment(void); +void general_protection(void); +void page_fault(void); +void coprocessor_error(void); +void simd_coprocessor_error(void); +void alignment_check(void); +void spurious_interrupt_bug(void); +void machine_check(void); + + +void dump_regs(struct pt_regs *regs) +{ + printk("Thread: %s\n", current->name); +#ifdef __i386__ + printk("EIP: %x, EFLAGS %x.\n", regs->eip, regs->eflags); + printk("EBX: %08x ECX: %08x EDX: %08x\n", + regs->ebx, regs->ecx, regs->edx); + printk("ESI: %08x EDI: %08x EBP: %08x EAX: %08x\n", + regs->esi, regs->edi, regs->ebp, regs->eax); + printk("DS: %04x ES: %04x orig_eax: %08x, eip: %08x\n", + regs->xds, regs->xes, regs->orig_eax, regs->eip); + printk("CS: %04x EFLAGS: %08x esp: %08x ss: %04x\n", + regs->xcs, regs->eflags, regs->esp, regs->xss); +#else + printk("RIP: %04lx:[<%016lx>] ", regs->cs & 0xffff, regs->rip); + printk("\nRSP: %04lx:%016lx EFLAGS: %08lx\n", + regs->ss, regs->rsp, regs->eflags); + printk("RAX: %016lx RBX: %016lx RCX: %016lx\n", + regs->rax, regs->rbx, regs->rcx); + printk("RDX: %016lx RSI: %016lx RDI: %016lx\n", + regs->rdx, regs->rsi, regs->rdi); + printk("RBP: %016lx R08: %016lx R09: %016lx\n", + regs->rbp, regs->r8, regs->r9); + printk("R10: %016lx R11: %016lx R12: %016lx\n", + regs->r10, regs->r11, regs->r12); + printk("R13: %016lx R14: %016lx R15: %016lx\n", + regs->r13, regs->r14, regs->r15); +#endif +} + +static void do_trap(int trapnr, char *str, struct pt_regs * regs, unsigned long error_code) +{ + printk("FATAL: Unhandled Trap %d (%s), error code=0x%lx\n", trapnr, str, error_code); + printk("Regs address %p\n", regs); + dump_regs(regs); + do_exit(); +} + +#define DO_ERROR(trapnr, str, name) \ +void do_##name(struct pt_regs * regs, unsigned long error_code) \ +{ \ + do_trap(trapnr, str, regs, error_code); \ +} + +#define DO_ERROR_INFO(trapnr, str, name, sicode, siaddr) \ +void do_##name(struct pt_regs * regs, unsigned long error_code) \ +{ \ + do_trap(trapnr, str, regs, error_code); \ +} + +DO_ERROR_INFO( 0, "divide error", divide_error, FPE_INTDIV, regs->eip) +DO_ERROR( 3, "int3", int3) +DO_ERROR( 4, "overflow", overflow) +DO_ERROR( 5, "bounds", bounds) +DO_ERROR_INFO( 6, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip) +DO_ERROR( 7, "device not available", device_not_available) +DO_ERROR( 9, "coprocessor segment overrun", coprocessor_segment_overrun) +DO_ERROR(10, "invalid TSS", invalid_TSS) +DO_ERROR(11, "segment not present", segment_not_present) +DO_ERROR(12, "stack segment", stack_segment) +DO_ERROR_INFO(17, "alignment check", alignment_check, BUS_ADRALN, 0) +DO_ERROR(18, "machine check", machine_check) + +void page_walk(unsigned long virt_address) +{ + pgentry_t *tab = (pgentry_t *)start_info.pt_base, page; + unsigned long addr = virt_address; + printk("Pagetable walk from virt %lx, base %lx:\n", virt_address, start_info.pt_base); + +#if defined(__x86_64__) + page = tab[l4_table_offset(addr)]; + tab = pte_to_virt(page); + printk(" L4 = %"PRIpte" (%p) [offset = %lx]\n", page, tab, l4_table_offset(addr)); +#endif + page = tab[l3_table_offset(addr)]; + tab = pte_to_virt(page); + printk(" L3 = %"PRIpte" (%p) [offset = %lx]\n", page, tab, l3_table_offset(addr)); + page = tab[l2_table_offset(addr)]; + tab = pte_to_virt(page); + printk(" L2 = %"PRIpte" (%p) [offset = %lx]\n", page, tab, l2_table_offset(addr)); + + page = tab[l1_table_offset(addr)]; + printk(" L1 = %"PRIpte" [offset = %lx]\n", page, l1_table_offset(addr)); + +} + +static int handle_cow(unsigned long addr) { + pgentry_t *tab = (pgentry_t *)start_info.pt_base, page; + unsigned long new_page; + int rc; + +#if defined(__x86_64__) + page = tab[l4_table_offset(addr)]; + if (!(page & _PAGE_PRESENT)) + return 0; + tab = pte_to_virt(page); +#endif + page = tab[l3_table_offset(addr)]; + if (!(page & _PAGE_PRESENT)) + return 0; + tab = pte_to_virt(page); + + page = tab[l2_table_offset(addr)]; + if (!(page & _PAGE_PRESENT)) + return 0; + tab = pte_to_virt(page); + + page = tab[l1_table_offset(addr)]; + if (!(page & _PAGE_PRESENT)) + return 0; + /* Only support CoW for the zero page. */ + if (PHYS_PFN(page) != mfn_zero) + return 0; + + new_page = alloc_pages(0); + memset((void*) new_page, 0, PAGE_SIZE); + + rc = HYPERVISOR_update_va_mapping(addr & PAGE_MASK, __pte(virt_to_mach(new_page) | L1_PROT), UVMF_INVLPG); + if (!rc) + return 1; + + printk("Map zero page to %lx failed: %d.\n", addr, rc); + return 0; +} + +static void do_stack_walk(unsigned long frame_base) +{ + unsigned long *frame = (void*) frame_base; + printk("base is %#lx ", frame_base); + printk("caller is %#lx\n", frame[1]); + if (frame[0]) + do_stack_walk(frame[0]); +} + +void stack_walk(void) +{ + unsigned long bp; +#ifdef __x86_64__ + asm("movq %%rbp, %0":"=r"(bp)); +#else + asm("movl %%ebp, %0":"=r"(bp)); +#endif + do_stack_walk(bp); +} + +static void dump_mem(unsigned long addr) +{ + unsigned long i; + if (addr < PAGE_SIZE) + return; + + for (i = ((addr)-16 ) & ~15; i < (((addr)+48 ) & ~15); i++) + { + if (!(i%16)) + printk("\n%lx:", i); + printk(" %02x", *(unsigned char *)i); + } + printk("\n"); +} +#define read_cr2() \ + (HYPERVISOR_shared_info->vcpu_info[smp_processor_id()].arch.cr2) + +static int handling_pg_fault = 0; + +void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + unsigned long addr = read_cr2(); + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_crash }; + + if ((error_code & TRAP_PF_WRITE) && handle_cow(addr)) + return; + + /* If we are already handling a page fault, and got another one + that means we faulted in pagetable walk. Continuing here would cause + a recursive fault */ + if(handling_pg_fault == 1) + { + printk("Page fault in pagetable walk (access to invalid memory?).\n"); + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + } + handling_pg_fault++; + barrier(); + +#if defined(__x86_64__) + printk("Page fault at linear address %p, rip %p, regs %p, sp %p, our_sp %p, code %lx\n", + addr, regs->rip, regs, regs->rsp, &addr, error_code); +#else + printk("Page fault at linear address %p, eip %p, regs %p, sp %p, our_sp %p, code %lx\n", + addr, regs->eip, regs, regs->esp, &addr, error_code); +#endif + + dump_regs(regs); +#if defined(__x86_64__) + do_stack_walk(regs->rbp); + dump_mem(regs->rsp); + dump_mem(regs->rbp); + dump_mem(regs->rip); +#else + do_stack_walk(regs->ebp); + dump_mem(regs->esp); + dump_mem(regs->ebp); + dump_mem(regs->eip); +#endif + page_walk(addr); + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + /* We should never get here ... but still */ + handling_pg_fault--; +} + +void do_general_protection(struct pt_regs *regs, long error_code) +{ + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_crash }; +#ifdef __i386__ + printk("GPF eip: %p, error_code=%lx\n", regs->eip, error_code); +#else + printk("GPF rip: %p, error_code=%lx\n", regs->rip, error_code); +#endif + dump_regs(regs); +#if defined(__x86_64__) + do_stack_walk(regs->rbp); + dump_mem(regs->rsp); + dump_mem(regs->rbp); + dump_mem(regs->rip); +#else + do_stack_walk(regs->ebp); + dump_mem(regs->esp); + dump_mem(regs->ebp); + dump_mem(regs->eip); +#endif + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); +} + + +void do_debug(struct pt_regs * regs) +{ + printk("Debug exception\n"); +#define TF_MASK 0x100 + regs->eflags &= ~TF_MASK; + dump_regs(regs); + do_exit(); +} + +void do_coprocessor_error(struct pt_regs * regs) +{ + printk("Copro error\n"); + dump_regs(regs); + do_exit(); +} + +void simd_math_error(void *eip) +{ + printk("SIMD error\n"); +} + +void do_simd_coprocessor_error(struct pt_regs * regs) +{ + printk("SIMD copro error\n"); +} + +void do_spurious_interrupt_bug(struct pt_regs * regs) +{ +} + +/* + * Submit a virtual IDT to teh hypervisor. This consists of tuples + * (interrupt vector, privilege ring, CS:EIP of handler). + * The 'privilege ring' field specifies the least-privileged ring that + * can trap to that vector using a software-interrupt instruction (INT). + */ +static trap_info_t trap_table[] = { + { 0, 0, __KERNEL_CS, (unsigned long)divide_error }, + { 1, 0, __KERNEL_CS, (unsigned long)debug }, + { 3, 3, __KERNEL_CS, (unsigned long)int3 }, + { 4, 3, __KERNEL_CS, (unsigned long)overflow }, + { 5, 3, __KERNEL_CS, (unsigned long)bounds }, + { 6, 0, __KERNEL_CS, (unsigned long)invalid_op }, + { 7, 0, __KERNEL_CS, (unsigned long)device_not_available }, + { 9, 0, __KERNEL_CS, (unsigned long)coprocessor_segment_overrun }, + { 10, 0, __KERNEL_CS, (unsigned long)invalid_TSS }, + { 11, 0, __KERNEL_CS, (unsigned long)segment_not_present }, + { 12, 0, __KERNEL_CS, (unsigned long)stack_segment }, + { 13, 0, __KERNEL_CS, (unsigned long)general_protection }, + { 14, 0, __KERNEL_CS, (unsigned long)page_fault }, + { 15, 0, __KERNEL_CS, (unsigned long)spurious_interrupt_bug }, + { 16, 0, __KERNEL_CS, (unsigned long)coprocessor_error }, + { 17, 0, __KERNEL_CS, (unsigned long)alignment_check }, + { 19, 0, __KERNEL_CS, (unsigned long)simd_coprocessor_error }, + { 0, 0, 0, 0 } +}; + + + +void trap_init(void) +{ + HYPERVISOR_set_trap_table(trap_table); +} + +void trap_fini(void) +{ + HYPERVISOR_set_trap_table(NULL); +} diff --git a/extras/mini-os/arch/x86/x86_32.S b/extras/mini-os/arch/x86/x86_32.S new file mode 100644 index 0000000..e1426ac --- /dev/null +++ b/extras/mini-os/arch/x86/x86_32.S @@ -0,0 +1,305 @@ +#include +#include +#include + +.section __xen_guest + .ascii "GUEST_OS=Mini-OS" + .ascii ",XEN_VER=xen-3.0" + .ascii ",VIRT_BASE=0x0" /* &_text from minios_x86_32.lds */ + .ascii ",ELF_PADDR_OFFSET=0x0" + .ascii ",HYPERCALL_PAGE=0x2" + .ascii ",PAE=yes[extended-cr3]" + .ascii ",LOADER=generic" + .byte 0 +.text + +.globl _start, shared_info, hypercall_page + +_start: + cld + lss stack_start,%esp + andl $(~(__STACK_SIZE-1)), %esp + push %esi + call start_kernel + +stack_start: + .long stack+(2*__STACK_SIZE), __KERNEL_SS + + /* Unpleasant -- the PTE that maps this page is actually overwritten */ + /* to map the real shared-info page! :-) */ + .org 0x1000 +shared_info: + .org 0x2000 + +hypercall_page: + .org 0x3000 + +ES = 0x20 +ORIG_EAX = 0x24 +EIP = 0x28 +CS = 0x2C + +#define ENTRY(X) .globl X ; X : + +#define SAVE_ALL \ + cld; \ + pushl %es; \ + pushl %ds; \ + pushl %eax; \ + pushl %ebp; \ + pushl %edi; \ + pushl %esi; \ + pushl %edx; \ + pushl %ecx; \ + pushl %ebx; \ + movl $(__KERNEL_DS),%edx; \ + movl %edx,%ds; \ + movl %edx,%es; + +#define RESTORE_ALL \ + popl %ebx; \ + popl %ecx; \ + popl %edx; \ + popl %esi; \ + popl %edi; \ + popl %ebp; \ + popl %eax; \ + popl %ds; \ + popl %es; \ + addl $4,%esp; \ + iret; + +ENTRY(divide_error) + pushl $0 # no error code + pushl $do_divide_error +do_exception: + pushl %ds + pushl %eax + xorl %eax, %eax + pushl %ebp + pushl %edi + pushl %esi + pushl %edx + decl %eax # eax = -1 + pushl %ecx + pushl %ebx + cld + movl %es, %ecx + movl ES(%esp), %edi # get the function address + movl ORIG_EAX(%esp), %edx # get the error code + movl %eax, ORIG_EAX(%esp) + movl %ecx, ES(%esp) + movl $(__KERNEL_DS), %ecx + movl %ecx, %ds + movl %ecx, %es + movl %esp,%eax # pt_regs pointer + pushl %edx + pushl %eax + call *%edi + jmp ret_from_exception + +ret_from_exception: + movb CS(%esp),%cl + addl $8,%esp + RESTORE_ALL + +# A note on the "critical region" in our callback handler. +# We want to avoid stacking callback handlers due to events occurring +# during handling of the last event. To do this, we keep events disabled +# until weve done all processing. HOWEVER, we must enable events before +# popping the stack frame (cant be done atomically) and so it would still +# be possible to get enough handler activations to overflow the stack. +# Although unlikely, bugs of that kind are hard to track down, so wed +# like to avoid the possibility. +# So, on entry to the handler we detect whether we interrupted an +# existing activation in its critical region -- if so, we pop the current +# activation and restart the handler using the previous one. +ENTRY(hypervisor_callback) + pushl %eax + SAVE_ALL + movl EIP(%esp),%eax + cmpl $scrit,%eax + jb 11f + cmpl $ecrit,%eax + jb critical_region_fixup +11: push %esp + xorl %ebp,%ebp + call do_hypervisor_callback + add $4,%esp + movl HYPERVISOR_shared_info,%esi + xorl %eax,%eax + movb CS(%esp),%cl + test $2,%cl # slow return to ring 2 or 3 + jne safesti +safesti:movb $0,1(%esi) # reenable event callbacks +scrit: /**** START OF CRITICAL REGION ****/ + testb $0xFF,(%esi) + jnz 14f # process more events if necessary... + RESTORE_ALL +14: movb $1,1(%esi) + jmp 11b +ecrit: /**** END OF CRITICAL REGION ****/ +# [How we do the fixup]. We want to merge the current stack frame with the +# just-interrupted frame. How we do this depends on where in the critical +# region the interrupted handler was executing, and so how many saved +# registers are in each frame. We do this quickly using the lookup table +# 'critical_fixup_table'. For each byte offset in the critical region, it +# provides the number of bytes which have already been popped from the +# interrupted stack frame. +critical_region_fixup: + addl $critical_fixup_table-scrit,%eax + movzbl (%eax),%eax # %eax contains num bytes popped + mov %esp,%esi + add %eax,%esi # %esi points at end of src region + mov %esp,%edi + add $0x34,%edi # %edi points at end of dst region + mov %eax,%ecx + shr $2,%ecx # convert words to bytes + je 16f # skip loop if nothing to copy +15: subl $4,%esi # pre-decrementing copy loop + subl $4,%edi + movl (%esi),%eax + movl %eax,(%edi) + loop 15b +16: movl %edi,%esp # final %edi is top of merged stack + jmp 11b + +critical_fixup_table: + .byte 0x00,0x00,0x00 # testb $0xff,(%esi) + .byte 0x00,0x00 # jne 14f + .byte 0x00 # pop %ebx + .byte 0x04 # pop %ecx + .byte 0x08 # pop %edx + .byte 0x0c # pop %esi + .byte 0x10 # pop %edi + .byte 0x14 # pop %ebp + .byte 0x18 # pop %eax + .byte 0x1c # pop %ds + .byte 0x20 # pop %es + .byte 0x24,0x24,0x24 # add $4,%esp + .byte 0x28 # iret + .byte 0x00,0x00,0x00,0x00 # movb $1,1(%esi) + .byte 0x00,0x00 # jmp 11b + +# Hypervisor uses this for application faults while it executes. +ENTRY(failsafe_callback) + pop %ds + pop %es + pop %fs + pop %gs + iret + +ENTRY(coprocessor_error) + pushl $0 + pushl $do_coprocessor_error + jmp do_exception + +ENTRY(simd_coprocessor_error) + pushl $0 + pushl $do_simd_coprocessor_error + jmp do_exception + +ENTRY(device_not_available) + iret + +ENTRY(debug) + pushl $0 + pushl $do_debug + jmp do_exception + +ENTRY(int3) + pushl $0 + pushl $do_int3 + jmp do_exception + +ENTRY(overflow) + pushl $0 + pushl $do_overflow + jmp do_exception + +ENTRY(bounds) + pushl $0 + pushl $do_bounds + jmp do_exception + +ENTRY(invalid_op) + pushl $0 + pushl $do_invalid_op + jmp do_exception + + +ENTRY(coprocessor_segment_overrun) + pushl $0 + pushl $do_coprocessor_segment_overrun + jmp do_exception + + +ENTRY(invalid_TSS) + pushl $do_invalid_TSS + jmp do_exception + + +ENTRY(segment_not_present) + pushl $do_segment_not_present + jmp do_exception + + +ENTRY(stack_segment) + pushl $do_stack_segment + jmp do_exception + + +ENTRY(general_protection) + pushl $do_general_protection + jmp do_exception + + +ENTRY(alignment_check) + pushl $do_alignment_check + jmp do_exception + + +ENTRY(page_fault) + pushl $do_page_fault + jmp do_exception + +ENTRY(machine_check) + pushl $0 + pushl $do_machine_check + jmp do_exception + + +ENTRY(spurious_interrupt_bug) + pushl $0 + pushl $do_spurious_interrupt_bug + jmp do_exception + + + +ENTRY(thread_starter) + popl %eax + popl %ebx + pushl $0 + xorl %ebp,%ebp + pushl %eax + call *%ebx + call exit_thread + +ENTRY(__arch_switch_threads) + movl 4(%esp), %ecx /* prev */ + movl 8(%esp), %edx /* next */ + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + movl %esp, (%ecx) /* save ESP */ + movl (%edx), %esp /* restore ESP */ + movl $1f, 4(%ecx) /* save EIP */ + pushl 4(%edx) /* restore EIP */ + ret +1: + popl %edi + popl %esi + popl %ebx + popl %ebp + ret diff --git a/extras/mini-os/arch/x86/x86_64.S b/extras/mini-os/arch/x86/x86_64.S new file mode 100644 index 0000000..c7a797d --- /dev/null +++ b/extras/mini-os/arch/x86/x86_64.S @@ -0,0 +1,410 @@ +#include +#include +#include + +.section __xen_guest + .ascii "GUEST_OS=Mini-OS" + .ascii ",XEN_VER=xen-3.0" + .ascii ",VIRT_BASE=0x0" /* &_text from minios_x86_64.lds */ + .ascii ",ELF_PADDR_OFFSET=0x0" + .ascii ",HYPERCALL_PAGE=0x2" + .ascii ",LOADER=generic" + .byte 0 +.text + +#define ENTRY(X) .globl X ; X : +.globl _start, shared_info, hypercall_page + + +_start: + cld + movq stack_start(%rip),%rsp + andq $(~(__STACK_SIZE-1)), %rsp + movq %rsi,%rdi + call start_kernel + +stack_start: + .quad stack+(2*__STACK_SIZE) + + /* Unpleasant -- the PTE that maps this page is actually overwritten */ + /* to map the real shared-info page! :-) */ + .org 0x1000 +shared_info: + .org 0x2000 + +hypercall_page: + .org 0x3000 + + +/* Offsets into shared_info_t. */ +#define evtchn_upcall_pending /* 0 */ +#define evtchn_upcall_mask 1 + +NMI_MASK = 0x80000000 + +#define RDI 112 +#define ORIG_RAX 120 /* + error_code */ +#define EFLAGS 144 + +#define REST_SKIP 6*8 +.macro SAVE_REST + subq $REST_SKIP,%rsp +# CFI_ADJUST_CFA_OFFSET REST_SKIP + movq %rbx,5*8(%rsp) +# CFI_REL_OFFSET rbx,5*8 + movq %rbp,4*8(%rsp) +# CFI_REL_OFFSET rbp,4*8 + movq %r12,3*8(%rsp) +# CFI_REL_OFFSET r12,3*8 + movq %r13,2*8(%rsp) +# CFI_REL_OFFSET r13,2*8 + movq %r14,1*8(%rsp) +# CFI_REL_OFFSET r14,1*8 + movq %r15,(%rsp) +# CFI_REL_OFFSET r15,0*8 +.endm + + +.macro RESTORE_REST + movq (%rsp),%r15 +# CFI_RESTORE r15 + movq 1*8(%rsp),%r14 +# CFI_RESTORE r14 + movq 2*8(%rsp),%r13 +# CFI_RESTORE r13 + movq 3*8(%rsp),%r12 +# CFI_RESTORE r12 + movq 4*8(%rsp),%rbp +# CFI_RESTORE rbp + movq 5*8(%rsp),%rbx +# CFI_RESTORE rbx + addq $REST_SKIP,%rsp +# CFI_ADJUST_CFA_OFFSET -(REST_SKIP) +.endm + + +#define ARG_SKIP 9*8 +.macro RESTORE_ARGS skiprax=0,addskip=0,skiprcx=0,skipr11=0,skipr8910=0,skiprdx=0 + .if \skipr11 + .else + movq (%rsp),%r11 +# CFI_RESTORE r11 + .endif + .if \skipr8910 + .else + movq 1*8(%rsp),%r10 +# CFI_RESTORE r10 + movq 2*8(%rsp),%r9 +# CFI_RESTORE r9 + movq 3*8(%rsp),%r8 +# CFI_RESTORE r8 + .endif + .if \skiprax + .else + movq 4*8(%rsp),%rax +# CFI_RESTORE rax + .endif + .if \skiprcx + .else + movq 5*8(%rsp),%rcx +# CFI_RESTORE rcx + .endif + .if \skiprdx + .else + movq 6*8(%rsp),%rdx +# CFI_RESTORE rdx + .endif + movq 7*8(%rsp),%rsi +# CFI_RESTORE rsi + movq 8*8(%rsp),%rdi +# CFI_RESTORE rdi + .if ARG_SKIP+\addskip > 0 + addq $ARG_SKIP+\addskip,%rsp +# CFI_ADJUST_CFA_OFFSET -(ARG_SKIP+\addskip) + .endif +.endm + + +.macro HYPERVISOR_IRET flag +# testb $3,1*8(%rsp) /* Don't need to do that in Mini-os, as */ +# jnz 2f /* there is no userspace? */ + testl $NMI_MASK,2*8(%rsp) + jnz 2f + + testb $1,(xen_features+XENFEAT_supervisor_mode_kernel) + jnz 1f + + /* Direct iret to kernel space. Correct CS and SS. */ + orb $3,1*8(%rsp) + orb $3,4*8(%rsp) +1: iretq + +2: /* Slow iret via hypervisor. */ + andl $~NMI_MASK, 16(%rsp) + pushq $\flag + jmp hypercall_page + (__HYPERVISOR_iret * 32) +.endm + +/* + * Exception entry point. This expects an error code/orig_rax on the stack + * and the exception handler in %rax. + */ +ENTRY(error_entry) +# _frame RDI + /* rdi slot contains rax, oldrax contains error code */ + cld + subq $14*8,%rsp +# CFI_ADJUST_CFA_OFFSET (14*8) + movq %rsi,13*8(%rsp) +# CFI_REL_OFFSET rsi,RSI + movq 14*8(%rsp),%rsi /* load rax from rdi slot */ + movq %rdx,12*8(%rsp) +# CFI_REL_OFFSET rdx,RDX + movq %rcx,11*8(%rsp) +# CFI_REL_OFFSET rcx,RCX + movq %rsi,10*8(%rsp) /* store rax */ +# CFI_REL_OFFSET rax,RAX + movq %r8, 9*8(%rsp) +# CFI_REL_OFFSET r8,R8 + movq %r9, 8*8(%rsp) +# CFI_REL_OFFSET r9,R9 + movq %r10,7*8(%rsp) +# CFI_REL_OFFSET r10,R10 + movq %r11,6*8(%rsp) +# CFI_REL_OFFSET r11,R11 + movq %rbx,5*8(%rsp) +# CFI_REL_OFFSET rbx,RBX + movq %rbp,4*8(%rsp) +# CFI_REL_OFFSET rbp,RBP + movq %r12,3*8(%rsp) +# CFI_REL_OFFSET r12,R12 + movq %r13,2*8(%rsp) +# CFI_REL_OFFSET r13,R13 + movq %r14,1*8(%rsp) +# CFI_REL_OFFSET r14,R14 + movq %r15,(%rsp) +# CFI_REL_OFFSET r15,R15 +#if 0 + cmpl $__KERNEL_CS,CS(%rsp) + je error_kernelspace +#endif +error_call_handler: + movq %rdi, RDI(%rsp) + movq %rsp,%rdi + movq ORIG_RAX(%rsp),%rsi # get error code + movq $-1,ORIG_RAX(%rsp) + call *%rax + jmp error_exit + +.macro zeroentry sym +# INTR_FRAME + movq (%rsp),%rcx + movq 8(%rsp),%r11 + addq $0x10,%rsp /* skip rcx and r11 */ + pushq $0 /* push error code/oldrax */ +# CFI_ADJUST_CFA_OFFSET 8 + pushq %rax /* push real oldrax to the rdi slot */ +# CFI_ADJUST_CFA_OFFSET 8 + leaq \sym(%rip),%rax + jmp error_entry +# CFI_ENDPROC +.endm + +.macro errorentry sym +# XCPT_FRAME + movq (%rsp),%rcx + movq 8(%rsp),%r11 + addq $0x10,%rsp /* rsp points to the error code */ + pushq %rax +# CFI_ADJUST_CFA_OFFSET 8 + leaq \sym(%rip),%rax + jmp error_entry +# CFI_ENDPROC +.endm + +#define XEN_GET_VCPU_INFO(reg) movq HYPERVISOR_shared_info,reg +#define XEN_PUT_VCPU_INFO(reg) +#define XEN_PUT_VCPU_INFO_fixup +#define XEN_LOCKED_BLOCK_EVENTS(reg) movb $1,evtchn_upcall_mask(reg) +#define XEN_LOCKED_UNBLOCK_EVENTS(reg) movb $0,evtchn_upcall_mask(reg) +#define XEN_TEST_PENDING(reg) testb $0xFF,evtchn_upcall_pending(reg) + +#define XEN_BLOCK_EVENTS(reg) XEN_GET_VCPU_INFO(reg) ; \ + XEN_LOCKED_BLOCK_EVENTS(reg) ; \ + XEN_PUT_VCPU_INFO(reg) + +#define XEN_UNBLOCK_EVENTS(reg) XEN_GET_VCPU_INFO(reg) ; \ + XEN_LOCKED_UNBLOCK_EVENTS(reg) ; \ + XEN_PUT_VCPU_INFO(reg) + + + +ENTRY(hypervisor_callback) + zeroentry hypervisor_callback2 + +ENTRY(hypervisor_callback2) + movq %rdi, %rsp +11: movq %gs:8,%rax + incl %gs:0 + cmovzq %rax,%rsp + pushq %rdi + call do_hypervisor_callback + popq %rsp + decl %gs:0 + jmp error_exit + +# ALIGN +restore_all_enable_events: + XEN_UNBLOCK_EVENTS(%rsi) # %rsi is already set up... + +scrit: /**** START OF CRITICAL REGION ****/ + XEN_TEST_PENDING(%rsi) + jnz 14f # process more events if necessary... + XEN_PUT_VCPU_INFO(%rsi) + RESTORE_ARGS 0,8,0 + HYPERVISOR_IRET 0 + +14: XEN_LOCKED_BLOCK_EVENTS(%rsi) + XEN_PUT_VCPU_INFO(%rsi) + SAVE_REST + movq %rsp,%rdi # set the argument again + jmp 11b +ecrit: /**** END OF CRITICAL REGION ****/ + + +retint_kernel: +retint_restore_args: + movl EFLAGS-REST_SKIP(%rsp), %eax + shr $9, %eax # EAX[0] == IRET_EFLAGS.IF + XEN_GET_VCPU_INFO(%rsi) + andb evtchn_upcall_mask(%rsi),%al + andb $1,%al # EAX[0] == IRET_EFLAGS.IF & event_mask + jnz restore_all_enable_events # != 0 => enable event delivery + XEN_PUT_VCPU_INFO(%rsi) + + RESTORE_ARGS 0,8,0 + HYPERVISOR_IRET 0 + + +error_exit: + RESTORE_REST +/* cli */ + XEN_BLOCK_EVENTS(%rsi) + jmp retint_kernel + + + +ENTRY(failsafe_callback) + popq %rcx + popq %r11 + iretq + + +ENTRY(coprocessor_error) + zeroentry do_coprocessor_error + + +ENTRY(simd_coprocessor_error) + zeroentry do_simd_coprocessor_error + + +ENTRY(device_not_available) + zeroentry do_device_not_available + + +ENTRY(debug) +# INTR_FRAME +# CFI_ADJUST_CFA_OFFSET 8 */ + zeroentry do_debug +# CFI_ENDPROC + + +ENTRY(int3) +# INTR_FRAME +# CFI_ADJUST_CFA_OFFSET 8 */ + zeroentry do_int3 +# CFI_ENDPROC + +ENTRY(overflow) + zeroentry do_overflow + + +ENTRY(bounds) + zeroentry do_bounds + + +ENTRY(invalid_op) + zeroentry do_invalid_op + + +ENTRY(coprocessor_segment_overrun) + zeroentry do_coprocessor_segment_overrun + + +ENTRY(invalid_TSS) + errorentry do_invalid_TSS + + +ENTRY(segment_not_present) + errorentry do_segment_not_present + + +/* runs on exception stack */ +ENTRY(stack_segment) +# XCPT_FRAME + errorentry do_stack_segment +# CFI_ENDPROC + + +ENTRY(general_protection) + errorentry do_general_protection + + +ENTRY(alignment_check) + errorentry do_alignment_check + + +ENTRY(divide_error) + zeroentry do_divide_error + + +ENTRY(spurious_interrupt_bug) + zeroentry do_spurious_interrupt_bug + + +ENTRY(page_fault) + errorentry do_page_fault + + + + + +ENTRY(thread_starter) + popq %rdi + popq %rbx + pushq $0 + xorq %rbp,%rbp + call *%rbx + call exit_thread + + +ENTRY(__arch_switch_threads) + pushq %rbp + pushq %rbx + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + movq %rsp, (%rdi) /* save ESP */ + movq (%rsi), %rsp /* restore ESP */ + movq $1f, 8(%rdi) /* save EIP */ + pushq 8(%rsi) /* restore EIP */ + ret +1: + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbx + popq %rbp + ret diff --git a/extras/mini-os/blkfront.c b/extras/mini-os/blkfront.c new file mode 100644 index 0000000..d4b0ea5 --- /dev/null +++ b/extras/mini-os/blkfront.c @@ -0,0 +1,505 @@ +/* Minimal block driver for Mini-OS. + * Copyright (c) 2007-2008 Samuel Thibault. + * Based on netfront.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_LIBC +#define strtoul simple_strtoul +#endif + +/* Note: we generally don't need to disable IRQs since we hardly do anything in + * the interrupt handler. */ + +/* Note: we really suppose non-preemptive threads. */ + +DECLARE_WAIT_QUEUE_HEAD(blkfront_queue); + + + + +#define BLK_RING_SIZE __RING_SIZE((struct blkif_sring *)0, PAGE_SIZE) +#define GRANT_INVALID_REF 0 + + +struct blk_buffer { + void* page; + grant_ref_t gref; +}; + +struct blkfront_dev { + domid_t dom; + + struct blkif_front_ring ring; + grant_ref_t ring_ref; + evtchn_port_t evtchn; + blkif_vdev_t handle; + + char *nodename; + char *backend; + struct blkfront_info info; + + xenbus_event_queue events; + +#ifdef HAVE_LIBC + int fd; +#endif +}; + +void blkfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ +#ifdef HAVE_LIBC + struct blkfront_dev *dev = data; + int fd = dev->fd; + + if (fd != -1) + files[fd].read = 1; +#endif + wake_up(&blkfront_queue); +} + +static void free_blkfront(struct blkfront_dev *dev) +{ + mask_evtchn(dev->evtchn); + + free(dev->backend); + + gnttab_end_access(dev->ring_ref); + free_page(dev->ring.sring); + + unbind_evtchn(dev->evtchn); + + free(dev->nodename); + free(dev); +} + +struct blkfront_dev *init_blkfront(char *_nodename, struct blkfront_info *info) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + struct blkif_sring *s; + int retry=0; + char* msg; + char* c; + char* nodename = _nodename ? _nodename : "device/vbd/768"; + + struct blkfront_dev *dev; + + char path[strlen(nodename) + 1 + 10 + 1]; + + printk("******************* BLKFRONT for %s **********\n\n\n", nodename); + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->nodename = strdup(nodename); +#ifdef HAVE_LIBC + dev->fd = -1; +#endif + + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dev->dom = xenbus_read_integer(path); + evtchn_alloc_unbound(dev->dom, blkfront_handler, dev, &dev->evtchn); + + s = (struct blkif_sring*) alloc_page(); + memset(s,0,PAGE_SIZE); + + + SHARED_RING_INIT(s); + FRONT_RING_INIT(&dev->ring, s, PAGE_SIZE); + + dev->ring_ref = gnttab_grant_access(dev->dom,virt_to_mfn(s),0); + + dev->events = NULL; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "ring-ref","%u", + dev->ring_ref); + if (err) { + message = "writing ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, + "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, + "protocol", "%s", XEN_IO_PROTO_ABI_NATIVE); + if (err) { + message = "writing protocol"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "state", "%u", + 4); /* connected */ + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + goto error; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + if (msg) { + printk("Error %s when reading the backend path %s\n", msg, path); + goto error; + } + + printk("backend at %s\n", dev->backend); + + dev->handle = strtoul(strrchr(nodename, '/')+1, NULL, 0); + + { + char path[strlen(dev->backend) + 1 + 19 + 1]; + snprintf(path, sizeof(path), "%s/mode", dev->backend); + msg = xenbus_read(XBT_NIL, path, &c); + if (msg) { + printk("Error %s when reading the mode\n", msg); + goto error; + } + if (*c == 'w') + dev->info.mode = O_RDWR; + else + dev->info.mode = O_RDONLY; + free(c); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path_token(XBT_NIL, path, path, &dev->events); + + xenbus_wait_for_value(path, "4", &dev->events); + + snprintf(path, sizeof(path), "%s/info", dev->backend); + dev->info.info = xenbus_read_integer(path); + + snprintf(path, sizeof(path), "%s/sectors", dev->backend); + // FIXME: read_integer returns an int, so disk size limited to 1TB for now + dev->info.sectors = xenbus_read_integer(path); + + snprintf(path, sizeof(path), "%s/sector-size", dev->backend); + dev->info.sector_size = xenbus_read_integer(path); + + snprintf(path, sizeof(path), "%s/feature-barrier", dev->backend); + dev->info.barrier = xenbus_read_integer(path); + + snprintf(path, sizeof(path), "%s/feature-flush-cache", dev->backend); + dev->info.flush = xenbus_read_integer(path); + + *info = dev->info; + } + unmask_evtchn(dev->evtchn); + + printk("%u sectors of %u bytes\n", dev->info.sectors, dev->info.sector_size); + printk("**************************\n"); + + return dev; + +error: + free_blkfront(dev); + return NULL; +} + +void shutdown_blkfront(struct blkfront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + blkfront_sync(dev); + + printk("close blk: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path, "5", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path, "6", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 1); + xenbus_wait_for_value(path, "2", &dev->events); + + xenbus_unwatch_path(XBT_NIL, path); + + snprintf(path, sizeof(path), "%s/ring-ref", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/event-channel", nodename); + xenbus_rm(XBT_NIL, path); + + free_blkfront(dev); +} + +static void blkfront_wait_slot(struct blkfront_dev *dev) +{ + /* Wait for a slot */ + if (RING_FULL(&dev->ring)) { + unsigned long flags; + DEFINE_WAIT(w); + local_irq_save(flags); + while (1) { + blkfront_aio_poll(dev); + if (!RING_FULL(&dev->ring)) + break; + /* Really no slot, go to sleep. */ + add_waiter(w, blkfront_queue); + local_irq_restore(flags); + schedule(); + local_irq_save(flags); + } + remove_waiter(w); + local_irq_restore(flags); + } +} + +/* Issue an aio */ +void blkfront_aio(struct blkfront_aiocb *aiocbp, int write) +{ + struct blkfront_dev *dev = aiocbp->aio_dev; + struct blkif_request *req; + RING_IDX i; + int notify; + int n, j; + uintptr_t start, end; + + // Can't io at non-sector-aligned location + ASSERT(!(aiocbp->aio_offset & (dev->info.sector_size-1))); + // Can't io non-sector-sized amounts + ASSERT(!(aiocbp->aio_nbytes & (dev->info.sector_size-1))); + // Can't io non-sector-aligned buffer + ASSERT(!((uintptr_t) aiocbp->aio_buf & (dev->info.sector_size-1))); + + start = (uintptr_t)aiocbp->aio_buf & PAGE_MASK; + end = ((uintptr_t)aiocbp->aio_buf + aiocbp->aio_nbytes + PAGE_SIZE - 1) & PAGE_MASK; + aiocbp->n = n = (end - start) / PAGE_SIZE; + + /* qemu's IDE max multsect is 16 (8KB) and SCSI max DMA was set to 32KB, + * so max 44KB can't happen */ + ASSERT(n <= BLKIF_MAX_SEGMENTS_PER_REQUEST); + + blkfront_wait_slot(dev); + i = dev->ring.req_prod_pvt; + req = RING_GET_REQUEST(&dev->ring, i); + + req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ; + req->nr_segments = n; + req->handle = dev->handle; + req->id = (uintptr_t) aiocbp; + req->sector_number = aiocbp->aio_offset / dev->info.sector_size; + + for (j = 0; j < n; j++) { + uintptr_t data = start + j * PAGE_SIZE; + if (!write) { + /* Trigger CoW if needed */ + *(char*)data = 0; + barrier(); + } + aiocbp->gref[j] = req->seg[j].gref = + gnttab_grant_access(dev->dom, virtual_to_mfn(data), write); + req->seg[j].first_sect = 0; + req->seg[j].last_sect = PAGE_SIZE / dev->info.sector_size - 1; + } + req->seg[0].first_sect = ((uintptr_t)aiocbp->aio_buf & ~PAGE_MASK) / dev->info.sector_size; + req->seg[n-1].last_sect = (((uintptr_t)aiocbp->aio_buf + aiocbp->aio_nbytes - 1) & ~PAGE_MASK) / dev->info.sector_size; + + dev->ring.req_prod_pvt = i + 1; + + wmb(); + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify); + + if(notify) notify_remote_via_evtchn(dev->evtchn); +} + +static void blkfront_aio_cb(struct blkfront_aiocb *aiocbp, int ret) +{ + aiocbp->data = (void*) 1; +} + +void blkfront_io(struct blkfront_aiocb *aiocbp, int write) +{ + unsigned long flags; + DEFINE_WAIT(w); + + ASSERT(!aiocbp->aio_cb); + aiocbp->aio_cb = blkfront_aio_cb; + blkfront_aio(aiocbp, write); + aiocbp->data = NULL; + + local_irq_save(flags); + while (1) { + blkfront_aio_poll(aiocbp->aio_dev); + if (aiocbp->data) + break; + + add_waiter(w, blkfront_queue); + local_irq_restore(flags); + schedule(); + local_irq_save(flags); + } + remove_waiter(w); + local_irq_restore(flags); +} + +static void blkfront_push_operation(struct blkfront_dev *dev, uint8_t op, uint64_t id) +{ + int i; + struct blkif_request *req; + int notify; + + blkfront_wait_slot(dev); + i = dev->ring.req_prod_pvt; + req = RING_GET_REQUEST(&dev->ring, i); + req->operation = op; + req->nr_segments = 0; + req->handle = dev->handle; + req->id = id; + /* Not needed anyway, but the backend will check it */ + req->sector_number = 0; + dev->ring.req_prod_pvt = i + 1; + wmb(); + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify); + if (notify) notify_remote_via_evtchn(dev->evtchn); +} + +void blkfront_aio_push_operation(struct blkfront_aiocb *aiocbp, uint8_t op) +{ + struct blkfront_dev *dev = aiocbp->aio_dev; + blkfront_push_operation(dev, op, (uintptr_t) aiocbp); +} + +void blkfront_sync(struct blkfront_dev *dev) +{ + unsigned long flags; + DEFINE_WAIT(w); + + if (dev->info.mode == O_RDWR) { + if (dev->info.barrier == 1) + blkfront_push_operation(dev, BLKIF_OP_WRITE_BARRIER, 0); + + if (dev->info.flush == 1) + blkfront_push_operation(dev, BLKIF_OP_FLUSH_DISKCACHE, 0); + } + + /* Note: This won't finish if another thread enqueues requests. */ + local_irq_save(flags); + while (1) { + blkfront_aio_poll(dev); + if (RING_FREE_REQUESTS(&dev->ring) == RING_SIZE(&dev->ring)) + break; + + add_waiter(w, blkfront_queue); + local_irq_restore(flags); + schedule(); + local_irq_save(flags); + } + remove_waiter(w); + local_irq_restore(flags); +} + +int blkfront_aio_poll(struct blkfront_dev *dev) +{ + RING_IDX rp, cons; + struct blkif_response *rsp; + int more; + int nr_consumed; + +moretodo: +#ifdef HAVE_LIBC + if (dev->fd != -1) { + files[dev->fd].read = 0; + mb(); /* Make sure to let the handler set read to 1 before we start looking at the ring */ + } +#endif + + rp = dev->ring.sring->rsp_prod; + rmb(); /* Ensure we see queued responses up to 'rp'. */ + cons = dev->ring.rsp_cons; + + nr_consumed = 0; + while ((cons != rp)) + { + struct blkfront_aiocb *aiocbp; + int status; + + rsp = RING_GET_RESPONSE(&dev->ring, cons); + nr_consumed++; + + aiocbp = (void*) (uintptr_t) rsp->id; + status = rsp->status; + + if (status != BLKIF_RSP_OKAY) + printk("block error %d for op %d\n", status, rsp->operation); + + switch (rsp->operation) { + case BLKIF_OP_READ: + case BLKIF_OP_WRITE: + { + int j; + + for (j = 0; j < aiocbp->n; j++) + gnttab_end_access(aiocbp->gref[j]); + + break; + } + + case BLKIF_OP_WRITE_BARRIER: + case BLKIF_OP_FLUSH_DISKCACHE: + break; + + default: + printk("unrecognized block operation %d response\n", rsp->operation); + } + + dev->ring.rsp_cons = ++cons; + /* Nota: callback frees aiocbp itself */ + if (aiocbp && aiocbp->aio_cb) + aiocbp->aio_cb(aiocbp, status ? -EIO : 0); + if (dev->ring.rsp_cons != cons) + /* We reentered, we must not continue here */ + break; + } + + RING_FINAL_CHECK_FOR_RESPONSES(&dev->ring, more); + if (more) goto moretodo; + + return nr_consumed; +} + +#ifdef HAVE_LIBC +int blkfront_open(struct blkfront_dev *dev) +{ + dev->fd = alloc_fd(FTYPE_BLK); + printk("blk_open(%s) -> %d\n", dev->nodename, dev->fd); + files[dev->fd].blk.dev = dev; + return dev->fd; +} +#endif diff --git a/extras/mini-os/console/console.c b/extras/mini-os/console/console.c new file mode 100644 index 0000000..3805943 --- /dev/null +++ b/extras/mini-os/console/console.c @@ -0,0 +1,157 @@ +/* + **************************************************************************** + * (C) 2006 - Grzegorz Milos - Cambridge University + **************************************************************************** + * + * File: console.h + * Author: Grzegorz Milos + * Changes: + * + * Date: Mar 2006 + * + * Environment: Xen Minimal OS + * Description: Console interface. + * + * Handles console I/O. Defines printk. + * + **************************************************************************** + * 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 + + +/* Copies all print output to the Xen emergency console apart + of standard dom0 handled console */ +#define USE_XEN_CONSOLE + + +/* If console not initialised the printk will be sent to xen serial line + NOTE: you need to enable verbose in xen/Rules.mk for it to work. */ +static int console_initialised = 0; + + +#ifndef HAVE_LIBC +void xencons_rx(char *buf, unsigned len, struct pt_regs *regs) +{ + if(len > 0) + { + /* Just repeat what's written */ + buf[len] = '\0'; + printk("%s", buf); + + if(buf[len-1] == '\r') + printk("\nNo console input handler.\n"); + } +} + +void xencons_tx(void) +{ + /* Do nothing, handled by _rx */ +} +#endif + + +void console_print(char *data, int length) +{ + char *curr_char, saved_char; + int part_len; + int (*ring_send_fn)(const char *data, unsigned length); + + if(!console_initialised) + ring_send_fn = xencons_ring_send_no_notify; + else + ring_send_fn = xencons_ring_send; + + for(curr_char = data; curr_char < data+length-1; curr_char++) + { + if(*curr_char == '\n') + { + saved_char = *(curr_char+1); + *(curr_char+1) = '\r'; + part_len = curr_char - data + 2; + ring_send_fn(data, part_len); + *(curr_char+1) = saved_char; + data = curr_char+1; + length -= part_len - 1; + } + } + + ring_send_fn(data, length); + + if(data[length-1] == '\n') + ring_send_fn("\r", 1); +} + +void print(int direct, const char *fmt, va_list args) +{ + static char buf[1024]; + + (void)vsnprintf(buf, sizeof(buf), fmt, args); + + if(direct) + { + (void)HYPERVISOR_console_io(CONSOLEIO_write, strlen(buf), buf); + return; + } else { +#ifndef USE_XEN_CONSOLE + if(!console_initialised) +#endif + (void)HYPERVISOR_console_io(CONSOLEIO_write, strlen(buf), buf); + + console_print(buf, strlen(buf)); + } +} + +void printk(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + print(0, fmt, args); + va_end(args); +} + +void xprintk(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + print(1, fmt, args); + va_end(args); +} +void init_console(void) +{ + printk("Initialising console ... "); + xencons_ring_init(); + console_initialised = 1; + /* This is also required to notify the daemon */ + printk("done.\n"); +} + +void fini_console(void) +{ + /* Destruct the console and get the parameters of the restarted one */ +} diff --git a/extras/mini-os/console/xencons_ring.c b/extras/mini-os/console/xencons_ring.c new file mode 100644 index 0000000..251a4f9 --- /dev/null +++ b/extras/mini-os/console/xencons_ring.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_WAIT_QUEUE_HEAD(console_queue); + +static inline struct xencons_interface *xencons_interface(void) +{ + return mfn_to_virt(start_info.console.domU.mfn); +} + +static inline void notify_daemon(void) +{ + /* Use evtchn: this is called early, before irq is set up. */ + notify_remote_via_evtchn(start_info.console.domU.evtchn); +} + +int xencons_ring_send_no_notify(const char *data, unsigned len) +{ + int sent = 0; + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + cons = intf->out_cons; + prod = intf->out_prod; + mb(); + BUG_ON((prod - cons) > sizeof(intf->out)); + + while ((sent < len) && ((prod - cons) < sizeof(intf->out))) + intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; + + wmb(); + intf->out_prod = prod; + + return sent; +} + +int xencons_ring_send(const char *data, unsigned len) +{ + int sent; + sent = xencons_ring_send_no_notify(data, len); + notify_daemon(); + + return sent; +} + + + +static void handle_input(evtchn_port_t port, struct pt_regs *regs, void *ign) +{ +#ifdef HAVE_LIBC + wake_up(&console_queue); +#else + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + + cons = intf->in_cons; + prod = intf->in_prod; + mb(); + BUG_ON((prod - cons) > sizeof(intf->in)); + + while (cons != prod) { + xencons_rx(intf->in+MASK_XENCONS_IDX(cons,intf->in), 1, regs); + cons++; + } + + mb(); + intf->in_cons = cons; + + notify_daemon(); + + xencons_tx(); +#endif +} + +#ifdef HAVE_LIBC +int xencons_ring_avail(void) +{ + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + + cons = intf->in_cons; + prod = intf->in_prod; + mb(); + BUG_ON((prod - cons) > sizeof(intf->in)); + + return prod - cons; +} + +int xencons_ring_recv(char *data, unsigned len) +{ + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + unsigned filled = 0; + + cons = intf->in_cons; + prod = intf->in_prod; + mb(); + BUG_ON((prod - cons) > sizeof(intf->in)); + + while (filled < len && cons + filled != prod) { + data[filled] = *(intf->in + MASK_XENCONS_IDX(cons + filled, intf->in)); + filled++; + } + + mb(); + intf->in_cons = cons + filled; + + notify_daemon(); + + return filled; +} +#endif + +int xencons_ring_init(void) +{ + int err; + + if (!start_info.console.domU.evtchn) + return 0; + + err = bind_evtchn(start_info.console.domU.evtchn, handle_input, + NULL); + if (err <= 0) { + printk("XEN console request chn bind failed %i\n", err); + return err; + } + unmask_evtchn(start_info.console.domU.evtchn); + + /* In case we have in-flight data after save/restore... */ + notify_daemon(); + + return 0; +} + +void xencons_resume(void) +{ + (void)xencons_ring_init(); +} + diff --git a/extras/mini-os/daytime.c b/extras/mini-os/daytime.c new file mode 100644 index 0000000..7dc0de0 --- /dev/null +++ b/extras/mini-os/daytime.c @@ -0,0 +1,67 @@ +/* + * daytime.c: a simple network service based on lwIP and mini-os + * + * Tim Deegan , July 2007 + */ + +#include +#include +#include +#include +#include + +static char message[29]; + +void run_server(void *p) +{ + struct ip_addr listenaddr = { 0 }; + struct netconn *listener; + struct netconn *session; + struct timeval tv; + err_t rc; + + start_networking(); + + if (0) { + struct ip_addr ipaddr = { htonl(0x0a000001) }; + struct ip_addr netmask = { htonl(0xff000000) }; + struct ip_addr gw = { 0 }; + networking_set_addr(&ipaddr, &netmask, &gw); + } + + tprintk("Opening connection\n"); + + listener = netconn_new(NETCONN_TCP); + tprintk("Connection at %p\n", listener); + + rc = netconn_bind(listener, &listenaddr, 13); + if (rc != ERR_OK) { + tprintk("Failed to bind connection: %i\n", rc); + return; + } + + rc = netconn_listen(listener); + if (rc != ERR_OK) { + tprintk("Failed to listen on connection: %i\n", rc); + return; + } + + while (1) { + session = netconn_accept(listener); + if (session == NULL) + continue; + + gettimeofday(&tv, NULL); + sprintf(message, "%20lu.%6.6lu\n", tv.tv_sec, tv.tv_usec); + (void) netconn_write(session, message, strlen(message), NETCONN_COPY); + (void) netconn_disconnect(session); + (void) netconn_delete(session); + } +} + + +int app_main(start_info_t *si) +{ + create_thread("server", run_server, NULL); + return 0; +} diff --git a/extras/mini-os/domain_config b/extras/mini-os/domain_config new file mode 100644 index 0000000..f3ec1d1 --- /dev/null +++ b/extras/mini-os/domain_config @@ -0,0 +1,19 @@ +# -*- mode: python; -*- +#============================================================================ +# Python configuration setup for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# You use a separate script for each domain you want to create, or +# you can set the parameters for the domain on the xm command line. +#============================================================================ + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "mini-os.gz" + +# Initial memory allocation (in megabytes) for the new domain. +memory = 32 + +# A name for your domain. All domains must have different names. +name = "Mini-OS" + +on_crash = 'destroy' diff --git a/extras/mini-os/events.c b/extras/mini-os/events.c new file mode 100644 index 0000000..95ad4e4 --- /dev/null +++ b/extras/mini-os/events.c @@ -0,0 +1,239 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: events.c + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * + * Date: Jul 2003, changes Jun 2005 + * + * Environment: Xen Minimal OS + * Description: Deals with events recieved on event channels + * + **************************************************************************** + */ + +#include +#include +#include +#include +#include + +#define NR_EVS 1024 + +/* this represents a event handler. Chaining or sharing is not allowed */ +typedef struct _ev_action_t { + evtchn_handler_t handler; + void *data; + u32 count; +} ev_action_t; + +static ev_action_t ev_actions[NR_EVS]; +void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data); + +static unsigned long bound_ports[NR_EVS/(8*sizeof(unsigned long))]; + +void unbind_all_ports(void) +{ + int i; + int cpu = 0; + shared_info_t *s = HYPERVISOR_shared_info; + vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; + + for (i = 0; i < NR_EVS; i++) + { + if (i == start_info.console.domU.evtchn || + i == start_info.store_evtchn) + continue; + if (test_and_clear_bit(i, bound_ports)) + { + struct evtchn_close close; + printk("port %d still bound!\n", i); + mask_evtchn(i); + close.port = i; + HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); + clear_evtchn(i); + } + } + vcpu_info->evtchn_upcall_pending = 0; + vcpu_info->evtchn_pending_sel = 0; +} + +/* + * Demux events to different handlers. + */ +int do_event(evtchn_port_t port, struct pt_regs *regs) +{ + ev_action_t *action; + + clear_evtchn(port); + + if (port >= NR_EVS) { + printk("Port number too large: %d\n", port); + return 1; + } + + action = &ev_actions[port]; + action->count++; + + /* call the handler */ + action->handler(port, regs, action->data); + + return 1; + +} + +evtchn_port_t bind_evtchn(evtchn_port_t port, evtchn_handler_t handler, + void *data) +{ + if(ev_actions[port].handler != default_handler) + printk("WARN: Handler for port %d already registered, replacing\n", + port); + + ev_actions[port].data = data; + wmb(); + ev_actions[port].handler = handler; + set_bit(port, bound_ports); + + return port; +} + +void unbind_evtchn(evtchn_port_t port ) +{ + struct evtchn_close close; + + if (ev_actions[port].handler == default_handler) + printk("WARN: No handler for port %d when unbinding\n", port); + mask_evtchn(port); + clear_evtchn(port); + + ev_actions[port].handler = default_handler; + wmb(); + ev_actions[port].data = NULL; + clear_bit(port, bound_ports); + + close.port = port; + HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); +} + +evtchn_port_t bind_virq(uint32_t virq, evtchn_handler_t handler, void *data) +{ + evtchn_bind_virq_t op; + + /* Try to bind the virq to a port */ + op.virq = virq; + op.vcpu = smp_processor_id(); + + if ( HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &op) != 0 ) + { + printk("Failed to bind virtual IRQ %d\n", virq); + return -1; + } + bind_evtchn(op.port, handler, data); + return op.port; +} + +evtchn_port_t bind_pirq(uint32_t pirq, int will_share, evtchn_handler_t handler, void *data) +{ + evtchn_bind_pirq_t op; + + /* Try to bind the pirq to a port */ + op.pirq = pirq; + op.flags = will_share ? BIND_PIRQ__WILL_SHARE : 0; + + if ( HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &op) != 0 ) + { + printk("Failed to bind physical IRQ %d\n", pirq); + return -1; + } + bind_evtchn(op.port, handler, data); + return op.port; +} + +#if defined(__x86_64__) +char irqstack[2 * STACK_SIZE]; + +static struct pda +{ + int irqcount; /* offset 0 (used in x86_64.S) */ + char *irqstackptr; /* 8 */ +} cpu0_pda; +#endif + +/* + * Initially all events are without a handler and disabled + */ +void init_events(void) +{ + int i; +#if defined(__x86_64__) + asm volatile("movl %0,%%fs ; movl %0,%%gs" :: "r" (0)); + wrmsrl(0xc0000101, &cpu0_pda); /* 0xc0000101 is MSR_GS_BASE */ + cpu0_pda.irqcount = -1; + cpu0_pda.irqstackptr = (void*) (((unsigned long)irqstack + 2 * STACK_SIZE) & ~(STACK_SIZE - 1)); +#endif + /* initialize event handler */ + for ( i = 0; i < NR_EVS; i++ ) + { + ev_actions[i].handler = default_handler; + mask_evtchn(i); + } +} + +void fini_events(void) +{ + /* Dealloc all events */ + unbind_all_ports(); +#if defined(__x86_64__) + wrmsrl(0xc0000101, NULL); /* 0xc0000101 is MSR_GS_BASE */ +#endif +} + +void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore) +{ + printk("[Port %d] - event received\n", port); +} + +/* Create a port available to the pal for exchanging notifications. + Returns the result of the hypervisor call. */ + +/* Unfortunate confusion of terminology: the port is unbound as far + as Xen is concerned, but we automatically bind a handler to it + from inside mini-os. */ + +int evtchn_alloc_unbound(domid_t pal, evtchn_handler_t handler, + void *data, evtchn_port_t *port) +{ + int err; + evtchn_alloc_unbound_t op; + op.dom = DOMID_SELF; + op.remote_dom = pal; + err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); + if (err) + return err; + *port = bind_evtchn(op.port, handler, data); + return err; +} + +/* Connect to a port so as to allow the exchange of notifications with + the pal. Returns the result of the hypervisor call. */ + +int evtchn_bind_interdomain(domid_t pal, evtchn_port_t remote_port, + evtchn_handler_t handler, void *data, + evtchn_port_t *local_port) +{ + int err; + evtchn_port_t port; + evtchn_bind_interdomain_t op; + op.remote_dom = pal; + op.remote_port = remote_port; + err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, &op); + if (err) + return err; + port = op.local_port; + *local_port = bind_evtchn(port, handler, data); + return err; +} diff --git a/extras/mini-os/fbfront.c b/extras/mini-os/fbfront.c new file mode 100644 index 0000000..fe227d6 --- /dev/null +++ b/extras/mini-os/fbfront.c @@ -0,0 +1,597 @@ +/* + * Frame Buffer + Keyboard driver for Mini-OS. + * Samuel Thibault , 2008 + * Based on blkfront.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_WAIT_QUEUE_HEAD(kbdfront_queue); + + + + + + +struct kbdfront_dev { + domid_t dom; + + struct xenkbd_page *page; + evtchn_port_t evtchn; + + char *nodename; + char *backend; + + xenbus_event_queue events; + +#ifdef HAVE_LIBC + int fd; +#endif +}; + +void kbdfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ +#ifdef HAVE_LIBC + struct kbdfront_dev *dev = data; + int fd = dev->fd; + + if (fd != -1) + files[fd].read = 1; +#endif + wake_up(&kbdfront_queue); +} + +static void free_kbdfront(struct kbdfront_dev *dev) +{ + mask_evtchn(dev->evtchn); + + free(dev->backend); + + free_page(dev->page); + + unbind_evtchn(dev->evtchn); + + free(dev->nodename); + free(dev); +} + +struct kbdfront_dev *init_kbdfront(char *_nodename, int abs_pointer) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + struct xenkbd_page *s; + int retry=0; + char* msg; + char* nodename = _nodename ? _nodename : "device/vkbd/0"; + struct kbdfront_dev *dev; + + char path[strlen(nodename) + 1 + 10 + 1]; + + printk("******************* KBDFRONT for %s **********\n\n\n", nodename); + + dev = malloc(sizeof(*dev)); + dev->nodename = strdup(nodename); +#ifdef HAVE_LIBC + dev->fd = -1; +#endif + + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dev->dom = xenbus_read_integer(path); + evtchn_alloc_unbound(dev->dom, kbdfront_handler, dev, &dev->evtchn); + + dev->page = s = (struct xenkbd_page*) alloc_page(); + memset(s,0,PAGE_SIZE); + + dev->events = NULL; + + s->in_cons = s->in_prod = 0; + s->out_cons = s->out_prod = 0; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "page-ref","%u", virt_to_mfn(s)); + if (err) { + message = "writing page-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + if (abs_pointer) { + err = xenbus_printf(xbt, nodename, "request-abs-pointer", "1"); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + } + + err = xenbus_printf(xbt, nodename, "state", "%u", 3); /* initialized */ + if (err) + printk("error writing initialized: %s\n", err); + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + goto error; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + if (msg) { + printk("Error %s when reading the backend path %s\n", msg, path); + goto error; + } + + printk("backend at %s\n", dev->backend); + + { + char path[strlen(dev->backend) + 1 + 6 + 1]; + + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path_token(XBT_NIL, path, path, &dev->events); + + xenbus_wait_for_value(path, "4", &dev->events); + + printk("%s connected\n", dev->backend); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 4); /* connected */ + } + unmask_evtchn(dev->evtchn); + + printk("************************** KBDFRONT\n"); + + return dev; +error: + free_kbdfront(dev); + return NULL; +} + +int kbdfront_receive(struct kbdfront_dev *dev, union xenkbd_in_event *buf, int n) +{ + struct xenkbd_page *page = dev->page; + uint32_t prod, cons; + int i; + +#ifdef HAVE_LIBC + if (dev->fd != -1) { + files[dev->fd].read = 0; + mb(); /* Make sure to let the handler set read to 1 before we start looking at the ring */ + } +#endif + + prod = page->in_prod; + + if (prod == page->in_cons) + return 0; + + rmb(); /* ensure we see ring contents up to prod */ + + for (i = 0, cons = page->in_cons; i < n && cons != prod; i++, cons++) + memcpy(buf + i, &XENKBD_IN_RING_REF(page, cons), sizeof(*buf)); + + mb(); /* ensure we got ring contents */ + page->in_cons = cons; + notify_remote_via_evtchn(dev->evtchn); + +#ifdef HAVE_LIBC + if (cons != prod && dev->fd != -1) + /* still some events to read */ + files[dev->fd].read = 1; +#endif + + return i; +} + + +void shutdown_kbdfront(struct kbdfront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + printk("close kbd: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path, "5", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path, "6", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 1); + // does not work yet. + //xenbus_wait_for_value(path, "2", &dev->events); + + xenbus_unwatch_path(XBT_NIL, path); + + snprintf(path, sizeof(path), "%s/page-ref", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/event-channel", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/request-abs-pointer", nodename); + xenbus_rm(XBT_NIL, path); + + free_kbdfront(dev); +} + +#ifdef HAVE_LIBC +int kbdfront_open(struct kbdfront_dev *dev) +{ + dev->fd = alloc_fd(FTYPE_KBD); + printk("kbd_open(%s) -> %d\n", dev->nodename, dev->fd); + files[dev->fd].kbd.dev = dev; + return dev->fd; +} +#endif + + + + + +DECLARE_WAIT_QUEUE_HEAD(fbfront_queue); + + + + + + +struct fbfront_dev { + domid_t dom; + + struct xenfb_page *page; + evtchn_port_t evtchn; + + char *nodename; + char *backend; + int request_update; + + int width; + int height; + int depth; + int stride; + int mem_length; + int offset; + + xenbus_event_queue events; + +#ifdef HAVE_LIBC + int fd; +#endif +}; + +void fbfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ +#ifdef HAVE_LIBC + struct fbfront_dev *dev = data; + int fd = dev->fd; + + if (fd != -1) + files[fd].read = 1; +#endif + wake_up(&fbfront_queue); +} + +static void free_fbfront(struct fbfront_dev *dev) +{ + mask_evtchn(dev->evtchn); + + free(dev->backend); + + free_page(dev->page); + + unbind_evtchn(dev->evtchn); + + free(dev->nodename); + free(dev); +} + +int fbfront_receive(struct fbfront_dev *dev, union xenfb_in_event *buf, int n) +{ + struct xenfb_page *page = dev->page; + uint32_t prod, cons; + int i; + +#ifdef HAVE_LIBC + if (dev->fd != -1) { + files[dev->fd].read = 0; + mb(); /* Make sure to let the handler set read to 1 before we start looking at the ring */ + } +#endif + + prod = page->in_prod; + + if (prod == page->in_cons) + return 0; + + rmb(); /* ensure we see ring contents up to prod */ + + for (i = 0, cons = page->in_cons; i < n && cons != prod; i++, cons++) + memcpy(buf + i, &XENFB_IN_RING_REF(page, cons), sizeof(*buf)); + + mb(); /* ensure we got ring contents */ + page->in_cons = cons; + notify_remote_via_evtchn(dev->evtchn); + +#ifdef HAVE_LIBC + if (cons != prod && dev->fd != -1) + /* still some events to read */ + files[dev->fd].read = 1; +#endif + + return i; +} + +struct fbfront_dev *init_fbfront(char *_nodename, unsigned long *mfns, int width, int height, int depth, int stride, int n) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + struct xenfb_page *s; + int retry=0; + char* msg; + int i, j; + struct fbfront_dev *dev; + int max_pd; + unsigned long mapped; + char* nodename = _nodename ? _nodename : "device/vfb/0"; + + char path[strlen(nodename) + 1 + 10 + 1]; + + printk("******************* FBFRONT for %s **********\n\n\n", nodename); + + dev = malloc(sizeof(*dev)); + dev->nodename = strdup(nodename); +#ifdef HAVE_LIBC + dev->fd = -1; +#endif + + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dev->dom = xenbus_read_integer(path); + evtchn_alloc_unbound(dev->dom, fbfront_handler, dev, &dev->evtchn); + + dev->page = s = (struct xenfb_page*) alloc_page(); + memset(s,0,PAGE_SIZE); + + s->in_cons = s->in_prod = 0; + s->out_cons = s->out_prod = 0; + dev->width = s->width = width; + dev->height = s->height = height; + dev->depth = s->depth = depth; + dev->stride = s->line_length = stride; + dev->mem_length = s->mem_length = n * PAGE_SIZE; + dev->offset = 0; + dev->events = NULL; + + max_pd = sizeof(s->pd) / sizeof(s->pd[0]); + mapped = 0; + + for (i = 0; mapped < n && i < max_pd; i++) { + unsigned long *pd = (unsigned long *) alloc_page(); + for (j = 0; mapped < n && j < PAGE_SIZE / sizeof(unsigned long); j++) + pd[j] = mfns[mapped++]; + for ( ; j < PAGE_SIZE / sizeof(unsigned long); j++) + pd[j] = 0; + s->pd[i] = virt_to_mfn(pd); + } + for ( ; i < max_pd; i++) + s->pd[i] = 0; + + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "page-ref","%u", virt_to_mfn(s)); + if (err) { + message = "writing page-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "protocol", "%s", + XEN_IO_PROTO_ABI_NATIVE); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "feature-update", "1"); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "state", "%u", 3); /* initialized */ + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + goto error; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + if (msg) { + printk("Error %s when reading the backend path %s\n", msg, path); + goto error; + } + + printk("backend at %s\n", dev->backend); + + { + char path[strlen(dev->backend) + 1 + 14 + 1]; + + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path_token(XBT_NIL, path, path, &dev->events); + + xenbus_wait_for_value(path, "4", &dev->events); + + printk("%s connected\n", dev->backend); + + snprintf(path, sizeof(path), "%s/request-update", dev->backend); + dev->request_update = xenbus_read_integer(path); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 4); /* connected */ + } + unmask_evtchn(dev->evtchn); + + printk("************************** FBFRONT\n"); + + return dev; + +error: + free_fbfront(dev); + return NULL; +} + +static void fbfront_out_event(struct fbfront_dev *dev, union xenfb_out_event *event) +{ + struct xenfb_page *page = dev->page; + uint32_t prod; + DEFINE_WAIT(w); + + add_waiter(w, fbfront_queue); + while (page->out_prod - page->out_cons == XENFB_OUT_RING_LEN) + schedule(); + remove_waiter(w); + + prod = page->out_prod; + mb(); /* ensure ring space available */ + XENFB_OUT_RING_REF(page, prod) = *event; + wmb(); /* ensure ring contents visible */ + page->out_prod = prod + 1; + notify_remote_via_evtchn(dev->evtchn); +} + +void fbfront_update(struct fbfront_dev *dev, int x, int y, int width, int height) +{ + struct xenfb_update update; + + if (dev->request_update <= 0) + return; + + if (x < 0) { + width += x; + x = 0; + } + if (x + width > dev->width) + width = dev->width - x; + + if (y < 0) { + height += y; + y = 0; + } + if (y + height > dev->height) + height = dev->height - y; + + if (width <= 0 || height <= 0) + return; + + update.type = XENFB_TYPE_UPDATE; + update.x = x; + update.y = y; + update.width = width; + update.height = height; + fbfront_out_event(dev, (union xenfb_out_event *) &update); +} + +void fbfront_resize(struct fbfront_dev *dev, int width, int height, int stride, int depth, int offset) +{ + struct xenfb_resize resize; + + resize.type = XENFB_TYPE_RESIZE; + dev->width = resize.width = width; + dev->height = resize.height = height; + dev->stride = resize.stride = stride; + dev->depth = resize.depth = depth; + dev->offset = resize.offset = offset; + fbfront_out_event(dev, (union xenfb_out_event *) &resize); +} + +void shutdown_fbfront(struct fbfront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + printk("close fb: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path, "5", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path, "6", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 1); + // does not work yet + //xenbus_wait_for_value(path, "2", &dev->events); + + xenbus_unwatch_path(XBT_NIL, path); + + snprintf(path, sizeof(path), "%s/page-ref", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/event-channel", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/protocol", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/feature-update", nodename); + xenbus_rm(XBT_NIL, path); + + unbind_evtchn(dev->evtchn); + + free_fbfront(dev); +} + +#ifdef HAVE_LIBC +int fbfront_open(struct fbfront_dev *dev) +{ + dev->fd = alloc_fd(FTYPE_FB); + printk("fb_open(%s) -> %d\n", dev->nodename, dev->fd); + files[dev->fd].fb.dev = dev; + return dev->fd; +} +#endif + diff --git a/extras/mini-os/fs-front.c b/extras/mini-os/fs-front.c new file mode 100644 index 0000000..1cecab0 --- /dev/null +++ b/extras/mini-os/fs-front.c @@ -0,0 +1,1264 @@ +/****************************************************************************** + * fs-front.c + * + * Frontend driver for FS split device driver. + * + * Copyright (c) 2007, Grzegorz Milos, . + * + * 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. + */ + +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define preempt_disable() +#define preempt_enable() +#define cmpxchg(p,o,n) synch_cmpxchg(p,o,n) + + +#ifdef FS_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=fs-front.c, line=%d) " _f "\n", __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + + +struct fs_request; +struct fs_import *fs_import; +void *alloc_buffer_page(struct fs_request *req, domid_t domid, grant_ref_t *gref); +void free_buffer_page(struct fs_request *req); + +/******************************************************************************/ +/* RING REQUEST/RESPONSES HANDLING */ +/******************************************************************************/ + +struct fs_request +{ + void *private1; /* Specific to request type */ + void *private2; + struct thread *thread; /* Thread blocked on this request */ + struct fsif_response shadow_rsp; /* Response copy writen by the + interrupt handler */ +}; + +struct fs_rw_gnts +{ + /* TODO 16 bit? */ + int count; + grant_ref_t grefs[FSIF_NR_READ_GNTS]; + void *pages[FSIF_NR_READ_GNTS]; +}; + +/* Ring operations: + * FSIF ring is used differently to Linux-like split devices. This stems from + * the fact that no I/O request queue is present. The use of some of the macros + * defined in ring.h is not allowed, in particular: + * RING_PUSH_REQUESTS_AND_CHECK_NOTIFY cannot be used. + * + * The protocol used for FSIF ring is described below: + * + * In order to reserve a request the frontend: + * a) saves current frontend_ring->req_prod_pvt into a local variable + * b) checks that there are free request using the local req_prod_pvt + * c) tries to reserve the request using cmpxchg on frontend_ring->req_prod_pvt + * if cmpxchg fails, it means that someone reserved the request, start from + * a) + * + * In order to commit a request to the shared ring: + * a) cmpxchg shared_ring->req_prod from local req_prod_pvt to req_prod_pvt+1 + * Loop if unsuccessful. + * NOTE: Request should be commited to the shared ring as quickly as possible, + * because otherwise other threads might busy loop trying to commit next + * requests. It also follows that preemption should be disabled, if + * possible, for the duration of the request construction. + */ + +/* Number of free requests (for use on front side only). */ +#define FS_RING_FREE_REQUESTS(_r, _req_prod_pvt) \ + (RING_SIZE(_r) - (_req_prod_pvt - (_r)->rsp_cons)) + + + +static RING_IDX reserve_fsif_request(struct fs_import *import) +{ + RING_IDX idx; + + down(&import->reqs_sem); + preempt_disable(); +again: + /* We will attempt to reserve slot idx */ + idx = import->ring.req_prod_pvt; + ASSERT (FS_RING_FREE_REQUESTS(&import->ring, idx)); + /* Attempt to reserve */ + if(cmpxchg(&import->ring.req_prod_pvt, idx, idx+1) != idx) + goto again; + + return idx; +} + +static void commit_fsif_request(struct fs_import *import, RING_IDX idx) +{ + while(cmpxchg(&import->ring.sring->req_prod, idx, idx+1) != idx) + { + printk("Failed to commit a request: req_prod=%d, idx=%d\n", + import->ring.sring->req_prod, idx); + } + preempt_enable(); + + /* NOTE: we cannot do anything clever about rsp_event, to hold off + * notifications, because we don't know if we are a single request (in which + * case we have to notify always), or a part of a larger request group + * (when, in some cases, notification isn't required) */ + notify_remote_via_evtchn(import->local_port); +} + + + +static inline void add_id_to_freelist(unsigned int id,unsigned short* freelist) +{ + unsigned int old_id, new_id; + +again: + old_id = freelist[0]; + /* Note: temporal inconsistency, since freelist[0] can be changed by someone + * else, but we are a sole owner of freelist[id + 1], it's OK. */ + freelist[id + 1] = old_id; + new_id = id; + if(cmpxchg(&freelist[0], old_id, new_id) != old_id) + { + printk("Cmpxchg on freelist add failed.\n"); + goto again; + } +} + +/* always call reserve_fsif_request(import) before this, to protect from + * depletion. */ +static inline unsigned short get_id_from_freelist(unsigned short* freelist) +{ + unsigned int old_id, new_id; + +again: + old_id = freelist[0]; + new_id = freelist[old_id + 1]; + if(cmpxchg(&freelist[0], old_id, new_id) != old_id) + { + printk("Cmpxchg on freelist remove failed.\n"); + goto again; + } + + return old_id; +} + +/******************************************************************************/ +/* END OF RING REQUEST/RESPONSES HANDLING */ +/******************************************************************************/ + + + +/******************************************************************************/ +/* INDIVIDUAL FILE OPERATIONS */ +/******************************************************************************/ +int fs_open(struct fs_import *import, char *file) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + grant_ref_t gref; + void *buffer; + RING_IDX back_req_id; + struct fsif_request *req; + int fd; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_open call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + buffer = alloc_buffer_page(fsr, import->dom_id, &gref); + DEBUG("gref id=%d\n", gref); + fsr->thread = current; + sprintf(buffer, "%s", file); + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FILE_OPEN; + req->id = priv_req_id; + req->u.fopen.gref = gref; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + fd = (int)fsr->shadow_rsp.ret_val; + DEBUG("The following FD returned: %d\n", fd); + free_buffer_page(fsr); + add_id_to_freelist(priv_req_id, import->freelist); + + return fd; +} + +int fs_close(struct fs_import *import, int fd) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_close call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + fsr->thread = current; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FILE_CLOSE; + req->id = priv_req_id; + req->u.fclose.fd = fd; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("Close returned: %d\n", ret); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +ssize_t fs_read(struct fs_import *import, int fd, void *buf, + ssize_t len, ssize_t offset) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + struct fs_rw_gnts gnts; + RING_IDX back_req_id; + struct fsif_request *req; + ssize_t ret; + int i; + + BUG_ON(len > PAGE_SIZE * FSIF_NR_READ_GNTS); + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_read call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FILE_READ; + req->id = priv_req_id; + req->u.fread.fd = fd; + req->u.fread.len = len; + req->u.fread.offset = offset; + + + ASSERT(len > 0); + gnts.count = ((len - 1) / PAGE_SIZE) + 1; + for(i=0; idom_id, + virt_to_mfn(gnts.pages[i]), + 0); + memset(gnts.pages[i], 0, PAGE_SIZE); + req->u.fread.grefs[i] = gnts.grefs[i]; + } + fsr->thread = current; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (ssize_t)fsr->shadow_rsp.ret_val; + DEBUG("The following ret value returned %d\n", ret); + if(ret > 0) + { + ssize_t to_copy = ret, current_copy; + for(i=0; i PAGE_SIZE ? PAGE_SIZE : to_copy; + if(current_copy > 0) + memcpy(buf, gnts.pages[i], current_copy); + to_copy -= current_copy; + buf = (char*) buf + current_copy; + free_page(gnts.pages[i]); + } + } + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +ssize_t fs_write(struct fs_import *import, int fd, void *buf, + ssize_t len, ssize_t offset) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + struct fs_rw_gnts gnts; + RING_IDX back_req_id; + struct fsif_request *req; + ssize_t ret, to_copy; + int i; + + BUG_ON(len > PAGE_SIZE * FSIF_NR_WRITE_GNTS); + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_read call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FILE_WRITE; + req->id = priv_req_id; + req->u.fwrite.fd = fd; + req->u.fwrite.len = len; + req->u.fwrite.offset = offset; + + ASSERT(len > 0); + gnts.count = ((len - 1) / PAGE_SIZE) + 1; + to_copy = len; + for(i=0; i PAGE_SIZE ? PAGE_SIZE : to_copy); + gnts.pages[i] = (void *)alloc_page(); + gnts.grefs[i] = gnttab_grant_access(import->dom_id, + virt_to_mfn(gnts.pages[i]), + 0); + memcpy(gnts.pages[i], buf, current_copy); + if(current_copy < PAGE_SIZE) + memset((char *)gnts.pages[i] + current_copy, + 0, + PAGE_SIZE - current_copy); + req->u.fwrite.grefs[i] = gnts.grefs[i]; + to_copy -= current_copy; + buf = (char*) buf + current_copy; + } + fsr->thread = current; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (ssize_t)fsr->shadow_rsp.ret_val; + DEBUG("The following ret value returned %d\n", ret); + for(i=0; ifreelist); + + return ret; +} + +int fs_stat(struct fs_import *import, + int fd, + struct fsif_stat_response *stat) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_stat call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + fsr->thread = current; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_STAT; + req->id = priv_req_id; + req->u.fstat.fd = fd; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("Following ret from fstat: %d\n", ret); + memcpy(stat, + &fsr->shadow_rsp.fstat, + sizeof(struct fsif_stat_response)); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +int fs_truncate(struct fs_import *import, + int fd, + int64_t length) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_truncate call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + fsr->thread = current; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FILE_TRUNCATE; + req->id = priv_req_id; + req->u.ftruncate.fd = fd; + req->u.ftruncate.length = length; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("Following ret from ftruncate: %d\n", ret); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +int fs_remove(struct fs_import *import, char *file) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + grant_ref_t gref; + void *buffer; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_open call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + buffer = alloc_buffer_page(fsr, import->dom_id, &gref); + DEBUG("gref=%d\n", gref); + fsr->thread = current; + sprintf(buffer, "%s", file); + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_REMOVE; + req->id = priv_req_id; + req->u.fremove.gref = gref; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("The following ret: %d\n", ret); + free_buffer_page(fsr); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + + +int fs_rename(struct fs_import *import, + char *old_file_name, + char *new_file_name) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + grant_ref_t gref; + void *buffer; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + char old_header[] = "old: "; + char new_header[] = "new: "; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_open call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + buffer = alloc_buffer_page(fsr, import->dom_id, &gref); + DEBUG("gref=%d\n", gref); + fsr->thread = current; + sprintf(buffer, "%s%s%c%s%s", + old_header, old_file_name, '\0', new_header, new_file_name); + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_RENAME; + req->id = priv_req_id; + req->u.frename.gref = gref; + req->u.frename.old_name_offset = strlen(old_header); + req->u.frename.new_name_offset = strlen(old_header) + + strlen(old_file_name) + + strlen(new_header) + + 1 /* Accouning for the additional + end of string character */; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("The following ret: %d\n", ret); + free_buffer_page(fsr); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +int fs_create(struct fs_import *import, char *name, + int8_t directory, int32_t mode) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + grant_ref_t gref; + void *buffer; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_create call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + buffer = alloc_buffer_page(fsr, import->dom_id, &gref); + DEBUG("gref=%d\n", gref); + fsr->thread = current; + sprintf(buffer, "%s", name); + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_CREATE; + req->id = priv_req_id; + req->u.fcreate.gref = gref; + req->u.fcreate.directory = directory; + req->u.fcreate.mode = mode; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("The following ret: %d\n", ret); + free_buffer_page(fsr); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +char** fs_list(struct fs_import *import, char *name, + int32_t offset, int32_t *nr_files, int *has_more) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + grant_ref_t gref; + void *buffer; + RING_IDX back_req_id; + struct fsif_request *req; + char **files, *current_file; + int i; + + DEBUG("Different masks: NR_FILES=(%llx, %d), ERROR=(%llx, %d), HAS_MORE(%llx, %d)\n", + NR_FILES_MASK, NR_FILES_SHIFT, ERROR_MASK, ERROR_SHIFT, HAS_MORE_FLAG, HAS_MORE_SHIFT); + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_list call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + buffer = alloc_buffer_page(fsr, import->dom_id, &gref); + DEBUG("gref=%d\n", gref); + fsr->thread = current; + sprintf(buffer, "%s", name); + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_DIR_LIST; + req->id = priv_req_id; + req->u.flist.gref = gref; + req->u.flist.offset = offset; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + *nr_files = (fsr->shadow_rsp.ret_val & NR_FILES_MASK) >> NR_FILES_SHIFT; + files = NULL; + if(*nr_files <= 0) goto exit; + files = malloc(sizeof(char*) * (*nr_files)); + current_file = buffer; + for(i=0; i<*nr_files; i++) + { + files[i] = strdup(current_file); + current_file += strlen(current_file) + 1; + } + if(has_more != NULL) + *has_more = fsr->shadow_rsp.ret_val & HAS_MORE_FLAG; + free_buffer_page(fsr); + add_id_to_freelist(priv_req_id, import->freelist); +exit: + return files; +} + +int fs_chmod(struct fs_import *import, int fd, int32_t mode) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_chmod call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + fsr->thread = current; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_CHMOD; + req->id = priv_req_id; + req->u.fchmod.fd = fd; + req->u.fchmod.mode = mode; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("The following returned: %d\n", ret); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +int64_t fs_space(struct fs_import *import, char *location) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + grant_ref_t gref; + void *buffer; + RING_IDX back_req_id; + struct fsif_request *req; + int64_t ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_space is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + buffer = alloc_buffer_page(fsr, import->dom_id, &gref); + DEBUG("gref=%d\n", gref); + fsr->thread = current; + sprintf(buffer, "%s", location); + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FS_SPACE; + req->id = priv_req_id; + req->u.fspace.gref = gref; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int64_t)fsr->shadow_rsp.ret_val; + DEBUG("The following returned: %lld\n", ret); + free_buffer_page(fsr); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + +int fs_sync(struct fs_import *import, int fd) +{ + struct fs_request *fsr; + unsigned short priv_req_id; + RING_IDX back_req_id; + struct fsif_request *req; + int ret; + + /* Prepare request for the backend */ + back_req_id = reserve_fsif_request(import); + DEBUG("Backend request id=%d\n", back_req_id); + + /* Prepare our private request structure */ + priv_req_id = get_id_from_freelist(import->freelist); + DEBUG("Request id for fs_sync call is: %d\n", priv_req_id); + fsr = &import->requests[priv_req_id]; + fsr->thread = current; + + req = RING_GET_REQUEST(&import->ring, back_req_id); + req->type = REQ_FILE_SYNC; + req->id = priv_req_id; + req->u.fsync.fd = fd; + + /* Set blocked flag before commiting the request, thus avoiding missed + * response race */ + block(current); + commit_fsif_request(import, back_req_id); + schedule(); + + /* Read the response */ + ret = (int)fsr->shadow_rsp.ret_val; + DEBUG("Close returned: %d\n", ret); + add_id_to_freelist(priv_req_id, import->freelist); + + return ret; +} + + +/******************************************************************************/ +/* END OF INDIVIDUAL FILE OPERATIONS */ +/******************************************************************************/ + +void *alloc_buffer_page(struct fs_request *req, domid_t domid, grant_ref_t *gref) +{ + void *page; + + page = (void *)alloc_page(); + *gref = gnttab_grant_access(domid, virt_to_mfn(page), 0); + req->private1 = page; + req->private2 = (void *)(uintptr_t)(*gref); + + return page; +} + +void free_buffer_page(struct fs_request *req) +{ + gnttab_end_access((grant_ref_t)(uintptr_t)req->private2); + free_page(req->private1); +} + +static void fsfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ + struct fs_import *import = (struct fs_import*)data; + static int in_irq = 0; + RING_IDX cons, rp; + int more; + + /* Check for non-reentrance */ + BUG_ON(in_irq); + in_irq = 1; + + DEBUG("Event from import [%d:%d].\n", import->dom_id, import->export_id); +moretodo: + rp = import->ring.sring->rsp_prod; + rmb(); /* Ensure we see queued responses up to 'rp'. */ + cons = import->ring.rsp_cons; + while (cons != rp) + { + struct fsif_response *rsp; + struct fs_request *req; + + rsp = RING_GET_RESPONSE(&import->ring, cons); + DEBUG("Response at idx=%d to request id=%d, ret_val=%lx\n", + cons, rsp->id, rsp->ret_val); + req = &import->requests[rsp->id]; + memcpy(&req->shadow_rsp, rsp, sizeof(struct fsif_response)); + DEBUG("Waking up: %s\n", req->thread->name); + wake(req->thread); + + cons++; + up(&import->reqs_sem); + } + + import->ring.rsp_cons = rp; + RING_FINAL_CHECK_FOR_RESPONSES(&import->ring, more); + if(more) goto moretodo; + + in_irq = 0; +} + +/* Small utility function to figure out our domain id */ +static domid_t get_self_id(void) +{ + char *dom_id; + domid_t ret; + + BUG_ON(xenbus_read(XBT_NIL, "domid", &dom_id)); + sscanf(dom_id, "%d", &ret); + + return ret; +} + +static void alloc_request_table(struct fs_import *import) +{ + struct fs_request *requests; + int i; + + BUG_ON(import->nr_entries <= 0); + printk("Allocating request array for import %d, nr_entries = %d.\n", + import->import_id, import->nr_entries); + requests = xmalloc_array(struct fs_request, import->nr_entries); + import->freelist = xmalloc_array(unsigned short, import->nr_entries + 1); + memset(import->freelist, 0, sizeof(unsigned short) * (import->nr_entries + 1)); + for(i=0; inr_entries; i++) + add_id_to_freelist(i, import->freelist); + import->requests = requests; +} + + +/******************************************************************************/ +/* FS TESTS */ +/******************************************************************************/ + + +void test_fs_import(void *data) +{ + struct fs_import *import = (struct fs_import *)data; + int ret, fd, i, repeat_count; + int32_t nr_files; + char buffer[1024]; + ssize_t offset; + char **files; + long ret64; + struct fsif_stat_response stat; + + repeat_count = 10; + /* Sleep for 1s and then try to open a file */ + msleep(1000); +again: + ret = fs_create(import, "mini-os-created-directory", 1, 0777); + printk("Directory create: %d\n", ret); + + sprintf(buffer, "mini-os-created-directory/mini-os-created-file-%d", + repeat_count); + ret = fs_create(import, buffer, 0, 0666); + printk("File create: %d\n", ret); + + fd = fs_open(import, buffer); + printk("File descriptor: %d\n", fd); + if(fd < 0) return; + + offset = 0; + for(i=0; i<10; i++) + { + sprintf(buffer, "Current time is: %lld\n", NOW()); + ret = fs_write(import, fd, buffer, strlen(buffer), offset); + printk("Writen current time (%d)\n", ret); + if(ret < 0) + return; + offset += ret; + } + ret = fs_stat(import, fd, &stat); + printk("Ret after stat: %d\n", ret); + printk(" st_mode=%o\n", stat.stat_mode); + printk(" st_uid =%d\n", stat.stat_uid); + printk(" st_gid =%d\n", stat.stat_gid); + printk(" st_size=%ld\n", stat.stat_size); + printk(" st_atime=%ld\n", stat.stat_atime); + printk(" st_mtime=%ld\n", stat.stat_mtime); + printk(" st_ctime=%ld\n", stat.stat_ctime); + + ret = fs_close(import, fd); + printk("Closed fd: %d, ret=%d\n", fd, ret); + + printk("Listing files in /\n"); + files = fs_list(import, "/", 0, &nr_files, NULL); + for(i=0; i> 20)); + repeat_count--; + if(repeat_count > 0) + goto again; + +} + +#if 0 +// char *content = (char *)alloc_page(); + int fd, ret; +// int read; + char write_string[] = "\"test data written from minios\""; + struct fsif_stat_response stat; + char **files; + int32_t nr_files, i; + int64_t ret64; + + + fd = fs_open(import, "test-export-file"); +// read = fs_read(import, fd, content, PAGE_SIZE, 0); +// printk("Read: %d bytes\n", read); +// content[read] = '\0'; +// printk("Value: %s\n", content); + ret = fs_write(import, fd, write_string, strlen(write_string), 0); + printk("Ret after write: %d\n", ret); + ret = fs_stat(import, fd, &stat); + printk("Ret after stat: %d\n", ret); + printk(" st_mode=%o\n", stat.stat_mode); + printk(" st_uid =%d\n", stat.stat_uid); + printk(" st_gid =%d\n", stat.stat_gid); + printk(" st_size=%ld\n", stat.stat_size); + printk(" st_atime=%ld\n", stat.stat_atime); + printk(" st_mtime=%ld\n", stat.stat_mtime); + printk(" st_ctime=%ld\n", stat.stat_ctime); + ret = fs_truncate(import, fd, 30); + printk("Ret after truncate: %d\n", ret); + ret = fs_remove(import, "test-to-remove/test-file"); + printk("Ret after remove: %d\n", ret); + ret = fs_remove(import, "test-to-remove"); + printk("Ret after remove: %d\n", ret); + ret = fs_chmod(import, fd, 0700); + printk("Ret after chmod: %d\n", ret); + ret = fs_sync(import, fd); + printk("Ret after sync: %d\n", ret); + ret = fs_close(import, fd); + //ret = fs_rename(import, "test-export-file", "renamed-test-export-file"); + //printk("Ret after rename: %d\n", ret); + ret = fs_create(import, "created-dir", 1, 0777); + printk("Ret after dir create: %d\n", ret); + ret = fs_create(import, "created-dir/created-file", 0, 0777); + printk("Ret after file create: %d\n", ret); + files = fs_list(import, "/", 15, &nr_files, NULL); + for(i=0; idom_id); + /* Allocate page for the shared ring */ + sring = (struct fsif_sring*) alloc_pages(FSIF_RING_SIZE_ORDER); + memset(sring, 0, PAGE_SIZE * FSIF_RING_SIZE_PAGES); + + /* Init the shared ring */ + SHARED_RING_INIT(sring); + ASSERT(FSIF_NR_READ_GNTS == FSIF_NR_WRITE_GNTS); + + /* Init private frontend ring */ + FRONT_RING_INIT(&import->ring, sring, PAGE_SIZE * FSIF_RING_SIZE_PAGES); + import->nr_entries = import->ring.nr_ents; + + /* Allocate table of requests */ + alloc_request_table(import); + init_SEMAPHORE(&import->reqs_sem, import->nr_entries); + + /* Grant access to the shared ring */ + for(i=0; ignt_refs[i] = + gnttab_grant_access(import->dom_id, + virt_to_mfn((char *)sring + i * PAGE_SIZE), + 0); + + /* Allocate event channel */ + BUG_ON(evtchn_alloc_unbound(import->dom_id, + fsfront_handler, + //ANY_CPU, + import, + &import->local_port)); + unmask_evtchn(import->local_port); + + + self_id = get_self_id(); + /* Write the frontend info to a node in our Xenbus */ + sprintf(nodename, "/local/domain/%d/device/vfs/%d", + self_id, import->import_id); + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, + nodename, + "ring-size", + "%u", + FSIF_RING_SIZE_PAGES); + if (err) { + message = "writing ring-size"; + goto abort_transaction; + } + + for(i=0; ignt_refs[i]); + if (err) { + message = "writing ring-refs"; + goto abort_transaction; + } + } + + err = xenbus_printf(xbt, + nodename, + "event-channel", + "%u", + import->local_port); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "state", STATE_READY, 0xdeadbeef); + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + /* Now, when our node is prepared we write request in the exporting domain + * */ + printk("Our own id is %d\n", self_id); + sprintf(r_nodename, + "/local/domain/%d/backend/vfs/exports/requests/%d/%d/frontend", + import->dom_id, self_id, import->export_id); + BUG_ON(xenbus_write(XBT_NIL, r_nodename, nodename)); + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + +done: + +#define WAIT_PERIOD 10 /* Wait period in ms */ +#define MAX_WAIT 10 /* Max number of WAIT_PERIODs */ + import->backend = NULL; + sprintf(r_nodename, "%s/backend", nodename); + + for(retry = MAX_WAIT; retry > 0; retry--) + { + xenbus_read(XBT_NIL, r_nodename, &import->backend); + if(import->backend) + { + printk("Backend found at %s\n", import->backend); + break; + } + msleep(WAIT_PERIOD); + } + + if(!import->backend) + { + printk("No backend available.\n"); + /* TODO - cleanup datastructures/xenbus */ + return 0; + } + sprintf(r_nodename, "%s/state", import->backend); + sprintf(token, "fs-front-%d", import->import_id); + /* The token will not be unique if multiple imports are inited */ + xenbus_watch_path_token(XBT_NIL, r_nodename, r_nodename, &events); + xenbus_wait_for_value(r_nodename, STATE_READY, &events); + xenbus_unwatch_path(XBT_NIL, r_nodename); + printk("Backend ready.\n"); + + //create_thread("fs-tester", test_fs_import, import); + + return 1; +} + +static void add_export(struct minios_list_head *exports, unsigned int domid) +{ + char node[1024], **exports_list = NULL, *ret_msg; + int j = 0; + static int import_id = 0; + + sprintf(node, "/local/domain/%d/backend/vfs/exports", domid); + ret_msg = xenbus_ls(XBT_NIL, node, &exports_list); + if (ret_msg && strcmp(ret_msg, "ENOENT")) + printk("couldn't read %s: %s\n", node, ret_msg); + while(exports_list && exports_list[j]) + { + struct fs_import *import; + int export_id = -1; + + sscanf(exports_list[j], "%d", &export_id); + if(export_id >= 0) + { + import = xmalloc(struct fs_import); + import->dom_id = domid; + import->export_id = export_id; + import->import_id = import_id++; + MINIOS_INIT_LIST_HEAD(&import->list); + minios_list_add(&import->list, exports); + } + free(exports_list[j]); + j++; + } + if(exports_list) + free(exports_list); + if(ret_msg) + free(ret_msg); +} + +#if 0 +static struct minios_list_head* probe_exports(void) +{ + struct minios_list_head *exports; + char **node_list = NULL, *msg = NULL; + int i = 0; + + exports = xmalloc(struct minios_list_head); + MINIOS_INIT_LIST_HEAD(exports); + + msg = xenbus_ls(XBT_NIL, "/local/domain", &node_list); + if(msg) + { + printk("Could not list VFS exports (%s).\n", msg); + goto exit; + } + + while(node_list[i]) + { + add_export(exports, atoi(node_list[i])); + free(node_list[i]); + i++; + } + +exit: + if(msg) + free(msg); + if(node_list) + free(node_list); + return exports; +} +#endif + +MINIOS_LIST_HEAD(exports); + +void init_fs_frontend(void) +{ + struct minios_list_head *entry; + struct fs_import *import = NULL; + printk("Initing FS fronend(s).\n"); + + //exports = probe_exports(); + add_export(&exports, 0); + minios_list_for_each(entry, &exports) + { + import = minios_list_entry(entry, struct fs_import, list); + printk("FS export [dom=%d, id=%d] found\n", + import->dom_id, import->export_id); + init_fs_import(import); + } + + fs_import = import; + + if (!fs_import) + printk("No FS import\n"); +} + +/* TODO: shutdown */ diff --git a/extras/mini-os/gntmap.c b/extras/mini-os/gntmap.c new file mode 100644 index 0000000..abbd91a --- /dev/null +++ b/extras/mini-os/gntmap.c @@ -0,0 +1,252 @@ +/* + * Manages grant mappings from other domains. + * + * Diego Ongaro , July 2008 + * + * Files of type FTYPE_GNTMAP contain a gntmap, which is an array of + * (host address, grant handle) pairs. Grant handles come from a hypervisor map + * operation and are needed for the corresponding unmap. + * + * This is a rather naive implementation in terms of performance. If we start + * using it frequently, there's definitely some low-hanging fruit here. + * + * + * 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 "gntmap.h" + +#define DEFAULT_MAX_GRANTS 128 + +struct gntmap_entry { + unsigned long host_addr; + grant_handle_t handle; +}; + +static inline int +gntmap_entry_used(struct gntmap_entry *entry) +{ + return entry->host_addr != 0; +} + +static struct gntmap_entry* +gntmap_find_free_entry(struct gntmap *map) +{ + int i; + + for (i = 0; i < map->nentries; i++) { + if (!gntmap_entry_used(&map->entries[i])) + return &map->entries[i]; + } + +#ifdef GNTMAP_DEBUG + printk("gntmap_find_free_entry(map=%p): all %d entries full\n", + map, map->nentries); +#endif + return NULL; +} + +static struct gntmap_entry* +gntmap_find_entry(struct gntmap *map, unsigned long addr) +{ + int i; + + for (i = 0; i < map->nentries; i++) { + if (map->entries[i].host_addr == addr) + return &map->entries[i]; + } + return NULL; +} + +int +gntmap_set_max_grants(struct gntmap *map, int count) +{ +#ifdef GNTMAP_DEBUG + printk("gntmap_set_max_grants(map=%p, count=%d)\n", map, count); +#endif + + if (map->nentries != 0) + return -EBUSY; + + map->entries = xmalloc_array(struct gntmap_entry, count); + if (map->entries == NULL) + return -ENOMEM; + + memset(map->entries, 0, sizeof(struct gntmap_entry) * count); + map->nentries = count; + return 0; +} + +static int +_gntmap_map_grant_ref(struct gntmap_entry *entry, + unsigned long host_addr, + uint32_t domid, + uint32_t ref, + int writable) +{ + struct gnttab_map_grant_ref op; + int rc; + + op.ref = (grant_ref_t) ref; + op.dom = (domid_t) domid; + op.host_addr = (uint64_t) host_addr; + op.flags = GNTMAP_host_map; + if (!writable) + op.flags |= GNTMAP_readonly; + + rc = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1); + if (rc != 0 || op.status != GNTST_okay) { + printk("GNTTABOP_map_grant_ref failed: " + "returned %d, status %" PRId16 "\n", + rc, op.status); + return rc != 0 ? rc : op.status; + } + + entry->host_addr = host_addr; + entry->handle = op.handle; + return 0; +} + +static int +_gntmap_unmap_grant_ref(struct gntmap_entry *entry) +{ + struct gnttab_unmap_grant_ref op; + int rc; + + op.host_addr = (uint64_t) entry->host_addr; + op.dev_bus_addr = 0; + op.handle = entry->handle; + + rc = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1); + if (rc != 0 || op.status != GNTST_okay) { + printk("GNTTABOP_unmap_grant_ref failed: " + "returned %d, status %" PRId16 "\n", + rc, op.status); + return rc != 0 ? rc : op.status; + } + + entry->host_addr = 0; + return 0; +} + +int +gntmap_munmap(struct gntmap *map, unsigned long start_address, int count) +{ + int i, rc; + struct gntmap_entry *ent; + +#ifdef GNTMAP_DEBUG + printk("gntmap_munmap(map=%p, start_address=%lx, count=%d)\n", + map, start_address, count); +#endif + + for (i = 0; i < count; i++) { + ent = gntmap_find_entry(map, start_address + PAGE_SIZE * i); + if (ent == NULL) { + printk("gntmap: tried to munmap unknown page\n"); + return -EINVAL; + } + + rc = _gntmap_unmap_grant_ref(ent); + if (rc != 0) + return rc; + } + + return 0; +} + +void* +gntmap_map_grant_refs(struct gntmap *map, + uint32_t count, + uint32_t *domids, + int domids_stride, + uint32_t *refs, + int writable) +{ + unsigned long addr; + struct gntmap_entry *ent; + int i; + +#ifdef GNTMAP_DEBUG + printk("gntmap_map_grant_refs(map=%p, count=%" PRIu32 ", " + "domids=%p [%" PRIu32 "...], domids_stride=%d, " + "refs=%p [%" PRIu32 "...], writable=%d)\n", + map, count, + domids, domids == NULL ? 0 : domids[0], domids_stride, + refs, refs == NULL ? 0 : refs[0], writable); +#endif + + (void) gntmap_set_max_grants(map, DEFAULT_MAX_GRANTS); + + addr = allocate_ondemand((unsigned long) count, 1); + if (addr == 0) + return NULL; + + for (i = 0; i < count; i++) { + ent = gntmap_find_free_entry(map); + if (ent == NULL || + _gntmap_map_grant_ref(ent, + addr + PAGE_SIZE * i, + domids[i * domids_stride], + refs[i], + writable) != 0) { + + (void) gntmap_munmap(map, addr, i); + return NULL; + } + } + + return (void*) addr; +} + +void +gntmap_init(struct gntmap *map) +{ +#ifdef GNTMAP_DEBUG + printk("gntmap_init(map=%p)\n", map); +#endif + map->nentries = 0; + map->entries = NULL; +} + +void +gntmap_fini(struct gntmap *map) +{ + struct gntmap_entry *ent; + int i; + +#ifdef GNTMAP_DEBUG + printk("gntmap_fini(map=%p)\n", map); +#endif + + for (i = 0; i < map->nentries; i++) { + ent = &map->entries[i]; + if (gntmap_entry_used(ent)) + (void) _gntmap_unmap_grant_ref(ent); + } + + xfree(map->entries); + map->entries = NULL; + map->nentries = 0; +} diff --git a/extras/mini-os/gnttab.c b/extras/mini-os/gnttab.c new file mode 100644 index 0000000..18fd87e --- /dev/null +++ b/extras/mini-os/gnttab.c @@ -0,0 +1,207 @@ +/* + **************************************************************************** + * (C) 2006 - Cambridge University + **************************************************************************** + * + * File: gnttab.c + * Author: Steven Smith (sos22@cam.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * + * Date: July 2006 + * + * Environment: Xen Minimal OS + * Description: Simple grant tables implementation. About as stupid as it's + * possible to be and still work. + * + **************************************************************************** + */ +#include +#include +#include +#include + +#define NR_RESERVED_ENTRIES 8 + +/* NR_GRANT_FRAMES must be less than or equal to that configured in Xen */ +#ifdef __ia64__ +#define NR_GRANT_FRAMES 1 +#else +#define NR_GRANT_FRAMES 4 +#endif +#define NR_GRANT_ENTRIES (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(grant_entry_t)) + +static grant_entry_t *gnttab_table; +static grant_ref_t gnttab_list[NR_GRANT_ENTRIES]; +#ifdef GNT_DEBUG +static char inuse[NR_GRANT_ENTRIES]; +#endif +static __DECLARE_SEMAPHORE_GENERIC(gnttab_sem, 0); + +static void +put_free_entry(grant_ref_t ref) +{ + unsigned long flags; + local_irq_save(flags); +#ifdef GNT_DEBUG + BUG_ON(!inuse[ref]); + inuse[ref] = 0; +#endif + gnttab_list[ref] = gnttab_list[0]; + gnttab_list[0] = ref; + local_irq_restore(flags); + up(&gnttab_sem); +} + +static grant_ref_t +get_free_entry(void) +{ + unsigned int ref; + unsigned long flags; + down(&gnttab_sem); + local_irq_save(flags); + ref = gnttab_list[0]; + BUG_ON(ref < NR_RESERVED_ENTRIES || ref >= NR_GRANT_ENTRIES); + gnttab_list[0] = gnttab_list[ref]; +#ifdef GNT_DEBUG + BUG_ON(inuse[ref]); + inuse[ref] = 1; +#endif + local_irq_restore(flags); + return ref; +} + +grant_ref_t +gnttab_grant_access(domid_t domid, unsigned long frame, int readonly) +{ + grant_ref_t ref; + + ref = get_free_entry(); + gnttab_table[ref].frame = frame; + gnttab_table[ref].domid = domid; + wmb(); + readonly *= GTF_readonly; + gnttab_table[ref].flags = GTF_permit_access | readonly; + + return ref; +} + +grant_ref_t +gnttab_grant_transfer(domid_t domid, unsigned long pfn) +{ + grant_ref_t ref; + + ref = get_free_entry(); + gnttab_table[ref].frame = pfn; + gnttab_table[ref].domid = domid; + wmb(); + gnttab_table[ref].flags = GTF_accept_transfer; + + return ref; +} + +int +gnttab_end_access(grant_ref_t ref) +{ + u16 flags, nflags; + + BUG_ON(ref >= NR_GRANT_ENTRIES || ref < NR_RESERVED_ENTRIES); + + nflags = gnttab_table[ref].flags; + do { + if ((flags = nflags) & (GTF_reading|GTF_writing)) { + printk("WARNING: g.e. still in use! (%x)\n", flags); + return 0; + } + } while ((nflags = synch_cmpxchg(&gnttab_table[ref].flags, flags, 0)) != + flags); + + put_free_entry(ref); + return 1; +} + +unsigned long +gnttab_end_transfer(grant_ref_t ref) +{ + unsigned long frame; + u16 flags; + + BUG_ON(ref >= NR_GRANT_ENTRIES || ref < NR_RESERVED_ENTRIES); + + while (!((flags = gnttab_table[ref].flags) & GTF_transfer_committed)) { + if (synch_cmpxchg(&gnttab_table[ref].flags, flags, 0) == flags) { + printk("Release unused transfer grant.\n"); + put_free_entry(ref); + return 0; + } + } + + /* If a transfer is in progress then wait until it is completed. */ + while (!(flags & GTF_transfer_completed)) { + flags = gnttab_table[ref].flags; + } + + /* Read the frame number /after/ reading completion status. */ + rmb(); + frame = gnttab_table[ref].frame; + + put_free_entry(ref); + + return frame; +} + +grant_ref_t +gnttab_alloc_and_grant(void **map) +{ + unsigned long mfn; + grant_ref_t gref; + + *map = (void *)alloc_page(); + mfn = virt_to_mfn(*map); + gref = gnttab_grant_access(0, mfn, 0); + return gref; +} + +static const char * const gnttabop_error_msgs[] = GNTTABOP_error_msgs; + +const char * +gnttabop_error(int16_t status) +{ + status = -status; + if (status < 0 || status >= ARRAY_SIZE(gnttabop_error_msgs)) + return "bad status"; + else + return gnttabop_error_msgs[status]; +} + +void +init_gnttab(void) +{ + struct gnttab_setup_table setup; + unsigned long frames[NR_GRANT_FRAMES]; + int i; + +#ifdef GNT_DEBUG + memset(inuse, 1, sizeof(inuse)); +#endif + for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++) + put_free_entry(i); + + setup.dom = DOMID_SELF; + setup.nr_frames = NR_GRANT_FRAMES; + set_xen_guest_handle(setup.frame_list, frames); + + HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1); + gnttab_table = map_frames(frames, NR_GRANT_FRAMES); + printk("gnttab_table mapped at %p.\n", gnttab_table); +} + +void +fini_gnttab(void) +{ + struct gnttab_setup_table setup; + + setup.dom = DOMID_SELF; + setup.nr_frames = 0; + + HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1); +} diff --git a/extras/mini-os/hypervisor.c b/extras/mini-os/hypervisor.c new file mode 100644 index 0000000..936a17b --- /dev/null +++ b/extras/mini-os/hypervisor.c @@ -0,0 +1,121 @@ +/****************************************************************************** + * hypervisor.c + * + * Communication to/from hypervisor. + * + * Copyright (c) 2002-2003, K A Fraser + * Copyright (c) 2005, Grzegorz Milos, gm281@cam.ac.uk,Intel Research Cambridge + * + * 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 + +#define active_evtchns(cpu,sh,idx) \ + ((sh)->evtchn_pending[idx] & \ + ~(sh)->evtchn_mask[idx]) + +int in_callback; + +void do_hypervisor_callback(struct pt_regs *regs) +{ + unsigned long l1, l2, l1i, l2i; + unsigned int port; + int cpu = 0; + shared_info_t *s = HYPERVISOR_shared_info; + vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; + + in_callback = 1; + + vcpu_info->evtchn_upcall_pending = 0; + /* NB x86. No need for a barrier here -- XCHG is a barrier on x86. */ +#if !defined(__i386__) && !defined(__x86_64__) + /* Clear master flag /before/ clearing selector flag. */ + wmb(); +#endif + l1 = xchg(&vcpu_info->evtchn_pending_sel, 0); + while ( l1 != 0 ) + { + l1i = __ffs(l1); + l1 &= ~(1UL << l1i); + + while ( (l2 = active_evtchns(cpu, s, l1i)) != 0 ) + { + l2i = __ffs(l2); + l2 &= ~(1UL << l2i); + + port = (l1i * (sizeof(unsigned long) * 8)) + l2i; + do_event(port, regs); + } + } + + in_callback = 0; +} + +void force_evtchn_callback(void) +{ + int save; + vcpu_info_t *vcpu; + vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; + save = vcpu->evtchn_upcall_mask; + + while (vcpu->evtchn_upcall_pending) { + vcpu->evtchn_upcall_mask = 1; + barrier(); + do_hypervisor_callback(NULL); + barrier(); + vcpu->evtchn_upcall_mask = save; + barrier(); + }; +} + +inline void mask_evtchn(u32 port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + synch_set_bit(port, &s->evtchn_mask[0]); +} + +inline void unmask_evtchn(u32 port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + vcpu_info_t *vcpu_info = &s->vcpu_info[smp_processor_id()]; + + synch_clear_bit(port, &s->evtchn_mask[0]); + + /* + * The following is basically the equivalent of 'hw_resend_irq'. Just like + * a real IO-APIC we 'lose the interrupt edge' if the channel is masked. + */ + if ( synch_test_bit (port, &s->evtchn_pending[0]) && + !synch_test_and_set_bit(port / (sizeof(unsigned long) * 8), + &vcpu_info->evtchn_pending_sel) ) + { + vcpu_info->evtchn_upcall_pending = 1; + if ( !vcpu_info->evtchn_upcall_mask ) + force_evtchn_callback(); + } +} + +inline void clear_evtchn(u32 port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + synch_clear_bit(port, &s->evtchn_pending[0]); +} diff --git a/extras/mini-os/include/arch/cc.h b/extras/mini-os/include/arch/cc.h new file mode 100644 index 0000000..02aeb2f --- /dev/null +++ b/extras/mini-os/include/arch/cc.h @@ -0,0 +1,87 @@ +/* + * lwip/arch/cc.h + * + * Compiler-specific types and macros for lwIP running on mini-os + * + * Tim Deegan , July 2007 + */ + +#ifndef __LWIP_ARCH_CC_H__ +#define __LWIP_ARCH_CC_H__ + +/* Typedefs for the types used by lwip - */ +#include +#include +#include +typedef u8 u8_t; +typedef s8 s8_t; +typedef u16 u16_t; +typedef s16 s16_t; +typedef u32 u32_t; +typedef s32 s32_t; +typedef u64 u64_t; +typedef s64 s64_t; +typedef uintptr_t mem_ptr_t; + +typedef u16 u_short; + +/* Compiler hints for packing lwip's structures - */ +#define PACK_STRUCT_FIELD(_x) _x +#define PACK_STRUCT_STRUCT __attribute__ ((packed)) +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_END + +/* Platform specific diagnostic output - */ + +extern void lwip_printk(char *fmt, ...); +#define LWIP_PLATFORM_DIAG(_x) do { lwip_printk _x ; } while (0) + +extern void lwip_die(char *fmt, ...); +#define LWIP_PLATFORM_ASSERT(_x) do { lwip_die(_x); } while(0) + +/* "lightweight" synchronization mechanisms - */ +/* SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. */ +/* SYS_ARCH_PROTECT(x) - enter protection mode. */ +/* SYS_ARCH_UNPROTECT(x) - leave protection mode. */ + +/* If the compiler does not provide memset() this file must include a */ +/* definition of it, or include a file which defines it. */ +#include + +/* This file must either include a system-local which defines */ +/* the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO */ +/* to make lwip/arch.h define the codes which are used throughout. */ +#include + +/* Not required by the docs, but needed for network-order calculations */ +#ifdef HAVE_LIBC +#include +#ifndef BIG_ENDIAN +#error endian.h does not define byte order +#endif +#else +#include +#endif + +#include +#define S16_F PRIi16 +#define U16_F PRIu16 +#define X16_F PRIx16 +#define S32_F PRIi32 +#define U32_F PRIu32 +#define X32_F PRIx32 + +#if 0 +#ifndef DBG_ON +#define DBG_ON LWIP_DBG_ON +#endif +#define LWIP_DEBUG DBG_ON +//#define IP_DEBUG DBG_ON +#define TCP_DEBUG DBG_ON +#define TCP_INPUT_DEBUG DBG_ON +#define TCP_QLEN_DEBUG DBG_ON +#define TCPIP_DEBUG DBG_ON +#define DBG_TYPES_ON DBG_ON +#endif + +#endif /* __LWIP_ARCH_CC_H__ */ diff --git a/extras/mini-os/include/arch/perf.h b/extras/mini-os/include/arch/perf.h new file mode 100644 index 0000000..dda87f2 --- /dev/null +++ b/extras/mini-os/include/arch/perf.h @@ -0,0 +1,15 @@ +/* + * lwip/arch/perf.h + * + * Arch-specific performance measurement for lwIP running on mini-os + * + * Tim Deegan , July 2007 + */ + +#ifndef __LWIP_ARCH_PERF_H__ +#define __LWIP_ARCH_PERF_H__ + +#define PERF_START do { } while(0) +#define PERF_STOP(_x) do { (void)(_x); } while (0) + +#endif /* __LWIP_ARCH_PERF_H__ */ diff --git a/extras/mini-os/include/arch/sys_arch.h b/extras/mini-os/include/arch/sys_arch.h new file mode 100644 index 0000000..11d5328 --- /dev/null +++ b/extras/mini-os/include/arch/sys_arch.h @@ -0,0 +1,35 @@ +/* + * lwip/arch/sys_arch.h + * + * Arch-specific semaphores and mailboxes for lwIP running on mini-os + * + * Tim Deegan , July 2007 + */ + +#ifndef __LWIP_ARCH_SYS_ARCH_H__ +#define __LWIP_ARCH_SYS_ARCH_H__ + +#include +#include +#include + +typedef struct semaphore *sys_sem_t; +#define SYS_SEM_NULL ((sys_sem_t) NULL) + +struct mbox { + int count; + void **messages; + struct semaphore read_sem; + struct semaphore write_sem; + int writer; + int reader; +}; + +typedef struct mbox *sys_mbox_t; +#define SYS_MBOX_NULL ((sys_mbox_t) 0) + +typedef struct thread *sys_thread_t; + +typedef unsigned long sys_prot_t; + +#endif /*__LWIP_ARCH_SYS_ARCH_H__ */ diff --git a/extras/mini-os/include/blkfront.h b/extras/mini-os/include/blkfront.h new file mode 100644 index 0000000..8cbc090 --- /dev/null +++ b/extras/mini-os/include/blkfront.h @@ -0,0 +1,42 @@ +#include +#include +#include +struct blkfront_dev; +struct blkfront_aiocb +{ + struct blkfront_dev *aio_dev; + uint8_t *aio_buf; + size_t aio_nbytes; + off_t aio_offset; + void *data; + + grant_ref_t gref[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int n; + + void (*aio_cb)(struct blkfront_aiocb *aiocb, int ret); +}; +struct blkfront_info +{ + uint64_t sectors; + unsigned sector_size; + int mode; + int info; + int barrier; + int flush; +}; +struct blkfront_dev *init_blkfront(char *nodename, struct blkfront_info *info); +#ifdef HAVE_LIBC +int blkfront_open(struct blkfront_dev *dev); +#endif +void blkfront_aio(struct blkfront_aiocb *aiocbp, int write); +#define blkfront_aio_read(aiocbp) blkfront_aio(aiocbp, 0) +#define blkfront_aio_write(aiocbp) blkfront_aio(aiocbp, 1) +void blkfront_io(struct blkfront_aiocb *aiocbp, int write); +#define blkfront_read(aiocbp) blkfront_io(aiocbp, 0) +#define blkfront_write(aiocbp) blkfront_io(aiocbp, 1) +void blkfront_aio_push_operation(struct blkfront_aiocb *aiocbp, uint8_t op); +int blkfront_aio_poll(struct blkfront_dev *dev); +void blkfront_sync(struct blkfront_dev *dev); +void shutdown_blkfront(struct blkfront_dev *dev); + +extern struct wait_queue_head blkfront_queue; diff --git a/extras/mini-os/include/byteswap.h b/extras/mini-os/include/byteswap.h new file mode 100644 index 0000000..46821ae --- /dev/null +++ b/extras/mini-os/include/byteswap.h @@ -0,0 +1,33 @@ +#ifndef _BYTESWAP_H_ +#define _BYTESWAP_H_ + +/* Unfortunately not provided by newlib. */ + +#include +static inline uint16_t bswap_16(uint16_t x) +{ + return + ((((x) & 0xff00) >> 8) | (((x) & 0xff) << 8)); +} + +static inline uint32_t bswap_32(uint32_t x) +{ + return + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); +} + +static inline uint64_t bswap_64(uint64_t x) +{ + return + ((((x) & 0xff00000000000000ULL) >> 56) | + (((x) & 0x00ff000000000000ULL) >> 40) | + (((x) & 0x0000ff0000000000ULL) >> 24) | + (((x) & 0x000000ff00000000ULL) >> 8) | + (((x) & 0x00000000ff000000ULL) << 8) | + (((x) & 0x0000000000ff0000ULL) << 24) | + (((x) & 0x000000000000ff00ULL) << 40) | + (((x) & 0x00000000000000ffULL) << 56)); +} + +#endif /* _BYTESWAP_H */ diff --git a/extras/mini-os/include/console.h b/extras/mini-os/include/console.h new file mode 100644 index 0000000..e9e5161 --- /dev/null +++ b/extras/mini-os/include/console.h @@ -0,0 +1,65 @@ +/* + **************************************************************************** + * (C) 2006 - Grzegorz Milos - Cambridge University + **************************************************************************** + * + * File: console.h + * Author: Grzegorz Milos + * Changes: + * + * Date: Mar 2006 + * + * Environment: Xen Minimal OS + * Description: Console interface. + * + * Handles console I/O. Defines printk. + * + **************************************************************************** + * 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. + */ +#ifndef _LIB_CONSOLE_H_ +#define _LIB_CONSOLE_H_ + +#include +#include +#include + +void print(int direct, const char *fmt, va_list args); +void printk(const char *fmt, ...); +void xprintk(const char *fmt, ...); + +#define tprintk(_fmt, _args...) printk("[%s] " _fmt, current->name, ##_args) + +void xencons_rx(char *buf, unsigned len, struct pt_regs *regs); +void xencons_tx(void); + +void init_console(void); +void console_print(char *data, int length); +void fini_console(void); + +/* Low level functions defined in xencons_ring.c */ +extern struct wait_queue_head console_queue; +int xencons_ring_init(void); +int xencons_ring_send(const char *data, unsigned len); +int xencons_ring_send_no_notify(const char *data, unsigned len); +int xencons_ring_avail(void); +int xencons_ring_recv(char *data, unsigned len); + + +#endif /* _LIB_CONSOLE_H_ */ diff --git a/extras/mini-os/include/ctype.h b/extras/mini-os/include/ctype.h new file mode 100644 index 0000000..ac0dd67 --- /dev/null +++ b/extras/mini-os/include/ctype.h @@ -0,0 +1,60 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +#ifdef HAVE_LIBC +#include_next +#else +/* + * NOTE! This ctype does not handle EOF like the standard C + * library is required to. + */ + +#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 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) + +#define isascii(c) (((unsigned char)(c))<=0x7f) +#define toascii(c) (((unsigned char)(c))&0x7f) + +static inline unsigned char __tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#define tolower(c) __tolower(c) +#define toupper(c) __toupper(c) + +#endif + +#endif diff --git a/extras/mini-os/include/err.h b/extras/mini-os/include/err.h new file mode 100644 index 0000000..4e19619 --- /dev/null +++ b/extras/mini-os/include/err.h @@ -0,0 +1,31 @@ +#ifndef _ERR_H +#define _ERR_H + +#include + +/* + * Kernel pointers have redundant information, so we can use a + * scheme where we can return either an error code or a dentry + * pointer with the same return value. + * + * This should be a per-architecture thing, to allow different + * error and pointer decisions. + */ +#define IS_ERR_VALUE(x) ((x) > (unsigned long)-1000L) + +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 IS_ERR_VALUE((unsigned long)ptr); +} + +#endif /* _LINUX_ERR_H */ diff --git a/extras/mini-os/include/errno-base.h b/extras/mini-os/include/errno-base.h new file mode 100644 index 0000000..036a080 --- /dev/null +++ b/extras/mini-os/include/errno-base.h @@ -0,0 +1,39 @@ +#ifndef _ERRNO_BASE_H +#define _ERRNO_BASE_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ + +#endif diff --git a/extras/mini-os/include/errno.h b/extras/mini-os/include/errno.h new file mode 100644 index 0000000..f71b131 --- /dev/null +++ b/extras/mini-os/include/errno.h @@ -0,0 +1,122 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +#include + +typedef int error_t; + +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define ENOTSUP EOPNOTSUPP +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ + +/* for robust mutexes */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ + + +#define EFTYPE 132 /* Inappropriate file type or format */ + +#ifdef HAVE_LIBC +#include +extern int errno; +#define ERRNO +#define errno (get_current()->reent._errno) +#endif + +#endif diff --git a/extras/mini-os/include/events.h b/extras/mini-os/include/events.h new file mode 100644 index 0000000..912e4cf --- /dev/null +++ b/extras/mini-os/include/events.h @@ -0,0 +1,51 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2005 - Grzegorz Milos - Intel Reseach Cambridge + **************************************************************************** + * + * File: events.h + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * + * Date: Jul 2003, changes Jun 2005 + * + * Environment: Xen Minimal OS + * Description: Deals with events on the event channels + * + **************************************************************************** + */ + +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#include +#include + +typedef void (*evtchn_handler_t)(evtchn_port_t, struct pt_regs *, void *); + +/* prototypes */ +int do_event(evtchn_port_t port, struct pt_regs *regs); +evtchn_port_t bind_virq(uint32_t virq, evtchn_handler_t handler, void *data); +evtchn_port_t bind_pirq(uint32_t pirq, int will_share, evtchn_handler_t handler, void *data); +evtchn_port_t bind_evtchn(evtchn_port_t port, evtchn_handler_t handler, + void *data); +void unbind_evtchn(evtchn_port_t port); +void init_events(void); +int evtchn_alloc_unbound(domid_t pal, evtchn_handler_t handler, + void *data, evtchn_port_t *port); +int evtchn_bind_interdomain(domid_t pal, evtchn_port_t remote_port, + evtchn_handler_t handler, void *data, + evtchn_port_t *local_port); +void unbind_all_ports(void); + +static inline int notify_remote_via_evtchn(evtchn_port_t port) +{ + evtchn_send_t op; + op.port = port; + return HYPERVISOR_event_channel_op(EVTCHNOP_send, &op); +} + +void fini_events(void); + +#endif /* _EVENTS_H_ */ diff --git a/extras/mini-os/include/fbfront.h b/extras/mini-os/include/fbfront.h new file mode 100644 index 0000000..7e29b4c --- /dev/null +++ b/extras/mini-os/include/fbfront.h @@ -0,0 +1,45 @@ +#include +#include +#include + +/* from */ +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 +#endif +#ifndef BTN_RIGHT +#define BTN_RIGHT 0x111 +#endif +#ifndef BTN_MIDDLE +#define BTN_MIDDLE 0x112 +#endif +#ifndef KEY_Q +#define KEY_Q 16 +#endif +#ifndef KEY_MAX +#define KEY_MAX 0x1ff +#endif + + +struct kbdfront_dev; +struct kbdfront_dev *init_kbdfront(char *nodename, int abs_pointer); +#ifdef HAVE_LIBC +int kbdfront_open(struct kbdfront_dev *dev); +#endif + +int kbdfront_receive(struct kbdfront_dev *dev, union xenkbd_in_event *buf, int n); +extern struct wait_queue_head kbdfront_queue; + +void shutdown_kbdfront(struct kbdfront_dev *dev); + + +struct fbfront_dev *init_fbfront(char *nodename, unsigned long *mfns, int width, int height, int depth, int stride, int n); +#ifdef HAVE_LIBC +int fbfront_open(struct fbfront_dev *dev); +#endif + +int fbfront_receive(struct fbfront_dev *dev, union xenfb_in_event *buf, int n); +extern struct wait_queue_head fbfront_queue; +void fbfront_update(struct fbfront_dev *dev, int x, int y, int width, int height); +void fbfront_resize(struct fbfront_dev *dev, int width, int height, int stride, int depth, int offset); + +void shutdown_fbfront(struct fbfront_dev *dev); diff --git a/extras/mini-os/include/fcntl.h b/extras/mini-os/include/fcntl.h new file mode 100644 index 0000000..cc59b3c --- /dev/null +++ b/extras/mini-os/include/fcntl.h @@ -0,0 +1,99 @@ +#ifndef _I386_FCNTL_H +#define _I386_FCNTL_H + +#ifdef HAVE_LIBC +#include_next +#else + +/* open/fcntl - O_SYNC is only implemented on blocks devices and on files + located on an ext2 file system */ +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 +#define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_DIRECT 040000 /* direct disk access hint */ +#define O_LARGEFILE 0100000 +#define O_DIRECTORY 0200000 /* must be a directory */ +#define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 + +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get close_on_exec */ +#define F_SETFD 2 /* set/clear close_on_exec */ +#define F_GETFL 3 /* get file->f_flags */ +#define F_SETFL 4 /* set file->f_flags */ +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_SETOWN 8 /* for sockets. */ +#define F_GETOWN 9 /* for sockets. */ +#define F_SETSIG 10 /* for sockets. */ +#define F_GETSIG 11 /* for sockets. */ + +#define F_GETLK64 12 /* using 'struct flock64' */ +#define F_SETLK64 13 +#define F_SETLKW64 14 + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* for old implementation of bsd flock () */ +#define F_EXLCK 4 /* or 3 */ +#define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 + +/* operations for bsd flock(), also used by the kernel implementation */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* remove lock */ + +#define LOCK_MAND 32 /* This is a mandatory flock */ +#define LOCK_READ 64 /* ... Which allows concurrent read operations */ +#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */ +#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */ + +/* +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +struct flock64 { + short l_type; + short l_whence; + loff_t l_start; + loff_t l_len; + pid_t l_pid; +}; + +#define F_LINUX_SPECIFIC_BASE 1024 +*/ + +#endif + +int open(const char *path, int flags, ...) asm("open64"); +int fcntl(int fd, int cmd, ...); + +#endif diff --git a/extras/mini-os/include/fs.h b/extras/mini-os/include/fs.h new file mode 100644 index 0000000..aa19135 --- /dev/null +++ b/extras/mini-os/include/fs.h @@ -0,0 +1,56 @@ +#ifndef __FS_H__ +#define __FS_H__ + +#include +#include +#include + +#define FSIF_RING_SIZE_ORDER 1 +#define FSIF_RING_SIZE_PAGES (1< + +/* + * Please consider struct gntmap opaque. If instead you choose to disregard + * this message, I insist that you keep an eye out for raptors. + */ +struct gntmap { + int nentries; + struct gntmap_entry *entries; +}; + +int +gntmap_set_max_grants(struct gntmap *map, int count); + +int +gntmap_munmap(struct gntmap *map, unsigned long start_address, int count); + +void* +gntmap_map_grant_refs(struct gntmap *map, + uint32_t count, + uint32_t *domids, + int domids_stride, + uint32_t *refs, + int writable); + +void +gntmap_init(struct gntmap *map); + +void +gntmap_fini(struct gntmap *map); + +#endif /* !__GNTMAP_H__ */ diff --git a/extras/mini-os/include/gnttab.h b/extras/mini-os/include/gnttab.h new file mode 100644 index 0000000..acd6c39 --- /dev/null +++ b/extras/mini-os/include/gnttab.h @@ -0,0 +1,16 @@ +#ifndef __GNTTAB_H__ +#define __GNTTAB_H__ + +#include + +void init_gnttab(void); +grant_ref_t gnttab_alloc_and_grant(void **map); +grant_ref_t gnttab_grant_access(domid_t domid, unsigned long frame, + int readonly); +grant_ref_t gnttab_grant_transfer(domid_t domid, unsigned long pfn); +unsigned long gnttab_end_transfer(grant_ref_t gref); +int gnttab_end_access(grant_ref_t ref); +const char *gnttabop_error(int16_t status); +void fini_gnttab(void); + +#endif /* !__GNTTAB_H__ */ diff --git a/extras/mini-os/include/hypervisor.h b/extras/mini-os/include/hypervisor.h new file mode 100644 index 0000000..b4c7292 --- /dev/null +++ b/extras/mini-os/include/hypervisor.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * hypervisor.h + * + * Hypervisor handling. + * + * + * Copyright (c) 2002, K A Fraser + * Copyright (c) 2005, Grzegorz Milos + * Updates: Aravindh Puthiyaparambil + * Updates: Dietmar Hahn for ia64 + */ + +#ifndef _HYPERVISOR_H_ +#define _HYPERVISOR_H_ + +#include +#include +#if defined(__i386__) +#include +#elif defined(__x86_64__) +#include +#elif defined(__ia64__) +#include +#else +#error "Unsupported architecture" +#endif +#include + +/* + * a placeholder for the start of day information passed up from the hypervisor + */ +union start_info_union +{ + start_info_t start_info; + char padding[512]; +}; +extern union start_info_union start_info_union; +#define start_info (start_info_union.start_info) + +/* hypervisor.c */ +void force_evtchn_callback(void); +void do_hypervisor_callback(struct pt_regs *regs); +void mask_evtchn(u32 port); +void unmask_evtchn(u32 port); +void clear_evtchn(u32 port); + +extern int in_callback; + +#endif /* __HYPERVISOR_H__ */ diff --git a/extras/mini-os/include/ia64/arch_limits.h b/extras/mini-os/include/ia64/arch_limits.h new file mode 100644 index 0000000..c7bb9b6 --- /dev/null +++ b/extras/mini-os/include/ia64/arch_limits.h @@ -0,0 +1,12 @@ + +#ifndef __ARCH_LIMITS_H__ +#define __ARCH_LIMITS_H__ + +/* Commonly 16K pages are used. */ +#define __PAGE_SHIFT 14 /* 16K pages */ +#define __PAGE_SIZE (1<<(__PAGE_SHIFT)) + +#define __STACK_SIZE_PAGE_ORDER 2 +#define __STACK_SIZE (__PAGE_SIZE * (1 << __STACK_SIZE_PAGE_ORDER)) + +#endif /* __ARCH_LIMITS_H__ */ diff --git a/extras/mini-os/include/ia64/arch_mm.h b/extras/mini-os/include/ia64/arch_mm.h new file mode 100644 index 0000000..5a1a1a9 --- /dev/null +++ b/extras/mini-os/include/ia64/arch_mm.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 - Dietmar Hahn + * + **************************************************************************** + * 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. + */ + +#ifndef __ARCH_MM_H__ +#define __ARCH_MM_H__ + +#include "page.h" +#include "ia64_cpu.h" + +#define PFN_PHYS(x) (pfn_to_page(x)) +#define PHYS_PFN(x) (page_to_pfn(x)) +#define to_virt(x) __va(x) +#define to_phys(x) __pa(x) + +#define virt_to_mfn(x) virt_to_pfn(x) +#define virtual_to_mfn(x) (ia64_tpa((uint64_t)(x)) >> PAGE_SHIFT) + +#define map_frames(f, n) map_frames_ex(f, n, 1, 0, 1, DOMID_SELF, 0, 0) +/* TODO */ +#define map_zero(n, a) map_frames_ex(NULL, n, 0, 0, a, DOMID_SELF, 0, 0) +#define do_map_zero(start, n) ASSERT(n == 0) + +#endif /* __ARCH_MM_H__ */ diff --git a/extras/mini-os/include/ia64/arch_sched.h b/extras/mini-os/include/ia64/arch_sched.h new file mode 100644 index 0000000..f5714a3 --- /dev/null +++ b/extras/mini-os/include/ia64/arch_sched.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2006 Dietmar Hahn + * All rights reserved. + * + * The file contains ia64 specific scheduler declarations. + * + **************************************************************************** + * 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. + */ + +#ifndef __ARCH_SCHED_H__ +#define __ARCH_SCHED_H__ + +#include "os.h" + +struct thread; /* Only declaration */ + +struct thread_regs +{ + unsigned long unat_b; /* NaT before spilling */ + unsigned long sp; + unsigned long rp; + unsigned long pr; + unsigned long bsp; + unsigned long pfs; + unsigned long rnat; + unsigned long lc; + + unsigned long unat_a; /* NaT after spilling. */ + unsigned long r4; + unsigned long r5; + unsigned long r6; + unsigned long r7; + + unsigned long b1; + unsigned long b2; + unsigned long b3; + unsigned long b4; + unsigned long b5; + + ia64_fpreg_t f2; + ia64_fpreg_t f3; + ia64_fpreg_t f4; + ia64_fpreg_t f5; + ia64_fpreg_t f16; + ia64_fpreg_t f17; + ia64_fpreg_t f18; + ia64_fpreg_t f19; + ia64_fpreg_t f20; + ia64_fpreg_t f21; + ia64_fpreg_t f22; + ia64_fpreg_t f23; + ia64_fpreg_t f24; + ia64_fpreg_t f25; + ia64_fpreg_t f26; + ia64_fpreg_t f27; + ia64_fpreg_t f28; + ia64_fpreg_t f29; + ia64_fpreg_t f30; + ia64_fpreg_t f31; +}; + +typedef struct thread_regs thread_regs_t; + +void arch_switch_threads(struct thread* prev, struct thread* next); + +static inline struct thread* get_current(void) +{ + register struct thread *current asm("r13"); + return current; +} + + +#endif /* __ARCH_SCHED_H__ */ diff --git a/extras/mini-os/include/ia64/arch_spinlock.h b/extras/mini-os/include/ia64/arch_spinlock.h new file mode 100644 index 0000000..60712b5 --- /dev/null +++ b/extras/mini-os/include/ia64/arch_spinlock.h @@ -0,0 +1,61 @@ +/* + * Done by Dietmar Hahn slock), + SPIN_LOCK_UNUSED, SPIN_LOCK_USED); + } while (ret == SPIN_LOCK_USED); +} + +static inline void +_raw_spin_unlock(spinlock_t *lck) +{ + asm volatile ("st4.rel.nta [%0] = r0\n\t" :: "r"(&(lck->slock)) + : "memory" ); +} + +static inline uint32_t +_raw_spin_trylock(spinlock_t* lck) +{ + uint32_t ret; + ret = ia64_cmpxchg_acq_32(&(lck->slock), SPIN_LOCK_USED, SPIN_LOCK_USED); + return (ret == SPIN_LOCK_USED); +} + +#endif /* _ARCH_SPINLOCK_H_ */ diff --git a/extras/mini-os/include/ia64/asm.h b/extras/mini-os/include/ia64/asm.h new file mode 100644 index 0000000..d96216f --- /dev/null +++ b/extras/mini-os/include/ia64/asm.h @@ -0,0 +1,18 @@ +/* + * Done by Dietmar Hahn + * + **************************************************************************** + * Copyright (c) 1998 Doug Rabson + * All rights reserved. + * + * 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 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 THE 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. + */ + +#ifndef _MACHINE_ATOMIC_H_ +#define _MACHINE_ATOMIC_H_ + +/* + * Various simple arithmetic on memory which is atomic in the presence + * of interrupts and SMP safe. + */ + +#if !defined(__ASSEMBLY__) + +#include + + +/* + * Everything is built out of cmpxchg. + */ +#define IA64_CMPXCHG(sz, sem, p, cmpval, newval, ret) \ + __asm __volatile ( \ + "mov ar.ccv=%2;;\n\t" \ + "cmpxchg" #sz "." #sem " %0=%4,%3,ar.ccv\n\t" \ + : "=r" (ret), "=m" (*p) \ + : "r" (cmpval), "r" (newval), "m" (*p) \ + : "memory") + + +/* + * Some common forms of cmpxch. + */ + +static __inline uint8_t +ia64_cmpxchg_acq_8(volatile uint8_t* p, uint8_t cmpval, uint8_t newval) +{ + uint8_t ret; + + IA64_CMPXCHG(1, acq, p, cmpval, newval, ret); + return (ret); +} + +static __inline uint16_t +ia64_cmpxchg_acq_16(volatile uint16_t* p, uint16_t cmpval, uint16_t newval) +{ + uint16_t ret; + + IA64_CMPXCHG(2, acq, p, cmpval, newval, ret); + return (ret); +} + +static __inline uint32_t +ia64_cmpxchg_acq_32(volatile uint32_t* p, uint32_t cmpval, uint32_t newval) +{ + uint32_t ret; + + IA64_CMPXCHG(4, acq, p, cmpval, newval, ret); + return (ret); +} + +static __inline uint32_t +ia64_cmpxchg_rel_32(volatile uint32_t* p, uint32_t cmpval, uint32_t newval) +{ + uint32_t ret; + + IA64_CMPXCHG(4, rel, p, cmpval, newval, ret); + return (ret); +} + +static __inline uint64_t +ia64_cmpxchg_acq_64(volatile uint64_t* p, uint64_t cmpval, uint64_t newval) +{ + uint64_t ret; + + IA64_CMPXCHG(8, acq, p, cmpval, newval, ret); + return (ret); +} + +static __inline uint64_t +ia64_cmpxchg_rel_64(volatile uint64_t* p, uint64_t cmpval, uint64_t newval) +{ + uint64_t ret; + + IA64_CMPXCHG(8, rel, p, cmpval, newval, ret); + return (ret); +} + +#define ATOMIC_STORE_LOAD(type, width, size) \ +static __inline uint##width##_t \ +ia64_ld_acq_##width(volatile uint##width##_t* p) \ +{ \ + uint##width##_t v; \ + \ + __asm __volatile ("ld" size ".acq %0=%1" \ + : "=r" (v) \ + : "m" (*p) \ + : "memory"); \ + return (v); \ +} \ + \ +static __inline uint##width##_t \ +atomic_load_acq_##width(volatile uint##width##_t* p) \ +{ \ + uint##width##_t v; \ + \ + __asm __volatile ("ld" size ".acq %0=%1" \ + : "=r" (v) \ + : "m" (*p) \ + : "memory"); \ + return (v); \ +} \ + \ +static __inline uint##width##_t \ +atomic_load_acq_##type(volatile uint##width##_t* p) \ +{ \ + uint##width##_t v; \ + \ + __asm __volatile ("ld" size ".acq %0=%1" \ + : "=r" (v) \ + : "m" (*p) \ + : "memory"); \ + return (v); \ +} \ + \ +static __inline void \ +ia64_st_rel_##width(volatile uint##width##_t* p, uint##width##_t v)\ +{ \ + __asm __volatile ("st" size ".rel %0=%1" \ + : "=m" (*p) \ + : "r" (v) \ + : "memory"); \ +} \ + \ +static __inline void \ +atomic_store_rel_##width(volatile uint##width##_t* p, uint##width##_t v)\ +{ \ + __asm __volatile ("st" size ".rel %0=%1" \ + : "=m" (*p) \ + : "r" (v) \ + : "memory"); \ +} \ + \ +static __inline void \ +atomic_store_rel_##type(volatile uint##width##_t* p, uint##width##_t v)\ +{ \ + __asm __volatile ("st" size ".rel %0=%1" \ + : "=m" (*p) \ + : "r" (v) \ + : "memory"); \ +} + +ATOMIC_STORE_LOAD(char, 8, "1") +ATOMIC_STORE_LOAD(short, 16, "2") +ATOMIC_STORE_LOAD(int, 32, "4") +ATOMIC_STORE_LOAD(long, 64, "8") + +#undef ATOMIC_STORE_LOAD + +#define IA64_ATOMIC(sz, type, name, width, op) \ + \ +static __inline type \ +atomic_##name##_acq_##width(volatile type *p, type v) \ +{ \ + type old, ret; \ + do { \ + old = *p; \ + IA64_CMPXCHG(sz, acq, p, old, old op v, ret); \ + } while (ret != old); \ + return(ret); \ +} \ + \ +static __inline type \ +atomic_##name##_rel_##width(volatile type *p, type v) \ +{ \ + type old, ret; \ + do { \ + old = *p; \ + IA64_CMPXCHG(sz, rel, p, old, old op v, ret); \ + } while (ret != old); \ + return(ret); \ +} + +IA64_ATOMIC(1, uint8_t, set, 8, |) +IA64_ATOMIC(2, uint16_t, set, 16, |) +IA64_ATOMIC(4, uint32_t, set, 32, |) +IA64_ATOMIC(8, uint64_t, set, 64, |) + +IA64_ATOMIC(1, uint8_t, clear, 8, &~) +IA64_ATOMIC(2, uint16_t, clear, 16, &~) +IA64_ATOMIC(4, uint32_t, clear, 32, &~) +IA64_ATOMIC(8, uint64_t, clear, 64, &~) + +IA64_ATOMIC(1, uint8_t, add, 8, +) +IA64_ATOMIC(2, uint16_t, add, 16, +) +IA64_ATOMIC(4, uint32_t, add, 32, +) +IA64_ATOMIC(8, uint64_t, add, 64, +) + +IA64_ATOMIC(1, uint8_t, subtract, 8, -) +IA64_ATOMIC(2, uint16_t, subtract, 16, -) +IA64_ATOMIC(4, uint32_t, subtract, 32, -) +IA64_ATOMIC(8, uint64_t, subtract, 64, -) + +#undef IA64_ATOMIC +#undef IA64_CMPXCHG + +#define atomic_set_8 atomic_set_acq_8 +#define atomic_clear_8 atomic_clear_acq_8 +#define atomic_add_8 atomic_add_acq_8 +#define atomic_subtract_8 atomic_subtract_acq_8 + +#define atomic_set_16 atomic_set_acq_16 +#define atomic_clear_16 atomic_clear_acq_16 +#define atomic_add_16 atomic_add_acq_16 +#define atomic_subtract_16 atomic_subtract_acq_16 + +#define atomic_set_32 atomic_set_acq_32 +#define atomic_clear_32 atomic_clear_acq_32 +#define atomic_add_32 atomic_add_acq_32 +#define atomic_subtract_32 atomic_subtract_acq_32 + +#define atomic_set_64 atomic_set_acq_64 +#define atomic_clear_64 atomic_clear_acq_64 +#define atomic_add_64 atomic_add_acq_64 +#define atomic_subtract_64 atomic_subtract_acq_64 + +#define atomic_set_char atomic_set_8 +#define atomic_clear_char atomic_clear_8 +#define atomic_add_char atomic_add_8 +#define atomic_subtract_char atomic_subtract_8 +#define atomic_set_acq_char atomic_set_acq_8 +#define atomic_clear_acq_char atomic_clear_acq_8 +#define atomic_add_acq_char atomic_add_acq_8 +#define atomic_subtract_acq_char atomic_subtract_acq_8 +#define atomic_set_rel_char atomic_set_rel_8 +#define atomic_clear_rel_char atomic_clear_rel_8 +#define atomic_add_rel_char atomic_add_rel_8 +#define atomic_subtract_rel_char atomic_subtract_rel_8 + +#define atomic_set_short atomic_set_16 +#define atomic_clear_short atomic_clear_16 +#define atomic_add_short atomic_add_16 +#define atomic_subtract_short atomic_subtract_16 +#define atomic_set_acq_short atomic_set_acq_16 +#define atomic_clear_acq_short atomic_clear_acq_16 +#define atomic_add_acq_short atomic_add_acq_16 +#define atomic_subtract_acq_short atomic_subtract_acq_16 +#define atomic_set_rel_short atomic_set_rel_16 +#define atomic_clear_rel_short atomic_clear_rel_16 +#define atomic_add_rel_short atomic_add_rel_16 +#define atomic_subtract_rel_short atomic_subtract_rel_16 + +#define atomic_set_int atomic_set_32 +#define atomic_clear_int atomic_clear_32 +#define atomic_add_int atomic_add_32 +#define atomic_subtract_int atomic_subtract_32 +#define atomic_set_acq_int atomic_set_acq_32 +#define atomic_clear_acq_int atomic_clear_acq_32 +#define atomic_add_acq_int atomic_add_acq_32 +#define atomic_subtract_acq_int atomic_subtract_acq_32 +#define atomic_set_rel_int atomic_set_rel_32 +#define atomic_clear_rel_int atomic_clear_rel_32 +#define atomic_add_rel_int atomic_add_rel_32 +#define atomic_subtract_rel_int atomic_subtract_rel_32 + +#define atomic_set_long atomic_set_64 +#define atomic_clear_long atomic_clear_64 +#define atomic_add_long atomic_add_64 +#define atomic_subtract_long atomic_subtract_64 +#define atomic_set_acq_long atomic_set_acq_64 +#define atomic_clear_acq_long atomic_clear_acq_64 +#define atomic_add_acq_long atomic_add_acq_64 +#define atomic_subtract_acq_long atomic_subtract_acq_64 +#define atomic_set_rel_long atomic_set_rel_64 +#define atomic_clear_rel_long atomic_clear_rel_64 +#define atomic_add_rel_long atomic_add_rel_64 +#define atomic_subtract_rel_long atomic_subtract_rel_64 + +/* + * Atomically compare the value stored at *p with cmpval and if the + * two values are equal, update the value of *p with newval. Returns + * zero if the compare failed, nonzero otherwise. + */ +static __inline int +atomic_cmpset_acq_32(volatile uint32_t* p, uint32_t cmpval, uint32_t newval) +{ + return ia64_cmpxchg_acq_32(p, cmpval, newval) == cmpval; +} + +static __inline int +atomic_cmpset_rel_32(volatile uint32_t* p, uint32_t cmpval, uint32_t newval) +{ + return ia64_cmpxchg_rel_32(p, cmpval, newval) == cmpval; +} + +/* + * Atomically compare the value stored at *p with cmpval and if the + * two values are equal, update the value of *p with newval. Returns + * zero if the compare failed, nonzero otherwise. + */ +static __inline int +atomic_cmpset_acq_64(volatile uint64_t* p, uint64_t cmpval, uint64_t newval) +{ + return ia64_cmpxchg_acq_64(p, cmpval, newval) == cmpval; +} + +static __inline int +atomic_cmpset_rel_64(volatile uint64_t* p, uint64_t cmpval, uint64_t newval) +{ + return ia64_cmpxchg_rel_64(p, cmpval, newval) == cmpval; +} + +#define atomic_cmpset_32 atomic_cmpset_acq_32 +#define atomic_cmpset_64 atomic_cmpset_acq_64 +#define atomic_cmpset_int atomic_cmpset_32 +#define atomic_cmpset_long atomic_cmpset_64 +#define atomic_cmpset_acq_int atomic_cmpset_acq_32 +#define atomic_cmpset_rel_int atomic_cmpset_rel_32 +#define atomic_cmpset_acq_long atomic_cmpset_acq_64 +#define atomic_cmpset_rel_long atomic_cmpset_rel_64 + +static __inline int +atomic_cmpset_acq_ptr(volatile void *dst, void *exp, void *src) +{ + return atomic_cmpset_acq_long((volatile u_long *)dst, + (u_long)exp, (u_long)src); +} + +static __inline int +atomic_cmpset_rel_ptr(volatile void *dst, void *exp, void *src) +{ + return atomic_cmpset_rel_long((volatile u_long *)dst, + (u_long)exp, (u_long)src); +} + +#define atomic_cmpset_ptr atomic_cmpset_acq_ptr + +static __inline void * +atomic_load_acq_ptr(volatile void *p) +{ + return (void *)atomic_load_acq_long((volatile u_long *)p); +} + +static __inline void +atomic_store_rel_ptr(volatile void *p, void *v) +{ + atomic_store_rel_long((volatile u_long *)p, (u_long)v); +} + +#define IA64_ATOMIC_PTR(NAME) \ +static __inline void \ +atomic_##NAME##_ptr(volatile void *p, uintptr_t v) \ +{ \ + atomic_##NAME##_long((volatile u_long *)p, v); \ +} \ + \ +static __inline void \ +atomic_##NAME##_acq_ptr(volatile void *p, uintptr_t v) \ +{ \ + atomic_##NAME##_acq_long((volatile u_long *)p, v);\ +} \ + \ +static __inline void \ +atomic_##NAME##_rel_ptr(volatile void *p, uintptr_t v) \ +{ \ + atomic_##NAME##_rel_long((volatile u_long *)p, v);\ +} + +IA64_ATOMIC_PTR(set) +IA64_ATOMIC_PTR(clear) +IA64_ATOMIC_PTR(add) +IA64_ATOMIC_PTR(subtract) + +#undef IA64_ATOMIC_PTR + +static __inline uint32_t +atomic_readandclear_32(volatile uint32_t* p) +{ + uint32_t val; + do { + val = *p; + } while (!atomic_cmpset_32(p, val, 0)); + return val; +} + +static __inline uint64_t +atomic_readandclear_64(volatile uint64_t* p) +{ + uint64_t val; + do { + val = *p; + } while (!atomic_cmpset_64(p, val, 0)); + return val; +} + +#define atomic_readandclear_int atomic_readandclear_32 +#define atomic_readandclear_long atomic_readandclear_64 + + +/* Some bit operations */ + +static inline void +set_bit(int num, volatile void *addr) +{ + uint32_t bit, b, old, new; + volatile uint32_t *p; + p = (volatile uint32_t *) addr + (num >> 5); + b = 1 << (num & 31); + bit = SWAP(b); + do + { + old = *p; + new = old | bit; + } while(ia64_cmpxchg_acq_32(p, old, new) != old); +} + +static __inline__ void +clear_bit(int num, volatile void *addr) +{ + uint32_t mask, m, old, new; + volatile uint32_t *p; + p = (volatile uint32_t *) addr + (num >> 5); + m = ~(1 << (num & 31)); + mask = SWAP(m); + do { + old = *p; + new = old & mask; + } while (ia64_cmpxchg_acq_32(p, old, new) != old); +} + +static __inline__ int +test_bit(int num, const volatile void *addr) +{ + uint32_t val = SWAP(1); + return val & (((const volatile uint32_t *) addr)[num >> 5] >> (num & 31)); +} + +/* + * test_and_set_bit - Set a bit and return its old value + * num: Bit to set + * addr: Address to count from + */ +static inline int +test_and_set_bit (int num, volatile void *addr) +{ + uint32_t bit, b, old, new; + volatile uint32_t *m; + + m = (volatile uint32_t *) addr + (num >> 5); + b = 1 << (num & 31); + bit = SWAP(b); + do { + old = *m; + new = old | bit; + } while (ia64_cmpxchg_acq_32(m, old, new) != old); + return (old & bit) != 0; +} + +/* + * test_and_clear_bit - Clear a bit and return its old value + * num: Bit to set + * addr: Address to count from + */ +static +inline int test_and_clear_bit(int num, volatile unsigned long * addr) +{ + uint32_t bit, b, old, new; + volatile uint32_t* a; + + a = (volatile uint32_t *) addr + (num >> 5); + b = ~(1 << (num & 31)); + bit = SWAP(b); + do { + old = *a; + new = old & bit; + } while (ia64_cmpxchg_acq_32(a, old, new) != old); + return (old & ~bit) != 0; +} + + +#endif /* !defined(__ASSEMBLY__) */ + +#endif /* ! _MACHINE_ATOMIC_H_ */ diff --git a/extras/mini-os/include/ia64/efi.h b/extras/mini-os/include/ia64/efi.h new file mode 100644 index 0000000..b70a0a8 --- /dev/null +++ b/extras/mini-os/include/ia64/efi.h @@ -0,0 +1,396 @@ +/* + * This is a short summary of declarations and definitions from different + * efi header files of Intels' EFI_Toolkit_1.10.14.62 + * used for the minimal implementation in mini-os. + * Changes: Dietmar Hahn + * + **************************************************************************** + * Copyright (C) 2001-2004, Intel Corporation. + * THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO WARRANTIES WHATSOEVER, + * INCLUDING ANY WARRANTY OF MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR + * ANY PARTICULAR PURPOSE, OR ANY WARRANTY OTHERWISE ARISING OUT OF ANY + * PROPOSAL, SPECIFICATION OR SAMPLE. Except for a limited copyright license + * to copy this specification for internal use only, no license, express or + * implied, by estoppel or otherwise, to any intellectual property rights is + * granted herein. Intel disclaims all liability, including liability for + * infringement of any proprietary rights, relating to implementation of + * information in this specification. Intel does not warrant or represent + * that such implementation(s) will not infringe such rights. Designers must + * not rely on the absence or characteristics of any features or instructions + * marked "reserved" or "undefined." Intel reserves these for future + * definition and shall have no responsibility whatsoever for conflicts or + * incompatibilities arising from future changes to them. + * This document is an intermediate draft for comment only and is subject to + * change without notice. Readers should not design products based on this + * document. + * Intel, the Intel logo, and Itanium are trademarks or registered trademarks + * of Intel Corporation or its subsidiaries in the United States and other + * countries. + * Other names and brands may be claimed as the property of others. + */ + +#ifndef _EFI_H_ +#define _EFI_H_ + +#include + + +#define EFIWARN(a) (a) +#define EFI_ERROR(a) (((int64_t) a) < 0) + + +#define EFI_SUCCESS 0 +#define EFI_LOAD_ERROR EFIERR(1) +#define EFI_INVALID_PARAMETER EFIERR(2) +#define EFI_UNSUPPORTED EFIERR(3) +#define EFI_BAD_BUFFER_SIZE EFIERR(4) +#define EFI_BUFFER_TOO_SMALL EFIERR(5) +#define EFI_NOT_READY EFIERR(6) +#define EFI_DEVICE_ERROR EFIERR(7) +#define EFI_WRITE_PROTECTED EFIERR(8) +#define EFI_OUT_OF_RESOURCES EFIERR(9) +#define EFI_VOLUME_CORRUPTED EFIERR(10) +#define EFI_VOLUME_FULL EFIERR(11) +#define EFI_NO_MEDIA EFIERR(12) +#define EFI_MEDIA_CHANGED EFIERR(13) +#define EFI_NOT_FOUND EFIERR(14) +#define EFI_ACCESS_DENIED EFIERR(15) +#define EFI_NO_RESPONSE EFIERR(16) +#define EFI_NO_MAPPING EFIERR(17) +#define EFI_TIMEOUT EFIERR(18) +#define EFI_NOT_STARTED EFIERR(19) +#define EFI_ALREADY_STARTED EFIERR(20) +#define EFI_ABORTED EFIERR(21) +#define EFI_ICMP_ERROR EFIERR(22) +#define EFI_TFTP_ERROR EFIERR(23) +#define EFI_PROTOCOL_ERROR EFIERR(24) + +#define EFI_WARN_UNKOWN_GLYPH EFIWARN(1) +#define EFI_WARN_DELETE_FAILURE EFIWARN(2) +#define EFI_WARN_WRITE_FAILURE EFIWARN(3) +#define EFI_WARN_BUFFER_TOO_SMALL EFIWARN(4) + + +typedef uint64_t efi_status_t; +typedef void* efi_handle_t; +typedef void* efi_event_t; +typedef uint16_t efi_char16_t; + + +/* + * Standard EFI table header + */ + +struct efi_table_header +{ + uint64_t Signature; + // Revision of EFI table specification, + // upper 16 bit - major revision number + // lower 16 bit - minor revision number + uint32_t Revision; + uint32_t HeaderSize; + uint32_t CRC32; + uint32_t Reserved; +}; +typedef struct efi_table_header efi_table_header_t; + +/* + * EFI Time + */ +typedef struct +{ + uint16_t Year; /* 1998 - 20XX */ + uint8_t Month; /* 1 - 12 */ + uint8_t Day; /* 1 - 31 */ + uint8_t Hour; /* 0 - 23 */ + uint8_t Minute; /* 0 - 59 */ + uint8_t Second; /* 0 - 59 */ + uint8_t Pad1; + uint32_t Nanosecond; /* 0 - 999,999,999 */ + int16_t TimeZone; /* -1440 to 1440 or 2047 */ + uint8_t Daylight; + uint8_t Pad2; +} efi_time_t; + +/* Bit definitions for efi_time_t.Daylight */ +#define EFI_TIME_ADJUST_DAYLIGHT 0x01 +#define EFI_TIME_IN_DAYLIGHT 0x02 + +/* Value definition for efi_time_t.TimeZone */ +#define EFI_UNSPECIFIED_TIMEZONE 0x07FF + + + +typedef struct +{ + uint32_t Resolution; /* 1e-6 parts per million */ + uint32_t Accuracy; /* hertz */ + uint8_t SetsToZero; /* Set clears sub-second time */ +} efi_time_capabilities_t; + + +typedef efi_status_t (*efi_get_time_t) (efi_time_t*, efi_time_capabilities_t*); +typedef efi_status_t (*efi_set_time_t) (efi_time_t*); +typedef efi_status_t (*efi_get_wakeup_time_t) (uint8_t*, uint8_t*, efi_time_t*); +typedef efi_status_t (*efi_set_wakeup_time_t) (uint8_t, efi_time_t*); + +/* + * Memory + * Preseve the attr on any range supplied. + * ConventialMemory must have WB,SR,SW when supplied. + * When allocating from ConventialMemory always make it WB,SR,SW + * When returning to ConventialMemory always make it WB,SR,SW + * When getting the memory map, or on RT for runtime types + */ + +typedef enum { + EfiReservedMemoryType, /* 0 */ + EfiLoaderCode, + EfiLoaderData, + EfiBootServicesCode, + EfiBootServicesData, + EfiRuntimeServicesCode, + EfiRuntimeServicesData, /* 6 */ + EfiConventionalMemory, /* 7 */ + EfiUnusableMemory, + EfiACPIReclaimMemory, /* 9 */ + EfiACPIMemoryNVS, /* 10, a */ + EfiMemoryMappedIO, + EfiMemoryMappedIOPortSpace, /* 12, c */ + EfiPalCode, /* 13, d */ + EfiMaxMemoryType /* 14, e */ +} efi_memory_type_t; + +/* possible caching types for the memory range */ +#define EFI_MEMORY_UC 0x0000000000000001 +#define EFI_MEMORY_WC 0x0000000000000002 +#define EFI_MEMORY_WT 0x0000000000000004 +#define EFI_MEMORY_WB 0x0000000000000008 +#define EFI_MEMORY_UCE 0x0000000000000010 +/* physical memory protection on range */ +#define EFI_MEMORY_WP 0x0000000000001000 +#define EFI_MEMORY_RP 0x0000000000002000 +#define EFI_MEMORY_XP 0x0000000000004000 +/* range requires a runtime mapping */ +#define EFI_MEMORY_RUNTIME 0x8000000000000000 + +#define EFI_MEMORY_DESCRIPTOR_VERSION 1 + +typedef uint64_t efi_phys_addr_t; +typedef uint64_t efi_virt_addr_t; + +typedef struct +{ + uint32_t Type; /* 32 bit padding */ + efi_phys_addr_t PhysicalStart; + efi_virt_addr_t VirtualStart; + uint64_t NumberOfPages; + uint64_t Attribute; +} efi_memory_descriptor_t; + +#define NextMemoryDescriptor(Ptr,Size) ((efi_memory_descriptor_t*) (((uint8_t*) Ptr) + Size)) + + +typedef efi_status_t (*efi_set_virtual_address_map_t) + ( + uint64_t MemoryMapSize, + uint64_t DescriptorSize, + uint32_t DescriptorVersion, + efi_memory_descriptor_t* VirtualMap + ); + +typedef efi_status_t (*efi_convert_pointer_t) + ( + uint64_t DebugDisposition, + void** Address + ); + +/* + * A GUID + */ + +typedef struct +{ + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} efi_guid_t; + +/* + * EFI Configuration Table and GUID definitions + */ + +#define MPS_TABLE_GUID \ + { 0xeb9d2d2f, 0x2d88, 0x11d3, \ + { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } + +#define ACPI_TABLE_GUID \ + { 0xeb9d2d30, 0x2d88, 0x11d3, \ + { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } + +#define ACPI_20_TABLE_GUID \ + { 0x8868e871, 0xe4f1, 0x11d3, \ + { 0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } } + +#define SMBIOS_TABLE_GUID \ + { 0xeb9d2d31, 0x2d88, 0x11d3, \ + { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } + +#define SAL_SYSTEM_TABLE_GUID \ + { 0xeb9d2d32, 0x2d88, 0x11d3, \ + { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } + +/* DIG64 Headless Console & Debug Port Table. */ +#define HCDP_TABLE_GUID \ + {0xf951938d, 0x620b, 0x42ef, \ + {0x82, 0x79, 0xa8, 0x4b, 0x79, 0x61, 0x78, 0x98 } } + + +typedef struct efi_configuration_table +{ + efi_guid_t VendorGuid; + void* VendorTable; +} efi_configuration_table_t; + + +/* + * EFI platform variables + */ + +#define EFI_GLOBAL_VARIABLE \ + { 0x8BE4DF61, 0x93CA, 0x11d2, 0xAA, 0x0D, 0x00, \ + 0xE0, 0x98, 0x03, 0x2B, 0x8C } + +/* Variable attributes */ +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 + +/* Variable size limitation */ +#define EFI_MAXIMUM_VARIABLE_SIZE 1024 + +typedef efi_status_t (*efi_get_variable_t) + ( + efi_char16_t* VariableName, + efi_guid_t *VendorGuid, + uint32_t* Attributes, + uint64_t* DataSize, + void* Data + ); + +typedef +efi_status_t (*efi_get_next_variable_name_t) + ( + uint64_t* VariableNameSize, + efi_char16_t* VariableName, + efi_guid_t* VendorGuid + ); + +typedef efi_status_t (*efi_set_variable_t) + ( + efi_char16_t* VariableName, + efi_guid_t* VendorGuid, + uint32_t Attributes, + uint64_t DataSize, + void* Data + ); + +/* + * Misc + */ + +typedef enum +{ + EfiResetCold, + EfiResetWarm, + EfiResetShutdown +} efi_reset_type_t; + + +typedef efi_status_t (*efi_reset_system_t) + ( + efi_reset_type_t ResetType, + efi_status_t ResetStatus, + uint64_t DataSize, + efi_char16_t* ResetData + ); + +typedef efi_status_t (*efi_get_next_high_mono_count_t) (uint32_t* HighCount); + + +/* + * EFI Runtime Serivces Table + */ + +#define EFI_RUNTIME_SERVICES_SIGNATURE 0x5652453544e5552ULL +#define EFI_RUNTIME_SERVICES_REVISION ((EFI_SPECIFICATION_MAJOR_REVISION<<16) \ + | (EFI_SPECIFICATION_MINOR_REVISION)) + +typedef struct +{ + efi_table_header_t Hdr; + /* Time services */ + efi_get_time_t GetTime; + efi_set_time_t SetTime; + efi_get_wakeup_time_t GetWakeupTime; + efi_set_wakeup_time_t SetWakeupTime; + /* Virtual memory services */ + efi_set_virtual_address_map_t SetVirtualAddressMap; + efi_convert_pointer_t ConvertPointer; + /* Variable serviers */ + efi_get_variable_t GetVariable; + efi_get_next_variable_name_t GetNextVariableName; + efi_set_variable_t SetVariable; + /* Misc */ + efi_get_next_high_mono_count_t GetNextHighMonotonicCount; + efi_reset_system_t ResetSystem; + +} efi_runtime_services_t; + + +#define EFI_SPECIFICATION_MAJOR_REVISION 1 +#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249 +#define EFI_SYSTEM_TABLE_REVISION ((EFI_SPECIFICATION_MAJOR_REVISION<<16) \ + | (EFI_SPECIFICATION_MINOR_REVISION)) + +struct efi_system_table +{ + efi_table_header_t Hdr; + + uint64_t FirmwareVendor; // phys addr of CHAR16 + uint32_t FirmwareRevision; // Firmware vendor specific + + efi_handle_t ConsoleInHandle; + uint64_t ConIn; + + efi_handle_t ConsoleOutHandle; + uint64_t ConOut; + + efi_handle_t StandardErrorHandle; + uint64_t StdErr; + + uint64_t RuntimeServices; // phys addr + uint64_t BootServices; // phys addr + + uint64_t NumberOfTableEntries; // Number of entries in Config + uint64_t ConfigurationTable; // phys addr of ConfigTable +}; + +typedef struct efi_system_table efi_system_table_t; + + +#define EFI_PAGE_SIZE 4096 +#define EFI_PAGE_MASK 0xFFF +#define EFI_PAGE_SHIFT 12 + +#define EFI_SIZE_TO_PAGES(a) \ + ( ((a) >> EFI_PAGE_SHIFT) + ((a) & EFI_PAGE_MASK ? 1 : 0) ) + + +void init_efi(void); +int efi_get_time(efi_time_t* tmP); +efi_status_t ia64_call_efi_physical(void *, ...); + + +#endif /* _EFI_H_ */ diff --git a/extras/mini-os/include/ia64/endian.h b/extras/mini-os/include/ia64/endian.h new file mode 100644 index 0000000..1d036ff --- /dev/null +++ b/extras/mini-os/include/ia64/endian.h @@ -0,0 +1,75 @@ +/* + * Done by Dietmar Hahn + * Parts are taken from FreeBSD. + * + **************************************************************************** + * 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. + */ + + +#if !defined(_ENDIAN_H_) +#define _ENDIAN_H_ + +#include + + +#if !defined(__ASSEMBLY__) + +#if defined(BIG_ENDIAN) + +static __inline uint64_t +__bswap64(uint64_t __x) +{ + uint64_t __r; + asm __volatile("mux1 %0=%1,@rev" : "=r" (__r) : "r"(__x)); + return __r; +} + +static __inline uint32_t +__bswap32(uint32_t __x) +{ + return (__bswap64(__x) >> 32); +} + +static __inline uint16_t +__bswap16(uint16_t __x) +{ + return (__bswap64(__x) >> 48); +} + +#define doswap(x,sz) ( \ + ((sz)==1)? (uint8_t)(x): \ + ((sz)==2)? __bswap16(x): \ + ((sz)==4)? __bswap32(x): \ + ((sz)==8)? __bswap64(x): \ + ~0l ) + +#define SWAP(x) doswap((x), sizeof((x))) + + +#else /* defined(BIG_ENDIAN) */ + +#define SWAP(x) (x) + +#endif /* defined(BIG_ENDIAN) */ + +#endif /* !defined(__ASSEMBLY__) */ + + +#endif /* !defined(_ENDIAN_H_) */ diff --git a/extras/mini-os/include/ia64/hypercall-ia64.h b/extras/mini-os/include/ia64/hypercall-ia64.h new file mode 100644 index 0000000..dbab3d5 --- /dev/null +++ b/extras/mini-os/include/ia64/hypercall-ia64.h @@ -0,0 +1,236 @@ +/****************************************************************************** + * hypercall.h + * + * Mini-OS-specific hypervisor handling for ia64. + * + * Copyright (c) 2002-2004, K A Fraser + * Changes: Dietmar Hahn + * + * 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 __HYPERCALL_H__ +#define __HYPERCALL_H__ + +#include /* memcpy() */ +#include /* ENOSYS() */ +#include +#include +#include + +#ifndef _HYPERVISOR_H_ +# error "please don't include this file directly" +#endif + +// See linux/compiler.h +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +extern unsigned long __hypercall(unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long cmd); +/* + * Assembler stubs for hyper-calls. + */ + +#define _hypercall0(type, name) \ +({ \ + long __res; \ + __res = __hypercall(0, 0, 0, 0, 0, \ + __HYPERVISOR_##name); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + long __res; \ + __res = __hypercall((unsigned long)a1, \ + 0, 0, 0, 0, __HYPERVISOR_##name); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + long __res; \ + __res = __hypercall((unsigned long)a1, \ + (unsigned long)a2, \ + 0, 0, 0, __HYPERVISOR_##name); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + long __res; \ + __res = __hypercall((unsigned long)a1, \ + (unsigned long)a2, \ + (unsigned long)a3, \ + 0, 0, __HYPERVISOR_##name); \ + (type)__res; \ +}) + +#define _hypercall4(type, name, a1, a2, a3, a4) \ +({ \ + long __res; \ + __res = __hypercall((unsigned long)a1, \ + (unsigned long)a2, \ + (unsigned long)a3, \ + (unsigned long)a4, \ + 0, __HYPERVISOR_##name); \ + (type)__res; \ +}) + +#define _hypercall5(type, name, a1, a2, a3, a4, a5) \ +({ \ + long __res; \ + __res = __hypercall((unsigned long)a1, \ + (unsigned long)a2, \ + (unsigned long)a3, \ + (unsigned long)a4, \ + (unsigned long)a5, \ + __HYPERVISOR_##name); \ + (type)__res; \ +}) + + +extern unsigned long xencomm_vaddr_to_paddr(unsigned long vaddr); +struct xencomm_handle; + +/* Inline version. To be used only on linear space (kernel space). */ +static inline struct xencomm_handle * +xencomm_create_inline(void *buffer) +{ + unsigned long paddr; + + paddr = xencomm_vaddr_to_paddr((unsigned long)buffer); + return (struct xencomm_handle *)(paddr | XENCOMM_INLINE_FLAG); +} + +static inline int +xencomm_arch_event_channel_op(int cmd, void *arg) +{ + int rc; + struct xencomm_handle *newArg; + + newArg = xencomm_create_inline(arg); + rc = _hypercall2(int, event_channel_op, cmd, newArg); + if (unlikely(rc == -ENOSYS)) { + struct evtchn_op op; + + op.cmd = SWAP(cmd); + memcpy(&op.u, arg, sizeof(op.u)); + rc = _hypercall1(int, event_channel_op_compat, &op); + } + return rc; +} +#define HYPERVISOR_event_channel_op xencomm_arch_event_channel_op + +static inline int +xencomm_arch_xen_version(int cmd, struct xencomm_handle *arg) +{ + return _hypercall2(int, xen_version, cmd, arg); +} + +static inline int +xencomm_arch_xen_feature(int cmd, struct xencomm_handle *arg) +{ + struct xencomm_handle *newArg; + + newArg = xencomm_create_inline(arg); + return _hypercall2(int, xen_version, cmd, newArg); +} + +static inline int +HYPERVISOR_xen_version(int cmd, void *arg) +{ + switch(cmd) { + case XENVER_version: + return xencomm_arch_xen_version(cmd, 0); + case XENVER_get_features: + return xencomm_arch_xen_feature(cmd, arg); + default: + return -1; + } +} + +static inline int +xencomm_arch_console_io(int cmd, int count, char *str) +{ + struct xencomm_handle *newStr; + + newStr = xencomm_create_inline(str); + return _hypercall3(int, console_io, cmd, count, newStr); +} + + +#define HYPERVISOR_console_io xencomm_arch_console_io + +static inline int +HYPERVISOR_sched_op_compat(int cmd, unsigned long arg) +{ + return _hypercall2(int, sched_op_compat, cmd, arg); +} + +static inline int +xencomm_arch_sched_op(int cmd, void *arg) +{ + struct xencomm_handle *newArg; + + newArg = xencomm_create_inline(arg); + return _hypercall2(int, sched_op, cmd, newArg); +} + +#define HYPERVISOR_sched_op xencomm_arch_sched_op + +static inline int +xencomm_arch_callback_op(int cmd, void *arg) +{ + struct xencomm_handle *newArg; + + newArg = xencomm_create_inline(arg); + return _hypercall2(int, callback_op, cmd, newArg); +} +#define HYPERVISOR_callback_op xencomm_arch_callback_op + +static inline int +xencomm_arch_hypercall_grant_table_op(unsigned int cmd, + struct xencomm_handle *uop, + unsigned int count) +{ + return _hypercall3(int, grant_table_op, cmd, uop, count); +} + +int HYPERVISOR_grant_table_op(unsigned int cmd, void *uop, unsigned int count); + +static inline int +HYPERVISOR_opt_feature(void *arg) +{ + struct xencomm_handle *new_arg; + + new_arg = xencomm_create_inline(arg); + + return _hypercall1(int, opt_feature, new_arg); +} + +#endif /* __HYPERCALL_H__ */ diff --git a/extras/mini-os/include/ia64/ia64_cpu.h b/extras/mini-os/include/ia64/ia64_cpu.h new file mode 100644 index 0000000..f9ebf73 --- /dev/null +++ b/extras/mini-os/include/ia64/ia64_cpu.h @@ -0,0 +1,776 @@ +/* + * Done by Dietmar Hahn + * This code is mostly taken from FreeBSD. + * + * + **************************************************************************** + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * 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 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 THE 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. + * + */ + +#ifndef _IA64_CPU_H_ +#define _IA64_CPU_H_ + +#include "ia64_fpu.h" + +/* + * Definition of Region Register bits (RR) + * + * RR bit field positions + */ +#define IA64_RR_VE 0 +#define IA64_RR_MBZ0 1 +#define IA64_RR_PS 2 +#define IA64_RR_PS_LEN 6 +#define IA64_RR_RID 8 +#define IA64_RR_RID_LEN 24 +#define IA64_RR_MBZ1 32 + +#define IA64_RR_IDX_POS 61 + +#define IA64_RR_VAL(size,rid) (((size) << IA64_RR_PS) | ((rid) << IA64_RR_RID)) + +/* + * Define Protection Key Register (PKR) + * + * PKR bit field positions + */ +#define IA64_PKR_V 0 +#define IA64_PKR_WD 1 +#define IA64_PKR_RD 2 +#define IA64_PKR_XD 3 +#define IA64_PKR_MBZ0 4 +#define IA64_PKR_KEY 8 +#define IA64_PKR_KEY_LEN 24 +#define IA64_PKR_MBZ1 32 + +#define IA64_PKR_VALID (1 << IA64_PKR_V) + + +/* + * ITIR bit field positions + */ + +#define IA64_ITIR_MBZ0 0 +#define IA64_ITIR_PS 2 +#define IA64_ITIR_PS_LEN 6 +#define IA64_ITIR_KEY 8 +#define IA64_ITIR_KEY_LEN 24 +#define IA64_ITIR_MBZ1 32 +#define IA64_ITIR_MBZ1_LEN 16 +#define IA64_ITIR_PPN 48 +#define IA64_ITIR_PPN_LEN 15 +#define IA64_ITIR_MBZ2 63 + +/* + * Definition of PSR and IPSR bits. + */ +#define IA64_PSR_BE 0x0000000000000002 +#define IA64_PSR_UP 0x0000000000000004 +#define IA64_PSR_AC 0x0000000000000008 +#define IA64_PSR_MFL 0x0000000000000010 +#define IA64_PSR_MFH_BIT 5 +#define IA64_PSR_MFH (1 << IA64_PSR_MFH_BIT) +#define IA64_PSR_UMASK (IA64_PSR_BE | IA64_PSR_UP | \ + IA64_PSR_AC | IA64_PSR_MFL | \ + IA64_PSR_MFH) +#define IA64_PSR_IC_BIT 13 +#define IA64_PSR_IC (1<func) +#define FDESC_GP(fn) (((struct ia64_fdesc *) fn)->gp) + + +/* + * Various special ia64 instructions. + */ + +/* + * Memory Fence. + */ +static __inline void +ia64_mf(void) +{ + __asm __volatile("mf" ::: "memory"); +} + +static __inline void +ia64_mf_a(void) +{ + __asm __volatile("mf.a"); +} + +/* + * Flush Cache. + */ +static __inline void +ia64_fc(uint64_t va) +{ + __asm __volatile("fc %0" :: "r"(va)); +} + +/* + * Sync instruction stream. + */ +static __inline void +ia64_sync_i(void) +{ + __asm __volatile("sync.i"); +} + +/* + * Calculate address in VHPT for va. + */ +static __inline uint64_t +ia64_thash(uint64_t va) +{ + uint64_t result; + __asm __volatile("thash %0=%1" : "=r" (result) : "r" (va)); + return result; +} + +/* + * Calculate VHPT tag for va. + */ +static __inline uint64_t +ia64_ttag(uint64_t va) +{ + uint64_t result; + __asm __volatile("ttag %0=%1" : "=r" (result) : "r" (va)); + return result; +} + +/* + * Convert virtual address to physical. + */ +static __inline uint64_t +ia64_tpa(uint64_t va) +{ + uint64_t result; + __asm __volatile("tpa %0=%1" : "=r" (result) : "r" (va)); + return result; +} + +/* + * Generate a ptc.e instruction. + */ +static __inline void +ia64_ptc_e(uint64_t v) +{ + __asm __volatile("ptc.e %0;; srlz.d;;" :: "r"(v)); +} + +/* + * Generate a ptc.g instruction. + */ +static __inline void +ia64_ptc_g(uint64_t va, uint64_t size) +{ + __asm __volatile("ptc.g %0,%1;; srlz.d;;" :: "r"(va), "r"(size<<2)); +} + +/* + * Generate a ptc.ga instruction. + */ +static __inline void +ia64_ptc_ga(uint64_t va, uint64_t size) +{ + __asm __volatile("ptc.ga %0,%1;; srlz.d;;" :: "r"(va), "r"(size<<2)); +} + +/* + * Generate a ptc.l instruction. + */ +static __inline void +ia64_ptc_l(uint64_t va, uint64_t size) +{ + __asm __volatile("ptc.l %0,%1;; srlz.d;;" :: "r"(va), "r"(size<<2)); +} + +/* + * Read the value of psr. + */ +static __inline uint64_t +ia64_get_psr(void) +{ + uint64_t result; + __asm __volatile("mov %0=psr;;" : "=r" (result)); + return result; +} + +static __inline void +ia64_set_psr(uint64_t v) +{ + __asm __volatile("mov psr.l=%0" :: "r" (v)); +} + +static __inline void +ia64_srlz_d(void) +{ + __asm __volatile("srlz.d;;"); +} + +static __inline void +disable_intr(void) +{ + __asm __volatile ("rsm psr.ic|psr.i"); +} + +static __inline void +enable_intr(void) +{ + __asm __volatile ("ssm psr.ic|psr.i"); +} + +/* + * Define accessors for application registers. + */ + +#define IA64_AR(name) \ + \ +static __inline uint64_t \ +ia64_get_##name(void) \ +{ \ + uint64_t result; \ + __asm __volatile(";;mov %0=ar." #name ";;" : "=r" (result)); \ + return result; \ +} \ + \ +static __inline void \ +ia64_set_##name(uint64_t v) \ +{ \ + __asm __volatile("mov ar." #name "=%0" :: "r" (v)); \ +} + +IA64_AR(k0) +IA64_AR(k1) +IA64_AR(k2) +IA64_AR(k3) +IA64_AR(k4) +IA64_AR(k5) +IA64_AR(k6) +IA64_AR(k7) + +IA64_AR(rsc) +IA64_AR(bsp) +IA64_AR(bspstore) +IA64_AR(rnat) + +IA64_AR(fcr) + +IA64_AR(eflag) +IA64_AR(csd) +IA64_AR(ssd) +IA64_AR(cflg) +IA64_AR(fsr) +IA64_AR(fir) +IA64_AR(fdr) + +IA64_AR(ccv) + +IA64_AR(unat) + +IA64_AR(fpsr) + +IA64_AR(itc) + +IA64_AR(pfs) +IA64_AR(lc) +IA64_AR(ec) + +/* + * Define accessors for control registers. + */ + +#define IA64_CR(name) \ + \ +static __inline uint64_t \ +ia64_get_##name(void) \ +{ \ + uint64_t result; \ + __asm __volatile("mov %0=cr." #name : "=r" (result)); \ + return result; \ +} \ + \ +static __inline void \ +ia64_set_##name(uint64_t v) \ +{ \ + __asm __volatile("mov cr." #name "=%0" :: "r" (v)); \ +} + +IA64_CR(dcr) +IA64_CR(itm) +IA64_CR(iva) + +IA64_CR(pta) + +IA64_CR(ipsr) +IA64_CR(isr) + +IA64_CR(iip) +IA64_CR(ifa) +IA64_CR(itir) +IA64_CR(iipa) +IA64_CR(ifs) +IA64_CR(iim) +IA64_CR(iha) + +IA64_CR(lid) +IA64_CR(ivr) +IA64_CR(tpr) +IA64_CR(eoi) +IA64_CR(irr0) +IA64_CR(irr1) +IA64_CR(irr2) +IA64_CR(irr3) +IA64_CR(itv) +IA64_CR(pmv) +IA64_CR(cmcv) + +IA64_CR(lrr0) +IA64_CR(lrr1) + +#define IA64_GR(name) \ + \ +static __inline uint64_t \ +ia64_get_##name(void) \ +{ \ + uint64_t result; \ + __asm __volatile("mov %0=" #name : "=r" (result)); \ + return result; \ +} \ + \ +static __inline void \ +ia64_set_##name(uint64_t v) \ +{ \ + __asm __volatile("mov " #name "=%0" :: "r" (v)); \ +} + +IA64_GR(sp) +IA64_GR(b0) +IA64_GR(r13) // tp + + +/* + * Write a region register. + */ +static __inline void +ia64_set_rr(uint64_t rrbase, uint64_t v) +{ + __asm __volatile("mov rr[%0]=%1;; srlz.d;;" + :: "r"(rrbase), "r"(v) : "memory"); +} + +/* + * Read a region register. + */ +static __inline uint64_t +ia64_get_rr(uint64_t rrbase) +{ + uint64_t v; + __asm __volatile("mov %1=rr[%0];;" + : "=r" (v) : "r"(rrbase) : "memory"); + return v; +} + + +/* + * Read a CPUID register. + */ +static __inline uint64_t +ia64_get_cpuid(int i) +{ + uint64_t result; + __asm __volatile("mov %0=cpuid[%1]" + : "=r" (result) : "r"(i)); + return result; +} + + +struct trap_frame +{ + uint64_t rsc; + uint64_t ndirty; /* number of dirty regs */ + uint64_t ssd; + uint64_t iip; /* interrupted ip */ + uint64_t ipsr; /* interrupted psr */ + uint64_t ifs; /* interruption func status register */ + + uint16_t trap_num; /* Trap num, index in trap_vec */ + uint64_t cfm; /* current frame marker */ + uint64_t pfs; /* previous function state ar64 */ + uint64_t bsp; /* backing store pointer ar17 */ + uint64_t rnat; /* rse nat collection ar19 */ + uint64_t csd; /* comp and store data reg ar25 */ + uint64_t ccv; /* comp and xchange val reg ar32 */ + uint64_t unat; /* */ + uint64_t fpsr; /* floating point state reg ar40 */ + uint64_t pr; /* predicate regs 0-63 */ + + uint64_t gp; /* the gp pointer */ + uint64_t sp; /* stack pointer */ + uint64_t tp; /* thread pointer */ + + uint64_t r2; /* global reg 2 */ + uint64_t r3; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + + uint64_t b0; + uint64_t b6; + uint64_t b7; + + ia64_fpreg_t f6; /* floating point register 6 */ + ia64_fpreg_t f7; + ia64_fpreg_t f8; + ia64_fpreg_t f9; + ia64_fpreg_t f10; + ia64_fpreg_t f11; + + uint64_t ifa; /* interruption faulting address */ + uint64_t isr; /* interruption status register */ + uint64_t iim; /* interruption immediate register */ +}; + +typedef struct trap_frame trap_frame_t; + + +#endif /* __ASSEMBLY__ */ + +/* Page access parameters. */ +#define PTE_P_SHIFT 0 +#define PTE_P 1 + +#define PTE_MA_SHIFT 2 +#define PTE_MA_WB 0 + +#define PTE_A_SHIFT 5 +#define PTE_A 1 +#define PTE_D_SHIFT 6 +#define PTE_D 1 + +#define PTE_AR_SHIFT 9 +#define PTE_AR_R 0 +#define PTE_AR_RX 1 +#define PTE_AR_RW 2 +#define PTE_AR_RWX 3 +#define PTE_AR_R_RW 4 +#define PTE_AR_RX_RWX 5 +#define PTE_AR_RWX_RW 6 +/* privilege level */ +#define PTE_PL_SHIFT 7 +#define PTE_PL_KERN 0 /* used for kernel */ +/* page size */ +#define PTE_PS_4K 12 +#define PTE_PS_8K 13 +#define PTE_PS_16K 14 +#define PTE_PS_64K 16 +#define PTE_PS_256K 18 +#define PTE_PS_1M 20 +#define PTE_PS_4M 22 +#define PTE_PS_16M 24 +#define PTE_PS_64M 26 +#define PTE_PS_256M 28 + + + /* Some offsets for ia64_pte_t. */ +#define PTE_OFF_P 0 +#define PTE_OFF_MA 3 +#define PTE_OFF_A 5 +#define PTE_OFF_D 6 +#define PTE_OFF_PL 7 +#define PTE_OFF_AR 9 +#define PTE_OFF_PPN 12 +#define PTE_OFF_ED 52 + +#if !defined(_ASM) && !defined(__ASSEMBLY__) +/* + * A short-format VHPT entry. Also matches the TLB insertion format. + */ +typedef struct +{ +#if defined(BIG_ENDIAN) + uint64_t pte_ig :11; /* bits 53..63 */ + uint64_t pte_ed :1; /* bits 52..52 */ + uint64_t pte_rv2:2; /* bits 50..51 */ + uint64_t pte_ppn:38; /* bits 12..49 */ + uint64_t pte_ar :3; /* bits 9..11 */ + uint64_t pte_pl :2; /* bits 7..8 */ + uint64_t pte_d :1; /* bits 6..6 */ + uint64_t pte_a :1; /* bits 5..5 */ + uint64_t pte_ma :3; /* bits 2..4 */ + uint64_t pte_rv1:1; /* bits 1..1 */ + uint64_t pte_p :1; /* bits 0..0 */ +#else + uint64_t pte_p :1; /* bits 0..0 */ + uint64_t pte_rv1:1; /* bits 1..1 */ + uint64_t pte_ma :3; /* bits 2..4 */ + uint64_t pte_a :1; /* bits 5..5 */ + uint64_t pte_d :1; /* bits 6..6 */ + uint64_t pte_pl :2; /* bits 7..8 */ + uint64_t pte_ar :3; /* bits 9..11 */ + uint64_t pte_ppn:38; /* bits 12..49 */ + uint64_t pte_rv2:2; /* bits 50..51 */ + uint64_t pte_ed :1; /* bits 52..52 */ + uint64_t pte_ig :11; /* bits 53..63 */ +#endif +} ia64_pte_t; + + +/* + * A long-format VHPT entry. + */ +typedef struct +{ + uint64_t pte_p :1; /* bits 0..0 */ + uint64_t pte_rv1 :1; /* bits 1..1 */ + uint64_t pte_ma :3; /* bits 2..4 */ + uint64_t pte_a :1; /* bits 5..5 */ + uint64_t pte_d :1; /* bits 6..6 */ + uint64_t pte_pl :2; /* bits 7..8 */ + uint64_t pte_ar :3; /* bits 9..11 */ + uint64_t pte_ppn :38; /* bits 12..49 */ + uint64_t pte_rv2 :2; /* bits 50..51 */ + uint64_t pte_ed :1; /* bits 52..52 */ + uint64_t pte_ig :11; /* bits 53..63 */ + uint64_t pte_rv3 :2; /* bits 0..1 */ + uint64_t pte_ps :6; /* bits 2..7 */ + uint64_t pte_key :24; /* bits 8..31 */ + uint64_t pte_rv4 :32; /* bits 32..63 */ + uint64_t pte_tag; /* includes ti */ + uint64_t pte_chain; /* pa of collision chain */ +} ia64_lpte_t; + +#endif /* __ASSEMBLY__ */ + +#endif /* _IA64_CPU_H_ */ diff --git a/extras/mini-os/include/ia64/ia64_fpu.h b/extras/mini-os/include/ia64/ia64_fpu.h new file mode 100644 index 0000000..91a17b9 --- /dev/null +++ b/extras/mini-os/include/ia64/ia64_fpu.h @@ -0,0 +1,99 @@ +/* + * Done by Dietmar Hahn + * This code is mostly taken from FreeBSD. + * + **************************************************************************** + * + * 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 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 THE 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. + * + */ + +#ifndef _IA64_FPU_H_ +#define _IA64_FPU_H_ + +#include "os.h" + +/* + * Floating point status register bits. + */ +#define IA64_FPSR_TRAP_VD UL_CONST(0x0000000000000001) +#define IA64_FPSR_TRAP_DD UL_CONST(0x0000000000000002) +#define IA64_FPSR_TRAP_ZD UL_CONST(0x0000000000000004) +#define IA64_FPSR_TRAP_OD UL_CONST(0x0000000000000008) +#define IA64_FPSR_TRAP_UD UL_CONST(0x0000000000000010) +#define IA64_FPSR_TRAP_ID UL_CONST(0x0000000000000020) +#define IA64_FPSR_SF(i,v) ((v) << ((i)*13+6)) + +#define IA64_SF_FTZ UL_CONST(0x0001) +#define IA64_SF_WRE UL_CONST(0x0002) +#define IA64_SF_PC UL_CONST(0x000c) +#define IA64_SF_PC_0 UL_CONST(0x0000) +#define IA64_SF_PC_1 UL_CONST(0x0004) +#define IA64_SF_PC_2 UL_CONST(0x0008) +#define IA64_SF_PC_3 UL_CONST(0x000c) +#define IA64_SF_RC UL_CONST(0x0030) +#define IA64_SF_RC_NEAREST UL_CONST(0x0000) +#define IA64_SF_RC_NEGINF UL_CONST(0x0010) +#define IA64_SF_RC_POSINF UL_CONST(0x0020) +#define IA64_SF_RC_TRUNC UL_CONST(0x0030) +#define IA64_SF_TD UL_CONST(0x0040) +#define IA64_SF_V UL_CONST(0x0080) +#define IA64_SF_D UL_CONST(0x0100) +#define IA64_SF_Z UL_CONST(0x0200) +#define IA64_SF_O UL_CONST(0x0400) +#define IA64_SF_U UL_CONST(0x0800) +#define IA64_SF_I UL_CONST(0x1000) + +#define IA64_SF_DEFAULT (IA64_SF_PC_3 | IA64_SF_RC_NEAREST) + +#define IA64_FPSR_DEFAULT (IA64_FPSR_TRAP_VD \ + | IA64_FPSR_TRAP_DD \ + | IA64_FPSR_TRAP_ZD \ + | IA64_FPSR_TRAP_OD \ + | IA64_FPSR_TRAP_UD \ + | IA64_FPSR_TRAP_ID \ + | IA64_FPSR_SF(0, IA64_SF_DEFAULT) \ + | IA64_FPSR_SF(1, (IA64_SF_DEFAULT \ + | IA64_SF_TD \ + | IA64_SF_WRE)) \ + | IA64_FPSR_SF(2, (IA64_SF_DEFAULT \ + | IA64_SF_TD)) \ + | IA64_FPSR_SF(3, (IA64_SF_DEFAULT \ + | IA64_SF_TD))) + + +#ifndef __ASSEMBLY__ + + /* This is from sys/cdefs.h in FreeBSD */ +#define __aligned(x) __attribute__((__aligned__(x))) + + /* A single Floating Point register. */ +struct ia64_fpreg +{ + uint8_t fpr_bits[16]; +} __aligned(16); + +typedef struct ia64_fpreg ia64_fpreg_t; + +#endif /* __ASSEMBLY__ */ + +#endif /* _IA64_FPU_H_ */ diff --git a/extras/mini-os/include/ia64/os.h b/extras/mini-os/include/ia64/os.h new file mode 100644 index 0000000..2cbfa42 --- /dev/null +++ b/extras/mini-os/include/ia64/os.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2007 - Dietmar Hahn + * + **************************************************************************** + * 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. + */ + + +#if !defined(__OS_H__) +#define __OS_H__ + +#if !defined(__ASSEMBLY__) + +#include +#include "endian.h" +#include "ia64_cpu.h" +#include "atomic.h" +#include "efi.h" +#include "sal.h" +#include "pal.h" +#include +#include + + +typedef uint64_t paddr_t; /* Physical address. */ +#ifndef HAVE_LIBC +typedef uint64_t caddr_t; /* rr7/kernel memory address. */ +#endif + +#include "page.h" +#include "mm.h" + + +void arch_init(start_info_t *si); /* in common.c */ +void arch_print_info(void); /* in common.c */ +void arch_fini(void); + + +/* Size of xen_ia64_boot_param.command_line */ +#define COMMAND_LINE_SIZE 512 + +extern struct xen_ia64_boot_param* ia64_boot_paramP; +extern struct xen_ia64_boot_param ia64BootParamG; +extern char boot_cmd_line[]; +extern efi_system_table_t* efiSysTableP; +extern int bootverbose; + +extern void ia64_probe_sapics(void); + + + +/* Contains the needed stuff from efi. */ +struct efi +{ + + efi_system_table_t* efiSysTableP; + efi_set_virtual_address_map_t setVirtAddrMapF; + efi_get_time_t getTimeF; + efi_reset_system_t resetSystemF; + +}; + +struct machine_fw +{ + struct efi efi; + + uint64_t ia64_port_base; /* physical address */ + uint64_t ia64_pal_base; /* virtual rr7 address */ + + sal_system_table_t* ia64_sal_tableP; + sal_entry_t* ia64_sal_entryP; /* SAL_PROC entrypoint */ + + uint64_t ia64_efi_acpi_table; /* physical address */ + uint64_t ia64_efi_acpi20_table; /* physical address */ + + uint64_t mach_mem_start; /* phys start addr of machine memory */ + uint64_t mach_mem_size; /* size of machine memory */ + + uint64_t kernstart; /* virt address of kern text start */ + uint64_t kernend; + uint64_t kernpstart; /* phys address of kern text start */ + uint64_t kernpend; +}; + +extern struct machine_fw machineFwG; + +#define ia64_sal_entry machineFwG.ia64_sal_entryP + +#define smp_processor_id() 0 + +static inline uint64_t +xchg8(uint64_t* ptr, uint64_t x) \ +{ + uint64_t oldVal; + asm volatile ("xchg8 %0=[%1],%2" : "=r" (oldVal) + : "r" (ptr), "r" (x) : "memory"); + return oldVal; +} +#define xchg xchg8 + +// Counts the number of 1-bits in x. +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define get_popcnt(x) __builtin_popcountl(x) +#else +# define get_popcnt(x) \ + ({ \ + uint64_t num; \ + asm ("popcnt %0=%1" : "=r" (num) : "r" (x)); \ + num; \ + }) +#endif + +/** + * __ffs - find first bit in word. + * @x: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static inline unsigned long +__ffs (unsigned long x) +{ + unsigned long result; + + result = get_popcnt((x-1) & ~x); + return result; +} + + +static inline void +synch_clear_bit(int num, volatile void *addr) +{ + clear_bit(num, addr); +} + +static inline void +synch_set_bit(int num, volatile void *addr) +{ + set_bit(num, addr); +} + +static inline int +synch_test_bit(int nr, const volatile void *addr) +{ + return test_bit(nr, addr); +} + +static inline int +synch_test_and_set_bit(int num, volatile void * addr) +{ + return test_and_set_bit(num, addr); +} + + +#define synch_cmpxchg(ptr, old, new) \ +((__typeof__(*(ptr)))__synch_cmpxchg((ptr),\ + (unsigned long)(old), \ + (unsigned long)(new), \ + sizeof(*(ptr)))) + +static inline unsigned long +__synch_cmpxchg(volatile void *ptr, uint64_t old, uint64_t new, int size) +{ + switch (size) + { + case 1: + return ia64_cmpxchg_acq_8(ptr, old, new); + case 2: + return ia64_cmpxchg_acq_16(ptr, old, new); + case 4: + return ia64_cmpxchg_acq_32(ptr, old, new); + case 8: + return ia64_cmpxchg_acq_64(ptr, old, new); + } + return ia64_cmpxchg_acq_64(ptr, old, new); +} + +extern shared_info_t *HYPERVISOR_shared_info; + +static inline int +HYPERVISOR_shutdown(unsigned int reason) +{ + struct sched_shutdown sched_shutdown = { + .reason = reason + }; + + int rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + + if (rc == -ENOSYS) + rc = HYPERVISOR_sched_op_compat(SCHEDOP_shutdown, reason); + + return rc; +} + + +/* + * This code is from the originally os.h and should be put in a + * common header file! + */ + +/* + * The use of 'barrier' in the following reflects their use as local-lock + * operations. Reentrancy must be prevented (e.g., __cli()) /before/ following + * critical operations are executed. All critical operations must complete + * /before/ reentrancy is permitted (e.g., __sti()). Alpha architecture also + * includes these barriers, for example. + */ + +#define __cli() \ +do { \ + vcpu_info_t *_vcpu; \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + _vcpu->evtchn_upcall_mask = SWAP(1); \ + barrier(); \ +} while (0) + +#define __sti() \ +do { \ + vcpu_info_t *_vcpu; \ + barrier(); \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + _vcpu->evtchn_upcall_mask = 0; \ + barrier(); /* unmask then check (avoid races) */ \ + if (unlikely(SWAP(_vcpu->evtchn_upcall_pending))) \ + force_evtchn_callback(); \ +} while (0) + +#define __save_flags(x) \ +do { \ + vcpu_info_t *_vcpu; \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + (x) = SWAP(_vcpu->evtchn_upcall_mask); \ +} while (0) + +#define __restore_flags(x) \ +do { \ + vcpu_info_t *_vcpu; \ + barrier(); \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + if ((_vcpu->evtchn_upcall_mask = (x)) == 0) { \ + barrier(); /* unmask then check (avoid races) */ \ + if ( unlikely(SWAP(_vcpu->evtchn_upcall_pending)) ) \ + force_evtchn_callback(); \ + }\ +} while (0) + +#define safe_halt() ((void)0) + +#define __save_and_cli(x) \ +do { \ + vcpu_info_t *_vcpu; \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + (x) = SWAP(_vcpu->evtchn_upcall_mask); \ + _vcpu->evtchn_upcall_mask = SWAP(1); \ + barrier(); \ +} while (0) + +#define local_irq_save(x) __save_and_cli(x) +#define local_irq_restore(x) __restore_flags(x) +#define local_save_flags(x) __save_flags(x) +#define local_irq_disable() __cli() +#define local_irq_enable() __sti() + +#define irqs_disabled() \ + SWAP(HYPERVISOR_shared_info->vcpu_info[smp_processor_id()].evtchn_upcall_mask) + +/* This is a barrier for the compiler only, NOT the processor! */ +#define barrier() __asm__ __volatile__("": : :"memory") + +#define mb() ia64_mf() +#define rmb() mb() +#define wmb() mb() + + +#define BUG() \ + { printk("mini-os BUG at %s:%d!\n", __FILE__, __LINE__); do_exit(); } + +#define PRINT_BV(_fmt, _params...) \ + if (bootverbose) \ + printk(_fmt , ## _params) + +#endif /* !defined(__ASSEMBLY__) */ + +#if defined(__ASSEMBLY__) + +#define UL_CONST(x) x +#define UL_TYPE(x) x + +#else /* defined(__ASSEMBLY__) */ + +#define UL_CONST(x) x##UL +#define UL_TYPE(x) ((uint64_t)x) + +#endif /* defined(__ASSEMBLY__) */ + +#endif /* !defined(__OS_H__) */ diff --git a/extras/mini-os/include/ia64/page.h b/extras/mini-os/include/ia64/page.h new file mode 100644 index 0000000..67be0a5 --- /dev/null +++ b/extras/mini-os/include/ia64/page.h @@ -0,0 +1,110 @@ +/* + * Done by Dietmar Hahn + * Common stuff for memory and page handling. + * Parts are taken from FreeBSD. + * + **************************************************************************** + * 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. + */ + + +#if !defined(_PAGE_H_) +#define _PAGE_H_ + +#include "os.h" +#include "ia64_cpu.h" + +#define PTE_KERNEL_ATTR ((PTE_P<> 61) + +#define IA64_PHYS_TO_RR5(x) ((x) | IA64_RR_BASE(5)) +#define IA64_PHYS_TO_RR7(x) ((x) | IA64_RR_BASE(7)) + +#define __pa(x) IA64_RR_MASK(x) +#define __va(x) IA64_PHYS_TO_RR7(x) + +#define roundup_page(x) ((((unsigned long)(x)) + PAGE_SIZE -1) & PAGE_MASK) +#define trunc_page(x) ((unsigned long)(x) & PAGE_MASK) + + +#if !defined(__ASSEMBLY__) + +/* Contains the parts of the physically memory. */ +extern paddr_t phys_avail[]; + +#define page_to_pfn(page) ((uint64_t)(page) >> PAGE_SHIFT) +#define pfn_to_page(pfn) ((uint64_t)pfn << PAGE_SHIFT) +/* Get phyiscal address of page of virtual address. */ +#define virt_to_page(addr) ((uint64_t)__pa(addr) & PAGE_MASK) +#define virt_to_pfn(addr) (page_to_pfn(virt_to_page(addr))) + + +#endif /* __ASSEMBLY__ */ + + +/* For both see minios-ia64.lds. */ +/* This is where the kernel virtually starts. */ +#define KERNEL_START IA64_PHYS_TO_RR5(0x100000000) +/* !!!!! + * For physical start of kernel + * Currently used in arch/ia64/fw.S. + * !!!!! + */ +#define KERNEL_PHYS_START_SHIFT 20 + +/* A region 5 address to physical address */ +#define KERN_VIRT_2_PHYS(x) (((x) - KERNEL_START) + \ + (1 << KERNEL_PHYS_START_SHIFT)) + +/* Some protection keys for region 5 and 7 addresses. */ +#define IA64_KEY_REG7 0x234 /* Region 7 - identity mapped addresses */ +#define IA64_KEY_REG5 0x89a /* Region 5 - kernel addresses */ + +// This is xen specific ! +#define PAGE_SHIFT_XEN_16K 14 // For 16KB page size +#define mfn_to_virt(mfn) ((void*)__va((mfn) << PAGE_SHIFT_XEN_16K)) + +#endif /* !defined(_PAGE_H_) */ diff --git a/extras/mini-os/include/ia64/pal.h b/extras/mini-os/include/ia64/pal.h new file mode 100644 index 0000000..8eb853d --- /dev/null +++ b/extras/mini-os/include/ia64/pal.h @@ -0,0 +1,87 @@ +/* + * Done by Dietmar Hahn + * The code is mostly taken from FreeBSD. + * + **************************************************************************** + * + * 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 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 THE 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. + * + */ + +#ifndef _PAL_H_ +#define _PAL_H_ + +/* + * Architected static calling convention procedures. + */ +#define PAL_CACHE_FLUSH 1 +#define PAL_CACHE_INFO 2 +#define PAL_CACHE_INIT 3 +#define PAL_CACHE_SUMMARY 4 +#define PAL_MEM_ATTRIB 5 +#define PAL_PTCE_INFO 6 +#define PAL_VM_INFO 7 +#define PAL_VM_SUMMARY 8 +#define PAL_BUS_GET_FEATURES 9 +#define PAL_BUS_SET_FEATURES 10 +#define PAL_DEBUG_INFO 11 +#define PAL_FIXED_ADDR 12 +#define PAL_FREQ_BASE 13 +#define PAL_FREQ_RATIOS 14 +#define PAL_PERF_MON_INFO 15 +#define PAL_PLATFORM_ADDR 16 +#define PAL_PROC_GET_FEATURE 17 +#define PAL_PROC_SET_FEATURE 18 +#define PAL_RSE_INFO 19 +#define PAL_VERSION 20 +#define PAL_MC_CLEAR_LOG 21 +#define PAL_MC_DRAIN 22 +#define PAL_MC_DYNAMIC_STATE 24 +#define PAL_MC_ERROR_INFO 25 +#define PAL_MC_EXPECTED 23 +#define PAL_MC_REGISTER_MEM 27 +#define PAL_MC_RESUME 26 +#define PAL_HALT 28 +#define PAL_HALT_LIGHT 29 +#define PAL_COPY_INFO 30 +#define PAL_CACHE_LINE_INIT 31 +#define PAL_PMI_ENTRYPOINT 32 +#define PAL_ENTER_IA_32_ENV 33 +#define PAL_VM_PAGE_SIZE 34 +#define PAL_MEM_FOR_TEST 37 +#define PAL_CACHE_PROT_INFO 38 +#define PAL_REGISTER_INFO 39 +#define PAL_SHUTDOWN 40 +#define PAL_PREFETCH_VISIBILITY 41 + + +struct ia64_pal_result +{ + int64_t pal_status; + uint64_t pal_result[3]; +}; + +extern struct ia64_pal_result + ia64_call_pal_static(uint64_t proc, uint64_t arg1, + uint64_t arg2, uint64_t arg3); + +#endif /* _PAL_H_ */ diff --git a/extras/mini-os/include/ia64/privop.h b/extras/mini-os/include/ia64/privop.h new file mode 100644 index 0000000..d8cdbae --- /dev/null +++ b/extras/mini-os/include/ia64/privop.h @@ -0,0 +1,97 @@ + +/* + * Copyright (C) 2005 Hewlett-Packard Co + * Dan Magenheimer + * + * Paravirtualizations of privileged operations for Xen/ia64 + * + */ + +#ifndef _PRIVOP_H_ +#define _PRIVOP_H_ + +#include + +#define IA64_PARAVIRTUALIZED + +/* At 1 MB, before per-cpu space but still addressable using addl instead + of movl. */ +#define XSI_BASE 0xfffffffffff00000 + +/* Address of mapped regs. */ +#define XMAPPEDREGS_BASE (XSI_BASE + XSI_SIZE) + +#ifdef __ASSEMBLY__ +#define XEN_HYPER_RFI break HYPERPRIVOP_RFI +#define XEN_HYPER_RSM_PSR_DT break HYPERPRIVOP_RSM_DT +#define XEN_HYPER_SSM_PSR_DT break HYPERPRIVOP_SSM_DT +#define XEN_HYPER_COVER break HYPERPRIVOP_COVER +#define XEN_HYPER_ITC_D break HYPERPRIVOP_ITC_D +#define XEN_HYPER_ITC_I break HYPERPRIVOP_ITC_I +#define XEN_HYPER_SSM_I break HYPERPRIVOP_SSM_I +#define XEN_HYPER_GET_IVR break HYPERPRIVOP_GET_IVR +#define XEN_HYPER_GET_TPR break HYPERPRIVOP_GET_TPR +#define XEN_HYPER_SET_TPR break HYPERPRIVOP_SET_TPR +#define XEN_HYPER_EOI break HYPERPRIVOP_EOI +#define XEN_HYPER_SET_ITM break HYPERPRIVOP_SET_ITM +#define XEN_HYPER_THASH break HYPERPRIVOP_THASH +#define XEN_HYPER_PTC_GA break HYPERPRIVOP_PTC_GA +#define XEN_HYPER_ITR_D break HYPERPRIVOP_ITR_D +#define XEN_HYPER_GET_RR break HYPERPRIVOP_GET_RR +#define XEN_HYPER_SET_RR break HYPERPRIVOP_SET_RR +#define XEN_HYPER_SET_KR break HYPERPRIVOP_SET_KR +#define XEN_HYPER_FC break HYPERPRIVOP_FC +#define XEN_HYPER_GET_CPUID break HYPERPRIVOP_GET_CPUID +#define XEN_HYPER_GET_PMD break HYPERPRIVOP_GET_PMD +#define XEN_HYPER_GET_EFLAG break HYPERPRIVOP_GET_EFLAG +#define XEN_HYPER_SET_EFLAG break HYPERPRIVOP_SET_EFLAG +#define XEN_HYPER_RSM_BE break HYPERPRIVOP_RSM_BE +#define XEN_HYPER_GET_PSR break HYPERPRIVOP_GET_PSR + +#define XSI_IFS (XSI_BASE + XSI_IFS_OFS) +#define XSI_PRECOVER_IFS (XSI_BASE + XSI_PRECOVER_IFS_OFS) +#define XSI_INCOMPL_REGFR (XSI_BASE + XSI_INCOMPL_REGFR_OFS) +#define XSI_IFA (XSI_BASE + XSI_IFA_OFS) +#define XSI_ISR (XSI_BASE + XSI_ISR_OFS) +#define XSI_IIM (XSI_BASE + XSI_IIM_OFS) +#define XSI_ITIR (XSI_BASE + XSI_ITIR_OFS) +#define XSI_PSR_I_ADDR (XSI_BASE + XSI_PSR_I_ADDR_OFS) +#define XSI_PSR_IC (XSI_BASE + XSI_PSR_IC_OFS) +#define XSI_IPSR (XSI_BASE + XSI_IPSR_OFS) +#define XSI_IIP (XSI_BASE + XSI_IIP_OFS) +#define XSI_BANK1_R16 (XSI_BASE + XSI_BANK1_R16_OFS) +#define XSI_BANKNUM (XSI_BASE + XSI_BANKNUM_OFS) +#define XSI_IHA (XSI_BASE + XSI_IHA_OFS) +#endif + +#ifndef __ASSEMBLY__ +#define XEN_HYPER_SSM_I asm("break %0" : : "i" (HYPERPRIVOP_SSM_I)) +#define XEN_HYPER_GET_IVR asm("break %0" : : "i" (HYPERPRIVOP_GET_IVR)) + +/************************************************/ +/* Instructions paravirtualized for performance */ +/************************************************/ + +/* Xen uses memory-mapped virtual privileged registers for access to many + * performance-sensitive privileged registers. Some, like the processor + * status register (psr), are broken up into multiple memory locations. + * Others, like "pend", are abstractions based on privileged registers. + * "Pend" is guaranteed to be set if reading cr.ivr would return a + * (non-spurious) interrupt. */ +#define XEN_MAPPEDREGS ((struct mapped_regs *)XMAPPEDREGS_BASE) +#define XSI_PSR_I \ + (*XEN_MAPPEDREGS->interrupt_mask_addr) +#define xen_get_virtual_psr_i() \ + (!XSI_PSR_I) +#define xen_set_virtual_psr_i(_val) \ + ({ XSI_PSR_I = (uint8_t)(_val) ? 0 : 1; }) +#define xen_get_virtual_psr_ic() \ + ( XEN_MAPPEDREGS->interrupt_collection_enabled ) +#define xen_set_virtual_psr_ic(_val) \ + ({ XEN_MAPPEDREGS->interrupt_collection_enabled = _val ? 1 : 0; }) +#define xen_get_virtual_pend() (XEN_MAPPEDREGS->pending_interruption) + +#endif /* __ASSEMBLY__ */ + +#endif /* _PRIVOP_H_ */ + diff --git a/extras/mini-os/include/ia64/sal.h b/extras/mini-os/include/ia64/sal.h new file mode 100644 index 0000000..c0271c7 --- /dev/null +++ b/extras/mini-os/include/ia64/sal.h @@ -0,0 +1,188 @@ +/* + * Done by Dietmar Hahn + * The code is mostly taken from FreeBSD. + * + **************************************************************************** + * + * 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 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 THE 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. + * + */ + + +/* + * The SAL System Table starts with a header which is described in + * sal_system_table_t. + * Table header will be followed by a variable number of variable length + * entries. The first byte of each entry will identify the entry type and + * the entries shall be in ascending order by the entry type. Each entry + * type will have a known fixed length. The total length of this table + * depends upon the configuration of the system. Operating system software + * must step through each entry until it reaches the ENTRY_COUNT. The entries + * are sorted on entry type in ascending order. + * Unless otherwise stated, there is one entry per entry type. + */ + +#ifndef _SAL_H_ +#define _SAL_H_ + +typedef uint64_t u_int64_t; +typedef uint32_t u_int32_t; +typedef uint16_t u_int16_t; +typedef uint8_t u_int8_t; + +struct sal_system_table { + char sal_signature[4]; +#define SAL_SIGNATURE "SST_" + uint32_t sal_length; + uint8_t sal_rev[2]; /* Byte 8 - Minor, Byte 0 - Major */ + uint16_t sal_entry_count; // num entries in var part + uint8_t sal_checksum; + uint8_t sal_reserved1[7]; + uint8_t sal_a_version[2]; // like sal_rev + uint8_t sal_b_version[2]; // like sal_rev + char sal_oem_id[32]; // Ascii - manufacturer of HW + char sal_product_id[32]; // ascii - identification + uint8_t sal_reserved2[8]; +}; + +typedef struct sal_system_table sal_system_table_t; + +#define SAL_DESC_ENTRYPOINT 0 +#define SAL_DESC_ENTRYPOINT_LENGTH 48 +#define SAL_DESC_MEMORY 1 +#define SAL_DESC_MEMORY_LENGTH 32 +#define SAL_DESC_PLATFORM 2 +#define SAL_DESC_PLATFORM_LENGT 16 +#define SAL_DESC_TR_REG 3 +#define SAL_DESC_TR_REG_LENGTH 32 +#define SAL_DESC_PURGE_TR_CACHE 4 +#define SAL_DESC_PURGE_TR_CACHE_LENGTH 16 +#define SAL_DESC_AP_WAKEUP 5 +#define SAL_DESC_AP_WAKEUP_LENGTH 16 + + +struct sal_entrypoint_descriptor +{ + uint8_t sale_type; /* == 0 */ + uint8_t sale_reserved1[7]; + uint64_t sale_pal_proc; /* PAL_PROC entry point */ + uint64_t sale_sal_proc; /* SAL_PROC entry point */ + uint64_t sale_sal_gp; /* gp for SAL_PROC, PAL_PROC */ + uint8_t sale_reserved2[16]; +}; + +struct sal_memory_descriptor +{ + uint8_t sale_type; /* == 1 */ + uint8_t sale_need_virtual; + uint8_t sale_current_attribute; + uint8_t sale_access_rights; + uint8_t sale_supported_attributes; + uint8_t sale_reserved1; + uint8_t sale_memory_type[2]; + uint64_t sale_physical_address; + uint32_t sale_length; + uint8_t sale_reserved2[12]; +}; + +struct sal_platform_descriptor +{ + uint8_t sale_type; /* == 2 */ + uint8_t sale_features; + uint8_t sale_reserved[14]; +}; + +struct sal_tr_descriptor +{ + u_int8_t sale_type; /* == 3 */ + u_int8_t sale_register_type; + u_int8_t sale_register_number; + u_int8_t sale_reserved1[5]; + u_int64_t sale_virtual_address; + u_int64_t sale_page_size; + u_int8_t sale_reserved2[8]; +}; + +struct sal_ptc_cache_descriptor +{ + uint8_t sale_type; /* == 4 */ + uint8_t sale_reserved[3]; + uint32_t sale_domains; + uint64_t sale_address; +}; + +struct sal_ap_wakeup_descriptor +{ + uint8_t sale_type; /* == 5 */ + uint8_t sale_mechanism; + uint8_t sale_reserved[6]; + uint64_t sale_vector; +}; + +/* + * SAL Procedure numbers. + */ + +#define SAL_SET_VECTORS 0x01000000 +#define SAL_GET_STATE_INFO 0x01000001 +#define SAL_GET_STATE_INFO_SIZE 0x01000002 +#define SAL_CLEAR_STATE_INFO 0x01000003 +#define SAL_MC_RENDEZ 0x01000004 +#define SAL_MC_SET_PARAMS 0x01000005 +#define SAL_REGISTER_PHYSICAL_ADDR 0x01000006 +#define SAL_CACHE_FLUSH 0x01000008 +#define SAL_CACHE_INIT 0x01000009 +#define SAL_PCI_CONFIG_READ 0x01000010 +#define SAL_PCI_CONFIG_WRITE 0x01000011 +#define SAL_FREQ_BASE 0x01000012 +#define SAL_UPDATE_PAL 0x01000020 + +/* SAL_SET_VECTORS event handler types */ +#define SAL_OS_MCA 0 +#define SAL_OS_INIT 1 +#define SAL_OS_BOOT_RENDEZ 2 + +/* SAL_GET_STATE_INFO, SAL_GET_STATE_INFO_SIZE types */ +#define SAL_INFO_MCA 0 +#define SAL_INFO_INIT 1 +#define SAL_INFO_CMC 2 +#define SAL_INFO_CPE 3 +#define SAL_INFO_TYPES 4 /* number of types we know about */ + +struct ia64_sal_result +{ + int64_t sal_status; + uint64_t sal_result[3]; +}; +typedef struct ia64_sal_result ia64_sal_result_t; + +typedef ia64_sal_result_t sal_entry_t + ( uint64_t, uint64_t, uint64_t, uint64_t, + uint64_t, uint64_t, uint64_t, uint64_t + ); + +extern ia64_sal_result_t ia64_sal_call(uint64_t, uint64_t, uint64_t, uint64_t, + uint64_t, uint64_t, uint64_t, uint64_t); + +extern void ia64_sal_init(sal_system_table_t *saltab); + +#endif /* _SAL_H_ */ diff --git a/extras/mini-os/include/ia64/traps.h b/extras/mini-os/include/ia64/traps.h new file mode 100644 index 0000000..cd5589c --- /dev/null +++ b/extras/mini-os/include/ia64/traps.h @@ -0,0 +1,54 @@ +/* + * Done by Dietmar Hahn + * + **************************************************************************** + * 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. + */ + +#if !defined(_TRAPS_H_) +#define _TRAPS_H_ + +#if !defined(__ASSEMBLY__) + +/* See ia64_cpu.h */ +struct trap_frame; + +#define pt_regs trap_frame + +/* + * A dummy function, which is currently not supported. + */ +inline static void trap_init(void) +{ + //printk("trap_init() until now not needed!\n"); +} +inline static void trap_fini(void) +{ + //printk("trap_fini() until now not needed!\n"); +} + + +#endif /* !defined(__ASSEMBLY__) */ + +#include "ia64_cpu.h" + +void stack_walk(void); + +#endif /* !defined(_TRAPS_H_) */ + diff --git a/extras/mini-os/include/kernel.h b/extras/mini-os/include/kernel.h new file mode 100644 index 0000000..b36f172 --- /dev/null +++ b/extras/mini-os/include/kernel.h @@ -0,0 +1,7 @@ +#ifndef _KERNEL_H_ +#define _KERNEL_H_ + +extern void do_exit(void) __attribute__((noreturn)); +extern void stop_kernel(void); + +#endif /* _KERNEL_H_ */ diff --git a/extras/mini-os/include/lib.h b/extras/mini-os/include/lib.h new file mode 100644 index 0000000..8822dd1 --- /dev/null +++ b/extras/mini-os/include/lib.h @@ -0,0 +1,201 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + **************************************************************************** + * + * File: lib.h + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: + * + * Date: Aug 2003 + * + * Environment: Xen Minimal OS + * Description: Random useful library functions, contains some freebsd stuff + * + **************************************************************************** + * $Id: h-insert.h,v 1.4 2002/11/08 16:03:55 rn Exp $ + **************************************************************************** + * + *- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)stdarg.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/i386/include/stdarg.h,v 1.10 1999/08/28 00:44:26 peter Exp $ + */ + +#ifndef _LIB_H_ +#define _LIB_H_ + +#include +#include +#include +#include +#include "gntmap.h" + +#ifdef HAVE_LIBC +#include +#else +/* printing */ +#define _p(_x) ((void *)(unsigned long)(_x)) +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); +int vscnprintf(char *buf, size_t size, const char *fmt, va_list args); +int snprintf(char * buf, size_t size, const char *fmt, ...); +int scnprintf(char * buf, size_t size, const char *fmt, ...); +int vsprintf(char *buf, const char *fmt, va_list args); +int sprintf(char * buf, const char *fmt, ...); +int vsscanf(const char * buf, const char * fmt, va_list args); +int sscanf(const char * buf, const char * fmt, ...); +#endif + +long simple_strtol(const char *cp,char **endp,unsigned int base); +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base); +long long simple_strtoll(const char *cp,char **endp,unsigned int base); +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base); + +#ifdef HAVE_LIBC +#include +#else +/* string and memory manipulation */ +int memcmp(const void *cs, const void *ct, size_t count); +void *memcpy(void *dest, const void *src, size_t count); +int strncmp(const char *cs, const char *ct, size_t count); +int strcmp(const char *cs, const char *ct); +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, size_t count); +void *memset(void *s,int c, size_t count); +size_t strnlen(const char *s, size_t count); +size_t strlen(const char *s); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strstr(const char *s1, const char *s2); +char * strcat(char * dest, const char * src); +char *strdup(const char *s); +#endif +#include + +#define RAND_MIX 2654435769U + +int rand(void); + +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +struct kvec { + void *iov_base; + size_t iov_len; +}; + +#define ASSERT(x) \ +do { \ + if (!(x)) { \ + printk("ASSERTION FAILED: %s at %s:%d.\n", \ + # x , \ + __FILE__, \ + __LINE__); \ + BUG(); \ + } \ +} while(0) + +#define BUG_ON(x) ASSERT(!(x)) + +/* Consistency check as much as possible. */ +void sanity_check(void); + +#ifdef HAVE_LIBC +enum fd_type { + FTYPE_NONE = 0, + FTYPE_CONSOLE, + FTYPE_FILE, + FTYPE_XENBUS, + FTYPE_XC, + FTYPE_EVTCHN, + FTYPE_GNTMAP, + FTYPE_SOCKET, + FTYPE_TAP, + FTYPE_BLK, + FTYPE_KBD, + FTYPE_FB, +}; + +#define MAX_EVTCHN_PORTS 16 + +extern struct file { + enum fd_type type; + union { + struct { + /* lwIP fd */ + int fd; + } socket; + struct { + /* FS import fd */ + int fd; + off_t offset; + } file; + struct { + /* To each event channel FD is associated a series of ports which + * wakes select for this FD. */ + struct { + evtchn_port_t port; + unsigned long pending; + int bound; + } ports[MAX_EVTCHN_PORTS]; + } evtchn; + struct gntmap gntmap; + struct { + struct netfront_dev *dev; + } tap; + struct { + struct blkfront_dev *dev; + } blk; + struct { + struct kbdfront_dev *dev; + } kbd; + struct { + struct fbfront_dev *dev; + } fb; + struct { + /* To each xenbus FD is associated a queue of watch events for this + * FD. */ + xenbus_event_queue events; + } xenbus; + }; + int read; /* maybe available for read */ +} files[]; + +int alloc_fd(enum fd_type type); +void close_all_files(void); +extern struct thread *main_thread; +void sparse(unsigned long data, size_t size); +#endif + +#endif /* _LIB_H_ */ diff --git a/extras/mini-os/include/linux/types.h b/extras/mini-os/include/linux/types.h new file mode 100644 index 0000000..978f29e --- /dev/null +++ b/extras/mini-os/include/linux/types.h @@ -0,0 +1,5 @@ +#ifndef _LINUX_TYPES_H_ +#define _LINUX_TYPES_H_ +#include +typedef u64 __u64; +#endif /* _LINUX_TYPES_H_ */ diff --git a/extras/mini-os/include/list.h b/extras/mini-os/include/list.h new file mode 100644 index 0000000..a60ae23 --- /dev/null +++ b/extras/mini-os/include/list.h @@ -0,0 +1,190 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * 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 minios_list_head { + struct minios_list_head *next, *prev; +}; + +#define MINIOS_LIST_HEAD_INIT(name) { &(name), &(name) } + +#define MINIOS_LIST_HEAD(name) \ + struct minios_list_head name = MINIOS_LIST_HEAD_INIT(name) + +#define MINIOS_INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +#define minios_list_top(head, type, member) \ +({ \ + struct minios_list_head *_head = (head); \ + minios_list_empty(_head) ? NULL : minios_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 __minios_list_add(struct minios_list_head * new, + struct minios_list_head * prev, + struct minios_list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * minios_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 minios_list_add(struct minios_list_head *new, struct minios_list_head *head) +{ + __minios_list_add(new, head, head->next); +} + +/** + * minios_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 minios_list_add_tail(struct minios_list_head *new, struct minios_list_head *head) +{ + __minios_list_add(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 __minios_list_del(struct minios_list_head * prev, + struct minios_list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * minios_list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: minios_list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ void minios_list_del(struct minios_list_head *entry) +{ + __minios_list_del(entry->prev, entry->next); +} + +/** + * minios_list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void minios_list_del_init(struct minios_list_head *entry) +{ + __minios_list_del(entry->prev, entry->next); + MINIOS_INIT_LIST_HEAD(entry); +} + +/** + * minios_list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int minios_list_empty(struct minios_list_head *head) +{ + return head->next == head; +} + +/** + * minios_list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void minios_list_splice(struct minios_list_head *list, struct minios_list_head *head) +{ + struct minios_list_head *first = list->next; + + if (first != list) { + struct minios_list_head *last = list->prev; + struct minios_list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * minios_list_entry - get the struct for this entry + * @ptr: the &struct minios_list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the minios_list_struct within the struct. + */ +#define minios_list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * minios_list_for_each - iterate over a list + * @pos: the &struct minios_list_head to use as a loop counter. + * @head: the head for your list. + */ +#define minios_list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * minios_list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct minios_list_head to use as a loop counter. + * @n: another &struct minios_list_head to use as temporary storage + * @head: the head for your list. + */ +#define minios_list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * minios_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 minios_list_struct within the struct. + */ +#define minios_list_for_each_entry(pos, head, member) \ + for (pos = minios_list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = minios_list_entry(pos->member.next, typeof(*pos), member)) + +/** + * minios_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 minios_list_struct within the struct. + */ +#define minios_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = minios_list_entry((head)->next, typeof(*pos), member), \ + n = minios_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = minios_list_entry(n->member.next, typeof(*n), member)) +#endif /* _LINUX_LIST_H */ + diff --git a/extras/mini-os/include/lwipopts.h b/extras/mini-os/include/lwipopts.h new file mode 100644 index 0000000..bc5555e --- /dev/null +++ b/extras/mini-os/include/lwipopts.h @@ -0,0 +1,23 @@ +/* + * lwipopts.h + * + * Configuration for lwIP running on mini-os + * + * Tim Deegan , July 2007 + */ + +#ifndef __LWIP_LWIPOPTS_H__ +#define __LWIP_LWIPOPTS_H__ + +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_LIBC_MALLOC 1 +#define LWIP_TIMEVAL_PRIVATE 0 +#define LWIP_DHCP 1 +#define LWIP_COMPAT_SOCKETS 0 +#define LWIP_IGMP 1 +#define LWIP_USE_HEAP_FROM_INTERRUPT 1 +#define MEMP_NUM_SYS_TIMEOUT 10 +#define TCP_SND_BUF 3000 +#define TCP_MSS 1500 + +#endif /* __LWIP_LWIPOPTS_H__ */ diff --git a/extras/mini-os/include/mm.h b/extras/mini-os/include/mm.h new file mode 100644 index 0000000..32ce7d9 --- /dev/null +++ b/extras/mini-os/include/mm.h @@ -0,0 +1,81 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + * + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * Copyright (c) 2005, Keir A Fraser + * + * 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. + */ + +#ifndef _MM_H_ +#define _MM_H_ + +#if defined(__i386__) +#include +#elif defined(__x86_64__) +#include +#elif defined(__ia64__) +#include +#else +#error "Unsupported architecture" +#endif + +#include + +#include +#include + +#define STACK_SIZE_PAGE_ORDER __STACK_SIZE_PAGE_ORDER +#define STACK_SIZE __STACK_SIZE + + +void init_mm(void); +unsigned long alloc_pages(int order); +#define alloc_page() alloc_pages(0) +void free_pages(void *pointer, int order); +#define free_page(p) free_pages(p, 0) + +static __inline__ int get_order(unsigned long size) +{ + int order; + size = (size-1) >> PAGE_SHIFT; + for ( order = 0; size; order++ ) + size >>= 1; + return order; +} + +void arch_init_demand_mapping_area(unsigned long max_pfn); +void arch_init_mm(unsigned long* start_pfn_p, unsigned long* max_pfn_p); +void arch_init_p2m(unsigned long max_pfn_p); + +unsigned long allocate_ondemand(unsigned long n, unsigned long alignment); +/* map f[i*stride]+i*increment for i in 0..n-1, aligned on alignment pages */ +void *map_frames_ex(unsigned long *f, unsigned long n, unsigned long stride, + unsigned long increment, unsigned long alignment, domid_t id, + int may_fail, unsigned long prot); +void do_map_frames(unsigned long addr, + unsigned long *f, unsigned long n, unsigned long stride, + unsigned long increment, domid_t id, int may_fail, unsigned long prot); +#ifdef HAVE_LIBC +extern unsigned long heap, brk, heap_mapped, heap_end; +#endif + +int free_physical_pages(xen_pfn_t *mfns, int n); +void fini_mm(void); + +#endif /* _MM_H_ */ diff --git a/extras/mini-os/include/netfront.h b/extras/mini-os/include/netfront.h new file mode 100644 index 0000000..2b95da9 --- /dev/null +++ b/extras/mini-os/include/netfront.h @@ -0,0 +1,24 @@ +#include +#ifdef HAVE_LWIP +#include +#endif +struct netfront_dev; +struct netfront_dev *init_netfront(char *nodename, void (*netif_rx)(unsigned char *data, int len), unsigned char rawmac[6], char **ip); +void netfront_xmit(struct netfront_dev *dev, unsigned char* data,int len); +void shutdown_netfront(struct netfront_dev *dev); +#ifdef HAVE_LIBC +int netfront_tap_open(char *nodename); +ssize_t netfront_receive(struct netfront_dev *dev, unsigned char *data, size_t len); +#endif + +extern struct wait_queue_head netfront_queue; + +#ifdef HAVE_LWIP +/* Call this to bring up the netfront interface and the lwIP stack. + * N.B. _must_ be called from a thread; it's not safe to call this from + * app_main(). */ +void start_networking(void); +void stop_networking(void); + +void networking_set_addr(struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw); +#endif diff --git a/extras/mini-os/include/pcifront.h b/extras/mini-os/include/pcifront.h new file mode 100644 index 0000000..3bb3713 --- /dev/null +++ b/extras/mini-os/include/pcifront.h @@ -0,0 +1,28 @@ +#include +#include +struct pcifront_dev; +struct pcifront_dev *init_pcifront(char *nodename); +void pcifront_op(struct pcifront_dev *dev, struct xen_pci_op *op); +void pcifront_scan(struct pcifront_dev *dev, void (*fun)(unsigned int domain, unsigned int bus, unsigned slot, unsigned int fun)); +int pcifront_conf_read(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun, + unsigned int off, unsigned int size, unsigned int *val); +int pcifront_conf_write(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun, + unsigned int off, unsigned int size, unsigned int val); +int pcifront_enable_msi(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun); +int pcifront_disable_msi(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun); +int pcifront_enable_msix(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun, + struct xen_msix_entry *entries, int n); +int pcifront_disable_msix(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun); +void shutdown_pcifront(struct pcifront_dev *dev); diff --git a/extras/mini-os/include/posix/arpa/inet.h b/extras/mini-os/include/posix/arpa/inet.h new file mode 100644 index 0000000..012f3a4 --- /dev/null +++ b/extras/mini-os/include/posix/arpa/inet.h @@ -0,0 +1,7 @@ +#ifndef _POSIX_ARPA_INET_H_ +#define _POSIX_ARPA_INET_H_ + +#include + +#endif /* _POSIX_ARPA_INET_H_ */ + diff --git a/extras/mini-os/include/posix/dirent.h b/extras/mini-os/include/posix/dirent.h new file mode 100644 index 0000000..884b69e --- /dev/null +++ b/extras/mini-os/include/posix/dirent.h @@ -0,0 +1,24 @@ +#ifndef _POSIX_DIRENT_H +#define _POSIX_DIRENT_H + +#include + +struct dirent { + char *d_name; +}; + +typedef struct { + struct dirent dirent; + char *name; + int32_t offset; + char **entries; + int32_t curentry; + int32_t nbentries; + int has_more; +} DIR; + +DIR *opendir(const char *name); +struct dirent *readdir(DIR *dir); +int closedir(DIR *dir); + +#endif /* _POSIX_DIRENT_H */ diff --git a/extras/mini-os/include/posix/err.h b/extras/mini-os/include/posix/err.h new file mode 100644 index 0000000..1079f58 --- /dev/null +++ b/extras/mini-os/include/posix/err.h @@ -0,0 +1,15 @@ +#ifndef _POSIX_ERR_H +#define _POSIX_ERR_H + +#include + +void err(int eval, const char *fmt, ...); +void errx(int eval, const char *fmt, ...); +void warn(const char *fmt, ...); +void warnx(const char *fmt, ...); +void verr(int eval, const char *fmt, va_list args); +void verrx(int eval, const char *fmt, va_list args); +void vwarn(const char *fmt, va_list args); +void vwarnx(const char *fmt, va_list args); + +#endif /* _POSIX_ERR_H */ diff --git a/extras/mini-os/include/posix/fcntl.h b/extras/mini-os/include/posix/fcntl.h new file mode 100644 index 0000000..ecfd8c8 --- /dev/null +++ b/extras/mini-os/include/posix/fcntl.h @@ -0,0 +1,11 @@ +#ifndef _POSIX_FCNTL_H +#define _POSIX_FCNTL_H + +#include_next + +#define F_ULOCK 0 +#define F_LOCK 1 +#define F_TLOCK 2 +#define F_TEST 3 + +#endif /* _POSIX_FCNTL_H */ diff --git a/extras/mini-os/include/posix/limits.h b/extras/mini-os/include/posix/limits.h new file mode 100644 index 0000000..c45e039 --- /dev/null +++ b/extras/mini-os/include/posix/limits.h @@ -0,0 +1,46 @@ +#ifndef _POSIX_LIMITS_H +#define _POSIX_LIMITS_H + +#include + +#define CHAR_BIT 8 + +#define SCHAR_MAX 0x7f +#define SCHAR_MIN (-SCHAR_MAX-1) +#define UCHAR_MAX 0xff + +#ifdef __CHAR_UNSIGNED__ +# define CHAR_MIN 0 +# define CHAR_MAX UCHAR_MAX +#else +# define CHAR_MIN SCHAR_MIN +# define CHAR_MAX SCHAR_MAX +#endif + +#define INT_MAX 0x7fffffff +#define INT_MIN (-INT_MAX-1) +#define UINT_MAX 0xffffffff + +#define SHRT_MAX 0x7fff + +#if defined(__x86_64__) || defined(__ia64__) +# define LONG_MAX 0x7fffffffffffffffL +# define ULONG_MAX 0xffffffffffffffffUL +#else +# define LONG_MAX 0x7fffffffL +# define ULONG_MAX 0xffffffffUL +#endif +#define LONG_MIN (-LONG_MAX-1L) + +#define LLONG_MAX 0x7fffffffffffffffLL +#define LLONG_MIN (-LLONG_MAX-1LL) +#define ULLONG_MAX 0xffffffffffffffffULL + +#define LONG_LONG_MIN LLONG_MIN +#define LONG_LONG_MAX LLONG_MAX +#define ULONG_LONG_MAX ULLONG_MAX + +#define PATH_MAX __PAGE_SIZE +#define PAGE_SIZE __PAGE_SIZE + +#endif /* _POSIX_LIMITS_H */ diff --git a/extras/mini-os/include/posix/netdb.h b/extras/mini-os/include/posix/netdb.h new file mode 100644 index 0000000..8f76a95 --- /dev/null +++ b/extras/mini-os/include/posix/netdb.h @@ -0,0 +1,9 @@ +#ifndef _POSIX_NETDB_H_ +#define _POSIX_NETDB_H_ + +struct hostent { + char *h_addr; +}; +#define gethostbyname(buf) NULL + +#endif /* _POSIX_NETDB_H_ */ diff --git a/extras/mini-os/include/posix/netinet/in.h b/extras/mini-os/include/posix/netinet/in.h new file mode 100644 index 0000000..cc1a910 --- /dev/null +++ b/extras/mini-os/include/posix/netinet/in.h @@ -0,0 +1,7 @@ +#ifndef _POSIX_SYS_IN_H_ +#define _POSIX_SYS_IN_H_ + +#include +#include + +#endif /* _POSIX_SYS_IN_H_ */ diff --git a/extras/mini-os/include/posix/netinet/tcp.h b/extras/mini-os/include/posix/netinet/tcp.h new file mode 100644 index 0000000..3e3b060 --- /dev/null +++ b/extras/mini-os/include/posix/netinet/tcp.h @@ -0,0 +1,6 @@ +#ifndef _POSIX_SYS_TCP_H_ +#define _POSIX_SYS_TCP_H_ + +#include + +#endif /* _POSIX_SYS_TCP_H_ */ diff --git a/extras/mini-os/include/posix/pthread.h b/extras/mini-os/include/posix/pthread.h new file mode 100644 index 0000000..f74d924 --- /dev/null +++ b/extras/mini-os/include/posix/pthread.h @@ -0,0 +1,64 @@ +#ifndef _POSIX_PTHREAD_H +#define _POSIX_PTHREAD_H + +#include + +/* Let's be single-threaded for now. */ + +typedef struct { + void *ptr; +} *pthread_key_t; +static inline int pthread_key_create(pthread_key_t *key, void (*destr_function)(void*)) +{ + *key = malloc(sizeof(**key)); + (*key)->ptr = NULL; + return 0; +} +static inline int pthread_setspecific(pthread_key_t key, const void *pointer) +{ + key->ptr = (void*) pointer; + return 0; +} +static inline void *pthread_getspecific(pthread_key_t key) +{ + return key->ptr; +} +static inline int pthread_key_delete(pthread_key_t key) +{ + free(key); + return 0; +} + + + +typedef struct {} pthread_mutexattr_t; +static inline int pthread_mutexattr_init(pthread_mutexattr_t *mattr) { return 0; } +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_RECURSIVE 1 +static inline int pthread_mutexattr_settype(pthread_mutexattr_t *mattr, int kind) { return 0; } +static inline int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr) { return 0; } +typedef struct {} pthread_mutex_t; +#define PTHREAD_MUTEX_INITIALIZER {} +static inline int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *mattr) { return 0; } +static inline int pthread_mutex_lock(pthread_mutex_t *mutex) { return 0; } +static inline int pthread_mutex_unlock(pthread_mutex_t *mutex) { return 0; } + + + +typedef struct { + int done; +} pthread_once_t; +#define PTHREAD_ONCE_INIT { 0 } + +static inline int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + if (!once_control->done) { + once_control->done = 1; + init_routine(); + } + return 0; +} + +#define __thread + +#endif /* _POSIX_PTHREAD_H */ diff --git a/extras/mini-os/include/posix/signal.h b/extras/mini-os/include/posix/signal.h new file mode 100644 index 0000000..be9e9f3 --- /dev/null +++ b/extras/mini-os/include/posix/signal.h @@ -0,0 +1,10 @@ +#ifndef _POSIX_SIGNAL_H +#define _POSIX_SIGNAL_H + +#include_next + +int sigaction(int signum, const struct sigaction * __restrict, + struct sigaction * __restrict); + +#endif + diff --git a/extras/mini-os/include/posix/stdlib.h b/extras/mini-os/include/posix/stdlib.h new file mode 100644 index 0000000..53e6289 --- /dev/null +++ b/extras/mini-os/include/posix/stdlib.h @@ -0,0 +1,8 @@ +#ifndef _POSIX_STDLIB_H +#define _POSIX_STDLIB_H + +#include_next + +#define realpath(p,r) strcpy(r,p) + +#endif /* _POSIX_STDLIB_H */ diff --git a/extras/mini-os/include/posix/strings.h b/extras/mini-os/include/posix/strings.h new file mode 100644 index 0000000..8619ba2 --- /dev/null +++ b/extras/mini-os/include/posix/strings.h @@ -0,0 +1,8 @@ +#ifndef _POSIX_STRINGS_H +#define _POSIX_STRINGS_H + +#include + +#define bzero(ptr, size) (memset((ptr), '\0', (size)), (void) 0) + +#endif /* _POSIX_STRINGS_H */ diff --git a/extras/mini-os/include/posix/sys/ioctl.h b/extras/mini-os/include/posix/sys/ioctl.h new file mode 100644 index 0000000..ecf3080 --- /dev/null +++ b/extras/mini-os/include/posix/sys/ioctl.h @@ -0,0 +1,16 @@ +#ifndef _POSIX_SYS_IOCTL_H +#define _POSIX_SYS_IOCTL_H + +int ioctl(int fd, int request, ...); + +#define _IOC_NONE 0 +#define _IOC_WRITE 1 +#define _IOC_READ 2 + +#define _IOC(rw, class, n, size) \ + (((rw ) << 30) | \ + ((class) << 22) | \ + ((n ) << 14) | \ + ((size ) << 0)) + +#endif /* _POSIX_SYS_IOCTL_H */ diff --git a/extras/mini-os/include/posix/sys/mman.h b/extras/mini-os/include/posix/sys/mman.h new file mode 100644 index 0000000..cfd6586 --- /dev/null +++ b/extras/mini-os/include/posix/sys/mman.h @@ -0,0 +1,22 @@ +#ifndef _POSIX_SYS_MMAN_H +#define _POSIX_SYS_MMAN_H + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANON 0x20 + +/* Pages are always resident anyway */ +#define MAP_LOCKED 0x0 + +#define MAP_FAILED ((void*)0) + +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) asm("mmap64"); +int munmap(void *start, size_t length); +#define munlock(addr, len) ((void)addr, (void)len, 0) +#define mlock(addr, len) ((void)addr, (void)len, 0) + +#endif /* _POSIX_SYS_MMAN_H */ diff --git a/extras/mini-os/include/posix/sys/poll.h b/extras/mini-os/include/posix/sys/poll.h new file mode 100644 index 0000000..f9d7f5c --- /dev/null +++ b/extras/mini-os/include/posix/sys/poll.h @@ -0,0 +1,79 @@ +/* + * This code is mostly taken from FreeBSD sys/sys/poll.h + * Changes: Stefano Stabellini + * + **************************************************************************** + * Copyright (c) 1997 Peter Wemm + * All rights reserved. + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 THE 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. + */ + +#ifndef _POSIX_SYS_POLL_H_ +#define _POSIX_SYS_POLL_H_ + +/* + * This file is intended to be compatible with the traditional poll.h. + */ + +typedef unsigned int nfds_t; + +/* + * This structure is passed as an array to poll(2). + */ +struct pollfd { + int fd; /* which file descriptor to poll */ + short events; /* events we are interested in */ + short revents; /* events found on return */ +}; + +/* + * Requestable events. If poll(2) finds any of these set, they are + * copied to revents on return. + * XXX Note that FreeBSD doesn't make much distinction between POLLPRI + * and POLLRDBAND since none of the file types have distinct priority + * bands - and only some have an urgent "mode". + * XXX Note POLLIN isn't really supported in true SVSV terms. Under SYSV + * POLLIN includes all of normal, band and urgent data. Most poll handlers + * on FreeBSD only treat it as "normal" data. + */ +#define POLLIN 0x0001 /* any readable data available */ +#define POLLPRI 0x0002 /* OOB/Urgent readable data */ +#define POLLOUT 0x0004 /* file descriptor is writeable */ +#define POLLRDNORM 0x0040 /* non-OOB/URG data available */ +#define POLLWRNORM POLLOUT /* no write type differentiation */ +#define POLLRDBAND 0x0080 /* OOB/Urgent readable data */ +#define POLLWRBAND 0x0100 /* OOB/Urgent data can be written */ + +/* + * These events are set if they occur regardless of whether they were + * requested. + */ +#define POLLERR 0x0008 /* some poll error occurred */ +#define POLLHUP 0x0010 /* file descriptor was "hung up" */ +#define POLLNVAL 0x0020 /* requested events "invalid" */ + +int poll(struct pollfd _pfd[], nfds_t _nfds, int _timeout); + +#endif /* _POSIX_SYS_POLL_H_ */ diff --git a/extras/mini-os/include/posix/sys/select.h b/extras/mini-os/include/posix/sys/select.h new file mode 100644 index 0000000..a9337be --- /dev/null +++ b/extras/mini-os/include/posix/sys/select.h @@ -0,0 +1,8 @@ +#ifndef _POSIX_SELECT_H +#define _POSIX_SELECT_H + +#include +#include +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + +#endif /* _POSIX_SELECT_H */ diff --git a/extras/mini-os/include/posix/sys/socket.h b/extras/mini-os/include/posix/sys/socket.h new file mode 100644 index 0000000..7c039a2 --- /dev/null +++ b/extras/mini-os/include/posix/sys/socket.h @@ -0,0 +1,31 @@ +#ifndef _POSIX_SYS_SOCKET_H_ +#define _POSIX_SYS_SOCKET_H_ + +#include +#include + +int accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int bind(int s, struct sockaddr *name, socklen_t namelen); +int shutdown(int s, int how); +int getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int close(int s); +int connect(int s, struct sockaddr *name, socklen_t namelen); +int listen(int s, int backlog); +int recv(int s, void *mem, int len, unsigned int flags); +//int read(int s, void *mem, int len); +int recvfrom(int s, void *mem, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen); +int send(int s, void *dataptr, int size, unsigned int flags); +int sendto(int s, void *dataptr, int size, unsigned int flags, + struct sockaddr *to, socklen_t tolen); +int socket(int domain, int type, int protocol); +//int write(int s, void *dataptr, int size); +int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +//int ioctl(int s, long cmd, void *argp); +int getsockname(int s, struct sockaddr *name, socklen_t *namelen); + +#endif /* _POSIX_SYS_SOCKET_H_ */ diff --git a/extras/mini-os/include/posix/sys/stat.h b/extras/mini-os/include/posix/sys/stat.h new file mode 100644 index 0000000..0c13bea --- /dev/null +++ b/extras/mini-os/include/posix/sys/stat.h @@ -0,0 +1,7 @@ +#ifndef _POSIX_SYS_STAT_H +#define _POSIX_SYS_STAT_H + +#include_next +int fstat(int fd, struct stat *buf) asm("fstat64"); + +#endif /* _POSIX_SYS_STAT_H */ diff --git a/extras/mini-os/include/posix/syslog.h b/extras/mini-os/include/posix/syslog.h new file mode 100644 index 0000000..aabd0e4 --- /dev/null +++ b/extras/mini-os/include/posix/syslog.h @@ -0,0 +1,37 @@ +#ifndef _POSIX_SYSLOG_H +#define _POSIX_SYSLOG_H + +#include + +#define LOG_PID 0 +#define LOG_CONS 0 +#define LOG_NDELAY 0 +#define LOG_ODELAY 0 +#define LOG_NOWAIT 0 + +#define LOG_KERN 0 +#define LOG_USER 0 +#define LOG_MAIL 0 +#define LOG_NEWS 0 +#define LOG_UUCP 0 +#define LOG_DAEMON 0 +#define LOG_AUTH 0 +#define LOG_CRON 0 +#define LOG_LPR 0 + +/* TODO: support */ +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +void openlog(const char *ident, int option, int facility); +void syslog(int priority, const char *format, ...); +void closelog(void); +void vsyslog(int priority, const char *format, va_list ap); + +#endif /* _POSIX_SYSLOG_H */ diff --git a/extras/mini-os/include/posix/termios.h b/extras/mini-os/include/posix/termios.h new file mode 100644 index 0000000..a57aee4 --- /dev/null +++ b/extras/mini-os/include/posix/termios.h @@ -0,0 +1,87 @@ +#ifndef _POSIX_TERMIOS_H +#define _POSIX_TERMIOS_H + +#define NCC 32 + +struct termios { + unsigned long c_iflag; + unsigned long c_oflag; + unsigned long c_lflag; + unsigned long c_cflag; + unsigned char c_cc[NCC]; +}; + +/* modem lines */ +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RI 0x080 +#define TIOCM_DSR 0x100 + +/* c_iflag */ +#define IGNBRK 0x00000001 +#define BRKINT 0x00000002 +#define IGNPAR 0x00000004 +#define PARMRK 0x00000008 +#define INPCK 0x00000010 +#define ISTRIP 0x00000020 +#define INLCR 0x00000040 +#define IGNCR 0x00000080 +#define ICRNL 0x00000100 +#define IUCLC 0x00000200 +#define IXON 0x00000400 +#define IXANY 0x00000800 +#define IXOFF 0x00001000 +#define IMAXBEL 0x00002000 +#define IUTF8 0x00004000 + +/* c_oflag */ +#define OPOST 0x00000001 +#define OLCUC 0x00000002 +#define ONLCR 0x00000004 +#define OCRNL 0x00000008 +#define ONOCR 0x00000010 +#define ONLRET 0x00000020 +#define OFILL 0x00000040 +#define OFDEL 0x00000080 + +/* c_lflag */ +#define ISIG 0x00000001 +#define ICANON 0x00000002 +#define XCASE 0x00000004 +#define ECHO 0x00000008 +#define ECHOE 0x00000010 +#define ECHOK 0x00000020 +#define ECHONL 0x00000040 +#define NOFLSH 0x00000080 +#define TOSTOP 0x00000100 +#define ECHOCTL 0x00000200 +#define ECHOPRT 0x00000400 +#define ECHOKE 0x00000800 +#define FLUSHO 0x00002000 +#define PENDIN 0x00004000 +#define IEXTEN 0x00008000 + +/* c_cflag */ +#define CSIZE 0x00000030 +#define CS8 0x00000030 +#define CSTOPB 0x00000040 +#define CREAD 0x00000080 +#define PARENB 0x00000100 +#define PARODD 0x00000200 +#define HUPCL 0x00000400 +#define CLOCAL 0x00000800 + +/* c_cc */ +#define VTIME 5 +#define VMIN 6 + +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +int tcsetattr(int fildes, int action, const struct termios *tios); +int tcgetattr(int fildes, struct termios *tios); + +#endif /* _POSIX_TERMIOS_H */ diff --git a/extras/mini-os/include/posix/time.h b/extras/mini-os/include/posix/time.h new file mode 100644 index 0000000..ce75f32 --- /dev/null +++ b/extras/mini-os/include/posix/time.h @@ -0,0 +1,11 @@ +#ifndef _POSIX_TIME_H +#define _POSIX_TIME_H + +#include +#define CLOCK_MONOTONIC 2 +#include_next + +int nanosleep(const struct timespec *req, struct timespec *rem); +int clock_gettime(clockid_t clock_id, struct timespec *tp); + +#endif /* _POSIX_TIME_H */ diff --git a/extras/mini-os/include/posix/unistd.h b/extras/mini-os/include/posix/unistd.h new file mode 100644 index 0000000..5104782 --- /dev/null +++ b/extras/mini-os/include/posix/unistd.h @@ -0,0 +1,10 @@ +#ifndef _POSIX_UNISTD_H +#define _POSIX_UNISTD_H + +#include_next + +size_t getpagesize(void); +int ftruncate(int fd, off_t length); +int lockf(int fd, int cmd, off_t len); + +#endif /* _POSIX_UNISTD_H */ diff --git a/extras/mini-os/include/sched.h b/extras/mini-os/include/sched.h new file mode 100644 index 0000000..3359439 --- /dev/null +++ b/extras/mini-os/include/sched.h @@ -0,0 +1,58 @@ +#ifndef __SCHED_H__ +#define __SCHED_H__ + +#include +#include +#include +#ifdef HAVE_LIBC +#include +#endif + +struct thread +{ + char *name; + char *stack; +#if !defined(__ia64__) + /* keep in that order */ + unsigned long sp; /* Stack pointer */ + unsigned long ip; /* Instruction pointer */ +#else /* !defined(__ia64__) */ + thread_regs_t regs; +#endif /* !defined(__ia64__) */ + struct minios_list_head thread_list; + u32 flags; + s_time_t wakeup_time; +#ifdef HAVE_LIBC + struct _reent reent; +#endif +}; + +extern struct thread *idle_thread; +void idle_thread_fn(void *unused); + +#define RUNNABLE_FLAG 0x00000001 + +#define is_runnable(_thread) (_thread->flags & RUNNABLE_FLAG) +#define set_runnable(_thread) (_thread->flags |= RUNNABLE_FLAG) +#define clear_runnable(_thread) (_thread->flags &= ~RUNNABLE_FLAG) + +#define switch_threads(prev, next) arch_switch_threads(prev, next) + + /* Architecture specific setup of thread creation. */ +struct thread* arch_create_thread(char *name, void (*function)(void *), + void *data); + +void init_sched(void); +void run_idle_thread(void); +struct thread* create_thread(char *name, void (*function)(void *), void *data); +void exit_thread(void) __attribute__((noreturn)); +void schedule(void); + +#define current get_current() + + +void wake(struct thread *thread); +void block(struct thread *thread); +void msleep(u32 millisecs); + +#endif /* __SCHED_H__ */ diff --git a/extras/mini-os/include/semaphore.h b/extras/mini-os/include/semaphore.h new file mode 100644 index 0000000..8236046 --- /dev/null +++ b/extras/mini-os/include/semaphore.h @@ -0,0 +1,111 @@ +#ifndef _SEMAPHORE_H_ +#define _SEMAPHORE_H_ + +#include +#include + +/* + * Implementation of semaphore in Mini-os is simple, because + * there are no preemptive threads, the atomicity is guaranteed. + */ + +struct semaphore +{ + int count; + struct wait_queue_head wait; +}; + +/* + * the semaphore definition + */ +struct rw_semaphore { + signed long count; + spinlock_t wait_lock; + struct minios_list_head wait_list; + int debug; +}; + +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = n, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} + +#define __MUTEX_INITIALIZER(name) \ + __SEMAPHORE_INITIALIZER(name,1) + +#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) + +#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) + +#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) + +static inline void init_SEMAPHORE(struct semaphore *sem, int count) +{ + sem->count = count; + init_waitqueue_head(&sem->wait); +} + +#define init_MUTEX(sem) init_SEMAPHORE(sem, 1) + +static inline int trydown(struct semaphore *sem) +{ + unsigned long flags; + int ret = 0; + local_irq_save(flags); + if (sem->count > 0) { + ret = 1; + sem->count--; + } + local_irq_restore(flags); + return ret; +} + +static void inline down(struct semaphore *sem) +{ + unsigned long flags; + while (1) { + wait_event(sem->wait, sem->count > 0); + local_irq_save(flags); + if (sem->count > 0) + break; + local_irq_restore(flags); + } + sem->count--; + local_irq_restore(flags); +} + +static void inline up(struct semaphore *sem) +{ + unsigned long flags; + local_irq_save(flags); + sem->count++; + wake_up(&sem->wait); + local_irq_restore(flags); +} + +/* FIXME! Thre read/write semaphores are unimplemented! */ +static inline void init_rwsem(struct rw_semaphore *sem) +{ + sem->count = 1; +} + +static inline void down_read(struct rw_semaphore *sem) +{ +} + + +static inline void up_read(struct rw_semaphore *sem) +{ +} + +static inline void up_write(struct rw_semaphore *sem) +{ +} + +static inline void down_write(struct rw_semaphore *sem) +{ +} + +#endif /* _SEMAPHORE_H */ diff --git a/extras/mini-os/include/spinlock.h b/extras/mini-os/include/spinlock.h new file mode 100644 index 0000000..70cf20f --- /dev/null +++ b/extras/mini-os/include/spinlock.h @@ -0,0 +1,55 @@ +#ifndef __ASM_SPINLOCK_H +#define __ASM_SPINLOCK_H + +#include + +/* + * Your basic SMP spinlocks, allowing only a single CPU anywhere + */ + +typedef struct { + volatile unsigned int slock; +} spinlock_t; + + +#include + + +#define SPINLOCK_MAGIC 0xdead4ead + +#define SPIN_LOCK_UNLOCKED ARCH_SPIN_LOCK_UNLOCKED + +#define spin_lock_init(x) do { *(x) = SPIN_LOCK_UNLOCKED; } while(0) + +/* + * Simple spin lock operations. There are two variants, one clears IRQ's + * on the local processor, one does not. + * + * We make no fairness assumptions. They have a cost. + */ + +#define spin_is_locked(x) arch_spin_is_locked(x) + +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) + + +#define _spin_trylock(lock) ({_raw_spin_trylock(lock) ? \ + 1 : ({ 0;});}) + +#define _spin_lock(lock) \ +do { \ + _raw_spin_lock(lock); \ +} while(0) + +#define _spin_unlock(lock) \ +do { \ + _raw_spin_unlock(lock); \ +} while (0) + + +#define spin_lock(lock) _spin_lock(lock) +#define spin_unlock(lock) _spin_unlock(lock) + +#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED + +#endif diff --git a/extras/mini-os/include/sys/lock.h b/extras/mini-os/include/sys/lock.h new file mode 100644 index 0000000..8004536 --- /dev/null +++ b/extras/mini-os/include/sys/lock.h @@ -0,0 +1,52 @@ +#ifndef _MINIOS_SYS_LOCK_H_ +#define _MINIOS_SYS_LOCK_H_ + +#ifdef HAVE_LIBC + +/* Due to inclusion loop, we can not include sched.h, so have to hide things */ + +#include + + +typedef struct { + int busy; + struct wait_queue_head wait; +} _LOCK_T; + +#define __LOCK_INIT(class,lock) \ + class _LOCK_T lock = { .wait = __WAIT_QUEUE_HEAD_INITIALIZER(lock.wait) } +int ___lock_init(_LOCK_T *lock); +int ___lock_acquire(_LOCK_T *lock); +int ___lock_try_acquire(_LOCK_T *lock); +int ___lock_release(_LOCK_T *lock); +int ___lock_close(_LOCK_T *lock); +#define __lock_init(__lock) ___lock_init(&__lock) +#define __lock_acquire(__lock) ___lock_acquire(&__lock) +#define __lock_release(__lock) ___lock_release(&__lock) +#define __lock_try_acquire(__lock) ___lock_try_acquire(&__lock) +#define __lock_close(__lock) 0 + + +typedef struct { + struct thread *owner; + int count; + struct wait_queue_head wait; +} _LOCK_RECURSIVE_T; + +#define __LOCK_INIT_RECURSIVE(class, lock) \ + class _LOCK_RECURSIVE_T lock = { .wait = __WAIT_QUEUE_HEAD_INITIALIZER((lock).wait) } + +int ___lock_init_recursive(_LOCK_RECURSIVE_T *lock); +int ___lock_acquire_recursive(_LOCK_RECURSIVE_T *lock); +int ___lock_try_acquire_recursive(_LOCK_RECURSIVE_T *lock); +int ___lock_release_recursive(_LOCK_RECURSIVE_T *lock); +int ___lock_close_recursive(_LOCK_RECURSIVE_T *lock); +#define __lock_init_recursive(__lock) ___lock_init_recursive(&__lock) +#define __lock_acquire_recursive(__lock) ___lock_acquire_recursive(&__lock) +#define __lock_release_recursive(__lock) ___lock_release_recursive(&__lock) +#define __lock_try_acquire_recursive(__lock) ___lock_try_acquire_recursive(&__lock) +#define __lock_close_recursive(__lock) 0 + +#endif + +#endif /* _MINIOS_SYS_LOCK_H_ */ diff --git a/extras/mini-os/include/sys/time.h b/extras/mini-os/include/sys/time.h new file mode 100644 index 0000000..d6623a4 --- /dev/null +++ b/extras/mini-os/include/sys/time.h @@ -0,0 +1,42 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: time.h + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * Robert Kaiser (kaiser@informatik.fh-wiesbaden.de) + * + * Date: Jul 2003, changes: Jun 2005, Sep 2006 + * + * Environment: Xen Minimal OS + * Description: Time and timer functions + * + **************************************************************************** + */ + +#ifndef _MINIOS_SYS_TIME_H_ +#define _MINIOS_SYS_TIME_H_ + +#ifdef HAVE_LIBC +#include_next +#else +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +struct timezone { +}; + +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* microseconds */ +}; + +int gettimeofday(struct timeval *tv, void *tz); +#endif + +#endif /* _MINIOS_SYS_TIME_H_ */ diff --git a/extras/mini-os/include/time.h b/extras/mini-os/include/time.h new file mode 100644 index 0000000..ac83df8 --- /dev/null +++ b/extras/mini-os/include/time.h @@ -0,0 +1,63 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: time.h + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * Robert Kaiser (kaiser@informatik.fh-wiesbaden.de) + * + * Date: Jul 2003, changes: Jun 2005, Sep 2006 + * + * Environment: Xen Minimal OS + * Description: Time and timer functions + * + **************************************************************************** + */ + +#ifndef _MINIOS_TIME_H_ +#define _MINIOS_TIME_H_ +#include + +/* + * System Time + * 64 bit value containing the nanoseconds elapsed since boot time. + * This value is adjusted by frequency drift. + * NOW() returns the current time. + * The other macros are for convenience to approximate short intervals + * of real time into system time + */ +typedef s64 s_time_t; +#define NOW() ((s_time_t)monotonic_clock()) +#define SECONDS(_s) (((s_time_t)(_s)) * 1000000000UL ) +#define TENTHS(_ts) (((s_time_t)(_ts)) * 100000000UL ) +#define HUNDREDTHS(_hs) (((s_time_t)(_hs)) * 10000000UL ) +#define MILLISECS(_ms) (((s_time_t)(_ms)) * 1000000UL ) +#define MICROSECS(_us) (((s_time_t)(_us)) * 1000UL ) +#define Time_Max ((s_time_t) 0x7fffffffffffffffLL) +#define FOREVER Time_Max +#define NSEC_TO_USEC(_nsec) ((_nsec) / 1000UL) +#define NSEC_TO_MSEC(_nsec) ((_nsec) / 1000000ULL) +#define NSEC_TO_SEC(_nsec) ((_nsec) / 1000000000ULL) + +/* wall clock time */ +typedef long time_t; +typedef long suseconds_t; + +#include + +#ifdef HAVE_LIBC +#include_next +#endif + +/* prototypes */ +void init_time(void); +void fini_time(void); +s_time_t get_s_time(void); +s_time_t get_v_time(void); +u64 monotonic_clock(void); +void block_domain(s_time_t until); + +#endif /* _MINIOS_TIME_H_ */ diff --git a/extras/mini-os/include/types.h b/extras/mini-os/include/types.h new file mode 100644 index 0000000..19b7302 --- /dev/null +++ b/extras/mini-os/include/types.h @@ -0,0 +1,93 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + **************************************************************************** + * + * File: types.h + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: + * + * Date: May 2003 + * + * Environment: Xen Minimal OS + * Description: a random collection of type definitions + * + **************************************************************************** + * $Id: h-insert.h,v 1.4 2002/11/08 16:03:55 rn Exp $ + **************************************************************************** + */ + +#ifndef _TYPES_H_ +#define _TYPES_H_ +#include + +typedef signed char s8; +typedef unsigned char u8; +typedef signed short s16; +typedef unsigned short u16; +typedef signed int s32; +typedef unsigned int u32; +#ifdef __i386__ +typedef signed long long s64; +typedef unsigned long long u64; +#elif defined(__x86_64__) || defined(__ia64__) +typedef signed long s64; +typedef unsigned long u64; +#endif + +/* FreeBSD compat types */ +#ifndef HAVE_LIBC +typedef unsigned char u_char; +typedef unsigned int u_int; +typedef unsigned long u_long; +#endif +#ifdef __i386__ +typedef long long quad_t; +typedef unsigned long long u_quad_t; + +typedef struct { unsigned long pte_low, pte_high; } pte_t; + +#elif defined(__x86_64__) || defined(__ia64__) +typedef long quad_t; +typedef unsigned long u_quad_t; + +typedef struct { unsigned long pte; } pte_t; +#endif /* __i386__ || __x86_64__ */ + +#ifdef __x86_64__ +#define __pte(x) ((pte_t) { (x) } ) +#else +#define __pte(x) ({ unsigned long long _x = (x); \ + ((pte_t) {(unsigned long)(_x), (unsigned long)(_x>>32)}); }) +#endif + +#ifdef HAVE_LIBC +#include +#include +#else +#ifdef __i386__ +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif defined(__x86_64__) || defined(__ia64__) +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif /* __i386__ || __x86_64__ */ +typedef u8 uint8_t; +typedef s8 int8_t; +typedef u16 uint16_t; +typedef s16 int16_t; +typedef u32 uint32_t; +typedef s32 int32_t; +typedef u64 uint64_t, uintmax_t; +typedef s64 int64_t, intmax_t; +typedef u64 off_t; +#endif + +typedef intptr_t ptrdiff_t; + + +#ifndef HAVE_LIBC +typedef long ssize_t; +#endif + +#endif /* _TYPES_H_ */ diff --git a/extras/mini-os/include/wait.h b/extras/mini-os/include/wait.h new file mode 100644 index 0000000..14e98ba --- /dev/null +++ b/extras/mini-os/include/wait.h @@ -0,0 +1,95 @@ +#ifndef __WAIT_H__ +#define __WAIT_H__ + +#include +#include +#include + +#define DEFINE_WAIT(name) \ +struct wait_queue name = { \ + .thread = current, \ + .thread_list = MINIOS_LIST_HEAD_INIT((name).thread_list), \ +} + + +static inline void init_waitqueue_head(struct wait_queue_head *h) +{ + MINIOS_INIT_LIST_HEAD(&h->thread_list); +} + +static inline void init_waitqueue_entry(struct wait_queue *q, struct thread *thread) +{ + q->thread = thread; + MINIOS_INIT_LIST_HEAD(&q->thread_list); +} + + +static inline void add_wait_queue(struct wait_queue_head *h, struct wait_queue *q) +{ + if (minios_list_empty(&q->thread_list)) + minios_list_add(&q->thread_list, &h->thread_list); +} + +static inline void remove_wait_queue(struct wait_queue *q) +{ + minios_list_del(&q->thread_list); +} + +static inline void wake_up(struct wait_queue_head *head) +{ + unsigned long flags; + struct minios_list_head *tmp, *next; + local_irq_save(flags); + minios_list_for_each_safe(tmp, next, &head->thread_list) + { + struct wait_queue *curr; + curr = minios_list_entry(tmp, struct wait_queue, thread_list); + wake(curr->thread); + } + local_irq_restore(flags); +} + +#define add_waiter(w, wq) do { \ + unsigned long flags; \ + local_irq_save(flags); \ + add_wait_queue(&wq, &w); \ + block(current); \ + local_irq_restore(flags); \ +} while (0) + +#define remove_waiter(w) do { \ + unsigned long flags; \ + local_irq_save(flags); \ + remove_wait_queue(&w); \ + local_irq_restore(flags); \ +} while (0) + +#define wait_event_deadline(wq, condition, deadline) do { \ + unsigned long flags; \ + DEFINE_WAIT(__wait); \ + if(condition) \ + break; \ + for(;;) \ + { \ + /* protect the list */ \ + local_irq_save(flags); \ + add_wait_queue(&wq, &__wait); \ + current->wakeup_time = deadline; \ + clear_runnable(current); \ + local_irq_restore(flags); \ + if((condition) || (deadline && NOW() >= deadline)) \ + break; \ + schedule(); \ + } \ + local_irq_save(flags); \ + /* need to wake up */ \ + wake(current); \ + remove_wait_queue(&__wait); \ + local_irq_restore(flags); \ +} while(0) + +#define wait_event(wq, condition) wait_event_deadline(wq, condition, 0) + + + +#endif /* __WAIT_H__ */ diff --git a/extras/mini-os/include/waittypes.h b/extras/mini-os/include/waittypes.h new file mode 100644 index 0000000..1215ffe --- /dev/null +++ b/extras/mini-os/include/waittypes.h @@ -0,0 +1,26 @@ +#ifndef __WAITTYPE_H__ +#define __WAITTYPE_H__ + +#include + +struct thread; +struct wait_queue +{ + struct thread *thread; + struct minios_list_head thread_list; +}; + +struct wait_queue_head +{ + /* TODO - lock required? */ + struct minios_list_head thread_list; +}; + +#define DECLARE_WAIT_QUEUE_HEAD(name) \ + struct wait_queue_head name = \ + { .thread_list = { &(name).thread_list, &(name).thread_list} } + +#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \ + .thread_list = { &(name).thread_list, &(name).thread_list } } + +#endif diff --git a/extras/mini-os/include/x86/arch_limits.h b/extras/mini-os/include/x86/arch_limits.h new file mode 100644 index 0000000..41f8620 --- /dev/null +++ b/extras/mini-os/include/x86/arch_limits.h @@ -0,0 +1,20 @@ + +#ifndef __ARCH_LIMITS_H__ +#define __ARCH_LIMITS_H__ + +#define __PAGE_SHIFT 12 + +#ifdef __ASSEMBLY__ +#define __PAGE_SIZE (1 << __PAGE_SHIFT) +#else +#ifdef __x86_64__ +#define __PAGE_SIZE (1UL << __PAGE_SHIFT) +#else +#define __PAGE_SIZE (1ULL << __PAGE_SHIFT) +#endif +#endif + +#define __STACK_SIZE_PAGE_ORDER 4 +#define __STACK_SIZE (__PAGE_SIZE * (1 << __STACK_SIZE_PAGE_ORDER)) + +#endif /* __ARCH_LIMITS_H__ */ diff --git a/extras/mini-os/include/x86/arch_mm.h b/extras/mini-os/include/x86/arch_mm.h new file mode 100644 index 0000000..4a04812 --- /dev/null +++ b/extras/mini-os/include/x86/arch_mm.h @@ -0,0 +1,226 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + * + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * Copyright (c) 2005, Keir A Fraser + * + * 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. + */ + +#ifndef _ARCH_MM_H_ +#define _ARCH_MM_H_ + +#ifndef __ASSEMBLY__ +#include +#if defined(__i386__) +#include +#elif defined(__x86_64__) +#include +#else +#error "Unsupported architecture" +#endif +#endif + +#define L1_FRAME 1 +#define L2_FRAME 2 +#define L3_FRAME 3 + +#define L1_PAGETABLE_SHIFT 12 + +#if defined(__i386__) + +#define L2_PAGETABLE_SHIFT 21 +#define L3_PAGETABLE_SHIFT 30 + +#define L1_PAGETABLE_ENTRIES 512 +#define L2_PAGETABLE_ENTRIES 512 +#define L3_PAGETABLE_ENTRIES 4 + +#define PADDR_BITS 44 +#define PADDR_MASK ((1ULL << PADDR_BITS)-1) + +#define L2_MASK ((1UL << L3_PAGETABLE_SHIFT) - 1) + +/* + * If starting from virtual address greater than 0xc0000000, + * this value will be 2 to account for final mid-level page + * directory which is always mapped in at this location. + */ +#define NOT_L1_FRAMES 3 +#define PRIpte "016llx" +#ifndef __ASSEMBLY__ +typedef uint64_t pgentry_t; +#endif + +#elif defined(__x86_64__) + +#define L2_PAGETABLE_SHIFT 21 +#define L3_PAGETABLE_SHIFT 30 +#define L4_PAGETABLE_SHIFT 39 + +#define L1_PAGETABLE_ENTRIES 512 +#define L2_PAGETABLE_ENTRIES 512 +#define L3_PAGETABLE_ENTRIES 512 +#define L4_PAGETABLE_ENTRIES 512 + +/* These are page-table limitations. Current CPUs support only 40-bit phys. */ +#define PADDR_BITS 52 +#define VADDR_BITS 48 +#define PADDR_MASK ((1UL << PADDR_BITS)-1) +#define VADDR_MASK ((1UL << VADDR_BITS)-1) + +#define L2_MASK ((1UL << L3_PAGETABLE_SHIFT) - 1) +#define L3_MASK ((1UL << L4_PAGETABLE_SHIFT) - 1) + +#define NOT_L1_FRAMES 3 +#define PRIpte "016lx" +#ifndef __ASSEMBLY__ +typedef unsigned long pgentry_t; +#endif + +#endif + +#define L1_MASK ((1UL << L2_PAGETABLE_SHIFT) - 1) + +/* Given a virtual address, get an entry offset into a page table. */ +#define l1_table_offset(_a) \ + (((_a) >> L1_PAGETABLE_SHIFT) & (L1_PAGETABLE_ENTRIES - 1)) +#define l2_table_offset(_a) \ + (((_a) >> L2_PAGETABLE_SHIFT) & (L2_PAGETABLE_ENTRIES - 1)) +#define l3_table_offset(_a) \ + (((_a) >> L3_PAGETABLE_SHIFT) & (L3_PAGETABLE_ENTRIES - 1)) +#if defined(__x86_64__) +#define l4_table_offset(_a) \ + (((_a) >> L4_PAGETABLE_SHIFT) & (L4_PAGETABLE_ENTRIES - 1)) +#endif + +#define _PAGE_PRESENT 0x001ULL +#define _PAGE_RW 0x002ULL +#define _PAGE_USER 0x004ULL +#define _PAGE_PWT 0x008ULL +#define _PAGE_PCD 0x010ULL +#define _PAGE_ACCESSED 0x020ULL +#define _PAGE_DIRTY 0x040ULL +#define _PAGE_PAT 0x080ULL +#define _PAGE_PSE 0x080ULL +#define _PAGE_GLOBAL 0x100ULL + +#if defined(__i386__) +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED) +#define L1_PROT_RO (_PAGE_PRESENT|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY |_PAGE_USER) +#define L3_PROT (_PAGE_PRESENT) +#elif defined(__x86_64__) +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_USER) +#define L1_PROT_RO (_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_USER) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) +#define L3_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) +#define L4_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) +#endif /* __i386__ || __x86_64__ */ + +#include "arch_limits.h" +#define PAGE_SIZE __PAGE_SIZE +#define PAGE_SHIFT __PAGE_SHIFT +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> L1_PAGETABLE_SHIFT) +#define PFN_DOWN(x) ((x) >> L1_PAGETABLE_SHIFT) +#define PFN_PHYS(x) ((uint64_t)(x) << L1_PAGETABLE_SHIFT) +#define PHYS_PFN(x) ((x) >> L1_PAGETABLE_SHIFT) + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) + +#ifndef __ASSEMBLY__ +/* Definitions for machine and pseudophysical addresses. */ +#ifdef __i386__ +typedef unsigned long long paddr_t; +typedef unsigned long long maddr_t; +#else +typedef unsigned long paddr_t; +typedef unsigned long maddr_t; +#endif + +extern unsigned long *phys_to_machine_mapping; +extern char _text, _etext, _erodata, _edata, _end; +extern unsigned long mfn_zero; +#define pfn_to_mfn(_pfn) (phys_to_machine_mapping[(_pfn)]) +static __inline__ maddr_t phys_to_machine(paddr_t phys) +{ + maddr_t machine = pfn_to_mfn(phys >> PAGE_SHIFT); + machine = (machine << PAGE_SHIFT) | (phys & ~PAGE_MASK); + return machine; +} + +#define mfn_to_pfn(_mfn) (machine_to_phys_mapping[(_mfn)]) +static __inline__ paddr_t machine_to_phys(maddr_t machine) +{ + paddr_t phys = mfn_to_pfn(machine >> PAGE_SHIFT); + phys = (phys << PAGE_SHIFT) | (machine & ~PAGE_MASK); + return phys; +} +#endif + +#define VIRT_START ((unsigned long)&_text) + +#define to_phys(x) ((unsigned long)(x)-VIRT_START) +#define to_virt(x) ((void *)((unsigned long)(x)+VIRT_START)) + +#define virt_to_pfn(_virt) (PFN_DOWN(to_phys(_virt))) +#define virt_to_mfn(_virt) (pfn_to_mfn(virt_to_pfn(_virt))) +#define mach_to_virt(_mach) (to_virt(machine_to_phys(_mach))) +#define virt_to_mach(_virt) (phys_to_machine(to_phys(_virt))) +#define mfn_to_virt(_mfn) (to_virt(mfn_to_pfn(_mfn) << PAGE_SHIFT)) +#define pfn_to_virt(_pfn) (to_virt((_pfn) << PAGE_SHIFT)) + +/* Pagetable walking. */ +#define pte_to_mfn(_pte) (((_pte) & (PADDR_MASK&PAGE_MASK)) >> L1_PAGETABLE_SHIFT) +#define pte_to_virt(_pte) to_virt(mfn_to_pfn(pte_to_mfn(_pte)) << PAGE_SHIFT) + + +#define PT_BASE ((pgentry_t *)start_info.pt_base) + +#ifdef __x86_64__ +#define virtual_to_l3(_virt) ((pgentry_t *)pte_to_virt(PT_BASE[l4_table_offset(_virt)])) +#else +#define virtual_to_l3(_virt) PT_BASE +#endif + +#define virtual_to_l2(_virt) ({ \ + unsigned long __virt2 = (_virt); \ + (pgentry_t *) pte_to_virt(virtual_to_l3(__virt2)[l3_table_offset(__virt2)]); \ +}) + +#define virtual_to_l1(_virt) ({ \ + unsigned long __virt1 = (_virt); \ + (pgentry_t *) pte_to_virt(virtual_to_l2(__virt1)[l2_table_offset(__virt1)]); \ +}) + +#define virtual_to_pte(_virt) ({ \ + unsigned long __virt0 = (unsigned long) (_virt); \ + virtual_to_l1(__virt0)[l1_table_offset(__virt0)]; \ +}) +#define virtual_to_mfn(_virt) pte_to_mfn(virtual_to_pte(_virt)) + +#define map_frames(f, n) map_frames_ex(f, n, 1, 0, 1, DOMID_SELF, 0, L1_PROT) +#define map_zero(n, a) map_frames_ex(&mfn_zero, n, 0, 0, a, DOMID_SELF, 0, L1_PROT_RO) +#define do_map_zero(start, n) do_map_frames(start, &mfn_zero, n, 0, 0, DOMID_SELF, 0, L1_PROT_RO) + +pgentry_t *need_pgt(unsigned long addr); + +#endif /* _ARCH_MM_H_ */ diff --git a/extras/mini-os/include/x86/arch_sched.h b/extras/mini-os/include/x86/arch_sched.h new file mode 100644 index 0000000..b494eca --- /dev/null +++ b/extras/mini-os/include/x86/arch_sched.h @@ -0,0 +1,25 @@ + +#ifndef __ARCH_SCHED_H__ +#define __ARCH_SCHED_H__ + +#include "arch_limits.h" + +static inline struct thread* get_current(void) +{ + struct thread **current; +#ifdef __i386__ + register unsigned long sp asm("esp"); +#else + register unsigned long sp asm("rsp"); +#endif + current = (void *)(unsigned long)(sp & ~(__STACK_SIZE-1)); + return *current; +} + +extern void __arch_switch_threads(unsigned long *prevctx, unsigned long *nextctx); + +#define arch_switch_threads(prev,next) __arch_switch_threads(&(prev)->sp, &(next)->sp) + + + +#endif /* __ARCH_SCHED_H__ */ diff --git a/extras/mini-os/include/x86/arch_spinlock.h b/extras/mini-os/include/x86/arch_spinlock.h new file mode 100644 index 0000000..4b8faf7 --- /dev/null +++ b/extras/mini-os/include/x86/arch_spinlock.h @@ -0,0 +1,94 @@ + + +#ifndef __ARCH_ASM_SPINLOCK_H +#define __ARCH_ASM_SPINLOCK_H + +#include +#include "os.h" + + +#define ARCH_SPIN_LOCK_UNLOCKED { 1 } + +/* + * Simple spin lock operations. There are two variants, one clears IRQ's + * on the local processor, one does not. + * + * We make no fairness assumptions. They have a cost. + */ + +#define arch_spin_is_locked(x) (*(volatile signed char *)(&(x)->slock) <= 0) +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) + +#define spin_lock_string \ + "1:\n" \ + LOCK \ + "decb %0\n\t" \ + "jns 3f\n" \ + "2:\t" \ + "rep;nop\n\t" \ + "cmpb $0,%0\n\t" \ + "jle 2b\n\t" \ + "jmp 1b\n" \ + "3:\n\t" + +#define spin_lock_string_flags \ + "1:\n" \ + LOCK \ + "decb %0\n\t" \ + "jns 4f\n\t" \ + "2:\t" \ + "testl $0x200, %1\n\t" \ + "jz 3f\n\t" \ + "#sti\n\t" \ + "3:\t" \ + "rep;nop\n\t" \ + "cmpb $0, %0\n\t" \ + "jle 3b\n\t" \ + "#cli\n\t" \ + "jmp 1b\n" \ + "4:\n\t" + +/* + * This works. Despite all the confusion. + * (except on PPro SMP or if we are using OOSTORE) + * (PPro errata 66, 92) + */ + +#define spin_unlock_string \ + "xchgb %b0, %1" \ + :"=q" (oldval), "=m" (lock->slock) \ + :"0" (oldval) : "memory" + +static inline void _raw_spin_unlock(spinlock_t *lock) +{ + char oldval = 1; + __asm__ __volatile__( + spin_unlock_string + ); +} + +static inline int _raw_spin_trylock(spinlock_t *lock) +{ + char oldval; + __asm__ __volatile__( + "xchgb %b0,%1\n" + :"=q" (oldval), "=m" (lock->slock) + :"0" (0) : "memory"); + return oldval > 0; +} + +static inline void _raw_spin_lock(spinlock_t *lock) +{ + __asm__ __volatile__( + spin_lock_string + :"=m" (lock->slock) : : "memory"); +} + +static inline void _raw_spin_lock_flags (spinlock_t *lock, unsigned long flags) +{ + __asm__ __volatile__( + spin_lock_string_flags + :"=m" (lock->slock) : "r" (flags) : "memory"); +} + +#endif diff --git a/extras/mini-os/include/x86/os.h b/extras/mini-os/include/x86/os.h new file mode 100644 index 0000000..7fc3a83 --- /dev/null +++ b/extras/mini-os/include/x86/os.h @@ -0,0 +1,571 @@ +/****************************************************************************** + * os.h + * + * random collection of macros and definition + */ + +#ifndef _OS_H_ +#define _OS_H_ + +#if __GNUC__ == 2 && __GNUC_MINOR__ < 96 +#define __builtin_expect(x, expected_value) (x) +#endif +#define unlikely(x) __builtin_expect((x),0) +#define likely(x) __builtin_expect((x),1) + +#define smp_processor_id() 0 + + +#ifndef __ASSEMBLY__ +#include +#include +#include + +#define USED __attribute__ ((used)) + +#define BUG do_exit + +#endif +#include + + + +#define __KERNEL_CS FLAT_KERNEL_CS +#define __KERNEL_DS FLAT_KERNEL_DS +#define __KERNEL_SS FLAT_KERNEL_SS + +#define TRAP_divide_error 0 +#define TRAP_debug 1 +#define TRAP_nmi 2 +#define TRAP_int3 3 +#define TRAP_overflow 4 +#define TRAP_bounds 5 +#define TRAP_invalid_op 6 +#define TRAP_no_device 7 +#define TRAP_double_fault 8 +#define TRAP_copro_seg 9 +#define TRAP_invalid_tss 10 +#define TRAP_no_segment 11 +#define TRAP_stack_error 12 +#define TRAP_gp_fault 13 +#define TRAP_page_fault 14 +#define TRAP_spurious_int 15 +#define TRAP_copro_error 16 +#define TRAP_alignment_check 17 +#define TRAP_machine_check 18 +#define TRAP_simd_error 19 +#define TRAP_deferred_nmi 31 + +/* Everything below this point is not included by assembler (.S) files. */ +#ifndef __ASSEMBLY__ + +extern shared_info_t *HYPERVISOR_shared_info; + +void trap_init(void); +void trap_fini(void); + +void arch_init(start_info_t *si); +void arch_print_info(void); +void arch_fini(void); + + + + + +/* + * The use of 'barrier' in the following reflects their use as local-lock + * operations. Reentrancy must be prevented (e.g., __cli()) /before/ following + * critical operations are executed. All critical operations must complete + * /before/ reentrancy is permitted (e.g., __sti()). Alpha architecture also + * includes these barriers, for example. + */ + +#define __cli() \ +do { \ + vcpu_info_t *_vcpu; \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + _vcpu->evtchn_upcall_mask = 1; \ + barrier(); \ +} while (0) + +#define __sti() \ +do { \ + vcpu_info_t *_vcpu; \ + barrier(); \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + _vcpu->evtchn_upcall_mask = 0; \ + barrier(); /* unmask then check (avoid races) */ \ + if ( unlikely(_vcpu->evtchn_upcall_pending) ) \ + force_evtchn_callback(); \ +} while (0) + +#define __save_flags(x) \ +do { \ + vcpu_info_t *_vcpu; \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + (x) = _vcpu->evtchn_upcall_mask; \ +} while (0) + +#define __restore_flags(x) \ +do { \ + vcpu_info_t *_vcpu; \ + barrier(); \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + if ((_vcpu->evtchn_upcall_mask = (x)) == 0) { \ + barrier(); /* unmask then check (avoid races) */ \ + if ( unlikely(_vcpu->evtchn_upcall_pending) ) \ + force_evtchn_callback(); \ + }\ +} while (0) + +#define safe_halt() ((void)0) + +#define __save_and_cli(x) \ +do { \ + vcpu_info_t *_vcpu; \ + _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ + (x) = _vcpu->evtchn_upcall_mask; \ + _vcpu->evtchn_upcall_mask = 1; \ + barrier(); \ +} while (0) + +#define local_irq_save(x) __save_and_cli(x) +#define local_irq_restore(x) __restore_flags(x) +#define local_save_flags(x) __save_flags(x) +#define local_irq_disable() __cli() +#define local_irq_enable() __sti() + +#define irqs_disabled() \ + HYPERVISOR_shared_info->vcpu_info[smp_processor_id()].evtchn_upcall_mask + +/* This is a barrier for the compiler only, NOT the processor! */ +#define barrier() __asm__ __volatile__("": : :"memory") + +#if defined(__i386__) +#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") +#define rmb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") +#define wmb() __asm__ __volatile__ ("": : :"memory") +#elif defined(__x86_64__) +#define mb() __asm__ __volatile__ ("mfence":::"memory") +#define rmb() __asm__ __volatile__ ("lfence":::"memory") +#define wmb() __asm__ __volatile__ ("sfence" ::: "memory") /* From CONFIG_UNORDERED_IO (linux) */ +#endif + + +#define LOCK_PREFIX "" +#define LOCK "" +#define ADDR (*(volatile long *) addr) +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +typedef struct { volatile int counter; } atomic_t; + + +/************************** i386 *******************************/ +#if defined (__i386__) + +#define xchg(ptr,v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v),(ptr),sizeof(*(ptr)))) +struct __xchg_dummy { unsigned long a[100]; }; +#define __xg(x) ((struct __xchg_dummy *)(x)) +static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) +{ + switch (size) { + case 1: + __asm__ __volatile__("xchgb %b0,%1" + :"=q" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 2: + __asm__ __volatile__("xchgw %w0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 4: + __asm__ __volatile__("xchgl %0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + } + return x; +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It can be reorderdered on other architectures other than x86. + * It also implies a memory barrier. + */ +static inline int test_and_clear_bit(int nr, volatile unsigned long * addr) +{ + int oldbit; + + __asm__ __volatile__( LOCK + "btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"Ir" (nr) : "memory"); + return oldbit; +} + +static inline int constant_test_bit(int nr, const volatile unsigned long *addr) +{ + return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; +} + +static inline int variable_test_bit(int nr, const volatile unsigned long * addr) +{ + int oldbit; + + __asm__ __volatile__( + "btl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit) + :"m" (ADDR),"Ir" (nr)); + return oldbit; +} + +#define test_bit(nr,addr) \ +(__builtin_constant_p(nr) ? \ + constant_test_bit((nr),(addr)) : \ + variable_test_bit((nr),(addr))) + +/** + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is atomic and may not be reordered. See __set_bit() + * if you do not require the atomic guarantees. + * + * Note: there are no guarantees that this function will not be reordered + * on non x86 architectures, so if you are writting portable code, + * make sure not to rely on its reordering guarantees. + * + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static inline void set_bit(int nr, volatile unsigned long * addr) +{ + __asm__ __volatile__( LOCK + "btsl %1,%0" + :"=m" (ADDR) + :"Ir" (nr)); +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * clear_bit() is atomic and may not be reordered. However, it does + * not contain a memory barrier, so if it is used for locking purposes, + * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() + * in order to ensure changes are visible on other processors. + */ +static inline void clear_bit(int nr, volatile unsigned long * addr) +{ + __asm__ __volatile__( LOCK + "btrl %1,%0" + :"=m" (ADDR) + :"Ir" (nr)); +} + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static inline unsigned long __ffs(unsigned long word) +{ + __asm__("bsfl %1,%0" + :"=r" (word) + :"rm" (word)); + return word; +} + + +/* + * These have to be done with inline assembly: that way the bit-setting + * is guaranteed to be atomic. All bit operations return 0 if the bit + * was cleared before the operation and != 0 if it was not. + * + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ +#define ADDR (*(volatile long *) addr) + +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + + + +#elif defined(__x86_64__)/* ifdef __i386__ */ +/************************** x86_84 *******************************/ + +#define xchg(ptr,v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v),(ptr),sizeof(*(ptr)))) +#define __xg(x) ((volatile long *)(x)) +static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) +{ + switch (size) { + case 1: + __asm__ __volatile__("xchgb %b0,%1" + :"=q" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 2: + __asm__ __volatile__("xchgw %w0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 4: + __asm__ __volatile__("xchgl %k0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 8: + __asm__ __volatile__("xchgq %0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + } + return x; +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_clear_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__ __volatile__( LOCK_PREFIX + "btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr) : "memory"); + return oldbit; +} + +static __inline__ int constant_test_bit(int nr, const volatile void * addr) +{ + return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0; +} + +static __inline__ int variable_test_bit(int nr, volatile const void * addr) +{ + int oldbit; + + __asm__ __volatile__( + "btl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit) + :"m" (ADDR),"dIr" (nr)); + return oldbit; +} + +#define test_bit(nr,addr) \ +(__builtin_constant_p(nr) ? \ + constant_test_bit((nr),(addr)) : \ + variable_test_bit((nr),(addr))) + + +/** + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is atomic and may not be reordered. See __set_bit() + * if you do not require the atomic guarantees. + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static __inline__ void set_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btsl %1,%0" + :"=m" (ADDR) + :"dIr" (nr) : "memory"); +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * clear_bit() is atomic and may not be reordered. However, it does + * not contain a memory barrier, so if it is used for locking purposes, + * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() + * in order to ensure changes are visible on other processors. + */ +static __inline__ void clear_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btrl %1,%0" + :"=m" (ADDR) + :"dIr" (nr)); +} + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static __inline__ unsigned long __ffs(unsigned long word) +{ + __asm__("bsfq %1,%0" + :"=r" (word) + :"rm" (word)); + return word; +} + +#define ADDR (*(volatile long *) addr) + +#define rdtscll(val) do { \ + unsigned int __a,__d; \ + asm volatile("rdtsc" : "=a" (__a), "=d" (__d)); \ + (val) = ((unsigned long)__a) | (((unsigned long)__d)<<32); \ +} while(0) + +#define wrmsr(msr,val1,val2) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (val1), "d" (val2)) + +#define wrmsrl(msr,val) wrmsr(msr,(u32)((u64)(val)),((u64)(val))>>32) + + +#else /* ifdef __x86_64__ */ +#error "Unsupported architecture" +#endif + + +/********************* common i386 and x86_64 ****************************/ +struct __synch_xchg_dummy { unsigned long a[100]; }; +#define __synch_xg(x) ((struct __synch_xchg_dummy *)(x)) + +#define synch_cmpxchg(ptr, old, new) \ +((__typeof__(*(ptr)))__synch_cmpxchg((ptr),\ + (unsigned long)(old), \ + (unsigned long)(new), \ + sizeof(*(ptr)))) + +static inline unsigned long __synch_cmpxchg(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + unsigned long prev; + switch (size) { + case 1: + __asm__ __volatile__("lock; cmpxchgb %b1,%2" + : "=a"(prev) + : "q"(new), "m"(*__synch_xg(ptr)), + "0"(old) + : "memory"); + return prev; + case 2: + __asm__ __volatile__("lock; cmpxchgw %w1,%2" + : "=a"(prev) + : "r"(new), "m"(*__synch_xg(ptr)), + "0"(old) + : "memory"); + return prev; +#ifdef __x86_64__ + case 4: + __asm__ __volatile__("lock; cmpxchgl %k1,%2" + : "=a"(prev) + : "r"(new), "m"(*__synch_xg(ptr)), + "0"(old) + : "memory"); + return prev; + case 8: + __asm__ __volatile__("lock; cmpxchgq %1,%2" + : "=a"(prev) + : "r"(new), "m"(*__synch_xg(ptr)), + "0"(old) + : "memory"); + return prev; +#else + case 4: + __asm__ __volatile__("lock; cmpxchgl %1,%2" + : "=a"(prev) + : "r"(new), "m"(*__synch_xg(ptr)), + "0"(old) + : "memory"); + return prev; +#endif + } + return old; +} + + +static __inline__ void synch_set_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__ ( + "lock btsl %1,%0" + : "=m" (ADDR) : "Ir" (nr) : "memory" ); +} + +static __inline__ void synch_clear_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__ ( + "lock btrl %1,%0" + : "=m" (ADDR) : "Ir" (nr) : "memory" ); +} + +static __inline__ int synch_test_and_set_bit(int nr, volatile void * addr) +{ + int oldbit; + __asm__ __volatile__ ( + "lock btsl %2,%1\n\tsbbl %0,%0" + : "=r" (oldbit), "=m" (ADDR) : "Ir" (nr) : "memory"); + return oldbit; +} + +static __inline__ int synch_test_and_clear_bit(int nr, volatile void * addr) +{ + int oldbit; + __asm__ __volatile__ ( + "lock btrl %2,%1\n\tsbbl %0,%0" + : "=r" (oldbit), "=m" (ADDR) : "Ir" (nr) : "memory"); + return oldbit; +} + +static __inline__ int synch_const_test_bit(int nr, const volatile void * addr) +{ + return ((1UL << (nr & 31)) & + (((const volatile unsigned int *) addr)[nr >> 5])) != 0; +} + +static __inline__ int synch_var_test_bit(int nr, volatile void * addr) +{ + int oldbit; + __asm__ __volatile__ ( + "btl %2,%1\n\tsbbl %0,%0" + : "=r" (oldbit) : "m" (ADDR), "Ir" (nr) ); + return oldbit; +} + +#define synch_test_bit(nr,addr) \ +(__builtin_constant_p(nr) ? \ + synch_const_test_bit((nr),(addr)) : \ + synch_var_test_bit((nr),(addr))) + + +#undef ADDR + +#endif /* not assembly */ +#endif /* _OS_H_ */ diff --git a/extras/mini-os/include/x86/traps.h b/extras/mini-os/include/x86/traps.h new file mode 100644 index 0000000..bfb6781 --- /dev/null +++ b/extras/mini-os/include/x86/traps.h @@ -0,0 +1,78 @@ +/* + **************************************************************************** + * (C) 2005 - Grzegorz Milos - Intel Reseach Cambridge + **************************************************************************** + * + * File: traps.h + * Author: Grzegorz Milos (gm281@cam.ac.uk) + * + * Date: Jun 2005 + * + * Environment: Xen Minimal OS + * Description: Deals with traps + * + **************************************************************************** + */ + +#ifndef _TRAPS_H_ +#define _TRAPS_H_ + +#ifdef __i386__ +struct pt_regs { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + int xds; + int xes; + long orig_eax; + long eip; + int xcs; + long eflags; + long esp; + int xss; +}; +#elif __x86_64__ + +struct pt_regs { + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long rbp; + unsigned long rbx; +/* arguments: non interrupts/non tracing syscalls only save upto here*/ + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rax; + unsigned long rcx; + unsigned long rdx; + unsigned long rsi; + unsigned long rdi; + unsigned long orig_rax; +/* end of arguments */ +/* cpu exception frame or undefined */ + unsigned long rip; + unsigned long cs; + unsigned long eflags; + unsigned long rsp; + unsigned long ss; +/* top of stack page */ +}; + + +#endif + +void dump_regs(struct pt_regs *regs); +void stack_walk(void); + +#define TRAP_PF_PROT 0x1 +#define TRAP_PF_WRITE 0x2 +#define TRAP_PF_USER 0x4 + +#endif /* _TRAPS_H_ */ diff --git a/extras/mini-os/include/x86/x86_32/hypercall-x86_32.h b/extras/mini-os/include/x86/x86_32/hypercall-x86_32.h new file mode 100644 index 0000000..d5f5b1e --- /dev/null +++ b/extras/mini-os/include/x86/x86_32/hypercall-x86_32.h @@ -0,0 +1,324 @@ +/****************************************************************************** + * hypercall-x86_32.h + * + * Copied from XenLinux. + * + * Copyright (c) 2002-2004, K A Fraser + * + * This file may be 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 __HYPERCALL_X86_32_H__ +#define __HYPERCALL_X86_32_H__ + +#include +#include +#include +#include + +#define __STR(x) #x +#define STR(x) __STR(x) + +extern char hypercall_page[PAGE_SIZE]; + +#define _hypercall0(type, name) \ +({ \ + long __res; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res) \ + : \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + long __res, __ign1; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=b" (__ign1) \ + : "1" ((long)(a1)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ + : "1" ((long)(a1)), "2" ((long)(a2)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3) \ + : "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 hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4) \ + : "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 hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + "5" ((long)(a5)) \ + : "memory" ); \ + (type)__res; \ +}) + +static inline int +HYPERVISOR_set_trap_table( + trap_info_t *table) +{ + return _hypercall1(int, set_trap_table, table); +} + +static inline int +HYPERVISOR_mmu_update( + mmu_update_t *req, int count, int *success_count, domid_t domid) +{ + return _hypercall4(int, mmu_update, req, count, success_count, domid); +} + +static inline int +HYPERVISOR_mmuext_op( + struct mmuext_op *op, int count, int *success_count, domid_t domid) +{ + return _hypercall4(int, mmuext_op, op, count, success_count, domid); +} + +static inline int +HYPERVISOR_set_gdt( + unsigned long *frame_list, int entries) +{ + return _hypercall2(int, set_gdt, frame_list, entries); +} + +static inline int +HYPERVISOR_stack_switch( + unsigned long ss, unsigned long esp) +{ + return _hypercall2(int, stack_switch, ss, esp); +} + +static inline int +HYPERVISOR_set_callbacks( + unsigned long event_selector, unsigned long event_address, + unsigned long failsafe_selector, unsigned long failsafe_address) +{ + return _hypercall4(int, set_callbacks, + event_selector, event_address, + failsafe_selector, failsafe_address); +} + +static inline int +HYPERVISOR_fpu_taskswitch( + int set) +{ + return _hypercall1(int, fpu_taskswitch, set); +} + +static inline int +HYPERVISOR_sched_op( + int cmd, void *arg) +{ + return _hypercall2(int, sched_op, cmd, arg); +} + +static inline long +HYPERVISOR_set_timer_op( + u64 timeout) +{ + unsigned long timeout_hi = (unsigned long)(timeout>>32); + unsigned long timeout_lo = (unsigned long)timeout; + return _hypercall2(long, set_timer_op, timeout_lo, timeout_hi); +} + +static inline int +HYPERVISOR_set_debugreg( + int reg, unsigned long value) +{ + return _hypercall2(int, set_debugreg, reg, value); +} + +static inline unsigned long +HYPERVISOR_get_debugreg( + int reg) +{ + return _hypercall1(unsigned long, get_debugreg, reg); +} + +static inline int +HYPERVISOR_update_descriptor( + u64 ma, u64 desc) +{ + return _hypercall4(int, update_descriptor, ma, ma>>32, desc, desc>>32); +} + +static inline int +HYPERVISOR_memory_op( + unsigned int cmd, void *arg) +{ + return _hypercall2(int, memory_op, cmd, arg); +} + +static inline int +HYPERVISOR_multicall( + void *call_list, int nr_calls) +{ + return _hypercall2(int, multicall, call_list, nr_calls); +} + +static inline int +HYPERVISOR_update_va_mapping( + unsigned long va, pte_t new_val, unsigned long flags) +{ + return _hypercall4(int, update_va_mapping, va, + new_val.pte_low, new_val.pte_high, flags); +} + +static inline int +HYPERVISOR_event_channel_op( + int cmd, void *op) +{ + return _hypercall2(int, event_channel_op, cmd, op); +} + +static inline int +HYPERVISOR_xen_version( + int cmd, void *arg) +{ + return _hypercall2(int, xen_version, cmd, arg); +} + +static inline int +HYPERVISOR_console_io( + int cmd, int count, char *str) +{ + return _hypercall3(int, console_io, cmd, count, str); +} + +static inline int +HYPERVISOR_physdev_op( + void *physdev_op) +{ + return _hypercall1(int, physdev_op, physdev_op); +} + +static inline int +HYPERVISOR_grant_table_op( + unsigned int cmd, void *uop, unsigned int count) +{ + return _hypercall3(int, grant_table_op, cmd, uop, count); +} + +static inline int +HYPERVISOR_update_va_mapping_otherdomain( + unsigned long va, pte_t new_val, unsigned long flags, domid_t domid) +{ + return _hypercall5(int, update_va_mapping_otherdomain, va, + new_val.pte_low, new_val.pte_high, flags, domid); +} + +static inline int +HYPERVISOR_vm_assist( + unsigned int cmd, unsigned int type) +{ + return _hypercall2(int, vm_assist, cmd, type); +} + +static inline int +HYPERVISOR_vcpu_op( + int cmd, int vcpuid, void *extra_args) +{ + return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); +} + +static inline int +HYPERVISOR_suspend( + unsigned long srec) +{ + return _hypercall3(int, sched_op, SCHEDOP_shutdown, + SHUTDOWN_suspend, srec); +} + +static inline int +HYPERVISOR_nmi_op( + unsigned long op, + unsigned long arg) +{ + return _hypercall2(int, nmi_op, op, arg); +} + +static inline int +HYPERVISOR_sysctl( + unsigned long op) +{ + return _hypercall1(int, sysctl, op); +} + +static inline int +HYPERVISOR_domctl( + unsigned long op) +{ + return _hypercall1(int, domctl, op); +} + +#endif /* __HYPERCALL_X86_32_H__ */ + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/extras/mini-os/include/x86/x86_64/hypercall-x86_64.h b/extras/mini-os/include/x86/x86_64/hypercall-x86_64.h new file mode 100644 index 0000000..32ea5bd --- /dev/null +++ b/extras/mini-os/include/x86/x86_64/hypercall-x86_64.h @@ -0,0 +1,332 @@ +/****************************************************************************** + * hypercall-x86_64.h + * + * Copied from XenLinux. + * + * Copyright (c) 2002-2004, K A Fraser + * + * 64-bit updates: + * Benjamin Liu + * Jun Nakajima + * + * This file may be 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 __HYPERCALL_X86_64_H__ +#define __HYPERCALL_X86_64_H__ + +#include +#include +#include + +#define __STR(x) #x +#define STR(x) __STR(x) + +extern char hypercall_page[PAGE_SIZE]; + +#define _hypercall0(type, name) \ +({ \ + long __res; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res) \ + : \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + long __res, __ign1; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=D" (__ign1) \ + : "1" ((long)(a1)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=D" (__ign1), "=S" (__ign2) \ + : "1" ((long)(a1)), "2" ((long)(a2)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \ + "=d" (__ign3) \ + : "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; \ + asm volatile ( \ + "movq %7,%%r10; " \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \ + "=d" (__ign3) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "g" ((long)(a4)) \ + : "memory", "r10" ); \ + (type)__res; \ +}) + +#define _hypercall5(type, name, a1, a2, a3, a4, a5) \ +({ \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "movq %7,%%r10; movq %8,%%r8; " \ + "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\ + : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \ + "=d" (__ign3) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "g" ((long)(a4)), \ + "g" ((long)(a5)) \ + : "memory", "r10", "r8" ); \ + (type)__res; \ +}) + +static inline int +HYPERVISOR_set_trap_table( + trap_info_t *table) +{ + return _hypercall1(int, set_trap_table, table); +} + +static inline int +HYPERVISOR_mmu_update( + mmu_update_t *req, int count, int *success_count, domid_t domid) +{ + return _hypercall4(int, mmu_update, req, count, success_count, domid); +} + +static inline int +HYPERVISOR_mmuext_op( + struct mmuext_op *op, int count, int *success_count, domid_t domid) +{ + return _hypercall4(int, mmuext_op, op, count, success_count, domid); +} + +static inline int +HYPERVISOR_set_gdt( + unsigned long *frame_list, int entries) +{ + return _hypercall2(int, set_gdt, frame_list, entries); +} + +static inline int +HYPERVISOR_stack_switch( + unsigned long ss, unsigned long esp) +{ + return _hypercall2(int, stack_switch, ss, esp); +} + +static inline int +HYPERVISOR_set_callbacks( + unsigned long event_address, unsigned long failsafe_address, + unsigned long syscall_address) +{ + return _hypercall3(int, set_callbacks, + event_address, failsafe_address, syscall_address); +} + +static inline int +HYPERVISOR_fpu_taskswitch( + int set) +{ + return _hypercall1(int, fpu_taskswitch, set); +} + +static inline int +HYPERVISOR_sched_op( + int cmd, void *arg) +{ + return _hypercall2(int, sched_op, cmd, arg); +} + +static inline long +HYPERVISOR_set_timer_op( + u64 timeout) +{ + return _hypercall1(long, set_timer_op, timeout); +} + +static inline int +HYPERVISOR_set_debugreg( + int reg, unsigned long value) +{ + return _hypercall2(int, set_debugreg, reg, value); +} + +static inline unsigned long +HYPERVISOR_get_debugreg( + int reg) +{ + return _hypercall1(unsigned long, get_debugreg, reg); +} + +static inline int +HYPERVISOR_update_descriptor( + unsigned long ma, unsigned long word) +{ + return _hypercall2(int, update_descriptor, ma, word); +} + +static inline int +HYPERVISOR_memory_op( + unsigned int cmd, void *arg) +{ + return _hypercall2(int, memory_op, cmd, arg); +} + +static inline int +HYPERVISOR_multicall( + void *call_list, int nr_calls) +{ + return _hypercall2(int, multicall, call_list, nr_calls); +} + +static inline int +HYPERVISOR_update_va_mapping( + unsigned long va, pte_t new_val, unsigned long flags) +{ + return _hypercall3(int, update_va_mapping, va, new_val.pte, flags); +} + +static inline int +HYPERVISOR_event_channel_op( + int cmd, void *op) +{ + return _hypercall2(int, event_channel_op, cmd, op); +} + +static inline int +HYPERVISOR_xen_version( + int cmd, void *arg) +{ + return _hypercall2(int, xen_version, cmd, arg); +} + +static inline int +HYPERVISOR_console_io( + int cmd, int count, char *str) +{ + return _hypercall3(int, console_io, cmd, count, str); +} + +static inline int +HYPERVISOR_physdev_op( + void *physdev_op) +{ + return _hypercall1(int, physdev_op, physdev_op); +} + +static inline int +HYPERVISOR_grant_table_op( + unsigned int cmd, void *uop, unsigned int count) +{ + return _hypercall3(int, grant_table_op, cmd, uop, count); +} + +static inline int +HYPERVISOR_update_va_mapping_otherdomain( + unsigned long va, pte_t new_val, unsigned long flags, domid_t domid) +{ + return _hypercall4(int, update_va_mapping_otherdomain, va, + new_val.pte, flags, domid); +} + +static inline int +HYPERVISOR_vm_assist( + unsigned int cmd, unsigned int type) +{ + return _hypercall2(int, vm_assist, cmd, type); +} + +static inline int +HYPERVISOR_vcpu_op( + int cmd, int vcpuid, void *extra_args) +{ + return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); +} + +static inline int +HYPERVISOR_set_segment_base( + int reg, unsigned long value) +{ + return _hypercall2(int, set_segment_base, reg, value); +} + +static inline int +HYPERVISOR_suspend( + unsigned long srec) +{ + return _hypercall3(int, sched_op, SCHEDOP_shutdown, + SHUTDOWN_suspend, srec); +} + +static inline int +HYPERVISOR_nmi_op( + unsigned long op, + unsigned long arg) +{ + return _hypercall2(int, nmi_op, op, arg); +} + +static inline int +HYPERVISOR_sysctl( + unsigned long op) +{ + return _hypercall1(int, sysctl, op); +} + +static inline int +HYPERVISOR_domctl( + unsigned long op) +{ + return _hypercall1(int, domctl, op); +} + +#endif /* __HYPERCALL_X86_64_H__ */ + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/extras/mini-os/include/xenbus.h b/extras/mini-os/include/xenbus.h new file mode 100644 index 0000000..e8de09f --- /dev/null +++ b/extras/mini-os/include/xenbus.h @@ -0,0 +1,97 @@ +#ifndef XENBUS_H__ +#define XENBUS_H__ + +typedef unsigned long xenbus_transaction_t; +#define XBT_NIL ((xenbus_transaction_t)0) + +/* Initialize the XenBus system. */ +void init_xenbus(void); + +/* Read the value associated with a path. Returns a malloc'd error + string on failure and sets *value to NULL. On success, *value is + set to a malloc'd copy of the value. */ +char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value); + +/* Watch event queue */ +struct xenbus_event { + /* Keep these two as this for xs.c */ + char *path; + char *token; + struct xenbus_event *next; +}; +typedef struct xenbus_event *xenbus_event_queue; + +char *xenbus_watch_path_token(xenbus_transaction_t xbt, const char *path, const char *token, xenbus_event_queue *events); +char *xenbus_unwatch_path_token(xenbus_transaction_t xbt, const char *path, const char *token); +extern struct wait_queue_head xenbus_watch_queue; +void xenbus_wait_for_watch(xenbus_event_queue *queue); +char **xenbus_wait_for_watch_return(xenbus_event_queue *queue); +char* xenbus_wait_for_value(const char *path, const char *value, xenbus_event_queue *queue); + +/* When no token is provided, use a global queue. */ +#define XENBUS_WATCH_PATH_TOKEN "xenbus_watch_path" +extern xenbus_event_queue xenbus_events; +#define xenbus_watch_path(xbt, path) xenbus_watch_path_token(xbt, path, XENBUS_WATCH_PATH_TOKEN, NULL) +#define xenbus_unwatch_path(xbt, path) xenbus_unwatch_path_token(xbt, path, XENBUS_WATCH_PATH_TOKEN) + + +/* Associates a value with a path. Returns a malloc'd error string on + failure. */ +char *xenbus_write(xenbus_transaction_t xbt, const char *path, const char *value); + +struct write_req { + const void *data; + unsigned len; +}; + +/* Send a message to xenbus, in the same fashion as xb_write, and + block waiting for a reply. The reply is malloced and should be + freed by the caller. */ +struct xsd_sockmsg * +xenbus_msg_reply(int type, + xenbus_transaction_t trans, + struct write_req *io, + int nr_reqs); + +/* Removes the value associated with a path. Returns a malloc'd error + string on failure. */ +char *xenbus_rm(xenbus_transaction_t xbt, const char *path); + +/* List the contents of a directory. Returns a malloc'd error string + on failure and sets *contents to NULL. On success, *contents is + set to a malloc'd array of pointers to malloc'd strings. The array + is NULL terminated. May block. */ +char *xenbus_ls(xenbus_transaction_t xbt, const char *prefix, char ***contents); + +/* Reads permissions associated with a path. Returns a malloc'd error + string on failure and sets *value to NULL. On success, *value is + set to a malloc'd copy of the value. */ +char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value); + +/* Sets the permissions associated with a path. Returns a malloc'd + error string on failure. */ +char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path, domid_t dom, char perm); + +/* Start a xenbus transaction. Returns the transaction in xbt on + success or a malloc'd error string otherwise. */ +char *xenbus_transaction_start(xenbus_transaction_t *xbt); + +/* End a xenbus transaction. Returns a malloc'd error string if it + fails. abort says whether the transaction should be aborted. + Returns 1 in *retry iff the transaction should be retried. */ +char *xenbus_transaction_end(xenbus_transaction_t, int abort, + int *retry); + +/* Read path and parse it as an integer. Returns -1 on error. */ +int xenbus_read_integer(const char *path); + +/* Contraction of snprintf and xenbus_write(path/node). */ +char* xenbus_printf(xenbus_transaction_t xbt, + const char* node, const char* path, + const char* fmt, ...) + __attribute__((__format__(printf, 4, 5))); + +/* Reset the XenBus system. */ +void fini_xenbus(void); + +#endif /* XENBUS_H__ */ diff --git a/extras/mini-os/include/xmalloc.h b/extras/mini-os/include/xmalloc.h new file mode 100644 index 0000000..13b242e --- /dev/null +++ b/extras/mini-os/include/xmalloc.h @@ -0,0 +1,44 @@ +#ifndef __XMALLOC_H__ +#define __XMALLOC_H__ + +#ifdef HAVE_LIBC + +#include +#include +/* Allocate space for typed object. */ +#define _xmalloc(size, align) memalign(align, size) +#define xfree(ptr) free(ptr) + +#else + +#include + +#define DEFAULT_ALIGN (sizeof(unsigned long)) +#define malloc(size) _xmalloc(size, DEFAULT_ALIGN) +#define free(ptr) xfree(ptr) +#define realloc(ptr, size) _realloc(ptr, size) + +/* Free any of the above. */ +extern void xfree(const void *); + +/* Underlying functions */ +extern void *_xmalloc(size_t size, size_t align); +extern void *_realloc(void *ptr, size_t size); + +#endif + +static inline void *_xmalloc_array(size_t size, size_t align, size_t num) +{ + /* Check for overflow. */ + if (size && num > UINT_MAX / size) + return NULL; + return _xmalloc(size * num, align); +} + +/* Allocate space for typed object. */ +#define xmalloc(_type) ((_type *)_xmalloc(sizeof(_type), __alignof__(_type))) + +/* Allocate space for array of typed objects. */ +#define xmalloc_array(_type, _num) ((_type *)_xmalloc_array(sizeof(_type), __alignof__(_type), _num)) + +#endif /* __XMALLOC_H__ */ diff --git a/extras/mini-os/kernel.c b/extras/mini-os/kernel.c new file mode 100644 index 0000000..877168c --- /dev/null +++ b/extras/mini-os/kernel.c @@ -0,0 +1,601 @@ +/****************************************************************************** + * kernel.c + * + * Assorted crap goes here, including the initial C entry point, jumped at + * from head.S. + * + * Copyright (c) 2002-2003, K A Fraser & R Neugebauer + * Copyright (c) 2005, Grzegorz Milos, Intel Research Cambridge + * Copyright (c) 2006, Robert Kaiser, FH Wiesbaden + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static struct netfront_dev *net_dev; + +u8 xen_features[XENFEAT_NR_SUBMAPS * 32]; + +void setup_xen_features(void) +{ + xen_feature_info_t fi; + int i, j; + + for (i = 0; i < XENFEAT_NR_SUBMAPS; i++) + { + fi.submap_idx = i; + if (HYPERVISOR_xen_version(XENVER_get_features, &fi) < 0) + break; + + for (j=0; j<32; j++) + xen_features[i*32+j] = !!(fi.submap & 1<aiocb.aio_dev = blk_dev; + req->aiocb.aio_buf = _xmalloc(blk_info.sector_size, blk_info.sector_size); + req->aiocb.aio_nbytes = blk_info.sector_size; + req->aiocb.aio_offset = sector * blk_info.sector_size; + req->aiocb.data = req; + req->next = NULL; + return req; +} + +static void blk_read_completed(struct blkfront_aiocb *aiocb, int ret) +{ + struct blk_req *req = aiocb->data; + if (ret) + printk("got error code %d when reading at offset %ld\n", ret, aiocb->aio_offset); + else + blk_size_read += blk_info.sector_size; + free(aiocb->aio_buf); + free(req); +} + +static void blk_read_sector(uint64_t sector) +{ + struct blk_req *req; + + req = blk_alloc_req(sector); + req->aiocb.aio_cb = blk_read_completed; + + blkfront_aio_read(&req->aiocb); +} + +#ifdef BLKTEST_WRITE +static void blk_write_read_completed(struct blkfront_aiocb *aiocb, int ret) +{ + struct blk_req *req = aiocb->data; + int rand_value; + int i; + int *buf; + + if (ret) { + printk("got error code %d when reading back at offset %ld\n", ret, aiocb->aio_offset); + free(aiocb->aio_buf); + free(req); + return; + } + blk_size_read += blk_info.sector_size; + buf = (int*) aiocb->aio_buf; + rand_value = req->rand_value; + for (i = 0; i < blk_info.sector_size / sizeof(int); i++) { + if (buf[i] != rand_value) { + printk("bogus data at offset %ld\n", aiocb->aio_offset + i); + break; + } + rand_value *= RAND_MIX; + } + free(aiocb->aio_buf); + free(req); +} + +static void blk_write_completed(struct blkfront_aiocb *aiocb, int ret) +{ + struct blk_req *req = aiocb->data; + if (ret) { + printk("got error code %d when writing at offset %ld\n", ret, aiocb->aio_offset); + free(aiocb->aio_buf); + free(req); + return; + } + blk_size_write += blk_info.sector_size; + /* Push write check */ + req->next = blk_to_read; + blk_to_read = req; +} + +static void blk_write_sector(uint64_t sector) +{ + struct blk_req *req; + int rand_value; + int i; + int *buf; + + req = blk_alloc_req(sector); + req->aiocb.aio_cb = blk_write_completed; + req->rand_value = rand_value = rand(); + + buf = (int*) req->aiocb.aio_buf; + for (i = 0; i < blk_info.sector_size / sizeof(int); i++) { + buf[i] = rand_value; + rand_value *= RAND_MIX; + } + + blkfront_aio_write(&req->aiocb); +} +#endif + +static void blkfront_thread(void *p) +{ + time_t lasttime = 0; + + blk_dev = init_blkfront(NULL, &blk_info); + if (!blk_dev) + return; + + if (blk_info.info & VDISK_CDROM) + printk("Block device is a CDROM\n"); + if (blk_info.info & VDISK_REMOVABLE) + printk("Block device is removable\n"); + if (blk_info.info & VDISK_READONLY) + printk("Block device is read-only\n"); + +#ifdef BLKTEST_WRITE + if (blk_info.mode == O_RDWR) { + blk_write_sector(0); + blk_write_sector(blk_info.sectors-1); + } else +#endif + { + blk_read_sector(0); + blk_read_sector(blk_info.sectors-1); + } + + while (1) { + uint64_t sector = rand() % blk_info.sectors; + struct timeval tv; +#ifdef BLKTEST_WRITE + if (blk_info.mode == O_RDWR) + blk_write_sector(sector); + else +#endif + blk_read_sector(sector); + blkfront_aio_poll(blk_dev); + gettimeofday(&tv, NULL); + if (tv.tv_sec > lasttime + 10) { + printk("%llu read, %llu write\n", blk_size_read, blk_size_write); + lasttime = tv.tv_sec; + } + +#ifdef BLKTEST_WRITE + while (blk_to_read) { + struct blk_req *req = blk_to_read; + blk_to_read = blk_to_read->next; + req->aiocb.aio_cb = blk_write_read_completed; + blkfront_aio_read(&req->aiocb); + } +#endif + } +} + +#define WIDTH 800 +#define HEIGHT 600 +#define DEPTH 32 + +static uint32_t *fb; +static int refresh_period = 50; +static struct fbfront_dev *fb_dev; +static struct semaphore fbfront_sem = __SEMAPHORE_INITIALIZER(fbfront_sem, 0); + +static void fbfront_drawvert(int x, int y1, int y2, uint32_t color) +{ + int y; + if (x < 0) + return; + if (x >= WIDTH) + return; + if (y1 < 0) + y1 = 0; + if (y2 >= HEIGHT) + y2 = HEIGHT-1; + for (y = y1; y <= y2; y++) + fb[x + y*WIDTH] ^= color; +} + +static void fbfront_drawhoriz(int x1, int x2, int y, uint32_t color) +{ + int x; + if (y < 0) + return; + if (y >= HEIGHT) + return; + if (x1 < 0) + x1 = 0; + if (x2 >= WIDTH) + x2 = WIDTH-1; + for (x = x1; x <= x2; x++) + fb[x + y*WIDTH] ^= color; +} + +static void fbfront_thread(void *p) +{ + size_t line_length = WIDTH * (DEPTH / 8); + size_t memsize = HEIGHT * line_length; + unsigned long *mfns; + int i, n = (memsize + PAGE_SIZE-1) / PAGE_SIZE; + + memsize = n * PAGE_SIZE; + fb = _xmalloc(memsize, PAGE_SIZE); + memset(fb, 0, memsize); + mfns = xmalloc_array(unsigned long, n); + for (i = 0; i < n; i++) + mfns[i] = virtual_to_mfn((char *) fb + i * PAGE_SIZE); + fb_dev = init_fbfront(NULL, mfns, WIDTH, HEIGHT, DEPTH, line_length, n); + xfree(mfns); + if (!fb_dev) { + xfree(fb); + return; + } + up(&fbfront_sem); +} + +static void clip_cursor(int *x, int *y) +{ + if (*x < 0) + *x = 0; + if (*x >= WIDTH) + *x = WIDTH - 1; + if (*y < 0) + *y = 0; + if (*y >= HEIGHT) + *y = HEIGHT - 1; +} + +static void refresh_cursor(int new_x, int new_y) +{ + static int old_x = -1, old_y = -1; + + if (!refresh_period) + return; + + if (old_x != -1 && old_y != -1) { + fbfront_drawvert(old_x, old_y + 1, old_y + 8, 0xffffffff); + fbfront_drawhoriz(old_x + 1, old_x + 8, old_y, 0xffffffff); + fbfront_update(fb_dev, old_x, old_y, 9, 9); + } + old_x = new_x; + old_y = new_y; + fbfront_drawvert(new_x, new_y + 1, new_y + 8, 0xffffffff); + fbfront_drawhoriz(new_x + 1, new_x + 8, new_y, 0xffffffff); + fbfront_update(fb_dev, new_x, new_y, 9, 9); +} + +static struct kbdfront_dev *kbd_dev; +static void kbdfront_thread(void *p) +{ + DEFINE_WAIT(w); + int x = WIDTH / 2, y = HEIGHT / 2, z = 0; + + kbd_dev = init_kbdfront(NULL, 1); + if (!kbd_dev) + return; + + down(&fbfront_sem); + refresh_cursor(x, y); + while (1) { + union xenkbd_in_event kbdevent; + union xenfb_in_event fbevent; + int sleep = 1; + + add_waiter(w, kbdfront_queue); + add_waiter(w, fbfront_queue); + + while (kbdfront_receive(kbd_dev, &kbdevent, 1) != 0) { + sleep = 0; + switch(kbdevent.type) { + case XENKBD_TYPE_MOTION: + printk("motion x:%d y:%d z:%d\n", + kbdevent.motion.rel_x, + kbdevent.motion.rel_y, + kbdevent.motion.rel_z); + x += kbdevent.motion.rel_x; + y += kbdevent.motion.rel_y; + z += kbdevent.motion.rel_z; + clip_cursor(&x, &y); + refresh_cursor(x, y); + break; + case XENKBD_TYPE_POS: + printk("pos x:%d y:%d dz:%d\n", + kbdevent.pos.abs_x, + kbdevent.pos.abs_y, + kbdevent.pos.rel_z); + x = kbdevent.pos.abs_x; + y = kbdevent.pos.abs_y; + z = kbdevent.pos.rel_z; + clip_cursor(&x, &y); + refresh_cursor(x, y); + break; + case XENKBD_TYPE_KEY: + printk("key %d %s\n", + kbdevent.key.keycode, + kbdevent.key.pressed ? "pressed" : "released"); + if (kbdevent.key.keycode == BTN_LEFT) { + printk("mouse %s at (%d,%d,%d)\n", + kbdevent.key.pressed ? "clic" : "release", x, y, z); + if (kbdevent.key.pressed) { + uint32_t color = rand(); + fbfront_drawvert(x - 16, y - 16, y + 15, color); + fbfront_drawhoriz(x - 16, x + 15, y + 16, color); + fbfront_drawvert(x + 16, y - 15, y + 16, color); + fbfront_drawhoriz(x - 15, x + 16, y - 16, color); + fbfront_update(fb_dev, x - 16, y - 16, 33, 33); + } + } else if (kbdevent.key.keycode == KEY_Q) { + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_poweroff }; + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + do_exit(); + } + break; + } + } + while (fbfront_receive(fb_dev, &fbevent, 1) != 0) { + sleep = 0; + switch(fbevent.type) { + case XENFB_TYPE_REFRESH_PERIOD: + refresh_period = fbevent.refresh_period.period; + printk("refresh period %d\n", refresh_period); + refresh_cursor(x, y); + break; + } + } + if (sleep) + schedule(); + } +} + +static struct pcifront_dev *pci_dev; + +static void pcifront_thread(void *p) +{ + void print(unsigned int domain, unsigned int bus, unsigned int slot, unsigned int fun) + { + unsigned int vendor, device, rev, class; + + pcifront_conf_read(pci_dev, domain, bus, slot, fun, 0x00, 2, &vendor); + pcifront_conf_read(pci_dev, domain, bus, slot, fun, 0x02, 2, &device); + pcifront_conf_read(pci_dev, domain, bus, slot, fun, 0x08, 1, &rev); + pcifront_conf_read(pci_dev, domain, bus, slot, fun, 0x0a, 2, &class); + + printk("%04x:%02x:%02x.%02x %04x: %04x:%04x (rev %02x)\n", domain, bus, slot, fun, class, vendor, device, rev); + } + + pci_dev = init_pcifront(NULL); + if (!pci_dev) + return; + printk("PCI devices:\n"); + pcifront_scan(pci_dev, print); +} + +static void fs_thread(void *p) +{ + init_fs_frontend(); +} + +/* This should be overridden by the application we are linked against. */ +__attribute__((weak)) int app_main(start_info_t *si) +{ + printk("Dummy main: start_info=%p\n", si); + create_thread("xenbus_tester", xenbus_tester, si); + create_thread("periodic_thread", periodic_thread, si); + create_thread("netfront", netfront_thread, si); + create_thread("blkfront", blkfront_thread, si); + create_thread("fbfront", fbfront_thread, si); + create_thread("kbdfront", kbdfront_thread, si); + create_thread("pcifront", pcifront_thread, si); + create_thread("fs-frontend", fs_thread, si); + return 0; +} + +/* + * INITIAL C ENTRY POINT. + */ +void start_kernel(start_info_t *si) +{ + static char hello[] = "Bootstrapping...\n"; + + (void)HYPERVISOR_console_io(CONSOLEIO_write, strlen(hello), hello); + + arch_init(si); + + trap_init(); + + /* print out some useful information */ + printk("Xen Minimal OS!\n"); + printk("start_info: %p\n", si); + printk(" nr_pages: %lu", si->nr_pages); + printk(" shared_inf: %08lx\n", si->shared_info); + printk(" pt_base: %p", (void *)si->pt_base); + printk(" mod_start: 0x%lx\n", si->mod_start); + printk(" mod_len: %lu\n", si->mod_len); + printk(" flags: 0x%x\n", (unsigned int)si->flags); + printk(" cmd_line: %s\n", + si->cmd_line ? (const char *)si->cmd_line : "NULL"); + + /* Set up events. */ + init_events(); + + /* ENABLE EVENT DELIVERY. This is disabled at start of day. */ + __sti(); + + arch_print_info(); + + setup_xen_features(); + + /* Init memory management. */ + init_mm(); + + /* Init time and timers. */ + init_time(); + + /* Init the console driver. */ + init_console(); + + /* Init grant tables */ + init_gnttab(); + + /* Init scheduler. */ + init_sched(); + + /* Init XenBus */ + init_xenbus(); + + /* Call (possibly overridden) app_main() */ + app_main(&start_info); + + /* Everything initialised, start idle thread */ + run_idle_thread(); +} + +void stop_kernel(void) +{ + if (net_dev) + shutdown_netfront(net_dev); + + if (blk_dev) + shutdown_blkfront(blk_dev); + + if (fb_dev) + shutdown_fbfront(fb_dev); + + if (kbd_dev) + shutdown_kbdfront(kbd_dev); + + if (pci_dev) + shutdown_pcifront(pci_dev); + + /* TODO: fs import */ + + local_irq_disable(); + + /* Reset grant tables */ + fini_gnttab(); + + /* Reset the console driver. */ + fini_console(); + /* TODO: record new ring mfn & event in start_info */ + + /* Reset XenBus */ + fini_xenbus(); + + /* Reset timers */ + fini_time(); + + /* Reset memory management. */ + fini_mm(); + + /* Reset events. */ + fini_events(); + + /* Reset traps */ + trap_fini(); + + /* Reset arch details */ + arch_fini(); +} + +/* + * do_exit: This is called whenever an IRET fails in entry.S. + * This will generally be because an application has got itself into + * a really bad state (probably a bad CS or SS). It must be killed. + * Of course, minimal OS doesn't have applications :-) + */ + +void do_exit(void) +{ + printk("Do_exit called!\n"); + stack_walk(); + for( ;; ) + { + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_crash }; + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + } +} diff --git a/extras/mini-os/lib/ctype.c b/extras/mini-os/lib/ctype.c new file mode 100644 index 0000000..3f3bdb0 --- /dev/null +++ b/extras/mini-os/lib/ctype.c @@ -0,0 +1,29 @@ +#ifndef HAVE_LIBC +#include + +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 */ +#endif diff --git a/extras/mini-os/lib/math.c b/extras/mini-os/lib/math.c new file mode 100644 index 0000000..f1f7826 --- /dev/null +++ b/extras/mini-os/lib/math.c @@ -0,0 +1,427 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + **************************************************************************** + * + * File: math.c + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: + * + * Date: Aug 2003 + * + * Environment: Xen Minimal OS + * Description: Library functions for 64bit arith and other + * from freebsd, files in sys/libkern/ (qdivrem.c, etc) + * + **************************************************************************** + * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $ + **************************************************************************** + *- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $ +*/ + +#include +#include +#include + + /* On ia64 these functions lead to crashes. These are replaced by + * assembler functions. */ +#if !defined(__ia64__) + +/* + * Depending on the desired operation, we view a `long long' (aka quad_t) in + * one or more of the following formats. + */ +union uu { + s64 q; /* as a (signed) quad */ + s64 uq; /* as an unsigned quad */ + long sl[2]; /* as two signed longs */ + unsigned long ul[2]; /* as two unsigned longs */ +}; +/* XXX RN: Yuck hardcoded endianess :) */ +#define _QUAD_HIGHWORD 1 +#define _QUAD_LOWWORD 0 +/* + * Define high and low longwords. + */ +#define H _QUAD_HIGHWORD +#define L _QUAD_LOWWORD + +/* + * Total number of bits in a quad_t and in the pieces that make it up. + * These are used for shifting, and also below for halfword extraction + * and assembly. + */ +#ifndef HAVE_LIBC +#define CHAR_BIT 8 /* number of bits in a char */ +#endif +#define QUAD_BITS (sizeof(s64) * CHAR_BIT) +#define LONG_BITS (sizeof(long) * CHAR_BIT) +#define HALF_BITS (sizeof(long) * CHAR_BIT / 2) + +/* + * Extract high and low shortwords from longword, and move low shortword of + * longword to upper half of long, i.e., produce the upper longword of + * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.) + * + * These are used in the multiply code, to split a longword into upper + * and lower halves, and to reassemble a product as a quad_t, shifted left + * (sizeof(long)*CHAR_BIT/2). + */ +#define HHALF(x) ((x) >> HALF_BITS) +#define LHALF(x) ((x) & ((1UL << HALF_BITS) - 1)) +#define LHUP(x) ((x) << HALF_BITS) + +/* + * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed), + * section 4.3.1, pp. 257--259. + */ +#define B (1UL << HALF_BITS) /* digit base */ + +/* Combine two `digits' to make a single two-digit number. */ +#define COMBINE(a, b) (((u_long)(a) << HALF_BITS) | (b)) + +/* select a type for digits in base B: use unsigned short if they fit */ +#if ULONG_MAX == 0xffffffff && USHRT_MAX >= 0xffff +typedef unsigned short digit; +#else +typedef u_long digit; +#endif + + +/* + * Shift p[0]..p[len] left `sh' bits, ignoring any bits that + * `fall out' the left (there never will be any such anyway). + * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS. + */ +static void +shl(register digit *p, register int len, register int sh) +{ + register int i; + + for (i = 0; i < len; i++) + p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh)); + p[i] = LHALF(p[i] << sh); +} + +/* + * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v. + * + * We do this in base 2-sup-HALF_BITS, so that all intermediate products + * fit within u_long. As a consequence, the maximum length dividend and + * divisor are 4 `digits' in this base (they are shorter if they have + * leading zeros). + */ +u64 +__qdivrem(u64 uq, u64 vq, u64 *arq) +{ + union uu tmp; + digit *u, *v, *q; + register digit v1, v2; + u_long qhat, rhat, t; + int m, n, d, j, i; + digit uspace[5], vspace[5], qspace[5]; + + /* + * Take care of special cases: divide by zero, and u < v. + */ + if (vq == 0) { + /* divide by zero. */ + static volatile const unsigned int zero = 0; + + tmp.ul[H] = tmp.ul[L] = 1 / zero; + if (arq) + *arq = uq; + return (tmp.q); + } + if (uq < vq) { + if (arq) + *arq = uq; + return (0); + } + u = &uspace[0]; + v = &vspace[0]; + q = &qspace[0]; + + /* + * Break dividend and divisor into digits in base B, then + * count leading zeros to determine m and n. When done, we + * will have: + * u = (u[1]u[2]...u[m+n]) sub B + * v = (v[1]v[2]...v[n]) sub B + * v[1] != 0 + * 1 < n <= 4 (if n = 1, we use a different division algorithm) + * m >= 0 (otherwise u < v, which we already checked) + * m + n = 4 + * and thus + * m = 4 - n <= 2 + */ + tmp.uq = uq; + u[0] = 0; + u[1] = HHALF(tmp.ul[H]); + u[2] = LHALF(tmp.ul[H]); + u[3] = HHALF(tmp.ul[L]); + u[4] = LHALF(tmp.ul[L]); + tmp.uq = vq; + v[1] = HHALF(tmp.ul[H]); + v[2] = LHALF(tmp.ul[H]); + v[3] = HHALF(tmp.ul[L]); + v[4] = LHALF(tmp.ul[L]); + for (n = 4; v[1] == 0; v++) { + if (--n == 1) { + u_long rbj; /* r*B+u[j] (not root boy jim) */ + digit q1, q2, q3, q4; + + /* + * Change of plan, per exercise 16. + * r = 0; + * for j = 1..4: + * q[j] = floor((r*B + u[j]) / v), + * r = (r*B + u[j]) % v; + * We unroll this completely here. + */ + t = v[2]; /* nonzero, by definition */ + q1 = u[1] / t; + rbj = COMBINE(u[1] % t, u[2]); + q2 = rbj / t; + rbj = COMBINE(rbj % t, u[3]); + q3 = rbj / t; + rbj = COMBINE(rbj % t, u[4]); + q4 = rbj / t; + if (arq) + *arq = rbj % t; + tmp.ul[H] = COMBINE(q1, q2); + tmp.ul[L] = COMBINE(q3, q4); + return (tmp.q); + } + } + + /* + * By adjusting q once we determine m, we can guarantee that + * there is a complete four-digit quotient at &qspace[1] when + * we finally stop. + */ + for (m = 4 - n; u[1] == 0; u++) + m--; + for (i = 4 - m; --i >= 0;) + q[i] = 0; + q += 4 - m; + + /* + * Here we run Program D, translated from MIX to C and acquiring + * a few minor changes. + * + * D1: choose multiplier 1 << d to ensure v[1] >= B/2. + */ + d = 0; + for (t = v[1]; t < B / 2; t <<= 1) + d++; + if (d > 0) { + shl(&u[0], m + n, d); /* u <<= d */ + shl(&v[1], n - 1, d); /* v <<= d */ + } + /* + * D2: j = 0. + */ + j = 0; + v1 = v[1]; /* for D3 -- note that v[1..n] are constant */ + v2 = v[2]; /* for D3 */ + do { + register digit uj0, uj1, uj2; + + /* + * D3: Calculate qhat (\^q, in TeX notation). + * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and + * let rhat = (u[j]*B + u[j+1]) mod v[1]. + * While rhat < B and v[2]*qhat > rhat*B+u[j+2], + * decrement qhat and increase rhat correspondingly. + * Note that if rhat >= B, v[2]*qhat < rhat*B. + */ + uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */ + uj1 = u[j + 1]; /* for D3 only */ + uj2 = u[j + 2]; /* for D3 only */ + if (uj0 == v1) { + qhat = B; + rhat = uj1; + goto qhat_too_big; + } else { + u_long nn = COMBINE(uj0, uj1); + qhat = nn / v1; + rhat = nn % v1; + } + while (v2 * qhat > COMBINE(rhat, uj2)) { + qhat_too_big: + qhat--; + if ((rhat += v1) >= B) + break; + } + /* + * D4: Multiply and subtract. + * The variable `t' holds any borrows across the loop. + * We split this up so that we do not require v[0] = 0, + * and to eliminate a final special case. + */ + for (t = 0, i = n; i > 0; i--) { + t = u[i + j] - v[i] * qhat - t; + u[i + j] = LHALF(t); + t = (B - HHALF(t)) & (B - 1); + } + t = u[j] - t; + u[j] = LHALF(t); + /* + * D5: test remainder. + * There is a borrow if and only if HHALF(t) is nonzero; + * in that (rare) case, qhat was too large (by exactly 1). + * Fix it by adding v[1..n] to u[j..j+n]. + */ + if (HHALF(t)) { + qhat--; + for (t = 0, i = n; i > 0; i--) { /* D6: add back. */ + t += u[i + j] + v[i]; + u[i + j] = LHALF(t); + t = HHALF(t); + } + u[j] = LHALF(u[j] + t); + } + q[j] = qhat; + } while (++j <= m); /* D7: loop on j. */ + + /* + * If caller wants the remainder, we have to calculate it as + * u[m..m+n] >> d (this is at most n digits and thus fits in + * u[m+1..m+n], but we may need more source digits). + */ + if (arq) { + if (d) { + for (i = m + n; i > m; --i) + u[i] = (u[i] >> d) | + LHALF(u[i - 1] << (HALF_BITS - d)); + u[i] = 0; + } + tmp.ul[H] = COMBINE(uspace[1], uspace[2]); + tmp.ul[L] = COMBINE(uspace[3], uspace[4]); + *arq = tmp.q; + } + + tmp.ul[H] = COMBINE(qspace[1], qspace[2]); + tmp.ul[L] = COMBINE(qspace[3], qspace[4]); + return (tmp.q); +} + + +/* + * Divide two signed quads. + * ??? if -1/2 should produce -1 on this machine, this code is wrong + */ +s64 +__divdi3(s64 a, s64 b) +{ + u64 ua, ub, uq; + int neg; + + if (a < 0) + ua = -(u64)a, neg = 1; + else + ua = a, neg = 0; + if (b < 0) + ub = -(u64)b, neg ^= 1; + else + ub = b; + uq = __qdivrem(ua, ub, (u64 *)0); + return (neg ? -uq : uq); +} + +/* + * Divide two unsigned quads. + */ +u64 +__udivdi3(u64 a, u64 b) +{ + return (__qdivrem(a, b, (u64 *)0)); +} + + +/* + * Return remainder after dividing two unsigned quads. + */ +u_quad_t +__umoddi3(u_quad_t a, u_quad_t b) +{ + u_quad_t r; + + (void)__qdivrem(a, b, &r); + return (r); +} + +/* + * Return remainder after dividing two signed quads. + * + * XXX + * If -1/2 should produce -1 on this machine, this code is wrong. + */ +quad_t +__moddi3(quad_t a, quad_t b) +{ + u_quad_t ua, ub, ur; + int neg; + + if (a < 0) + ua = -(u_quad_t)a, neg = 1; + else + ua = a, neg = 0; + if (b < 0) + ub = -(u_quad_t)b; + else + ub = b; + (void)__qdivrem(ua, ub, &ur); + return (neg ? -ur : ur); +} +#endif /* !defined(__ia64__) */ + +#ifndef HAVE_LIBC +/* Should be random enough for our uses */ +int rand(void) +{ + static unsigned int previous; + struct timeval tv; + gettimeofday(&tv, NULL); + previous += tv.tv_sec + tv.tv_usec; + previous *= RAND_MIX; + return previous; +} +#endif diff --git a/extras/mini-os/lib/printf.c b/extras/mini-os/lib/printf.c new file mode 100644 index 0000000..a986616 --- /dev/null +++ b/extras/mini-os/lib/printf.c @@ -0,0 +1,793 @@ +/* + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + **************************************************************************** + * + * File: printf.c + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * + * Date: Aug 2003, Aug 2005 + * + * Environment: Xen Minimal OS + * Description: Library functions for printing + * (freebsd port, mainly sys/subr_prf.c) + * + **************************************************************************** + * + *- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $ + */ + +#if !defined HAVE_LIBC + +#include +#include +#include +#include +#include +#include +#include + +/** + * simple_strtoul - convert a string to an unsigned long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +/** + * simple_strtol - convert a string to a signed long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} + +/** + * simple_strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +/** + * simple_strtoll - convert a string to a signed long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long long simple_strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoull(cp+1,endp,base); + return simple_strtoull(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * buf, char * end, long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits; + const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return buf; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else + { + /* XXX KAF: force unsigned mod and div. */ + unsigned long long num2=(unsigned long long)num; + unsigned int base2=(unsigned int)base; + while (num2 != 0) { tmp[i++] = digits[num2%base2]; num2 /= base2; } + } + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) { + while(size-->0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + } + if (sign) { + if (buf <= end) + *buf = sign; + ++buf; + } + if (type & SPECIAL) { + if (base==8) { + if (buf <= end) + *buf = '0'; + ++buf; + } else if (base==16) { + if (buf <= end) + *buf = '0'; + ++buf; + if (buf <= end) + *buf = digits[33]; + ++buf; + } + } + if (!(type & LEFT)) { + while (size-- > 0) { + if (buf <= end) + *buf = c; + ++buf; + } + } + while (i < precision--) { + if (buf <= end) + *buf = '0'; + ++buf; + } + while (i-- > 0) { + if (buf <= end) + *buf = tmp[i]; + ++buf; + } + while (size-- > 0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + return buf; +} + +/** +* vsnprintf - Format a string and place it in a buffer +* @buf: The buffer to place the result into +* @size: The size of the buffer, including the trailing null space +* @fmt: The format string to use +* @args: Arguments for the format string +* +* Call this function if you are already dealing with a va_list. +* You probably want snprintf instead. + */ +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char *str, *end, c; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + + str = buf; + end = buf + size - 1; + + if (end < buf - 1) { + end = ((void *) -1); + size = end - buf + 1; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str <= end) + *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + if (*fmt == 'q') { + qualifier = 'L'; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) { + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str <= end) + *str = c; + ++str; + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + } + for (i = 0; i < len; ++i) { + if (str <= end) + *str = *s; + ++str; ++s; + } + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, end, + (unsigned long) va_arg(args, void *), + 16, field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str <= end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str <= end) + *str = '%'; + ++str; + if (*fmt) { + if (str <= end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z') { + num = va_arg(args, size_t); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + + str = number(str, end, num, base, + field_width, precision, flags); + } + if (str <= end) + *str = '\0'; + else if (size > 0) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + +/** + * snprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + */ +int snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} + +/** + * vsprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @args: Arguments for the format string + * + * Call this function if you are already dealing with a va_list. + * You probably want sprintf instead. + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return vsnprintf(buf, 0xFFFFFFFFUL, fmt, args); +} + + +/** + * sprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + */ +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + return i; +} + +/** + * vsscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: format of buffer + * @args: arguments + */ +int vsscanf(const char * buf, const char * fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + int qualifier; + int base; + int field_width; + int is_sign = 0; + + while(*fmt && *str) { + /* skip any white space in format */ + /* white space in format matchs any amount of + * white space, including none, in the input. + */ + if (isspace(*fmt)) { + while (isspace(*fmt)) + ++fmt; + while (isspace(*str)) + ++str; + } + + /* anything that is not a conversion must match exactly */ + if (*fmt != '%' && *fmt) { + if (*fmt++ != *str++) + break; + continue; + } + + if (!*fmt) + break; + ++fmt; + + /* skip this conversion. + * advance both strings to next white space + */ + if (*fmt == '*') { + while (!isspace(*fmt) && *fmt) + fmt++; + while (!isspace(*str) && *str) + str++; + continue; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + + /* get conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z') { + qualifier = *fmt++; + if (unlikely(qualifier == *fmt)) { + if (qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if (qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + base = 10; + is_sign = 0; + + if (!*fmt || !*str) + break; + + switch(*fmt++) { + case 'c': + { + char *s = (char *) va_arg(args,char*); + if (field_width == -1) + field_width = 1; + do { + *s++ = *str++; + } while (--field_width > 0 && *str); + num++; + } + continue; + case 's': + { + char *s = (char *) va_arg(args, char *); + if(field_width == -1) + field_width = INT_MAX; + /* first, skip leading white space in buffer */ + while (isspace(*str)) + str++; + + /* now copy until next white space */ + while (*str && !isspace(*str) && field_width--) { + *s++ = *str++; + } + *s = '\0'; + num++; + } + continue; + case 'n': + /* return number of characters read so far */ + { + int *i = (int *)va_arg(args,int*); + *i = str - buf; + } + continue; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + case 'i': + base = 0; + case 'd': + is_sign = 1; + case 'u': + break; + case '%': + /* looking for '%' in str */ + if (*str++ != '%') + return num; + continue; + default: + /* invalid format; stop here */ + return num; + } + + /* have some sort of integer conversion. + * first, skip white space in buffer. + */ + while (isspace(*str)) + str++; + + digit = *str; + if (is_sign && digit == '-') + digit = *(str + 1); + + if (!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) + break; + + switch(qualifier) { + case 'H': /* that's 'hh' in format */ + if (is_sign) { + signed char *s = (signed char *) va_arg(args,signed char *); + *s = (signed char) simple_strtol(str,&next,base); + } else { + unsigned char *s = (unsigned char *) va_arg(args, unsigned char *); + *s = (unsigned char) simple_strtoul(str, &next, base); + } + break; + case 'h': + if (is_sign) { + short *s = (short *) va_arg(args,short *); + *s = (short) simple_strtol(str,&next,base); + } else { + unsigned short *s = (unsigned short *) va_arg(args, unsigned short *); + *s = (unsigned short) simple_strtoul(str, &next, base); + } + break; + case 'l': + if (is_sign) { + long *l = (long *) va_arg(args,long *); + *l = simple_strtol(str,&next,base); + } else { + unsigned long *l = (unsigned long*) va_arg(args,unsigned long*); + *l = simple_strtoul(str,&next,base); + } + break; + case 'L': + if (is_sign) { + long long *l = (long long*) va_arg(args,long long *); + *l = simple_strtoll(str,&next,base); + } else { + unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*); + *l = simple_strtoull(str,&next,base); + } + break; + case 'Z': + case 'z': + { + size_t *s = (size_t*) va_arg(args,size_t*); + *s = (size_t) simple_strtoul(str,&next,base); + } + break; + default: + if (is_sign) { + int *i = (int *) va_arg(args, int*); + *i = (int) simple_strtol(str,&next,base); + } else { + unsigned int *i = (unsigned int*) va_arg(args, unsigned int*); + *i = (unsigned int) simple_strtoul(str,&next,base); + } + break; + } + num++; + + if (!next) + break; + str = next; + } + return num; +} + +/** + * sscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: formatting of buffer + * @...: resulting arguments + */ +int sscanf(const char * buf, const char * fmt, ...) +{ + va_list args; + int i; + + va_start(args,fmt); + i = vsscanf(buf,fmt,args); + va_end(args); + return i; +} + +#endif diff --git a/extras/mini-os/lib/string.c b/extras/mini-os/lib/string.c new file mode 100644 index 0000000..f6dfa60 --- /dev/null +++ b/extras/mini-os/lib/string.c @@ -0,0 +1,178 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + **************************************************************************** + * + * File: string.c + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: + * + * Date: Aug 2003 + * + * Environment: Xen Minimal OS + * Description: Library function for string and memory manipulation + * Origin unknown + * + **************************************************************************** + * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $ + **************************************************************************** + */ + +#if !defined HAVE_LIBC + +#include +#include +#include +#include + +int memcmp(const void * cs,const void * ct,size_t count) +{ + const unsigned char *su1, *su2; + signed char res = 0; + + for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} + +void * memcpy(void * dest,const void *src,size_t count) +{ + char *tmp = (char *) dest; + const char *s = src; + + while (count--) + *tmp++ = *s++; + + return dest; +} + +int strncmp(const char * cs,const char * ct,size_t count) +{ + register signed char __res = 0; + + while (count) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + + return __res; +} + +int strcmp(const char * cs,const char * ct) +{ + register signed char __res; + + while (1) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + } + + return __res; +} + +char * strcpy(char * dest,const char *src) +{ + char *tmp = dest; + + while ((*dest++ = *src++) != '\0') + /* nothing */; + return tmp; +} + +char * strncpy(char * dest,const char *src,size_t count) +{ + char *tmp = dest; + + while (count-- && (*dest++ = *src++) != '\0') + /* nothing */; + + return tmp; +} + +void * memset(void * s,int c,size_t count) +{ + char *xs = (char *) s; + + while (count--) + *xs++ = c; + + return s; +} + +size_t strnlen(const char * s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + + +char * strcat(char * dest, const char * src) +{ + char *tmp = dest; + + while (*dest) + dest++; + + while ((*dest++ = *src++) != '\0'); + + return tmp; +} + +size_t strlen(const char * s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +char * strchr(const char * s, int c) +{ + for(; *s != (char) c; ++s) + if (*s == '\0') + return NULL; + return (char *)s; +} + +char * strrchr(const char * s, int c) +{ + const char *res = NULL; + for(; *s != '\0'; ++s) + if (*s == (char) c) + res = s; + return (char *)res; +} + +char * strstr(const char * s1,const char * s2) +{ + int l1, l2; + + l2 = strlen(s2); + if (!l2) + return (char *) s1; + l1 = strlen(s1); + while (l1 >= l2) { + l1--; + if (!memcmp(s1,s2,l2)) + return (char *) s1; + s1++; + } + return NULL; +} + +char *strdup(const char *x) +{ + int l = strlen(x); + char *res = malloc(l + 1); + if (!res) return NULL; + memcpy(res, x, l + 1); + return res; +} + +#endif diff --git a/extras/mini-os/lib/sys.c b/extras/mini-os/lib/sys.c new file mode 100644 index 0000000..34e4fb6 --- /dev/null +++ b/extras/mini-os/lib/sys.c @@ -0,0 +1,1335 @@ +/* + * POSIX-compatible libc layer + * + * Samuel Thibault , October 2007 + * + * Provides the UNIXish part of the standard libc function. + * + * Relatively straight-forward: just multiplex the file descriptor operations + * among the various file types (console, FS, network, ...) + */ + +//#define LIBC_VERBOSE +//#define LIBC_DEBUG + +#ifdef LIBC_DEBUG +#define DEBUG(fmt,...) printk(fmt, ##__VA_ARGS__) +#else +#define DEBUG(fmt,...) +#endif + +#ifdef HAVE_LIBC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LWIP +#include +#endif +#include + +#define debug(fmt, ...) \ + +#define print_unsupported(fmt, ...) \ + printk("Unsupported function "fmt" called in Mini-OS kernel\n", ## __VA_ARGS__); + +/* Crash on function call */ +#define unsupported_function_crash(function) \ + int __unsup_##function(void) asm(#function); \ + int __unsup_##function(void) \ + { \ + print_unsupported(#function); \ + do_exit(); \ + } + +/* Log and err out on function call */ +#define unsupported_function_log(type, function, ret) \ + type __unsup_##function(void) asm(#function); \ + type __unsup_##function(void) \ + { \ + print_unsupported(#function); \ + errno = ENOSYS; \ + return ret; \ + } + +/* Err out on function call */ +#define unsupported_function(type, function, ret) \ + type __unsup_##function(void) asm(#function); \ + type __unsup_##function(void) \ + { \ + errno = ENOSYS; \ + return ret; \ + } + +#define NOFILE 32 +extern int xc_evtchn_close(int fd); +extern int xc_interface_close(int fd); +extern int xc_gnttab_close(int fd); + +pthread_mutex_t fd_lock = PTHREAD_MUTEX_INITIALIZER; +struct file files[NOFILE] = { + { .type = FTYPE_CONSOLE }, /* stdin */ + { .type = FTYPE_CONSOLE }, /* stdout */ + { .type = FTYPE_CONSOLE }, /* stderr */ +}; + +DECLARE_WAIT_QUEUE_HEAD(event_queue); + +int alloc_fd(enum fd_type type) +{ + int i; + pthread_mutex_lock(&fd_lock); + for (i=0; i 0; i--) + if (files[i].type != FTYPE_NONE) + close(i); + pthread_mutex_unlock(&fd_lock); +} + +int dup2(int oldfd, int newfd) +{ + pthread_mutex_lock(&fd_lock); + if (files[newfd].type != FTYPE_NONE) + close(newfd); + // XXX: this is a bit bogus, as we are supposed to share the offset etc + files[newfd] = files[oldfd]; + pthread_mutex_unlock(&fd_lock); + return 0; +} + +pid_t getpid(void) +{ + return 1; +} + +pid_t getppid(void) +{ + return 1; +} + +pid_t setsid(void) +{ + return 1; +} + +char *getcwd(char *buf, size_t size) +{ + snprintf(buf, size, "/"); + return buf; +} + +#define LOG_PATH "/var/log/" + +int mkdir(const char *pathname, mode_t mode) +{ + int ret; + ret = fs_create(fs_import, (char *) pathname, 1, mode); + if (ret < 0) { + errno = EIO; + return -1; + } + return 0; +} + +int open(const char *pathname, int flags, ...) +{ + int fs_fd, fd; + /* Ugly, but fine. */ + if (!strncmp(pathname,LOG_PATH,strlen(LOG_PATH))) { + fd = alloc_fd(FTYPE_CONSOLE); + printk("open(%s) -> %d\n", pathname, fd); + return fd; + } + printk("open(%s, %x)", pathname, flags); + switch (flags & ~O_ACCMODE) { + case 0: + fs_fd = fs_open(fs_import, (void *) pathname); + break; + case O_CREAT|O_TRUNC: + { + va_list ap; + mode_t mode; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + fs_fd = fs_create(fs_import, (void *) pathname, 0, mode); + break; + } + default: + printk(" unsupported flags\n"); + do_exit(); + } + if (fs_fd < 0) { + errno = EIO; + return -1; + } + fd = alloc_fd(FTYPE_FILE); + printk("-> %d\n", fd); + files[fd].file.fd = fs_fd; + files[fd].file.offset = 0; + return fd; +} + +int isatty(int fd) +{ + return files[fd].type == FTYPE_CONSOLE; +} + +int read(int fd, void *buf, size_t nbytes) +{ + switch (files[fd].type) { + case FTYPE_CONSOLE: { + int ret; + DEFINE_WAIT(w); + while(1) { + add_waiter(w, console_queue); + ret = xencons_ring_recv(buf, nbytes); + if (ret) + break; + schedule(); + } + remove_waiter(w); + return ret; + } + case FTYPE_FILE: { + ssize_t ret; + if (nbytes > PAGE_SIZE * FSIF_NR_READ_GNTS) + nbytes = PAGE_SIZE * FSIF_NR_READ_GNTS; + ret = fs_read(fs_import, files[fd].file.fd, buf, nbytes, files[fd].file.offset); + if (ret > 0) { + files[fd].file.offset += ret; + return ret; + } else if (ret < 0) { + errno = EIO; + return -1; + } + return 0; + } +#ifdef HAVE_LWIP + case FTYPE_SOCKET: + return lwip_read(files[fd].socket.fd, buf, nbytes); +#endif + case FTYPE_TAP: { + ssize_t ret; + ret = netfront_receive(files[fd].tap.dev, buf, nbytes); + if (ret <= 0) { + errno = EAGAIN; + return -1; + } + return ret; + } + case FTYPE_KBD: { + int ret, n; + n = nbytes / sizeof(union xenkbd_in_event); + ret = kbdfront_receive(files[fd].kbd.dev, buf, n); + if (ret <= 0) { + errno = EAGAIN; + return -1; + } + return ret * sizeof(union xenkbd_in_event); + } + case FTYPE_FB: { + int ret, n; + n = nbytes / sizeof(union xenfb_in_event); + ret = fbfront_receive(files[fd].fb.dev, buf, n); + if (ret <= 0) { + errno = EAGAIN; + return -1; + } + return ret * sizeof(union xenfb_in_event); + } + default: + break; + } + printk("read(%d): Bad descriptor\n", fd); + errno = EBADF; + return -1; +} + +int write(int fd, const void *buf, size_t nbytes) +{ + switch (files[fd].type) { + case FTYPE_CONSOLE: + console_print((char *)buf, nbytes); + return nbytes; + case FTYPE_FILE: { + ssize_t ret; + if (nbytes > PAGE_SIZE * FSIF_NR_WRITE_GNTS) + nbytes = PAGE_SIZE * FSIF_NR_WRITE_GNTS; + ret = fs_write(fs_import, files[fd].file.fd, (void *) buf, nbytes, files[fd].file.offset); + if (ret > 0) { + files[fd].file.offset += ret; + return ret; + } else if (ret < 0) { + errno = EIO; + return -1; + } + return 0; + } +#ifdef HAVE_LWIP + case FTYPE_SOCKET: + return lwip_write(files[fd].socket.fd, (void*) buf, nbytes); +#endif + case FTYPE_TAP: + netfront_xmit(files[fd].tap.dev, (void*) buf, nbytes); + return nbytes; + default: + break; + } + printk("write(%d): Bad descriptor\n", fd); + errno = EBADF; + return -1; +} + +off_t lseek(int fd, off_t offset, int whence) +{ + if (files[fd].type != FTYPE_FILE) { + errno = ESPIPE; + return (off_t) -1; + } + switch (whence) { + case SEEK_SET: + files[fd].file.offset = offset; + break; + case SEEK_CUR: + files[fd].file.offset += offset; + break; + case SEEK_END: { + struct stat st; + int ret; + ret = fstat(fd, &st); + if (ret) + return -1; + files[fd].file.offset = st.st_size + offset; + break; + } + default: + errno = EINVAL; + return -1; + } + return files[fd].file.offset; +} + +int fsync(int fd) { + switch (files[fd].type) { + case FTYPE_FILE: { + int ret; + ret = fs_sync(fs_import, files[fd].file.fd); + if (ret < 0) { + errno = EIO; + return -1; + } + return 0; + } + default: + break; + } + printk("fsync(%d): Bad descriptor\n", fd); + errno = EBADF; + return -1; +} + +int close(int fd) +{ + printk("close(%d)\n", fd); + switch (files[fd].type) { + default: + files[fd].type = FTYPE_NONE; + return 0; + case FTYPE_FILE: { + int ret = fs_close(fs_import, files[fd].file.fd); + files[fd].type = FTYPE_NONE; + if (ret < 0) { + errno = EIO; + return -1; + } + return 0; + } + case FTYPE_XENBUS: + xs_daemon_close((void*)(intptr_t) fd); + return 0; +#ifdef HAVE_LWIP + case FTYPE_SOCKET: { + int res = lwip_close(files[fd].socket.fd); + files[fd].type = FTYPE_NONE; + return res; + } +#endif + case FTYPE_XC: + xc_interface_close(fd); + return 0; + case FTYPE_EVTCHN: + xc_evtchn_close(fd); + return 0; + case FTYPE_GNTMAP: + xc_gnttab_close(fd); + return 0; + case FTYPE_TAP: + shutdown_netfront(files[fd].tap.dev); + files[fd].type = FTYPE_NONE; + return 0; + case FTYPE_BLK: + shutdown_blkfront(files[fd].blk.dev); + files[fd].type = FTYPE_NONE; + return 0; + case FTYPE_KBD: + shutdown_kbdfront(files[fd].kbd.dev); + files[fd].type = FTYPE_NONE; + return 0; + case FTYPE_FB: + shutdown_fbfront(files[fd].fb.dev); + files[fd].type = FTYPE_NONE; + return 0; + case FTYPE_NONE: + break; + } + printk("close(%d): Bad descriptor\n", fd); + errno = EBADF; + return -1; +} + +static void init_stat(struct stat *buf) +{ + memset(buf, 0, sizeof(*buf)); + buf->st_dev = 0; + buf->st_ino = 0; + buf->st_nlink = 1; + buf->st_rdev = 0; + buf->st_blksize = 4096; + buf->st_blocks = 0; +} + +static void stat_from_fs(struct stat *buf, struct fsif_stat_response *stat) +{ + buf->st_mode = stat->stat_mode; + buf->st_uid = stat->stat_uid; + buf->st_gid = stat->stat_gid; + buf->st_size = stat->stat_size; + buf->st_atime = stat->stat_atime; + buf->st_mtime = stat->stat_mtime; + buf->st_ctime = stat->stat_ctime; +} + +int stat(const char *path, struct stat *buf) +{ + struct fsif_stat_response stat; + int ret; + int fs_fd; + printk("stat(%s)\n", path); + fs_fd = fs_open(fs_import, (char*) path); + if (fs_fd < 0) { + errno = EIO; + ret = -1; + goto out; + } + ret = fs_stat(fs_import, fs_fd, &stat); + if (ret < 0) { + errno = EIO; + ret = -1; + goto outfd; + } + init_stat(buf); + stat_from_fs(buf, &stat); + ret = 0; + +outfd: + fs_close(fs_import, fs_fd); +out: + return ret; +} + +int fstat(int fd, struct stat *buf) +{ + init_stat(buf); + switch (files[fd].type) { + case FTYPE_CONSOLE: + case FTYPE_SOCKET: { + buf->st_mode = (files[fd].type == FTYPE_CONSOLE?S_IFCHR:S_IFSOCK) | S_IRUSR|S_IWUSR; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = 0; + buf->st_atime = + buf->st_mtime = + buf->st_ctime = time(NULL); + return 0; + } + case FTYPE_FILE: { + struct fsif_stat_response stat; + int ret; + ret = fs_stat(fs_import, files[fd].file.fd, &stat); + if (ret < 0) { + errno = EIO; + return -1; + } + /* The protocol is a bit evasive about this value */ + stat_from_fs(buf, &stat); + return 0; + } + default: + break; + } + + printk("statf(%d): Bad descriptor\n", fd); + errno = EBADF; + return -1; +} + +int ftruncate(int fd, off_t length) +{ + switch (files[fd].type) { + case FTYPE_FILE: { + int ret; + ret = fs_truncate(fs_import, files[fd].file.fd, length); + if (ret < 0) { + errno = EIO; + return -1; + } + return 0; + } + default: + break; + } + + printk("ftruncate(%d): Bad descriptor\n", fd); + errno = EBADF; + return -1; +} + +int remove(const char *pathname) +{ + int ret; + printk("remove(%s)", pathname); + ret = fs_remove(fs_import, (char*) pathname); + if (ret < 0) { + errno = EIO; + return -1; + } + return 0; +} + +int unlink(const char *pathname) +{ + return remove(pathname); +} + +int rmdir(const char *pathname) +{ + return remove(pathname); +} + +int fcntl(int fd, int cmd, ...) +{ + long arg; + va_list ap; + va_start(ap, cmd); + arg = va_arg(ap, long); + va_end(ap); + + switch (cmd) { +#ifdef HAVE_LWIP + case F_SETFL: + if (files[fd].type == FTYPE_SOCKET && !(arg & ~O_NONBLOCK)) { + /* Only flag supported: non-blocking mode */ + uint32_t nblock = !!(arg & O_NONBLOCK); + return lwip_ioctl(files[fd].socket.fd, FIONBIO, &nblock); + } + /* Fallthrough */ +#endif + default: + printk("fcntl(%d, %d, %lx/%lo)\n", fd, cmd, arg, arg); + errno = ENOSYS; + return -1; + } +} + +DIR *opendir(const char *name) +{ + DIR *ret; + ret = malloc(sizeof(*ret)); + ret->name = strdup(name); + ret->offset = 0; + ret->entries = NULL; + ret->curentry = -1; + ret->nbentries = 0; + ret->has_more = 1; + return ret; +} + +struct dirent *readdir(DIR *dir) +{ + if (dir->curentry >= 0) { + free(dir->entries[dir->curentry]); + dir->entries[dir->curentry] = NULL; + } + dir->curentry++; + if (dir->curentry >= dir->nbentries) { + dir->offset += dir->nbentries; + free(dir->entries); + dir->curentry = -1; + dir->nbentries = 0; + if (!dir->has_more) + return NULL; + dir->entries = fs_list(fs_import, dir->name, dir->offset, &dir->nbentries, &dir->has_more); + if (!dir->entries || !dir->nbentries) + return NULL; + dir->curentry = 0; + } + dir->dirent.d_name = dir->entries[dir->curentry]; + return &dir->dirent; +} +int closedir(DIR *dir) +{ + int i; + for (i=0; inbentries; i++) + free(dir->entries[i]); + free(dir->entries); + free(dir->name); + free(dir); + return 0; +} + +/* We assume that only the main thread calls select(). */ + +static const char file_types[] = { + [FTYPE_NONE] = 'N', + [FTYPE_CONSOLE] = 'C', + [FTYPE_FILE] = 'F', + [FTYPE_XENBUS] = 'S', + [FTYPE_XC] = 'X', + [FTYPE_EVTCHN] = 'E', + [FTYPE_SOCKET] = 's', + [FTYPE_TAP] = 'T', + [FTYPE_BLK] = 'B', + [FTYPE_KBD] = 'K', + [FTYPE_FB] = 'G', +}; +#ifdef LIBC_DEBUG +static void dump_set(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + int i, comma; +#define printfds(set) do {\ + comma = 0; \ + for (i = 0; i < nfds; i++) { \ + if (FD_ISSET(i, set)) { \ + if (comma) \ + printk(", "); \ + printk("%d(%c)", i, file_types[files[i].type]); \ + comma = 1; \ + } \ + } \ +} while (0) + + printk("["); + if (readfds) + printfds(readfds); + printk("], ["); + if (writefds) + printfds(writefds); + printk("], ["); + if (exceptfds) + printfds(exceptfds); + printk("], "); + if (timeout) + printk("{ %ld, %ld }", timeout->tv_sec, timeout->tv_usec); +} +#else +#define dump_set(nfds, readfds, writefds, exceptfds, timeout) +#endif + +/* Just poll without blocking */ +static int select_poll(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) +{ + int i, n = 0; +#ifdef HAVE_LWIP + int sock_n, sock_nfds = 0; + fd_set sock_readfds, sock_writefds, sock_exceptfds; + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0}; +#endif + +#ifdef LIBC_VERBOSE + static int nb; + static int nbread[NOFILE], nbwrite[NOFILE], nbexcept[NOFILE]; + static s_time_t lastshown; + + nb++; +#endif + +#ifdef HAVE_LWIP + /* first poll network */ + FD_ZERO(&sock_readfds); + FD_ZERO(&sock_writefds); + FD_ZERO(&sock_exceptfds); + for (i = 0; i < nfds; i++) { + if (files[i].type == FTYPE_SOCKET) { + if (FD_ISSET(i, readfds)) { + FD_SET(files[i].socket.fd, &sock_readfds); + sock_nfds = i+1; + } + if (FD_ISSET(i, writefds)) { + FD_SET(files[i].socket.fd, &sock_writefds); + sock_nfds = i+1; + } + if (FD_ISSET(i, exceptfds)) { + FD_SET(files[i].socket.fd, &sock_exceptfds); + sock_nfds = i+1; + } + } + } + DEBUG("lwip_select("); + dump_set(nfds, &sock_readfds, &sock_writefds, &sock_exceptfds, &timeout); + DEBUG("); -> "); + sock_n = lwip_select(sock_nfds, &sock_readfds, &sock_writefds, &sock_exceptfds, &timeout); + dump_set(nfds, &sock_readfds, &sock_writefds, &sock_exceptfds, &timeout); + DEBUG("\n"); +#endif + + /* Then see others as well. */ + for (i = 0; i < nfds; i++) { + switch(files[i].type) { + default: + if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) + printk("bogus fd %d in select\n", i); + /* Fallthrough. */ + case FTYPE_FILE: + FD_CLR(i, readfds); + FD_CLR(i, writefds); + FD_CLR(i, exceptfds); + break; + case FTYPE_CONSOLE: + if (FD_ISSET(i, readfds)) { + if (xencons_ring_avail()) + n++; + else + FD_CLR(i, readfds); + } + if (FD_ISSET(i, writefds)) + n++; + FD_CLR(i, exceptfds); + break; + case FTYPE_XENBUS: + if (FD_ISSET(i, readfds)) { + if (files[i].xenbus.events) + n++; + else + FD_CLR(i, readfds); + } + FD_CLR(i, writefds); + FD_CLR(i, exceptfds); + break; + case FTYPE_EVTCHN: + case FTYPE_TAP: + case FTYPE_BLK: + case FTYPE_KBD: + case FTYPE_FB: + if (FD_ISSET(i, readfds)) { + if (files[i].read) + n++; + else + FD_CLR(i, readfds); + } + FD_CLR(i, writefds); + FD_CLR(i, exceptfds); + break; +#ifdef HAVE_LWIP + case FTYPE_SOCKET: + if (FD_ISSET(i, readfds)) { + /* Optimize no-network-packet case. */ + if (sock_n && FD_ISSET(files[i].socket.fd, &sock_readfds)) + n++; + else + FD_CLR(i, readfds); + } + if (FD_ISSET(i, writefds)) { + if (sock_n && FD_ISSET(files[i].socket.fd, &sock_writefds)) + n++; + else + FD_CLR(i, writefds); + } + if (FD_ISSET(i, exceptfds)) { + if (sock_n && FD_ISSET(files[i].socket.fd, &sock_exceptfds)) + n++; + else + FD_CLR(i, exceptfds); + } + break; +#endif + } +#ifdef LIBC_VERBOSE + if (FD_ISSET(i, readfds)) + nbread[i]++; + if (FD_ISSET(i, writefds)) + nbwrite[i]++; + if (FD_ISSET(i, exceptfds)) + nbexcept[i]++; +#endif + } +#ifdef LIBC_VERBOSE + if (NOW() > lastshown + 1000000000ull) { + lastshown = NOW(); + printk("%lu MB free, ", num_free_pages() / ((1 << 20) / PAGE_SIZE)); + printk("%d(%d): ", nb, sock_n); + for (i = 0; i < nfds; i++) { + if (nbread[i] || nbwrite[i] || nbexcept[i]) + printk(" %d(%c):", i, file_types[files[i].type]); + if (nbread[i]) + printk(" %dR", nbread[i]); + if (nbwrite[i]) + printk(" %dW", nbwrite[i]); + if (nbexcept[i]) + printk(" %dE", nbexcept[i]); + } + printk("\n"); + memset(nbread, 0, sizeof(nbread)); + memset(nbwrite, 0, sizeof(nbwrite)); + memset(nbexcept, 0, sizeof(nbexcept)); + nb = 0; + } +#endif + return n; +} + +/* The strategy is to + * - announce that we will maybe sleep + * - poll a bit ; if successful, return + * - if timeout, return + * - really sleep (except if somebody woke us in the meanwhile) */ +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + int n, ret; + fd_set myread, mywrite, myexcept; + struct thread *thread = get_current(); + s_time_t start = NOW(), stop; + DEFINE_WAIT(w1); + DEFINE_WAIT(w2); + DEFINE_WAIT(w3); + DEFINE_WAIT(w4); + DEFINE_WAIT(w5); + DEFINE_WAIT(w6); + + assert(thread == main_thread); + + DEBUG("select(%d, ", nfds); + dump_set(nfds, readfds, writefds, exceptfds, timeout); + DEBUG(");\n"); + + if (timeout) + stop = start + SECONDS(timeout->tv_sec) + timeout->tv_usec * 1000; + else + /* just make gcc happy */ + stop = start; + + /* Tell people we're going to sleep before looking at what they are + * saying, hence letting them wake us if events happen between here and + * schedule() */ + add_waiter(w1, netfront_queue); + add_waiter(w2, event_queue); + add_waiter(w3, blkfront_queue); + add_waiter(w4, xenbus_watch_queue); + add_waiter(w5, kbdfront_queue); + add_waiter(w6, console_queue); + + if (readfds) + myread = *readfds; + else + FD_ZERO(&myread); + if (writefds) + mywrite = *writefds; + else + FD_ZERO(&mywrite); + if (exceptfds) + myexcept = *exceptfds; + else + FD_ZERO(&myexcept); + + DEBUG("polling "); + dump_set(nfds, &myread, &mywrite, &myexcept, timeout); + DEBUG("\n"); + n = select_poll(nfds, &myread, &mywrite, &myexcept); + + if (n) { + dump_set(nfds, readfds, writefds, exceptfds, timeout); + if (readfds) + *readfds = myread; + if (writefds) + *writefds = mywrite; + if (exceptfds) + *exceptfds = myexcept; + DEBUG(" -> "); + dump_set(nfds, readfds, writefds, exceptfds, timeout); + DEBUG("\n"); + wake(thread); + ret = n; + goto out; + } + if (timeout && NOW() >= stop) { + if (readfds) + FD_ZERO(readfds); + if (writefds) + FD_ZERO(writefds); + if (exceptfds) + FD_ZERO(exceptfds); + timeout->tv_sec = 0; + timeout->tv_usec = 0; + wake(thread); + ret = 0; + goto out; + } + + if (timeout) + thread->wakeup_time = stop; + schedule(); + + if (readfds) + myread = *readfds; + else + FD_ZERO(&myread); + if (writefds) + mywrite = *writefds; + else + FD_ZERO(&mywrite); + if (exceptfds) + myexcept = *exceptfds; + else + FD_ZERO(&myexcept); + + n = select_poll(nfds, &myread, &mywrite, &myexcept); + + if (n) { + if (readfds) + *readfds = myread; + if (writefds) + *writefds = mywrite; + if (exceptfds) + *exceptfds = myexcept; + ret = n; + goto out; + } + errno = EINTR; + ret = -1; + +out: + remove_waiter(w1); + remove_waiter(w2); + remove_waiter(w3); + remove_waiter(w4); + remove_waiter(w5); + remove_waiter(w6); + return ret; +} + +#ifdef HAVE_LWIP +int socket(int domain, int type, int protocol) +{ + int fd, res; + fd = lwip_socket(domain, type, protocol); + if (fd < 0) + return -1; + res = alloc_fd(FTYPE_SOCKET); + printk("socket -> %d\n", res); + files[res].socket.fd = fd; + return res; +} + +int accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + int fd, res; + if (files[s].type != FTYPE_SOCKET) { + printk("accept(%d): Bad descriptor\n", s); + errno = EBADF; + return -1; + } + fd = lwip_accept(files[s].socket.fd, addr, addrlen); + if (fd < 0) + return -1; + res = alloc_fd(FTYPE_SOCKET); + files[res].socket.fd = fd; + printk("accepted on %d -> %d\n", s, res); + return res; +} + +#define LWIP_STUB(ret, name, proto, args) \ +ret name proto \ +{ \ + if (files[s].type != FTYPE_SOCKET) { \ + printk(#name "(%d): Bad descriptor\n", s); \ + errno = EBADF; \ + return -1; \ + } \ + s = files[s].socket.fd; \ + return lwip_##name args; \ +} + +LWIP_STUB(int, bind, (int s, struct sockaddr *my_addr, socklen_t addrlen), (s, my_addr, addrlen)) +LWIP_STUB(int, getsockopt, (int s, int level, int optname, void *optval, socklen_t *optlen), (s, level, optname, optval, optlen)) +LWIP_STUB(int, setsockopt, (int s, int level, int optname, void *optval, socklen_t optlen), (s, level, optname, optval, optlen)) +LWIP_STUB(int, connect, (int s, struct sockaddr *serv_addr, socklen_t addrlen), (s, serv_addr, addrlen)) +LWIP_STUB(int, listen, (int s, int backlog), (s, backlog)); +LWIP_STUB(ssize_t, recv, (int s, void *buf, size_t len, int flags), (s, buf, len, flags)) +LWIP_STUB(ssize_t, recvfrom, (int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen), (s, buf, len, flags, from, fromlen)) +LWIP_STUB(ssize_t, send, (int s, void *buf, size_t len, int flags), (s, buf, len, flags)) +LWIP_STUB(ssize_t, sendto, (int s, void *buf, size_t len, int flags, struct sockaddr *to, socklen_t tolen), (s, buf, len, flags, to, tolen)) +LWIP_STUB(int, getsockname, (int s, struct sockaddr *name, socklen_t *namelen), (s, name, namelen)) +#endif + +static char *syslog_ident; +void openlog(const char *ident, int option, int facility) +{ + if (syslog_ident) + free(syslog_ident); + syslog_ident = strdup(ident); +} + +void vsyslog(int priority, const char *format, va_list ap) +{ + printk("%s: ", syslog_ident); + print(0, format, ap); +} + +void syslog(int priority, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vsyslog(priority, format, ap); + va_end(ap); +} + +void closelog(void) +{ + free(syslog_ident); + syslog_ident = NULL; +} + +void vwarn(const char *format, va_list ap) +{ + int the_errno = errno; + printk("stubdom: "); + if (format) { + print(0, format, ap); + printk(", "); + } + printk("%s", strerror(the_errno)); +} + +void warn(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vwarn(format, ap); + va_end(ap); +} + +void verr(int eval, const char *format, va_list ap) +{ + vwarn(format, ap); + exit(eval); +} + +void err(int eval, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verr(eval, format, ap); + va_end(ap); +} + +void vwarnx(const char *format, va_list ap) +{ + printk("stubdom: "); + if (format) + print(0, format, ap); +} + +void warnx(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vwarnx(format, ap); + va_end(ap); +} + +void verrx(int eval, const char *format, va_list ap) +{ + vwarnx(format, ap); + exit(eval); +} + +void errx(int eval, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verrx(eval, format, ap); + va_end(ap); +} + +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + s_time_t start = NOW(); + s_time_t stop = start + SECONDS(req->tv_sec) + req->tv_nsec; + s_time_t stopped; + struct thread *thread = get_current(); + + thread->wakeup_time = stop; + clear_runnable(thread); + schedule(); + stopped = NOW(); + + if (rem) + { + s_time_t remaining = stop - stopped; + if (remaining > 0) + { + rem->tv_nsec = remaining % 1000000000ULL; + rem->tv_sec = remaining / 1000000000ULL; + } else memset(rem, 0, sizeof(*rem)); + } + + return 0; +} + +int usleep(useconds_t usec) +{ + /* "usec shall be less than one million." */ + struct timespec req; + req.tv_nsec = usec * 1000; + req.tv_sec = 0; + + if (nanosleep(&req, NULL)) + return -1; + + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + struct timespec req, rem; + req.tv_sec = seconds; + req.tv_nsec = 0; + + if (nanosleep(&req, &rem)) + return -1; + + if (rem.tv_nsec > 0) + rem.tv_sec++; + + return rem.tv_sec; +} + +int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + switch (clk_id) { + case CLOCK_MONOTONIC: + { + struct timeval tv; + + gettimeofday(&tv, NULL); + + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + + break; + } + case CLOCK_REALTIME: + { + u64 nsec = monotonic_clock(); + + tp->tv_sec = nsec / 1000000000ULL; + tp->tv_nsec = nsec % 1000000000ULL; + + break; + } + default: + print_unsupported("clock_gettime(%d)", clk_id); + errno = EINVAL; + return -1; + } + + return 0; +} + +size_t getpagesize(void) +{ + return PAGE_SIZE; +} + +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + unsigned long n = (length + PAGE_SIZE - 1) / PAGE_SIZE; + + ASSERT(!start); + ASSERT(prot == (PROT_READ|PROT_WRITE)); + ASSERT((fd == -1 && (flags == (MAP_SHARED|MAP_ANON) || flags == (MAP_PRIVATE|MAP_ANON))) + || (fd != -1 && flags == MAP_SHARED)); + ASSERT(offset == 0); + + if (fd == -1) + return map_zero(n, 1); + else if (files[fd].type == FTYPE_XC) { + unsigned long zero = 0; + return map_frames_ex(&zero, n, 0, 0, 1, DOMID_SELF, 0, 0); + } else ASSERT(0); +} + +#define UNMAP_BATCH ((STACK_SIZE / 2) / sizeof(multicall_entry_t)) +int munmap(void *start, size_t length) +{ + int total = length / PAGE_SIZE; + ASSERT(!((unsigned long)start & ~PAGE_MASK)); + while (total) { + int n = UNMAP_BATCH; + if (n > total) + n = total; + { + int i; + multicall_entry_t call[n]; + unsigned char (*data)[PAGE_SIZE] = start; + int ret; + + for (i = 0; i < n; i++) { + int arg = 0; + call[i].op = __HYPERVISOR_update_va_mapping; + call[i].args[arg++] = (unsigned long) &data[i]; + call[i].args[arg++] = 0; +#ifdef __i386__ + call[i].args[arg++] = 0; +#endif + call[i].args[arg++] = UVMF_INVLPG; + } + + ret = HYPERVISOR_multicall(call, n); + if (ret) { + errno = -ret; + return -1; + } + + for (i = 0; i < n; i++) { + if (call[i].result) { + errno = call[i].result; + return -1; + } + } + } + start = (char *)start + n * PAGE_SIZE; + total -= n; + } + return 0; +} + +void sparse(unsigned long data, size_t size) +{ + unsigned long newdata; + xen_pfn_t *mfns; + int i, n; + + newdata = (data + PAGE_SIZE - 1) & PAGE_MASK; + if (newdata - data > size) + return; + size -= newdata - data; + data = newdata; + n = size / PAGE_SIZE; + size = n * PAGE_SIZE; + + mfns = malloc(n * sizeof(*mfns)); + for (i = 0; i < n; i++) { +#ifdef LIBC_DEBUG + int j; + for (j=0; j> 20, data); + + munmap((void *) data, size); + free_physical_pages(mfns, n); + do_map_zero(data, n); +} + + +/* Not supported by FS yet. */ +unsupported_function_crash(link); +unsupported_function(int, readlink, -1); +unsupported_function_crash(umask); + +/* We could support that. */ +unsupported_function_log(int, chdir, -1); + +/* No dynamic library support. */ +unsupported_function_log(void *, dlopen, NULL); +unsupported_function_log(void *, dlsym, NULL); +unsupported_function_log(char *, dlerror, NULL); +unsupported_function_log(int, dlclose, -1); + +/* We don't raise signals anyway. */ +unsupported_function(int, sigemptyset, -1); +unsupported_function(int, sigfillset, -1); +unsupported_function(int, sigaddset, -1); +unsupported_function(int, sigdelset, -1); +unsupported_function(int, sigismember, -1); +unsupported_function(int, sigprocmask, -1); +unsupported_function(int, sigaction, -1); +unsupported_function(int, __sigsetjmp, 0); +unsupported_function(int, sigaltstack, -1); +unsupported_function_crash(kill); + +/* Unsupported */ +unsupported_function_crash(pipe); +unsupported_function_crash(fork); +unsupported_function_crash(execv); +unsupported_function_crash(execve); +unsupported_function_crash(waitpid); +unsupported_function_crash(wait); +unsupported_function_crash(lockf); +unsupported_function_crash(sysconf); +unsupported_function(int, tcsetattr, -1); +unsupported_function(int, tcgetattr, 0); +unsupported_function(int, poll, -1); + +/* Linuxish abi for the Caml runtime, don't support */ +unsupported_function_log(struct dirent *, readdir64, NULL); +unsupported_function_log(int, getrusage, -1); +unsupported_function_log(int, getrlimit, -1); +unsupported_function_log(int, getrlimit64, -1); +unsupported_function_log(int, __xstat64, -1); +unsupported_function_log(long, __strtol_internal, LONG_MIN); +unsupported_function_log(double, __strtod_internal, HUGE_VAL); +#endif diff --git a/extras/mini-os/lib/xmalloc.c b/extras/mini-os/lib/xmalloc.c new file mode 100644 index 0000000..c33c48a --- /dev/null +++ b/extras/mini-os/lib/xmalloc.c @@ -0,0 +1,302 @@ +/* + **************************************************************************** + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: xmaloc.c + * Author: Grzegorz Milos (gm281@cam.ac.uk) + * Samuel Thibault (samuel.thibault@eu.citrix.com) + * Changes: + * + * Date: Aug 2005 + * Jan 2008 + * + * Environment: Xen Minimal OS + * Description: simple memory allocator + * + **************************************************************************** + * Simple allocator for Mini-os. If larger than a page, simply use the + * page-order allocator. + * + * Copy of the allocator for Xen by Rusty Russell: + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_LIBC +static MINIOS_LIST_HEAD(freelist); +/* static spinlock_t freelist_lock = SPIN_LOCK_UNLOCKED; */ + +struct xmalloc_hdr +{ + /* Total including this hdr, unused padding and second hdr. */ + size_t size; + struct minios_list_head freelist; +} __cacheline_aligned; + +/* Unused padding data between the two hdrs. */ + +struct xmalloc_pad +{ + /* Size including both hdrs. */ + size_t hdr_size; +}; + +/* Return size, increased to alignment with align. */ +static inline size_t align_up(size_t size, size_t align) +{ + return (size + align - 1) & ~(align - 1); +} + +static void maybe_split(struct xmalloc_hdr *hdr, size_t size, size_t block) +{ + struct xmalloc_hdr *extra; + size_t leftover; + size = align_up(size, __alignof__(struct xmalloc_hdr)); + size = align_up(size, __alignof__(struct xmalloc_pad)); + leftover = block - size; + + /* If enough is left to make a block, put it on free list. */ + if ( leftover >= (2 * (sizeof(struct xmalloc_hdr) + sizeof(struct xmalloc_pad))) ) + { + extra = (struct xmalloc_hdr *)((unsigned long)hdr + size); + extra->size = leftover; + /* spin_lock_irqsave(&freelist_lock, flags); */ + minios_list_add(&extra->freelist, &freelist); + /* spin_unlock_irqrestore(&freelist_lock, flags); */ + } + else + { + size = block; + } + + hdr->size = size; + /* Debugging aid. */ + hdr->freelist.next = hdr->freelist.prev = NULL; +} + +static struct xmalloc_hdr *xmalloc_new_page(size_t size) +{ + struct xmalloc_hdr *hdr; + /* unsigned long flags; */ + + hdr = (struct xmalloc_hdr *)alloc_page(); + if ( hdr == NULL ) + return NULL; + + maybe_split(hdr, size, PAGE_SIZE); + + return hdr; +} + +/* Big object? Just use the page allocator. */ +static void *xmalloc_whole_pages(size_t size, size_t align) +{ + struct xmalloc_hdr *hdr; + struct xmalloc_pad *pad; + unsigned int pageorder; + void *ret; + /* Room for headers */ + size_t hdr_size = sizeof(struct xmalloc_hdr) + sizeof(struct xmalloc_pad); + /* Align for actual beginning of data */ + hdr_size = align_up(hdr_size, align); + + pageorder = get_order(hdr_size + size); + + hdr = (struct xmalloc_hdr *)alloc_pages(pageorder); + if ( hdr == NULL ) + return NULL; + + hdr->size = (1UL << (pageorder + PAGE_SHIFT)); + /* Debugging aid. */ + hdr->freelist.next = hdr->freelist.prev = NULL; + + ret = (char*)hdr + hdr_size; + pad = (struct xmalloc_pad *) ret - 1; + pad->hdr_size = hdr_size; + return ret; +} + +void *_xmalloc(size_t size, size_t align) +{ + struct xmalloc_hdr *i, *tmp, *hdr = NULL; + uintptr_t data_begin; + size_t hdr_size; + /* unsigned long flags; */ + + hdr_size = sizeof(struct xmalloc_hdr) + sizeof(struct xmalloc_pad); + /* Align on headers requirements. */ + align = align_up(align, __alignof__(struct xmalloc_hdr)); + align = align_up(align, __alignof__(struct xmalloc_pad)); + + /* For big allocs, give them whole pages. */ + if ( size + align_up(hdr_size, align) >= PAGE_SIZE ) + return xmalloc_whole_pages(size, align); + + /* Search free list. */ + /* spin_lock_irqsave(&freelist_lock, flags); */ + minios_list_for_each_entry_safe( i, tmp, &freelist, freelist ) + { + data_begin = align_up((uintptr_t)i + hdr_size, align); + + if ( data_begin + size > (uintptr_t)i + i->size ) + continue; + + minios_list_del(&i->freelist); + /* spin_unlock_irqrestore(&freelist_lock, flags); */ + + uintptr_t size_before = (data_begin - hdr_size) - (uintptr_t)i; + + if (size_before >= 2 * hdr_size) { + /* Worth splitting the beginning */ + struct xmalloc_hdr *new_i = (void*)(data_begin - hdr_size); + new_i->size = i->size - size_before; + i->size = size_before; + /* spin_lock_irqsave(&freelist_lock, flags); */ + minios_list_add(&i->freelist, &freelist); + /* spin_unlock_irqrestore(&freelist_lock, flags); */ + i = new_i; + } + maybe_split(i, (data_begin + size) - (uintptr_t)i, i->size); + hdr = i; + break; + } + + if (!hdr) { + /* spin_unlock_irqrestore(&freelist_lock, flags); */ + + /* Alloc a new page and return from that. */ + hdr = xmalloc_new_page(align_up(hdr_size, align) + size); + data_begin = (uintptr_t)hdr + align_up(hdr_size, align); + } + + struct xmalloc_pad *pad = (struct xmalloc_pad *) data_begin - 1; + pad->hdr_size = data_begin - (uintptr_t)hdr; + BUG_ON(data_begin % align); + return (void*)data_begin; +} + +void xfree(const void *p) +{ + /* unsigned long flags; */ + struct xmalloc_hdr *i, *tmp, *hdr; + struct xmalloc_pad *pad; + + if ( p == NULL ) + return; + + pad = (struct xmalloc_pad *)p - 1; + hdr = (struct xmalloc_hdr *)((char *)p - pad->hdr_size); + + /* Big allocs free directly. */ + if ( hdr->size >= PAGE_SIZE ) + { + free_pages(hdr, get_order(hdr->size)); + return; + } + + /* We know hdr will be on same page. */ + if(((long)p & PAGE_MASK) != ((long)hdr & PAGE_MASK)) + { + printk("Header should be on the same page\n"); + *(int*)0=0; + } + + /* Not previously freed. */ + if(hdr->freelist.next || hdr->freelist.prev) + { + printk("Should not be previously freed\n"); + *(int*)0=0; + } + + /* Merge with other free block, or put in list. */ + /* spin_lock_irqsave(&freelist_lock, flags); */ + minios_list_for_each_entry_safe( i, tmp, &freelist, freelist ) + { + unsigned long _i = (unsigned long)i; + unsigned long _hdr = (unsigned long)hdr; + + /* Do not merge across page boundaries. */ + if ( ((_i ^ _hdr) & PAGE_MASK) != 0 ) + continue; + + /* We follow this block? Swallow it. */ + if ( (_i + i->size) == _hdr ) + { + minios_list_del(&i->freelist); + i->size += hdr->size; + hdr = i; + } + + /* We precede this block? Swallow it. */ + if ( (_hdr + hdr->size) == _i ) + { + minios_list_del(&i->freelist); + hdr->size += i->size; + } + } + + /* Did we merge an entire page? */ + if ( hdr->size == PAGE_SIZE ) + { + if((((unsigned long)hdr) & (PAGE_SIZE-1)) != 0) + { + printk("Bug\n"); + *(int*)0=0; + } + free_page(hdr); + } + else + { + minios_list_add(&hdr->freelist, &freelist); + } + + /* spin_unlock_irqrestore(&freelist_lock, flags); */ +} + +void *_realloc(void *ptr, size_t size) +{ + void *new; + struct xmalloc_hdr *hdr; + struct xmalloc_pad *pad; + + if (ptr == NULL) + return _xmalloc(size, DEFAULT_ALIGN); + + pad = (struct xmalloc_pad *)ptr - 1; + hdr = (struct xmalloc_hdr *)((char*)ptr - pad->hdr_size); + if (hdr->size >= size) { + maybe_split(hdr, size, hdr->size); + return ptr; + } + + new = _xmalloc(size, DEFAULT_ALIGN); + if (new == NULL) + return NULL; + + memcpy(new, ptr, hdr->size); + xfree(ptr); + + return new; +} +#endif diff --git a/extras/mini-os/lib/xs.c b/extras/mini-os/lib/xs.c new file mode 100644 index 0000000..b654b0e --- /dev/null +++ b/extras/mini-os/lib/xs.c @@ -0,0 +1,187 @@ +/* + * libxs-compatible layer + * + * Samuel Thibault , 2007-2008 + * + * Mere wrapper around xenbus_* + */ + +#ifdef HAVE_LIBC +#include +#include +#include +#include +#include +#include + +static inline int _xs_fileno(struct xs_handle *h) { + return (intptr_t) h; +} + +struct xs_handle *xs_daemon_open() +{ + int fd = alloc_fd(FTYPE_XENBUS); + files[fd].xenbus.events = NULL; + printk("xs_daemon_open -> %d, %p\n", fd, &files[fd].xenbus.events); + return (void*)(intptr_t) fd; +} + +void xs_daemon_close(struct xs_handle *h) +{ + int fd = _xs_fileno(h); + struct xenbus_event *event; + for (event = files[fd].xenbus.events; event; event = event->next) + free(event); + files[fd].type = FTYPE_NONE; +} + +int xs_fileno(struct xs_handle *h) +{ + return _xs_fileno(h); +} + +void *xs_read(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *len) +{ + char *value; + char *msg; + + msg = xenbus_read(t, path, &value); + if (msg) { + printk("xs_read(%s): %s\n", path, msg); + return NULL; + } + + if (len) + *len = strlen(value); + return value; +} + +bool xs_write(struct xs_handle *h, xs_transaction_t t, + const char *path, const void *data, unsigned int len) +{ + char value[len + 1]; + char *msg; + + memcpy(value, data, len); + value[len] = 0; + + msg = xenbus_write(t, path, value); + if (msg) { + printk("xs_write(%s): %s\n", path, msg); + return false; + } + return true; +} + +static bool xs_bool(char *reply) +{ + if (!reply) + return true; + free(reply); + return false; +} + +bool xs_rm(struct xs_handle *h, xs_transaction_t t, const char *path) +{ + return xs_bool(xenbus_rm(t, path)); +} + +static void *xs_talkv(struct xs_handle *h, xs_transaction_t t, + enum xsd_sockmsg_type type, + struct write_req *iovec, + unsigned int num_vecs, + unsigned int *len) +{ + struct xsd_sockmsg *msg; + void *ret; + + msg = xenbus_msg_reply(type, t, iovec, num_vecs); + ret = malloc(msg->len); + memcpy(ret, (char*) msg + sizeof(*msg), msg->len); + if (len) + *len = msg->len - 1; + free(msg); + return ret; +} + +static void *xs_single(struct xs_handle *h, xs_transaction_t t, + enum xsd_sockmsg_type type, + const char *string, + unsigned int *len) +{ + struct write_req iovec; + + iovec.data = (void *)string; + iovec.len = strlen(string) + 1; + + return xs_talkv(h, t, type, &iovec, 1, len); +} + +char *xs_get_domain_path(struct xs_handle *h, unsigned int domid) +{ + char domid_str[MAX_STRLEN(domid)]; + + sprintf(domid_str, "%u", domid); + + return xs_single(h, XBT_NULL, XS_GET_DOMAIN_PATH, domid_str, NULL); +} + +char **xs_directory(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *num) +{ + char *msg; + char **entries, **res; + char *entry; + int i, n; + int size; + + msg = xenbus_ls(t, path, &res); + if (msg) { + printk("xs_directory(%s): %s\n", path, msg); + return NULL; + } + + size = 0; + for (n = 0; res[n]; n++) + size += strlen(res[n]) + 1; + + entries = malloc(n * sizeof(char *) + size); + entry = (char *) (&entries[n]); + + for (i = 0; i < n; i++) { + int l = strlen(res[i]) + 1; + memcpy(entry, res[i], l); + free(res[i]); + entries[i] = entry; + entry += l; + } + + *num = n; + return entries; +} + +bool xs_watch(struct xs_handle *h, const char *path, const char *token) +{ + int fd = _xs_fileno(h); + printk("xs_watch(%s, %s)\n", path, token); + return xs_bool(xenbus_watch_path_token(XBT_NULL, path, token, &files[fd].xenbus.events)); +} + +char **xs_read_watch(struct xs_handle *h, unsigned int *num) +{ + int fd = _xs_fileno(h); + struct xenbus_event *event; + event = files[fd].xenbus.events; + files[fd].xenbus.events = event->next; + printk("xs_read_watch() -> %s %s\n", event->path, event->token); + *num = 2; + return (char **) &event->path; +} + +bool xs_unwatch(struct xs_handle *h, const char *path, const char *token) +{ + printk("xs_unwatch(%s, %s)\n", path, token); + return xs_bool(xenbus_unwatch_path_token(XBT_NULL, path, token)); +} +#endif diff --git a/extras/mini-os/lock.c b/extras/mini-os/lock.c new file mode 100644 index 0000000..71a4971 --- /dev/null +++ b/extras/mini-os/lock.c @@ -0,0 +1,111 @@ +/* + * locks for newlib + * + * Samuel Thibault , July 20008 + */ + +#ifdef HAVE_LIBC + +#include +#include +#include + +int ___lock_init(_LOCK_T *lock) +{ + lock->busy = 0; + init_waitqueue_head(&lock->wait); + return 0; +} + +int ___lock_acquire(_LOCK_T *lock) +{ + unsigned long flags; + while(1) { + wait_event(lock->wait, !lock->busy); + local_irq_save(flags); + if (!lock->busy) + break; + local_irq_restore(flags); + } + lock->busy = 1; + local_irq_restore(flags); + return 0; +} + +int ___lock_try_acquire(_LOCK_T *lock) +{ + unsigned long flags; + int ret = -1; + local_irq_save(flags); + if (!lock->busy) { + lock->busy = 1; + ret = 0; + } + local_irq_restore(flags); + return ret; +} + +int ___lock_release(_LOCK_T *lock) +{ + unsigned long flags; + local_irq_save(flags); + lock->busy = 0; + wake_up(&lock->wait); + local_irq_restore(flags); + return 0; +} + + +int ___lock_init_recursive(_LOCK_RECURSIVE_T *lock) +{ + lock->owner = NULL; + init_waitqueue_head(&lock->wait); + return 0; +} + +int ___lock_acquire_recursive(_LOCK_RECURSIVE_T *lock) +{ + unsigned long flags; + if (lock->owner != get_current()) { + while (1) { + wait_event(lock->wait, lock->owner == NULL); + local_irq_save(flags); + if (lock->owner == NULL) + break; + local_irq_restore(flags); + } + lock->owner = get_current(); + local_irq_restore(flags); + } + lock->count++; + return 0; +} + +int ___lock_try_acquire_recursive(_LOCK_RECURSIVE_T *lock) +{ + unsigned long flags; + int ret = -1; + local_irq_save(flags); + if (!lock->owner) { + ret = 0; + lock->owner = get_current(); + lock->count++; + } + local_irq_restore(flags); + return ret; +} + +int ___lock_release_recursive(_LOCK_RECURSIVE_T *lock) +{ + unsigned long flags; + BUG_ON(lock->owner != get_current()); + if (--lock->count) + return 0; + local_irq_save(flags); + lock->owner = NULL; + wake_up(&lock->wait); + local_irq_restore(flags); + return 0; +} + +#endif diff --git a/extras/mini-os/lwip-arch.c b/extras/mini-os/lwip-arch.c new file mode 100644 index 0000000..cdd4fa6 --- /dev/null +++ b/extras/mini-os/lwip-arch.c @@ -0,0 +1,293 @@ +/* + * lwip-arch.c + * + * Arch-specific semaphores and mailboxes for lwIP running on mini-os + * + * Tim Deegan , July 2007 + */ + +#include +#include +#include +#include +#include +#include + +/* Is called to initialize the sys_arch layer */ +void sys_init(void) +{ +} + +/* Creates and returns a new semaphore. The "count" argument specifies + * the initial state of the semaphore. */ +sys_sem_t sys_sem_new(u8_t count) +{ + struct semaphore *sem = xmalloc(struct semaphore); + sem->count = count; + init_waitqueue_head(&sem->wait); + return sem; +} + +/* Deallocates a semaphore. */ +void sys_sem_free(sys_sem_t sem) +{ + xfree(sem); +} + +/* Signals a semaphore. */ +void sys_sem_signal(sys_sem_t sem) +{ + up(sem); +} + +/* Blocks the thread while waiting for the semaphore to be + * signaled. If the "timeout" argument is non-zero, the thread should + * only be blocked for the specified time (measured in + * milliseconds). + * + * If the timeout argument is non-zero, the return value is the number of + * milliseconds spent waiting for the semaphore to be signaled. If the + * semaphore wasn't signaled within the specified time, the return value is + * SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + * (i.e., it was already signaled), the function may return zero. */ +u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) +{ + /* Slightly more complicated than the normal minios semaphore: + * need to wake on timeout *or* signal */ + sys_prot_t prot; + s64_t then = NOW(); + s64_t deadline; + + if (timeout == 0) + deadline = 0; + else + deadline = then + MILLISECS(timeout); + + while(1) { + wait_event_deadline(sem->wait, (sem->count > 0), deadline); + + prot = sys_arch_protect(); + /* Atomically check that we can proceed */ + if (sem->count > 0 || (deadline && NOW() >= deadline)) + break; + sys_arch_unprotect(prot); + } + + if (sem->count > 0) { + sem->count--; + sys_arch_unprotect(prot); + return NSEC_TO_MSEC(NOW() - then); + } + + sys_arch_unprotect(prot); + return SYS_ARCH_TIMEOUT; +} + +/* Creates an empty mailbox. */ +sys_mbox_t sys_mbox_new(int size) +{ + struct mbox *mbox = xmalloc(struct mbox); + if (!size) + size = 32; + else if (size == 1) + size = 2; + mbox->count = size; + mbox->messages = xmalloc_array(void*, size); + init_SEMAPHORE(&mbox->read_sem, 0); + mbox->reader = 0; + init_SEMAPHORE(&mbox->write_sem, size); + mbox->writer = 0; + return mbox; +} + +/* Deallocates a mailbox. If there are messages still present in the + * mailbox when the mailbox is deallocated, it is an indication of a + * programming error in lwIP and the developer should be notified. */ +void sys_mbox_free(sys_mbox_t mbox) +{ + ASSERT(mbox->reader == mbox->writer); + xfree(mbox->messages); + xfree(mbox); +} + +/* Posts the "msg" to the mailbox, internal version that actually does the + * post. */ +static void do_mbox_post(sys_mbox_t mbox, void *msg) +{ + /* The caller got a semaphore token, so we are now allowed to increment + * writer, but we still need to prevent concurrency between writers + * (interrupt handler vs main) */ + sys_prot_t prot = sys_arch_protect(); + mbox->messages[mbox->writer] = msg; + mbox->writer = (mbox->writer + 1) % mbox->count; + ASSERT(mbox->reader != mbox->writer); + sys_arch_unprotect(prot); + up(&mbox->read_sem); +} + +/* Posts the "msg" to the mailbox. */ +void sys_mbox_post(sys_mbox_t mbox, void *msg) +{ + if (mbox == SYS_MBOX_NULL) + return; + down(&mbox->write_sem); + do_mbox_post(mbox, msg); +} + +/* Try to post the "msg" to the mailbox. */ +err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg) +{ + if (mbox == SYS_MBOX_NULL) + return ERR_BUF; + if (!trydown(&mbox->write_sem)) + return ERR_MEM; + do_mbox_post(mbox, msg); + return ERR_OK; +} + +/* + * Fetch a message from a mailbox. Internal version that actually does the + * fetch. + */ +static void do_mbox_fetch(sys_mbox_t mbox, void **msg) +{ + sys_prot_t prot; + /* The caller got a semaphore token, so we are now allowed to increment + * reader, but we may still need to prevent concurrency between readers. + * FIXME: can there be concurrent readers? */ + prot = sys_arch_protect(); + ASSERT(mbox->reader != mbox->writer); + if (msg != NULL) + *msg = mbox->messages[mbox->reader]; + mbox->reader = (mbox->reader + 1) % mbox->count; + sys_arch_unprotect(prot); + up(&mbox->write_sem); +} + +/* Blocks the thread until a message arrives in the mailbox, but does + * not block the thread longer than "timeout" milliseconds (similar to + * the sys_arch_sem_wait() function). The "msg" argument is a result + * parameter that is set by the function (i.e., by doing "*msg = + * ptr"). The "msg" parameter maybe NULL to indicate that the message + * should be dropped. + * + * The return values are the same as for the sys_arch_sem_wait() function: + * Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + * timeout. */ +u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) +{ + u32 rv; + if (mbox == SYS_MBOX_NULL) + return SYS_ARCH_TIMEOUT; + + rv = sys_arch_sem_wait(&mbox->read_sem, timeout); + if ( rv == SYS_ARCH_TIMEOUT ) + return rv; + + do_mbox_fetch(mbox, msg); + return 0; +} + +/* This is similar to sys_arch_mbox_fetch, however if a message is not + * present in the mailbox, it immediately returns with the code + * SYS_MBOX_EMPTY. On success 0 is returned. + * + * To allow for efficient implementations, this can be defined as a + * function-like macro in sys_arch.h instead of a normal function. For + * example, a naive implementation could be: + * #define sys_arch_mbox_tryfetch(mbox,msg) \ + * sys_arch_mbox_fetch(mbox,msg,1) + * although this would introduce unnecessary delays. */ + +u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) { + if (mbox == SYS_MBOX_NULL) + return SYS_ARCH_TIMEOUT; + + if (!trydown(&mbox->read_sem)) + return SYS_MBOX_EMPTY; + + do_mbox_fetch(mbox, msg); + return 0; +} + + +/* Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + * each thread has a list of timeouts which is repressented as a linked + * list of sys_timeout structures. The sys_timeouts structure holds a + * pointer to a linked list of timeouts. This function is called by + * the lwIP timeout scheduler and must not return a NULL value. + * + * In a single threadd sys_arch implementation, this function will + * simply return a pointer to a global sys_timeouts variable stored in + * the sys_arch module. */ +struct sys_timeouts *sys_arch_timeouts(void) +{ + static struct sys_timeouts timeout; + return &timeout; +} + + +/* Starts a new thread with priority "prio" that will begin its execution in the + * function "thread()". The "arg" argument will be passed as an argument to the + * thread() function. The id of the new thread is returned. Both the id and + * the priority are system dependent. */ +static struct thread *lwip_thread; +sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) +{ + struct thread *t; + if (stacksize > STACK_SIZE) { + printk("Can't start lwIP thread: stack size %d is too large for our %d\n", stacksize, STACK_SIZE); + do_exit(); + } + lwip_thread = t = create_thread(name, thread, arg); + return t; +} + +/* This optional function does a "fast" critical region protection and returns + * the previous protection level. This function is only called during very short + * critical regions. An embedded system which supports ISR-based drivers might + * want to implement this function by disabling interrupts. Task-based systems + * might want to implement this by using a mutex or disabling tasking. This + * function should support recursive calls from the same task or interrupt. In + * other words, sys_arch_protect() could be called while already protected. In + * that case the return value indicates that it is already protected. + * + * sys_arch_protect() is only required if your port is supporting an operating + * system. */ +sys_prot_t sys_arch_protect(void) +{ + unsigned long flags; + local_irq_save(flags); + return flags; +} + +/* This optional function does a "fast" set of critical region protection to the + * value specified by pval. See the documentation for sys_arch_protect() for + * more information. This function is only required if your port is supporting + * an operating system. */ +void sys_arch_unprotect(sys_prot_t pval) +{ + local_irq_restore(pval); +} + +/* non-fatal, print a message. */ +void lwip_printk(char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + printk("lwIP: "); + print(0, fmt, args); + va_end(args); +} + +/* fatal, print message and abandon execution. */ +void lwip_die(char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + printk("lwIP assertion failed: "); + print(0, fmt, args); + va_end(args); + printk("\n"); + BUG(); +} diff --git a/extras/mini-os/lwip-net.c b/extras/mini-os/lwip-net.c new file mode 100644 index 0000000..194ae27 --- /dev/null +++ b/extras/mini-os/lwip-net.c @@ -0,0 +1,388 @@ +/* + * lwip-net.c + * + * interface between lwIP's ethernet and Mini-os's netfront. + * For now, support only one network interface, as mini-os does. + * + * Tim Deegan , July 2007 + * based on lwIP's ethernetif.c skeleton file, copyrights as below. + */ + + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netif/etharp.h" + +#include + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +#define IF_IPADDR 0x00000000 +#define IF_NETMASK 0x00000000 + +/* Only have one network interface at a time. */ +static struct netif *the_interface = NULL; + +static unsigned char rawmac[6]; +static struct netfront_dev *dev; + +/* Forward declarations. */ +static err_t netfront_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr); + +/* + * low_level_output(): + * + * Should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + if (!dev) + return ERR_OK; + +#ifdef ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + if (!p->next) { + /* Only one fragment, can send it directly */ + netfront_xmit(dev, p->payload, p->len); + } else { + unsigned char data[p->tot_len], *cur; + struct pbuf *q; + + for(q = p, cur = data; q != NULL; cur += q->len, q = q->next) + memcpy(cur, q->payload, q->len); + netfront_xmit(dev, data, p->tot_len); + } + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + + + +/* + * netfront_output(): + * + * This function is called by the TCP/IP stack when an IP packet + * should be sent. It calls the function called low_level_output() to + * do the actual transmission of the packet. + * + */ + +static err_t +netfront_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) +{ + + /* resolve hardware address, then send (or queue) packet */ + return etharp_output(netif, p, ipaddr); + +} + +/* + * netfront_input(): + * + * This function should be called when a packet is ready to be read + * from the interface. + * + */ + +static void +netfront_input(struct netif *netif, unsigned char* data, int len) +{ + struct eth_hdr *ethhdr; + struct pbuf *p, *q; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* move received packet into a new pbuf */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + if (p == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return; + } + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL && len > 0; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. */ + memcpy(q->payload, data, len < q->len ? len : q->len); + data += q->len; + len -= q->len; + } + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP packet? */ + case ETHTYPE_IP: +#if 0 +/* CSi disabled ARP table update on ingress IP packets. + This seems to work but needs thorough testing. */ + /* update ARP table */ + etharp_ip_input(netif, p); +#endif + /* skip Ethernet header */ + pbuf_header(p, -(s16)sizeof(struct eth_hdr)); + /* pass to network layer */ + if (tcpip_input(p, netif) == ERR_MEM) + /* Could not store it, drop */ + pbuf_free(p); + break; + + case ETHTYPE_ARP: + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr *) netif->hwaddr, p); + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + + +/* + * netif_rx(): overrides the default netif_rx behaviour in the netfront driver. + * + * Pull received packets into a pbuf queue for the low_level_input() + * function to pass up to lwIP. + */ + +void netif_rx(unsigned char* data, int len) +{ + if (the_interface != NULL) { + netfront_input(the_interface, data, len); + wake_up(&netfront_queue); + } + /* By returning, we ack the packet and relinquish the RX ring slot */ +} + +/* + * Set the IP, mask and gateway of the IF + */ +void networking_set_addr(struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw) +{ + netif_set_ipaddr(the_interface, ipaddr); + netif_set_netmask(the_interface, netmask); + netif_set_gw(the_interface, gw); +} + + +static void +arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * netif_netfront_init(): + * + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + */ + +err_t +netif_netfront_init(struct netif *netif) +{ + unsigned char *mac = netif->state; + +#if LWIP_SNMP + /* ifType ethernetCsmacd(6) @see RFC1213 */ + netif->link_type = 6; + /* your link speed here */ + netif->link_speed = ; + netif->ts = 0; + netif->ifinoctets = 0; + netif->ifinucastpkts = 0; + netif->ifinnucastpkts = 0; + netif->ifindiscards = 0; + netif->ifoutoctets = 0; + netif->ifoutucastpkts = 0; + netif->ifoutnucastpkts = 0; + netif->ifoutdiscards = 0; +#endif + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + netif->output = netfront_output; + netif->linkoutput = low_level_output; + + the_interface = netif; + + /* set MAC hardware address */ + netif->hwaddr_len = 6; + netif->hwaddr[0] = mac[0]; + netif->hwaddr[1] = mac[1]; + netif->hwaddr[2] = mac[2]; + netif->hwaddr[3] = mac[3]; + netif->hwaddr[4] = mac[4]; + netif->hwaddr[5] = mac[5]; + + /* No interesting per-interface state */ + netif->state = NULL; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* broadcast capability */ + netif->flags = NETIF_FLAG_BROADCAST; + + etharp_init(); + + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + + return ERR_OK; +} + +/* + * Thread run by netfront: bring up the IP address and fire lwIP timers. + */ +static __DECLARE_SEMAPHORE_GENERIC(tcpip_is_up, 0); +static void tcpip_bringup_finished(void *p) +{ + tprintk("TCP/IP bringup ends.\n"); + up(&tcpip_is_up); +} + +/* + * Utility function to bring the whole lot up. Call this from app_main() + * or similar -- it starts netfront and have lwIP start its thread, + * which calls back to tcpip_bringup_finished(), which + * lets us know it's OK to continue. + */ +void start_networking(void) +{ + struct netif *netif; + struct ip_addr ipaddr = { htonl(IF_IPADDR) }; + struct ip_addr netmask = { htonl(IF_NETMASK) }; + struct ip_addr gw = { 0 }; + char *ip = NULL; + + tprintk("Waiting for network.\n"); + + dev = init_netfront(NULL, NULL, rawmac, &ip); + + if (ip) { + ipaddr.addr = inet_addr(ip); + if (IN_CLASSA(ntohl(ipaddr.addr))) + netmask.addr = htonl(IN_CLASSA_NET); + else if (IN_CLASSB(ntohl(ipaddr.addr))) + netmask.addr = htonl(IN_CLASSB_NET); + else if (IN_CLASSC(ntohl(ipaddr.addr))) + netmask.addr = htonl(IN_CLASSC_NET); + else + tprintk("Strange IP %s, leaving netmask to 0.\n", ip); + } + tprintk("IP %x netmask %x gateway %x.\n", + ntohl(ipaddr.addr), ntohl(netmask.addr), ntohl(gw.addr)); + + tprintk("TCP/IP bringup begins.\n"); + + netif = xmalloc(struct netif); + tcpip_init(tcpip_bringup_finished, netif); + + netif_add(netif, &ipaddr, &netmask, &gw, rawmac, + netif_netfront_init, ip_input); + netif_set_default(netif); + netif_set_up(netif); + + down(&tcpip_is_up); + + tprintk("Network is ready.\n"); +} + +/* Shut down the network */ +void stop_networking(void) +{ + if (dev) + shutdown_netfront(dev); +} diff --git a/extras/mini-os/main.c b/extras/mini-os/main.c new file mode 100644 index 0000000..3289c63 --- /dev/null +++ b/extras/mini-os/main.c @@ -0,0 +1,195 @@ +/* + * POSIX-compatible main layer + * + * Samuel Thibault , October 2007 + */ + +#ifdef HAVE_LIBC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int main(int argc, char *argv[], char *envp[]); +extern void __libc_init_array(void); +extern void __libc_fini_array(void); +extern unsigned long __CTOR_LIST__[]; +extern unsigned long __DTOR_LIST__[]; + +#if 0 +#include +int main(int argc, char *argv[], char *envp[]) +{ + printf("Hello, World!\n"); + return 1; +} +#endif + +void _init(void) +{ +} + +void _fini(void) +{ +} + +extern char __app_bss_start, __app_bss_end; +static void call_main(void *p) +{ + char *c, quote; +#ifdef CONFIG_QEMU + char *domargs, *msg; +#endif + int argc; + char **argv; + char *envp[] = { NULL }; +#ifdef CONFIG_QEMU + char *vm; + char path[128]; + int domid; +#endif + int i; + + /* Let other parts initialize (including console output) before maybe + * crashing. */ + //sleep(1); + +#ifndef CONFIG_GRUB + sparse((unsigned long) &__app_bss_start, &__app_bss_end - &__app_bss_start); +#ifdef HAVE_LWIP + start_networking(); +#endif + init_fs_frontend(); +#endif + +#ifdef CONFIG_QEMU + if (!fs_import) { + printk("No FS backend found, is it running?\n"); + do_exit(); + } + + /* Fetch argc, argv from XenStore */ + domid = xenbus_read_integer("target"); + if (domid == -1) { + printk("Couldn't read target\n"); + do_exit(); + } + + snprintf(path, sizeof(path), "/local/domain/%d/vm", domid); + msg = xenbus_read(XBT_NIL, path, &vm); + if (msg) { + printk("Couldn't read vm path\n"); + do_exit(); + } + printk("dom vm is at %s\n", vm); + + snprintf(path, sizeof(path), "%s/image/dmargs", vm); + free(vm); + msg = xenbus_read(XBT_NIL, path, &domargs); + + if (msg) { + printk("Couldn't get stubdom args: %s\n", msg); + domargs = strdup(""); + } +#endif + + argc = 1; + +#define PARSE_ARGS(ARGS,START,QUOTE,END) \ + c = ARGS; \ + quote = 0; \ + while (*c) { \ + if (*c != ' ') { \ + START; \ + while (*c) { \ + if (quote) { \ + if (*c == quote) { \ + quote = 0; \ + QUOTE; \ + continue; \ + } \ + } else if (*c == ' ') \ + break; \ + if (*c == '"' || *c == '\'') { \ + quote = *c; \ + QUOTE; \ + continue; \ + } \ + c++; \ + } \ + } else { \ + END; \ + while (*c == ' ') \ + c++; \ + } \ + } \ + if (quote) {\ + printk("Warning: unterminated quotation %c\n", quote); \ + quote = 0; \ + } +#define PARSE_ARGS_COUNT(ARGS) PARSE_ARGS(ARGS, argc++, c++, ) +#define PARSE_ARGS_STORE(ARGS) PARSE_ARGS(ARGS, argv[argc++] = c, memmove(c, c + 1, strlen(c + 1) + 1), *c++ = 0) + + PARSE_ARGS_COUNT((char*)start_info.cmd_line); +#ifdef CONFIG_QEMU + PARSE_ARGS_COUNT(domargs); +#endif + + argv = alloca((argc + 1) * sizeof(char *)); + argv[0] = "main"; + argc = 1; + + PARSE_ARGS_STORE((char*)start_info.cmd_line) +#ifdef CONFIG_QEMU + PARSE_ARGS_STORE(domargs) +#endif + + argv[argc] = NULL; + + for (i = 0; i < argc; i++) + printf("\"%s\" ", argv[i]); + printf("\n"); + + __libc_init_array(); + environ = envp; + for (i = 1; i <= __CTOR_LIST__[0]; i++) + ((void((*)(void)))__CTOR_LIST__[i]) (); + tzset(); + + exit(main(argc, argv, envp)); +} + +void _exit(int ret) +{ + int i; + + for (i = 1; i <= __DTOR_LIST__[0]; i++) + ((void((*)(void)))__DTOR_LIST__[i]) (); + close_all_files(); + __libc_fini_array(); + printk("main returned %d\n", ret); +#ifdef HAVE_LWIP + stop_networking(); +#endif + stop_kernel(); + if (!ret) { + /* No problem, just shutdown. */ + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_poweroff }; + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + } + do_exit(); +} + +int app_main(start_info_t *si) +{ + printk("Dummy main: start_info=%p\n", si); + main_thread = create_thread("main", call_main, si); + return 0; +} +#endif diff --git a/extras/mini-os/minios.mk b/extras/mini-os/minios.mk new file mode 100644 index 0000000..7ee19b3 --- /dev/null +++ b/extras/mini-os/minios.mk @@ -0,0 +1,73 @@ +# +# The file contains the common make rules for building mini-os. +# + +debug = y + +# Define some default flags. +# NB. '-Wcast-qual' is nasty, so I omitted it. +DEF_CFLAGS += -fno-builtin -Wall -Werror -Wredundant-decls -Wno-format -Wno-redundant-decls +DEF_CFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) +DEF_CFLAGS += $(call cc-option,$(CC),-fgnu89-inline) +DEF_CFLAGS += -Wstrict-prototypes -Wnested-externs -Wpointer-arith -Winline +DEF_CPPFLAGS += -D__XEN_INTERFACE_VERSION__=$(XEN_INTERFACE_VERSION) + +DEF_ASFLAGS += -D__ASSEMBLY__ +DEF_LDFLAGS += + +ifeq ($(debug),y) +DEF_CFLAGS += -g +#DEF_CFLAGS += -DMM_DEBUG +#DEF_CFLAGS += -DFS_DEBUG +#DEF_CFLAGS += -DLIBC_DEBUG +DEF_CFLAGS += -DGNT_DEBUG +DEF_CFLAGS += -DGNTMAP_DEBUG +else +DEF_CFLAGS += -O3 +endif + +# Build the CFLAGS and ASFLAGS for compiling and assembling. +# DEF_... flags are the common mini-os flags, +# ARCH_... flags may be defined in arch/$(TARGET_ARCH_FAM/rules.mk +CFLAGS := $(DEF_CFLAGS) $(ARCH_CFLAGS) +CPPFLAGS := $(DEF_CPPFLAGS) $(ARCH_CPPFLAGS) +ASFLAGS := $(DEF_ASFLAGS) $(ARCH_ASFLAGS) +LDFLAGS := $(DEF_LDFLAGS) $(ARCH_LDFLAGS) + +# Special build dependencies. +# Rebuild all after touching this/these file(s) +EXTRA_DEPS = $(MINI-OS_ROOT)/minios.mk \ + $(MINI-OS_ROOT)/$(TARGET_ARCH_DIR)/arch.mk + +# Find all header files for checking dependencies. +HDRS := $(wildcard $(MINI-OS_ROOT)/include/*.h) +HDRS += $(wildcard $(MINI-OS_ROOT)/include/xen/*.h) +HDRS += $(wildcard $(ARCH_INC)/*.h) +# For special wanted header directories. +extra_heads := $(foreach dir,$(EXTRA_INC),$(wildcard $(dir)/*.h)) +HDRS += $(extra_heads) + +# Add the special header directories to the include paths. +override CPPFLAGS := $(CPPFLAGS) $(extra_incl) + +# The name of the architecture specific library. +# This is on x86_32: libx86_32.a +# $(ARCH_LIB) has to built in the architecture specific directory. +ARCH_LIB_NAME = $(XEN_TARGET_ARCH) +ARCH_LIB := lib$(ARCH_LIB_NAME).a + +# This object contains the entrypoint for startup from Xen. +# $(HEAD_ARCH_OBJ) has to be built in the architecture specific directory. +HEAD_ARCH_OBJ := $(XEN_TARGET_ARCH).o +HEAD_OBJ := $(OBJ_DIR)/$(TARGET_ARCH_DIR)/$(HEAD_ARCH_OBJ) + + +$(OBJ_DIR)/%.o: %.c $(HDRS) Makefile $(EXTRA_DEPS) + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +$(OBJ_DIR)/%.o: %.S $(HDRS) Makefile $(EXTRA_DEPS) + $(CC) $(ASFLAGS) $(CPPFLAGS) -c $< -o $@ + + + + diff --git a/extras/mini-os/mm.c b/extras/mini-os/mm.c new file mode 100644 index 0000000..a5338b0 --- /dev/null +++ b/extras/mini-os/mm.c @@ -0,0 +1,443 @@ +/* + **************************************************************************** + * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: mm.c + * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk) + * Changes: Grzegorz Milos + * + * Date: Aug 2003, chages Aug 2005 + * + * Environment: Xen Minimal OS + * Description: memory management related functions + * contains buddy page allocator from Xen. + * + **************************************************************************** + * 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 + +#ifdef MM_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=mm.c, line=%d) " _f "\n", __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + +/********************* + * ALLOCATION BITMAP + * One bit per page of memory. Bit set => page is allocated. + */ + +static unsigned long *alloc_bitmap; +#define PAGES_PER_MAPWORD (sizeof(unsigned long) * 8) + +#define allocated_in_map(_pn) \ +(alloc_bitmap[(_pn)/PAGES_PER_MAPWORD] & (1UL<<((_pn)&(PAGES_PER_MAPWORD-1)))) + +/* + * Hint regarding bitwise arithmetic in map_{alloc,free}: + * -(1<= n. + * (1<next == NULL) + +#define round_pgdown(_p) ((_p)&PAGE_MASK) +#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK) + +#ifdef MM_DEBUG +/* + * Prints allocation[0/1] for @nr_pages, starting at @start + * address (virtual). + */ +USED static void print_allocation(void *start, int nr_pages) +{ + unsigned long pfn_start = virt_to_pfn(start); + int count; + for(count = 0; count < nr_pages; count++) + if(allocated_in_map(pfn_start + count)) printk("1"); + else printk("0"); + + printk("\n"); +} + +/* + * Prints chunks (making them with letters) for @nr_pages starting + * at @start (virtual). + */ +USED static void print_chunks(void *start, int nr_pages) +{ + char chunks[1001], current='A'; + int order, count; + chunk_head_t *head; + unsigned long pfn_start = virt_to_pfn(start); + + memset(chunks, (int)'_', 1000); + if(nr_pages > 1000) + { + DEBUG("Can only pring 1000 pages. Increase buffer size."); + } + + for(order=0; order < FREELIST_SIZE; order++) + { + head = free_head[order]; + while(!FREELIST_EMPTY(head)) + { + for(count = 0; count < 1UL<< head->level; count++) + { + if(count + virt_to_pfn(head) - pfn_start < 1000) + chunks[count + virt_to_pfn(head) - pfn_start] = current; + } + head = head->next; + current++; + } + } + chunks[nr_pages] = '\0'; + printk("%s\n", chunks); +} +#endif + + +/* + * Initialise allocator, placing addresses [@min,@max] in free pool. + * @min and @max are PHYSICAL addresses. + */ +static void init_page_allocator(unsigned long min, unsigned long max) +{ + int i; + unsigned long range, bitmap_size; + chunk_head_t *ch; + chunk_tail_t *ct; + for ( i = 0; i < FREELIST_SIZE; i++ ) + { + free_head[i] = &free_tail[i]; + free_tail[i].pprev = &free_head[i]; + free_tail[i].next = NULL; + } + + min = round_pgup (min); + max = round_pgdown(max); + + /* Allocate space for the allocation bitmap. */ + bitmap_size = (max+1) >> (PAGE_SHIFT+3); + bitmap_size = round_pgup(bitmap_size); + alloc_bitmap = (unsigned long *)to_virt(min); + min += bitmap_size; + range = max - min; + + /* All allocated by default. */ + memset(alloc_bitmap, ~0, bitmap_size); + /* Free up the memory we've been given to play with. */ + map_free(PHYS_PFN(min), range>>PAGE_SHIFT); + + /* The buddy lists are addressed in high memory. */ + min = (unsigned long) to_virt(min); + max = (unsigned long) to_virt(max); + + while ( range != 0 ) + { + /* + * Next chunk is limited by alignment of min, but also + * must not be bigger than remaining range. + */ + for ( i = PAGE_SHIFT; (1UL<<(i+1)) <= range; i++ ) + if ( min & (1UL<level = i; + ch->next = free_head[i]; + ch->pprev = &free_head[i]; + ch->next->pprev = &ch->next; + free_head[i] = ch; + ct->level = i; + } +} + + +/* Allocate 2^@order contiguous pages. Returns a VIRTUAL address. */ +unsigned long alloc_pages(int order) +{ + int i; + chunk_head_t *alloc_ch, *spare_ch; + chunk_tail_t *spare_ct; + + + /* Find smallest order which can satisfy the request. */ + for ( i = order; i < FREELIST_SIZE; i++ ) { + if ( !FREELIST_EMPTY(free_head[i]) ) + break; + } + + if ( i == FREELIST_SIZE ) goto no_memory; + + /* Unlink a chunk. */ + alloc_ch = free_head[i]; + free_head[i] = alloc_ch->next; + alloc_ch->next->pprev = alloc_ch->pprev; + + /* We may have to break the chunk a number of times. */ + while ( i != order ) + { + /* Split into two equal parts. */ + i--; + spare_ch = (chunk_head_t *)((char *)alloc_ch + (1UL<<(i+PAGE_SHIFT))); + spare_ct = (chunk_tail_t *)((char *)spare_ch + (1UL<<(i+PAGE_SHIFT)))-1; + + /* Create new header for spare chunk. */ + spare_ch->level = i; + spare_ch->next = free_head[i]; + spare_ch->pprev = &free_head[i]; + spare_ct->level = i; + + /* Link in the spare chunk. */ + spare_ch->next->pprev = &spare_ch->next; + free_head[i] = spare_ch; + } + + map_alloc(PHYS_PFN(to_phys(alloc_ch)), 1UL<level != order) + break; + + /* Merge with predecessor */ + freed_ch = to_merge_ch; + } + else + { + to_merge_ch = (chunk_head_t *)((char *)freed_ch + mask); + if(allocated_in_map(virt_to_pfn(to_merge_ch)) || + to_merge_ch->level != order) + break; + + /* Merge with successor */ + freed_ct = (chunk_tail_t *)((char *)to_merge_ch + mask) - 1; + } + + /* We are commited to merging, unlink the chunk */ + *(to_merge_ch->pprev) = to_merge_ch->next; + to_merge_ch->next->pprev = to_merge_ch->pprev; + + order++; + } + + /* Link the new chunk */ + freed_ch->level = order; + freed_ch->next = free_head[order]; + freed_ch->pprev = &free_head[order]; + freed_ct->level = order; + + freed_ch->next->pprev = &freed_ch->next; + free_head[order] = freed_ch; + +} + +#ifndef __ia64__ +int free_physical_pages(xen_pfn_t *mfns, int n) +{ + struct xen_memory_reservation reservation; + + set_xen_guest_handle(reservation.extent_start, mfns); + reservation.nr_extents = n; + reservation.extent_order = 0; + reservation.domid = DOMID_SELF; + return HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); +} +#endif + +#ifdef HAVE_LIBC +void *sbrk(ptrdiff_t increment) +{ + unsigned long old_brk = brk; + unsigned long new_brk = old_brk + increment; + + if (new_brk > heap_end) { + printk("Heap exhausted: %p + %lx = %p > %p\n", old_brk, increment, new_brk, heap_end); + return NULL; + } + + if (new_brk > heap_mapped) { + unsigned long n = (new_brk - heap_mapped + PAGE_SIZE - 1) / PAGE_SIZE; + do_map_zero(heap_mapped, n); + heap_mapped += n * PAGE_SIZE; + } + + brk = new_brk; + + return (void *) old_brk; +} +#endif + + + +void init_mm(void) +{ + + unsigned long start_pfn, max_pfn; + + printk("MM: Init\n"); + + arch_init_mm(&start_pfn, &max_pfn); + /* + * now we can initialise the page allocator + */ + printk("MM: Initialise page allocator for %lx(%lx)-%lx(%lx)\n", + (u_long)to_virt(PFN_PHYS(start_pfn)), PFN_PHYS(start_pfn), + (u_long)to_virt(PFN_PHYS(max_pfn)), PFN_PHYS(max_pfn)); + init_page_allocator(PFN_PHYS(start_pfn), PFN_PHYS(max_pfn)); + printk("MM: done\n"); + + arch_init_p2m(max_pfn); + + arch_init_demand_mapping_area(max_pfn); +} + +void fini_mm(void) +{ +} + +void sanity_check(void) +{ + int x; + chunk_head_t *head; + + for (x = 0; x < FREELIST_SIZE; x++) { + for (head = free_head[x]; !FREELIST_EMPTY(head); head = head->next) { + ASSERT(!allocated_in_map(virt_to_pfn(head))); + if (head->next) + ASSERT(head->next->pprev == &head->next); + } + if (free_head[x]) { + ASSERT(free_head[x]->pprev == &free_head[x]); + } + } +} diff --git a/extras/mini-os/netfront.c b/extras/mini-os/netfront.c new file mode 100644 index 0000000..824c42a --- /dev/null +++ b/extras/mini-os/netfront.c @@ -0,0 +1,625 @@ +/* Minimal network driver for Mini-OS. + * Copyright (c) 2006-2007 Jacob Gorm Hansen, University of Copenhagen. + * Based on netfront.c from Xen Linux. + * + * Does not handle fragments or extras. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_WAIT_QUEUE_HEAD(netfront_queue); + +#ifdef HAVE_LIBC +#define NETIF_SELECT_RX ((void*)-1) +#endif + + + +#define NET_TX_RING_SIZE __RING_SIZE((struct netif_tx_sring *)0, PAGE_SIZE) +#define NET_RX_RING_SIZE __RING_SIZE((struct netif_rx_sring *)0, PAGE_SIZE) +#define GRANT_INVALID_REF 0 + + +struct net_buffer { + void* page; + grant_ref_t gref; +}; + +struct netfront_dev { + domid_t dom; + + unsigned short tx_freelist[NET_TX_RING_SIZE + 1]; + struct semaphore tx_sem; + + struct net_buffer rx_buffers[NET_RX_RING_SIZE]; + struct net_buffer tx_buffers[NET_TX_RING_SIZE]; + + struct netif_tx_front_ring tx; + struct netif_rx_front_ring rx; + grant_ref_t tx_ring_ref; + grant_ref_t rx_ring_ref; + evtchn_port_t evtchn; + + char *nodename; + char *backend; + char *mac; + + xenbus_event_queue events; + +#ifdef HAVE_LIBC + int fd; + unsigned char *data; + size_t len; + size_t rlen; +#endif + + void (*netif_rx)(unsigned char* data, int len); +}; + +void init_rx_buffers(struct netfront_dev *dev); + +static inline void add_id_to_freelist(unsigned int id,unsigned short* freelist) +{ + freelist[id + 1] = freelist[0]; + freelist[0] = id; +} + +static inline unsigned short get_id_from_freelist(unsigned short* freelist) +{ + unsigned int id = freelist[0]; + freelist[0] = freelist[id + 1]; + return id; +} + +__attribute__((weak)) void netif_rx(unsigned char* data,int len) +{ + printk("%d bytes incoming at %p\n",len,data); +} + +__attribute__((weak)) void net_app_main(void*si,unsigned char*mac) {} + +static inline int xennet_rxidx(RING_IDX idx) +{ + return idx & (NET_RX_RING_SIZE - 1); +} + +void network_rx(struct netfront_dev *dev) +{ + RING_IDX rp,cons,req_prod; + struct netif_rx_response *rx; + int nr_consumed, some, more, i, notify; + + +moretodo: + rp = dev->rx.sring->rsp_prod; + rmb(); /* Ensure we see queued responses up to 'rp'. */ + cons = dev->rx.rsp_cons; + + nr_consumed = 0; + some = 0; + while ((cons != rp) && !some) + { + struct net_buffer* buf; + unsigned char* page; + int id; + + rx = RING_GET_RESPONSE(&dev->rx, cons); + + if (rx->flags & NETRXF_extra_info) + { + printk("+++++++++++++++++++++ we have extras!\n"); + continue; + } + + + if (rx->status == NETIF_RSP_NULL) continue; + + id = rx->id; + BUG_ON(id >= NET_TX_RING_SIZE); + + buf = &dev->rx_buffers[id]; + page = (unsigned char*)buf->page; + gnttab_end_access(buf->gref); + + if(rx->status>0) + { +#ifdef HAVE_LIBC + if (dev->netif_rx == NETIF_SELECT_RX) { + int len = rx->status; + ASSERT(current == main_thread); + if (len > dev->len) + len = dev->len; + memcpy(dev->data, page+rx->offset, len); + dev->rlen = len; + some = 1; + } else +#endif + dev->netif_rx(page+rx->offset,rx->status); + } + + nr_consumed++; + + ++cons; + } + dev->rx.rsp_cons=cons; + + RING_FINAL_CHECK_FOR_RESPONSES(&dev->rx,more); + if(more && !some) goto moretodo; + + req_prod = dev->rx.req_prod_pvt; + + for(i=0; irx, req_prod + i); + struct net_buffer* buf = &dev->rx_buffers[id]; + void* page = buf->page; + + /* We are sure to have free gnttab entries since they got released above */ + buf->gref = req->gref = + gnttab_grant_access(dev->dom,virt_to_mfn(page),0); + + req->id = id; + } + + wmb(); + + dev->rx.req_prod_pvt = req_prod + i; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->rx, notify); + if (notify) + notify_remote_via_evtchn(dev->evtchn); + +} + +void network_tx_buf_gc(struct netfront_dev *dev) +{ + + + RING_IDX cons, prod; + unsigned short id; + + do { + prod = dev->tx.sring->rsp_prod; + rmb(); /* Ensure we see responses up to 'rp'. */ + + for (cons = dev->tx.rsp_cons; cons != prod; cons++) + { + struct netif_tx_response *txrsp; + struct net_buffer *buf; + + txrsp = RING_GET_RESPONSE(&dev->tx, cons); + if (txrsp->status == NETIF_RSP_NULL) + continue; + + if (txrsp->status == NETIF_RSP_ERROR) + printk("packet error\n"); + + id = txrsp->id; + BUG_ON(id >= NET_TX_RING_SIZE); + buf = &dev->tx_buffers[id]; + gnttab_end_access(buf->gref); + buf->gref=GRANT_INVALID_REF; + + add_id_to_freelist(id,dev->tx_freelist); + up(&dev->tx_sem); + } + + dev->tx.rsp_cons = prod; + + /* + * Set a new event, then check for race with update of tx_cons. + * Note that it is essential to schedule a callback, no matter + * how few tx_buffers are pending. Even if there is space in the + * transmit ring, higher layers may be blocked because too much + * data is outstanding: in such cases notification from Xen is + * likely to be the only kick that we'll get. + */ + dev->tx.sring->rsp_event = + prod + ((dev->tx.sring->req_prod - prod) >> 1) + 1; + mb(); + } while ((cons == prod) && (prod != dev->tx.sring->rsp_prod)); + + +} + +void netfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ + int flags; + struct netfront_dev *dev = data; + + local_irq_save(flags); + + network_tx_buf_gc(dev); + network_rx(dev); + + local_irq_restore(flags); +} + +#ifdef HAVE_LIBC +void netfront_select_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ + int flags; + struct netfront_dev *dev = data; + int fd = dev->fd; + + local_irq_save(flags); + network_tx_buf_gc(dev); + local_irq_restore(flags); + + if (fd != -1) + files[fd].read = 1; + wake_up(&netfront_queue); +} +#endif + +static void free_netfront(struct netfront_dev *dev) +{ + int i; + + for(i=0;itx_sem); + + mask_evtchn(dev->evtchn); + + free(dev->mac); + free(dev->backend); + + gnttab_end_access(dev->rx_ring_ref); + gnttab_end_access(dev->tx_ring_ref); + + free_page(dev->rx.sring); + free_page(dev->tx.sring); + + unbind_evtchn(dev->evtchn); + + for(i=0;irx_buffers[i].gref); + free_page(dev->rx_buffers[i].page); + } + + for(i=0;itx_buffers[i].page) + free_page(dev->tx_buffers[i].page); + + free(dev->nodename); + free(dev); +} + +struct netfront_dev *init_netfront(char *_nodename, void (*thenetif_rx)(unsigned char* data, int len), unsigned char rawmac[6], char **ip) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + int retry=0; + int i; + char* msg; + char* nodename = _nodename ? _nodename : "device/vif/0"; + + struct netfront_dev *dev; + + char path[strlen(nodename) + 1 + 10 + 1]; + + if (!thenetif_rx) + thenetif_rx = netif_rx; + + printk("************************ NETFRONT for %s **********\n\n\n", nodename); + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->nodename = strdup(nodename); +#ifdef HAVE_LIBC + dev->fd = -1; +#endif + + printk("net TX ring size %d\n", NET_TX_RING_SIZE); + printk("net RX ring size %d\n", NET_RX_RING_SIZE); + init_SEMAPHORE(&dev->tx_sem, NET_TX_RING_SIZE); + for(i=0;itx_freelist); + dev->tx_buffers[i].page = NULL; + } + + for(i=0;irx_buffers[i].page = (char*)alloc_page(); + } + + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dev->dom = xenbus_read_integer(path); +#ifdef HAVE_LIBC + if (thenetif_rx == NETIF_SELECT_RX) + evtchn_alloc_unbound(dev->dom, netfront_select_handler, dev, &dev->evtchn); + else +#endif + evtchn_alloc_unbound(dev->dom, netfront_handler, dev, &dev->evtchn); + + txs = (struct netif_tx_sring *) alloc_page(); + rxs = (struct netif_rx_sring *) alloc_page(); + memset(txs,0,PAGE_SIZE); + memset(rxs,0,PAGE_SIZE); + + + SHARED_RING_INIT(txs); + SHARED_RING_INIT(rxs); + FRONT_RING_INIT(&dev->tx, txs, PAGE_SIZE); + FRONT_RING_INIT(&dev->rx, rxs, PAGE_SIZE); + + dev->tx_ring_ref = gnttab_grant_access(dev->dom,virt_to_mfn(txs),0); + dev->rx_ring_ref = gnttab_grant_access(dev->dom,virt_to_mfn(rxs),0); + + init_rx_buffers(dev); + + dev->netif_rx = thenetif_rx; + + dev->events = NULL; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "tx-ring-ref","%u", + dev->tx_ring_ref); + if (err) { + message = "writing tx ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "rx-ring-ref","%u", + dev->rx_ring_ref); + if (err) { + message = "writing rx ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, + "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "request-rx-copy", "%u", 1); + + if (err) { + message = "writing request-rx-copy"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "state", "%u", + 4); /* connected */ + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + goto error; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + snprintf(path, sizeof(path), "%s/mac", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->mac); + + if ((dev->backend == NULL) || (dev->mac == NULL)) { + printk("%s: backend/mac failed\n", __func__); + goto error; + } + + printk("backend at %s\n",dev->backend); + printk("mac is %s\n",dev->mac); + + { + char path[strlen(dev->backend) + 1 + 5 + 1]; + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path_token(XBT_NIL, path, path, &dev->events); + + xenbus_wait_for_value(path, "4", &dev->events); + + if (ip) { + snprintf(path, sizeof(path), "%s/ip", dev->backend); + xenbus_read(XBT_NIL, path, ip); + } + } + + printk("**************************\n"); + + unmask_evtchn(dev->evtchn); + + /* Special conversion specifier 'hh' needed for __ia64__. Without + this mini-os panics with 'Unaligned reference'. */ + if (rawmac) + sscanf(dev->mac,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &rawmac[0], + &rawmac[1], + &rawmac[2], + &rawmac[3], + &rawmac[4], + &rawmac[5]); + + return dev; +error: + free_netfront(dev); + return NULL; +} + +#ifdef HAVE_LIBC +int netfront_tap_open(char *nodename) { + struct netfront_dev *dev; + + dev = init_netfront(nodename, NETIF_SELECT_RX, NULL, NULL); + if (!dev) { + printk("TAP open failed\n"); + errno = EIO; + return -1; + } + dev->fd = alloc_fd(FTYPE_TAP); + printk("tap_open(%s) -> %d\n", nodename, dev->fd); + files[dev->fd].tap.dev = dev; + return dev->fd; +} +#endif + +void shutdown_netfront(struct netfront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + printk("close network: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path, "5", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path, "6", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 1); + xenbus_wait_for_value(path, "2", &dev->events); + + xenbus_unwatch_path(XBT_NIL, path); + + snprintf(path, sizeof(path), "%s/tx-ring-ref", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/rx-ring-ref", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/event-channel", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/request-rx-copy", nodename); + xenbus_rm(XBT_NIL, path); + + free_netfront(dev); +} + + +void init_rx_buffers(struct netfront_dev *dev) +{ + int i, requeue_idx; + netif_rx_request_t *req; + int notify; + + /* Rebuild the RX buffer freelist and the RX ring itself. */ + for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) + { + struct net_buffer* buf = &dev->rx_buffers[requeue_idx]; + req = RING_GET_REQUEST(&dev->rx, requeue_idx); + + buf->gref = req->gref = + gnttab_grant_access(dev->dom,virt_to_mfn(buf->page),0); + + req->id = requeue_idx; + + requeue_idx++; + } + + dev->rx.req_prod_pvt = requeue_idx; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->rx, notify); + + if (notify) + notify_remote_via_evtchn(dev->evtchn); + + dev->rx.sring->rsp_event = dev->rx.rsp_cons + 1; +} + + +void netfront_xmit(struct netfront_dev *dev, unsigned char* data,int len) +{ + int flags; + struct netif_tx_request *tx; + RING_IDX i; + int notify; + unsigned short id; + struct net_buffer* buf; + void* page; + + BUG_ON(len > PAGE_SIZE); + + down(&dev->tx_sem); + + local_irq_save(flags); + id = get_id_from_freelist(dev->tx_freelist); + local_irq_restore(flags); + + buf = &dev->tx_buffers[id]; + page = buf->page; + if (!page) + page = buf->page = (char*) alloc_page(); + + i = dev->tx.req_prod_pvt; + tx = RING_GET_REQUEST(&dev->tx, i); + + memcpy(page,data,len); + + buf->gref = + tx->gref = gnttab_grant_access(dev->dom,virt_to_mfn(page),1); + + tx->offset=0; + tx->size = len; + tx->flags=0; + tx->id = id; + dev->tx.req_prod_pvt = i + 1; + + wmb(); + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->tx, notify); + + if(notify) notify_remote_via_evtchn(dev->evtchn); + + local_irq_save(flags); + network_tx_buf_gc(dev); + local_irq_restore(flags); +} + +#ifdef HAVE_LIBC +ssize_t netfront_receive(struct netfront_dev *dev, unsigned char *data, size_t len) +{ + unsigned long flags; + int fd = dev->fd; + ASSERT(current == main_thread); + + dev->rlen = 0; + dev->data = data; + dev->len = len; + + local_irq_save(flags); + network_rx(dev); + if (!dev->rlen && fd != -1) + /* No data for us, make select stop returning */ + files[fd].read = 0; + /* Before re-enabling the interrupts, in case a packet just arrived in the + * meanwhile. */ + local_irq_restore(flags); + + dev->data = NULL; + dev->len = 0; + + return dev->rlen; +} +#endif diff --git a/extras/mini-os/pcifront.c b/extras/mini-os/pcifront.c new file mode 100644 index 0000000..5b68d86 --- /dev/null +++ b/extras/mini-os/pcifront.c @@ -0,0 +1,371 @@ +/* Minimal PCI driver for Mini-OS. + * Copyright (c) 2007-2008 Samuel Thibault. + * Based on blkfront.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +DECLARE_WAIT_QUEUE_HEAD(pcifront_queue); + +struct pcifront_dev { + domid_t dom; + + struct xen_pci_sharedinfo *info; + grant_ref_t info_ref; + evtchn_port_t evtchn; + + char *nodename; + char *backend; + + xenbus_event_queue events; +}; + +void pcifront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ + wake_up(&pcifront_queue); +} + +static void free_pcifront(struct pcifront_dev *dev) +{ + mask_evtchn(dev->evtchn); + + free(dev->backend); + + gnttab_end_access(dev->info_ref); + free_page(dev->info); + + unbind_evtchn(dev->evtchn); + + free(dev->nodename); + free(dev); +} + +struct pcifront_dev *init_pcifront(char *_nodename) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + int retry=0; + char* msg; + char* nodename = _nodename ? _nodename : "device/pci/0"; + int dom; + + struct pcifront_dev *dev; + + char path[strlen(nodename) + 1 + 10 + 1]; + + printk("******************* PCIFRONT for %s **********\n\n\n", nodename); + + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dom = xenbus_read_integer(path); + if (dom == -1) { + printk("no backend\n"); + return NULL; + } + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->nodename = strdup(nodename); + dev->dom = dom; + + evtchn_alloc_unbound(dev->dom, pcifront_handler, dev, &dev->evtchn); + + dev->info = (struct xen_pci_sharedinfo*) alloc_page(); + memset(dev->info,0,PAGE_SIZE); + + dev->info_ref = gnttab_grant_access(dev->dom,virt_to_mfn(dev->info),0); + + dev->events = NULL; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "pci-op-ref","%u", + dev->info_ref); + if (err) { + message = "writing pci-op-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, + "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, + "magic", XEN_PCI_MAGIC); + if (err) { + message = "writing magic"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "state", "%u", + 3); /* initialised */ + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + goto error; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + if (msg) { + printk("Error %s when reading the backend path %s\n", msg, path); + goto error; + } + + printk("backend at %s\n", dev->backend); + + { + char path[strlen(dev->backend) + 1 + 5 + 1]; + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path_token(XBT_NIL, path, path, &dev->events); + + xenbus_wait_for_value(path, "4", &dev->events); + + xenbus_printf(xbt, nodename, "state", "%u", 4); /* connected */ + } + unmask_evtchn(dev->evtchn); + + printk("**************************\n"); + + return dev; + +error: + free_pcifront(dev); + return NULL; +} + +void pcifront_scan(struct pcifront_dev *dev, void (*func)(unsigned int domain, unsigned int bus, unsigned slot, unsigned int fun)) +{ + char path[strlen(dev->backend) + 1 + 5 + 10 + 1]; + int i, n; + char *s, *msg; + unsigned int domain, bus, slot, fun; + + snprintf(path, sizeof(path), "%s/num_devs", dev->backend); + n = xenbus_read_integer(path); + + for (i = 0; i < n; i++) { + snprintf(path, sizeof(path), "%s/vdev-%d", dev->backend, i); + msg = xenbus_read(XBT_NIL, path, &s); + if (msg) { + printk("Error %s when reading the PCI root name at %s\n", path); + continue; + } + + if (sscanf(s, "%x:%x:%x.%x", &domain, &bus, &slot, &fun) != 4) { + printk("\"%s\" does not look like a PCI device address\n", s); + free(s); + continue; + } + free(s); + + func(domain, bus, slot, fun); + } +} + +void shutdown_pcifront(struct pcifront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + printk("close pci: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path, "5", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path, "6", &dev->events); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 1); + xenbus_wait_for_value(path, "2", &dev->events); + + xenbus_unwatch_path(XBT_NIL, path); + + snprintf(path, sizeof(path), "%s/info-ref", nodename); + xenbus_rm(XBT_NIL, path); + snprintf(path, sizeof(path), "%s/event-channel", nodename); + xenbus_rm(XBT_NIL, path); + + free_pcifront(dev); +} + + +void pcifront_op(struct pcifront_dev *dev, struct xen_pci_op *op) +{ + dev->info->op = *op; + /* Make sure info is written before the flag */ + wmb(); + set_bit(_XEN_PCIF_active, (void*) &dev->info->flags); + notify_remote_via_evtchn(dev->evtchn); + + wait_event(pcifront_queue, !test_bit(_XEN_PCIF_active, (void*) &dev->info->flags)); + + /* Make sure flag is read before info */ + rmb(); + *op = dev->info->op; +} + +int pcifront_conf_read(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun, + unsigned int off, unsigned int size, unsigned int *val) +{ + struct xen_pci_op op; + + memset(&op, 0, sizeof(op)); + + op.cmd = XEN_PCI_OP_conf_read; + op.domain = dom; + op.bus = bus; + op.devfn = PCI_DEVFN(slot, fun); + op.offset = off; + op.size = size; + + pcifront_op(dev, &op); + + if (op.err) + return op.err; + + *val = op.value; + + return 0; +} + +int pcifront_conf_write(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun, + unsigned int off, unsigned int size, unsigned int val) +{ + struct xen_pci_op op; + + memset(&op, 0, sizeof(op)); + + op.cmd = XEN_PCI_OP_conf_write; + op.domain = dom; + op.bus = bus; + op.devfn = PCI_DEVFN(slot, fun); + op.offset = off; + op.size = size; + + op.value = val; + + pcifront_op(dev, &op); + + return op.err; +} + +int pcifront_enable_msi(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun) +{ + struct xen_pci_op op; + + memset(&op, 0, sizeof(op)); + + op.cmd = XEN_PCI_OP_enable_msi; + op.domain = dom; + op.bus = bus; + op.devfn = PCI_DEVFN(slot, fun); + + pcifront_op(dev, &op); + + if (op.err) + return op.err; + else + return op.value; +} + +int pcifront_disable_msi(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun) +{ + struct xen_pci_op op; + + memset(&op, 0, sizeof(op)); + + op.cmd = XEN_PCI_OP_disable_msi; + op.domain = dom; + op.bus = bus; + op.devfn = PCI_DEVFN(slot, fun); + + pcifront_op(dev, &op); + + return op.err; +} + +int pcifront_enable_msix(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun, + struct xen_msix_entry *entries, int n) +{ + struct xen_pci_op op; + + if (n > SH_INFO_MAX_VEC) + return XEN_PCI_ERR_op_failed; + + memset(&op, 0, sizeof(op)); + + op.cmd = XEN_PCI_OP_enable_msix; + op.domain = dom; + op.bus = bus; + op.devfn = PCI_DEVFN(slot, fun); + op.value = n; + + memcpy(op.msix_entries, entries, n * sizeof(*entries)); + + pcifront_op(dev, &op); + + if (op.err) + return op.err; + + memcpy(entries, op.msix_entries, n * sizeof(*entries)); + + return 0; +} + + +int pcifront_disable_msix(struct pcifront_dev *dev, + unsigned int dom, + unsigned int bus, unsigned int slot, unsigned long fun) +{ + struct xen_pci_op op; + + memset(&op, 0, sizeof(op)); + + op.cmd = XEN_PCI_OP_disable_msix; + op.domain = dom; + op.bus = bus; + op.devfn = PCI_DEVFN(slot, fun); + + pcifront_op(dev, &op); + + return op.err; +} diff --git a/extras/mini-os/sched.c b/extras/mini-os/sched.c new file mode 100644 index 0000000..ab454cd --- /dev/null +++ b/extras/mini-os/sched.c @@ -0,0 +1,301 @@ +/* + **************************************************************************** + * (C) 2005 - Grzegorz Milos - Intel Research Cambridge + **************************************************************************** + * + * File: sched.c + * Author: Grzegorz Milos + * Changes: Robert Kaiser + * + * Date: Aug 2005 + * + * Environment: Xen Minimal OS + * Description: simple scheduler for Mini-Os + * + * The scheduler is non-preemptive (cooperative), and schedules according + * to Round Robin algorithm. + * + **************************************************************************** + * 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 + + +#ifdef SCHED_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=sched.c, line=%d) " _f "\n", __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + +struct thread *idle_thread = NULL; +MINIOS_LIST_HEAD(exited_threads); +static int threads_started; + +struct thread *main_thread; + +void inline print_runqueue(void) +{ + struct minios_list_head *it; + struct thread *th; + minios_list_for_each(it, &idle_thread->thread_list) + { + th = minios_list_entry(it, struct thread, thread_list); + printk(" Thread \"%s\", runnable=%d\n", th->name, is_runnable(th)); + } + printk("\n"); +} + +void schedule(void) +{ + struct thread *prev, *next, *thread; + struct minios_list_head *iterator, *next_iterator; + unsigned long flags; + + prev = current; + local_irq_save(flags); + + if (in_callback) { + printk("Must not call schedule() from a callback\n"); + BUG(); + } + if (flags) { + printk("Must not call schedule() with IRQs disabled\n"); + BUG(); + } + + do { + /* Examine all threads. + Find a runnable thread, but also wake up expired ones and find the + time when the next timeout expires, else use 10 seconds. */ + s_time_t now = NOW(); + s_time_t min_wakeup_time = now + SECONDS(10); + next = NULL; + minios_list_for_each_safe(iterator, next_iterator, &idle_thread->thread_list) + { + thread = minios_list_entry(iterator, struct thread, thread_list); + if (!is_runnable(thread) && thread->wakeup_time != 0LL) + { + if (thread->wakeup_time <= now) + wake(thread); + else if (thread->wakeup_time < min_wakeup_time) + min_wakeup_time = thread->wakeup_time; + } + if(is_runnable(thread)) + { + next = thread; + /* Put this thread on the end of the list */ + minios_list_del(&thread->thread_list); + minios_list_add_tail(&thread->thread_list, &idle_thread->thread_list); + break; + } + } + if (next) + break; + /* block until the next timeout expires, or for 10 secs, whichever comes first */ + block_domain(min_wakeup_time); + /* handle pending events if any */ + force_evtchn_callback(); + } while(1); + local_irq_restore(flags); + /* Interrupting the switch is equivalent to having the next thread + inturrupted at the return instruction. And therefore at safe point. */ + if(prev != next) switch_threads(prev, next); + + minios_list_for_each_safe(iterator, next_iterator, &exited_threads) + { + thread = minios_list_entry(iterator, struct thread, thread_list); + if(thread != prev) + { + minios_list_del(&thread->thread_list); + free_pages(thread->stack, STACK_SIZE_PAGE_ORDER); + xfree(thread); + } + } +} + +struct thread* create_thread(char *name, void (*function)(void *), void *data) +{ + struct thread *thread; + unsigned long flags; + /* Call architecture specific setup. */ + thread = arch_create_thread(name, function, data); + /* Not runable, not exited, not sleeping */ + thread->flags = 0; + thread->wakeup_time = 0LL; +#ifdef HAVE_LIBC + _REENT_INIT_PTR((&thread->reent)) +#endif + set_runnable(thread); + local_irq_save(flags); + if(idle_thread != NULL) { + minios_list_add_tail(&thread->thread_list, &idle_thread->thread_list); + } else if(function != idle_thread_fn) + { + printk("BUG: Not allowed to create thread before initialising scheduler.\n"); + BUG(); + } + local_irq_restore(flags); + return thread; +} + +#ifdef HAVE_LIBC +static struct _reent callback_reent; +struct _reent *__getreent(void) +{ + struct _reent *_reent; + + if (!threads_started) + _reent = _impure_ptr; + else if (in_callback) + _reent = &callback_reent; + else + _reent = &get_current()->reent; + +#ifndef NDEBUG +#if defined(__x86_64__) || defined(__x86__) + { +#ifdef __x86_64__ + register unsigned long sp asm ("rsp"); +#else + register unsigned long sp asm ("esp"); +#endif + if ((sp & (STACK_SIZE-1)) < STACK_SIZE / 16) { + static int overflowing; + if (!overflowing) { + overflowing = 1; + printk("stack overflow\n"); + BUG(); + } + } + } +#endif +#endif + return _reent; +} +#endif + +void exit_thread(void) +{ + unsigned long flags; + struct thread *thread = current; + printk("Thread \"%s\" exited.\n", thread->name); + local_irq_save(flags); + /* Remove from the thread list */ + minios_list_del(&thread->thread_list); + clear_runnable(thread); + /* Put onto exited list */ + minios_list_add(&thread->thread_list, &exited_threads); + local_irq_restore(flags); + /* Schedule will free the resources */ + while(1) + { + schedule(); + printk("schedule() returned! Trying again\n"); + } +} + +void block(struct thread *thread) +{ + thread->wakeup_time = 0LL; + clear_runnable(thread); +} + +void msleep(u32 millisecs) +{ + struct thread *thread = get_current(); + thread->wakeup_time = NOW() + MILLISECS(millisecs); + clear_runnable(thread); + schedule(); +} + +void wake(struct thread *thread) +{ + thread->wakeup_time = 0LL; + set_runnable(thread); +} + +void idle_thread_fn(void *unused) +{ + threads_started = 1; + while (1) { + block(current); + schedule(); + } +} + +DECLARE_MUTEX(mutex); + +void th_f1(void *data) +{ + struct timeval tv1, tv2; + + for(;;) + { + down(&mutex); + printk("Thread \"%s\" got semaphore, runnable %d\n", current->name, is_runnable(current)); + schedule(); + printk("Thread \"%s\" releases the semaphore\n", current->name); + up(&mutex); + + + gettimeofday(&tv1, NULL); + for(;;) + { + gettimeofday(&tv2, NULL); + if(tv2.tv_sec - tv1.tv_sec > 2) break; + } + + + schedule(); + } +} + +void th_f2(void *data) +{ + for(;;) + { + printk("Thread OTHER executing, data 0x%lx\n", data); + schedule(); + } +} + + + +void init_sched(void) +{ + printk("Initialising scheduler\n"); + +#ifdef HAVE_LIBC + _REENT_INIT_PTR((&callback_reent)) +#endif + idle_thread = create_thread("Idle", idle_thread_fn, NULL); + MINIOS_INIT_LIST_HEAD(&idle_thread->thread_list); +} + diff --git a/extras/mini-os/xenbus/xenbus.c b/extras/mini-os/xenbus/xenbus.c new file mode 100644 index 0000000..7b9c35a --- /dev/null +++ b/extras/mini-os/xenbus/xenbus.c @@ -0,0 +1,761 @@ +/* + **************************************************************************** + * (C) 2006 - Cambridge University + **************************************************************************** + * + * File: xenbus.c + * Author: Steven Smith (sos22@cam.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * Changes: John D. Ramsdell + * + * Date: Jun 2006, chages Aug 2005 + * + * Environment: Xen Minimal OS + * Description: Minimal implementation of xenbus + * + **************************************************************************** + **/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define min(x,y) ({ \ + typeof(x) tmpx = (x); \ + typeof(y) tmpy = (y); \ + tmpx < tmpy ? tmpx : tmpy; \ + }) + +#ifdef XENBUS_DEBUG +#define DEBUG(_f, _a...) \ + printk("MINI_OS(file=xenbus.c, line=%d) " _f , __LINE__, ## _a) +#else +#define DEBUG(_f, _a...) ((void)0) +#endif + +static struct xenstore_domain_interface *xenstore_buf; +static DECLARE_WAIT_QUEUE_HEAD(xb_waitq); +DECLARE_WAIT_QUEUE_HEAD(xenbus_watch_queue); + +xenbus_event_queue xenbus_events; +static struct watch { + char *token; + xenbus_event_queue *events; + struct watch *next; +} *watches; +struct xenbus_req_info +{ + int in_use:1; + struct wait_queue_head waitq; + void *reply; +}; + +#define NR_REQS 32 +static struct xenbus_req_info req_info[NR_REQS]; + +static void memcpy_from_ring(const void *Ring, + void *Dest, + int off, + int len) +{ + int c1, c2; + const char *ring = Ring; + char *dest = Dest; + c1 = min(len, XENSTORE_RING_SIZE - off); + c2 = len - c1; + memcpy(dest, ring + off, c1); + memcpy(dest + c1, ring, c2); +} + +char **xenbus_wait_for_watch_return(xenbus_event_queue *queue) +{ + struct xenbus_event *event; + DEFINE_WAIT(w); + if (!queue) + queue = &xenbus_events; + while (!(event = *queue)) { + add_waiter(w, xenbus_watch_queue); + schedule(); + } + remove_waiter(w); + *queue = event->next; + return &event->path; +} + +void xenbus_wait_for_watch(xenbus_event_queue *queue) +{ + char **ret; + if (!queue) + queue = &xenbus_events; + ret = xenbus_wait_for_watch_return(queue); + free(ret); +} + +char* xenbus_wait_for_value(const char* path, const char* value, xenbus_event_queue *queue) +{ + if (!queue) + queue = &xenbus_events; + for(;;) + { + char *res, *msg; + int r; + + msg = xenbus_read(XBT_NIL, path, &res); + if(msg) return msg; + + r = strcmp(value,res); + free(res); + + if(r==0) break; + else xenbus_wait_for_watch(queue); + } + return NULL; +} + + +static void xenbus_thread_func(void *ign) +{ + struct xsd_sockmsg msg; + unsigned prod = xenstore_buf->rsp_prod; + + for (;;) + { + wait_event(xb_waitq, prod != xenstore_buf->rsp_prod); + while (1) + { + prod = xenstore_buf->rsp_prod; + DEBUG("Rsp_cons %d, rsp_prod %d.\n", xenstore_buf->rsp_cons, + xenstore_buf->rsp_prod); + if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg)) + break; + rmb(); + memcpy_from_ring(xenstore_buf->rsp, + &msg, + MASK_XENSTORE_IDX(xenstore_buf->rsp_cons), + sizeof(msg)); + DEBUG("Msg len %d, %d avail, id %d.\n", + msg.len + sizeof(msg), + xenstore_buf->rsp_prod - xenstore_buf->rsp_cons, + msg.req_id); + if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < + sizeof(msg) + msg.len) + break; + + DEBUG("Message is good.\n"); + + if(msg.type == XS_WATCH_EVENT) + { + struct xenbus_event *event = malloc(sizeof(*event) + msg.len); + xenbus_event_queue *events = NULL; + char *data = (char*)event + sizeof(*event); + struct watch *watch; + + memcpy_from_ring(xenstore_buf->rsp, + data, + MASK_XENSTORE_IDX(xenstore_buf->rsp_cons + sizeof(msg)), + msg.len); + + event->path = data; + event->token = event->path + strlen(event->path) + 1; + + xenstore_buf->rsp_cons += msg.len + sizeof(msg); + + for (watch = watches; watch; watch = watch->next) + if (!strcmp(watch->token, event->token)) { + events = watch->events; + break; + } + + if (events) { + event->next = *events; + *events = event; + wake_up(&xenbus_watch_queue); + } else { + printk("unexpected watch token %s\n", event->token); + free(event); + } + } + + else + { + req_info[msg.req_id].reply = malloc(sizeof(msg) + msg.len); + memcpy_from_ring(xenstore_buf->rsp, + req_info[msg.req_id].reply, + MASK_XENSTORE_IDX(xenstore_buf->rsp_cons), + msg.len + sizeof(msg)); + xenstore_buf->rsp_cons += msg.len + sizeof(msg); + wake_up(&req_info[msg.req_id].waitq); + } + } + } +} + +static void xenbus_evtchn_handler(evtchn_port_t port, struct pt_regs *regs, + void *ign) +{ + wake_up(&xb_waitq); +} + +static int nr_live_reqs; +static spinlock_t req_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_WAIT_QUEUE_HEAD(req_wq); + +/* Release a xenbus identifier */ +static void release_xenbus_id(int id) +{ + BUG_ON(!req_info[id].in_use); + spin_lock(&req_lock); + req_info[id].in_use = 0; + nr_live_reqs--; + req_info[id].in_use = 0; + if (nr_live_reqs == NR_REQS - 1) + wake_up(&req_wq); + spin_unlock(&req_lock); +} + +/* Allocate an identifier for a xenbus request. Blocks if none are + available. */ +static int allocate_xenbus_id(void) +{ + static int probe; + int o_probe; + + while (1) + { + spin_lock(&req_lock); + if (nr_live_reqs < NR_REQS) + break; + spin_unlock(&req_lock); + wait_event(req_wq, (nr_live_reqs < NR_REQS)); + } + + o_probe = probe; + for (;;) + { + if (!req_info[o_probe].in_use) + break; + o_probe = (o_probe + 1) % NR_REQS; + BUG_ON(o_probe == probe); + } + nr_live_reqs++; + req_info[o_probe].in_use = 1; + probe = (o_probe + 1) % NR_REQS; + spin_unlock(&req_lock); + init_waitqueue_head(&req_info[o_probe].waitq); + + return o_probe; +} + +/* Initialise xenbus. */ +void init_xenbus(void) +{ + int err; + printk("Initialising xenbus\n"); + DEBUG("init_xenbus called.\n"); + xenstore_buf = mfn_to_virt(start_info.store_mfn); + create_thread("xenstore", xenbus_thread_func, NULL); + DEBUG("buf at %p.\n", xenstore_buf); + err = bind_evtchn(start_info.store_evtchn, + xenbus_evtchn_handler, + NULL); + unmask_evtchn(start_info.store_evtchn); + DEBUG("xenbus on irq %d\n", err); +} + +void fini_xenbus(void) +{ +} + +/* Send data to xenbus. This can block. All of the requests are seen + by xenbus as if sent atomically. The header is added + automatically, using type %type, req_id %req_id, and trans_id + %trans_id. */ +static void xb_write(int type, int req_id, xenbus_transaction_t trans_id, + const struct write_req *req, int nr_reqs) +{ + XENSTORE_RING_IDX prod; + int r; + int len = 0; + const struct write_req *cur_req; + int req_off; + int total_off; + int this_chunk; + struct xsd_sockmsg m = {.type = type, .req_id = req_id, + .tx_id = trans_id }; + struct write_req header_req = { &m, sizeof(m) }; + + for (r = 0; r < nr_reqs; r++) + len += req[r].len; + m.len = len; + len += sizeof(m); + + cur_req = &header_req; + + BUG_ON(len > XENSTORE_RING_SIZE); + /* Wait for the ring to drain to the point where we can send the + message. */ + prod = xenstore_buf->req_prod; + if (prod + len - xenstore_buf->req_cons > XENSTORE_RING_SIZE) + { + /* Wait for there to be space on the ring */ + DEBUG("prod %d, len %d, cons %d, size %d; waiting.\n", + prod, len, xenstore_buf->req_cons, XENSTORE_RING_SIZE); + wait_event(xb_waitq, + xenstore_buf->req_prod + len - xenstore_buf->req_cons <= + XENSTORE_RING_SIZE); + DEBUG("Back from wait.\n"); + prod = xenstore_buf->req_prod; + } + + /* We're now guaranteed to be able to send the message without + overflowing the ring. Do so. */ + total_off = 0; + req_off = 0; + while (total_off < len) + { + this_chunk = min(cur_req->len - req_off, + XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod)); + memcpy((char *)xenstore_buf->req + MASK_XENSTORE_IDX(prod), + (char *)cur_req->data + req_off, this_chunk); + prod += this_chunk; + req_off += this_chunk; + total_off += this_chunk; + if (req_off == cur_req->len) + { + req_off = 0; + if (cur_req == &header_req) + cur_req = req; + else + cur_req++; + } + } + + DEBUG("Complete main loop of xb_write.\n"); + BUG_ON(req_off != 0); + BUG_ON(total_off != len); + BUG_ON(prod > xenstore_buf->req_cons + XENSTORE_RING_SIZE); + + /* Remote must see entire message before updating indexes */ + wmb(); + + xenstore_buf->req_prod += len; + + /* Send evtchn to notify remote */ + notify_remote_via_evtchn(start_info.store_evtchn); +} + +/* Send a mesasge to xenbus, in the same fashion as xb_write, and + block waiting for a reply. The reply is malloced and should be + freed by the caller. */ +struct xsd_sockmsg * +xenbus_msg_reply(int type, + xenbus_transaction_t trans, + struct write_req *io, + int nr_reqs) +{ + int id; + DEFINE_WAIT(w); + struct xsd_sockmsg *rep; + + id = allocate_xenbus_id(); + add_waiter(w, req_info[id].waitq); + + xb_write(type, id, trans, io, nr_reqs); + + schedule(); + remove_waiter(w); + wake(current); + + rep = req_info[id].reply; + BUG_ON(rep->req_id != id); + release_xenbus_id(id); + return rep; +} + +static char *errmsg(struct xsd_sockmsg *rep) +{ + char *res; + if (!rep) { + char msg[] = "No reply"; + size_t len = strlen(msg) + 1; + return memcpy(malloc(len), msg, len); + } + if (rep->type != XS_ERROR) + return NULL; + res = malloc(rep->len + 1); + memcpy(res, rep + 1, rep->len); + res[rep->len] = 0; + free(rep); + return res; +} + +/* Send a debug message to xenbus. Can block. */ +static void xenbus_debug_msg(const char *msg) +{ + int len = strlen(msg); + struct write_req req[] = { + { "print", sizeof("print") }, + { msg, len }, + { "", 1 }}; + struct xsd_sockmsg *reply; + + reply = xenbus_msg_reply(XS_DEBUG, 0, req, ARRAY_SIZE(req)); + DEBUG("Got a reply, type %d, id %d, len %d.\n", + reply->type, reply->req_id, reply->len); +} + +/* List the contents of a directory. Returns a malloc()ed array of + pointers to malloc()ed strings. The array is NULL terminated. May + block. */ +char *xenbus_ls(xenbus_transaction_t xbt, const char *pre, char ***contents) +{ + struct xsd_sockmsg *reply, *repmsg; + struct write_req req[] = { { pre, strlen(pre)+1 } }; + int nr_elems, x, i; + char **res, *msg; + + repmsg = xenbus_msg_reply(XS_DIRECTORY, xbt, req, ARRAY_SIZE(req)); + msg = errmsg(repmsg); + if (msg) { + *contents = NULL; + return msg; + } + reply = repmsg + 1; + for (x = nr_elems = 0; x < repmsg->len; x++) + nr_elems += (((char *)reply)[x] == 0); + res = malloc(sizeof(res[0]) * (nr_elems + 1)); + for (x = i = 0; i < nr_elems; i++) { + int l = strlen((char *)reply + x); + res[i] = malloc(l + 1); + memcpy(res[i], (char *)reply + x, l + 1); + x += l + 1; + } + res[i] = NULL; + free(repmsg); + *contents = res; + return NULL; +} + +char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value) +{ + struct write_req req[] = { {path, strlen(path) + 1} }; + struct xsd_sockmsg *rep; + char *res, *msg; + rep = xenbus_msg_reply(XS_READ, xbt, req, ARRAY_SIZE(req)); + msg = errmsg(rep); + if (msg) { + *value = NULL; + return msg; + } + res = malloc(rep->len + 1); + memcpy(res, rep + 1, rep->len); + res[rep->len] = 0; + free(rep); + *value = res; + return NULL; +} + +char *xenbus_write(xenbus_transaction_t xbt, const char *path, const char *value) +{ + struct write_req req[] = { + {path, strlen(path) + 1}, + {value, strlen(value)}, + }; + struct xsd_sockmsg *rep; + char *msg; + rep = xenbus_msg_reply(XS_WRITE, xbt, req, ARRAY_SIZE(req)); + msg = errmsg(rep); + if (msg) return msg; + free(rep); + return NULL; +} + +char* xenbus_watch_path_token( xenbus_transaction_t xbt, const char *path, const char *token, xenbus_event_queue *events) +{ + struct xsd_sockmsg *rep; + + struct write_req req[] = { + {path, strlen(path) + 1}, + {token, strlen(token) + 1}, + }; + + struct watch *watch = malloc(sizeof(*watch)); + + char *msg; + + if (!events) + events = &xenbus_events; + + watch->token = strdup(token); + watch->events = events; + watch->next = watches; + watches = watch; + + rep = xenbus_msg_reply(XS_WATCH, xbt, req, ARRAY_SIZE(req)); + + msg = errmsg(rep); + if (msg) return msg; + free(rep); + + return NULL; +} + +char* xenbus_unwatch_path_token( xenbus_transaction_t xbt, const char *path, const char *token) +{ + struct xsd_sockmsg *rep; + + struct write_req req[] = { + {path, strlen(path) + 1}, + {token, strlen(token) + 1}, + }; + + struct watch *watch, **prev; + + char *msg; + + rep = xenbus_msg_reply(XS_UNWATCH, xbt, req, ARRAY_SIZE(req)); + + msg = errmsg(rep); + if (msg) return msg; + free(rep); + + for (prev = &watches, watch = *prev; watch; prev = &watch->next, watch = *prev) + if (!strcmp(watch->token, token)) { + free(watch->token); + *prev = watch->next; + free(watch); + break; + } + + return NULL; +} + +char *xenbus_rm(xenbus_transaction_t xbt, const char *path) +{ + struct write_req req[] = { {path, strlen(path) + 1} }; + struct xsd_sockmsg *rep; + char *msg; + rep = xenbus_msg_reply(XS_RM, xbt, req, ARRAY_SIZE(req)); + msg = errmsg(rep); + if (msg) + return msg; + free(rep); + return NULL; +} + +char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value) +{ + struct write_req req[] = { {path, strlen(path) + 1} }; + struct xsd_sockmsg *rep; + char *res, *msg; + rep = xenbus_msg_reply(XS_GET_PERMS, xbt, req, ARRAY_SIZE(req)); + msg = errmsg(rep); + if (msg) { + *value = NULL; + return msg; + } + res = malloc(rep->len + 1); + memcpy(res, rep + 1, rep->len); + res[rep->len] = 0; + free(rep); + *value = res; + return NULL; +} + +#define PERM_MAX_SIZE 32 +char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path, domid_t dom, char perm) +{ + char value[PERM_MAX_SIZE]; + struct write_req req[] = { + {path, strlen(path) + 1}, + {value, 0}, + }; + struct xsd_sockmsg *rep; + char *msg; + snprintf(value, PERM_MAX_SIZE, "%c%hu", perm, dom); + req[1].len = strlen(value) + 1; + rep = xenbus_msg_reply(XS_SET_PERMS, xbt, req, ARRAY_SIZE(req)); + msg = errmsg(rep); + if (msg) + return msg; + free(rep); + return NULL; +} + +char *xenbus_transaction_start(xenbus_transaction_t *xbt) +{ + /* xenstored becomes angry if you send a length 0 message, so just + shove a nul terminator on the end */ + struct write_req req = { "", 1}; + struct xsd_sockmsg *rep; + char *err; + + rep = xenbus_msg_reply(XS_TRANSACTION_START, 0, &req, 1); + err = errmsg(rep); + if (err) + return err; + sscanf((char *)(rep + 1), "%u", xbt); + free(rep); + return NULL; +} + +char * +xenbus_transaction_end(xenbus_transaction_t t, int abort, int *retry) +{ + struct xsd_sockmsg *rep; + struct write_req req; + char *err; + + *retry = 0; + + req.data = abort ? "F" : "T"; + req.len = 2; + rep = xenbus_msg_reply(XS_TRANSACTION_END, t, &req, 1); + err = errmsg(rep); + if (err) { + if (!strcmp(err, "EAGAIN")) { + *retry = 1; + free(err); + return NULL; + } else { + return err; + } + } + free(rep); + return NULL; +} + +int xenbus_read_integer(const char *path) +{ + char *res, *buf; + int t; + + res = xenbus_read(XBT_NIL, path, &buf); + if (res) { + printk("Failed to read %s.\n", path); + free(res); + return -1; + } + sscanf(buf, "%d", &t); + free(buf); + return t; +} + +char* xenbus_printf(xenbus_transaction_t xbt, + const char* node, const char* path, + const char* fmt, ...) +{ +#define BUFFER_SIZE 256 + char fullpath[BUFFER_SIZE]; + char val[BUFFER_SIZE]; + va_list args; + + BUG_ON(strlen(node) + strlen(path) + 1 >= BUFFER_SIZE); + sprintf(fullpath,"%s/%s", node, path); + va_start(args, fmt); + vsprintf(val, fmt, args); + va_end(args); + return xenbus_write(xbt,fullpath,val); +} + +static void do_ls_test(const char *pre) +{ + char **dirs, *msg; + int x; + + DEBUG("ls %s...\n", pre); + msg = xenbus_ls(XBT_NIL, pre, &dirs); + if (msg) { + DEBUG("Error in xenbus ls: %s\n", msg); + free(msg); + return; + } + for (x = 0; dirs[x]; x++) + { + DEBUG("ls %s[%d] -> %s\n", pre, x, dirs[x]); + free(dirs[x]); + } + free(dirs); +} + +static void do_read_test(const char *path) +{ + char *res, *msg; + DEBUG("Read %s...\n", path); + msg = xenbus_read(XBT_NIL, path, &res); + if (msg) { + DEBUG("Error in xenbus read: %s\n", msg); + free(msg); + return; + } + DEBUG("Read %s -> %s.\n", path, res); + free(res); +} + +static void do_write_test(const char *path, const char *val) +{ + char *msg; + DEBUG("Write %s to %s...\n", val, path); + msg = xenbus_write(XBT_NIL, path, val); + if (msg) { + DEBUG("Result %s\n", msg); + free(msg); + } else { + DEBUG("Success.\n"); + } +} + +static void do_rm_test(const char *path) +{ + char *msg; + DEBUG("rm %s...\n", path); + msg = xenbus_rm(XBT_NIL, path); + if (msg) { + DEBUG("Result %s\n", msg); + free(msg); + } else { + DEBUG("Success.\n"); + } +} + +/* Simple testing thing */ +void test_xenbus(void) +{ + DEBUG("Doing xenbus test.\n"); + xenbus_debug_msg("Testing xenbus...\n"); + + DEBUG("Doing ls test.\n"); + do_ls_test("device"); + do_ls_test("device/vif"); + do_ls_test("device/vif/0"); + + DEBUG("Doing read test.\n"); + do_read_test("device/vif/0/mac"); + do_read_test("device/vif/0/backend"); + + DEBUG("Doing write test.\n"); + do_write_test("device/vif/0/flibble", "flobble"); + do_read_test("device/vif/0/flibble"); + do_write_test("device/vif/0/flibble", "widget"); + do_read_test("device/vif/0/flibble"); + + DEBUG("Doing rm test.\n"); + do_rm_test("device/vif/0/flibble"); + do_read_test("device/vif/0/flibble"); + DEBUG("(Should have said ENOENT)\n"); +} + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * End: + */ diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..fab9472 --- /dev/null +++ b/install.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +src='./install' +if [ -d ./dist ]; then + src='./dist/install' +fi + +if ! [ -d $src ]; then + echo "ERROR: Could not find a valid distribution directory." + echo " If this is a source-only release, try 'make dist'." + exit 1 +fi + +dst='/' +if [ $# -ne 0 ]; then + dst=$1 +fi + +if ! [ -d $dst ]; then + echo "ERROR: You must specify a valid install directory." + echo " The specified directory '$dst' is not valid." + exit 1 +fi + +tmp="`mktemp -d`" + +echo "Installing Xen from '$src' to '$dst'..." +(cd $src; tar -cf - * ) | tar -C "$tmp" -xf - + +[ -x "$(which udevinfo)" ] && \ + UDEV_VERSION=$(udevinfo -V | sed -e 's/^[^0-9]* \([0-9]\{1,\}\)[^0-9]\{0,\}/\1/') + +if [ -n "$UDEV_VERSION" ] && [ $UDEV_VERSION -ge 059 ]; then + echo " - installing for udev-based system" + rm -rf "$tmp/etc/hotplug" +else + echo " - installing for hotplug-based system" + rm -rf "$tmp/etc/udev" +fi + +echo " - modifying permissions" +chmod -R a+rX "$tmp" + +(cd $tmp; tar -cf - *) | tar --no-same-owner -C "$dst" -xf - +rm -rf "$tmp" + +echo "All done." + +echo "Checking to see whether prerequisite tools are installed..." +cd $src/../check +./chk install +echo "All done." + +exit 0 diff --git a/stubdom/Makefile b/stubdom/Makefile new file mode 100644 index 0000000..da7be4e --- /dev/null +++ b/stubdom/Makefile @@ -0,0 +1,356 @@ +XEN_ROOT = .. +MINI_OS = $(XEN_ROOT)/extras/mini-os + +export XEN_OS=MiniOS + +export stubdom=y +export debug=y +include $(XEN_ROOT)/Config.mk + +IOEMU_OPTIONS=--disable-sdl --disable-opengl --disable-vnc-tls --disable-brlapi --disable-kqemu +ZLIB_URL?=http://www.zlib.net +ZLIB_VERSION=1.2.3 +LIBPCI_URL?=http://www.kernel.org/pub/software/utils/pciutils +LIBPCI_VERSION=2.2.9 +NEWLIB_URL?=ftp://sources.redhat.com/pub/newlib +NEWLIB_VERSION=1.16.0 +LWIP_URL?=http://download.savannah.gnu.org/releases/lwip +LWIP_VERSION=1.3.0 +GRUB_URL?=http://alpha.gnu.org/gnu/grub +GRUB_VERSION=0.97 + +WGET=wget -c + +GNU_TARGET_ARCH:=$(XEN_TARGET_ARCH) +ifeq ($(XEN_TARGET_ARCH),x86_32) +GNU_TARGET_ARCH:=i686 +endif + +ifeq ($(GNU_TARGET_ARCH), i686) +TARGET_CFLAGS= +NEWLIB_CFLAGS+=-D_I386MACH_ALLOW_HW_INTERRUPTS +STUBDOM_SUPPORTED=1 +endif +ifeq ($(GNU_TARGET_ARCH), x86_64) +TARGET_CFLAGS=-mno-red-zone +NEWLIB_CFLAGS+=-D_I386MACH_ALLOW_HW_INTERRUPTS +STUBDOM_SUPPORTED=1 +endif +ifeq ($(GNU_TARGET_ARCH), ia64) +TARGET_CFLAGS=-mconstant-gp +endif + +CROSS_ROOT=cross-root-$(GNU_TARGET_ARCH) +CROSS_PREFIX=$(CURDIR)/$(CROSS_ROOT) + +# Disable PIE/SSP if GCC supports them. They can break us. +TARGET_CFLAGS += $(CFLAGS) +TARGET_CPPFLAGS += $(CPPFLAGS) +TARGET_CFLAGS += $(call cc-option,$(CC),-nopie,) +TARGET_CFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) +TARGET_CFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) + +# Do not use host headers and libs +GCC_INSTALL = $(shell LANG=C gcc -print-search-dirs | sed -n -e 's/install: \(.*\)/\1/p') +TARGET_CPPFLAGS += -U __linux__ -U __FreeBSD__ -U __sun__ +TARGET_CPPFLAGS += -nostdinc +TARGET_CPPFLAGS += -isystem $(CURDIR)/$(MINI_OS)/include/posix +TARGET_CPPFLAGS += -isystem $(CROSS_PREFIX)/$(GNU_TARGET_ARCH)-xen-elf/include +TARGET_CPPFLAGS += -isystem $(GCC_INSTALL)include +TARGET_CPPFLAGS += -isystem $(CURDIR)/lwip-$(XEN_TARGET_ARCH)/src/include +TARGET_CPPFLAGS += -isystem $(CURDIR)/lwip-$(XEN_TARGET_ARCH)/src/include/ipv4 +TARGET_CPPFLAGS += -I$(CURDIR)/include + +TARGET_LDFLAGS += -nostdlib -L$(CROSS_PREFIX)/$(GNU_TARGET_ARCH)-xen-elf/lib + +TARGETS=ioemu c caml grub + +.PHONY: all +all: build +ifeq ($(STUBDOM_SUPPORTED),1) +build: ioemu-stubdom c-stubdom pv-grub +else +build: +endif + +############## +# Cross-newlib +############## + +newlib-$(NEWLIB_VERSION).tar.gz: + $(WGET) $(NEWLIB_URL)/$@ + +newlib-$(NEWLIB_VERSION): newlib-$(NEWLIB_VERSION).tar.gz + tar xzf $< + patch -d $@ -p0 < newlib.patch + touch $@ + +NEWLIB_STAMPFILE=$(CROSS_ROOT)/$(GNU_TARGET_ARCH)-xen-elf/lib/libc.a +.PHONY: cross-newlib +cross-newlib: $(NEWLIB_STAMPFILE) +$(NEWLIB_STAMPFILE): mk-headers-$(XEN_TARGET_ARCH) newlib-$(NEWLIB_VERSION) + mkdir -p newlib-$(XEN_TARGET_ARCH) + ( cd newlib-$(XEN_TARGET_ARCH) && \ + CC_FOR_TARGET="$(CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(NEWLIB_CFLAGS)" AR_FOR_TARGET=$(AR) LD_FOR_TARGET=$(LD) RANLIB_FOR_TARGET=$(RANLIB) ../newlib-$(NEWLIB_VERSION)/configure --prefix=$(CROSS_PREFIX) --verbose --target=$(GNU_TARGET_ARCH)-xen-elf --enable-newlib-io-long-long --disable-multilib && \ + $(MAKE) && \ + DESTDIR= $(MAKE) install ) + +############ +# Cross-zlib +############ + +zlib-$(ZLIB_VERSION).tar.gz: + $(WGET) $(ZLIB_URL)/$@ + +zlib-$(XEN_TARGET_ARCH): zlib-$(ZLIB_VERSION).tar.gz + tar xzf $< + mv zlib-$(ZLIB_VERSION) $@ + +ZLIB_STAMPFILE=$(CROSS_ROOT)/$(GNU_TARGET_ARCH)-xen-elf/lib/libz.a +.PHONY: cross-zlib +cross-zlib: $(ZLIB_STAMPFILE) +$(ZLIB_STAMPFILE): zlib-$(XEN_TARGET_ARCH) $(NEWLIB_STAMPFILE) + ( cd $< && \ + CFLAGS="$(TARGET_CPPFLAGS) $(TARGET_CFLAGS)" CC=$(CC) ./configure --prefix=$(CROSS_PREFIX)/$(GNU_TARGET_ARCH)-xen-elf && \ + $(MAKE) libz.a && \ + $(MAKE) install ) + +############## +# Cross-libpci +############## + +pciutils-$(LIBPCI_VERSION).tar.bz2: + $(WGET) $(LIBPCI_URL)/$@ + +pciutils-$(XEN_TARGET_ARCH): pciutils-$(LIBPCI_VERSION).tar.bz2 + tar xjf $< + mv pciutils-$(LIBPCI_VERSION) $@ + patch -d $@ -p1 < pciutils.patch + touch $@ + +LIBPCI_STAMPFILE=$(CROSS_ROOT)/$(GNU_TARGET_ARCH)-xen-elf/lib/libpci.a +.PHONY: cross-libpci +cross-libpci: $(LIBPCI_STAMPFILE) +$(LIBPCI_STAMPFILE): pciutils-$(XEN_TARGET_ARCH) $(NEWLIB_STAMPFILE) $(ZLIB_STAMPFILE) + ( cd $< && \ + cp ../libpci.config.h lib/config.h && \ + echo '#define PCILIB_VERSION "$(LIBPCI_VERSION)"' >> lib/config.h && \ + cp ../libpci.config.mak lib/config.mk && \ + $(MAKE) CC="$(CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) -I$(realpath $(MINI_OS)/include)" lib/libpci.a && \ + $(INSTALL_DATA) lib/libpci.a $(CROSS_PREFIX)/$(GNU_TARGET_ARCH)-xen-elf/lib/ && \ + $(INSTALL_DIR) $(CROSS_PREFIX)/$(GNU_TARGET_ARCH)-xen-elf/include/pci && \ + $(INSTALL_DATA) lib/config.h lib/header.h lib/pci.h lib/types.h $(CROSS_PREFIX)/$(GNU_TARGET_ARCH)-xen-elf/include/pci/ \ + ) + +###### +# lwIP +###### + +lwip-$(LWIP_VERSION).tar.gz: + $(WGET) $(LWIP_URL)/$@ + +lwip-$(XEN_TARGET_ARCH): lwip-$(LWIP_VERSION).tar.gz + tar xzf $< + mv lwip $@ + patch -d $@ -p0 < lwip.patch-cvs + touch $@ + +####### +# Links +####### + +.PHONY: $(CROSS_ROOT) +$(CROSS_ROOT): cross-newlib cross-zlib cross-libpci + +$(XEN_ROOT)/tools/ioemu-dir: + make -C $(XEN_ROOT)/tools ioemu-dir-find + +ioemu/linkfarm.stamp: $(XEN_ROOT)/tools/ioemu-dir + mkdir -p ioemu + set -e; \ + $(absolutify_xen_root); \ + cd ioemu; \ + src="$$XEN_ROOT/tools/ioemu-dir"; export src; \ + (cd $$src && find * -type d -print) | xargs mkdir -p; \ + (cd $$src && find * ! -type l -type f $(addprefix ! -name , \ + '*.[oda1]' 'config-*' config.mak qemu-dm qemu-img-xen \ + '*.html' '*.pod' \ + )) | \ + while read f; do rm -f "$$f"; ln -s "$$src/$$f" "$$f"; done + touch ioemu/linkfarm.stamp + +mk-headers-$(XEN_TARGET_ARCH): ioemu/linkfarm.stamp + mkdir -p include/xen && \ + ln -sf $(addprefix ../../,$(wildcard $(XEN_ROOT)/xen/include/public/*.h)) include/xen && \ + ln -sf $(addprefix ../../$(XEN_ROOT)/xen/include/public/,arch-ia64 arch-x86 hvm io xsm) include/xen && \ + ( [ -h include/xen/sys ] || ln -sf ../../$(XEN_ROOT)/tools/include/xen-sys/MiniOS include/xen/sys ) && \ + mkdir -p include/xen-foreign && \ + ln -sf $(addprefix ../../,$(wildcard $(XEN_ROOT)/tools/include/xen-foreign/*)) include/xen-foreign/ && \ + $(MAKE) -C include/xen-foreign/ && \ + ( [ -h include/xen/foreign ] || ln -sf ../xen-foreign include/xen/foreign ) + mkdir -p libxc-$(XEN_TARGET_ARCH) + [ -h libxc-$(XEN_TARGET_ARCH)/Makefile ] || ( cd libxc-$(XEN_TARGET_ARCH) && \ + ln -sf ../$(XEN_ROOT)/tools/libxc/*.h . && \ + ln -sf ../$(XEN_ROOT)/tools/libxc/*.c . && \ + ln -sf ../$(XEN_ROOT)/tools/libxc/Makefile . ) + mkdir -p libxc-$(XEN_TARGET_ARCH)/$(XEN_TARGET_ARCH) + [ -h libxc-$(XEN_TARGET_ARCH)/$(XEN_TARGET_ARCH) ] || ( cd libxc-$(XEN_TARGET_ARCH)/$(XEN_TARGET_ARCH) && \ + ln -sf ../$(XEN_ROOT)/tools/libxc/$(XEN_TARGET_ARCH)/*.c . && \ + ln -sf ../$(XEN_ROOT)/tools/libxc/$(XEN_TARGET_ARCH)/*.h . && \ + ln -sf ../$(XEN_ROOT)/tools/libxc/$(XEN_TARGET_ARCH)/Makefile . ) + $(MAKE) -C $(MINI_OS) links + touch mk-headers-$(XEN_TARGET_ARCH) + +TARGETS_MINIOS=$(addprefix mini-os-$(XEN_TARGET_ARCH)-,$(TARGETS)) +$(TARGETS_MINIOS): mini-os-%: + [ -d $@ ] || \ + for i in $$(cd $(MINI_OS) ; find . -type d) ; do \ + mkdir -p $@/$$i ; \ + done + +####### +# libxc +####### + +.PHONY: libxc +libxc: libxc-$(XEN_TARGET_ARCH)/libxenctrl.a libxc-$(XEN_TARGET_ARCH)/libxenguest.a +libxc-$(XEN_TARGET_ARCH)/libxenctrl.a libxc-$(XEN_TARGET_ARCH)/libxenguest.a:: cross-zlib + CPPFLAGS="$(TARGET_CPPFLAGS)" CFLAGS="$(TARGET_CFLAGS)" $(MAKE) -C libxc-$(XEN_TARGET_ARCH) + +####### +# ioemu +####### + +.PHONY: ioemu +ioemu: cross-zlib cross-libpci libxc + [ -f ioemu/config-host.mak ] || \ + ( $(absolutify_xen_root); \ + cd ioemu ; \ + CONFIG_STUBDOM=yes XEN_TARGET_ARCH=$(XEN_TARGET_ARCH) CFLAGS="$(TARGET_CFLAGS)" sh ./xen-setup --cc=$(CC) --disable-gcc-check $(IOEMU_OPTIONS)) + CPPFLAGS= TARGET_CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C ioemu LWIPDIR=$(CURDIR)/lwip-$(XEN_TARGET_ARCH) TOOLS= CONFIG_STUBDOM=yes + +###### +# caml +###### + +.PHONY: caml +caml: $(CROSS_ROOT) + CPPFLAGS="$(TARGET_CPPFLAGS)" CFLAGS="$(TARGET_CFLAGS)" $(MAKE) -C $@ LWIPDIR=$(CURDIR)/lwip-$(XEN_TARGET_ARCH) + +### +# C +### + +.PHONY: c +c: $(CROSS_ROOT) + CPPFLAGS="$(TARGET_CPPFLAGS)" CFLAGS="$(TARGET_CFLAGS)" $(MAKE) -C $@ LWIPDIR=$(CURDIR)/lwip-$(XEN_TARGET_ARCH) + +###### +# Grub +###### + +grub-$(GRUB_VERSION).tar.gz: + $(WGET) $(GRUB_URL)/$@ + +grub-upstream: grub-$(GRUB_VERSION).tar.gz + tar xzf $< + mv grub-$(GRUB_VERSION) $@ + for i in grub.patches/* ; do \ + patch -d $@ -p1 < $$i ; \ + done + +.PHONY: grub +grub: grub-upstream $(CROSS_ROOT) + mkdir -p grub-$(XEN_TARGET_ARCH) + CPPFLAGS="$(TARGET_CPPFLAGS)" CFLAGS="$(TARGET_CFLAGS)" $(MAKE) -C $@ OBJ_DIR=$(CURDIR)/grub-$(XEN_TARGET_ARCH) + +######## +# minios +######## + +.PHONY: ioemu-stubdom +ioemu-stubdom: APP_OBJS=$(CURDIR)/ioemu/i386-stubdom/qemu.a $(CURDIR)/ioemu/i386-stubdom/libqemu.a $(CURDIR)/ioemu/libqemu_common.a +ioemu-stubdom: mini-os-$(XEN_TARGET_ARCH)-ioemu lwip-$(XEN_TARGET_ARCH) libxc ioemu + DEF_CPPFLAGS="$(TARGET_CPPFLAGS)" DEF_CFLAGS="-DCONFIG_QEMU $(TARGET_CFLAGS)" DEF_LDFLAGS="$(TARGET_LDFLAGS)" $(MAKE) -C $(MINI_OS) OBJ_DIR=$(CURDIR)/$< LWIPDIR=$(CURDIR)/lwip-$(XEN_TARGET_ARCH) APP_OBJS="$(APP_OBJS)" + +CAMLLIB = $(shell ocamlc -where) +.PHONY: caml-stubdom +caml-stubdom: mini-os-$(XEN_TARGET_ARCH)-caml lwip-$(XEN_TARGET_ARCH) libxc caml + DEF_CPPFLAGS="$(TARGET_CPPFLAGS)" DEF_CFLAGS="-DCONFIG_CAML $(TARGET_CFLAGS)" DEF_LDFLAGS="$(TARGET_LDFLAGS)" $(MAKE) -C $(MINI_OS) OBJ_DIR=$(CURDIR)/$< LWIPDIR=$(CURDIR)/lwip-$(XEN_TARGET_ARCH) APP_OBJS="$(CURDIR)/caml/main-caml.o $(CURDIR)/caml/caml.o $(CAMLLIB)/libasmrun.a" + +.PHONY: c-stubdom +c-stubdom: mini-os-$(XEN_TARGET_ARCH)-c lwip-$(XEN_TARGET_ARCH) libxc c + DEF_CPPFLAGS="$(TARGET_CPPFLAGS)" DEF_CFLAGS="-DCONFIG_C $(TARGET_CFLAGS)" DEF_LDFLAGS="$(TARGET_LDFLAGS)" $(MAKE) -C $(MINI_OS) OBJ_DIR=$(CURDIR)/$< LWIPDIR=$(CURDIR)/lwip-$(XEN_TARGET_ARCH) APP_OBJS=$(CURDIR)/c/main.a + +.PHONY: pv-grub +pv-grub: mini-os-$(XEN_TARGET_ARCH)-grub libxc grub + DEF_CPPFLAGS="$(TARGET_CPPFLAGS)" DEF_CFLAGS="-DCONFIG_GRUB $(TARGET_CFLAGS)" DEF_LDFLAGS="$(TARGET_LDFLAGS)" $(MAKE) -C $(MINI_OS) OBJ_DIR=$(CURDIR)/$< APP_OBJS=$(CURDIR)/grub-$(XEN_TARGET_ARCH)/main.a + +######### +# install +######### + +ifeq ($(STUBDOM_SUPPORTED),1) +install: install-readme install-ioemu install-grub +else +install: +endif + +install-readme: + $(INSTALL_DIR) $(DESTDIR)$(DOCDIR) + $(INSTALL_DATA) README $(DESTDIR)$(DOCDIR)/README.stubdom + +install-ioemu: ioemu-stubdom + $(INSTALL_DIR) "$(DESTDIR)/usr/lib/xen/bin" + $(INSTALL_PROG) stubdom-dm "$(DESTDIR)/usr/lib/xen/bin" + $(INSTALL_DIR) "$(DESTDIR)/usr/lib/xen/boot" + $(INSTALL_DATA) mini-os-$(XEN_TARGET_ARCH)-ioemu/mini-os.gz "$(DESTDIR)/usr/lib/xen/boot/ioemu-stubdom.gz" + +install-grub: pv-grub + $(INSTALL_DIR) "$(DESTDIR)/usr/lib/xen/boot" + $(INSTALL_DATA) mini-os-$(XEN_TARGET_ARCH)-grub/mini-os.gz "$(DESTDIR)/usr/lib/xen/boot/pv-grub-$(XEN_TARGET_ARCH).gz" + +####### +# clean +####### + +# Only clean the libxc/ioemu/mini-os part +.PHONY: clean +clean: + rm -fr mini-os-$(XEN_TARGET_ARCH)-ioemu + rm -fr mini-os-$(XEN_TARGET_ARCH)-c + rm -fr mini-os-$(XEN_TARGET_ARCH)-caml + rm -fr mini-os-$(XEN_TARGET_ARCH)-grub + $(MAKE) -C caml clean + $(MAKE) -C c clean + rm -fr grub-$(XEN_TARGET_ARCH) + [ ! -d libxc-$(XEN_TARGET_ARCH) ] || $(MAKE) -C libxc-$(XEN_TARGET_ARCH) clean + -[ ! -d ioemu ] || $(MAKE) -C ioemu clean + +# clean the cross-compilation result +.PHONY: crossclean +crossclean: clean + rm -fr $(CROSS_ROOT) + rm -fr newlib-$(XEN_TARGET_ARCH) + rm -fr zlib-$(XEN_TARGET_ARCH) pciutils-$(XEN_TARGET_ARCH) + rm -fr libxc-$(XEN_TARGET_ARCH) ioemu + rm -f mk-headers-$(XEN_TARGET_ARCH) + +# clean patched sources +.PHONY: patchclean +patchclean: crossclean + rm -fr newlib-$(NEWLIB_VERSION) + rm -fr lwip-$(XEN_TARGET_ARCH) + rm -fr grub-upstream + +# clean downloads +.PHONY: downloadclean +downloadclean: patchclean + rm -f newlib-$(NEWLIB_VERSION).tar.gz + rm -f zlib-$(ZLIB_VERSION).tar.gz + rm -f pciutils-$(LIBPCI_VERSION).tar.bz2 + rm -f grub-$(GRUB_VERSION).tar.gz + rm -f lwip-$(LWIP_VERSION).tar.gz + +.PHONY: distclean +distclean: downloadclean diff --git a/stubdom/README b/stubdom/README new file mode 100644 index 0000000..3b25196 --- /dev/null +++ b/stubdom/README @@ -0,0 +1,152 @@ + IOEMU stubdom + ============= + + This boosts HVM performance by putting ioemu in its own lightweight domain. + +General Configuration +===================== + +Due to a race between the creation of the IOEMU stubdomain itself and allocation +of video memory for the HVM domain, you need to avoid the need for ballooning, +by using the hypervisor dom0_mem= option for instance. + + +There is a sample configuration set in xmexample.hvm-stubdom and +xmexample.hvm-dm + +In your HVM config "hvmconfig", + +- use /usr/lib/xen/bin/stubdom-dm as dm script: + +device_model = '/usr/lib/xen/bin/stubdom-dm' + +- comment the disk statement: + +#disk = [ 'file:/tmp/install.iso,hdc:cdrom,r', 'phy:/dev/sda6,hda,w', 'file:/tmp/test,hdb,r' ] + +- disable anything related to dom0, like pty serial assignments + + +Create /etc/xen/hvmconfig-dm (where "hvmconfig" is the name of your HVM +guest) with + +kernel = "/usr/lib/xen/boot/ioemu-stubdom.gz" +vif = [ '', 'ip=10.0.1.1,mac=aa:00:00:12:23:34'] +disk = [ 'file:/tmp/install.iso,hdc:cdrom,r', 'phy:/dev/sda6,hda,w', 'file:/tmp/test,hdb,r' ] + +where +- the first vif ('') is reserved for VNC (see below) +- 'ip=10.0.1.1,mac= etc...' is the same net configuration as in the hvmconfig +script, +- and disk = is the same block configuration as in the hvmconfig script. + +Display Configuration +===================== + +There are three posibilities + +* Using SDL + + - In hvmconfig, disable vnc and sdl: + +vnc = 0 +sdl = 0 + + - In hvmconfig-dm, set an sdl vfb: + +vfb = [ 'type=sdl' ] + +* Using a VNC server in the stub domain + + - In hvmconfig, set vnclisten to "172.30.206.1" for instance. Do not use a +host name as Mini-OS does not have a name resolver. Do not use 127.0.0.1 since +then you will not be able to connect to it. + +vnc = 1 +vnclisten = "172.30.206.1" + + - In hvmconfig-dm, fill the reserved vif with the same IP, for instance: + +vif = [ 'ip=172.30.206.1', 'ip=10.0.1.1,mac=aa:00:00:12:23:34'] + +* Using a VNC server in dom0 + + - In hvmconfig, disable vnc and sdl: + +vnc = 0 +sdl = 0 + + - In hvmconfig-dm, set a vnc vfb: + +vfb = [ 'type=vnc' ] + +and any other parameter as wished. + +To run +====== + +mkdir -p /exports/usr/share/xen/qemu +ln -s /usr/share/xen/qemu/keymaps /exports/usr/share/xen/qemu +mkdir -p /exports/var/lib +ln -s /var/lib/xen /exports/var/lib +/usr/sbin/fs-backend & + +xm create hvmconfig + + + + PV-GRUB + ======= + + This replaces pygrub to boot domU images safely: it runs the regular grub +inside the created domain itself and uses regular domU facilities to read the +disk / fetch files from network etc. ; it eventually loads the PV kernel and +chain-boots it. + +Configuration +============= + +In your PV config, + +- use /usr/lib/xen/boot/pv-grub.gz as kernel: + +kernel = "/usr/lib/xen/boot/pv-grub.gz" + +- set the path to menu.lst, as seen from the domU, in extra: + +extra = "(hd0,0)/boot/grub/menu.lst" + +you can also use a tftp path (dhcp will be automatically performed): + +extra = "(nd)/somepath/menu.lst" + +or you can set it in option 150 of your dhcp server and leave extra empty + +Limitations +=========== + +- You can not boot a 64bit kernel with a 32bit-compiled PV-GRUB and vice-versa. +To cross-compile a 32bit PV-GRUB, + +export XEN_TARGET_ARCH=x86_32 + +- bootsplash is supported, but the ioemu backend does not yet support restart +for use by the booted kernel. + + + Your own stubdom + ================ + + By running + +cd stubdom/ +make c-stubdom + + or + +cd stubdom/ +make caml-stubdom + + you can compile examples of C or caml stub domain kernels. You can use these +and the relevant Makefile rules as basis to build your own stub domain kernel. +Available libraries are libc, libxc, libxs, zlib and libpci. diff --git a/stubdom/c/Makefile b/stubdom/c/Makefile new file mode 100644 index 0000000..b5cb8e2 --- /dev/null +++ b/stubdom/c/Makefile @@ -0,0 +1,11 @@ +XEN_ROOT = ../.. + +include $(XEN_ROOT)/Config.mk + +all: main.a + +main.a: main.o + $(AR) cr $@ $^ + +clean: + rm -f *.a *.o diff --git a/stubdom/c/main.c b/stubdom/c/main.c new file mode 100644 index 0000000..f97a0f7 --- /dev/null +++ b/stubdom/c/main.c @@ -0,0 +1,8 @@ +#include +#include + +int main(void) { + sleep(2); + printf("Hello, world!\n"); + return 0; +} diff --git a/stubdom/caml/Makefile b/stubdom/caml/Makefile new file mode 100644 index 0000000..d760fe4 --- /dev/null +++ b/stubdom/caml/Makefile @@ -0,0 +1,23 @@ +XEN_ROOT = ../.. + +include $(XEN_ROOT)/Config.mk + +CAMLLIB = $(shell ocamlc -where) +DEF_CPPFLAGS += -I$(CAMLLIB) + +OCAMLFIND=ocamlfind +OCAMLOPT=ocamlopt + +OBJS := hello.cmx +LIBS := + +all: main-caml.o caml.o + +%.cmx: %.ml + $(OCAMLFIND) $(OCAMLOPT) -c $< -o $@ + +caml.o: $(OBJS) + $(OCAMLFIND) $(OCAMLOPT) $(LIBS) $^ -output-obj -o $@ + +clean: + rm -f *.a *.o *.cmx *.cmi diff --git a/stubdom/caml/hello.ml b/stubdom/caml/hello.ml new file mode 100644 index 0000000..3a71811 --- /dev/null +++ b/stubdom/caml/hello.ml @@ -0,0 +1,4 @@ +let main arg = + Printf.printf "Hello, world!\n%!." + +let _ = Callback.register "main" main diff --git a/stubdom/caml/main-caml.c b/stubdom/caml/main-caml.c new file mode 100644 index 0000000..dd55aca --- /dev/null +++ b/stubdom/caml/main-caml.c @@ -0,0 +1,42 @@ +/* + * Caml bootstrap + * + * Samuel Thibault , January 2008 + */ + +#include +#include + +#include +#include +#include + +/* Ugly binary compatibility with Linux */ +FILE *_stderr asm("stderr"); +int *__errno_location; +/* Will probably break everything, probably need to fetch from glibc */ +void *__ctype_b_loc; + +int main(int argc, char *argv[], char *envp[]) +{ + value *val; + + /* Get current thread's value */ + _stderr = stderr; + __errno_location = &errno; + + printf("starting caml\n"); + + /* Wait before things might hang up */ + sleep(1); + + caml_startup(argv); + val = caml_named_value("main"); + if (!val) { + printf("Couldn't find Caml main"); + return 1; + } + caml_callback(*val, Val_int(0)); + printf("callback returned\n"); + return 0; +} diff --git a/stubdom/grub.patches/00cvs b/stubdom/grub.patches/00cvs new file mode 100644 index 0000000..7947306 --- /dev/null +++ b/stubdom/grub.patches/00cvs @@ -0,0 +1,1382 @@ +diff -uprN grub-0.97/acinclude.m4 grub/acinclude.m4 +--- grub-0.97/acinclude.m4 2004-04-27 21:48:06.000000000 +0100 ++++ grub/acinclude.m4 2007-11-05 01:29:46.000000000 +0000 +@@ -57,7 +57,7 @@ else + fi + grub_cv_prog_objcopy_absolute=yes + for link_addr in 2000 8000 7C00; do +- if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -nostdlib -Wl,-N -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec]); then : ++ if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} ${LDFLAGS} -nostdlib -Wl,-N -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec]); then : + else + AC_MSG_ERROR([${CC-cc} cannot link at address $link_addr]) + fi +diff -uprN grub-0.97/ChangeLog grub/ChangeLog +--- grub-0.97/ChangeLog 2005-05-08 03:47:02.000000000 +0100 ++++ grub/ChangeLog 2008-05-20 12:04:18.000000000 +0100 +@@ -1,3 +1,127 @@ ++2008-05-20 Robert Millan ++ ++ * netboot/cs89x0.c: Fix license violation. ++ * netboot/cs89x0.h: Likewise. ++ ++2008-04-10 Pavel Roskin ++ ++ * configure.ac: Always use "_cv_" in cache variables for ++ compatibility with Autoconf 2.62. ++ ++2008-03-28 Robert Millan ++ ++ Surpass 1 TiB disk addressing limit. Note: there are no plans to handle ++ the 2 TiB disk limit in GRUB Legacy, since that would need considerable ++ rework. If you have >2TiB disks, use GRUB 2 instead. ++ ++ * grub/asmstub.c (biosdisk): Add unsigned qualifier to `sector'. ++ * stage2/bios.c (biosdisk): Likewise. ++ * stage2/disk_io.c (rawread, devread, rawwrite, devwrite): Likewise. ++ * stage2/shared.h (rawread, devread, rawwrite, devwrite): Likewise. ++ * lib/device.c (get_drive_geometry): Replace BLKGETSIZE with ++ BLKGETSIZE64. ++ ++2007-10-29 Pavel Roskin ++ ++ * configure.ac: Test if '--build-id=none' is supported by the ++ linker and add it to LDFLAGS if possible. Build ID causes ++ objcopy to generate huge binary files. ++ * aclocal.m4 (grub_PROG_OBJCOPY_ABSOLUTE): Use LDFLAGS when ++ linking, so that build ID doesn't break the test. ++ * stage1/Makefile.am: Preserve LDFLAGS, use stage1_exec_LDFLAGS. ++ ++2007-02-22 Pavel Roskin ++ ++ * stage2/iso9660.h: Remove unnecessary packed attributes. ++ ++2007-02-22 Robert Millan ++ ++ * util/mkbimage: Update my email address, and remove my name from ++ some places where unnecessary credit is given. ++ ++2006-09-10 Pavel Roskin ++ ++ * netboot/natsemi.c: Fix compile error with gcc 4.1.1. Cast ++ cannot make a variable volatile - it should be declared as such. ++ * netboot/sis900.c: Likewise. ++ ++2006-09-08 Pavel Roskin ++ ++ * netboot/etherboot.h: Remove incorrect extern declarations of ++ the variables later declared static. Move BOOTP_DATA_ADDR ... ++ * netboot/main.c: ... here. Eliminate end_of_rfc1533 - it's ++ write-only. ++ ++2006-06-24 Yoshinori K. Okuji ++ ++ * docs/grub.texi: Changed the license term to the GNU Free ++ Documentation License 1.2. ++ ++ * docs/multiboot.texi: Reformatted to show the license term ++ and the version number explicitly. ++ ++ * docs/fdl.texi: New file. ++ ++ * docs/Makefile.am (grub_TEXINFOS): Added fdl.texi. ++ ++2006-06-24 Robert Millan ++ ++ * lib/device.c (write_to_partition): /dev/ataraid/ and /dev/rd/ ++ partitions have a "p" prefix. Add it. ++ ++2006-06-24 Robert Millan ++ ++ * lib/device.c (get_i2o_disk_name): New function. ++ (init_device_map) [__linux__]: Add support for I2O devices. ++ ++2006-05-02 Pavel Roskin ++ ++ * stage2/stage2.c (run_menu): Fix "savedefault" to save only top ++ level menu positions. Remember current position when calling a ++ submenu. Don't recalculate it when booting from a submenu. ++ ++ * grub/main.c (main): Make sure the boot drive number doesn't ++ exceed 255. ++ ++2006-05-02 Vesa Jaaskelainen ++ ++ * stage2/shared.h (vbe_mode): Back ported aligment fix from GRUB 2 ++ to GRUB Legacy. Problem reported by Gerardo Richarte. ++ ++2006-04-23 Robert Millan ++ ++ * grub/asmstub.c (get_diskinfo): Optimize sysctl routine. ++ ++2006-04-20 Robert Millan ++ ++ Fixes for kernel of FreeBSD: ++ * grub/asmstub.c (get_diskinfo): Toggle "kern.geom.debugflags" sysctl ++ before opening a device for writing. ++ * util/grub-install.in: Devices don't have this "r" prefix anymore. ++ ++2006-04-16 Yoshinori K. Okuji ++ ++ * docs/multiboot.texi: Correct the offset of address ++ fields. Reported by Jeroen Dekkers. ++ ++2006-03-21 Yoshinori K. Okuji ++ ++ * stage2/builtins.c (setup_func): Specify the size of DEVICE to ++ grub_strncat instead of a strange number 256. Reported by Vitaly ++ Fertman . ++ ++2005-09-29 Yoshinori K. Okuji ++ ++ * docs/multiboot.texi: Fix a bug in the byte order of ++ boot_device. I hope this won't affect any OS image. ++ Increased the version number to 0.6.94. ++ ++2005-09-28 Yoshinori K. Okuji ++ ++ * stage2/boot.c (load_image): Even if an OS image is an ELF ++ object, use the a.out kludge if MULTIBOOT_AOUT_KLUDGE is ++ specified. ++ + 2005-05-08 Yoshinori K. Okuji + + * configure.ac (AC_INIT): Upgraded to 0.97. +diff -uprN grub-0.97/configure grub/configure +--- grub-0.97/configure 2005-05-08 03:48:12.000000000 +0100 ++++ grub/configure 2007-11-05 01:29:46.000000000 +0000 +@@ -3694,6 +3694,64 @@ if test "x$undef_flag" = xyes; then + CPPFLAGS="$CPPFLAGS -Wundef" + fi + ++# Check if build ID can be disabled in the linker ++echo "$as_me:$LINENO: checking whether linker accepts \`--build-id=none'" >&5 ++echo $ECHO_N "checking whether linker accepts \`--build-id=none'... $ECHO_C" >&6 ++save_LDFLAGS="$LDFLAGS" ++LDFLAGS="$LDFLAGS -Wl,--build-id=none" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++int ++main () ++{ ++ ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ++ (eval $ac_link) 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && ++ { ac_try='test -z "$ac_c_werror_flag" ++ || test ! -s conftest.err' ++ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; } && ++ { ac_try='test -s conftest$ac_exeext' ++ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; }; then ++ build_id_flag=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++build_id_flag=no ++fi ++rm -f conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++echo "$as_me:$LINENO: result: $build_id_flag" >&5 ++echo "${ECHO_T}$build_id_flag" >&6 ++LDFLAGS="$save_LDFLAGS" ++if test "x$build_id_flag" = xyes; then ++ LDFLAGS="$LDFLAGS -Wl,--build-id=none" ++fi ++ + if test "x$with_binutils" != x; then + # Extract the first word of "objcopy", so it can be a program name with args. + set dummy objcopy; ac_word=$2 +@@ -3892,7 +3950,7 @@ echo "$as_me: error: ${CC-cc} cannot com + fi + grub_cv_prog_objcopy_absolute=yes + for link_addr in 2000 8000 7C00; do +- if { ac_try='${CC-cc} ${CFLAGS} -nostdlib -Wl,-N -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec' ++ if { ac_try='${CC-cc} ${CFLAGS} ${LDFLAGS} -nostdlib -Wl,-N -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? +diff -uprN grub-0.97/configure.ac grub/configure.ac +--- grub-0.97/configure.ac 2005-05-08 03:36:03.000000000 +0100 ++++ grub/configure.ac 2008-04-10 23:26:50.000000000 +0100 +@@ -86,13 +86,13 @@ if test "x$ac_cv_prog_gcc" = xyes; then + fi + STAGE1_CFLAGS="-O2" + GRUB_CFLAGS="-O2" +- AC_CACHE_CHECK([whether optimization for size works], size_flag, [ ++ AC_CACHE_CHECK([whether optimization for size works], grub_cv_cc_Os, [ + saved_CFLAGS=$CFLAGS + CFLAGS="-Os -g" +- AC_TRY_COMPILE(, , size_flag=yes, size_flag=no) ++ AC_TRY_COMPILE(, , grub_cv_cc_Os=yes, grub_cv_cc_Os=no) + CFLAGS=$saved_CFLAGS + ]) +- if test "x$size_flag" = xyes; then ++ if test "x$grub_cv_cc_Os" = xyes; then + STAGE2_CFLAGS="-Os" + else + STAGE2_CFLAGS="-O2 -fno-strength-reduce -fno-unroll-loops" +@@ -100,16 +100,16 @@ if test "x$ac_cv_prog_gcc" = xyes; then + # OpenBSD has a GCC extension for protecting applications from + # stack smashing attacks, but GRUB doesn't want this feature. + AC_CACHE_CHECK([whether gcc has -fno-stack-protector], +- no_stack_protector_flag, [ ++ grub_cv_cc_no_stack_protector, [ + saved_CFLAGS=$CFLAGS + CFLAGS="-fno-stack-protector" + AC_TRY_COMPILE(, + , +- no_stack_protector_flag=yes, +- no_stack_protector_flag=no) ++ grub_cv_cc_no_stack_protector=yes, ++ grub_cv_cc_no_stack_protector=no) + CFLAGS=$saved_CFLAGS + ]) +- if test "x$no_stack_protector_flag" = xyes; then ++ if test "x$grub_cv_cc_no_stack_protector" = xyes; then + STAGE2_CFLAGS="$STAGE2_CFLAGS -fno-stack-protector" + fi + fi +@@ -123,33 +123,44 @@ AC_SUBST(GRUB_CFLAGS) + CPPFLAGS="$CPPFLAGS -Wall -Wmissing-prototypes -Wunused -Wshadow" + CPPFLAGS="$CPPFLAGS -Wpointer-arith" + +-AC_CACHE_CHECK([whether -Wundef works], undef_flag, [ ++AC_CACHE_CHECK([whether -Wundef works], grub_cv_cc_Wundef, [ + saved_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="-Wundef" +- AC_TRY_COMPILE(, , undef_flag=yes, undef_flag=no) ++ AC_TRY_COMPILE(, , grub_cv_cc_Wundef=yes, grub_cv_cc_Wundef=no) + CPPFLAGS="$saved_CPPFLAGS" + ]) + + # The options `-falign-*' are supported by gcc 3.0 or later. + # Probably it is sufficient to only check for -falign-loops. +-AC_CACHE_CHECK([whether -falign-loops works], [falign_loop_flag], [ ++AC_CACHE_CHECK([whether -falign-loops works], [grub_cv_cc_falign_loop], [ + saved_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="-falign-loops=1" +- AC_TRY_COMPILE(, , [falign_loop_flag=yes], [falign_loop_flag=no]) ++ AC_TRY_COMPILE(, , [grub_cv_cc_falign_loop=yes], [grub_cv_cc_falign_loop=no]) + CPPFLAGS="$saved_CPPFLAGS" + ]) + + # Force no alignment to save space. +-if test "x$falign_loop_flag" = xyes; then ++if test "x$grub_cv_cc_falign_loop" = xyes; then + CPPFLAGS="$CPPFLAGS -falign-jumps=1 -falign-loops=1 -falign-functions=1" + else + CPPFLAGS="$CPPFLAGS -malign-jumps=1 -malign-loops=1 -malign-functions=1" + fi + +-if test "x$undef_flag" = xyes; then ++if test "x$grub_cv_cc_Wundef" = xyes; then + CPPFLAGS="$CPPFLAGS -Wundef" + fi + ++# Check if build ID can be disabled in the linker ++AC_MSG_CHECKING([whether linker accepts `--build-id=none']) ++save_LDFLAGS="$LDFLAGS" ++LDFLAGS="$LDFLAGS -Wl,--build-id=none" ++AC_TRY_LINK(, , build_id_flag=yes, build_id_flag=no) ++AC_MSG_RESULT([$build_id_flag]) ++LDFLAGS="$save_LDFLAGS" ++if test "x$build_id_flag" = xyes; then ++ LDFLAGS="$LDFLAGS -Wl,--build-id=none" ++fi ++ + if test "x$with_binutils" != x; then + dnl AC_PATH_TOOL(OBJCOPY, objcopy, , "$with_binutils:$PATH") + AC_PATH_PROG(OBJCOPY, objcopy, , "$with_binutils:$PATH") +diff -uprN grub-0.97/docs/grub.8 grub/docs/grub.8 +--- grub-0.97/docs/grub.8 2005-05-08 03:48:56.000000000 +0100 ++++ grub/docs/grub.8 2006-06-24 15:40:02.000000000 +0100 +@@ -1,5 +1,5 @@ + .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.23. +-.TH GRUB "8" "May 2005" "grub (GNU GRUB 0.97)" FSF ++.TH GRUB "8" "June 2006" "grub (GNU GRUB 0.97)" FSF + .SH NAME + grub \- the grub shell + .SH SYNOPSIS +diff -uprN grub-0.97/docs/grub-install.8 grub/docs/grub-install.8 +--- grub-0.97/docs/grub-install.8 2005-05-08 03:48:56.000000000 +0100 ++++ grub/docs/grub-install.8 2006-06-24 15:40:02.000000000 +0100 +@@ -1,5 +1,5 @@ + .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.23. +-.TH GRUB-INSTALL "8" "May 2005" "grub-install (GNU GRUB 0.97)" FSF ++.TH GRUB-INSTALL "8" "June 2006" "grub-install (GNU GRUB 0.97)" FSF + .SH NAME + grub-install \- install GRUB on your drive + .SH SYNOPSIS +diff -uprN grub-0.97/docs/grub-md5-crypt.8 grub/docs/grub-md5-crypt.8 +--- grub-0.97/docs/grub-md5-crypt.8 2005-05-08 03:48:56.000000000 +0100 ++++ grub/docs/grub-md5-crypt.8 2006-06-24 15:40:02.000000000 +0100 +@@ -1,5 +1,5 @@ + .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.23. +-.TH GRUB-MD5-CRYPT "8" "May 2005" "grub-md5-crypt (GNU GRUB )" FSF ++.TH GRUB-MD5-CRYPT "8" "June 2006" "grub-md5-crypt (GNU GRUB )" FSF + .SH NAME + grub-md5-crypt \- Encrypt a password in MD5 format + .SH SYNOPSIS +diff -uprN grub-0.97/docs/grub-terminfo.8 grub/docs/grub-terminfo.8 +--- grub-0.97/docs/grub-terminfo.8 2005-05-08 03:48:56.000000000 +0100 ++++ grub/docs/grub-terminfo.8 2006-06-24 15:40:02.000000000 +0100 +@@ -1,5 +1,5 @@ + .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.23. +-.TH GRUB-TERMINFO "8" "May 2005" "grub-terminfo (GNU GRUB 0.97)" FSF ++.TH GRUB-TERMINFO "8" "June 2006" "grub-terminfo (GNU GRUB 0.97)" FSF + .SH NAME + grub-terminfo \- Generate a terminfo command from a terminfo name + .SH SYNOPSIS +diff -uprN grub-0.97/docs/grub.texi grub/docs/grub.texi +--- grub-0.97/docs/grub.texi 2005-05-08 03:59:59.000000000 +0100 ++++ grub/docs/grub.texi 2006-06-24 15:40:02.000000000 +0100 +@@ -2,22 +2,41 @@ + @c -*-texinfo-*- + @c %**start of header + @setfilename grub.info +-@settitle GRUB Manual +-@c %**end of header +- + @include version.texi +- ++@settitle GNU GRUB Manual @value{VERSION} + @c Unify all our little indices for now. + @syncodeindex fn cp + @syncodeindex vr cp + @syncodeindex ky cp + @syncodeindex pg cp + @syncodeindex tp cp ++@c %**end of header + + @footnotestyle separate + @paragraphindent 3 + @finalout + ++@copying ++This manual is for GNU GRUB (version @value{VERSION}, ++@value{UPDATED}). ++ ++Copyright @copyright{} 1999,2000,2001,2002,2004,2006 Free Software Foundation, Inc. ++ ++@quotation ++Permission is granted to copy, distribute and/or modify this document ++under the terms of the GNU Free Documentation License, Version 1.2 or ++any later version published by the Free Software Foundation; with no ++Invariant Sections, with the Front-Cover Texts being ``A GNU Manual,'' ++and with the Back-Cover Texts as in (a) below. A copy of the ++license is included in the section entitled ``GNU Free Documentation ++License.'' ++ ++(a) The FSF's Back-Cover Text is: ``You have freedom to copy and modify ++this GNU Manual, like GNU software. Copies published by the Free ++Software Foundation raise funds for GNU development.'' ++@end quotation ++@end copying ++ + @dircategory Kernel + @direntry + * GRUB: (grub). The GRand Unified Bootloader +@@ -34,53 +53,16 @@ + + @setchapternewpage odd + +-@ifinfo +-Copyright @copyright{} 1999,2000,2001,2002,2004 Free Software Foundation, Inc. +- +-Permission is granted to make and distribute verbatim copies of +-this manual provided the copyright notice and this permission notice +-are preserved on all copies. +- +-@ignore +-Permission is granted to process this file through TeX and print the +-results, provided the printed document carries a copying permission +-notice identical to this one except for the removal of this paragraph +-(this paragraph not being relevant to the printed manual). +- +-@end ignore +- +-Permission is granted to copy and distribute modified versions of this +-manual under the conditions for verbatim copying, provided also that +-the entire resulting derived work is distributed under the terms of a +-permission notice identical to this one. +- +-Permission is granted to copy and distribute translations of this manual +-into another language, under the above conditions for modified versions. +-@end ifinfo +- + @titlepage + @sp 10 +-@title the GRUB manual ++@title the GNU GRUB manual + @subtitle The GRand Unified Bootloader, version @value{VERSION}, @value{UPDATED}. + @author Gordon Matzigkeit + @author Yoshinori K. Okuji + @c The following two commands start the copyright page. + @page + @vskip 0pt plus 1filll +-Copyright @copyright{} 1999,2000,2001,2002,2004 Free Software Foundation, Inc. +- +-Permission is granted to make and distribute verbatim copies of +-this manual provided the copyright notice and this permission notice +-are preserved on all copies. +-Permission is granted to copy and distribute modified versions of this +-manual under the conditions for verbatim copying, provided that the entire +-resulting derived work is distributed under the terms of a permission +-notice identical to this one. +- +-Permission is granted to copy and distribute translations of this manual +-into another language, under the above conditions for modified versions, +-except that this permission notice may be stated in a translation approved +-by Free Software Foundation. ++@insertcopying + @end titlepage + + @c Output the table of contents at the beginning. +@@ -91,12 +73,14 @@ by Free Software Foundation. + + @ifnottex + @node Top +-@top GRUB manual ++@top GNU GRUB manual + + This is the documentation of GNU GRUB, the GRand Unified Bootloader, + a flexible and powerful boot loader program for @sc{pc}s. + + This edition documents version @value{VERSION}. ++ ++@insertcopying + @end ifnottex + + @menu +@@ -124,6 +108,7 @@ This edition documents version @value{VE + * Reporting bugs:: Where you should send a bug report + * Future:: Some future plans on GRUB + * Internals:: Hacking GRUB ++* Copying This Manual:: Copying This Manual + * Index:: + @end menu + +@@ -3965,6 +3950,16 @@ homepage}. + @include internals.texi + + ++@node Copying This Manual ++@appendix Copying This Manual ++ ++@menu ++* GNU Free Documentation License:: License for copying this manual. ++@end menu ++ ++@include fdl.texi ++ ++ + @node Index + @unnumbered Index + +diff -uprN grub-0.97/docs/Makefile.am grub/docs/Makefile.am +--- grub-0.97/docs/Makefile.am 2003-07-09 12:45:35.000000000 +0100 ++++ grub/docs/Makefile.am 2006-06-24 15:40:02.000000000 +0100 +@@ -1,5 +1,5 @@ + info_TEXINFOS = grub.texi multiboot.texi +-grub_TEXINFOS = internals.texi ++grub_TEXINFOS = internals.texi fdl.texi + EXAMPLES = boot.S kernel.c multiboot.h + multiboot_TEXINFOS = boot.S.texi kernel.c.texi multiboot.h.texi + man_MANS = grub.8 mbchk.1 grub-install.8 grub-md5-crypt.8 grub-terminfo.8 +diff -uprN grub-0.97/docs/Makefile.in grub/docs/Makefile.in +--- grub-0.97/docs/Makefile.in 2005-05-08 03:42:34.000000000 +0100 ++++ grub/docs/Makefile.in 2006-06-24 15:40:02.000000000 +0100 +@@ -202,7 +202,7 @@ sharedstatedir = @sharedstatedir@ + sysconfdir = @sysconfdir@ + target_alias = @target_alias@ + info_TEXINFOS = grub.texi multiboot.texi +-grub_TEXINFOS = internals.texi ++grub_TEXINFOS = internals.texi fdl.texi + EXAMPLES = boot.S kernel.c multiboot.h + multiboot_TEXINFOS = boot.S.texi kernel.c.texi multiboot.h.texi + man_MANS = grub.8 mbchk.1 grub-install.8 grub-md5-crypt.8 grub-terminfo.8 +diff -uprN grub-0.97/docs/mbchk.1 grub/docs/mbchk.1 +--- grub-0.97/docs/mbchk.1 2005-05-08 03:48:56.000000000 +0100 ++++ grub/docs/mbchk.1 2006-06-24 15:40:02.000000000 +0100 +@@ -1,5 +1,5 @@ + .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.23. +-.TH MBCHK "1" "May 2005" "mbchk (GNU GRUB 0.97)" FSF ++.TH MBCHK "1" "June 2006" "mbchk (GNU GRUB 0.97)" FSF + .SH NAME + mbchk \- check the format of a Multiboot kernel + .SH SYNOPSIS +diff -uprN grub-0.97/docs/multiboot.texi grub/docs/multiboot.texi +--- grub-0.97/docs/multiboot.texi 2003-07-09 12:45:36.000000000 +0100 ++++ grub/docs/multiboot.texi 2006-06-24 15:40:02.000000000 +0100 +@@ -1,32 +1,28 @@ + \input texinfo @c -*-texinfo-*- +-@c -*-texinfo-*- + @c %**start of header + @setfilename multiboot.info +-@settitle Multiboot Specification +-@c %**end of header +- ++@set VERSION 0.6.95 ++@settitle Multiboot Specification version @value{VERSION} + @c Unify all our little indices for now. + @syncodeindex fn cp + @syncodeindex vr cp + @syncodeindex ky cp + @syncodeindex pg cp + @syncodeindex tp cp ++@c %**end of header + + @footnotestyle separate + @paragraphindent 3 + @finalout + ++@copying ++Copyright @copyright{} 1995,96 Bryan Ford + +-@dircategory Kernel +-@direntry +-* Multiboot Specification: (multiboot). Multiboot Specification. +-@end direntry ++Copyright @copyright{} 1995,96 Erich Stefan Boleyn + +-@ifinfo +-Copyright @copyright{} 1995, 96 Bryan Ford +-Copyright @copyright{} 1995, 96 Erich Stefan Boleyn +-Copyright @copyright{} 1999, 2000, 2001, 2002 Free Software Foundation, Inc. ++Copyright @copyright{} 1999,2000,2001,2002,2005,2006 Free Software Foundation, Inc. + ++@quotation + Permission is granted to make and distribute verbatim copies of + this manual provided the copyright notice and this permission notice + are preserved on all copies. +@@ -36,7 +32,6 @@ Permission is granted to process this fi + results, provided the printed document carries a copying permission + notice identical to this one except for the removal of this paragraph + (this paragraph not being relevant to the printed manual). +- + @end ignore + + Permission is granted to copy and distribute modified versions of this +@@ -45,31 +40,23 @@ the entire resulting derived work is dis + permission notice identical to this one. + + Permission is granted to copy and distribute translations of this manual +-into another language, under the above conditions for modified versions. +-@end ifinfo ++into another language, under the above conditions for modified ++versions. ++@end quotation ++@end copying ++ ++@dircategory Kernel ++@direntry ++* Multiboot Specification: (multiboot). Multiboot Specification. ++@end direntry + + @titlepage + @sp 10 +-@title The Multiboot Specification ++@title The Multiboot Specification version @value{VERSION} + @author Yoshinori K. Okuji, Bryan Ford, Erich Stefan Boleyn, Kunihiro Ishiguro + @page +- + @vskip 0pt plus 1filll +-Copyright @copyright{} 1995, 96 Bryan Ford +-Copyright @copyright{} 1995, 96 Erich Stefan Boleyn +-Copyright @copyright{} 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +- +-Permission is granted to make and distribute verbatim copies of +-this manual provided the copyright notice and this permission notice +-are preserved on all copies. +- +-Permission is granted to copy and distribute modified versions of this +-manual under the conditions for verbatim copying, provided also that +-the entire resulting derived work is distributed under the terms of a +-permission notice identical to this one. +- +-Permission is granted to copy and distribute translations of this manual +-into another language, under the above conditions for modified versions. ++@insertcopying + @end titlepage + + @finalout +@@ -80,7 +67,9 @@ into another language, under the above c + @top Multiboot Specification + + This file documents Multiboot Specification, the proposal for the boot +-sequence standard. This edition documents version 0.6.93. ++sequence standard. This edition documents version @value{VERSION}. ++ ++@insertcopying + @end ifnottex + + @menu +@@ -426,7 +415,7 @@ mode table (@pxref{Boot information form + kernel. + + If bit 16 in the @samp{flags} word is set, then the fields at offsets +-8-24 in the Multiboot header are valid, and the boot loader should use ++12-28 in the Multiboot header are valid, and the boot loader should use + them instead of the fields in the actual executable header to calculate + where to load the OS image. This information does not need to be + provided if the kernel image is in @sc{elf} format, but it @emph{must} +@@ -677,7 +666,7 @@ follows: + @example + @group + +-------+-------+-------+-------+ +-| drive | part1 | part2 | part3 | ++| part3 | part2 | part1 | drive | + +-------+-------+-------+-------+ + @end group + @end example +@@ -1197,6 +1186,17 @@ Rewritten, using more strict words. + @item + The maintainer changes to the GNU GRUB maintainer team + @email{bug-grub@@gnu.org}, from Bryan Ford and Erich Stefan Boleyn. ++ ++@item ++The byte order of the @samp{boot_device} in Multiboot information is ++reversed. This was a mistake. ++ ++@item ++The offset of the address fields were wrong. ++ ++@item ++The format is adapted to a newer Texinfo, and the version number is ++specified more explicitly in the title. + @end itemize + + @item 0.6 +diff -uprN grub-0.97/grub/asmstub.c grub/grub/asmstub.c +--- grub-0.97/grub/asmstub.c 2005-02-16 20:45:14.000000000 +0000 ++++ grub/grub/asmstub.c 2008-03-28 13:22:28.000000000 +0000 +@@ -55,6 +55,10 @@ int grub_stage2 (void); + # endif /* ! BLKFLSBUF */ + #endif /* __linux__ */ + ++#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) ++# include ++#endif ++ + /* We want to prevent any circularararity in our stubs, as well as + libc name clashes. */ + #define WITHOUT_LIBC_STUBS 1 +@@ -777,7 +781,39 @@ get_diskinfo (int drive, struct geometry + + /* Open read/write, or read-only if that failed. */ + if (! read_only) +- disks[drive].flags = open (devname, O_RDWR); ++ { ++/* By default, kernel of FreeBSD does not allow overwriting MBR */ ++#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) ++#define GEOM_SYSCTL "kern.geom.debugflags" ++ int old_flags, flags; ++ size_t sizeof_int = sizeof (int); ++ ++ if (sysctlbyname (GEOM_SYSCTL, &old_flags, &sizeof_int, NULL, 0) != 0) ++ grub_printf ("failed to get " GEOM_SYSCTL "sysctl: %s\n", strerror (errno)); ++ ++ if ((old_flags & 0x10) == 0) ++ { ++ /* "allow foot shooting", see geom(4) */ ++ flags = old_flags | 0x10; ++ ++ if (sysctlbyname (GEOM_SYSCTL, NULL, NULL, &flags, sizeof (int)) != 0) ++ { ++ flags = old_flags; ++ grub_printf ("failed to set " GEOM_SYSCTL "sysctl: %s\n", strerror (errno)); ++ } ++ } ++ else ++ flags = old_flags; ++#endif ++ disks[drive].flags = open (devname, O_RDWR); ++#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) ++ if (flags != old_flags) ++ { ++ if (sysctlbyname (GEOM_SYSCTL, NULL, NULL, &old_flags, sizeof (int)) != 0) ++ grub_printf ("failed to set " GEOM_SYSCTL "sysctl: %s\n", strerror (errno)); ++ } ++#endif ++ } + + if (disks[drive].flags == -1) + { +@@ -926,7 +962,7 @@ hex_dump (void *buf, size_t size) + + int + biosdisk (int subfunc, int drive, struct geometry *geometry, +- int sector, int nsec, int segment) ++ unsigned int sector, int nsec, int segment) + { + char *buf; + int fd = geometry->flags; +diff -uprN grub-0.97/grub/main.c grub/grub/main.c +--- grub-0.97/grub/main.c 2003-07-09 12:45:36.000000000 +0100 ++++ grub/grub/main.c 2006-05-05 22:43:46.000000000 +0100 +@@ -32,6 +32,7 @@ int grub_stage2 (void); + #define WITHOUT_LIBC_STUBS 1 + #include + #include ++#include + + char *program_name = 0; + int use_config_file = 1; +@@ -192,6 +193,12 @@ main (int argc, char **argv) + perror ("strtoul"); + exit (1); + } ++ if (boot_drive >= NUM_DISKS) ++ { ++ fprintf (stderr, "boot_drive should be from 0 to %d\n", ++ NUM_DISKS - 1); ++ exit (1); ++ } + break; + + case OPT_NO_CONFIG_FILE: +diff -uprN grub-0.97/lib/device.c grub/lib/device.c +--- grub-0.97/lib/device.c 2005-03-28 00:14:25.000000000 +0100 ++++ grub/lib/device.c 2008-03-28 13:22:28.000000000 +0000 +@@ -69,9 +69,9 @@ struct hd_geometry + # ifndef CDROM_GET_CAPABILITY + # define CDROM_GET_CAPABILITY 0x5331 /* get capabilities */ + # endif /* ! CDROM_GET_CAPABILITY */ +-# ifndef BLKGETSIZE +-# define BLKGETSIZE _IO(0x12,96) /* return device size */ +-# endif /* ! BLKGETSIZE */ ++# ifndef BLKGETSIZE64 ++# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */ ++# endif /* ! BLKGETSIZE64 */ + #endif /* __linux__ */ + + /* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with +@@ -152,19 +152,19 @@ get_drive_geometry (struct geometry *geo + /* Linux */ + { + struct hd_geometry hdg; +- unsigned long nr; ++ unsigned long long nr; + + if (ioctl (fd, HDIO_GETGEO, &hdg)) + goto fail; + +- if (ioctl (fd, BLKGETSIZE, &nr)) ++ if (ioctl (fd, BLKGETSIZE64, &nr)) + goto fail; + + /* Got the geometry, so save it. */ + geom->cylinders = hdg.cylinders; + geom->heads = hdg.heads; + geom->sectors = hdg.sectors; +- geom->total_sectors = nr; ++ geom->total_sectors = nr / 512; + + goto success; + } +@@ -407,6 +407,12 @@ get_ataraid_disk_name (char *name, int u + { + sprintf (name, "/dev/ataraid/d%c", unit + '0'); + } ++ ++static void ++get_i2o_disk_name (char *name, char unit) ++{ ++ sprintf (name, "/dev/i2o/hd%c", unit); ++} + #endif + + /* Check if DEVICE can be read. If an error occurs, return zero, +@@ -801,6 +807,29 @@ init_device_map (char ***map, const char + } + } + } ++ ++ /* This is for I2O - we have /dev/i2o/hd */ ++ { ++ int unit; ++ ++ for (unit = 'a'; unit < 'f'; unit++) ++ { ++ char name[24]; ++ ++ get_i2o_disk_name (name, unit); ++ if (check_device (name)) ++ { ++ (*map)[num_hd + 0x80] = strdup (name); ++ assert ((*map)[num_hd + 0x80]); ++ ++ /* If the device map file is opened, write the map. */ ++ if (fp) ++ fprintf (fp, "(hd%d)\t%s\n", num_hd, name); ++ ++ num_hd++; ++ } ++ } ++ } + #endif /* __linux__ */ + + /* OK, close the device map file if opened. */ +@@ -861,6 +890,12 @@ write_to_partition (char **map, int driv + if (strcmp (dev + strlen(dev) - 5, "/disc") == 0) + strcpy (dev + strlen(dev) - 5, "/part"); + } ++ else ++ { ++ if ((strncmp (dev, "/dev/ataraid/", 13) == 0) || ++ (strncmp (dev, "/dev/rd/", 8) == 0)) ++ strcpy (dev + strlen(dev), "p"); ++ } + sprintf (dev + strlen(dev), "%d", ((partition >> 16) & 0xFF) + 1); + + /* Open the partition. */ +diff -uprN grub-0.97/netboot/3c509.c grub/netboot/3c509.c +--- grub-0.97/netboot/3c509.c 2003-07-09 12:45:37.000000000 +0100 ++++ grub/netboot/3c509.c 2002-01-02 21:56:40.000000000 +0000 +@@ -18,7 +18,7 @@ Author: Martin Renters. + + 3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) + +-$Id: 3c509.c,v 1.4 2002/01/02 21:56:40 okuji Exp $ ++$Id: 3c509.c 609 2002-01-02 21:56:40Z okuji $ + + ***************************************************************************/ + +diff -uprN grub-0.97/netboot/cs89x0.c grub/netboot/cs89x0.c +--- grub-0.97/netboot/cs89x0.c 2003-07-09 12:45:37.000000000 +0100 ++++ grub/netboot/cs89x0.c 2008-05-20 12:04:18.000000000 +0100 +@@ -1,3 +1,21 @@ ++/** ++ Per an email message from Russ Nelson on ++ 18 March 2008 this file is now licensed under GPL Version 2. ++ ++ From: Russ Nelson ++ Date: Tue, 18 Mar 2008 12:42:00 -0400 ++ Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot ++ -- quote from email ++ As copyright holder, if I say it doesn't conflict with the GPL, ++ then it doesn't conflict with the GPL. ++ ++ However, there's no point in causing people's brains to overheat, ++ so yes, I grant permission for the code to be relicensed under the ++ GPLv2. Please make sure that this change in licensing makes its ++ way upstream. -russ ++ -- quote from email ++**/ ++ + /* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */ + /* + Permission is granted to distribute the enclosed cs89x0.[ch] driver +diff -uprN grub-0.97/netboot/cs89x0.h grub/netboot/cs89x0.h +--- grub-0.97/netboot/cs89x0.h 2003-07-09 12:45:37.000000000 +0100 ++++ grub/netboot/cs89x0.h 2008-05-20 12:04:18.000000000 +0100 +@@ -1,3 +1,21 @@ ++/** ++ Per an email message from Russ Nelson on ++ 18 March 2008 this file is now licensed under GPL Version 2. ++ ++ From: Russ Nelson ++ Date: Tue, 18 Mar 2008 12:42:00 -0400 ++ Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot ++ -- quote from email ++ As copyright holder, if I say it doesn't conflict with the GPL, ++ then it doesn't conflict with the GPL. ++ ++ However, there's no point in causing people's brains to overheat, ++ so yes, I grant permission for the code to be relicensed under the ++ GPLv2. Please make sure that this change in licensing makes its ++ way upstream. -russ ++ -- quote from email ++**/ ++ + /* Copyright, 1988-1992, Russell Nelson, Crynwr Software + + This program is free software; you can redistribute it and/or modify +diff -uprN grub-0.97/netboot/etherboot.h grub/netboot/etherboot.h +--- grub-0.97/netboot/etherboot.h 2003-07-09 12:45:37.000000000 +0100 ++++ grub/netboot/etherboot.h 2006-09-08 13:56:22.000000000 +0100 +@@ -531,9 +531,6 @@ extern int ip_abort; + extern int network_ready; + extern struct rom_info rom; + extern struct arptable_t arptable[MAX_ARP]; +-extern struct bootpd_t bootp_data; +-#define BOOTP_DATA_ADDR (&bootp_data) +-extern unsigned char *end_of_rfc1533; + + /* config.c */ + extern struct nic nic; +diff -uprN grub-0.97/netboot/main.c grub/netboot/main.c +--- grub-0.97/netboot/main.c 2004-05-20 23:19:33.000000000 +0100 ++++ grub/netboot/main.c 2006-09-08 13:56:22.000000000 +0100 +@@ -56,7 +56,8 @@ static int vendorext_isvalid; + static unsigned long netmask; + static struct bootpd_t bootp_data; + static unsigned long xid; +-static unsigned char *end_of_rfc1533 = NULL; ++ ++#define BOOTP_DATA_ADDR (&bootp_data) + + #ifndef NO_DHCP_SUPPORT + #endif /* NO_DHCP_SUPPORT */ +@@ -967,7 +968,6 @@ decode_rfc1533 (unsigned char *p, int bl + + if (block == 0) + { +- end_of_rfc1533 = NULL; + vendorext_isvalid = 0; + + if (grub_memcmp (p, rfc1533_cookie, 4)) +@@ -1021,7 +1021,7 @@ decode_rfc1533 (unsigned char *p, int bl + } + else if (c == RFC1533_END) + { +- end_of_rfc1533 = endp = p; ++ endp = p; + continue; + } + else if (c == RFC1533_NETMASK) +diff -uprN grub-0.97/netboot/natsemi.c grub/netboot/natsemi.c +--- grub-0.97/netboot/natsemi.c 2003-07-09 12:45:38.000000000 +0100 ++++ grub/netboot/natsemi.c 2006-09-10 08:26:10.000000000 +0100 +@@ -608,7 +608,7 @@ natsemi_transmit(struct nic *nic, + const char *p) /* Packet */ + { + u32 status, to, nstype; +- u32 tx_status; ++ volatile u32 tx_status; + + /* Stop the transmitter */ + outl(TxOff, ioaddr + ChipCmd); +@@ -647,7 +647,7 @@ natsemi_transmit(struct nic *nic, + + to = currticks() + TX_TIMEOUT; + +- while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to)) ++ while (((tx_status=txd.cmdsts) & OWN) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { +diff -uprN grub-0.97/netboot/sis900.c grub/netboot/sis900.c +--- grub-0.97/netboot/sis900.c 2003-07-09 12:45:38.000000000 +0100 ++++ grub/netboot/sis900.c 2006-09-10 08:26:10.000000000 +0100 +@@ -901,7 +901,7 @@ sis900_transmit(struct nic *nic, + const char *p) /* Packet */ + { + u32 status, to, nstype; +- u32 tx_status; ++ volatile u32 tx_status; + + /* Stop the transmitter */ + outl(TxDIS, ioaddr + cr); +@@ -940,7 +940,7 @@ sis900_transmit(struct nic *nic, + + to = currticks() + TX_TIMEOUT; + +- while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to)) ++ while (((tx_status=txd.cmdsts) & OWN) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { +diff -uprN grub-0.97/netboot/sk_g16.c grub/netboot/sk_g16.c +--- grub-0.97/netboot/sk_g16.c 2003-07-09 12:45:38.000000000 +0100 ++++ grub/netboot/sk_g16.c 2002-01-02 21:56:40.000000000 +0000 +@@ -13,12 +13,12 @@ Changes to make it work with Etherboot b + * + * Module : sk_g16.c + * +- * Version : $Revision: 1.4 $ ++ * Version : $Revision: 609 $ + * + * Author : Patrick J.D. Weichmann + * + * Date Created : 94/05/26 +- * Last Updated : $Date: 2002/01/02 21:56:40 $ ++ * Last Updated : $Date: 2002-01-02 21:56:40 +0000 (mer 02 jan 2002) $ + * + * Description : Schneider & Koch G16 Ethernet Device Driver for + * Linux Kernel >= 1.1.22 +diff -uprN grub-0.97/netboot/sk_g16.h grub/netboot/sk_g16.h +--- grub-0.97/netboot/sk_g16.h 2003-07-09 12:45:38.000000000 +0100 ++++ grub/netboot/sk_g16.h 2000-07-29 20:22:54.000000000 +0100 +@@ -4,7 +4,7 @@ + * of the GNU Public License, incorporated herein by reference. + * + * Module : sk_g16.h +- * Version : $Revision: 1.3 $ ++ * Version : $Revision: 388 $ + * + * Author : M.Hipp (mhipp@student.uni-tuebingen.de) + * changes by : Patrick J.D. Weichmann +@@ -15,8 +15,8 @@ + * the am7990 (LANCE) chip used for writing a + * network device driver which uses this chip + * +- * $Log: sk_g16.h,v $ +- * Revision 1.3 2000/07/29 19:22:54 okuji ++ * $Log$ ++ * Revision 1.3 2000-07-29 19:22:54 okuji + * update the network support to etherboot-4.6.4. + * + -*/ +diff -uprN grub-0.97/stage1/Makefile.am grub/stage1/Makefile.am +--- grub-0.97/stage1/Makefile.am 2004-07-16 12:44:56.000000000 +0100 ++++ grub/stage1/Makefile.am 2007-11-05 01:29:46.000000000 +0000 +@@ -5,7 +5,7 @@ CLEANFILES = $(nodist_pkglib_DATA) + + # We can't use builtins or standard includes. + AM_CCASFLAGS = $(STAGE1_CFLAGS) -fno-builtin -nostdinc +-LDFLAGS = -nostdlib -Wl,-N,-Ttext,7C00 ++stage1_exec_LDFLAGS = -nostdlib -Wl,-N,-Ttext,7C00 + + noinst_PROGRAMS = stage1.exec + stage1_exec_SOURCES = stage1.S stage1.h +diff -uprN grub-0.97/stage1/Makefile.in grub/stage1/Makefile.in +--- grub-0.97/stage1/Makefile.in 2005-05-08 03:42:36.000000000 +0100 ++++ grub/stage1/Makefile.in 2007-11-05 01:29:46.000000000 +0000 +@@ -110,7 +110,7 @@ INSTALL_DATA = @INSTALL_DATA@ + INSTALL_PROGRAM = @INSTALL_PROGRAM@ + INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-LDFLAGS = -nostdlib -Wl,-N,-Ttext,7C00 ++LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ + LTLIBOBJS = @LTLIBOBJS@ +@@ -188,6 +188,7 @@ CLEANFILES = $(nodist_pkglib_DATA) + + # We can't use builtins or standard includes. + AM_CCASFLAGS = $(STAGE1_CFLAGS) -fno-builtin -nostdinc ++stage1_exec_LDFLAGS = -nostdlib -Wl,-N,-Ttext,7C00 + stage1_exec_SOURCES = stage1.S stage1.h + SUFFIXES = .exec + all: all-am +diff -uprN grub-0.97/stage2/bios.c grub/stage2/bios.c +--- grub-0.97/stage2/bios.c 2004-03-27 16:34:04.000000000 +0000 ++++ grub/stage2/bios.c 2008-03-28 13:22:28.000000000 +0000 +@@ -47,7 +47,7 @@ extern int get_diskinfo_floppy (int driv + return the error number. Otherwise, return 0. */ + int + biosdisk (int read, int drive, struct geometry *geometry, +- int sector, int nsec, int segment) ++ unsigned int sector, int nsec, int segment) + { + int err; + +diff -uprN grub-0.97/stage2/boot.c grub/stage2/boot.c +--- grub-0.97/stage2/boot.c 2004-03-30 12:44:08.000000000 +0100 ++++ grub/stage2/boot.c 2005-09-28 22:47:55.000000000 +0100 +@@ -1,7 +1,7 @@ + /* boot.c - load and bootstrap a kernel */ + /* + * GRUB -- GRand Unified Bootloader +- * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. ++ * Copyright (C) 1999,2000,2001,2002,2003,2004,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 +@@ -96,7 +96,7 @@ load_image (char *kernel, char *arg, ker + lh = (struct linux_kernel_header *) buffer; + + /* ELF loading supported if multiboot, FreeBSD and NetBSD. */ +- if ((type == KERNEL_TYPE_MULTIBOOT ++ if (((type == KERNEL_TYPE_MULTIBOOT && ! (flags & MULTIBOOT_AOUT_KLUDGE)) + || pu.elf->e_ident[EI_OSABI] == ELFOSABI_FREEBSD + || grub_strcmp (pu.elf->e_ident + EI_BRAND, "FreeBSD") == 0 + || suggested_type == KERNEL_TYPE_NETBSD) +diff -uprN grub-0.97/stage2/builtins.c grub/stage2/builtins.c +--- grub-0.97/stage2/builtins.c 2005-02-15 21:58:23.000000000 +0000 ++++ grub/stage2/builtins.c 2006-03-21 20:51:58.000000000 +0000 +@@ -3830,15 +3830,15 @@ setup_func (char *arg, int flags) + { + char tmp[16]; + grub_sprintf (tmp, ",%d", (partition >> 16) & 0xFF); +- grub_strncat (device, tmp, 256); ++ grub_strncat (device, tmp, sizeof (device)); + } + if ((partition & 0x00FF00) != 0x00FF00) + { + char tmp[16]; + grub_sprintf (tmp, ",%c", 'a' + ((partition >> 8) & 0xFF)); +- grub_strncat (device, tmp, 256); ++ grub_strncat (device, tmp, sizeof (device)); + } +- grub_strncat (device, ")", 256); ++ grub_strncat (device, ")", sizeof (device)); + } + + int embed_stage1_5 (char *stage1_5, int drive, int partition) +diff -uprN grub-0.97/stage2/disk_io.c grub/stage2/disk_io.c +--- grub-0.97/stage2/disk_io.c 2004-05-23 17:35:24.000000000 +0100 ++++ grub/stage2/disk_io.c 2008-03-28 13:22:28.000000000 +0000 +@@ -137,7 +137,7 @@ log2 (unsigned long word) + } + + int +-rawread (int drive, int sector, int byte_offset, int byte_len, char *buf) ++rawread (int drive, unsigned int sector, int byte_offset, int byte_len, char *buf) + { + int slen, sectors_per_vtrack; + int sector_size_bits = log2 (buf_geom.sector_size); +@@ -261,7 +261,7 @@ rawread (int drive, int sector, int byte + */ + if (disk_read_func) + { +- int sector_num = sector; ++ unsigned int sector_num = sector; + int length = buf_geom.sector_size - byte_offset; + if (length > size) + length = size; +@@ -291,7 +291,7 @@ rawread (int drive, int sector, int byte + + + int +-devread (int sector, int byte_offset, int byte_len, char *buf) ++devread (unsigned int sector, int byte_offset, int byte_len, char *buf) + { + /* + * Check partition boundaries +@@ -330,7 +330,7 @@ devread (int sector, int byte_offset, in + + #ifndef STAGE1_5 + int +-rawwrite (int drive, int sector, char *buf) ++rawwrite (int drive, unsigned int sector, char *buf) + { + if (sector == 0) + { +@@ -363,7 +363,7 @@ rawwrite (int drive, int sector, char *b + } + + int +-devwrite (int sector, int sector_count, char *buf) ++devwrite (unsigned int sector, int sector_count, char *buf) + { + #if defined(GRUB_UTIL) && defined(__linux__) + if (current_partition != 0xFFFFFF +diff -uprN grub-0.97/stage2/fsys_ffs.c grub/stage2/fsys_ffs.c +--- grub-0.97/stage2/fsys_ffs.c 2003-07-09 12:45:52.000000000 +0100 ++++ grub/stage2/fsys_ffs.c 2001-11-12 06:57:29.000000000 +0000 +@@ -50,7 +50,7 @@ + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd +- * $Id: fsys_ffs.c,v 1.10 2001/11/12 06:57:29 okuji Exp $ ++ * $Id: fsys_ffs.c 594 2001-11-12 06:57:29Z okuji $ + */ + + #ifdef FSYS_FFS +diff -uprN grub-0.97/stage2/fsys_ufs2.c grub/stage2/fsys_ufs2.c +--- grub-0.97/stage2/fsys_ufs2.c 2004-06-19 13:17:52.000000000 +0100 ++++ grub/stage2/fsys_ufs2.c 2004-06-19 13:17:52.000000000 +0100 +@@ -51,7 +51,7 @@ + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd +- * $Id: fsys_ufs2.c,v 1.2 2004/06/19 12:17:52 okuji Exp $ ++ * $Id: fsys_ufs2.c 841 2004-06-19 12:17:52Z okuji $ + */ + + #ifdef FSYS_UFS2 +diff -uprN grub-0.97/stage2/imgact_aout.h grub/stage2/imgact_aout.h +--- grub-0.97/stage2/imgact_aout.h 2003-07-09 12:45:53.000000000 +0100 ++++ grub/stage2/imgact_aout.h 1999-06-24 01:03:29.000000000 +0100 +@@ -32,7 +32,7 @@ + * SUCH DAMAGE. + * + * from: @(#)exec.h 8.1 (Berkeley) 6/11/93 +- * $Id: imgact_aout.h,v 1.1 1999/06/24 00:03:22 okuji Exp $ ++ * $Id: imgact_aout.h 98 1999-06-24 00:03:29Z okuji $ + */ + /* + * 11/23/95 - Kludge to get "ntohl" null macro added. -- ESB +diff -uprN grub-0.97/stage2/iso9660.h grub/stage2/iso9660.h +--- grub-0.97/stage2/iso9660.h 2004-03-27 16:02:38.000000000 +0000 ++++ grub/stage2/iso9660.h 2007-02-22 23:40:25.000000000 +0000 +@@ -73,11 +73,11 @@ typedef union { + + typedef struct __iso_16bit { + u_int16_t l, b; +-} iso_16bit_t __attribute__ ((packed)); ++} iso_16bit_t; + + typedef struct __iso_32bit { + u_int32_t l, b; +-} iso_32bit_t __attribute__ ((packed)); ++} iso_32bit_t; + + typedef u_int8_t iso_date_t[7]; + +diff -uprN grub-0.97/stage2/shared.h grub/stage2/shared.h +--- grub-0.97/stage2/shared.h 2004-06-19 17:40:09.000000000 +0100 ++++ grub/stage2/shared.h 2008-03-28 13:22:28.000000000 +0000 +@@ -499,7 +499,11 @@ struct vbe_mode + unsigned char linear_reserved_field_position; + unsigned long max_pixel_clock; + +- unsigned char reserved3[189]; ++ /* Reserved field to make structure to be 256 bytes long, VESA BIOS ++ Extension 3.0 Specification says to reserve 189 bytes here but ++ that doesn't make structure to be 256 bytes. So additional one is ++ added here. */ ++ unsigned char reserved3[189 + 1]; + } __attribute__ ((packed)); + + +@@ -807,7 +811,7 @@ int checkkey (void); + /* Low-level disk I/O */ + int get_diskinfo (int drive, struct geometry *geometry); + int biosdisk (int subfunc, int drive, struct geometry *geometry, +- int sector, int nsec, int segment); ++ unsigned int sector, int nsec, int segment); + void stop_floppy (void); + + /* Command-line interface functions. */ +@@ -920,10 +924,10 @@ int gunzip_test_header (void); + int gunzip_read (char *buf, int len); + #endif /* NO_DECOMPRESSION */ + +-int rawread (int drive, int sector, int byte_offset, int byte_len, char *buf); +-int devread (int sector, int byte_offset, int byte_len, char *buf); +-int rawwrite (int drive, int sector, char *buf); +-int devwrite (int sector, int sector_len, char *buf); ++int rawread (int drive, unsigned int sector, int byte_offset, int byte_len, char *buf); ++int devread (unsigned int sector, int byte_offset, int byte_len, char *buf); ++int rawwrite (int drive, unsigned int sector, char *buf); ++int devwrite (unsigned int sector, int sector_len, char *buf); + + /* Parse a device string and initialize the global parameters. */ + char *set_device (char *device); +diff -uprN grub-0.97/stage2/stage2.c grub/stage2/stage2.c +--- grub-0.97/stage2/stage2.c 2005-03-19 17:51:57.000000000 +0000 ++++ grub/stage2/stage2.c 2006-05-05 23:06:31.000000000 +0100 +@@ -651,7 +651,10 @@ restart: + *(new_heap++) = 0; + + if (config_entries) +- run_menu (heap, NULL, new_num_entries, new_heap, 0); ++ { ++ current_entryno = first_entry + entryno; ++ run_menu (heap, NULL, new_num_entries, new_heap, 0); ++ } + else + { + cls (); +@@ -727,7 +730,8 @@ restart: + cur_entry = get_entry (config_entries, first_entry + entryno, 1); + + /* Set CURRENT_ENTRYNO for the command "savedefault". */ +- current_entryno = first_entry + entryno; ++ if (config_entries) ++ current_entryno = first_entry + entryno; + + if (run_script (cur_entry, heap)) + { +diff -uprN grub-0.97/stage2/tparm.c grub/stage2/tparm.c +--- grub-0.97/stage2/tparm.c 2003-07-09 12:45:53.000000000 +0100 ++++ grub/stage2/tparm.c 2002-11-29 20:39:24.000000000 +0000 +@@ -63,7 +63,7 @@ typedef char grub_bool; + #define MAX_FORMAT_LEN 256 + #define max(a,b) ((a) > (b) ? (a) : (b)) + +-//MODULE_ID("$Id: tparm.c,v 1.1 2002/11/29 20:39:24 okuji Exp $") ++//MODULE_ID("$Id: tparm.c 708 2002-11-29 20:39:24Z okuji $") + + /* + * char * +diff -uprN grub-0.97/stamp-h.in grub/stamp-h.in +--- grub-0.97/stamp-h.in 1970-01-01 01:00:00.000000000 +0100 ++++ grub/stamp-h.in 1999-09-13 14:32:31.000000000 +0100 +@@ -0,0 +1 @@ ++timestamp +diff -uprN grub-0.97/THANKS grub/THANKS +--- grub-0.97/THANKS 2005-05-08 03:17:43.000000000 +0100 ++++ grub/THANKS 2006-03-21 20:51:58.000000000 +0000 +@@ -121,3 +121,4 @@ Vesa Jaaskelainen + Yury V. Umanets + Yuri Zaporogets ++Vitaly Fertman +diff -uprN grub-0.97/util/grub-install.in grub/util/grub-install.in +--- grub-0.97/util/grub-install.in 2004-07-24 19:57:31.000000000 +0100 ++++ grub/util/grub-install.in 2006-04-20 14:46:46.000000000 +0100 +@@ -112,8 +112,8 @@ convert () { + tmp_disk=`echo "$1" | sed 's%\([sh]d[0-9]*\).*%\1%'` + tmp_part=`echo "$1" | sed "s%$tmp_disk%%"` ;; + freebsd* | kfreebsd*-gnu) +- tmp_disk=`echo "$1" | sed 's%r\{0,1\}\([saw]d[0-9]*\).*$%r\1%' \ +- | sed 's%r\{0,1\}\(da[0-9]*\).*$%r\1%'` ++ tmp_disk=`echo "$1" | sed 's%r\{0,1\}\([saw]d[0-9]*\).*$%\1%' \ ++ | sed 's%r\{0,1\}\(da[0-9]*\).*$%\1%'` + tmp_part=`echo "$1" \ + | sed "s%.*/r\{0,1\}[saw]d[0-9]\(s[0-9]*[a-h]\)%\1%" \ + | sed "s%.*/r\{0,1\}da[0-9]\(s[0-9]*[a-h]\)%\1%"` +diff -uprN grub-0.97/util/mkbimage grub/util/mkbimage +--- grub-0.97/util/mkbimage 2004-07-24 19:57:31.000000000 +0100 ++++ grub/util/mkbimage 2007-02-22 16:01:03.000000000 +0000 +@@ -1,7 +1,7 @@ + #!/bin/sh + # MaKe a Bootable IMAGE --- 1.44, 2.88 and El Torito no-emulation mode + # C) 2001,2002,2003 Thierry Laronde +-# C) 2001,2002,2003 Robert Millan ++# C) 2001,2002,2003 Robert Millan + + + # This program is free software; you can redistribute it and/or modify +@@ -19,7 +19,7 @@ + # program's maintainer or write to: The Free Software Foundation, + # Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA. + +-# $Id: mkbimage,v 1.19 2004/07/21 14:43:04 robertmh Exp $ ++# $Id: mkbimage 1233 2007-02-22 16:01:03Z robertmh $ + + # Global variables + tarfile= +@@ -58,7 +58,7 @@ stage2_os_name= + + # Name by which this script was invoked. + program=`echo "$0" | sed -e 's/[^\/]*\///g'` +-version_number='$Revision: 1.19 $' ++version_number='$Revision: 1233 $' + + usage=" + Usage: $program [-hVF] [-t TYPE] [-d DIRECTORY] [-s FS_TYPE] -f TAR_FILE +@@ -94,15 +94,13 @@ Options: + display Version information and exit + + Copyright (c) 2001,2002,2003 Thierry Laronde . +-Copyright (c) 2001,2002 Robert Millan . + GPLed." + + version="mkbimage $version_number + +-Written by Thierry Laronde and Robert Millan. ++Written by Thierry Laronde. + + Copyright (c) 2001,2002,2003 Thierry Laronde . +-Copyright (c) 2001,2002,2003 Robert Millan . + + This is free software under the GPL version 2 or later; see the source for + copying conditions. There is NO warranty, not even for MERCHANTABILITY or diff --git a/stubdom/grub.patches/10graphics.diff b/stubdom/grub.patches/10graphics.diff new file mode 100644 index 0000000..d891c51 --- /dev/null +++ b/stubdom/grub.patches/10graphics.diff @@ -0,0 +1,2299 @@ +diff -Naur grub-0.97.orig/configure.ac grub-0.97/configure.ac +--- grub-0.97.orig/configure.ac 2005-05-07 23:36:03.000000000 -0300 ++++ grub-0.97/configure.ac 2005-06-12 20:56:49.000000000 -0300 +@@ -595,6 +595,11 @@ + [ --enable-diskless enable diskless support]) + AM_CONDITIONAL(DISKLESS_SUPPORT, test "x$enable_diskless" = xyes) + ++dnl Graphical splashscreen support ++AC_ARG_ENABLE(graphics, ++ [ --disable-graphics disable graphics terminal support]) ++AM_CONDITIONAL(GRAPHICS_SUPPORT, test "x$enable_graphics" != xno) ++ + dnl Hercules terminal + AC_ARG_ENABLE(hercules, + [ --disable-hercules disable hercules terminal support]) +diff -Naur grub-0.97.orig/stage2/asm.S grub-0.97/stage2/asm.S +--- grub-0.97.orig/stage2/asm.S 2004-06-19 13:55:22.000000000 -0300 ++++ grub-0.97/stage2/asm.S 2005-06-13 14:05:31.000000000 -0300 +@@ -2216,7 +2216,304 @@ + pop %ebx + pop %ebp + ret +- ++ ++ ++/* graphics mode functions */ ++#ifdef SUPPORT_GRAPHICS ++VARIABLE(cursorX) ++.word 0 ++VARIABLE(cursorY) ++.word 0 ++VARIABLE(cursorCount) ++.word 0 ++VARIABLE(cursorBuf) ++.byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++ ++ ++/* ++ * set_int1c_handler(void) ++ */ ++ENTRY(set_int1c_handler) ++ pushl %edi ++ ++ /* save the original int1c handler */ ++ movl $0x70, %edi ++ movw (%edi), %ax ++ movw %ax, ABS(int1c_offset) ++ movw 2(%edi), %ax ++ movw %ax, ABS(int1c_segment) ++ ++ /* save the new int1c handler */ ++ movw $ABS(int1c_handler), %ax ++ movw %ax, (%edi) ++ xorw %ax, %ax ++ movw %ax, 2(%edi) ++ ++ popl %edi ++ ret ++ ++ ++/* ++ * unset_int1c_handler(void) ++ */ ++ENTRY(unset_int1c_handler) ++ pushl %edi ++ ++ /* check if int1c_handler is set */ ++ movl $0x70, %edi ++ movw $ABS(int1c_handler), %ax ++ cmpw %ax, (%edi) ++ jne int1c_1 ++ xorw %ax, %ax ++ cmpw %ax, 2(%edi) ++ jne int1c_1 ++ ++ /* restore the original */ ++ movw ABS(int1c_offset), %ax ++ movw %ax, (%edi) ++ movw ABS(int1c_segment), %ax ++ movw %ax, 2(%edi) ++ ++int1c_1: ++ popl %edi ++ ret ++ ++ ++/* ++ * blinks graphics cursor ++ */ ++ .code16 ++write_data: ++ movw $0, %ax ++ movw %ax, %ds ++ ++ mov $0xA000, %ax /* video in es:di */ ++ mov %ax, %es ++ mov $80, %ax ++ movw $ABS(cursorY), %si ++ mov %ds:(%si), %bx ++ mul %bx ++ movw $ABS(cursorX), %si ++ mov %ds:(%si), %bx ++ shr $3, %bx /* %bx /= 8 */ ++ add %bx, %ax ++ mov %ax, %di ++ ++ movw $ABS(cursorBuf), %si /* fontBuf in ds:si */ ++ ++ /* prepare for data moving */ ++ mov $16, %dx /* altura da fonte */ ++ mov $80, %bx /* bytes por linha */ ++ ++write_loop: ++ movb %ds:(%si), %al ++ xorb $0xff, %al ++ movb %al, %ds:(%si) /* invert cursorBuf */ ++ movb %al, %es:(%di) /* write to video */ ++ add %bx, %di ++ inc %si ++ dec %dx ++ jg write_loop ++ ret ++ ++int1c_handler: ++ pusha ++ mov $0, %ax ++ mov %ax, %ds ++ mov $ABS(cursorCount), %si ++ mov %ds:(%si), %ax ++ inc %ax ++ mov %ax, %ds:(%si) ++ cmp $9, %ax ++ jne int1c_done ++ ++ mov $0, %ax ++ mov %ax, %ds:(%si) ++ call write_data ++ ++int1c_done: ++ popa ++ iret ++ /* call previous int1c handler */ ++ /* ljmp */ ++ .byte 0xea ++int1c_offset: .word 0 ++int1c_segment: .word 0 ++ .code32 ++ ++ ++/* ++ * unsigned char set_videomode(unsigned char mode) ++ * BIOS call "INT 10H Function 0h" to set video mode ++ * Call with %ah = 0x0 ++ * %al = video mode ++ * Returns old videomode. ++ */ ++ENTRY(set_videomode) ++ pushl %ebp ++ movl %esp,%ebp ++ pushl %ebx ++ pushl %ecx ++ ++ movb 8(%ebp), %cl ++ ++ call EXT_C(prot_to_real) ++ .code16 ++ ++ xorb %al, %al ++ movb $0xf, %ah ++ int $0x10 /* Get Current Video mode */ ++ movb %al, %ch ++ xorb %ah, %ah ++ movb %cl, %al ++ int $0x10 /* Set Video mode */ ++ ++ DATA32 call EXT_C(real_to_prot) ++ .code32 ++ ++ xorl %eax, %eax ++ movb %ch, %al ++ ++ popl %ecx ++ popl %ebx ++ popl %ebp ++ ret ++ ++ ++/* ++ * int get_videomode() ++ * BIOS call "INT 10H Function 0Fh" to get current video mode ++ * Call with %al = 0x0 ++ * %ah = 0xF ++ * Returns current videomode. ++ */ ++ENTRY(get_videomode) ++ pushl %ebp ++ movl %esp,%ebp ++ pushl %ebx ++ pushl %ecx ++ ++ call EXT_C(prot_to_real) ++ .code16 ++ ++ xorb %al, %al ++ movb $0xF, %ah ++ int $0x10 /* Get Current Video mode */ ++ movb %al, %cl /* For now we only want display mode */ ++ ++ DATA32 call EXT_C(real_to_prot) ++ .code32 ++ ++ xorl %eax, %eax ++ movb %cl, %al ++ ++ popl %ecx ++ popl %ebx ++ popl %ebp ++ ret ++ ++ ++/* ++ * unsigned char * graphics_get_font() ++ * BIOS call "INT 10H Function 11h" to set font ++ * Call with %ah = 0x11 ++ */ ++ENTRY(graphics_get_font) ++ push %ebp ++ push %ebx ++ push %ecx ++ push %edx ++ ++ call EXT_C(prot_to_real) ++ .code16 ++ ++ movw $0x1130, %ax ++ movb $6, %bh /* font 8x16 */ ++ int $0x10 ++ movw %bp, %dx ++ movw %es, %cx ++ ++ DATA32 call EXT_C(real_to_prot) ++ .code32 ++ ++ xorl %eax, %eax ++ movw %cx, %ax ++ shll $4, %eax ++ movw %dx, %ax ++ ++ pop %edx ++ pop %ecx ++ pop %ebx ++ pop %ebp ++ ret ++ ++ ++/* ++ * graphics_set_palette(index, red, green, blue) ++ * BIOS call "INT 10H Function 10h" to set individual dac register ++ * Call with %ah = 0x10 ++ * %bx = register number ++ * %ch = new value for green (0-63) ++ * %cl = new value for blue (0-63) ++ * %dh = new value for red (0-63) ++ */ ++ ++ENTRY(graphics_set_palette) ++ push %ebp ++ push %eax ++ push %ebx ++ push %ecx ++ push %edx ++ ++ movw $0x3c8, %bx /* address write mode register */ ++ ++ /* wait vertical retrace */ ++ movw $0x3da, %dx ++l1b: ++ inb %dx, %al /* wait vertical active display */ ++ test $8, %al ++ jnz l1b ++ ++l2b: ++ inb %dx, %al /* wait vertical retrace */ ++ test $8, %al ++ jnz l2b ++ ++ mov %bx, %dx ++ movb 0x18(%esp), %al /* index */ ++ outb %al, %dx ++ inc %dx ++ ++ movb 0x1c(%esp), %al /* red */ ++ outb %al, %dx ++ ++ movb 0x20(%esp), %al /* green */ ++ outb %al, %dx ++ ++ movb 0x24(%esp), %al /* blue */ ++ outb %al, %dx ++ ++ movw 0x18(%esp), %bx ++ ++ call EXT_C(prot_to_real) ++ .code16 ++ ++ movb %bl, %bh ++ movw $0x1000, %ax ++ int $0x10 ++ ++ DATA32 call EXT_C(real_to_prot) ++ .code32 ++ ++ pop %edx ++ pop %ecx ++ pop %ebx ++ pop %eax ++ pop %ebp ++ ret ++#endif /* SUPPORT_GRAPHICS */ ++ ++ + /* + * getrtsecs() + * if a seconds value can be read, read it and return it (BCD), +diff -Naur grub-0.97.orig/stage2/builtins.c grub-0.97/stage2/builtins.c +--- grub-0.97.orig/stage2/builtins.c 2005-02-15 19:58:23.000000000 -0200 ++++ grub-0.97/stage2/builtins.c 2005-06-13 18:44:03.000000000 -0300 +@@ -28,6 +28,10 @@ + #include + #include + ++#ifdef SUPPORT_GRAPHICS ++# include ++#endif ++ + #ifdef SUPPORT_NETBOOT + # define GRUB 1 + # include +@@ -237,12 +241,22 @@ + static int + boot_func (char *arg, int flags) + { ++ struct term_entry *prev_term = current_term; + /* Clear the int15 handler if we can boot the kernel successfully. + This assumes that the boot code never fails only if KERNEL_TYPE is + not KERNEL_TYPE_NONE. Is this assumption is bad? */ + if (kernel_type != KERNEL_TYPE_NONE) + unset_int15_handler (); + ++ /* if our terminal needed initialization, we should shut it down ++ * before booting the kernel, but we want to save what it was so ++ * we can come back if needed */ ++ if (current_term->shutdown) ++ { ++ current_term->shutdown(); ++ current_term = term_table; /* assumption: console is first */ ++ } ++ + #ifdef SUPPORT_NETBOOT + /* Shut down the networking. */ + cleanup_net (); +@@ -306,6 +320,13 @@ + return 1; + } + ++ /* if we get back here, we should go back to what our term was before */ ++ current_term = prev_term; ++ if (current_term->startup) ++ /* if our terminal fails to initialize, fall back to console since ++ * it should always work */ ++ if (current_term->startup() == 0) ++ current_term = term_table; /* we know that console is first */ + return 0; + } + +@@ -852,6 +873,251 @@ + }; + #endif /* SUPPORT_NETBOOT */ + ++#ifdef SUPPORT_GRAPHICS ++ ++static int splashimage_func(char *arg, int flags) { ++ int i; ++ ++ /* filename can only be 256 characters due to our buffer size */ ++ if (grub_strlen(arg) > 256) { ++ grub_printf("Splash image filename too large\n"); ++ grub_printf("Press any key to continue..."); ++ getkey(); ++ return 1; ++ } ++ ++ /* get rid of TERM_NEED_INIT from the graphics terminal. */ ++ for (i = 0; term_table[i].name; i++) { ++ if (grub_strcmp (term_table[i].name, "graphics") == 0) { ++ term_table[i].flags &= ~TERM_NEED_INIT; ++ break; ++ } ++ } ++ ++ graphics_set_splash(arg); ++ ++ if (flags == BUILTIN_CMDLINE && graphics_inited) { ++ graphics_end(); ++ if (graphics_init() == 0) { ++ /* Fallback to default term */ ++ current_term = term_table; ++ max_lines = current_term->max_lines; ++ if (current_term->cls) ++ current_term->cls(); ++ grub_printf("Failed to set splash image and/or graphics mode\n"); ++ return 1; ++ } ++ graphics_cls(); ++ } ++ ++ if (flags == BUILTIN_MENU) ++ current_term = term_table + i; ++ ++ return 0; ++} ++ ++static struct builtin builtin_splashimage = ++{ ++ "splashimage", ++ splashimage_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, ++ "splashimage FILE", ++ "Load FILE as the background image when in graphics mode." ++}; ++ ++ ++/* shade */ ++static int ++shade_func(char *arg, int flags) ++{ ++ int new_shade; ++ ++ if (!arg || safe_parse_maxint(&arg, &new_shade) == 0) ++ return (1); ++ ++ if (shade != new_shade) { ++ shade = new_shade; ++ if (flags == BUILTIN_CMDLINE && graphics_inited) { ++ graphics_end(); ++ graphics_init(); ++ graphics_cls(); ++ } ++ } ++ ++ return 0; ++} ++ ++static struct builtin builtin_shade = ++{ ++ "shade", ++ shade_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, ++ "shade INTEGER", ++ "If set to 0, disables the use of shaded text, else enables it." ++}; ++ ++ ++/* foreground */ ++static int ++foreground_func(char *arg, int flags) ++{ ++ if (grub_strlen(arg) == 6) { ++ int r = ((hex(arg[0]) << 4) | hex(arg[1])) >> 2; ++ int g = ((hex(arg[2]) << 4) | hex(arg[3])) >> 2; ++ int b = ((hex(arg[4]) << 4) | hex(arg[5])) >> 2; ++ ++ foreground = (r << 16) | (g << 8) | b; ++ if (graphics_inited) ++ graphics_set_palette(15, r, g, b); ++ ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct builtin builtin_foreground = ++{ ++ "foreground", ++ foreground_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, ++ "foreground RRGGBB", ++ "Sets the foreground color when in graphics mode." ++ "RR is red, GG is green, and BB blue. Numbers must be in hexadecimal." ++}; ++ ++ ++/* background */ ++static int ++background_func(char *arg, int flags) ++{ ++ if (grub_strlen(arg) == 6) { ++ int r = ((hex(arg[0]) << 4) | hex(arg[1])) >> 2; ++ int g = ((hex(arg[2]) << 4) | hex(arg[3])) >> 2; ++ int b = ((hex(arg[4]) << 4) | hex(arg[5])) >> 2; ++ ++ background = (r << 16) | (g << 8) | b; ++ if (graphics_inited) ++ graphics_set_palette(0, r, g, b); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct builtin builtin_background = ++{ ++ "background", ++ background_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, ++ "background RRGGBB", ++ "Sets the background color when in graphics mode." ++ "RR is red, GG is green, and BB blue. Numbers must be in hexadecimal." ++}; ++ ++ ++/* border */ ++static int ++border_func(char *arg, int flags) ++{ ++ if (grub_strlen(arg) == 6) { ++ int r = ((hex(arg[0]) << 4) | hex(arg[1])) >> 2; ++ int g = ((hex(arg[2]) << 4) | hex(arg[3])) >> 2; ++ int b = ((hex(arg[4]) << 4) | hex(arg[5])) >> 2; ++ ++ window_border = (r << 16) | (g << 8) | b; ++ if (graphics_inited) ++ graphics_set_palette(0x11, r, g, b); ++ ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct builtin builtin_border = ++{ ++ "border", ++ border_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, ++ "border RRGGBB", ++ "Sets the border video color when in graphics mode." ++ "RR is red, GG is green, and BB blue. Numbers must be in hexadecimal." ++}; ++ ++ ++/* viewport */ ++static int ++viewport_func (char *arg, int flags) ++{ ++ int i; ++ int x0 = 0, y0 = 0, x1 = 80, y1 = 30; ++ int *pos[4] = { &x0, &y0, &x1, &y1 }; ++ ++ if (!arg) ++ return (1); ++ for (i = 0; i < 4; i++) { ++ if (!*arg) ++ return (1); ++ while (*arg && (*arg == ' ' || *arg == '\t')) ++ ++arg; ++ if (!safe_parse_maxint(&arg, pos[i])) ++ return (1); ++ while (*arg && (*arg != ' ' && *arg != '\t')) ++ ++arg; ++ } ++ ++ /* minimum size is 65 colums and 16 rows */ ++ if (x0 > x1 - 66 || y0 > y1 - 16 || x0 < 0 || y0 < 0 || x1 > 80 || y1 > 30) ++ return 1; ++ ++ view_x0 = x0; ++ view_y0 = y0; ++ view_x1 = x1; ++ view_y1 = y1; ++ ++ if (flags == BUILTIN_CMDLINE && graphics_inited) { ++ graphics_end(); ++ graphics_init(); ++ graphics_cls(); ++ } ++ ++ return 0; ++} ++ ++static struct builtin builtin_viewport = ++{ ++ "viewport", ++ viewport_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, ++ "viewport x0 y0 x1 y1", ++ "Changes grub internals to output text in the window defined by" ++ " four parameters. The x and y parameters are 0 based. This option" ++ " only works with the graphics interface." ++}; ++ ++#endif /* SUPPORT_GRAPHICS */ ++ ++ ++/* clear */ ++static int ++clear_func() ++{ ++ if (current_term->cls) ++ current_term->cls(); ++ ++ return 0; ++} ++ ++static struct builtin builtin_clear = ++{ ++ "clear", ++ clear_func, ++ BUILTIN_CMDLINE | BUILTIN_HELP_LIST, ++ "clear", ++ "Clear the screen" ++}; ++ + + /* displayapm */ + static int +@@ -1454,14 +1720,20 @@ + + + /* help */ +-#define MAX_SHORT_DOC_LEN 39 +-#define MAX_LONG_DOC_LEN 66 +- + static int + help_func (char *arg, int flags) + { +- int all = 0; +- ++ int all = 0, max_short_doc_len, max_long_doc_len; ++ max_short_doc_len = 39; ++ max_long_doc_len = 66; ++#ifdef SUPPORT_GRAPHICS ++ if (grub_memcmp (current_term->name, "graphics", sizeof ("graphics") - 1) == 0) ++ { ++ max_short_doc_len = (view_x1 - view_x0 + 1) / 2 - 1; ++ max_long_doc_len = (view_x1 - view_x0) - 14; ++ } ++#endif ++ + if (grub_memcmp (arg, "--all", sizeof ("--all") - 1) == 0) + { + all = 1; +@@ -1491,13 +1763,13 @@ + + len = grub_strlen ((*builtin)->short_doc); + /* If the length of SHORT_DOC is too long, truncate it. */ +- if (len > MAX_SHORT_DOC_LEN - 1) +- len = MAX_SHORT_DOC_LEN - 1; ++ if (len > max_short_doc_len - 1) ++ len = max_short_doc_len - 1; + + for (i = 0; i < len; i++) + grub_putchar ((*builtin)->short_doc[i]); + +- for (; i < MAX_SHORT_DOC_LEN; i++) ++ for (; i < max_short_doc_len; i++) + grub_putchar (' '); + + if (! left) +@@ -1546,10 +1818,10 @@ + int i; + + /* If LEN is too long, fold DOC. */ +- if (len > MAX_LONG_DOC_LEN) ++ if (len > max_long_doc_len) + { + /* Fold this line at the position of a space. */ +- for (len = MAX_LONG_DOC_LEN; len > 0; len--) ++ for (len = max_long_doc_len; len > 0; len--) + if (doc[len - 1] == ' ') + break; + } +@@ -4085,7 +4357,7 @@ + }; + + +-#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) ++#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS) + /* terminal */ + static int + terminal_func (char *arg, int flags) +@@ -4244,17 +4516,29 @@ + end: + current_term = term_table + default_term; + current_term->flags = term_flags; +- ++ + if (lines) + max_lines = lines; + else +- /* 24 would be a good default value. */ +- max_lines = 24; +- ++ max_lines = current_term->max_lines; ++ + /* If the interface is currently the command-line, + restart it to repaint the screen. */ +- if (current_term != prev_term && (flags & BUILTIN_CMDLINE)) ++ if ((current_term != prev_term) && (flags & BUILTIN_CMDLINE)){ ++ if (prev_term->shutdown) ++ prev_term->shutdown(); ++ if (current_term->startup) { ++ /* If startup fails, return to previous term */ ++ if (current_term->startup() == 0) { ++ current_term = prev_term; ++ max_lines = current_term->max_lines; ++ if (current_term->cls) { ++ current_term->cls(); ++ } ++ } ++ } + grub_longjmp (restart_cmdline_env, 0); ++ } + + return 0; + } +@@ -4264,7 +4548,7 @@ + "terminal", + terminal_func, + BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST, +- "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules]", ++ "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules] [graphics]", + "Select a terminal. When multiple terminals are specified, wait until" + " you push any key to continue. If both console and serial are specified," + " the terminal to which you input a key first will be selected. If no" +@@ -4276,7 +4560,7 @@ + " seconds. The option --lines specifies the maximum number of lines." + " The option --silent is used to suppress messages." + }; +-#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */ ++#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */ + + + #ifdef SUPPORT_SERIAL +@@ -4795,13 +5079,20 @@ + /* The table of builtin commands. Sorted in dictionary order. */ + struct builtin *builtin_table[] = + { ++#ifdef SUPPORT_GRAPHICS ++ &builtin_background, ++#endif + &builtin_blocklist, + &builtin_boot, + #ifdef SUPPORT_NETBOOT + &builtin_bootp, + #endif /* SUPPORT_NETBOOT */ ++#ifdef SUPPORT_GRAPHICS ++ &builtin_border, ++#endif + &builtin_cat, + &builtin_chainloader, ++ &builtin_clear, + &builtin_cmp, + &builtin_color, + &builtin_configfile, +@@ -4821,6 +5112,9 @@ + &builtin_embed, + &builtin_fallback, + &builtin_find, ++#ifdef SUPPORT_GRAPHICS ++ &builtin_foreground, ++#endif + &builtin_fstest, + &builtin_geometry, + &builtin_halt, +@@ -4864,9 +5158,13 @@ + #endif /* SUPPORT_SERIAL */ + &builtin_setkey, + &builtin_setup, +-#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) ++#ifdef SUPPORT_GRAPHICS ++ &builtin_shade, ++ &builtin_splashimage, ++#endif /* SUPPORT_GRAPHICS */ ++#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS) + &builtin_terminal, +-#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */ ++#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */ + #ifdef SUPPORT_SERIAL + &builtin_terminfo, + #endif /* SUPPORT_SERIAL */ +@@ -4880,5 +5178,8 @@ + &builtin_unhide, + &builtin_uppermem, + &builtin_vbeprobe, ++#ifdef SUPPORT_GRAPHICS ++ &builtin_viewport, ++#endif + 0 + }; +diff -Naur grub-0.97.orig/stage2/char_io.c grub-0.97/stage2/char_io.c +--- grub-0.97.orig/stage2/char_io.c 2005-02-01 18:51:23.000000000 -0200 ++++ grub-0.97/stage2/char_io.c 2005-06-12 20:56:49.000000000 -0300 +@@ -29,12 +29,17 @@ + # include + #endif + ++#ifdef SUPPORT_GRAPHICS ++# include ++#endif ++ + #ifndef STAGE1_5 + struct term_entry term_table[] = + { + { + "console", + 0, ++ 24, + console_putchar, + console_checkkey, + console_getkey, +@@ -43,13 +48,16 @@ + console_cls, + console_setcolorstate, + console_setcolor, +- console_setcursor ++ console_setcursor, ++ 0, ++ 0 + }, + #ifdef SUPPORT_SERIAL + { + "serial", + /* A serial device must be initialized. */ + TERM_NEED_INIT, ++ 24, + serial_putchar, + serial_checkkey, + serial_getkey, +@@ -58,6 +66,8 @@ + serial_cls, + serial_setcolorstate, + 0, ++ 0, ++ 0, + 0 + }, + #endif /* SUPPORT_SERIAL */ +@@ -65,6 +75,7 @@ + { + "hercules", + 0, ++ 24, + hercules_putchar, + console_checkkey, + console_getkey, +@@ -73,11 +84,30 @@ + hercules_cls, + hercules_setcolorstate, + hercules_setcolor, +- hercules_setcursor ++ hercules_setcursor, ++ 0, ++ 0 + }, + #endif /* SUPPORT_HERCULES */ ++#ifdef SUPPORT_GRAPHICS ++ { "graphics", ++ TERM_NEED_INIT, /* flags */ ++ 30, /* number of lines */ ++ graphics_putchar, /* putchar */ ++ console_checkkey, /* checkkey */ ++ console_getkey, /* getkey */ ++ graphics_getxy, /* getxy */ ++ graphics_gotoxy, /* gotoxy */ ++ graphics_cls, /* cls */ ++ graphics_setcolorstate, /* setcolorstate */ ++ graphics_setcolor, /* setcolor */ ++ graphics_setcursor, /* nocursor */ ++ graphics_init, /* initialize */ ++ graphics_end /* shutdown */ ++ }, ++#endif /* SUPPORT_GRAPHICS */ + /* This must be the last entry. */ +- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ++ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + /* This must be console. */ +@@ -305,9 +335,10 @@ + + /* XXX: These should be defined in shared.h, but I leave these here, + until this code is freezed. */ +-#define CMDLINE_WIDTH 78 + #define CMDLINE_MARGIN 10 +- ++ ++ /* command-line limits */ ++ int cmdline_width = 78, col_start = 0; + int xpos, lpos, c, section; + /* The length of PROMPT. */ + int plen; +@@ -338,7 +369,7 @@ + + /* If the cursor is in the first section, display the first section + instead of the second. */ +- if (section == 1 && plen + lpos < CMDLINE_WIDTH) ++ if (section == 1 && plen + lpos < cmdline_width) + cl_refresh (1, 0); + else if (xpos - count < 1) + cl_refresh (1, 0); +@@ -354,7 +385,7 @@ + grub_putchar ('\b'); + } + else +- gotoxy (xpos, getxy () & 0xFF); ++ gotoxy (xpos + col_start, getxy () & 0xFF); + } + } + +@@ -364,7 +395,7 @@ + lpos += count; + + /* If the cursor goes outside, scroll the screen to the right. */ +- if (xpos + count >= CMDLINE_WIDTH) ++ if (xpos + count >= cmdline_width) + cl_refresh (1, 0); + else + { +@@ -383,7 +414,7 @@ + } + } + else +- gotoxy (xpos, getxy () & 0xFF); ++ gotoxy (xpos + col_start, getxy () & 0xFF); + } + } + +@@ -398,14 +429,14 @@ + if (full) + { + /* Recompute the section number. */ +- if (lpos + plen < CMDLINE_WIDTH) ++ if (lpos + plen < cmdline_width) + section = 0; + else +- section = ((lpos + plen - CMDLINE_WIDTH) +- / (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN) + 1); ++ section = ((lpos + plen - cmdline_width) ++ / (cmdline_width - 1 - CMDLINE_MARGIN) + 1); + + /* From the start to the end. */ +- len = CMDLINE_WIDTH; ++ len = cmdline_width; + pos = 0; + grub_putchar ('\r'); + +@@ -445,8 +476,8 @@ + if (! full) + offset = xpos - 1; + +- start = ((section - 1) * (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN) +- + CMDLINE_WIDTH - plen - CMDLINE_MARGIN); ++ start = ((section - 1) * (cmdline_width - 1 - CMDLINE_MARGIN) ++ + cmdline_width - plen - CMDLINE_MARGIN); + xpos = lpos + 1 - start; + start += offset; + } +@@ -471,7 +502,7 @@ + + /* If the cursor is at the last position, put `>' or a space, + depending on if there are more characters in BUF. */ +- if (pos == CMDLINE_WIDTH) ++ if (pos == cmdline_width) + { + if (start + len < llen) + grub_putchar ('>'); +@@ -488,7 +519,7 @@ + grub_putchar ('\b'); + } + else +- gotoxy (xpos, getxy () & 0xFF); ++ gotoxy (xpos + col_start, getxy () & 0xFF); + } + + /* Initialize the command-line. */ +@@ -518,10 +549,10 @@ + + llen += l; + lpos += l; +- if (xpos + l >= CMDLINE_WIDTH) ++ if (xpos + l >= cmdline_width) + cl_refresh (1, 0); +- else if (xpos + l + llen - lpos > CMDLINE_WIDTH) +- cl_refresh (0, CMDLINE_WIDTH - xpos); ++ else if (xpos + l + llen - lpos > cmdline_width) ++ cl_refresh (0, cmdline_width - xpos); + else + cl_refresh (0, l + llen - lpos); + } +@@ -533,12 +564,22 @@ + grub_memmove (buf + lpos, buf + lpos + count, llen - count + 1); + llen -= count; + +- if (xpos + llen + count - lpos > CMDLINE_WIDTH) +- cl_refresh (0, CMDLINE_WIDTH - xpos); ++ if (xpos + llen + count - lpos > cmdline_width) ++ cl_refresh (0, cmdline_width - xpos); + else + cl_refresh (0, llen + count - lpos); + } + ++ max_lines = current_term->max_lines; ++#ifdef SUPPORT_GRAPHICS ++ if (grub_memcmp (current_term->name, "graphics", sizeof ("graphics") - 1) == 0) ++ { ++ cmdline_width = (view_x1 - view_x0) - 2; ++ col_start = view_x0; ++ max_lines = view_y1 - view_y0; ++ } ++#endif ++ + plen = grub_strlen (prompt); + llen = grub_strlen (cmdline); + +@@ -1006,6 +1047,48 @@ + } + #endif /* ! STAGE1_5 */ + ++#ifndef STAGE1_5 ++/* Internal pager. */ ++int ++do_more (void) ++{ ++ if (count_lines >= 0) ++ { ++ count_lines++; ++ if (count_lines >= max_lines - 2) ++ { ++ int tmp; ++ ++ /* It's important to disable the feature temporarily, because ++ the following grub_printf call will print newlines. */ ++ count_lines = -1; ++ ++ grub_printf("\n"); ++ if (current_term->setcolorstate) ++ current_term->setcolorstate (COLOR_STATE_HIGHLIGHT); ++ ++ grub_printf ("[Hit return to continue]"); ++ ++ if (current_term->setcolorstate) ++ current_term->setcolorstate (COLOR_STATE_NORMAL); ++ ++ ++ do ++ { ++ tmp = ASCII_CHAR (getkey ()); ++ } ++ while (tmp != '\n' && tmp != '\r'); ++ grub_printf ("\r \r"); ++ ++ /* Restart to count lines. */ ++ count_lines = 0; ++ return 1; ++ } ++ } ++ return 0; ++} ++#endif ++ + /* Display an ASCII character. */ + void + grub_putchar (int c) +@@ -1034,38 +1117,11 @@ + + if (c == '\n') + { ++ int flag; + /* Internal `more'-like feature. */ +- if (count_lines >= 0) +- { +- count_lines++; +- if (count_lines >= max_lines - 2) +- { +- int tmp; +- +- /* It's important to disable the feature temporarily, because +- the following grub_printf call will print newlines. */ +- count_lines = -1; +- +- if (current_term->setcolorstate) +- current_term->setcolorstate (COLOR_STATE_HIGHLIGHT); +- +- grub_printf ("\n[Hit return to continue]"); +- +- if (current_term->setcolorstate) +- current_term->setcolorstate (COLOR_STATE_NORMAL); +- +- do +- { +- tmp = ASCII_CHAR (getkey ()); +- } +- while (tmp != '\n' && tmp != '\r'); +- grub_printf ("\r \r"); +- +- /* Restart to count lines. */ +- count_lines = 0; +- return; +- } +- } ++ flag = do_more (); ++ if (flag) ++ return; + } + + current_term->putchar (c); +@@ -1090,7 +1146,7 @@ + cls (void) + { + /* If the terminal is dumb, there is no way to clean the terminal. */ +- if (current_term->flags & TERM_DUMB) ++ if (current_term->flags & TERM_DUMB) + grub_putchar ('\n'); + else + current_term->cls (); +@@ -1217,6 +1273,16 @@ + return ! errnum; + } + ++void ++grub_memcpy(void *dest, const void *src, int len) ++{ ++ int i; ++ register char *d = (char*)dest, *s = (char*)src; ++ ++ for (i = 0; i < len; i++) ++ d[i] = s[i]; ++} ++ + void * + grub_memmove (void *to, const void *from, int len) + { +diff -Naur grub-0.97.orig/stage2/cmdline.c grub-0.97/stage2/cmdline.c +--- grub-0.97.orig/stage2/cmdline.c 2004-08-16 20:23:01.000000000 -0300 ++++ grub-0.97/stage2/cmdline.c 2005-06-12 20:56:49.000000000 -0300 +@@ -50,10 +50,11 @@ + void + print_cmdline_message (int forever) + { +- printf (" [ Minimal BASH-like line editing is supported. For the first word, TAB\n" +- " lists possible command completions. Anywhere else TAB lists the possible\n" +- " completions of a device/filename.%s ]\n", +- (forever ? "" : " ESC at any time exits.")); ++ grub_printf(" [ Minimal BASH-like line editing is supported. For\n" ++ " the first word, TAB lists possible command\n" ++ " completions. Anywhere else TAB lists the possible\n" ++ " completions of a device/filename.%s ]\n", ++ (forever ? "" : " ESC at any time\n exits.")); + } + + /* Find the builtin whose command name is COMMAND and return the +diff -Naur grub-0.97.orig/stage2/graphics.c grub-0.97/stage2/graphics.c +--- grub-0.97.orig/stage2/graphics.c 1969-12-31 21:00:00.000000000 -0300 ++++ grub-0.97/stage2/graphics.c 2005-06-13 19:13:31.000000000 -0300 +@@ -0,0 +1,585 @@ ++/* ++ * graphics.c - graphics mode support for GRUB ++ * Implemented as a terminal type by Jeremy Katz based ++ * on a patch by Paulo César Pereira de Andrade ++ * Options and enhancements made by Herton Ronaldo Krzesinski ++ * ++ * ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2001,2002 Red Hat, Inc. ++ * Portions copyright (C) 2000 Conectiva, 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, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifdef SUPPORT_GRAPHICS ++ ++#include ++#include ++#include ++ ++int saved_videomode; ++unsigned char *font8x16; ++ ++int graphics_inited = 0; ++static char splashimage[256]; ++ ++int shade = 1, no_cursor = 0; ++ ++#define VSHADOW VSHADOW1 ++unsigned char VSHADOW1[38400]; ++unsigned char VSHADOW2[38400]; ++unsigned char VSHADOW4[38400]; ++unsigned char VSHADOW8[38400]; ++ ++/* define the default viewable area */ ++int view_x0 = 0; ++int view_y0 = 0; ++int view_x1 = 80; ++int view_y1 = 30; ++ ++/* text buffer has to be kept around so that we can write things as we ++ * scroll and the like */ ++unsigned short text[80 * 30]; ++ ++/* graphics options */ ++int foreground = (63 << 16) | (63 << 8) | (63), background = 0, window_border = 0; ++ ++/* current position */ ++static int fontx = 0; ++static int fonty = 0; ++ ++/* global state so that we don't try to recursively scroll or cursor */ ++static int no_scroll = 0; ++ ++/* color state */ ++static int graphics_standard_color = A_NORMAL; ++static int graphics_normal_color = A_NORMAL; ++static int graphics_highlight_color = A_REVERSE; ++static int graphics_current_color = A_NORMAL; ++static color_state graphics_color_state = COLOR_STATE_STANDARD; ++ ++static inline void outb(unsigned short port, unsigned char val) ++{ ++ __asm __volatile ("outb %0,%1"::"a" (val), "d" (port)); ++} ++ ++static void MapMask(int value) { ++ outb(0x3c4, 2); ++ outb(0x3c5, value); ++} ++ ++/* bit mask register */ ++static void BitMask(int value) { ++ outb(0x3ce, 8); ++ outb(0x3cf, value); ++} ++ ++/* move the graphics cursor location to col, row */ ++static void graphics_setxy(int col, int row) { ++ if (col >= view_x0 && col < view_x1) { ++ fontx = col; ++ cursorX = col << 3; ++ } ++ if (row >= view_y0 && row < view_y1) { ++ fonty = row; ++ cursorY = row << 4; ++ } ++} ++ ++/* scroll the screen */ ++static void graphics_scroll() { ++ int i, j, k; ++ ++ /* we don't want to scroll recursively... that would be bad */ ++ if (no_scroll) ++ return; ++ no_scroll = 1; ++ ++ /* disable pager temporarily */ ++ k = count_lines; ++ count_lines = -1; ++ ++ /* move everything up a line */ ++ for (j = view_y0 + 1; j < view_y1; j++) { ++ graphics_gotoxy(view_x0, j - 1); ++ for (i = view_x0; i < view_x1; i++) { ++ graphics_putchar(text[j * 80 + i]); ++ } ++ } ++ ++ /* last line should be blank */ ++ graphics_gotoxy(view_x0, view_y1 - 1); ++ for (i = view_x0; i < view_x1; i++) ++ graphics_putchar(' '); ++ graphics_setxy(view_x0, view_y1 - 1); ++ ++ count_lines = k; ++ ++ no_scroll = 0; ++} ++ ++/* Set the splash image */ ++void graphics_set_splash(char *splashfile) { ++ grub_strcpy(splashimage, splashfile); ++} ++ ++/* Get the current splash image */ ++char *graphics_get_splash(void) { ++ return splashimage; ++} ++ ++/* ++ * Initialize a vga16 graphics display with the palette based off of ++ * the image in splashimage. If the image doesn't exist, leave graphics ++ * mode. The mode initiated is 12h. From "Ralf Brown's Interrupt List": ++ * text/ text pixel pixel colors disply scrn system ++ * grph resol box resolution pages addr ++ * 12h G 80x30 8x16 640x480 16/256K . A000 VGA,ATI VIP ++ * G 80x30 8x16 640x480 16/64 . A000 ATI EGA Wonder ++ * G . . 640x480 16 . . UltraVision+256K EGA ++ */ ++int graphics_init() ++{ ++ if (!graphics_inited) { ++ saved_videomode = set_videomode(0x12); ++ if (get_videomode() != 0x12) { ++ set_videomode(saved_videomode); ++ return 0; ++ } ++ graphics_inited = 1; ++ } ++ else ++ return 1; ++ ++ font8x16 = (unsigned char*)graphics_get_font(); ++ ++ /* make sure that the highlight color is set correctly */ ++ graphics_highlight_color = ((graphics_normal_color >> 4) | ++ ((graphics_normal_color & 0xf) << 4)); ++ ++ graphics_cls(); ++ ++ if (!read_image(splashimage)) { ++ grub_printf("Failed to read splash image (%s)\n", splashimage); ++ grub_printf("Press any key to continue..."); ++ getkey(); ++ set_videomode(saved_videomode); ++ graphics_inited = 0; ++ return 0; ++ } ++ ++ set_int1c_handler(); ++ ++ return 1; ++} ++ ++/* Leave graphics mode */ ++void graphics_end(void) ++{ ++ if (graphics_inited) { ++ unset_int1c_handler(); ++ set_videomode(saved_videomode); ++ graphics_inited = 0; ++ no_cursor = 0; ++ } ++} ++ ++/* Print ch on the screen. Handle any needed scrolling or the like */ ++void graphics_putchar(int ch) { ++ ch &= 0xff; ++ ++ graphics_cursor(0); ++ ++ if (ch == '\n') { ++ if (fonty + 1 < view_y1) ++ graphics_setxy(fontx, fonty + 1); ++ else ++ graphics_scroll(); ++ graphics_cursor(1); ++ return; ++ } else if (ch == '\r') { ++ graphics_setxy(view_x0, fonty); ++ graphics_cursor(1); ++ return; ++ } ++ ++ graphics_cursor(0); ++ ++ text[fonty * 80 + fontx] = ch; ++ text[fonty * 80 + fontx] &= 0x00ff; ++ if (graphics_current_color & 0xf0) ++ text[fonty * 80 + fontx] |= 0x100; ++ ++ graphics_cursor(0); ++ ++ if ((fontx + 1) >= view_x1) { ++ graphics_setxy(view_x0, fonty); ++ if (fonty + 1 < view_y1) ++ graphics_setxy(view_x0, fonty + 1); ++ else ++ graphics_scroll(); ++ graphics_cursor(1); ++ do_more (); ++ graphics_cursor(0); ++ } else { ++ graphics_setxy(fontx + 1, fonty); ++ } ++ ++ graphics_cursor(1); ++} ++ ++/* get the current location of the cursor */ ++int graphics_getxy(void) { ++ return (fontx << 8) | fonty; ++} ++ ++void graphics_gotoxy(int x, int y) { ++ graphics_cursor(0); ++ ++ graphics_setxy(x, y); ++ ++ graphics_cursor(1); ++} ++ ++void graphics_cls(void) { ++ int i; ++ unsigned char *mem, *s1, *s2, *s4, *s8; ++ ++ graphics_cursor(0); ++ graphics_gotoxy(view_x0, view_y0); ++ ++ mem = (unsigned char*)VIDEOMEM; ++ s1 = (unsigned char*)VSHADOW1; ++ s2 = (unsigned char*)VSHADOW2; ++ s4 = (unsigned char*)VSHADOW4; ++ s8 = (unsigned char*)VSHADOW8; ++ ++ for (i = 0; i < 80 * 30; i++) ++ text[i] = ' '; ++ graphics_cursor(1); ++ ++ BitMask(0xff); ++ ++ /* plane 1 */ ++ MapMask(1); ++ grub_memcpy(mem, s1, 38400); ++ ++ /* plane 2 */ ++ MapMask(2); ++ grub_memcpy(mem, s2, 38400); ++ ++ /* plane 3 */ ++ MapMask(4); ++ grub_memcpy(mem, s4, 38400); ++ ++ /* plane 4 */ ++ MapMask(8); ++ grub_memcpy(mem, s8, 38400); ++ ++ MapMask(15); ++ ++ if (no_cursor) { ++ no_cursor = 0; ++ set_int1c_handler(); ++ } ++} ++ ++void graphics_setcolorstate (color_state state) { ++ switch (state) { ++ case COLOR_STATE_STANDARD: ++ graphics_current_color = graphics_standard_color; ++ break; ++ case COLOR_STATE_NORMAL: ++ graphics_current_color = graphics_normal_color; ++ break; ++ case COLOR_STATE_HIGHLIGHT: ++ graphics_current_color = graphics_highlight_color; ++ break; ++ default: ++ graphics_current_color = graphics_standard_color; ++ break; ++ } ++ ++ graphics_color_state = state; ++} ++ ++void graphics_setcolor (int normal_color, int highlight_color) { ++ graphics_normal_color = normal_color; ++ graphics_highlight_color = highlight_color; ++ ++ graphics_setcolorstate (graphics_color_state); ++} ++ ++int graphics_setcursor (int on) { ++ if (!no_cursor && !on) { ++ no_cursor = 1; ++ unset_int1c_handler(); ++ graphics_cursor(0); ++ } ++ else if(no_cursor && on) { ++ no_cursor = 0; ++ set_int1c_handler(); ++ graphics_cursor(1); ++ } ++ return 0; ++} ++ ++/* Read in the splashscreen image and set the palette up appropriately. ++ * Format of splashscreen is an xpm (can be gzipped) with 16 colors and ++ * 640x480. */ ++int read_image(char *s) ++{ ++ char buf[32], pal[16], c; ++ unsigned char base, mask, *s1, *s2, *s4, *s8; ++ unsigned i, len, idx, colors, x, y, width, height; ++ ++ if (!grub_open(s)) ++ return 0; ++ ++ /* read header */ ++ if (!grub_read((char*)&buf, 10) || grub_memcmp(buf, "/* XPM */\n", 10)) { ++ grub_close(); ++ return 0; ++ } ++ ++ /* parse info */ ++ while (grub_read(&c, 1)) { ++ if (c == '"') ++ break; ++ } ++ ++ while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ++ ; ++ ++ i = 0; ++ width = c - '0'; ++ while (grub_read(&c, 1)) { ++ if (c >= '0' && c <= '9') ++ width = width * 10 + c - '0'; ++ else ++ break; ++ } ++ while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ++ ; ++ ++ height = c - '0'; ++ while (grub_read(&c, 1)) { ++ if (c >= '0' && c <= '9') ++ height = height * 10 + c - '0'; ++ else ++ break; ++ } ++ while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ++ ; ++ ++ colors = c - '0'; ++ while (grub_read(&c, 1)) { ++ if (c >= '0' && c <= '9') ++ colors = colors * 10 + c - '0'; ++ else ++ break; ++ } ++ ++ base = 0; ++ while (grub_read(&c, 1) && c != '"') ++ ; ++ ++ /* palette */ ++ for (i = 0, idx = 1; i < colors; i++) { ++ len = 0; ++ ++ while (grub_read(&c, 1) && c != '"') ++ ; ++ grub_read(&c, 1); /* char */ ++ base = c; ++ grub_read(buf, 4); /* \t c # */ ++ ++ while (grub_read(&c, 1) && c != '"') { ++ if (len < sizeof(buf)) ++ buf[len++] = c; ++ } ++ ++ if (len == 6 && idx < 15) { ++ int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2; ++ int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2; ++ int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2; ++ ++ pal[idx] = base; ++ graphics_set_palette(idx, r, g, b); ++ ++idx; ++ } ++ } ++ ++ x = y = len = 0; ++ ++ s1 = (unsigned char*)VSHADOW1; ++ s2 = (unsigned char*)VSHADOW2; ++ s4 = (unsigned char*)VSHADOW4; ++ s8 = (unsigned char*)VSHADOW8; ++ ++ for (i = 0; i < 38400; i++) ++ s1[i] = s2[i] = s4[i] = s8[i] = 0; ++ ++ /* parse xpm data */ ++ while (y < height) { ++ while (1) { ++ if (!grub_read(&c, 1)) { ++ grub_close(); ++ return 0; ++ } ++ if (c == '"') ++ break; ++ } ++ ++ while (grub_read(&c, 1) && c != '"') { ++ for (i = 1; i < 15; i++) ++ if (pal[i] == c) { ++ c = i; ++ break; ++ } ++ ++ mask = 0x80 >> (x & 7); ++ if (c & 1) ++ s1[len + (x >> 3)] |= mask; ++ if (c & 2) ++ s2[len + (x >> 3)] |= mask; ++ if (c & 4) ++ s4[len + (x >> 3)] |= mask; ++ if (c & 8) ++ s8[len + (x >> 3)] |= mask; ++ ++ if (++x >= 640) { ++ x = 0; ++ ++ if (y < 480) ++ len += 80; ++ ++y; ++ } ++ } ++ } ++ ++ grub_close(); ++ ++ graphics_set_palette(0, (background >> 16), (background >> 8) & 63, ++ background & 63); ++ graphics_set_palette(15, (foreground >> 16), (foreground >> 8) & 63, ++ foreground & 63); ++ graphics_set_palette(0x11, (window_border >> 16), (window_border >> 8) & 63, ++ window_border & 63); ++ ++ return 1; ++} ++ ++/* Convert a character which is a hex digit to the appropriate integer */ ++int hex(int v) ++{ ++ if (v >= 'A' && v <= 'F') ++ return (v - 'A' + 10); ++ if (v >= 'a' && v <= 'f') ++ return (v - 'a' + 10); ++ return (v - '0'); ++} ++ ++void graphics_cursor(int set) { ++ unsigned char *pat, *mem, *ptr, chr[16 << 2]; ++ int i, ch, invert, offset; ++ ++ if (set && (no_cursor || no_scroll)) ++ return; ++ ++ offset = cursorY * 80 + fontx; ++ ch = text[fonty * 80 + fontx] & 0xff; ++ invert = (text[fonty * 80 + fontx] & 0xff00) != 0; ++ pat = font8x16 + (ch << 4); ++ ++ mem = (unsigned char*)VIDEOMEM + offset; ++ ++ if (!set) { ++ for (i = 0; i < 16; i++) { ++ unsigned char mask = pat[i]; ++ ++ if (!invert) { ++ chr[i ] = ((unsigned char*)VSHADOW1)[offset]; ++ chr[16 + i] = ((unsigned char*)VSHADOW2)[offset]; ++ chr[32 + i] = ((unsigned char*)VSHADOW4)[offset]; ++ chr[48 + i] = ((unsigned char*)VSHADOW8)[offset]; ++ ++ if (shade) { ++ if (ch == DISP_VERT || ch == DISP_LL || ++ ch == DISP_UR || ch == DISP_LR) { ++ unsigned char pmask = ~(pat[i] >> 1); ++ ++ chr[i ] &= pmask; ++ chr[16 + i] &= pmask; ++ chr[32 + i] &= pmask; ++ chr[48 + i] &= pmask; ++ } ++ if (i > 0 && ch != DISP_VERT) { ++ unsigned char pmask = ~(pat[i - 1] >> 1); ++ ++ chr[i ] &= pmask; ++ chr[16 + i] &= pmask; ++ chr[32 + i] &= pmask; ++ chr[48 + i] &= pmask; ++ if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) { ++ pmask = ~pat[i - 1]; ++ ++ chr[i ] &= pmask; ++ chr[16 + i] &= pmask; ++ chr[32 + i] &= pmask; ++ chr[48 + i] &= pmask; ++ } ++ } ++ } ++ chr[i ] |= mask; ++ chr[16 + i] |= mask; ++ chr[32 + i] |= mask; ++ chr[48 + i] |= mask; ++ ++ offset += 80; ++ } ++ else { ++ chr[i ] = mask; ++ chr[16 + i] = mask; ++ chr[32 + i] = mask; ++ chr[48 + i] = mask; ++ } ++ } ++ } ++ else { ++ MapMask(15); ++ ptr = mem; ++ for (i = 0; i < 16; i++, ptr += 80) { ++ cursorBuf[i] = pat[i]; ++ *ptr = ~pat[i]; ++ } ++ return; ++ } ++ ++ offset = 0; ++ for (i = 1; i < 16; i <<= 1, offset += 16) { ++ int j; ++ ++ MapMask(i); ++ ptr = mem; ++ for (j = 0; j < 16; j++, ptr += 80) ++ *ptr = chr[j + offset]; ++ } ++ ++ MapMask(15); ++} ++ ++#endif /* SUPPORT_GRAPHICS */ +diff -Naur grub-0.97.orig/stage2/graphics.h grub-0.97/stage2/graphics.h +--- grub-0.97.orig/stage2/graphics.h 1969-12-31 21:00:00.000000000 -0300 ++++ grub-0.97/stage2/graphics.h 2005-06-12 20:56:49.000000000 -0300 +@@ -0,0 +1,44 @@ ++/* graphics.h - graphics console interface */ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2002 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, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef GRAPHICS_H ++#define GRAPHICS_H ++ ++/* magic constant */ ++#define VIDEOMEM 0xA0000 ++ ++/* function prototypes */ ++char *graphics_get_splash(void); ++ ++int read_image(char *s); ++void graphics_cursor(int set); ++ ++/* function prototypes for asm functions */ ++void * graphics_get_font(); ++void graphics_set_palette(int idx, int red, int green, int blue); ++void set_int1c_handler(); ++void unset_int1c_handler(); ++ ++extern short cursorX, cursorY; ++extern char cursorBuf[16]; ++extern int shade; ++extern int view_x0, view_y0, view_x1, view_y1; ++ ++#endif /* GRAPHICS_H */ +diff -Naur grub-0.97.orig/stage2/Makefile.am grub-0.97/stage2/Makefile.am +--- grub-0.97.orig/stage2/Makefile.am 2005-02-02 18:37:35.000000000 -0200 ++++ grub-0.97/stage2/Makefile.am 2005-06-12 20:56:49.000000000 -0300 +@@ -7,7 +7,7 @@ + fat.h filesys.h freebsd.h fs.h hercules.h i386-elf.h \ + imgact_aout.h iso9660.h jfs.h mb_header.h mb_info.h md5.h \ + nbi.h pc_slice.h serial.h shared.h smp-imps.h term.h \ +- terminfo.h tparm.h nbi.h ufs2.h vstafs.h xfs.h ++ terminfo.h tparm.h nbi.h ufs2.h vstafs.h xfs.h graphics.h + EXTRA_DIST = setjmp.S apm.S $(noinst_SCRIPTS) + + # For . +@@ -19,7 +19,7 @@ + disk_io.c fsys_ext2fs.c fsys_fat.c fsys_ffs.c fsys_iso9660.c \ + fsys_jfs.c fsys_minix.c fsys_reiserfs.c fsys_ufs2.c \ + fsys_vstafs.c fsys_xfs.c gunzip.c md5.c serial.c stage2.c \ +- terminfo.c tparm.c ++ terminfo.c tparm.c graphics.c + libgrub_a_CFLAGS = $(GRUB_CFLAGS) -I$(top_srcdir)/lib \ + -DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \ + -DFSYS_ISO9660=1 -DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 \ +@@ -79,8 +79,14 @@ + HERCULES_FLAGS = + endif + ++if GRAPHICS_SUPPORT ++GRAPHICS_FLAGS = -DSUPPORT_GRAPHICS=1 ++else ++GRAPHICS_FLAGS = ++endif ++ + STAGE2_COMPILE = $(STAGE2_CFLAGS) -fno-builtin -nostdinc \ +- $(NETBOOT_FLAGS) $(SERIAL_FLAGS) $(HERCULES_FLAGS) ++ $(NETBOOT_FLAGS) $(SERIAL_FLAGS) $(HERCULES_FLAGS) $(GRAPHICS_FLAGS) + + STAGE1_5_LINK = -nostdlib -Wl,-N -Wl,-Ttext -Wl,2000 + STAGE1_5_COMPILE = $(STAGE2_COMPILE) -DNO_DECOMPRESSION=1 -DSTAGE1_5=1 +@@ -90,7 +96,8 @@ + cmdline.c common.c console.c disk_io.c fsys_ext2fs.c \ + fsys_fat.c fsys_ffs.c fsys_iso9660.c fsys_jfs.c fsys_minix.c \ + fsys_reiserfs.c fsys_ufs2.c fsys_vstafs.c fsys_xfs.c gunzip.c \ +- hercules.c md5.c serial.c smp-imps.c stage2.c terminfo.c tparm.c ++ hercules.c md5.c serial.c smp-imps.c stage2.c terminfo.c tparm.c \ ++ graphics.c + pre_stage2_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS) + pre_stage2_exec_CCASFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS) + pre_stage2_exec_LDFLAGS = $(PRE_STAGE2_LINK) +diff -Naur grub-0.97.orig/stage2/shared.h grub-0.97/stage2/shared.h +--- grub-0.97.orig/stage2/shared.h 2004-06-19 13:40:09.000000000 -0300 ++++ grub-0.97/stage2/shared.h 2005-06-12 20:56:49.000000000 -0300 +@@ -792,6 +792,11 @@ + /* Set the cursor position. */ + void gotoxy (int x, int y); + ++/* Internal pager ++ Returns 1 = if pager was used ++ 0 = if pager wasn't used */ ++int do_more (void); ++ + /* Displays an ASCII character. IBM displays will translate some + characters to special graphical ones (see the DISP_* constants). */ + void grub_putchar (int c); +@@ -871,6 +876,7 @@ + int grub_tolower (int c); + int grub_isspace (int c); + int grub_strncat (char *s1, const char *s2, int n); ++void grub_memcpy(void *dest, const void *src, int len); + void *grub_memmove (void *to, const void *from, int len); + void *grub_memset (void *start, int c, int len); + int grub_strncat (char *s1, const char *s2, int n); +diff -Naur grub-0.97.orig/stage2/stage2.c grub-0.97/stage2/stage2.c +--- grub-0.97.orig/stage2/stage2.c 2005-03-19 14:51:57.000000000 -0300 ++++ grub-0.97/stage2/stage2.c 2005-06-13 22:38:08.000000000 -0300 +@@ -20,6 +20,12 @@ + #include + #include + ++#ifdef SUPPORT_GRAPHICS ++# include ++#endif ++ ++int col_start, col_end, row_start, box_size; ++ + grub_jmp_buf restart_env; + + #if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS) +@@ -105,13 +111,13 @@ + if (highlight && current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_HIGHLIGHT); + +- gotoxy (2, y); ++ gotoxy (2 + col_start, y); + grub_putchar (' '); +- for (x = 3; x < 75; x++) ++ for (x = 3 + col_start; x < (col_end - 5); x++) + { +- if (*entry && x <= 72) ++ if (*entry && x <= (col_end - 8)) + { +- if (x == 72) ++ if (x == (col_end - 8)) + grub_putchar (DISP_RIGHT); + else + grub_putchar (*entry++); +@@ -119,7 +125,7 @@ + else + grub_putchar (' '); + } +- gotoxy (74, y); ++ gotoxy ((col_end - 6), y); + + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_STANDARD); +@@ -131,7 +137,7 @@ + { + int i; + +- gotoxy (77, y + 1); ++ gotoxy ((col_end - 3), y + 1); + + if (first) + grub_putchar (DISP_UP); +@@ -151,14 +157,14 @@ + menu_entries++; + } + +- gotoxy (77, y + size); ++ gotoxy ((col_end - 3), y + size); + + if (*menu_entries) + grub_putchar (DISP_DOWN); + else + grub_putchar (' '); + +- gotoxy (74, y + entryno + 1); ++ gotoxy ((col_end - 6), y + entryno + 1); + } + + static void +@@ -196,30 +202,30 @@ + if (current_term->setcolorstate) + current_term->setcolorstate (COLOR_STATE_NORMAL); + +- gotoxy (1, y); ++ gotoxy (1 + col_start, y); + + grub_putchar (DISP_UL); +- for (i = 0; i < 73; i++) ++ for (i = col_start; i < (col_end - 7); i++) + grub_putchar (DISP_HORIZ); + grub_putchar (DISP_UR); + + i = 1; + while (1) + { +- gotoxy (1, y + i); ++ gotoxy (1 + col_start, y + i); + + if (i > size) + break; + + grub_putchar (DISP_VERT); +- gotoxy (75, y + i); ++ gotoxy ((col_end - 5), y + i); + grub_putchar (DISP_VERT); + + i++; + } + + grub_putchar (DISP_LL); +- for (i = 0; i < 73; i++) ++ for (i = col_start; i < (col_end - 7); i++) + grub_putchar (DISP_HORIZ); + grub_putchar (DISP_LR); + +@@ -233,6 +239,7 @@ + { + int c, time1, time2 = -1, first_entry = 0; + char *cur_entry = 0; ++ struct term_entry *prev_term = NULL; + + /* + * Main loop for menu UI. +@@ -250,6 +257,22 @@ + } + } + ++ col_start = 0; ++ col_end = 80; ++ row_start = 0; ++ box_size = 12; ++ /* if we're using viewport we need to make sure to setup ++ coordinates correctly. */ ++#ifdef SUPPORT_GRAPHICS ++ if (grub_memcmp (current_term->name, "graphics", sizeof ("graphics") - 1) == 0) ++ { ++ col_start = view_x0; ++ col_end = view_x1; ++ row_start = view_y0; ++ box_size = (view_y1 - view_y0) - 13; ++ } ++#endif ++ + /* If the timeout was expired or wasn't set, force to show the menu + interface. */ + if (grub_timeout < 0) +@@ -302,36 +325,36 @@ + if (current_term->flags & TERM_DUMB) + print_entries_raw (num_entries, first_entry, menu_entries); + else +- print_border (3, 12); ++ print_border (3 + row_start, box_size); + + grub_printf ("\n\ +- Use the %c and %c keys to select which entry is highlighted.\n", ++ Use the %c and %c keys to select which entry is highlighted.\n", + DISP_UP, DISP_DOWN); + + if (! auth && password) + { + printf ("\ +- Press enter to boot the selected OS or \'p\' to enter a\n\ +- password to unlock the next set of features."); ++ Press enter to boot the selected OS or \'p\' to enter a\n\ ++ password to unlock the next set of features."); + } + else + { + if (config_entries) + printf ("\ +- Press enter to boot the selected OS, \'e\' to edit the\n\ +- commands before booting, or \'c\' for a command-line."); ++ Press enter to boot the selected OS, \'e\' to edit the\n\ ++ commands before booting, or \'c\' for a command-line."); + else + printf ("\ +- Press \'b\' to boot, \'e\' to edit the selected command in the\n\ +- boot sequence, \'c\' for a command-line, \'o\' to open a new line\n\ +- after (\'O\' for before) the selected line, \'d\' to remove the\n\ +- selected line, or escape to go back to the main menu."); ++ Press \'b\' to boot, \'e\' to edit the selected command in the\n\ ++ boot sequence, \'c\' for a command-line, \'o\' to open a new line\n\ ++ after (\'O\' for before) the selected line, \'d\' to remove the\n\ ++ selected line, or escape to go back to the main menu."); + } + + if (current_term->flags & TERM_DUMB) + grub_printf ("\n\nThe selected entry is %d ", entryno); + else +- print_entries (3, 12, first_entry, entryno, menu_entries); ++ print_entries (3 + row_start, box_size, first_entry, entryno, menu_entries); + } + + /* XX using RT clock now, need to initialize value */ +@@ -358,10 +381,10 @@ + entryno, grub_timeout); + else + { +- gotoxy (3, 22); +- grub_printf ("The highlighted entry will be booted automatically in %d seconds. ", ++ gotoxy (3 + col_start, 10 + box_size + row_start); ++ grub_printf (" The highlighted entry will be booted automatically in %d seconds. ", + grub_timeout); +- gotoxy (74, 4 + entryno); ++ gotoxy ((col_end - 6), 4 + entryno + row_start); + } + + grub_timeout--; +@@ -387,12 +410,12 @@ + if (current_term->flags & TERM_DUMB) + grub_putchar ('\r'); + else +- gotoxy (3, 22); ++ gotoxy (3 + col_start, 10 + box_size + row_start); + printf (" "); + grub_timeout = -1; + fallback_entryno = -1; + if (! (current_term->flags & TERM_DUMB)) +- gotoxy (74, 4 + entryno); ++ gotoxy ((col_end - 6), 4 + entryno + row_start); + } + + /* We told them above (at least in SUPPORT_SERIAL) to use +@@ -408,12 +431,12 @@ + { + if (entryno > 0) + { +- print_entry (4 + entryno, 0, ++ print_entry (4 + entryno + row_start, 0, + get_entry (menu_entries, + first_entry + entryno, + 0)); + entryno--; +- print_entry (4 + entryno, 1, ++ print_entry (4 + entryno + row_start, 1, + get_entry (menu_entries, + first_entry + entryno, + 0)); +@@ -421,7 +444,7 @@ + else if (first_entry > 0) + { + first_entry--; +- print_entries (3, 12, first_entry, entryno, ++ print_entries (3 + row_start, box_size, first_entry, entryno, + menu_entries); + } + } +@@ -433,29 +456,29 @@ + entryno++; + else + { +- if (entryno < 11) ++ if (entryno < (box_size - 1)) + { +- print_entry (4 + entryno, 0, ++ print_entry (4 + entryno + row_start, 0, + get_entry (menu_entries, + first_entry + entryno, + 0)); + entryno++; +- print_entry (4 + entryno, 1, ++ print_entry (4 + entryno + row_start, 1, + get_entry (menu_entries, + first_entry + entryno, + 0)); + } +- else if (num_entries > 12 + first_entry) ++ else if (num_entries > box_size + first_entry) + { + first_entry++; +- print_entries (3, 12, first_entry, entryno, menu_entries); ++ print_entries (3 + row_start, box_size, first_entry, entryno, menu_entries); + } + } + } + else if (c == 7) + { + /* Page Up */ +- first_entry -= 12; ++ first_entry -= box_size; + if (first_entry < 0) + { + entryno += first_entry; +@@ -463,20 +486,20 @@ + if (entryno < 0) + entryno = 0; + } +- print_entries (3, 12, first_entry, entryno, menu_entries); ++ print_entries (3 + row_start, box_size, first_entry, entryno, menu_entries); + } + else if (c == 3) + { + /* Page Down */ +- first_entry += 12; ++ first_entry += box_size; + if (first_entry + entryno + 1 >= num_entries) + { +- first_entry = num_entries - 12; ++ first_entry = num_entries - box_size; + if (first_entry < 0) + first_entry = 0; + entryno = num_entries - first_entry - 1; + } +- print_entries (3, 12, first_entry, entryno, menu_entries); ++ print_entries (3 + row_start, box_size, first_entry, entryno, menu_entries); + } + + if (config_entries) +@@ -489,7 +512,7 @@ + if ((c == 'd') || (c == 'o') || (c == 'O')) + { + if (! (current_term->flags & TERM_DUMB)) +- print_entry (4 + entryno, 0, ++ print_entry (4 + entryno + row_start, 0, + get_entry (menu_entries, + first_entry + entryno, + 0)); +@@ -537,7 +560,7 @@ + + if (entryno >= num_entries) + entryno--; +- if (first_entry && num_entries < 12 + first_entry) ++ if (first_entry && num_entries < box_size + first_entry) + first_entry--; + } + +@@ -549,7 +572,7 @@ + grub_printf ("\n"); + } + else +- print_entries (3, 12, first_entry, entryno, menu_entries); ++ print_entries (3 + row_start, box_size, first_entry, entryno, menu_entries); + } + + cur_entry = menu_entries; +@@ -570,7 +593,7 @@ + if (current_term->flags & TERM_DUMB) + grub_printf ("\r "); + else +- gotoxy (1, 21); ++ gotoxy (1 + col_start, 9 + box_size + row_start); + + /* Wipe out the previously entered password */ + grub_memset (entered, 0, sizeof (entered)); +@@ -714,6 +737,15 @@ + + cls (); + setcursor (1); ++ /* if our terminal needed initialization, we should shut it down ++ * before booting the kernel, but we want to save what it was so ++ * we can come back if needed */ ++ prev_term = current_term; ++ if (current_term->shutdown) ++ { ++ current_term->shutdown(); ++ current_term = term_table; /* assumption: console is first */ ++ } + + while (1) + { +@@ -748,6 +780,13 @@ + break; + } + ++ /* if we get back here, we should go back to what our term was before */ ++ current_term = prev_term; ++ if (current_term->startup) ++ /* if our terminal fails to initialize, fall back to console since ++ * it should always work */ ++ if (current_term->startup() == 0) ++ current_term = term_table; /* we know that console is first */ + show_menu = 1; + goto restart; + } +@@ -1050,6 +1089,16 @@ + while (is_preset); + } + ++ /* go ahead and make sure the terminal is setup */ ++ if (current_term->startup) ++ { ++ /* If initialization fails, go back to default terminal */ ++ if (current_term->startup() == 0) ++ { ++ current_term = term_table; ++ } ++ } ++ + if (! num_entries) + { + /* If no acceptable config file, goto command-line, starting +diff -Naur grub-0.97.orig/stage2/term.h grub-0.97/stage2/term.h +--- grub-0.97.orig/stage2/term.h 2003-07-09 08:45:53.000000000 -0300 ++++ grub-0.97/stage2/term.h 2005-06-13 14:07:40.000000000 -0300 +@@ -60,6 +60,8 @@ + const char *name; + /* The feature flags defined above. */ + unsigned long flags; ++ /* Default for maximum number of lines if not specified */ ++ unsigned short max_lines; + /* Put a character. */ + void (*putchar) (int c); + /* Check if any input character is available. */ +@@ -79,6 +81,10 @@ + void (*setcolor) (int normal_color, int highlight_color); + /* Turn on/off the cursor. */ + int (*setcursor) (int on); ++ /* function to start a terminal */ ++ int (*startup) (void); ++ /* function to use to shutdown a terminal */ ++ void (*shutdown) (void); + }; + + /* This lists up available terminals. */ +@@ -124,4 +130,24 @@ + int hercules_setcursor (int on); + #endif + ++#ifdef SUPPORT_GRAPHICS ++extern int foreground, background, window_border, graphics_inited, saved_videomode; ++ ++void graphics_set_splash(char *splashfile); ++int set_videomode(int mode); ++int get_videomode(void); ++void graphics_putchar (int c); ++int graphics_getxy(void); ++void graphics_gotoxy(int x, int y); ++void graphics_cls(void); ++void graphics_setcolorstate (color_state state); ++void graphics_setcolor (int normal_color, int highlight_color); ++int graphics_setcursor (int on); ++int graphics_init(void); ++void graphics_end(void); ++ ++int hex(int v); ++void graphics_set_palette(int idx, int red, int green, int blue); ++#endif /* SUPPORT_GRAPHICS */ ++ + #endif /* ! GRUB_TERM_HEADER */ diff --git a/stubdom/grub.patches/20print_func.diff b/stubdom/grub.patches/20print_func.diff new file mode 100644 index 0000000..ec9da0e --- /dev/null +++ b/stubdom/grub.patches/20print_func.diff @@ -0,0 +1,80 @@ +2006-01-05 Otavio Salvador + + * Rediff. + +2005-16-10 Samuel Thibault + + * docs/grub.texi: Added print command description. + * stage2/builtins.c(print_func): New function. + (builtin_print): New variable. + (builtin_table): Added builtin_print in table. + +Debian Status Following: + Added by: Otavio Salvador + Date: 2006-01-05 + +diff -Nur grub-0.97-bkp/docs/grub.texi grub-0.97/docs/grub.texi +--- grub-0.97-bkp/docs/grub.texi 2006-01-05 10:59:05.564347912 -0200 ++++ grub-0.97/docs/grub.texi 2006-01-05 11:18:59.033912960 -0200 +@@ -2685,6 +2685,7 @@ + * module:: Load a module + * modulenounzip:: Load a module without decompression + * pause:: Wait for a key press ++* print:: Print a message + * quit:: Exit from the grub shell + * reboot:: Reboot your computer + * read:: Read data from memory +@@ -3091,6 +3092,16 @@ + @end deffn + + ++@node print ++@subsection print ++ ++@deffn Command print message @dots{} ++Print the @var{message}. Note that placing @key{^G} (ASCII code 7) in the ++message will cause the speaker to emit the standard beep sound, which is ++useful for visually impaired people. ++@end deffn ++ ++ + @node quit + @subsection quit + +diff -Nur grub-0.97-bkp/stage2/builtins.c grub-0.97/stage2/builtins.c +--- grub-0.97-bkp/stage2/builtins.c 2006-01-05 10:59:05.550350040 -0200 ++++ grub-0.97/stage2/builtins.c 2006-01-05 11:19:28.422445224 -0200 +@@ -2323,6 +2323,25 @@ + "Probe I/O ports used for the drive DRIVE." + }; + ++/* print */ ++static int ++print_func (char *arg, int flags) ++{ ++ printf("%s\n", arg); ++ ++ return 0; ++} ++ ++static struct builtin builtin_print = ++{ ++ "print", ++ print_func, ++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_NO_ECHO, ++ "print [MESSAGE ...]", ++ "Print MESSAGE." ++}; ++ ++ + + /* kernel */ + static int +@@ -4848,6 +4867,7 @@ + &builtin_parttype, + &builtin_password, + &builtin_pause, ++ &builtin_print, + #ifdef GRUB_UTIL + &builtin_quit, + #endif /* GRUB_UTIL */ diff --git a/stubdom/grub.patches/30savedefault.diff b/stubdom/grub.patches/30savedefault.diff new file mode 100644 index 0000000..2902b87 --- /dev/null +++ b/stubdom/grub.patches/30savedefault.diff @@ -0,0 +1,186 @@ +Index: grub/stage2/builtins.c +=================================================================== +--- grub.orig/stage2/builtins.c 2008-06-02 18:06:08.942580000 +0100 ++++ grub/stage2/builtins.c 2008-06-06 18:35:07.548390000 +0100 +@@ -86,6 +86,10 @@ + inside other functions. */ + static int configfile_func (char *arg, int flags); + ++static int savedefault_helper (char *arg, int flags); ++ ++static int savedefault_shell (char *arg, int flags); ++ + /* Initialize the data for builtins. */ + void + init_builtins (void) +@@ -3512,7 +3516,109 @@ + static int + savedefault_func (char *arg, int flags) + { +-#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL) ++#if !defined(SUPPORT_DISKLESS) ++ #if !defined(GRUB_UTIL) ++ return savedefault_helper(arg, flags); ++ #else ++ return savedefault_shell(arg, flags); ++ #endif ++#else /* !SUPPORT_DISKLESS */ ++ errnum = ERR_UNRECOGNIZED; ++ return 1; ++#endif /* !SUPPORT_DISKLESS */ ++} ++ ++#if !defined(SUPPORT_DISKLESS) && defined(GRUB_UTIL) ++/* savedefault_shell */ ++static int ++savedefault_shell(char *arg, int flags) ++ { ++ int once_only = 0; ++ int new_default; ++ int curr_default = -1; ++ int curr_prev_default = -1; ++ int new_prev_default = -1; ++ FILE *fp; ++ size_t bytes = 10; ++ char line[bytes]; ++ char *default_file = (char *) DEFAULT_FILE_BUF; ++ char buf[bytes]; ++ int i; ++ ++ while (1) ++ { ++ if (grub_memcmp ("--default=", arg, sizeof ("--default=") - 1) == 0) ++ { ++ char *p = arg + sizeof ("--default=") - 1; ++ if (! safe_parse_maxint (&p, &new_default)) ++ return 1; ++ arg = skip_to (0, arg); ++ } ++ else if (grub_memcmp ("--once", arg, sizeof ("--once") - 1) == 0) ++ { ++ once_only = 1; ++ arg = skip_to (0, arg); ++ } ++ else ++ break; ++ } ++ ++ *default_file = 0; ++ grub_strncat (default_file, config_file, DEFAULT_FILE_BUFLEN); ++ for (i = grub_strlen(default_file); i >= 0; i--) ++ if (default_file[i] == '/') ++ { ++ i++; ++ break; ++ } ++ default_file[i] = 0; ++ grub_strncat (default_file + i, "default", DEFAULT_FILE_BUFLEN - i); ++ ++ if(!(fp = fopen(default_file,"r"))) ++ { ++ errnum = ERR_READ; ++ goto fail; ++ } ++ ++ fgets(line, bytes, fp); ++ fclose(fp); ++ ++ sscanf(line, "%d:%d", &curr_prev_default, &curr_default); ++ ++ if(curr_default != -1) ++ new_prev_default = curr_default; ++ else ++ { ++ if(curr_prev_default != -1) ++ new_prev_default = curr_prev_default; ++ else ++ new_prev_default = 0; ++ } ++ ++ if(once_only) ++ sprintf(buf, "%d:%d", new_prev_default, new_default); ++ else ++ sprintf(buf, "%d", new_default); ++ ++ if(!(fp = fopen(default_file,"w"))) ++ { ++ errnum = ERR_READ; ++ goto fail; ++ } ++ ++ fprintf(fp, buf); ++ ++fail: ++ fclose(fp); ++ return errnum; ++} ++#endif ++ ++/* savedefault_helper */ ++static int ++savedefault_helper (char *arg, int flags) ++{ ++#if !defined(SUPPORT_DISKLESS) + unsigned long tmp_drive = saved_drive; + unsigned long tmp_partition = saved_partition; + char *default_file = (char *) DEFAULT_FILE_BUF; +@@ -3588,22 +3694,26 @@ + + disk_read_hook = disk_read_savesect_func; + len = grub_read (buf, sizeof (buf)); ++ buf[9]='\0';/* Make sure grub_strstr() below terminates */ + disk_read_hook = 0; + grub_close (); + +- if (len != sizeof (buf)) +- { +- /* This is too small. Do not modify the file manually, please! */ +- errnum = ERR_READ; +- goto fail; +- } +- + if (sector_count > 2) + { + /* Is this possible?! Too fragmented! */ + errnum = ERR_FSYS_CORRUPT; + goto fail; + } ++ ++ char *tmp; ++ if((tmp = grub_strstr(buf, ":")) != NULL) ++ { ++ int f_len = grub_strlen(buf) - grub_strlen(tmp); ++ char *def; ++ buf[f_len] = '\0'; ++ def = buf; ++ safe_parse_maxint (&def, &entryno); ++ } + + /* Set up a string to be written. */ + grub_memset (buf, '\n', sizeof (buf)); +Index: grub/stage2/stage2.c +=================================================================== +--- grub.orig/stage2/stage2.c 2008-06-02 18:06:08.858579000 +0100 ++++ grub/stage2/stage2.c 2008-06-06 18:04:03.585354000 +0100 +@@ -49,7 +49,8 @@ + return 0; + #endif /* GRUB_UTIL */ + +- preset_menu_offset = 0; ++ if (preset_menu_offset) ++ return 0; + return preset_menu != 0; + } + +@@ -934,7 +935,11 @@ + len = grub_read (buf, sizeof (buf)); + if (len > 0) + { ++ char *tmp; + buf[sizeof (buf) - 1] = 0; ++ if((tmp = grub_strstr(p, ":")) != NULL) ++ p = tmp + 1; ++ + safe_parse_maxint (&p, &saved_entryno); + } + diff --git a/stubdom/grub.patches/40ext3_256byte_inode.diff b/stubdom/grub.patches/40ext3_256byte_inode.diff new file mode 100644 index 0000000..0c40854 --- /dev/null +++ b/stubdom/grub.patches/40ext3_256byte_inode.diff @@ -0,0 +1,114 @@ + +Patch from Red Hat. See #463236, #463123. + +Index: grub/stage2/fsys_ext2fs.c +=================================================================== +--- grub.orig/stage2/fsys_ext2fs.c 2008-05-27 18:47:19.045183000 +0100 ++++ grub/stage2/fsys_ext2fs.c 2008-05-27 19:09:21.293187000 +0100 +@@ -79,7 +79,52 @@ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ +- __u32 s_reserved[235]; /* Padding to the end of the block */ ++ /* ++ * 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 +@@ -218,6 +263,9 @@ + #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 */ +@@ -242,7 +290,14 @@ + static __inline__ unsigned long + ffz (unsigned long word) + { +- __asm__ ("bsfl %1,%0" ++ __asm__ ("bsf" ++#ifdef __i386__ ++ "l" ++#endif ++#ifdef __x86_64__ ++ "q" ++#endif ++ " %1,%0" + : "=r" (word) + : "r" (~word)); + return word; +@@ -553,7 +608,7 @@ + gdp = GROUP_DESC; + ino_blk = gdp[desc].bg_inode_table + + (((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)) +- >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); ++ >> log2 (EXT2_INODES_PER_BLOCK (SUPERBLOCK))); + #ifdef E2DEBUG + printf ("inode table fsblock=%d\n", ino_blk); + #endif /* E2DEBUG */ +@@ -565,13 +620,12 @@ + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + +- raw_inode = INODE + +- ((current_ino - 1) +- & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 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_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)), +- sizeof (struct ext2_inode)); ++ 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; diff --git a/stubdom/grub.patches/50fs_fulldisk.diff b/stubdom/grub.patches/50fs_fulldisk.diff new file mode 100644 index 0000000..d0fbb07 --- /dev/null +++ b/stubdom/grub.patches/50fs_fulldisk.diff @@ -0,0 +1,72 @@ +diff -urN grub-0.97.orig/stage2/fsys_ext2fs.c grub-0.97/stage2/fsys_ext2fs.c +--- grub-0.97.orig/stage2/fsys_ext2fs.c 2004-08-08 18:19:18.000000000 +0000 ++++ grub-0.97/stage2/fsys_ext2fs.c 2008-09-16 19:31:15.000000000 +0000 +@@ -254,7 +254,7 @@ + { + int retval = 1; + +- if ((((current_drive & 0x80) || (current_slice != 0)) ++ if ((((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)) +diff -urN grub-0.97.orig/stage2/fsys_fat.c grub-0.97/stage2/fsys_fat.c +--- grub-0.97.orig/stage2/fsys_fat.c 2005-03-15 16:52:00.000000000 +0000 ++++ grub-0.97/stage2/fsys_fat.c 2008-09-16 19:31:26.000000000 +0000 +@@ -70,7 +70,7 @@ + __u32 magic, first_fat; + + /* Check partition type for harddisk */ +- if (((current_drive & 0x80) || (current_slice != 0)) ++ if (((current_slice != 0)) + && ! IS_PC_SLICE_TYPE_FAT (current_slice) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) + return 0; +diff -urN grub-0.97.orig/stage2/fsys_ffs.c grub-0.97/stage2/fsys_ffs.c +--- grub-0.97.orig/stage2/fsys_ffs.c 2003-07-09 11:45:52.000000000 +0000 ++++ grub-0.97/stage2/fsys_ffs.c 2008-09-16 19:31:32.000000000 +0000 +@@ -82,7 +82,7 @@ + { + int retval = 1; + +- if ((((current_drive & 0x80) || (current_slice != 0)) ++ if ((((current_slice != 0)) + && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_BSDFFS)) + || part_length < (SBLOCK + (SBSIZE / DEV_BSIZE)) + || !devread (SBLOCK, 0, SBSIZE, (char *) SUPERBLOCK) +diff -urN grub-0.97.orig/stage2/fsys_minix.c grub-0.97/stage2/fsys_minix.c +--- grub-0.97.orig/stage2/fsys_minix.c 2003-07-09 11:45:53.000000000 +0000 ++++ grub-0.97/stage2/fsys_minix.c 2008-09-16 19:32:01.000000000 +0000 +@@ -160,7 +160,7 @@ + int + minix_mount (void) + { +- if (((current_drive & 0x80) || current_slice != 0) ++ if ((current_slice != 0) + && ! IS_PC_SLICE_TYPE_MINIX (current_slice) + && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER)) + return 0; /* The partition is not of MINIX type */ +diff -urN grub-0.97.orig/stage2/fsys_ufs2.c grub-0.97/stage2/fsys_ufs2.c +--- grub-0.97.orig/stage2/fsys_ufs2.c 2004-06-19 12:17:52.000000000 +0000 ++++ grub-0.97/stage2/fsys_ufs2.c 2008-09-16 19:32:32.000000000 +0000 +@@ -87,7 +87,7 @@ + sblockloc = -1; + type = 0; + +- if (! (((current_drive & 0x80) || (current_slice != 0)) ++ if (! (((current_slice != 0)) + && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_BSDFFS))) + { + for (i = 0; sblock_try[i] != -1; ++i) +diff -urN grub-0.97.orig/stage2/fsys_vstafs.c grub-0.97/stage2/fsys_vstafs.c +--- grub-0.97.orig/stage2/fsys_vstafs.c 2003-07-09 11:45:53.000000000 +0000 ++++ grub-0.97/stage2/fsys_vstafs.c 2008-09-16 19:32:39.000000000 +0000 +@@ -47,7 +47,7 @@ + { + int retval = 1; + +- if( (((current_drive & 0x80) || (current_slice != 0)) ++ if( (((current_slice != 0)) + && current_slice != PC_SLICE_TYPE_VSTAFS) + || ! devread (0, 0, BLOCK_SIZE, (char *) FSYS_BUF) + || FIRST_SECTOR->fs_magic != 0xDEADFACE) diff --git a/stubdom/grub.patches/99minios b/stubdom/grub.patches/99minios new file mode 100644 index 0000000..6c481b1 --- /dev/null +++ b/stubdom/grub.patches/99minios @@ -0,0 +1,1495 @@ +Index: grub/stage2/builtins.c +=================================================================== +--- grub.orig/stage2/builtins.c 2008-06-16 15:18:14.649009000 +0100 ++++ grub/stage2/builtins.c 2008-06-16 15:18:14.719009000 +0100 +@@ -45,8 +45,10 @@ + #ifdef GRUB_UTIL + # include + #else /* ! GRUB_UTIL */ ++#ifndef __MINIOS + # include + # include ++#endif + #endif /* ! GRUB_UTIL */ + + #ifdef USE_MD5_PASSWORDS +@@ -246,11 +248,13 @@ + boot_func (char *arg, int flags) + { + struct term_entry *prev_term = current_term; ++#ifndef __MINIOS__ + /* Clear the int15 handler if we can boot the kernel successfully. + This assumes that the boot code never fails only if KERNEL_TYPE is + not KERNEL_TYPE_NONE. Is this assumption is bad? */ + if (kernel_type != KERNEL_TYPE_NONE) + unset_int15_handler (); ++#endif + + /* if our terminal needed initialization, we should shut it down + * before booting the kernel, but we want to save what it was so +@@ -261,13 +265,21 @@ + current_term = term_table; /* assumption: console is first */ + } + ++#ifndef __MINIOS__ + #ifdef SUPPORT_NETBOOT + /* Shut down the networking. */ + cleanup_net (); + #endif ++#endif + + switch (kernel_type) + { ++#ifdef __MINIOS__ ++ case KERNEL_TYPE_PV: ++ /* Paravirtualized */ ++ pv_boot(); ++ break; ++#else + case KERNEL_TYPE_FREEBSD: + case KERNEL_TYPE_NETBSD: + /* *BSD */ +@@ -319,6 +331,7 @@ + multi_boot ((int) entry_addr, (int) &mbi); + break; + ++#endif + default: + errnum = ERR_BOOT_COMMAND; + return 1; +@@ -1123,6 +1136,7 @@ + }; + + ++#ifndef __MINIOS__ + /* displayapm */ + static int + displayapm_func (char *arg, int flags) +@@ -1163,8 +1177,10 @@ + "displayapm", + "Display APM BIOS information." + }; ++#endif + + ++#ifndef __MINIOS__ + /* displaymem */ + static int + displaymem_func (char *arg, int flags) +@@ -1218,6 +1234,7 @@ + "Display what GRUB thinks the system address space map of the" + " machine is, including all regions of physical RAM installed." + }; ++#endif + + + /* dump FROM TO */ +@@ -1280,6 +1297,7 @@ + #endif /* GRUB_UTIL */ + + ++#ifndef __MINIOS__ + static char embed_info[32]; + /* embed */ + /* Embed a Stage 1.5 in the first cylinder after MBR or in the +@@ -1413,6 +1431,7 @@ + " is a drive, or in the \"bootloader\" area if DEVICE is a FFS partition." + " Print the number of sectors which STAGE1_5 occupies if successful." + }; ++#endif + + + /* fallback */ +@@ -1956,6 +1975,7 @@ + #endif /* SUPPORT_NETBOOT */ + + ++#ifndef __MINIOS__ + /* impsprobe */ + static int + impsprobe_func (char *arg, int flags) +@@ -1982,6 +2002,7 @@ + " configuration table and boot the various CPUs which are found into" + " a tight loop." + }; ++#endif + + + /* initrd */ +@@ -1992,6 +2013,7 @@ + { + case KERNEL_TYPE_LINUX: + case KERNEL_TYPE_BIG_LINUX: ++ case KERNEL_TYPE_PV: + if (! load_initrd (arg)) + return 1; + break; +@@ -2015,6 +2037,7 @@ + }; + + ++#ifndef __MINIOS__ + /* install */ + static int + install_func (char *arg, int flags) +@@ -2555,8 +2578,10 @@ + " for LBA mode. If the option `--stage2' is specified, rewrite the Stage" + " 2 via your OS's filesystem instead of the raw device." + }; ++#endif + + ++#ifndef __MINIOS__ + /* ioprobe */ + static int + ioprobe_func (char *arg, int flags) +@@ -2598,6 +2623,7 @@ + "ioprobe DRIVE", + "Probe I/O ports used for the drive DRIVE." + }; ++#endif + + /* print */ + static int +@@ -3776,6 +3802,7 @@ + }; + + ++#ifndef __MINIOS__ + #ifdef SUPPORT_SERIAL + /* serial */ + static int +@@ -3927,8 +3954,10 @@ + " default values are COM1, 9600, 8N1." + }; + #endif /* SUPPORT_SERIAL */ ++#endif + + ++#ifndef __MINIOS__ + /* setkey */ + struct keysym + { +@@ -4174,8 +4203,10 @@ + " is a digit), and delete. If no argument is specified, reset key" + " mappings." + }; ++#endif + + ++#ifndef __MINIOS__ + /* setup */ + static int + setup_func (char *arg, int flags) +@@ -4484,6 +4515,7 @@ + " partition where GRUB images reside, specify the option `--stage2'" + " to tell GRUB the file name under your OS." + }; ++#endif + + + #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS) +@@ -4788,6 +4820,7 @@ + #endif /* SUPPORT_SERIAL */ + + ++#ifndef __MINIOS__ + /* testload */ + static int + testload_func (char *arg, int flags) +@@ -4874,8 +4907,10 @@ + " consistent offset error. If this test succeeds, then a good next" + " step is to try loading a kernel." + }; ++#endif + + ++#ifndef __MINIOS__ + /* testvbe MODE */ + static int + testvbe_func (char *arg, int flags) +@@ -4979,6 +5014,7 @@ + "testvbe MODE", + "Test the VBE mode MODE. Hit any key to return." + }; ++#endif + + + #ifdef SUPPORT_NETBOOT +@@ -5075,6 +5111,7 @@ + }; + + ++#ifndef __MINIOS__ + /* uppermem */ + static int + uppermem_func (char *arg, int flags) +@@ -5095,8 +5132,10 @@ + "Force GRUB to assume that only KBYTES kilobytes of upper memory are" + " installed. Any system address range maps are discarded." + }; ++#endif + + ++#ifndef __MINIOS__ + /* vbeprobe */ + static int + vbeprobe_func (char *arg, int flags) +@@ -5203,6 +5242,7 @@ + "Probe VBE information. If the mode number MODE is specified, show only" + " the information about only the mode." + }; ++#endif + + + /* The table of builtin commands. Sorted in dictionary order. */ +@@ -5233,12 +5273,16 @@ + #ifdef SUPPORT_NETBOOT + &builtin_dhcp, + #endif /* SUPPORT_NETBOOT */ ++#ifndef __MINIOS__ + &builtin_displayapm, + &builtin_displaymem, ++#endif + #ifdef GRUB_UTIL + &builtin_dump, + #endif /* GRUB_UTIL */ ++#ifndef __MINIOS__ + &builtin_embed, ++#endif + &builtin_fallback, + &builtin_find, + #ifdef SUPPORT_GRAPHICS +@@ -5253,10 +5297,14 @@ + #ifdef SUPPORT_NETBOOT + &builtin_ifconfig, + #endif /* SUPPORT_NETBOOT */ ++#ifndef __MINIOS__ + &builtin_impsprobe, ++#endif + &builtin_initrd, ++#ifndef __MINIOS__ + &builtin_install, + &builtin_ioprobe, ++#endif + &builtin_kernel, + &builtin_lock, + &builtin_makeactive, +@@ -5283,11 +5331,13 @@ + &builtin_root, + &builtin_rootnoverify, + &builtin_savedefault, ++#ifndef __MINIOS__ + #ifdef SUPPORT_SERIAL + &builtin_serial, + #endif /* SUPPORT_SERIAL */ + &builtin_setkey, + &builtin_setup, ++#endif + #ifdef SUPPORT_GRAPHICS + &builtin_shade, + &builtin_splashimage, +@@ -5298,16 +5348,20 @@ + #ifdef SUPPORT_SERIAL + &builtin_terminfo, + #endif /* SUPPORT_SERIAL */ ++#ifndef __MINIOS__ + &builtin_testload, + &builtin_testvbe, ++#endif + #ifdef SUPPORT_NETBOOT + &builtin_tftpserver, + #endif /* SUPPORT_NETBOOT */ + &builtin_timeout, + &builtin_title, + &builtin_unhide, ++#ifndef __MINIOS__ + &builtin_uppermem, + &builtin_vbeprobe, ++#endif + #ifdef SUPPORT_GRAPHICS + &builtin_viewport, + #endif +Index: grub/stage2/char_io.c +=================================================================== +--- grub.orig/stage2/char_io.c 2008-06-16 15:18:14.516009000 +0100 ++++ grub/stage2/char_io.c 2008-06-16 15:18:14.726009000 +0100 +@@ -20,6 +20,7 @@ + + #include + #include ++#include + + #ifdef SUPPORT_HERCULES + # include +@@ -36,6 +37,7 @@ + #ifndef STAGE1_5 + struct term_entry term_table[] = + { ++#ifdef SUPPORT_CONSOLE + { + "console", + 0, +@@ -52,6 +54,7 @@ + 0, + 0 + }, ++#endif + #ifdef SUPPORT_SERIAL + { + "serial", +@@ -131,9 +134,9 @@ + } + + char * +-convert_to_ascii (char *buf, int c,...) ++convert_to_ascii (char *buf, int c, int _num) + { +- unsigned long num = *((&c) + 1), mult = 10; ++ unsigned long num = _num, mult = 10; + char *ptr = buf; + + #ifndef STAGE1_5 +@@ -182,11 +185,11 @@ + void + grub_printf (const char *format,...) + { +- int *dataptr = (int *) &format; ++ va_list ap; + char c, str[16]; +- +- dataptr++; + ++ va_start(ap, format); ++ + while ((c = *(format++)) != 0) + { + if (c != '%') +@@ -200,21 +203,32 @@ + case 'X': + #endif + case 'u': +- *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0; ++ { ++ unsigned i = va_arg(ap, unsigned); ++ *convert_to_ascii (str, c, i) = 0; + grub_putstr (str); + break; ++ } + + #ifndef STAGE1_5 + case 'c': +- grub_putchar ((*(dataptr++)) & 0xff); ++ { ++ int c = va_arg(ap, int); ++ grub_putchar (c & 0xff); + break; ++ } + + case 's': +- grub_putstr ((char *) *(dataptr++)); ++ { ++ char *s = va_arg(ap, char*); ++ grub_putstr (s); + break; ++ } + #endif + } + } ++ ++ va_end(ap); + } + + #ifndef STAGE1_5 +@@ -223,11 +237,11 @@ + { + /* XXX hohmuth + ugly hack -- should unify with printf() */ +- int *dataptr = (int *) &format; ++ va_list ap; + char c, *ptr, str[16]; + char *bp = buffer; + +- dataptr++; ++ va_start(ap, format); + + while ((c = *format++) != 0) + { +@@ -237,20 +251,27 @@ + switch (c = *(format++)) + { + case 'd': case 'u': case 'x': +- *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0; ++ { ++ unsigned i = va_arg(ap, unsigned); ++ *convert_to_ascii (str, c, i) = 0; + + ptr = str; + + while (*ptr) + *bp++ = *(ptr++); /* putchar(*(ptr++)); */ + break; ++ } + +- case 'c': *bp++ = (*(dataptr++))&0xff; ++ case 'c': ++ { ++ int c = va_arg(ap, int); ++ *bp++ = c&0xff; + /* putchar((*(dataptr++))&0xff); */ + break; ++ } + + case 's': +- ptr = (char *) (*(dataptr++)); ++ ptr = va_arg(ap, char *); + + while ((c = *ptr++) != 0) + *bp++ = c; /* putchar(c); */ +@@ -258,6 +279,8 @@ + } + } + ++ va_end(ap); ++ + *bp = 0; + return bp - buffer; + } +@@ -1263,12 +1286,14 @@ + return ! errnum; + #endif /* GRUB_UTIL */ + ++#ifndef __MINIOS__ + if ((addr < RAW_ADDR (0x1000)) + || (addr < RAW_ADDR (0x100000) + && RAW_ADDR (mbi.mem_lower * 1024) < (addr + len)) + || (addr >= RAW_ADDR (0x100000) + && RAW_ADDR (mbi.mem_upper * 1024) < ((addr - 0x100000) + len))) + errnum = ERR_WONT_FIT; ++#endif + + return ! errnum; + } +@@ -1342,7 +1367,7 @@ + } + #endif /* ! STAGE1_5 */ + +-#ifndef GRUB_UTIL ++#if !defined(GRUB_UTIL) && !defined(__MINIOS__) + # undef memcpy + /* GCC emits references to memcpy() for struct copies etc. */ + void *memcpy (void *dest, const void *src, int n) __attribute__ ((alias ("grub_memmove"))); +Index: grub/stage2/disk_io.c +=================================================================== +--- grub.orig/stage2/disk_io.c 2008-06-16 15:18:03.327932000 +0100 ++++ grub/stage2/disk_io.c 2008-06-16 15:18:14.733009000 +0100 +@@ -130,7 +130,14 @@ + static inline unsigned long + log2 (unsigned long word) + { +- asm volatile ("bsfl %1,%0" ++ asm volatile ("bsf" ++#ifdef __i386__ ++ "l" ++#endif ++#ifdef __x86_64__ ++ "q" ++#endif ++ " %1,%0" + : "=r" (word) + : "r" (word)); + return word; +Index: grub/stage2/fsys_fat.c +=================================================================== +--- grub.orig/stage2/fsys_fat.c 2008-06-16 15:18:03.337934000 +0100 ++++ grub/stage2/fsys_fat.c 2008-06-16 15:18:14.737009000 +0100 +@@ -57,7 +57,14 @@ + static __inline__ unsigned long + log2 (unsigned long word) + { +- __asm__ ("bsfl %1,%0" ++ __asm__ ("bsf" ++#ifdef __i386__ ++ "l" ++#endif ++#ifdef __x86_64__ ++ "q" ++#endif ++ " %1,%0" + : "=r" (word) + : "r" (word)); + return word; +Index: grub/stage2/pc_slice.h +=================================================================== +--- grub.orig/stage2/pc_slice.h 2008-06-16 15:18:03.347932000 +0100 ++++ grub/stage2/pc_slice.h 2008-06-16 15:18:14.746009000 +0100 +@@ -38,50 +38,50 @@ + */ + + #define PC_MBR_CHECK_SIG(mbr_ptr) \ +- ( *( (unsigned short *) (((int) mbr_ptr) + PC_MBR_SIG_OFFSET) ) \ ++ ( *( (unsigned short *) (((long) mbr_ptr) + PC_MBR_SIG_OFFSET) ) \ + == PC_MBR_SIGNATURE ) + + #define PC_MBR_SIG(mbr_ptr) \ +- ( *( (unsigned short *) (((int) mbr_ptr) + PC_MBR_SIG_OFFSET) ) ) ++ ( *( (unsigned short *) (((long) mbr_ptr) + PC_MBR_SIG_OFFSET) ) ) + + #define PC_SLICE_FLAG(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET \ + + (part << 4)) ) ) + + #define PC_SLICE_HEAD(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 1 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 1 \ + + (part << 4)) ) ) + + #define PC_SLICE_SEC(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 2 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 2 \ + + (part << 4)) ) ) + + #define PC_SLICE_CYL(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 3 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 3 \ + + (part << 4)) ) ) + + #define PC_SLICE_TYPE(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 4 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 4 \ + + (part << 4)) ) ) + + #define PC_SLICE_EHEAD(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 5 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 5 \ + + (part << 4)) ) ) + + #define PC_SLICE_ESEC(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 6 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 6 \ + + (part << 4)) ) ) + + #define PC_SLICE_ECYL(mbr_ptr, part) \ +- ( *( (unsigned char *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 7 \ ++ ( *( (unsigned char *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 7 \ + + (part << 4)) ) ) + + #define PC_SLICE_START(mbr_ptr, part) \ +- ( *( (unsigned long *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 8 \ ++ ( *( (unsigned long *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 8 \ + + (part << 4)) ) ) + + #define PC_SLICE_LENGTH(mbr_ptr, part) \ +- ( *( (unsigned long *) (((int) mbr_ptr) + PC_SLICE_OFFSET + 12 \ ++ ( *( (unsigned long *) (((long) mbr_ptr) + PC_SLICE_OFFSET + 12 \ + + (part << 4)) ) ) + + +Index: grub/stage2/shared.h +=================================================================== +--- grub.orig/stage2/shared.h 2008-06-16 15:18:14.537009000 +0100 ++++ grub/stage2/shared.h 2008-06-17 14:25:08.443906000 +0100 +@@ -39,6 +39,10 @@ + extern char *grub_scratch_mem; + # define RAW_ADDR(x) ((x) + (int) grub_scratch_mem) + # define RAW_SEG(x) (RAW_ADDR ((x) << 4) >> 4) ++#elif defined(__MINIOS__) ++extern char grub_scratch_mem[]; ++# define RAW_ADDR(x) ((x) + (int) grub_scratch_mem) ++# define RAW_SEG(x) (RAW_ADDR ((x) << 4) >> 4) + #else + # define RAW_ADDR(x) (x) + # define RAW_SEG(x) (x) +@@ -707,7 +711,9 @@ + + /* Halt the system, using APM if possible. If NO_APM is true, don't use + APM even if it is available. */ ++#ifndef __MINIOS__ + void grub_halt (int no_apm) __attribute__ ((noreturn)); ++#endif + + /* Copy MAP to the drive map and set up int13_handler. */ + void set_int13_handler (unsigned short *map); +@@ -857,7 +863,8 @@ + KERNEL_TYPE_BIG_LINUX, /* Big Linux. */ + KERNEL_TYPE_FREEBSD, /* FreeBSD. */ + KERNEL_TYPE_NETBSD, /* NetBSD. */ +- KERNEL_TYPE_CHAINLOADER /* Chainloader. */ ++ KERNEL_TYPE_CHAINLOADER, /* Chainloader. */ ++ KERNEL_TYPE_PV /* Paravirtualized. */ + } + kernel_t; + +@@ -890,7 +897,7 @@ + int grub_strlen (const char *str); + char *grub_strcpy (char *dest, const char *src); + +-#ifndef GRUB_UTIL ++#if !defined(GRUB_UTIL) && !defined(__MINIOS__) + typedef unsigned long grub_jmp_buf[6]; + #else + /* In the grub shell, use the libc jmp_buf instead. */ +@@ -898,7 +905,7 @@ + # define grub_jmp_buf jmp_buf + #endif + +-#ifdef GRUB_UTIL ++#if defined(GRUB_UTIL) || defined(__MINIOS__) + # define grub_setjmp setjmp + # define grub_longjmp longjmp + #else /* ! GRUB_UTIL */ +@@ -914,7 +921,7 @@ + /* misc */ + void init_page (void); + void print_error (void); +-char *convert_to_ascii (char *buf, int c, ...); ++char *convert_to_ascii (char *buf, int c, int num); + int get_cmdline (char *prompt, char *cmdline, int maxlen, + int echo_char, int history); + int substring (const char *s1, const char *s2); +Index: grub/netboot/etherboot.h +=================================================================== +--- grub.orig/netboot/etherboot.h 2008-06-16 15:18:03.446934000 +0100 ++++ grub/netboot/etherboot.h 2008-06-16 15:18:14.760009000 +0100 +@@ -246,7 +246,7 @@ + + typedef struct + { +- unsigned long s_addr; ++ unsigned int s_addr; + } + in_addr; + +@@ -302,7 +302,7 @@ + char bp_htype; + char bp_hlen; + char bp_hops; +- unsigned long bp_xid; ++ unsigned int bp_xid; + unsigned short bp_secs; + unsigned short unused; + in_addr bp_ciaddr; +@@ -411,25 +411,25 @@ + + struct + { +- long id; +- long type; +- long rpcvers; +- long prog; +- long vers; +- long proc; +- long data[1]; ++ int id; ++ int type; ++ int rpcvers; ++ int prog; ++ int vers; ++ int proc; ++ int data[1]; + } + call; + + struct + { +- long id; +- long type; +- long rstatus; +- long verifier; +- long v2; +- long astatus; +- long data[1]; ++ int id; ++ int type; ++ int rstatus; ++ int verifier; ++ int v2; ++ int astatus; ++ int data[1]; + } + reply; + } +@@ -517,7 +517,9 @@ + + /* misc.c */ + extern void twiddle (void); ++#ifndef __MINIOS__ + extern void sleep (int secs); ++#endif + extern int getdec (char **s); + extern void etherboot_printf (const char *, ...); + extern int etherboot_sprintf (char *, const char *, ...); +Index: grub/stage2/common.c +=================================================================== +--- grub.orig/stage2/common.c 2008-06-16 15:18:03.366934000 +0100 ++++ grub/stage2/common.c 2008-06-16 15:18:14.764009000 +0100 +@@ -137,6 +137,7 @@ + } + #endif /* ! STAGE1_5 */ + ++#ifndef __MINIOS__ + /* This queries for BIOS information. */ + void + init_bios_info (void) +@@ -335,3 +336,4 @@ + /* Start main routine here. */ + cmain (); + } ++#endif +Index: grub/stage2/serial.c +=================================================================== +--- grub.orig/stage2/serial.c 2008-06-16 15:18:03.376934000 +0100 ++++ grub/stage2/serial.c 2008-06-16 15:18:14.769009000 +0100 +@@ -37,7 +37,7 @@ + + /* Hardware-dependent definitions. */ + +-#ifndef GRUB_UTIL ++#if !defined(GRUB_UTIL) && !defined(__MINIOS__) + /* The structure for speed vs. divisor. */ + struct divisor + { +@@ -222,6 +222,8 @@ + {('3' | ('~' << 8)), 4}, + {('5' | ('~' << 8)), 7}, + {('6' | ('~' << 8)), 3}, ++ {('7' | ('~' << 8)), 1}, ++ {('8' | ('~' << 8)), 5}, + }; + + /* The buffer must start with ``ESC [''. */ +Index: grub/stage2/tparm.c +=================================================================== +--- grub.orig/stage2/tparm.c 2008-06-16 15:18:03.390933000 +0100 ++++ grub/stage2/tparm.c 2008-06-16 15:18:14.774010000 +0100 +@@ -48,6 +48,7 @@ + #include "shared.h" + + #include "tparm.h" ++#include + + /* + * Common/troublesome character definitions +@@ -320,7 +321,7 @@ + #define isLOWER(c) ((c) >= 'a' && (c) <= 'z') + + static inline char * +-tparam_internal(const char *string, int *dataptr) ++tparam_internal(const char *string, va_list ap) + { + #define NUM_VARS 26 + char *p_is_s[9]; +@@ -461,9 +462,9 @@ + * a char* and an int may not be the same size on the stack. + */ + if (p_is_s[i] != 0) { +- p_is_s[i] = (char *)(*(dataptr++)); ++ p_is_s[i] = va_arg(ap, char *); + } else { +- param[i] = (int)(*(dataptr++)); ++ param[i] = va_arg(ap, int); + } + } + +@@ -716,11 +717,13 @@ + grub_tparm(const char *string,...) + { + char *result; +- int *dataptr = (int *) &string; ++ va_list ap; + +- dataptr++; ++ va_start(ap, string); + +- result = tparam_internal(string, dataptr); ++ result = tparam_internal(string, ap); ++ ++ va_end(ap); + + return result; + } +Index: grub/stage2/fsys_iso9660.c +=================================================================== +--- grub.orig/stage2/fsys_iso9660.c 2008-06-16 15:18:03.400933000 +0100 ++++ grub/stage2/fsys_iso9660.c 2008-06-16 15:18:14.779009000 +0100 +@@ -59,7 +59,14 @@ + static inline unsigned long + log2 (unsigned long word) + { +- asm volatile ("bsfl %1,%0" ++ asm volatile ("bsf" ++#ifdef __i386__ ++ "l" ++#endif ++#ifdef __x86_64__ ++ "q" ++#endif ++ " %1,%0" + : "=r" (word) + : "r" (word)); + return word; +Index: grub/stage2/fsys_reiserfs.c +=================================================================== +--- grub.orig/stage2/fsys_reiserfs.c 2008-06-16 15:18:03.410933000 +0100 ++++ grub/stage2/fsys_reiserfs.c 2008-06-20 18:33:52.002100000 +0100 +@@ -224,8 +224,8 @@ + + struct disk_child + { +- unsigned long dc_block_number; /* Disk child's block number. */ +- unsigned short dc_size; /* Disk child's used space. */ ++ __u32 dc_block_number; /* Disk child's block number. */ ++ __u16 dc_size; /* Disk child's used space. */ + }; + + #define DC_SIZE (sizeof (struct disk_child)) +@@ -369,7 +369,14 @@ + static __inline__ unsigned long + log2 (unsigned long word) + { +- __asm__ ("bsfl %1,%0" ++ __asm__ ("bsf" ++#ifdef __i386__ ++ "l" ++#endif ++#ifdef __x86_64__ ++ "q" ++#endif ++ " %1,%0" + : "=r" (word) + : "r" (word)); + return word; +Index: grub/netboot/misc.c +=================================================================== +--- grub.orig/netboot/misc.c 2008-06-16 15:18:03.456934000 +0100 ++++ grub/netboot/misc.c 2008-06-16 15:18:14.790009000 +0100 +@@ -21,7 +21,9 @@ + + #define GRUB 1 + #include ++#include + ++#ifndef __MINIOS__ + void + sleep (int secs) + { +@@ -30,6 +32,7 @@ + while (currticks () < tmo) + ; + } ++#endif + + void + twiddle (void) +@@ -71,7 +74,7 @@ + Note: width specification not supported + **************************************************************************/ + static int +-etherboot_vsprintf (char *buf, const char *fmt, const int *dp) ++etherboot_vsprintf (char *buf, const char *fmt, va_list ap) + { + char *p, *s; + +@@ -86,7 +89,7 @@ + + if (*++fmt == 's') + { +- for (p = (char *) *dp++; *p != '\0'; p++) ++ for (p = va_arg(ap, char *); *p != '\0'; p++) + buf ? *s++ = *p : grub_putchar (*p); + } + else +@@ -121,11 +124,9 @@ + if ((*fmt | 0x20) == 'x') + { + /* With x86 gcc, sizeof(long) == sizeof(int) */ +- const long *lp = (const long *) dp; +- long h = *lp++; ++ long h = va_arg(ap, int); + int ncase = (*fmt & 0x20); + +- dp = (const int *) lp; + if (alt) + { + *q++ = '0'; +@@ -136,7 +137,7 @@ + } + else if (*fmt == 'd') + { +- int i = *dp++; ++ int i = va_arg(ap, int); + char *r; + + if (i < 0) +@@ -171,10 +172,8 @@ + unsigned char c[4]; + } + u; +- const long *lp = (const long *) dp; + +- u.l = *lp++; +- dp = (const int *) lp; ++ u.l = va_arg(ap, int); + + for (r = &u.c[0]; r < &u.c[4]; ++r) + q += etherboot_sprintf (q, "%d.", *r); +@@ -184,7 +183,7 @@ + else if (*fmt == '!') + { + char *r; +- p = (char *) *dp++; ++ p = va_arg(ap, char *); + + for (r = p + ETH_ALEN; p < r; ++p) + q += etherboot_sprintf (q, "%hhX:", *p); +@@ -192,7 +191,7 @@ + --q; + } + else if (*fmt == 'c') +- *q++ = *dp++; ++ *q++ = va_arg(ap, int); + else + *q++ = *fmt; + +@@ -211,13 +210,21 @@ + int + etherboot_sprintf (char *buf, const char *fmt, ...) + { +- return etherboot_vsprintf (buf, fmt, ((const int *) &fmt) + 1); ++ va_list ap; ++ int ret; ++ va_start(ap, fmt); ++ ret = etherboot_vsprintf (buf, fmt, ap); ++ va_end(ap); ++ return ret; + } + + void + etherboot_printf (const char *fmt, ...) + { +- (void) etherboot_vsprintf (0, fmt, ((const int *) &fmt) + 1); ++ va_list ap; ++ va_start(ap, fmt); ++ etherboot_vsprintf (0, fmt, ap); ++ va_end(ap); + } + + int +Index: grub/netboot/main.c +=================================================================== +--- grub.orig/netboot/main.c 2008-06-16 15:18:03.470932000 +0100 ++++ grub/netboot/main.c 2008-06-16 15:18:14.797009000 +0100 +@@ -55,7 +55,7 @@ + static int vendorext_isvalid; + static unsigned long netmask; + static struct bootpd_t bootp_data; +-static unsigned long xid; ++static unsigned int xid; + + #define BOOTP_DATA_ADDR (&bootp_data) + +@@ -778,7 +778,7 @@ + + arpreply = (struct arprequest *) &nic.packet[ETH_HLEN]; + +- if (arpreply->opcode == htons (ARP_REPLY) ++ if (arpreply->opcode == htons (ARP_REPLY) && ptr + && ! grub_memcmp (arpreply->sipaddr, ptr, sizeof (in_addr)) + && type == AWAIT_ARP) + { +@@ -827,7 +827,7 @@ + { + arpreply = (struct arprequest *) &nic.packet[ETH_HLEN]; + +- if (arpreply->opcode == htons (RARP_REPLY) ++ if (arpreply->opcode == htons (RARP_REPLY) && ptr + && ! grub_memcmp (arpreply->thwaddr, ptr, ETH_ALEN)) + { + grub_memmove ((char *) arptable[ARP_SERVER].node, +@@ -1135,7 +1135,7 @@ + long + rfc2131_sleep_interval (int base, int exp) + { +- static long seed = 0; ++ static unsigned seed = 0; + long q; + unsigned long tmo; + +Index: grub/stage2/graphics.c +=================================================================== +--- grub.orig/stage2/graphics.c 2008-06-16 15:18:14.524009000 +0100 ++++ grub/stage2/graphics.c 2008-06-17 14:29:05.204328000 +0100 +@@ -30,7 +30,29 @@ + #include + #include + ++#ifdef __MINIOS__ ++#include ++typedef uint8_t Bit8u; ++#include ++#include ++#include ++#define set_int1c_handler() (void)0 ++#define unset_int1c_handler() (void)0 ++static uint32_t *VIDEOMEM; ++static struct fbfront_dev *fb_dev; ++static uint32_t palette[17]; ++short cursorX, cursorY; ++/* TODO: blink */ ++uint32_t cursorBuf32[16*8]; ++#define WIDTH 640 ++#define HEIGHT 480 ++#define DEPTH 32 ++#define RAMSIZE (WIDTH * HEIGHT * (DEPTH / 8)) ++#else ++#define fbfront_update(dev, x, y, w, h) (void)0 + int saved_videomode; ++#endif ++ + unsigned char *font8x16; + + int graphics_inited = 0; +@@ -38,11 +60,15 @@ + + int shade = 1, no_cursor = 0; + ++#ifdef __MINIOS__ ++uint32_t VSHADOW[RAMSIZE]; ++#else + #define VSHADOW VSHADOW1 + unsigned char VSHADOW1[38400]; + unsigned char VSHADOW2[38400]; + unsigned char VSHADOW4[38400]; + unsigned char VSHADOW8[38400]; ++#endif + + /* define the default viewable area */ + int view_x0 = 0; +@@ -129,6 +155,8 @@ + count_lines = k; + + no_scroll = 0; ++ ++ fbfront_update(fb_dev, view_x0 * 8, view_y0 * 16, (view_x1 - view_x0) * 8, (view_y1 - view_y0) * 16); + } + + /* Set the splash image */ +@@ -154,17 +182,29 @@ + int graphics_init() + { + if (!graphics_inited) { ++#ifdef __MINIOS__ ++ VIDEOMEM = memalign(PAGE_SIZE, RAMSIZE); ++ if (!(fb_dev = fb_open(VIDEOMEM, WIDTH, HEIGHT, DEPTH))) { ++ free(VIDEOMEM); ++ return 0; ++ } ++#else + saved_videomode = set_videomode(0x12); + if (get_videomode() != 0x12) { + set_videomode(saved_videomode); + return 0; + } ++#endif + graphics_inited = 1; + } + else + return 1; + ++#ifdef __MINIOS__ ++ font8x16 = vgafont16; ++#else + font8x16 = (unsigned char*)graphics_get_font(); ++#endif + + /* make sure that the highlight color is set correctly */ + graphics_highlight_color = ((graphics_normal_color >> 4) | +@@ -176,7 +216,11 @@ + grub_printf("Failed to read splash image (%s)\n", splashimage); + grub_printf("Press any key to continue..."); + getkey(); ++#ifdef __MINIOS__ ++ fb_close(); ++#else + set_videomode(saved_videomode); ++#endif + graphics_inited = 0; + return 0; + } +@@ -190,8 +234,13 @@ + void graphics_end(void) + { + if (graphics_inited) { ++#ifdef __MINIOS__ ++ fb_close(); ++ free(VIDEOMEM); ++#else + unset_int1c_handler(); + set_videomode(saved_videomode); ++#endif + graphics_inited = 0; + no_cursor = 0; + } +@@ -204,15 +253,19 @@ + graphics_cursor(0); + + if (ch == '\n') { ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + if (fonty + 1 < view_y1) + graphics_setxy(fontx, fonty + 1); + else + graphics_scroll(); + graphics_cursor(1); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + return; + } else if (ch == '\r') { ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + graphics_setxy(view_x0, fonty); + graphics_cursor(1); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + return; + } + +@@ -224,6 +277,7 @@ + text[fonty * 80 + fontx] |= 0x100; + + graphics_cursor(0); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + + if ((fontx + 1) >= view_x1) { + graphics_setxy(view_x0, fonty); +@@ -232,13 +286,16 @@ + else + graphics_scroll(); + graphics_cursor(1); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + do_more (); + graphics_cursor(0); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + } else { + graphics_setxy(fontx + 1, fonty); + } + + graphics_cursor(1); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + } + + /* get the current location of the cursor */ +@@ -248,10 +305,12 @@ + + void graphics_gotoxy(int x, int y) { + graphics_cursor(0); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + + graphics_setxy(x, y); + + graphics_cursor(1); ++ fbfront_update(fb_dev, cursorX, cursorY, 8, 16); + } + + void graphics_cls(void) { +@@ -262,15 +321,21 @@ + graphics_gotoxy(view_x0, view_y0); + + mem = (unsigned char*)VIDEOMEM; ++#ifndef __MINIOS__ + s1 = (unsigned char*)VSHADOW1; + s2 = (unsigned char*)VSHADOW2; + s4 = (unsigned char*)VSHADOW4; + s8 = (unsigned char*)VSHADOW8; ++#endif + + for (i = 0; i < 80 * 30; i++) + text[i] = ' '; + graphics_cursor(1); + ++#ifdef __MINIOS__ ++ memcpy(mem, VSHADOW, RAMSIZE); ++ fbfront_update(fb_dev, 0, 0, 640, 480); ++#else + BitMask(0xff); + + /* plane 1 */ +@@ -290,6 +355,7 @@ + grub_memcpy(mem, s8, 38400); + + MapMask(15); ++#endif + + if (no_cursor) { + no_cursor = 0; +@@ -337,6 +403,11 @@ + return 0; + } + ++void graphics_set_palette(int idx, int red, int green, int blue) ++{ ++ palette[idx] = (red << (16 + 2)) | (green << (8 + 2)) | (blue << 2); ++} ++ + /* Read in the splashscreen image and set the palette up appropriately. + * Format of splashscreen is an xpm (can be gzipped) with 16 colors and + * 640x480. */ +@@ -413,18 +484,19 @@ + } + + if (len == 6 && idx < 15) { +- int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2; +- int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2; +- int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2; ++ int r = ((hex(buf[0]) << 4) | hex(buf[1])); ++ int g = ((hex(buf[2]) << 4) | hex(buf[3])); ++ int b = ((hex(buf[4]) << 4) | hex(buf[5])); + + pal[idx] = base; +- graphics_set_palette(idx, r, g, b); ++ graphics_set_palette(idx, r / 4, g / 4, b / 4); + ++idx; + } + } + + x = y = len = 0; + ++#ifndef __MINIOS__ + s1 = (unsigned char*)VSHADOW1; + s2 = (unsigned char*)VSHADOW2; + s4 = (unsigned char*)VSHADOW4; +@@ -432,6 +504,7 @@ + + for (i = 0; i < 38400; i++) + s1[i] = s2[i] = s4[i] = s8[i] = 0; ++#endif + + /* parse xpm data */ + while (y < height) { +@@ -451,6 +524,9 @@ + break; + } + ++#ifdef __MINIOS__ ++ VSHADOW[x + y * 640] = palette[i]; ++#else + mask = 0x80 >> (x & 7); + if (c & 1) + s1[len + (x >> 3)] |= mask; +@@ -460,6 +536,7 @@ + s4[len + (x >> 3)] |= mask; + if (c & 8) + s8[len + (x >> 3)] |= mask; ++#endif + + if (++x >= 640) { + x = 0; +@@ -494,7 +571,13 @@ + } + + void graphics_cursor(int set) { +- unsigned char *pat, *mem, *ptr, chr[16 << 2]; ++ unsigned char *pat; ++#ifdef __MINIOS__ ++ uint32_t *mem, *ptr, chr[16 * 8]; ++ int j; ++#else ++ unsigned char *mem, *ptr, chr[16 << 2]; ++#endif + int i, ch, invert, offset; + + if (set && (no_cursor || no_scroll)) +@@ -505,71 +588,127 @@ + invert = (text[fonty * 80 + fontx] & 0xff00) != 0; + pat = font8x16 + (ch << 4); + +- mem = (unsigned char*)VIDEOMEM + offset; ++ mem = (unsigned char*)VIDEOMEM + offset ++#ifdef __MINIOS__ ++ * 8 * 4 ++#endif ++ ; + + if (!set) { + for (i = 0; i < 16; i++) { + unsigned char mask = pat[i]; + + if (!invert) { ++#ifdef __MINIOS__ ++ memcpy(chr + i * 8, VSHADOW + offset * 8, 8 * 4); ++#else + chr[i ] = ((unsigned char*)VSHADOW1)[offset]; + chr[16 + i] = ((unsigned char*)VSHADOW2)[offset]; + chr[32 + i] = ((unsigned char*)VSHADOW4)[offset]; + chr[48 + i] = ((unsigned char*)VSHADOW8)[offset]; ++#endif + + if (shade) { + if (ch == DISP_VERT || ch == DISP_LL || + ch == DISP_UR || ch == DISP_LR) { + unsigned char pmask = ~(pat[i] >> 1); + ++#ifdef __MINIOS__ ++ for (j = 0; j < 8; j++) ++ if (!(pmask & (1U << j))) ++ chr[i * 8 + (7 - j)] = palette[0]; ++#else + chr[i ] &= pmask; + chr[16 + i] &= pmask; + chr[32 + i] &= pmask; + chr[48 + i] &= pmask; ++#endif + } + if (i > 0 && ch != DISP_VERT) { + unsigned char pmask = ~(pat[i - 1] >> 1); + ++#ifdef __MINIOS__ ++ for (j = 0; j < 8; j++) ++ if (!(pmask & (1U << j))) ++ chr[i * 8 + (7 - j)] = palette[0]; ++#else + chr[i ] &= pmask; + chr[16 + i] &= pmask; + chr[32 + i] &= pmask; + chr[48 + i] &= pmask; ++#endif + if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) { + pmask = ~pat[i - 1]; + ++#ifdef __MINIOS__ ++ for (j = 0; j < 8; j++) ++ if (!(pmask & (1U << j))) ++ chr[i * 8 + (7 - j)] = palette[0]; ++#else + chr[i ] &= pmask; + chr[16 + i] &= pmask; + chr[32 + i] &= pmask; + chr[48 + i] &= pmask; ++#endif + } + } + } ++#ifdef __MINIOS__ ++ for (j = 0; j < 8; j++) ++ if (mask & (1U << j)) ++ chr[i * 8 + (7 - j)] = palette[15]; ++#else + chr[i ] |= mask; + chr[16 + i] |= mask; + chr[32 + i] |= mask; + chr[48 + i] |= mask; ++#endif + + offset += 80; + } + else { ++#ifdef __MINIOS__ ++ for (j = 0; j < 8; j++) ++ if (mask & (1U << j)) ++ chr[i * 8 + (7 - j)] = palette[15]; ++ else ++ chr[i * 8 + (7 - j)] = palette[0]; ++#else + chr[i ] = mask; + chr[16 + i] = mask; + chr[32 + i] = mask; + chr[48 + i] = mask; ++#endif + } + } + } + else { ++#ifdef __MINIOS__ ++ ptr = mem; ++ for (i = 0; i < 16; i++, ptr += 80 * 8) ++ for (j = 0; j < 8; j++) { ++ if (pat[i] & (1U << (7 - j))) ++ cursorBuf32[i * 8 + j] = ptr[j] = palette[0]; ++ else ++ cursorBuf32[i * 8 + j] = ptr[j] = palette[15]; ++ } ++#else + MapMask(15); + ptr = mem; + for (i = 0; i < 16; i++, ptr += 80) { + cursorBuf[i] = pat[i]; + *ptr = ~pat[i]; + } ++#endif + return; + } + + offset = 0; ++#ifdef __MINIOS__ ++ ptr = mem; ++ for (j = 0; j < 16; j++, ptr += 80 * 8) ++ memcpy(ptr, chr + j * 8 + offset * 8, 8 * 4); ++#else + for (i = 1; i < 16; i <<= 1, offset += 16) { + int j; + +@@ -580,6 +719,7 @@ + } + + MapMask(15); ++#endif + } + + #endif /* SUPPORT_GRAPHICS */ +Index: grub/stage2/graphics.h +=================================================================== +--- grub.orig/stage2/graphics.h 2008-06-16 15:18:14.527010000 +0100 ++++ grub/stage2/graphics.h 2008-06-16 15:18:14.805010000 +0100 +@@ -21,8 +21,10 @@ + #ifndef GRAPHICS_H + #define GRAPHICS_H + ++#ifndef __MINIOS__ + /* magic constant */ + #define VIDEOMEM 0xA0000 ++#endif + + /* function prototypes */ + char *graphics_get_splash(void); +Index: grub/stage2/stage2.c +=================================================================== +--- grub.orig/stage2/stage2.c 2008-06-17 11:06:47.873523000 +0100 ++++ grub/stage2/stage2.c 2008-06-17 11:07:05.225628000 +0100 +@@ -31,10 +31,10 @@ + #if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS) + + # if defined(PRESET_MENU_STRING) +-static const char *preset_menu = PRESET_MENU_STRING; ++const char *preset_menu = PRESET_MENU_STRING; + # elif defined(SUPPORT_DISKLESS) + /* Execute the command "bootp" automatically. */ +-static const char *preset_menu = "bootp\n"; ++const char *preset_menu = "bootp\n"; + # endif /* SUPPORT_DISKLESS */ + + static int preset_menu_offset; +Index: grub/stage2/fsys_xfs.c +=================================================================== +RCS file: /sources/grub/grub/stage2/fsys_xfs.c,v +retrieving revision 1.5 +diff -u -p -r1.5 fsys_xfs.c +--- grub/stage2/fsys_xfs.c 8 May 2005 02:18:14 -0000 1.5 ++++ grub/stage2/fsys_xfs.c 10 Jul 2008 13:09:50 -0000 +@@ -101,7 +101,7 @@ static inline __const__ xfs_uint16_t + le16 (xfs_uint16_t x) + { + __asm__("xchgb %b0,%h0" \ +- : "=q" (x) \ ++ : "=Q" (x) \ + : "0" (x)); \ + return x; + } +@@ -109,9 +109,9 @@ le16 (xfs_uint16_t x) + static inline __const__ xfs_uint32_t + le32 (xfs_uint32_t x) + { +-#if 0 ++#if 1 + /* 386 doesn't have bswap. */ +- __asm__("bswap %0" : "=r" (x) : "0" (x)); ++ __asm__("bswapl %k0" : "=r" (x) : "0" (x)); + #else + /* This is slower but this works on all x86 architectures. */ + __asm__("xchgb %b0, %h0" \ diff --git a/stubdom/grub/Makefile b/stubdom/grub/Makefile new file mode 100644 index 0000000..77e5eb9 --- /dev/null +++ b/stubdom/grub/Makefile @@ -0,0 +1,84 @@ +XEN_ROOT = ../.. + +include $(XEN_ROOT)/Config.mk +vpath %.c ../grub-upstream + +BOOT=$(OBJ_DIR)/boot-$(XEN_TARGET_ARCH).o + +DEF_CPPFLAGS += -I$(XEN_ROOT)/tools/libxc -I$(XEN_ROOT)/tools/include -I. +DEF_CPPFLAGS += -I../grub-upstream/stage1 +DEF_CPPFLAGS += -I../grub-upstream/stage2 +DEF_CPPFLAGS += -I../grub-upstream/netboot +DEF_CPPFLAGS += -I$(XEN_ROOT)/tools/firmware/vgabios +DEF_CPPFLAGS += -DWITHOUT_LIBC_STUBS +DEF_CPPFLAGS += -DSUPPORT_NETBOOT +DEF_CPPFLAGS += -DSUPPORT_GRAPHICS +DEF_CPPFLAGS += -DSUPPORT_SERIAL +DEF_CPPFLAGS += -DPRESET_MENU_STRING='""' +DEF_CPPFLAGS += -DPACKAGE='"grubdom"' -DVERSION='"0.97"' + +all: $(OBJ_DIR)/main.a + +STAGE2_SOURCES=builtins.c char_io.c cmdline.c common.c console.c disk_io.c graphics.c gunzip.c md5.c serial.c stage2.c terminfo.c tparm.c + +NETBOOT_SOURCES=fsys_tftp.c main.c misc.c +CPPFLAGS += -DFSYS_TFTP=1 + +STAGE2_SOURCES+=fsys_ext2fs.c +CPPFLAGS += -DFSYS_EXT2FS=1 + +STAGE2_SOURCES+=fsys_fat.c +CPPFLAGS += -DFSYS_FAT=1 + +STAGE2_SOURCES+=fsys_ffs.c +CPPFLAGS += -DFSYS_FFS=1 + +STAGE2_SOURCES+=fsys_iso9660.c +CPPFLAGS += -DFSYS_ISO9660=1 + +STAGE2_SOURCES+=fsys_jfs.c +CPPFLAGS += -DFSYS_JFS=1 + +STAGE2_SOURCES+=fsys_minix.c +CPPFLAGS += -DFSYS_MINIX=1 + +STAGE2_SOURCES+=fsys_reiserfs.c +CPPFLAGS += -DFSYS_REISERFS=1 + +STAGE2_SOURCES+=fsys_ufs2.c +CPPFLAGS += -DFSYS_UFS2=1 + +STAGE2_SOURCES+=fsys_vstafs.c +CPPFLAGS += -DFSYS_VSTAFS=1 + +STAGE2_SOURCES+=fsys_xfs.c +CPPFLAGS += -DFSYS_XFS=1 + +STAGE2_SOURCES:=$(addprefix stage2/,$(STAGE2_SOURCES)) +NETBOOT_SOURCES:=$(addprefix netboot/,$(NETBOOT_SOURCES)) + +$(BOOT): DEF_CPPFLAGS+=-D__ASSEMBLY__ + +PV_GRUB_SOURCES = kexec.c mini-os.c + +SOURCES = $(NETBOOT_SOURCES) $(STAGE2_SOURCES) $(PV_GRUB_SOURCES) + +OBJS = $(addprefix $(OBJ_DIR)/,$(SOURCES:.c=.o)) + +$(OBJ_DIR)/dirs: + mkdir -p $(OBJ_DIR)/netboot $(OBJ_DIR)/stage2 + touch $@ + +$(OBJS): $(OBJ_DIR)/dirs + +$(OBJ_DIR)/main.a: $(BOOT) $(OBJS) + $(AR) cr $@ $^ + +$(OBJ_DIR)/%.o: %.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +$(OBJ_DIR)/%.o: %.S + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +clean: + rm -fr dirs *.a *.o stage2 netboot diff --git a/stubdom/grub/boot-x86_32.S b/stubdom/grub/boot-x86_32.S new file mode 100644 index 0000000..c95fd30 --- /dev/null +++ b/stubdom/grub/boot-x86_32.S @@ -0,0 +1,112 @@ +#include +#include +#include + +/* For simplicity, we keep all of this into just one data page */ +.data +.globl _boot_page +_boot_page: + .align __PAGE_SIZE + +/* + * The following data is initialized from C code + */ + +/* Pte of this page */ +.globl _boot_page_entry +_boot_page_entry: +_boot_page_entry_lo: + .long 0 +_boot_page_entry_hi: + .long 0 + +/* mmuext_op structure */ +/* Set new page directory */ +_boot_mmuext: + /* Op # */ + .long MMUEXT_NEW_BASEPTR + + /* MFN of target page table directory */ +.globl _boot_pdmfn +_boot_pdmfn: + .long 0 + + /* Unused */ + .long 0 + +/* Unpin old page directory */ + /* Op # */ + .long MMUEXT_UNPIN_TABLE + + /* MFN of old page table directory */ +.globl _boot_oldpdmfn +_boot_oldpdmfn: + .long 0 + + /* Unused */ + .long 0 + +/* Target stack address, also target virtual address of this page */ +.globl _boot_stack +_boot_stack: + .long 0 + .long __KERNEL_SS +.globl _boot_target +_boot_target: + .long 0 + +/* Target start info */ +.globl _boot_start_info +_boot_start_info: + .long 0 + +/* Target start address */ +.globl _boot_start +_boot_start: + .long 0 + +/* + * Boot target OS, does not return + */ +.globl _boot +_boot: + /* Project ourselves at the target place. */ + movl _boot_target, %ebx + movl %ebx, %ebp /* also keep it in ebp for relative addressing */ + movl _boot_page_entry_lo, %ecx + movl _boot_page_entry_hi, %edx + movl $2, %esi /* UVMF_INVLPG */ + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 + testl %eax, %eax + jz 0f + ud2 + +0: + /* Go there. */ + movl $(0f - _boot_page), %eax + movl _boot_target, %ebx + addl %ebx, %eax + jmpl *%eax +0: + + /* Load target page table and unpin old page table. */ + /* We shouldn't have any problem since in the new page table our page is + mapped at the same place. */ + leal (_boot_mmuext-_boot_page)(%ebp), %ebx + movl $2, %ecx + xorl %edx, %edx + movl $0x7FF0, %esi /* DOMID_SELF */ + movl $__HYPERVISOR_mmuext_op, %eax + int $0x82 + testl %eax, %eax + jns 0f + ud2 + +0: + /* Initialize registers. */ + lss (_boot_stack-_boot_page)(%ebp), %esp + movl (_boot_start_info-_boot_page)(%ebp), %esi + + /* Jump! */ + jmpl *(_boot_start-_boot_page)(%ebp) diff --git a/stubdom/grub/boot-x86_64.S b/stubdom/grub/boot-x86_64.S new file mode 100644 index 0000000..2eae6c3 --- /dev/null +++ b/stubdom/grub/boot-x86_64.S @@ -0,0 +1,108 @@ +#include +#include +#include + +/* For simplicity, we keep all of this into just one data page */ +.data +.globl _boot_page +_boot_page: + .align __PAGE_SIZE + +/* + * The following data is initialized from C code + */ + +/* Pte of this page */ +.globl _boot_page_entry +_boot_page_entry: + .quad 0 + +/* mmuext_op structure */ +/* Set new page directory */ +_boot_mmuext: + /* Op # */ + .long MMUEXT_NEW_BASEPTR + .long 0 /* pad */ + + /* MFN of target page table directory */ +.globl _boot_pdmfn +_boot_pdmfn: + .quad 0 + + /* Unused */ + .quad 0 + +/* Unpin old page directory */ + /* Op # */ + .long MMUEXT_UNPIN_TABLE + .long 0 /* pad */ + + /* MFN of old page table directory */ +.globl _boot_oldpdmfn +_boot_oldpdmfn: + .quad 0 + + /* Unused */ + .quad 0 + +/* Target stack address, also target virtual address of this page */ +.globl _boot_stack +_boot_stack: + .quad 0 +.globl _boot_target +_boot_target: + .quad 0 + +/* Target start info */ +.globl _boot_start_info +_boot_start_info: + .quad 0 + +/* Target start address */ +.globl _boot_start +_boot_start: + .quad 0 + +/* + * Boot target OS, does not return + */ +.globl _boot +_boot: + /* Project ourselves at the target place. */ + movq _boot_target, %rdi + movq _boot_page_entry, %rsi + movq $2, %rdx /* UVMF_INVLPG */ + movq $__HYPERVISOR_update_va_mapping, %rax + syscall + testq %rax, %rax + jz 0f + ud2 + +0: + /* Go there. */ + movq $(0f - _boot_page), %rax + movq _boot_target, %rbx + addq %rbx, %rax + jmpq *%rax +0: + + /* Load target page table and unpin old page table. */ + /* We shouldn't have any problem since in the new page table our page is + mapped at the same place. */ + leaq _boot_mmuext(%rip), %rdi + movq $2, %rsi + xorq %rdx, %rdx + movq $0x7FF0, %r10 /* DOMID_SELF */ + movq $__HYPERVISOR_mmuext_op, %rax + syscall + testq %rax, %rax + jns 0f + ud2 + +0: + /* Initialize registers. */ + movq _boot_stack(%rip), %rsp + movq _boot_start_info(%rip), %rsi + + /* Jump! */ + jmpq *_boot_start(%rip) diff --git a/stubdom/grub/config.h b/stubdom/grub/config.h new file mode 100644 index 0000000..dcb2e7a --- /dev/null +++ b/stubdom/grub/config.h @@ -0,0 +1,11 @@ +#include +#undef putchar +#include +#include +#define debug _debug +#define grub_halt(a) do_exit() +#define printf grub_printf +void kexec(void *kernel, long kernel_size, void *module, long module_size, char *cmdline); +struct fbfront_dev *fb_open(void *fb, int width, int height, int depth); +void fb_close(void); +void pv_boot (void); diff --git a/stubdom/grub/kexec.c b/stubdom/grub/kexec.c new file mode 100644 index 0000000..5400fe8 --- /dev/null +++ b/stubdom/grub/kexec.c @@ -0,0 +1,324 @@ +/* + * This supports booting another PV kernel from Mini-OS + * + * The idea is to setup it using libxc, answer to day0 memory allocation + * requests, and using a trampoline boot page to switch to the new page table. + * + * The procedure of the boot page is: + * - map itself at the target position (that may overwrite some C stuff, but we + * do not care any more) + * - jump there + * - switch to the target page table + * - unpin the old page table + * - jump to the new kernel + * + * Samuel Thibault , May 2008 + */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mini-os.h" + +#if 0 +#define DEBUG(fmt, ...) printk(fmt, ## __VA_ARGS__) +#else +#define DEBUG(fmt, ...) (void)0 +#endif + +/* Assembly boot page from boot.S */ +extern void _boot_page; +extern pgentry_t _boot_page_entry; +extern unsigned long _boot_pdmfn; +extern unsigned long _boot_stack, _boot_target, _boot_start_info, _boot_start; +extern xen_pfn_t _boot_oldpdmfn; +extern void _boot(void); + +static unsigned long *pages; +static unsigned long *pages_mfns; +static unsigned long allocated; + +int pin_table(int xc_handle, unsigned int type, unsigned long mfn, + domid_t dom); + +/* We need mfn to appear as target_pfn, so exchange with the MFN there */ +static void do_exchange(struct xc_dom_image *dom, xen_pfn_t target_pfn, xen_pfn_t source_mfn) +{ + xen_pfn_t source_pfn; + xen_pfn_t target_mfn; + + for (source_pfn = 0; source_pfn < start_info.nr_pages; source_pfn++) + if (dom->p2m_host[source_pfn] == source_mfn) + break; + ASSERT(source_pfn < start_info.nr_pages); + + target_mfn = dom->p2m_host[target_pfn]; + + /* Put target MFN at source PFN */ + dom->p2m_host[source_pfn] = target_mfn; + + /* Put source MFN at target PFN */ + dom->p2m_host[target_pfn] = source_mfn; +} + +int kexec_allocate(struct xc_dom_image *dom, xen_vaddr_t up_to) +{ + unsigned long new_allocated = (up_to - dom->parms.virt_base) / PAGE_SIZE; + unsigned long i; + + pages = realloc(pages, new_allocated * sizeof(*pages)); + pages_mfns = realloc(pages_mfns, new_allocated * sizeof(*pages_mfns)); + for (i = allocated; i < new_allocated; i++) { + /* Exchange old page of PFN i with a newly allocated page. */ + xen_pfn_t old_mfn = dom->p2m_host[i]; + xen_pfn_t new_pfn; + xen_pfn_t new_mfn; + + pages[i] = alloc_page(); + memset((void*) pages[i], 0, PAGE_SIZE); + new_pfn = PHYS_PFN(to_phys(pages[i])); + pages_mfns[i] = new_mfn = pfn_to_mfn(new_pfn); + + /* Put old page at new PFN */ + dom->p2m_host[new_pfn] = old_mfn; + + /* Put new page at PFN i */ + dom->p2m_host[i] = new_mfn; + } + + allocated = new_allocated; + + return 0; +} + +void kexec(void *kernel, long kernel_size, void *module, long module_size, char *cmdline) +{ + struct xc_dom_image *dom; + int rc; + domid_t domid = DOMID_SELF; + xen_pfn_t pfn; + int xc_handle; + unsigned long i; + void *seg; + xen_pfn_t boot_page_mfn = virt_to_mfn(&_boot_page); + char features[] = ""; + struct mmu_update *m2p_updates; + unsigned long nr_m2p_updates; + + DEBUG("booting with cmdline %s\n", cmdline); + xc_handle = xc_interface_open(); + + dom = xc_dom_allocate(cmdline, features); + dom->allocate = kexec_allocate; + + dom->kernel_blob = kernel; + dom->kernel_size = kernel_size; + + dom->ramdisk_blob = module; + dom->ramdisk_size = module_size; + + dom->flags = 0; + dom->console_evtchn = start_info.console.domU.evtchn; + dom->xenstore_evtchn = start_info.store_evtchn; + + if ( (rc = xc_dom_boot_xen_init(dom, xc_handle, domid)) != 0 ) { + grub_printf("xc_dom_boot_xen_init returned %d\n", rc); + errnum = ERR_BOOT_FAILURE; + goto out; + } + if ( (rc = xc_dom_parse_image(dom)) != 0 ) { + grub_printf("xc_dom_parse_image returned %d\n", rc); + errnum = ERR_BOOT_FAILURE; + goto out; + } + +#ifdef __i386__ + if (strcmp(dom->guest_type, "xen-3.0-x86_32p")) { + grub_printf("can only boot x86 32 PAE kernels, not %s\n", dom->guest_type); + errnum = ERR_EXEC_FORMAT; + goto out; + } +#endif +#ifdef __x86_64__ + if (strcmp(dom->guest_type, "xen-3.0-x86_64")) { + grub_printf("can only boot x86 64 kernels, not %s\n", dom->guest_type); + errnum = ERR_EXEC_FORMAT; + goto out; + } +#endif + + /* equivalent of xc_dom_mem_init */ + dom->arch_hooks = xc_dom_find_arch_hooks(dom->guest_type); + dom->total_pages = start_info.nr_pages; + + /* equivalent of arch_setup_meminit */ + + /* setup initial p2m */ + dom->p2m_host = malloc(sizeof(*dom->p2m_host) * dom->total_pages); + + /* Start with our current P2M */ + for (i = 0; i < dom->total_pages; i++) + dom->p2m_host[i] = pfn_to_mfn(i); + + if ( (rc = xc_dom_build_image(dom)) != 0 ) { + grub_printf("xc_dom_build_image returned %d\n", rc); + errnum = ERR_BOOT_FAILURE; + goto out; + } + + /* copy hypercall page */ + /* TODO: domctl instead, but requires privileges */ + if (dom->parms.virt_hypercall != -1) { + pfn = PHYS_PFN(dom->parms.virt_hypercall - dom->parms.virt_base); + memcpy((void *) pages[pfn], hypercall_page, PAGE_SIZE); + } + + /* Equivalent of xc_dom_boot_image */ + dom->shared_info_mfn = PHYS_PFN(start_info.shared_info); + + if (!xc_dom_compat_check(dom)) { + grub_printf("xc_dom_compat_check failed\n"); + errnum = ERR_EXEC_FORMAT; + goto out; + } + + /* Move current console, xenstore and boot MFNs to the allocated place */ + do_exchange(dom, dom->console_pfn, start_info.console.domU.mfn); + do_exchange(dom, dom->xenstore_pfn, start_info.store_mfn); + DEBUG("virt base at %llx\n", dom->parms.virt_base); + DEBUG("bootstack_pfn %lx\n", dom->bootstack_pfn); + _boot_target = dom->parms.virt_base + PFN_PHYS(dom->bootstack_pfn); + DEBUG("_boot_target %lx\n", _boot_target); + do_exchange(dom, PHYS_PFN(_boot_target - dom->parms.virt_base), + virt_to_mfn(&_boot_page)); + + /* Make sure the bootstrap page table does not RW-map any of our current + * page table frames */ + kexec_allocate(dom, dom->virt_pgtab_end); + + if ( (rc = xc_dom_update_guest_p2m(dom))) { + grub_printf("xc_dom_update_guest_p2m returned %d\n", rc); + errnum = ERR_BOOT_FAILURE; + goto out; + } + + if ( dom->arch_hooks->setup_pgtables ) + if ( (rc = dom->arch_hooks->setup_pgtables(dom))) { + grub_printf("setup_pgtables returned %d\n", rc); + errnum = ERR_BOOT_FAILURE; + goto out; + } + + /* start info page */ +#undef start_info + if ( dom->arch_hooks->start_info ) + dom->arch_hooks->start_info(dom); +#define start_info (start_info_union.start_info) + + xc_dom_log_memory_footprint(dom); + + /* Unmap libxc's projection of the boot page table */ + seg = xc_dom_seg_to_ptr(dom, &dom->pgtables_seg); + munmap(seg, dom->pgtables_seg.vend - dom->pgtables_seg.vstart); + + /* Unmap day0 pages to avoid having a r/w mapping of the future page table */ + for (pfn = 0; pfn < allocated; pfn++) + munmap((void*) pages[pfn], PAGE_SIZE); + + /* Pin the boot page table base */ + if ( (rc = pin_table(dom->guest_xc, +#ifdef __i386__ + MMUEXT_PIN_L3_TABLE, +#endif +#ifdef __x86_64__ + MMUEXT_PIN_L4_TABLE, +#endif + xc_dom_p2m_host(dom, dom->pgtables_seg.pfn), + dom->guest_domid)) != 0 ) { + grub_printf("pin_table(%lx) returned %d\n", xc_dom_p2m_host(dom, + dom->pgtables_seg.pfn), rc); + errnum = ERR_BOOT_FAILURE; + goto out_remap; + } + + /* We populate the Mini-OS page table here so that boot.S can just call + * update_va_mapping to project itself there. */ + need_pgt(_boot_target); + DEBUG("day0 pages %lx\n", allocated); + DEBUG("boot target page %lx\n", _boot_target); + DEBUG("boot page %p\n", &_boot_page); + DEBUG("boot page mfn %lx\n", boot_page_mfn); + _boot_page_entry = PFN_PHYS(boot_page_mfn) | L1_PROT; + DEBUG("boot page entry %llx\n", _boot_page_entry); + _boot_oldpdmfn = virt_to_mfn(start_info.pt_base); + DEBUG("boot old pd mfn %lx\n", _boot_oldpdmfn); + DEBUG("boot pd virt %lx\n", dom->pgtables_seg.vstart); + _boot_pdmfn = dom->p2m_host[PHYS_PFN(dom->pgtables_seg.vstart - dom->parms.virt_base)]; + DEBUG("boot pd mfn %lx\n", _boot_pdmfn); + _boot_stack = _boot_target + PAGE_SIZE; + DEBUG("boot stack %lx\n", _boot_stack); + _boot_start_info = dom->parms.virt_base + PFN_PHYS(dom->start_info_pfn); + DEBUG("boot start info %lx\n", _boot_start_info); + _boot_start = dom->parms.virt_entry; + DEBUG("boot start %lx\n", _boot_start); + + /* Keep only useful entries */ + for (nr_m2p_updates = pfn = 0; pfn < start_info.nr_pages; pfn++) + if (dom->p2m_host[pfn] != pfn_to_mfn(pfn)) + nr_m2p_updates++; + + m2p_updates = malloc(sizeof(*m2p_updates) * nr_m2p_updates); + for (i = pfn = 0; pfn < start_info.nr_pages; pfn++) + if (dom->p2m_host[pfn] != pfn_to_mfn(pfn)) { + m2p_updates[i].ptr = PFN_PHYS(dom->p2m_host[pfn]) | MMU_MACHPHYS_UPDATE; + m2p_updates[i].val = pfn; + i++; + } + + for (i = 0; i < blk_nb; i++) + shutdown_blkfront(blk_dev[i]); + if (net_dev) + shutdown_netfront(net_dev); + if (kbd_dev) + shutdown_kbdfront(kbd_dev); + stop_kernel(); + + /* Update M2P */ + if ((rc = HYPERVISOR_mmu_update(m2p_updates, nr_m2p_updates, NULL, DOMID_SELF)) < 0) { + xprintk("Could not update M2P\n"); + ASSERT(0); + } + + xprintk("go!\n"); + + /* Jump to trampoline boot page */ + _boot(); + + ASSERT(0); + +out_remap: + for (pfn = 0; pfn < allocated; pfn++) + do_map_frames(pages[pfn], &pages_mfns[pfn], 1, 0, 0, DOMID_SELF, 0, L1_PROT); +out: + xc_dom_release(dom); + for (pfn = 0; pfn < allocated; pfn++) + free_page((void*)pages[pfn]); + free(pages); + free(pages_mfns); + pages = NULL; + pages_mfns = NULL; + allocated = 0; + xc_interface_close(xc_handle ); +} diff --git a/stubdom/grub/mini-os.c b/stubdom/grub/mini-os.c new file mode 100644 index 0000000..ecb1191 --- /dev/null +++ b/stubdom/grub/mini-os.c @@ -0,0 +1,704 @@ +/* + * Mini-OS support for GRUB. + * + * Samuel Thibault , May 2008 + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mini-os.h" + +extern const char *preset_menu; +char config_file[DEFAULT_FILE_BUFLEN] = "(hd0,0)/boot/grub/menu.lst"; +unsigned long boot_drive = NETWORK_DRIVE; +unsigned long install_partition = 0xFFFFFF; + +char version_string[] = VERSION; + +/* Variables from asm.S */ +int saved_entryno; + +/* + * Disk + */ + +struct blkfront_dev **blk_dev; +int blk_nb; +static struct blkfront_info *blk_info; + +static int vbdcmp(const void *_vbd1, const void *_vbd2) { + char *vbd1 = *(char **)_vbd1; + char *vbd2 = *(char **)_vbd2; + int vbdn1 = atoi(vbd1); + int vbdn2 = atoi(vbd2); + return vbdn1 - vbdn2; +} + +void init_disk (void) +{ + char **list; + char *msg; + int i; + char *path; + + msg = xenbus_ls(XBT_NIL, "device/vbd", &list); + if (msg) { + printk("Error %s while reading list of disks\n", msg); + free(msg); + return; + } + blk_nb = 0; + while (list[blk_nb]) + blk_nb++; + blk_dev = malloc(blk_nb * sizeof(*blk_dev)); + blk_info = malloc(blk_nb * sizeof(*blk_info)); + + qsort(list, blk_nb, sizeof(*list), vbdcmp); + + for (i = 0; i < blk_nb; i++) { + printk("vbd %s is hd%d\n", list[i], i); + asprintf(&path, "device/vbd/%s", list[i]); + blk_dev[i] = init_blkfront(path, &blk_info[i]); + free(path); + free(list[i]); + } +} + +/* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return + non-zero, otherwise zero. */ +int get_diskinfo (int drive, struct geometry *geometry) +{ + int i; + if (!(drive & 0x80)) + return -1; + + i = drive - 0x80; + if (i >= blk_nb) + return -1; + + /* Bogus geometry */ + geometry->cylinders = 65535; + geometry->heads = 255; + geometry->sectors = 63; + + geometry->total_sectors = blk_info[i].sectors; + geometry->sector_size = blk_info[i].sector_size; + geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION; + if (blk_info[i].info & VDISK_CDROM) + geometry->flags |= BIOSDISK_FLAG_CDROM; + return 0; +} + +/* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY + from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it, + else if READ is BIOSDISK_WRITE, then write it. If an geometry error + occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then + return the error number. Otherwise, return 0. */ +int +biosdisk (int read, int drive, struct geometry *geometry, + unsigned int sector, int nsec, int segment) +{ + void *addr = (void *) ((unsigned long)segment << 4); + struct blkfront_aiocb aiocb; + int i; + + if (!(drive & 0x80)) + return -1; + + i = drive - 0x80; + if (i >= blk_nb) + return -1; + + aiocb.aio_dev = blk_dev[i]; + aiocb.aio_buf = addr; + aiocb.aio_nbytes = (size_t)nsec * blk_info[i].sector_size; + aiocb.aio_offset = (off_t)sector * blk_info[i].sector_size; + aiocb.aio_cb = NULL; + + blkfront_io(&aiocb, read == BIOSDISK_WRITE); + + return 0; +} + +static int +load_file(char *name, void **ptr, long *size) +{ + char *buf = NULL; + int allocated = 1 * 1024 * 1024; + int len, filled = 0; + + if (!grub_open (name)) + return -1; + + buf = malloc(allocated); + + errnum = 0; + while (1) { + len = grub_read (buf + filled, allocated - filled); + if (! len) { + if (!errnum) + break; + grub_close (); + return -1; + } + filled += len; + if (filled < allocated) + break; + allocated *= 2; + buf = realloc(buf, allocated); + } + grub_close (); + *ptr = buf; + *size = filled; + return 0; +} + +void *kernel_image, *module_image; +long kernel_size, module_size; +char *kernel_arg, *module_arg; + +kernel_t +load_image (char *kernel, char *arg, kernel_t suggested_type, + unsigned long load_flags) +{ + arg = skip_to(0, arg); + if (kernel_image) + free(kernel_image); + kernel_image = NULL; + if (load_file (kernel, &kernel_image, &kernel_size)) + return KERNEL_TYPE_NONE; + if (kernel_arg) + free(kernel_arg); + kernel_arg = strdup(arg); + return KERNEL_TYPE_PV; +} + +int +load_initrd (char *initrd) +{ + if (module_image) + free(module_image); + module_image = NULL; + load_file (initrd, &module_image, &module_size); + return ! errnum; +} + +int +load_module (char *module, char *arg) +{ + if (module_image) + free(module_image); + module_image = NULL; + load_file (module, &module_image, &module_size); + if (module_arg) + free(module_arg); + module_arg = strdup(arg); + return ! errnum; +} + +void +pv_boot (void) +{ + kexec(kernel_image, kernel_size, module_image, module_size, kernel_arg); +} + +/* + * Network + */ + +struct netfront_dev *net_dev; + +int +minios_probe (struct nic *nic) +{ + char *ip; + + if (net_dev) + return 1; + + /* Clear the ARP table. */ + grub_memset ((char *) arptable, 0, + MAX_ARP * sizeof (struct arptable_t)); + + net_dev = init_netfront(NULL, (void*) -1, nic->node_addr, &ip); + if (!net_dev) + return 0; + + return 1; +} + +/* reset adapter */ +static void minios_reset(struct nic *nic) +{ + /* TODO? */ +} + +static void minios_disable(struct nic *nic) +{ +} + +/* Wait for a frame */ +static int minios_poll(struct nic *nic) +{ + return !! (nic->packetlen = netfront_receive(net_dev, (void*) nic->packet, ETH_FRAME_LEN)); +} + +/* Transmit a frame */ +struct frame { + uint8_t dest[ETH_ALEN]; + uint8_t src[ETH_ALEN]; + uint16_t type; + unsigned char data[]; +}; +static void minios_transmit (struct nic *nic, const char *d, unsigned int t, + unsigned int s, const char *p) +{ + struct frame *frame = alloca(sizeof(frame) + s); + + memcpy(frame->dest, d, ETH_ALEN); + memcpy(frame->src, nic->node_addr, ETH_ALEN); + frame->type = htons(t); + memcpy(frame->data, p, s); + + netfront_xmit(net_dev, (void*) frame, sizeof(*frame) + s); +} + +static char packet[ETH_FRAME_LEN]; + +struct nic nic = { + .reset = minios_reset, + .poll = minios_poll, + .transmit = minios_transmit, + .disable = minios_disable, + .flags = 0, + .rom_info = NULL, + .node_addr = arptable[ARP_CLIENT].node, + .packet = packet, + .packetlen = 0, + .priv_data = NULL, +}; + +int +eth_probe (void) +{ + return minios_probe(&nic); +} + +int +eth_poll (void) +{ + return minios_poll (&nic); +} + +void +eth_disable (void) +{ + minios_disable (&nic); +} + +void +eth_transmit (const char *d, unsigned int t, + unsigned int s, const void *p) +{ + minios_transmit (&nic, d, t, s, p); + if (t == IP) + twiddle(); +} + +/* + * Console + */ +void +serial_hw_put (int _c) +{ + char c = _c; + console_print(&c, 1); +} + +int +serial_hw_fetch (void) +{ + char key; + + if (!xencons_ring_avail()) + return -1; + + read(STDIN_FILENO, &key, 1); + switch (key) { + case 0x7f: key = '\b'; break; + } + return key; +} + +/* + * PVFB + */ +struct kbdfront_dev *kbd_dev; +struct fbfront_dev *fb_dev; +static union xenkbd_in_event ev; +static int has_ev; +int console_checkkey (void) +{ + if (has_ev) + return 1; + has_ev = kbdfront_receive(kbd_dev, &ev, 1); + return has_ev; +} + +/* static QWERTY layout, that's what most PC BIOSes do anyway */ +static char linux2ascii[] = { + [ 1 ] = 27, + [ 2 ] = '1', + [ 3 ] = '2', + [ 4 ] = '3', + [ 5 ] = '4', + [ 6 ] = '5', + [ 7 ] = '6', + [ 8 ] = '7', + [ 9 ] = '8', + [ 10 ] = '9', + [ 11 ] = '0', + [ 12 ] = '-', + [ 13 ] = '=', + [ 14 ] = '\b', + [ 15 ] = '\t', + [ 16 ] = 'q', + [ 17 ] = 'w', + [ 18 ] = 'e', + [ 19 ] = 'r', + [ 20 ] = 't', + [ 21 ] = 'y', + [ 22 ] = 'u', + [ 23 ] = 'i', + [ 24 ] = 'o', + [ 25 ] = 'p', + [ 26 ] = '[', + [ 27 ] = ']', + [ 28 ] = '\n', + + [ 30 ] = 'a', + [ 31 ] = 's', + [ 32 ] = 'd', + [ 33 ] = 'f', + [ 34 ] = 'g', + [ 35 ] = 'h', + [ 36 ] = 'j', + [ 37 ] = 'k', + [ 38 ] = 'l', + [ 39 ] = ';', + [ 40 ] = '\'', + [ 41 ] = '`', + + [ 43 ] = '\\', + [ 44 ] = 'z', + [ 45 ] = 'x', + [ 46 ] = 'c', + [ 47 ] = 'v', + [ 48 ] = 'b', + [ 49 ] = 'n', + [ 50 ] = 'm', + [ 51 ] = ',', + [ 52 ] = '.', + [ 53 ] = '/', + + [ 55 ] = '*', + [ 57 ] = ' ', + + [ 71 ] = '7', + [ 72 ] = '8', + [ 73 ] = '9', + [ 74 ] = '-', + [ 75 ] = '4', + [ 76 ] = '5', + [ 77 ] = '6', + [ 78 ] = '+', + [ 79 ] = '1', + [ 80 ] = '2', + [ 81 ] = '3', + [ 82 ] = '0', + [ 83 ] = '.', + + [ 86 ] = '<', + + [ 96 ] = '\n', + + [ 98 ] = '/', + + [ 102 ] = 1, /* home */ + [ 103 ] = 16, /* up */ + [ 104 ] = 7, /* page up */ + [ 105 ] = 2, /* left */ + [ 106 ] = 6, /* right */ + [ 107 ] = 5, /* end */ + [ 108 ] = 14, /* down */ + [ 109 ] = 3, /* page down */ + + [ 111 ] = 4, /* delete */ +}; + +static char linux2ascii_shifted[] = { + [ 1 ] = 27, + [ 2 ] = '!', + [ 3 ] = '@', + [ 4 ] = '#', + [ 5 ] = '$', + [ 6 ] = '%', + [ 7 ] = '^', + [ 8 ] = '&', + [ 9 ] = '*', + [ 10 ] = '(', + [ 11 ] = ')', + [ 12 ] = '_', + [ 13 ] = '+', + [ 14 ] = '\b', + [ 15 ] = '\t', + [ 16 ] = 'Q', + [ 17 ] = 'W', + [ 18 ] = 'E', + [ 19 ] = 'R', + [ 20 ] = 'T', + [ 21 ] = 'Y', + [ 22 ] = 'U', + [ 23 ] = 'I', + [ 24 ] = 'O', + [ 25 ] = 'P', + [ 26 ] = '{', + [ 27 ] = '}', + [ 28 ] = '\n', + + [ 30 ] = 'A', + [ 31 ] = 'S', + [ 32 ] = 'D', + [ 33 ] = 'F', + [ 34 ] = 'G', + [ 35 ] = 'H', + [ 36 ] = 'J', + [ 37 ] = 'K', + [ 38 ] = 'L', + [ 39 ] = ':', + [ 40 ] = '"', + [ 41 ] = '~', + + [ 43 ] = '|', + [ 44 ] = 'Z', + [ 45 ] = 'X', + [ 46 ] = 'C', + [ 47 ] = 'V', + [ 48 ] = 'B', + [ 49 ] = 'N', + [ 50 ] = 'M', + [ 51 ] = '<', + [ 52 ] = '>', + [ 53 ] = '?', + + [ 55 ] = '*', + [ 57 ] = ' ', + + [ 71 ] = '7', + [ 72 ] = '8', + [ 73 ] = '9', + [ 74 ] = '-', + [ 75 ] = '4', + [ 76 ] = '5', + [ 77 ] = '6', + [ 78 ] = '+', + [ 79 ] = '1', + [ 80 ] = '2', + [ 81 ] = '3', + [ 82 ] = '0', + [ 83 ] = '.', + + [ 86 ] = '>', + + [ 96 ] = '\n', + + [ 98 ] = '/', + + [ 102 ] = 1, /* home */ + [ 103 ] = 16, /* up */ + [ 104 ] = 7, /* page up */ + [ 105 ] = 2, /* left */ + [ 106 ] = 6, /* right */ + [ 107 ] = 5, /* end */ + [ 108 ] = 14, /* down */ + [ 109 ] = 3, /* page down */ + + [ 111 ] = 4, /* delete */ +}; + +int console_getkey (void) +{ + static int shift, control, alt, caps_lock; + + if (!has_ev) + has_ev = kbdfront_receive(kbd_dev, &ev, 1); + if (!has_ev) + return 0; + + has_ev = 0; + if (ev.type != XENKBD_TYPE_KEY) + return 0; + + if (ev.key.keycode == 42 || ev.key.keycode == 54) { + caps_lock = 0; + shift = ev.key.pressed; + return 0; + } + if (ev.key.keycode == 58) { + caps_lock ^= 1; + return 0; + } + if (ev.key.keycode == 29 || ev.key.keycode == 97) { + control = ev.key.pressed; + return 0; + } + if (ev.key.keycode == 56) { + alt = ev.key.pressed; + return 0; + } + + if (!ev.key.pressed) + return 0; + + if (ev.key.keycode < sizeof(linux2ascii) / sizeof(*linux2ascii)) { + char val; + if (shift || caps_lock) + val = linux2ascii_shifted[ev.key.keycode]; + else + val = linux2ascii[ev.key.keycode]; + if (control) + val &= ~0x60; + return val; + } + + return 0; +} + +static void kbd_thread(void *p) +{ + struct semaphore *sem = p; + + kbd_dev = init_kbdfront(NULL, 1); + up(sem); +} + +struct fbfront_dev *fb_open(void *fb, int width, int height, int depth) +{ + unsigned long *mfns; + int linesize = width * (depth / 8); + int memsize = linesize * height; + int numpages = (memsize + PAGE_SIZE - 1) / PAGE_SIZE; + DECLARE_MUTEX_LOCKED(sem); + int i; + + create_thread("kbdfront", kbd_thread, &sem); + + mfns = malloc(numpages * sizeof(*mfns)); + for (i = 0; i < numpages; i++) { + memset(fb + i * PAGE_SIZE, 0, PAGE_SIZE); + mfns[i] = virtual_to_mfn(fb + i * PAGE_SIZE); + } + fb_dev = init_fbfront(NULL, mfns, width, height, depth, linesize, numpages); + free(mfns); + + if (!fb_dev) + return NULL; + + down(&sem); + if (!kbd_dev) + return NULL; + + return fb_dev; +} + +void kbd_close(void *foo) +{ + shutdown_kbdfront(kbd_dev); + kbd_dev = NULL; +} + +void fb_close(void) +{ + create_thread("kbdfront close", kbd_close, NULL); + shutdown_fbfront(fb_dev); + fb_dev = NULL; +} + +/* + * Misc + */ + +int getrtsecs (void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec; +} + +int currticks (void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000000ULL + tv.tv_usec) * TICKS_PER_SEC) / 1000000; +} + +void __attribute__ ((noreturn)) grub_reboot (void) +{ + for ( ;; ) + { + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_reboot }; + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + } +} + +#define SCRATCH_MEMSIZE (4 * 1024 * 1024) + +/* Note: not allocating it dynamically permits to make sure it lays below 4G + * for grub's 32bit pointers to work */ +char grub_scratch_mem[SCRATCH_MEMSIZE] __attribute__((aligned(PAGE_SIZE))); + +int main(int argc, char *argv[]) +{ + if (argc > 1) { + strncpy(config_file, argv[1], sizeof(config_file) - 1); + config_file[sizeof(config_file) - 1] = 0; + if (!strncmp(config_file, "(nd)", 4)) + preset_menu = "dhcp"; + } else if (start_info.mod_len) + preset_menu = (void*) start_info.mod_start; + else + preset_menu = "dhcp --with-configfile"; + + mbi.drives_addr = BOOTSEC_LOCATION + (60 * 1024); + mbi.drives_length = 0; + + mbi.boot_loader_name = (unsigned long) "GNU GRUB " VERSION; + mbi.mem_lower = (start_info.nr_pages * PAGE_SIZE) / 1024; + mbi.mem_upper = 0; + saved_drive = boot_drive; + saved_partition = install_partition; + + init_disk(); + + /* Try to make sure the client part got launched */ + sleep(1); + cmain(); + printk("cmain returned!\n"); +} diff --git a/stubdom/grub/mini-os.h b/stubdom/grub/mini-os.h new file mode 100644 index 0000000..6c68441 --- /dev/null +++ b/stubdom/grub/mini-os.h @@ -0,0 +1,5 @@ +extern int blk_nb; +extern struct blkfront_dev **blk_dev; +extern struct netfront_dev *net_dev; +extern struct kbdfront_dev *kbd_dev; +extern struct fbfront_dev *fb_dev; diff --git a/stubdom/grub/osdep.h b/stubdom/grub/osdep.h new file mode 100644 index 0000000..1d3e50a --- /dev/null +++ b/stubdom/grub/osdep.h @@ -0,0 +1,30 @@ +#ifndef __OSDEP_H__ +#define __OSDEP_H__ + +#include +#define swap32(x) bswap_32(x) +#define swap16(x) bswap_16(x) + +#include +#if BYTE_ORDER == BIG_ENDIAN +#define htons(x) (x) +#define ntohs(x) (x) +#define htonl(x) (x) +#define ntohl(x) (x) +#else +#define htons(x) swap16(x) +#define ntohs(x) swap16(x) +#define htonl(x) swap32(x) +#define ntohl(x) swap32(x) +#endif + +typedef unsigned long Address; + +/* ANSI prototyping macro */ +#ifdef __STDC__ +#define P(x) x +#else +#define P(x) () +#endif + +#endif diff --git a/stubdom/libpci.config.h b/stubdom/libpci.config.h new file mode 100644 index 0000000..28c2f6a --- /dev/null +++ b/stubdom/libpci.config.h @@ -0,0 +1,5 @@ +#define PCI_OS_MINIOS +#define PCI_HAVE_STDINT_H +#define PCI_PATH_IDS_DIR "." +#define PCI_COMPRESSED_IDS +#define PCI_IDS "pci.ids.gz" diff --git a/stubdom/libpci.config.mak b/stubdom/libpci.config.mak new file mode 100644 index 0000000..5c8632c --- /dev/null +++ b/stubdom/libpci.config.mak @@ -0,0 +1,7 @@ +LIBZ=-lz +LDLIBS+=$(LIBZ) +PCI_OS_MINIOS=1 +PCI_HAVE_STDINT_H=1 +PCI_PATH_IDS_DIR=. +PCI_COMPRESSED_IDS=1 +PCI_IDS=pci.ids.gz diff --git a/stubdom/lwip.patch-cvs b/stubdom/lwip.patch-cvs new file mode 100644 index 0000000..d03068d --- /dev/null +++ b/stubdom/lwip.patch-cvs @@ -0,0 +1,2398 @@ +? .ChangeLog.swp +? ChangeLog +Index: CHANGELOG +=================================================================== +RCS file: /sources/lwip/lwip/CHANGELOG,v +retrieving revision 1.300 +retrieving revision 1.318 +diff -u -p -r1.300 -r1.318 +--- CHANGELOG 23 Mar 2008 13:49:39 -0000 1.300 ++++ CHANGELOG 14 Jul 2008 20:12:36 -0000 1.318 +@@ -19,9 +19,77 @@ HISTORY + + ++ New features: + ++ 2008-06-30 Simon Goldschmidt ++ * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from ++ interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows ++ mem_free to run between mem_malloc iterations. Added illegal counter for ++ mem stats. ++ ++ 2008-06-27 Simon Goldschmidt ++ * stats.h/.c, some other files: patch #6483: stats module improvement: ++ Added defines to display each module's statistic individually, added stats ++ defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter. ++ ++ 2008-06-17 Simon Goldschmidt ++ * err.h: patch #6459: Made err_t overridable to use a more efficient type ++ (define LWIP_ERR_T in cc.h) ++ ++ 2008-06-17 Simon Goldschmidt ++ * slipif.c: patch #6480: Added a configuration option for slipif for symmetry ++ to loopif ++ ++ 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli) ++ * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly ++ modified version of patch # 6370: Moved loopif code to netif.c so that ++ loopback traffic is supported on all netifs (all local IPs). ++ Added option to limit loopback packets for each netifs. ++ + + ++ Bugfixes: + ++ 2008-08-14 Simon Goldschmidt ++ * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when ++ tcp_close returns != ERR_OK) ++ ++ 2008-07-08 Frédéric Bernon ++ * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters ++ in macros, mainly if MEM_STATS=0 and MEMP_STATS=0). ++ ++ 2008-06-24 Jonathan Larmour ++ * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused ++ if tcp_seg_copy fails. ++ ++ 2008-06-17 Simon Goldschmidt ++ * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations) ++ and created defines for swapping bytes and folding u32 to u16. ++ ++ 2008-05-30 Kieran Mansley ++ * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd ++ rather than rcv_ann_wnd when deciding if packets are in-window. ++ Contributed by ++ ++ 2008-05-30 Kieran Mansley ++ * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow ++ passing as function pointers when MEM_LIBC_MALLOC is defined. ++ ++ 2008-05-09 Jonathan Larmour ++ * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to ++ stop it being treated as a fatal error. ++ ++ 2008-04-15 Simon Goldschmidt ++ * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP ++ (flag now cleared) ++ ++ 2008-03-27 Simon Goldschmidt ++ * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free ++ from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1 ++ in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs ++ or heap memory from interrupt context ++ ++ 2008-03-26 Simon Goldschmidt ++ * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote ++ host sent a zero mss as TCP option. ++ + + (STABLE-1.3.0) + +Index: src/api/api_msg.c +=================================================================== +RCS file: /sources/lwip/lwip/src/api/api_msg.c,v +retrieving revision 1.102 +retrieving revision 1.104 +diff -u -p -r1.102 -r1.104 +--- src/api/api_msg.c 21 Mar 2008 16:23:14 -0000 1.102 ++++ src/api/api_msg.c 15 Jul 2008 11:18:58 -0000 1.104 +@@ -598,11 +598,16 @@ do_close_internal(struct netconn *conn) + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + + /* Set back some callback pointers */ ++ tcp_arg(conn->pcb.tcp, NULL); + if (conn->pcb.tcp->state == LISTEN) { +- tcp_arg(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + } else { + tcp_recv(conn->pcb.tcp, NULL); ++ tcp_accept(conn->pcb.tcp, NULL); ++ /* some callbacks have to be reset if tcp_close is not successful */ ++ tcp_sent(conn->pcb.tcp, NULL); ++ tcp_poll(conn->pcb.tcp, NULL, 4); ++ tcp_err(conn->pcb.tcp, NULL); + } + /* Try to close the connection */ + err = tcp_close(conn->pcb.tcp); +@@ -610,11 +615,6 @@ do_close_internal(struct netconn *conn) + /* Closing succeeded */ + conn->state = NETCONN_NONE; + /* Set back some callback pointers as conn is going away */ +- tcp_err(conn->pcb.tcp, NULL); +- tcp_poll(conn->pcb.tcp, NULL, 4); +- tcp_sent(conn->pcb.tcp, NULL); +- tcp_recv(conn->pcb.tcp, NULL); +- tcp_arg(conn->pcb.tcp, NULL); + conn->pcb.tcp = NULL; + conn->err = ERR_OK; + /* Trigger select() in socket layer. This send should something else so the +@@ -623,6 +623,14 @@ do_close_internal(struct netconn *conn) + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + /* wake up the application task */ + sys_sem_signal(conn->op_completed); ++ } else { ++ /* Closing failed, restore some of the callbacks */ ++ /* Closing of listen pcb will never fail! */ ++ LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); ++ tcp_sent(conn->pcb.tcp, sent_tcp); ++ tcp_poll(conn->pcb.tcp, poll_tcp, 4); ++ tcp_err(conn->pcb.tcp, err_tcp); ++ tcp_arg(conn->pcb.tcp, conn); + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +Index: src/api/err.c +=================================================================== +RCS file: /sources/lwip/lwip/src/api/err.c,v +retrieving revision 1.11 +retrieving revision 1.12 +diff -u -p -r1.11 -r1.12 +--- src/api/err.c 13 Dec 2007 23:06:50 -0000 1.11 ++++ src/api/err.c 9 May 2008 12:14:23 -0000 1.12 +@@ -44,17 +44,17 @@ static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ +- "Routing problem.", /* ERR_RTE -3 */ +- "Connection aborted.", /* ERR_ABRT -4 */ +- "Connection reset.", /* ERR_RST -5 */ +- "Connection closed.", /* ERR_CLSD -6 */ +- "Not connected.", /* ERR_CONN -7 */ +- "Illegal value.", /* ERR_VAL -8 */ +- "Illegal argument.", /* ERR_ARG -9 */ +- "Address in use.", /* ERR_USE -10 */ +- "Low-level netif error.", /* ERR_IF -11 */ +- "Already connected.", /* ERR_ISCONN -12 */ +- "Timeout.", /* ERR_TIMEOUT -13 */ ++ "Timeout.", /* ERR_TIMEOUT -3 */ ++ "Routing problem.", /* ERR_RTE -4 */ ++ "Connection aborted.", /* ERR_ABRT -5 */ ++ "Connection reset.", /* ERR_RST -6 */ ++ "Connection closed.", /* ERR_CLSD -7 */ ++ "Not connected.", /* ERR_CONN -8 */ ++ "Illegal value.", /* ERR_VAL -9 */ ++ "Illegal argument.", /* ERR_ARG -10 */ ++ "Address in use.", /* ERR_USE -11 */ ++ "Low-level netif error.", /* ERR_IF -12 */ ++ "Already connected.", /* ERR_ISCONN -13 */ + "Operation in progress." /* ERR_INPROGRESS -14 */ + }; + +Index: src/api/netdb.c +=================================================================== +RCS file: /sources/lwip/lwip/src/api/netdb.c,v +retrieving revision 1.4 +retrieving revision 1.5 +diff -u -p -r1.4 -r1.5 +--- src/api/netdb.c 26 Jan 2008 16:11:39 -0000 1.4 ++++ src/api/netdb.c 16 Jul 2008 20:36:12 -0000 1.5 +@@ -326,7 +326,8 @@ lwip_getaddrinfo(const char *nodename, c + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + size_t namelen = strlen(nodename); +- ai->ai_canonname = mem_malloc(namelen + 1); ++ LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); ++ ai->ai_canonname = mem_malloc((mem_size_t)(namelen + 1)); + if (ai->ai_canonname == NULL) { + goto memerr; + } +Index: src/api/sockets.c +=================================================================== +RCS file: /sources/lwip/lwip/src/api/sockets.c,v +retrieving revision 1.116 +retrieving revision 1.117 +diff -u -p -r1.116 -r1.117 +--- src/api/sockets.c 13 Mar 2008 20:03:57 -0000 1.116 ++++ src/api/sockets.c 9 May 2008 12:14:24 -0000 1.117 +@@ -128,17 +128,17 @@ static const int err_to_errno_table[] = + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ +- EHOSTUNREACH, /* ERR_RTE -3 Routing problem. */ +- ECONNABORTED, /* ERR_ABRT -4 Connection aborted. */ +- ECONNRESET, /* ERR_RST -5 Connection reset. */ +- ESHUTDOWN, /* ERR_CLSD -6 Connection closed. */ +- ENOTCONN, /* ERR_CONN -7 Not connected. */ +- EINVAL, /* ERR_VAL -8 Illegal value. */ +- EIO, /* ERR_ARG -9 Illegal argument. */ +- EADDRINUSE, /* ERR_USE -10 Address in use. */ +- -1, /* ERR_IF -11 Low-level netif error */ +- -1, /* ERR_ISCONN -12 Already connected. */ +- ETIMEDOUT, /* ERR_TIMEOUT -13 Timeout */ ++ ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */ ++ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ ++ ECONNABORTED, /* ERR_ABRT -5 Connection aborted. */ ++ ECONNRESET, /* ERR_RST -6 Connection reset. */ ++ ESHUTDOWN, /* ERR_CLSD -7 Connection closed. */ ++ ENOTCONN, /* ERR_CONN -8 Not connected. */ ++ EINVAL, /* ERR_VAL -9 Illegal value. */ ++ EIO, /* ERR_ARG -10 Illegal argument. */ ++ EADDRINUSE, /* ERR_USE -11 Address in use. */ ++ -1, /* ERR_IF -12 Low-level netif error */ ++ -1, /* ERR_ISCONN -13 Already connected. */ + EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ + }; + +Index: src/api/tcpip.c +=================================================================== +RCS file: /sources/lwip/lwip/src/api/tcpip.c,v +retrieving revision 1.70 +retrieving revision 1.73 +diff -u -p -r1.70 -r1.73 +--- src/api/tcpip.c 12 Jan 2008 11:52:22 -0000 1.70 ++++ src/api/tcpip.c 27 Jun 2008 20:34:51 -0000 1.73 +@@ -518,4 +518,42 @@ tcpip_init(void (* initfunc)(void *), vo + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); + } + ++/** ++ * Simple callback function used with tcpip_callback to free a pbuf ++ * (pbuf_free has a wrong signature for tcpip_callback) ++ * ++ * @param p The pbuf (chain) to be dereferenced. ++ */ ++static void ++pbuf_free_int(void *p) ++{ ++ struct pbuf *q = p; ++ pbuf_free(q); ++} ++ ++/** ++ * A simple wrapper function that allows you to free a pbuf from interrupt context. ++ * ++ * @param p The pbuf (chain) to be dereferenced. ++ * @return ERR_OK if callback could be enqueued, an err_t if not ++ */ ++err_t ++pbuf_free_callback(struct pbuf *p) ++{ ++ return tcpip_callback_with_block(pbuf_free_int, p, 0); ++} ++ ++/** ++ * A simple wrapper function that allows you to free heap memory from ++ * interrupt context. ++ * ++ * @param m the heap memory to free ++ * @return ERR_OK if callback could be enqueued, an err_t if not ++ */ ++err_t ++mem_free_callback(void *m) ++{ ++ return tcpip_callback_with_block(mem_free, m, 0); ++} ++ + #endif /* !NO_SYS */ +Index: src/core/dhcp.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/dhcp.c,v +retrieving revision 1.86 +retrieving revision 1.87 +diff -u -p -r1.86 -r1.87 +--- src/core/dhcp.c 4 Mar 2008 14:25:58 -0000 1.86 ++++ src/core/dhcp.c 15 Apr 2008 17:24:55 -0000 1.87 +@@ -568,6 +568,8 @@ dhcp_start(struct netif *netif) + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); ++ /* Remove the flag that says this netif is handled by DHCP, ++ it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* no DHCP client attached yet? */ +@@ -609,6 +611,7 @@ dhcp_start(struct netif *netif) + dhcp_stop(netif); + return ERR_MEM; + } ++ /* Set the flag that says this netif is handled by DHCP. */ + netif->flags |= NETIF_FLAG_DHCP; + return result; + } +@@ -1063,6 +1066,8 @@ dhcp_stop(struct netif *netif) + { + struct dhcp *dhcp = netif->dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); ++ /* Remove the flag that says this netif is handled by DHCP. */ ++ netif->flags &= ~NETIF_FLAG_DHCP; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | 3, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ +Index: src/core/mem.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/mem.c,v +retrieving revision 1.59 +retrieving revision 1.62 +diff -u -p -r1.59 -r1.62 +--- src/core/mem.c 4 Mar 2008 16:31:32 -0000 1.59 ++++ src/core/mem.c 30 Jun 2008 18:16:51 -0000 1.62 +@@ -177,9 +177,36 @@ static u8_t *ram; + static struct mem *ram_end; + /** pointer to the lowest free block, this is used for faster search */ + static struct mem *lfree; ++ + /** concurrent access protection */ + static sys_sem_t mem_sem; + ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ ++static volatile u8_t mem_free_count; ++ ++/* Allow mem_free from other (e.g. interrupt) context */ ++#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) ++#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) ++#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) ++#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) ++#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) ++#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) ++ ++#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ ++ ++/* Protect the heap only by using a semaphore */ ++#define LWIP_MEM_FREE_DECL_PROTECT() ++#define LWIP_MEM_FREE_PROTECT() sys_arch_sem_wait(mem_sem, 0) ++#define LWIP_MEM_FREE_UNPROTECT() sys_sem_signal(mem_sem) ++/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ ++#define LWIP_MEM_ALLOC_DECL_PROTECT() ++#define LWIP_MEM_ALLOC_PROTECT() ++#define LWIP_MEM_ALLOC_UNPROTECT() ++ ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ ++ ++ + /** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist +@@ -255,9 +282,7 @@ mem_init(void) + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)ram; + +-#if MEM_STATS +- lwip_stats.mem.avail = MEM_SIZE_ALIGNED; +-#endif /* MEM_STATS */ ++ MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + } + + /** +@@ -270,6 +295,7 @@ void + mem_free(void *rmem) + { + struct mem *mem; ++ LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n")); +@@ -277,20 +303,20 @@ mem_free(void *rmem) + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + +- /* protect the heap from concurrent access */ +- sys_arch_sem_wait(mem_sem, 0); +- + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { ++ SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory\n")); +-#if MEM_STATS +- ++lwip_stats.mem.err; +-#endif /* MEM_STATS */ +- sys_sem_signal(mem_sem); ++ /* protect mem stats from concurrent access */ ++ SYS_ARCH_PROTECT(lev); ++ MEM_STATS_INC(illegal); ++ SYS_ARCH_UNPROTECT(lev); + return; + } ++ /* protect the heap from concurrent access */ ++ LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ +@@ -303,13 +329,14 @@ mem_free(void *rmem) + lfree = mem; + } + +-#if MEM_STATS +- lwip_stats.mem.used -= mem->next - ((u8_t *)mem - ram); +-#endif /* MEM_STATS */ ++ MEM_STATS_DEC_USED(used, mem->next - ((u8_t *)mem - ram)); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +- sys_sem_signal(mem_sem); ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ mem_free_count = 1; ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ ++ LWIP_MEM_FREE_UNPROTECT(); + } + + /** +@@ -321,6 +348,8 @@ mem_free(void *rmem) + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment ++ * or NULL if newsize is > old size, in which case rmem is NOT touched ++ * or freed! + */ + void * + mem_realloc(void *rmem, mem_size_t newsize) +@@ -328,6 +357,8 @@ mem_realloc(void *rmem, mem_size_t newsi + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; ++ /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ ++ LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ +@@ -346,7 +377,12 @@ mem_realloc(void *rmem, mem_size_t newsi + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { ++ SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory\n")); ++ /* protect mem stats from concurrent access */ ++ SYS_ARCH_PROTECT(lev); ++ MEM_STATS_INC(illegal); ++ SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ +@@ -366,11 +402,9 @@ mem_realloc(void *rmem, mem_size_t newsi + } + + /* protect the heap from concurrent access */ +- sys_arch_sem_wait(mem_sem, 0); ++ LWIP_MEM_FREE_PROTECT(); + +-#if MEM_STATS +- lwip_stats.mem.used -= (size - newsize); +-#endif /* MEM_STATS */ ++ MEM_STATS_DEC_USED(used, (size - newsize)); + + mem2 = (struct mem *)&ram[mem->next]; + if(mem2->used == 0) { +@@ -426,7 +460,10 @@ mem_realloc(void *rmem, mem_size_t newsi + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +- sys_sem_signal(mem_sem); ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ mem_free_count = 1; ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ ++ LWIP_MEM_FREE_UNPROTECT(); + return rmem; + } + +@@ -444,6 +481,10 @@ mem_malloc(mem_size_t size) + { + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ u8_t local_mem_free_count = 0; ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ ++ LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; +@@ -464,88 +505,101 @@ mem_malloc(mem_size_t size) + + /* protect the heap from concurrent access */ + sys_arch_sem_wait(mem_sem, 0); ++ LWIP_MEM_ALLOC_PROTECT(); ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ /* run as long as a mem_free disturbed mem_malloc */ ++ do { ++ local_mem_free_count = 0; ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ ++ ++ /* Scan through the heap searching for a free block that is big enough, ++ * beginning with the lowest free block. ++ */ ++ for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; ++ ptr = ((struct mem *)&ram[ptr])->next) { ++ mem = (struct mem *)&ram[ptr]; ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ mem_free_count = 0; ++ LWIP_MEM_ALLOC_UNPROTECT(); ++ /* allow mem_free to run */ ++ LWIP_MEM_ALLOC_PROTECT(); ++ if (mem_free_count != 0) { ++ local_mem_free_count = mem_free_count; ++ } ++ mem_free_count = 0; ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +- /* Scan through the heap searching for a free block that is big enough, +- * beginning with the lowest free block. +- */ +- for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; +- ptr = ((struct mem *)&ram[ptr])->next) { +- mem = (struct mem *)&ram[ptr]; +- +- if ((!mem->used) && +- (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { +- /* mem is not used and at least perfect fit is possible: +- * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ +- +- if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { +- /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing +- * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') +- * -> split large block, create empty remainder, +- * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if +- * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, +- * struct mem would fit in but no data between mem2 and mem2->next +- * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty +- * region that couldn't hold data, but when mem->next gets freed, +- * the 2 regions would be combined, resulting in more free memory +- */ +- ptr2 = ptr + SIZEOF_STRUCT_MEM + size; +- /* create mem2 struct */ +- mem2 = (struct mem *)&ram[ptr2]; +- mem2->used = 0; +- mem2->next = mem->next; +- mem2->prev = ptr; +- /* and insert it between mem and mem->next */ +- mem->next = ptr2; +- mem->used = 1; +- +- if (mem2->next != MEM_SIZE_ALIGNED) { +- ((struct mem *)&ram[mem2->next])->prev = ptr2; +- } +-#if MEM_STATS +- lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM); +- if (lwip_stats.mem.max < lwip_stats.mem.used) { +- lwip_stats.mem.max = lwip_stats.mem.used; ++ if ((!mem->used) && ++ (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { ++ /* mem is not used and at least perfect fit is possible: ++ * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ ++ ++ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { ++ /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing ++ * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') ++ * -> split large block, create empty remainder, ++ * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if ++ * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, ++ * struct mem would fit in but no data between mem2 and mem2->next ++ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty ++ * region that couldn't hold data, but when mem->next gets freed, ++ * the 2 regions would be combined, resulting in more free memory ++ */ ++ ptr2 = ptr + SIZEOF_STRUCT_MEM + size; ++ /* create mem2 struct */ ++ mem2 = (struct mem *)&ram[ptr2]; ++ mem2->used = 0; ++ mem2->next = mem->next; ++ mem2->prev = ptr; ++ /* and insert it between mem and mem->next */ ++ mem->next = ptr2; ++ mem->used = 1; ++ ++ if (mem2->next != MEM_SIZE_ALIGNED) { ++ ((struct mem *)&ram[mem2->next])->prev = ptr2; ++ } ++ MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); ++ } else { ++ /* (a mem2 struct does no fit into the user data space of mem and mem->next will always ++ * be used at this point: if not we have 2 unused structs in a row, plug_holes should have ++ * take care of this). ++ * -> near fit or excact fit: do not split, no mem2 creation ++ * also can't move mem->next directly behind mem, since mem->next ++ * will always be used at this point! ++ */ ++ mem->used = 1; ++ MEM_STATS_INC_USED(used, mem->next - ((u8_t *)mem - ram)); + } +-#endif /* MEM_STATS */ +- } else { +- /* (a mem2 struct does no fit into the user data space of mem and mem->next will always +- * be used at this point: if not we have 2 unused structs in a row, plug_holes should have +- * take care of this). +- * -> near fit or excact fit: do not split, no mem2 creation +- * also can't move mem->next directly behind mem, since mem->next +- * will always be used at this point! +- */ +- mem->used = 1; +-#if MEM_STATS +- lwip_stats.mem.used += mem->next - ((u8_t *)mem - ram); +- if (lwip_stats.mem.max < lwip_stats.mem.used) { +- lwip_stats.mem.max = lwip_stats.mem.used; +- } +-#endif /* MEM_STATS */ +- } + +- if (mem == lfree) { +- /* Find next free block after mem and update lowest free pointer */ +- while (lfree->used && lfree != ram_end) { +- lfree = (struct mem *)&ram[lfree->next]; ++ if (mem == lfree) { ++ /* Find next free block after mem and update lowest free pointer */ ++ while (lfree->used && lfree != ram_end) { ++ LWIP_MEM_ALLOC_UNPROTECT(); ++ /* prevent high interrupt latency... */ ++ LWIP_MEM_ALLOC_PROTECT(); ++ lfree = (struct mem *)&ram[lfree->next]; ++ } ++ LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } +- LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); +- } +- sys_sem_signal(mem_sem); +- LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", +- (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); +- LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", +- (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); +- LWIP_ASSERT("mem_malloc: sanity check alignment", +- (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); ++ LWIP_MEM_ALLOC_UNPROTECT(); ++ sys_sem_signal(mem_sem); ++ LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", ++ (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); ++ LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", ++ (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); ++ LWIP_ASSERT("mem_malloc: sanity check alignment", ++ (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + +- return (u8_t *)mem + SIZEOF_STRUCT_MEM; ++ return (u8_t *)mem + SIZEOF_STRUCT_MEM; ++ } + } +- } ++#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++ /* if we got interrupted by a mem_free, try again */ ++ } while(local_mem_free_count != 0); ++#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); +-#if MEM_STATS +- ++lwip_stats.mem.err; +-#endif /* MEM_STATS */ ++ MEM_STATS_INC(err); ++ LWIP_MEM_ALLOC_UNPROTECT(); + sys_sem_signal(mem_sem); + return NULL; + } +Index: src/core/memp.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/memp.c,v +retrieving revision 1.55 +retrieving revision 1.56 +diff -u -p -r1.55 -r1.56 +--- src/core/memp.c 25 Nov 2007 10:43:28 -0000 1.55 ++++ src/core/memp.c 27 Jun 2008 18:37:54 -0000 1.56 +@@ -252,13 +252,12 @@ memp_init(void) + struct memp *memp; + u16_t i, j; + +-#if MEMP_STATS + for (i = 0; i < MEMP_MAX; ++i) { +- lwip_stats.memp[i].used = lwip_stats.memp[i].max = +- lwip_stats.memp[i].err = 0; +- lwip_stats.memp[i].avail = memp_num[i]; ++ MEMP_STATS_AVAIL(used, i, 0); ++ MEMP_STATS_AVAIL(max, i, 0); ++ MEMP_STATS_AVAIL(err, i, 0); ++ MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } +-#endif /* MEMP_STATS */ + + memp = LWIP_MEM_ALIGN(memp_memory); + /* for every pool: */ +@@ -315,20 +314,13 @@ memp_malloc_fn(memp_t type, const char* + memp->file = file; + memp->line = line; + #endif /* MEMP_OVERFLOW_CHECK */ +-#if MEMP_STATS +- ++lwip_stats.memp[type].used; +- if (lwip_stats.memp[type].used > lwip_stats.memp[type].max) { +- lwip_stats.memp[type].max = lwip_stats.memp[type].used; +- } +-#endif /* MEMP_STATS */ ++ MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); +-#if MEMP_STATS +- ++lwip_stats.memp[type].err; +-#endif /* MEMP_STATS */ ++ MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); +@@ -365,9 +357,7 @@ memp_free(memp_t type, void *mem) + #endif /* MEMP_OVERFLOW_CHECK >= 2 */ + #endif /* MEMP_OVERFLOW_CHECK */ + +-#if MEMP_STATS +- lwip_stats.memp[type].used--; +-#endif /* MEMP_STATS */ ++ MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; +Index: src/core/netif.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/netif.c,v +retrieving revision 1.65 +retrieving revision 1.68 +diff -u -p -r1.65 -r1.68 +--- src/core/netif.c 9 Oct 2007 20:00:55 -0000 1.65 ++++ src/core/netif.c 19 Jun 2008 16:27:18 -0000 1.68 +@@ -45,6 +45,12 @@ + #include "lwip/snmp.h" + #include "lwip/igmp.h" + #include "netif/etharp.h" ++#if ENABLE_LOOPBACK ++#include "lwip/sys.h" ++#if LWIP_NETIF_LOOPBACK_MULTITHREADING ++#include "lwip/tcpip.h" ++#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ ++#endif /* ENABLE_LOOPBACK */ + + #if LWIP_NETIF_STATUS_CALLBACK + #define NETIF_STATUS_CALLBACK(n) { if (n->status_callback) (n->status_callback)(n); } +@@ -106,6 +112,10 @@ netif_add(struct netif *netif, struct ip + #if LWIP_IGMP + netif->igmp_mac_filter = NULL; + #endif /* LWIP_IGMP */ ++#if ENABLE_LOOPBACK ++ netif->loop_first = NULL; ++ netif->loop_last = NULL; ++#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; +@@ -114,6 +124,9 @@ netif_add(struct netif *netif, struct ip + #if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; + #endif /* LWIP_NETIF_HWADDRHINT*/ ++#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS ++ netif->loop_cnt_current = 0; ++#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + +@@ -493,7 +506,158 @@ u8_t netif_is_link_up(struct netif *neti + */ + void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif )) + { +- if ( netif ) +- netif->link_callback = link_callback; ++ if (netif) { ++ netif->link_callback = link_callback; ++ } + } + #endif /* LWIP_NETIF_LINK_CALLBACK */ ++ ++#if ENABLE_LOOPBACK ++/** ++ * Send an IP packet to be received on the same netif (loopif-like). ++ * The pbuf is simply copied and handed back to netif->input. ++ * In multithreaded mode, this is done directly since netif->input must put ++ * the packet on a queue. ++ * In callback mode, the packet is put on an internal queue and is fed to ++ * netif->input by netif_poll(). ++ * ++ * @param netif the lwip network interface structure ++ * @param p the (IP) packet to 'send' ++ * @param ipaddr the ip address to send the packet to (not used) ++ * @return ERR_OK if the packet has been sent ++ * ERR_MEM if the pbuf used to copy the packet couldn't be allocated ++ */ ++err_t ++netif_loop_output(struct netif *netif, struct pbuf *p, ++ struct ip_addr *ipaddr) ++{ ++ struct pbuf *r; ++ err_t err; ++ struct pbuf *last; ++#if LWIP_LOOPBACK_MAX_PBUFS ++ u8_t clen = 0; ++#endif /* LWIP_LOOPBACK_MAX_PBUFS */ ++ SYS_ARCH_DECL_PROTECT(lev); ++ LWIP_UNUSED_ARG(ipaddr); ++ ++ /* Allocate a new pbuf */ ++ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); ++ if (r == NULL) { ++ return ERR_MEM; ++ } ++#if LWIP_LOOPBACK_MAX_PBUFS ++ clen = pbuf_clen(r); ++ /* check for overflow or too many pbuf on queue */ ++ if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || ++ ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { ++ pbuf_free(r); ++ r = NULL; ++ return ERR_MEM; ++ } ++ netif->loop_cnt_current += clen; ++#endif /* LWIP_LOOPBACK_MAX_PBUFS */ ++ ++ /* Copy the whole pbuf queue p into the single pbuf r */ ++ if ((err = pbuf_copy(r, p)) != ERR_OK) { ++ pbuf_free(r); ++ r = NULL; ++ return err; ++ } ++ ++ /* Put the packet on a linked list which gets emptied through calling ++ netif_poll(). */ ++ ++ /* let last point to the last pbuf in chain r */ ++ for (last = r; last->next != NULL; last = last->next); ++ ++ SYS_ARCH_PROTECT(lev); ++ if(netif->loop_first != NULL) { ++ LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); ++ netif->loop_last->next = r; ++ netif->loop_last = last; ++ } else { ++ netif->loop_first = r; ++ netif->loop_last = last; ++ } ++ SYS_ARCH_UNPROTECT(lev); ++ ++#if LWIP_NETIF_LOOPBACK_MULTITHREADING ++ /* For multithreading environment, schedule a call to netif_poll */ ++ tcpip_callback(netif_poll, netif); ++#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ ++ ++ return ERR_OK; ++} ++ ++/** ++ * Call netif_poll() in the main loop of your application. This is to prevent ++ * reentering non-reentrant functions like tcp_input(). Packets passed to ++ * netif_loop_output() are put on a list that is passed to netif->input() by ++ * netif_poll(). ++ */ ++void ++netif_poll(struct netif *netif) ++{ ++ struct pbuf *in; ++ SYS_ARCH_DECL_PROTECT(lev); ++ ++ do { ++ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ ++ SYS_ARCH_PROTECT(lev); ++ in = netif->loop_first; ++ if(in != NULL) { ++ struct pbuf *in_end = in; ++#if LWIP_LOOPBACK_MAX_PBUFS ++ u8_t clen = pbuf_clen(in); ++ /* adjust the number of pbufs on queue */ ++ LWIP_ASSERT("netif->loop_cnt_current underflow", ++ ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); ++ netif->loop_cnt_current -= clen; ++#endif /* LWIP_LOOPBACK_MAX_PBUFS */ ++ while(in_end->len != in_end->tot_len) { ++ LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); ++ in_end = in_end->next; ++ } ++ /* 'in_end' now points to the last pbuf from 'in' */ ++ if(in_end == netif->loop_last) { ++ /* this was the last pbuf in the list */ ++ netif->loop_first = netif->loop_last = NULL; ++ } else { ++ /* pop the pbuf off the list */ ++ netif->loop_first = in_end->next; ++ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); ++ } ++ /* De-queue the pbuf from its successors on the 'loop_' list. */ ++ in_end->next = NULL; ++ } ++ SYS_ARCH_UNPROTECT(lev); ++ ++ if(in != NULL) { ++ /* loopback packets are always IP packets! */ ++ if(ip_input(in, netif) != ERR_OK) { ++ pbuf_free(in); ++ } ++ /* Don't reference the packet any more! */ ++ in = NULL; ++ } ++ /* go on while there is a packet on the list */ ++ } while(netif->loop_first != NULL); ++} ++ ++#if !LWIP_NETIF_LOOPBACK_MULTITHREADING ++/** ++ * Calls netif_poll() for every netif on the netif_list. ++ */ ++void ++netif_poll_all(void) ++{ ++ struct netif *netif = netif_list; ++ /* loop through netifs */ ++ while (netif != NULL) { ++ netif_poll(netif); ++ /* proceed to next network interface */ ++ netif = netif->next; ++ } ++} ++#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ ++#endif /* ENABLE_LOOPBACK */ +Index: src/core/pbuf.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/pbuf.c,v +retrieving revision 1.127 +retrieving revision 1.128 +diff -u -p -r1.127 -r1.128 +--- src/core/pbuf.c 4 Mar 2008 16:37:46 -0000 1.127 ++++ src/core/pbuf.c 1 Apr 2008 19:05:40 -0000 1.128 +@@ -667,8 +667,8 @@ pbuf_dechain(struct pbuf *p) + * + * @note Only one packet is copied, no packet queue! + * +- * @param p_to pbuf source of the copy +- * @param p_from pbuf destination of the copy ++ * @param p_to pbuf destination of the copy ++ * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big +Index: src/core/stats.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/stats.c,v +retrieving revision 1.27 +retrieving revision 1.28 +diff -u -p -r1.27 -r1.28 +--- src/core/stats.c 4 Mar 2008 16:31:32 -0000 1.27 ++++ src/core/stats.c 27 Jun 2008 18:37:54 -0000 1.28 +@@ -54,7 +54,6 @@ stats_display_proto(struct stats_proto * + { + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); +- LWIP_PLATFORM_DIAG(("rexmit: %"STAT_COUNTER_F"\n\t", proto->rexmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); +@@ -68,6 +67,7 @@ stats_display_proto(struct stats_proto * + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); + } + ++#if IGMP_STATS + void + stats_display_igmp(struct stats_igmp *igmp) + { +@@ -82,7 +82,9 @@ stats_display_igmp(struct stats_igmp *ig + LWIP_PLATFORM_DIAG(("report_rxed: %"STAT_COUNTER_F"\n\t", igmp->report_rxed)); + LWIP_PLATFORM_DIAG(("group_query_rxed: %"STAT_COUNTER_F"\n", igmp->group_query_rxed)); + } ++#endif /* IGMP_STATS */ + ++#if MEM_STATS || MEMP_STATS + void + stats_display_mem(struct stats_mem *mem, char *name) + { +@@ -93,48 +95,53 @@ stats_display_mem(struct stats_mem *mem, + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); + } + ++#if MEMP_STATS + void +-stats_display(void) ++stats_display_memp(struct stats_mem *mem, int index) + { +-#if MEMP_STATS +- s16_t i; + char * memp_names[] = { + #define LWIP_MEMPOOL(name,num,size,desc) desc, + #include "lwip/memp_std.h" + }; +-#endif +-#if LINK_STATS +- stats_display_proto(&lwip_stats.link, "LINK"); +-#endif +-#if ETHARP_STATS +- stats_display_proto(&lwip_stats.etharp, "ETHARP"); +-#endif +-#if IPFRAG_STATS +- stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG"); +-#endif +-#if IP_STATS +- stats_display_proto(&lwip_stats.ip, "IP"); +-#endif +-#if ICMP_STATS +- stats_display_proto(&lwip_stats.icmp, "ICMP"); +-#endif +-#if IGMP_STATS +- stats_display_igmp(&lwip_stats.igmp); +-#endif +-#if UDP_STATS +- stats_display_proto(&lwip_stats.udp, "UDP"); +-#endif +-#if TCP_STATS +- stats_display_proto(&lwip_stats.tcp, "TCP"); +-#endif +-#if MEM_STATS +- stats_display_mem(&lwip_stats.mem, "HEAP"); +-#endif +-#if MEMP_STATS ++ if(index < MEMP_MAX) { ++ stats_display_mem(mem, memp_names[index]); ++ } ++} ++#endif /* MEMP_STATS */ ++#endif /* MEM_STATS || MEMP_STATS */ ++ ++#if SYS_STATS ++void ++stats_display_sys(struct stats_sys *sys) ++{ ++ LWIP_PLATFORM_DIAG(("\nSYS\n\t")); ++ LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); ++ LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); ++ LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); ++ LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); ++ LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); ++ LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); ++} ++#endif /* SYS_STATS */ ++ ++void ++stats_display(void) ++{ ++ s16_t i; ++ ++ LINK_STATS_DISPLAY(); ++ ETHARP_STATS_DISPLAY(); ++ IPFRAG_STATS_DISPLAY(); ++ IP_STATS_DISPLAY(); ++ IGMP_STATS_DISPLAY(); ++ ICMP_STATS_DISPLAY(); ++ UDP_STATS_DISPLAY(); ++ TCP_STATS_DISPLAY(); ++ MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { +- stats_display_mem(&lwip_stats.memp[i], memp_names[i]); ++ MEMP_STATS_DISPLAY(i); + } +-#endif ++ SYS_STATS_DISPLAY(); + } + #endif /* LWIP_STATS_DISPLAY */ + +Index: src/core/sys.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/sys.c,v +retrieving revision 1.32 +retrieving revision 1.33 +diff -u -p -r1.32 -r1.33 +--- src/core/sys.c 25 Nov 2007 13:57:05 -0000 1.32 ++++ src/core/sys.c 16 Jul 2008 20:36:12 -0000 1.33 +@@ -65,7 +65,7 @@ struct sswt_cb + void + sys_mbox_fetch(sys_mbox_t mbox, void **msg) + { +- u32_t time; ++ u32_t time_needed; + struct sys_timeouts *timeouts; + struct sys_timeo *tmptimeout; + sys_timeout_handler h; +@@ -76,18 +76,18 @@ sys_mbox_fetch(sys_mbox_t mbox, void **m + + if (!timeouts || !timeouts->next) { + UNLOCK_TCPIP_CORE(); +- time = sys_arch_mbox_fetch(mbox, msg, 0); ++ time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + LOCK_TCPIP_CORE(); + } else { + if (timeouts->next->time > 0) { + UNLOCK_TCPIP_CORE(); +- time = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); ++ time_needed = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + LOCK_TCPIP_CORE(); + } else { +- time = SYS_ARCH_TIMEOUT; ++ time_needed = SYS_ARCH_TIMEOUT; + } + +- if (time == SYS_ARCH_TIMEOUT) { ++ if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ +@@ -107,8 +107,8 @@ sys_mbox_fetch(sys_mbox_t mbox, void **m + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ +- if (time < timeouts->next->time) { +- timeouts->next->time -= time; ++ if (time_needed < timeouts->next->time) { ++ timeouts->next->time -= time_needed; + } else { + timeouts->next->time = 0; + } +@@ -125,7 +125,7 @@ sys_mbox_fetch(sys_mbox_t mbox, void **m + void + sys_sem_wait(sys_sem_t sem) + { +- u32_t time; ++ u32_t time_needed; + struct sys_timeouts *timeouts; + struct sys_timeo *tmptimeout; + sys_timeout_handler h; +@@ -139,12 +139,12 @@ sys_sem_wait(sys_sem_t sem) + sys_arch_sem_wait(sem, 0); + } else { + if (timeouts->next->time > 0) { +- time = sys_arch_sem_wait(sem, timeouts->next->time); ++ time_needed = sys_arch_sem_wait(sem, timeouts->next->time); + } else { +- time = SYS_ARCH_TIMEOUT; ++ time_needed = SYS_ARCH_TIMEOUT; + } + +- if (time == SYS_ARCH_TIMEOUT) { ++ if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ +@@ -164,8 +164,8 @@ sys_sem_wait(sys_sem_t sem) + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ +- if (time < timeouts->next->time) { +- timeouts->next->time -= time; ++ if (time_needed < timeouts->next->time) { ++ timeouts->next->time -= time_needed; + } else { + timeouts->next->time = 0; + } +Index: src/core/tcp.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/tcp.c,v +retrieving revision 1.85 +retrieving revision 1.86 +diff -u -p -r1.85 -r1.86 +--- src/core/tcp.c 22 Jan 2008 21:15:15 -0000 1.85 ++++ src/core/tcp.c 26 Mar 2008 11:57:13 -0000 1.86 +@@ -509,7 +509,8 @@ tcp_connect(struct tcp_pcb *pcb, struct + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->snd_wnd = TCP_WND; +- /* The send MSS is updated when an MSS option is received. */ ++ /* As initial send MSS, we use TCP_MSS but limit it to 536. ++ The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; + #if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); +@@ -991,7 +992,8 @@ tcp_alloc(u8_t prio) + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_TTL; +- /* The send MSS is updated when an MSS option is received. */ ++ /* As initial send MSS, we use TCP_MSS but limit it to 536. ++ The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; +Index: src/core/tcp_in.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/tcp_in.c,v +retrieving revision 1.97 +retrieving revision 1.100 +diff -u -p -r1.97 -r1.100 +--- src/core/tcp_in.c 22 Jan 2008 21:15:15 -0000 1.97 ++++ src/core/tcp_in.c 24 Jun 2008 15:46:39 -0000 1.100 +@@ -511,7 +511,7 @@ tcp_process(struct tcp_pcb *pcb) + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, +- pcb->rcv_nxt+pcb->rcv_ann_wnd)) { ++ pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } +@@ -1038,7 +1038,7 @@ tcp_receive(struct tcp_pcb *pcb) + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, +- pcb->rcv_nxt + pcb->rcv_ann_wnd - 1)){ ++ pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (pcb->rcv_nxt == seqno) { + accepted_inseq = 1; + /* The incoming segment is the next in sequence. We check if +@@ -1195,14 +1195,14 @@ tcp_receive(struct tcp_pcb *pcb) + } else { + pcb->ooseq = cseg; + } +- } +- tcp_seg_free(next); +- if (cseg->next != NULL) { +- next = cseg->next; +- if (TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { +- /* We need to trim the incoming segment. */ +- cseg->len = (u16_t)(next->tcphdr->seqno - seqno); +- pbuf_realloc(cseg->p, cseg->len); ++ tcp_seg_free(next); ++ if (cseg->next != NULL) { ++ next = cseg->next; ++ if (TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { ++ /* We need to trim the incoming segment. */ ++ cseg->len = (u16_t)(next->tcphdr->seqno - seqno); ++ pbuf_realloc(cseg->p, cseg->len); ++ } + } + } + break; +@@ -1282,10 +1282,7 @@ tcp_receive(struct tcp_pcb *pcb) + + } + } else { +- if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, +- pcb->rcv_nxt + pcb->rcv_ann_wnd-1)){ +- tcp_ack_now(pcb); +- } ++ tcp_ack_now(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that +@@ -1331,7 +1328,8 @@ tcp_parseopt(struct tcp_pcb *pcb) + opts[c + 1] == 0x04) { + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; +- pcb->mss = mss > TCP_MSS? TCP_MSS: mss; ++ /* Limit the mss to the configured TCP_MSS and prevent division by zero */ ++ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + + /* And we are done processing options. */ + break; +Index: src/core/ipv4/autoip.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/ipv4/autoip.c,v +retrieving revision 1.16 +retrieving revision 1.17 +diff -u -p -r1.16 -r1.17 +--- src/core/ipv4/autoip.c 26 Jan 2008 16:11:40 -0000 1.16 ++++ src/core/ipv4/autoip.c 17 Jun 2008 20:16:23 -0000 1.17 +@@ -395,8 +395,8 @@ autoip_arp_reply(struct netif *netif, st + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ +- MEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); +- MEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); ++ SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); ++ SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && +Index: src/core/ipv4/inet_chksum.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/ipv4/inet_chksum.c,v +retrieving revision 1.4 +retrieving revision 1.5 +diff -u -p -r1.4 -r1.5 +--- src/core/ipv4/inet_chksum.c 10 Mar 2008 16:12:31 -0000 1.4 ++++ src/core/ipv4/inet_chksum.c 17 Jun 2008 20:06:25 -0000 1.5 +@@ -41,8 +41,6 @@ + #include "lwip/inet_chksum.h" + #include "lwip/inet.h" + +-#include +- + /* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create +@@ -65,6 +63,11 @@ + # define LWIP_CHKSUM_ALGORITHM 0 + #endif + ++/** Like the name says... */ ++#define SWAP_BYTES_IN_WORD(w) ((w & 0xff) << 8) | ((w & 0xff00) >> 8) ++/** Split an u32_t in two u16_ts and add them up */ ++#define FOLD_U32T(u) ((u >> 16) + (u & 0x0000ffffUL)) ++ + #if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ + /** + * lwip checksum +@@ -86,8 +89,7 @@ lwip_standard_chksum(void *dataptr, u16_ + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; +- while (len > 1) +- { ++ while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; +@@ -98,8 +100,7 @@ lwip_standard_chksum(void *dataptr, u16_ + acc += src; + len -= 2; + } +- if (len > 0) +- { ++ if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; +@@ -154,19 +155,22 @@ lwip_standard_chksum(void *dataptr, int + } + + /* Consume left-over byte, if any */ +- if (len > 0) ++ if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps;; ++ } + + /* Add end bytes */ + sum += t; + +- /* Fold 32-bit sum to 16 bits */ +- while ((sum >> 16) != 0) +- sum = (sum & 0xffff) + (sum >> 16); ++ /* Fold 32-bit sum to 16 bits ++ calling this twice is propably faster than if statements... */ ++ sum = FOLD_U32T(sum); ++ sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ +- if (odd) +- sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8); ++ if (odd) { ++ sum = SWAP_BYTES_IN_WORD(sum); ++ } + + return sum; + } +@@ -211,18 +215,20 @@ lwip_standard_chksum(void *dataptr, int + + while (len > 7) { + tmp = sum + *pl++; /* ping */ +- if (tmp < sum) ++ if (tmp < sum) { + tmp++; /* add back carry */ ++ } + + sum = tmp + *pl++; /* pong */ +- if (sum < tmp) ++ if (sum < tmp) { + sum++; /* add back carry */ ++ } + + len -= 8; + } + + /* make room in upper bits */ +- sum = (sum >> 16) + (sum & 0xffff); ++ sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + +@@ -233,16 +239,20 @@ lwip_standard_chksum(void *dataptr, int + } + + /* dangling tail byte remaining? */ +- if (len > 0) /* include odd byte */ ++ if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; ++ } + + sum += t; /* add end bytes */ + +- while ((sum >> 16) != 0) /* combine halves */ +- sum = (sum >> 16) + (sum & 0xffff); ++ /* Fold 32-bit sum to 16 bits ++ calling this twice is propably faster than if statements... */ ++ sum = FOLD_U32T(sum); ++ sum = FOLD_U32T(sum); + +- if (odd) +- sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8); ++ if (odd) { ++ sum = SWAP_BYTES_IN_WORD(sum); ++ } + + return sum; + } +@@ -277,18 +287,18 @@ inet_chksum_pseudo(struct pbuf *p, + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ +- while ((acc >> 16) != 0) { +- acc = (acc & 0xffffUL) + (acc >> 16); +- } ++ /* just executing this next line is probably faster that the if statement needed ++ to check whether we really need to execute it, and does no harm */ ++ acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; +- acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); ++ acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { +- acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); ++ acc = SWAP_BYTES_IN_WORD(acc); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); +@@ -297,9 +307,10 @@ inet_chksum_pseudo(struct pbuf *p, + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + +- while ((acc >> 16) != 0) { +- acc = (acc & 0xffffUL) + (acc >> 16); +- } ++ /* Fold 32-bit sum to 16 bits ++ calling this twice is propably faster than if statements... */ ++ acc = FOLD_U32T(acc); ++ acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); + } +@@ -340,18 +351,17 @@ inet_chksum_pseudo_partial(struct pbuf * + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ +- while ((acc >> 16) != 0) { +- acc = (acc & 0xffffUL) + (acc >> 16); +- } ++ /* fold the upper bit down */ ++ acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; +- acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); ++ acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { +- acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); ++ acc = SWAP_BYTES_IN_WORD(acc); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); +@@ -360,9 +370,10 @@ inet_chksum_pseudo_partial(struct pbuf * + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + +- while ((acc >> 16) != 0) { +- acc = (acc & 0xffffUL) + (acc >> 16); +- } ++ /* Fold 32-bit sum to 16 bits ++ calling this twice is propably faster than if statements... */ ++ acc = FOLD_U32T(acc); ++ acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); + } +@@ -380,13 +391,7 @@ inet_chksum_pseudo_partial(struct pbuf * + u16_t + inet_chksum(void *dataptr, u16_t len) + { +- u32_t acc; +- +- acc = LWIP_CHKSUM(dataptr, len); +- while ((acc >> 16) != 0) { +- acc = (acc & 0xffff) + (acc >> 16); +- } +- return (u16_t)~(acc & 0xffff); ++ return ~LWIP_CHKSUM(dataptr, len); + } + + /** +@@ -407,17 +412,15 @@ inet_chksum_pbuf(struct pbuf *p) + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); +- while ((acc >> 16) != 0) { +- acc = (acc & 0xffffUL) + (acc >> 16); +- } ++ acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; +- acc = (acc & 0x00ffUL << 8) | (acc & 0xff00UL >> 8); ++ acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { +- acc = ((acc & 0x00ffUL) << 8) | ((acc & 0xff00UL) >> 8); ++ acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); + } +Index: src/core/ipv4/ip.c +=================================================================== +RCS file: /sources/lwip/lwip/src/core/ipv4/ip.c,v +retrieving revision 1.66 +retrieving revision 1.68 +diff -u -p -r1.66 -r1.68 +--- src/core/ipv4/ip.c 14 Jan 2008 20:53:23 -0000 1.66 ++++ src/core/ipv4/ip.c 17 Jun 2008 19:39:22 -0000 1.68 +@@ -531,9 +531,19 @@ ip_output_if(struct pbuf *p, struct ip_a + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +- LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); ++#if (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) ++ if (ip_addr_cmp(dest, &netif->ip_addr)) { ++ /* Packet to self, enqueue it for loopback */ ++ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); ++ ++ return netif_loop_output(netif, p, dest); ++ } else ++#endif /* (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) */ ++ { ++ LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + +- return netif->output(netif, p, dest); ++ return netif->output(netif, p, dest); ++ } + } + + /** +Index: src/include/lwip/debug.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/debug.h,v +retrieving revision 1.37 +retrieving revision 1.39 +diff -u -p -r1.37 -r1.39 +--- src/include/lwip/debug.h 22 Sep 2007 11:16:07 -0000 1.37 ++++ src/include/lwip/debug.h 16 Jul 2008 20:36:22 -0000 1.39 +@@ -61,26 +61,28 @@ + #define LWIP_DBG_HALT 0x08U + + #ifndef LWIP_NOASSERT +-#define LWIP_ASSERT(x,y) do { if(!(y)) LWIP_PLATFORM_ASSERT(x); } while(0) ++#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ ++ LWIP_PLATFORM_ASSERT(message); } while(0) + #else /* LWIP_NOASSERT */ +-#define LWIP_ASSERT(x,y) ++#define LWIP_ASSERT(message, assertion) + #endif /* LWIP_NOASSERT */ + +-/** print "m" message only if "e" is true, and execute "h" expression */ ++/** if "expression" isn't true, then print "message" and execute "handler" expression */ + #ifndef LWIP_ERROR +-#define LWIP_ERROR(m,e,h) do { if (!(e)) { LWIP_PLATFORM_ASSERT(m); h;}} while(0) ++#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ ++ LWIP_PLATFORM_ASSERT(message); handler;}} while(0) + #endif /* LWIP_ERROR */ + + #ifdef LWIP_DEBUG + /** print debug message only if debug message type is enabled... + * AND is of correct type AND is at least LWIP_DBG_LEVEL + */ +-#define LWIP_DEBUGF(debug,x) do { \ ++#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ +- LWIP_PLATFORM_DIAG(x); \ ++ LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ +@@ -88,7 +90,7 @@ + } while(0) + + #else /* LWIP_DEBUG */ +-#define LWIP_DEBUGF(debug,x) ++#define LWIP_DEBUGF(debug, message) + #endif /* LWIP_DEBUG */ + + #endif /* __LWIP_DEBUG_H__ */ +Index: src/include/lwip/err.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/err.h,v +retrieving revision 1.13 +retrieving revision 1.15 +diff -u -p -r1.13 -r1.15 +--- src/include/lwip/err.h 13 Dec 2007 23:06:50 -0000 1.13 ++++ src/include/lwip/err.h 17 Jun 2008 20:27:32 -0000 1.15 +@@ -33,37 +33,43 @@ + #define __LWIP_ERR_H__ + + #include "lwip/opt.h" ++#include "lwip/arch.h" + + #ifdef __cplusplus + extern "C" { + #endif + +-typedef s8_t err_t; ++/** Define LWIP_ERR_T in cc.h if you want to use ++ * a different type for your platform (must be signed). */ ++#ifdef LWIP_ERR_T ++typedef LWIP_ERR_T err_t; ++#else /* LWIP_ERR_T */ ++ typedef s8_t err_t; ++#endif /* LWIP_ERR_T*/ + + /* Definitions for error constants. */ + + #define ERR_OK 0 /* No error, everything OK. */ + #define ERR_MEM -1 /* Out of memory error. */ + #define ERR_BUF -2 /* Buffer error. */ +-#define ERR_RTE -3 /* Routing problem. */ ++#define ERR_TIMEOUT -3 /* Timeout. */ ++#define ERR_RTE -4 /* Routing problem. */ + + #define ERR_IS_FATAL(e) ((e) < ERR_RTE) + +-#define ERR_ABRT -4 /* Connection aborted. */ +-#define ERR_RST -5 /* Connection reset. */ +-#define ERR_CLSD -6 /* Connection closed. */ +-#define ERR_CONN -7 /* Not connected. */ ++#define ERR_ABRT -5 /* Connection aborted. */ ++#define ERR_RST -6 /* Connection reset. */ ++#define ERR_CLSD -7 /* Connection closed. */ ++#define ERR_CONN -8 /* Not connected. */ + +-#define ERR_VAL -8 /* Illegal value. */ ++#define ERR_VAL -9 /* Illegal value. */ + +-#define ERR_ARG -9 /* Illegal argument. */ ++#define ERR_ARG -10 /* Illegal argument. */ + +-#define ERR_USE -10 /* Address in use. */ ++#define ERR_USE -11 /* Address in use. */ + +-#define ERR_IF -11 /* Low-level netif error */ +-#define ERR_ISCONN -12 /* Already connected. */ +- +-#define ERR_TIMEOUT -13 /* Timeout. */ ++#define ERR_IF -12 /* Low-level netif error */ ++#define ERR_ISCONN -13 /* Already connected. */ + + #define ERR_INPROGRESS -14 /* Operation in progress */ + +Index: src/include/lwip/mem.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/mem.h,v +retrieving revision 1.21 +retrieving revision 1.22 +diff -u -p -r1.21 -r1.22 +--- src/include/lwip/mem.h 4 Mar 2008 16:31:32 -0000 1.21 ++++ src/include/lwip/mem.h 30 May 2008 11:37:15 -0000 1.22 +@@ -50,16 +50,16 @@ typedef size_t mem_size_t; + * allow these defines to be overridden. + */ + #ifndef mem_free +-#define mem_free(x) free(x) ++#define mem_free free + #endif + #ifndef mem_malloc +-#define mem_malloc(x) malloc(x) ++#define mem_malloc malloc + #endif + #ifndef mem_calloc +-#define mem_calloc(x, y) calloc(x, y) ++#define mem_calloc calloc + #endif + #ifndef mem_realloc +-#define mem_realloc(x, size) (x) ++#define mem_realloc realloc + #endif + #else /* MEM_LIBC_MALLOC */ + +Index: src/include/lwip/netif.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/netif.h,v +retrieving revision 1.43 +retrieving revision 1.46 +diff -u -p -r1.43 -r1.46 +--- src/include/lwip/netif.h 9 Oct 2007 19:59:59 -0000 1.43 ++++ src/include/lwip/netif.h 19 Jun 2008 16:27:23 -0000 1.46 +@@ -34,6 +34,8 @@ + + #include "lwip/opt.h" + ++#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) ++ + #include "lwip/err.h" + + #include "lwip/ip_addr.h" +@@ -165,6 +167,14 @@ struct netif { + #if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; + #endif /* LWIP_NETIF_HWADDRHINT */ ++#if ENABLE_LOOPBACK ++ /* List of packets to be queued for ourselves. */ ++ struct pbuf *loop_first; ++ struct pbuf *loop_last; ++#if LWIP_LOOPBACK_MAX_PBUFS ++ u16_t loop_cnt_current; ++#endif /* LWIP_LOOPBACK_MAX_PBUFS */ ++#endif /* ENABLE_LOOPBACK */ + }; + + #if LWIP_SNMP +@@ -242,4 +252,12 @@ void netif_set_link_callback(struct neti + } + #endif + ++#if ENABLE_LOOPBACK ++err_t netif_loop_output(struct netif *netif, struct pbuf *p, struct ip_addr *dest_ip); ++void netif_poll(struct netif *netif); ++#if !LWIP_NETIF_LOOPBACK_MULTITHREADING ++void netif_poll_all(void); ++#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ ++#endif /* ENABLE_LOOPBACK */ ++ + #endif /* __LWIP_NETIF_H__ */ +Index: src/include/lwip/opt.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/opt.h,v +retrieving revision 1.116 +retrieving revision 1.122 +diff -u -p -r1.116 -r1.122 +--- src/include/lwip/opt.h 31 Jan 2008 18:19:29 -0000 1.116 ++++ src/include/lwip/opt.h 30 Jun 2008 18:16:52 -0000 1.122 +@@ -155,6 +155,27 @@ + #define MEMP_USE_CUSTOM_POOLS 0 + #endif + ++/** ++ * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from ++ * interrupt context (or another context that doesn't allow waiting for a ++ * semaphore). ++ * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, ++ * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs ++ * with each loop so that mem_free can run. ++ * ++ * ATTENTION: As you can see from the above description, this leads to dis-/ ++ * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc ++ * can need longer. ++ * ++ * If you don't want that, at least for NO_SYS=0, you can still use the following ++ * functions to enqueue a deallocation call which then runs in the tcpip_thread ++ * context: ++ * - pbuf_free_callback(p); ++ * - mem_free_callback(m); ++ */ ++#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT ++#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 ++#endif + + /* + ------------------------------------------------ +@@ -815,6 +836,39 @@ + #define LWIP_NETIF_HWADDRHINT 0 + #endif + ++/** ++ * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP ++ * address equal to the netif IP address, looping them back up the stack. ++ */ ++#ifndef LWIP_NETIF_LOOPBACK ++#define LWIP_NETIF_LOOPBACK 0 ++#endif ++ ++/** ++ * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback ++ * sending for each netif (0 = disabled) ++ */ ++#ifndef LWIP_LOOPBACK_MAX_PBUFS ++#define LWIP_LOOPBACK_MAX_PBUFS 0 ++#endif ++ ++/** ++ * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in ++ * the system, as netifs must change how they behave depending on this setting ++ * for the LWIP_NETIF_LOOPBACK option to work. ++ * Setting this is needed to avoid reentering non-reentrant functions like ++ * tcp_input(). ++ * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a ++ * multithreaded environment like tcpip.c. In this case, netif->input() ++ * is called directly. ++ * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. ++ * The packets are put on a list and netif_poll() must be called in ++ * the main application loop. ++ */ ++#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING ++#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) ++#endif ++ + /* + ------------------------------------ + ---------- LOOPIF options ---------- +@@ -827,20 +881,16 @@ + #define LWIP_HAVE_LOOPIF 0 + #endif + ++/* ++ ------------------------------------ ++ ---------- SLIPIF options ---------- ++ ------------------------------------ ++*/ + /** +- * LWIP_LOOPIF_MULTITHREADING: Indicates whether threading is enabled in +- * the system, as LOOPIF must change how it behaves depending on this setting. +- * Setting this is needed to avoid reentering non-reentrant functions like +- * tcp_input(). +- * LWIP_LOOPIF_MULTITHREADING==1: Indicates that the user is using a +- * multithreaded environment like tcpip.c. In this case, netif->input() +- * is called directly. +- * LWIP_LOOPIF_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. +- * The packets are put on a list and loopif_poll() must be called in +- * the main application loop. ++ * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +-#ifndef LWIP_LOOPIF_MULTITHREADING +-#define LWIP_LOOPIF_MULTITHREADING 1 ++#ifndef LWIP_HAVE_SLIPIF ++#define LWIP_HAVE_SLIPIF 0 + #endif + + /* +Index: src/include/lwip/sio.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/sio.h,v +retrieving revision 1.7 +retrieving revision 1.8 +diff -u -p -r1.7 -r1.8 +--- src/include/lwip/sio.h 6 Sep 2007 16:43:44 -0000 1.7 ++++ src/include/lwip/sio.h 27 Mar 2008 18:06:02 -0000 1.8 +@@ -32,16 +32,24 @@ + * It needs to be implemented by those platforms which need SLIP or PPP + */ + ++#ifndef __SIO_H__ ++#define __SIO_H__ ++ + #include "lwip/arch.h" + + #ifdef __cplusplus + extern "C" { + #endif + ++/* If you want to define sio_fd_t elsewhere or differently, ++ define this in your cc.h file. */ + #ifndef __sio_fd_t_defined + typedef void * sio_fd_t; + #endif + ++/* The following functions can be defined to something else in your cc.h file ++ or be implemented in your custom sio.c file. */ ++ + #ifndef sio_open + sio_fd_t sio_open(u8_t); + #endif +@@ -69,3 +77,5 @@ void sio_read_abort(sio_fd_t); + #ifdef __cplusplus + } + #endif ++ ++#endif /* __SIO_H__ */ +Index: src/include/lwip/sockets.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/sockets.h,v +retrieving revision 1.38 +retrieving revision 1.39 +diff -u -p -r1.38 -r1.39 +--- src/include/lwip/sockets.h 2 Dec 2007 15:24:02 -0000 1.38 ++++ src/include/lwip/sockets.h 26 Apr 2008 10:46:23 -0000 1.39 +@@ -177,7 +177,22 @@ typedef struct ip_mreq { + } ip_mreq; + #endif /* LWIP_IGMP */ + +-/* Unimplemented for now... */ ++/* ++ * The Type of Service provides an indication of the abstract ++ * parameters of the quality of service desired. These parameters are ++ * to be used to guide the selection of the actual service parameters ++ * when transmitting a datagram through a particular network. Several ++ * networks offer service precedence, which somehow treats high ++ * precedence traffic as more important than other traffic (generally ++ * by accepting only traffic above a certain precedence at time of high ++ * load). The major choice is a three way tradeoff between low-delay, ++ * high-reliability, and high-throughput. ++ * The use of the Delay, Throughput, and Reliability indications may ++ * increase the cost (in some sense) of the service. In many networks ++ * better performance for one of these parameters is coupled with worse ++ * performance on another. Except for very unusual cases at most two ++ * of these three indications should be set. ++ */ + #define IPTOS_TOS_MASK 0x1E + #define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) + #define IPTOS_LOWDELAY 0x10 +@@ -187,7 +202,13 @@ typedef struct ip_mreq { + #define IPTOS_MINCOST IPTOS_LOWCOST + + /* +- * Definitions for IP precedence (also in ip_tos) (Unimplemented) ++ * The Network Control precedence designation is intended to be used ++ * within a network only. The actual use and control of that ++ * designation is up to each network. The Internetwork Control ++ * designation is intended for use by gateway control originators only. ++ * If the actual use of these precedence designations is of concern to ++ * a particular network, it is the responsibility of that network to ++ * control the access to, and use of, those precedence designations. + */ + #define IPTOS_PREC_MASK 0xe0 + #define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +Index: src/include/lwip/stats.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/stats.h,v +retrieving revision 1.19 +retrieving revision 1.23 +diff -u -p -r1.19 -r1.23 +--- src/include/lwip/stats.h 28 Nov 2007 21:25:07 -0000 1.19 ++++ src/include/lwip/stats.h 8 Jul 2008 09:15:57 -0000 1.23 +@@ -57,7 +57,6 @@ extern "C" { + + struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ +- STAT_COUNTER rexmit; /* Retransmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ +@@ -87,7 +86,8 @@ struct stats_mem { + mem_size_t avail; + mem_size_t used; + mem_size_t max; +- mem_size_t err; ++ STAT_COUNTER err; ++ STAT_COUNTER illegal; + }; + + struct stats_syselem { +@@ -142,64 +142,138 @@ extern struct stats_ lwip_stats; + #define stats_init() /* Compatibility define, not init needed. */ + + #define STATS_INC(x) ++lwip_stats.x ++#define STATS_DEC(x) --lwip_stats.x + #else + #define stats_init() + #define STATS_INC(x) ++#define STATS_DEC(x) + #endif /* LWIP_STATS */ + + #if TCP_STATS + #define TCP_STATS_INC(x) STATS_INC(x) ++#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") + #else + #define TCP_STATS_INC(x) ++#define TCP_STATS_DISPLAY() + #endif + + #if UDP_STATS + #define UDP_STATS_INC(x) STATS_INC(x) ++#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") + #else + #define UDP_STATS_INC(x) ++#define UDP_STATS_DISPLAY() + #endif + + #if ICMP_STATS + #define ICMP_STATS_INC(x) STATS_INC(x) ++#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") + #else + #define ICMP_STATS_INC(x) ++#define ICMP_STATS_DISPLAY() + #endif + + #if IGMP_STATS + #define IGMP_STATS_INC(x) STATS_INC(x) ++#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp) + #else + #define IGMP_STATS_INC(x) ++#define IGMP_STATS_DISPLAY() + #endif + + #if IP_STATS + #define IP_STATS_INC(x) STATS_INC(x) ++#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") + #else + #define IP_STATS_INC(x) ++#define IP_STATS_DISPLAY() + #endif + + #if IPFRAG_STATS + #define IPFRAG_STATS_INC(x) STATS_INC(x) ++#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") + #else + #define IPFRAG_STATS_INC(x) ++#define IPFRAG_STATS_DISPLAY() + #endif + + #if ETHARP_STATS + #define ETHARP_STATS_INC(x) STATS_INC(x) ++#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") + #else + #define ETHARP_STATS_INC(x) ++#define ETHARP_STATS_DISPLAY() + #endif + + #if LINK_STATS + #define LINK_STATS_INC(x) STATS_INC(x) ++#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") + #else + #define LINK_STATS_INC(x) ++#define LINK_STATS_DISPLAY() ++#endif ++ ++#if MEM_STATS ++#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y ++#define MEM_STATS_INC(x) STATS_INC(mem.x) ++#define MEM_STATS_INC_USED(x, y) do { lwip_stats.mem.used += y; \ ++ if (lwip_stats.mem.max < lwip_stats.mem.used) { \ ++ lwip_stats.mem.max = lwip_stats.mem.used; \ ++ } \ ++ } while(0) ++#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y ++#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") ++#else ++#define MEM_STATS_AVAIL(x, y) ++#define MEM_STATS_INC(x) ++#define MEM_STATS_INC_USED(x, y) ++#define MEM_STATS_DEC_USED(x, y) ++#define MEM_STATS_DISPLAY() ++#endif ++ ++#if MEMP_STATS ++#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y ++#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) ++#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) ++#define MEMP_STATS_INC_USED(x, i) do { ++lwip_stats.memp[i].used; \ ++ if (lwip_stats.memp[i].max < lwip_stats.memp[i].used) { \ ++ lwip_stats.memp[i].max = lwip_stats.memp[i].used; \ ++ } \ ++ } while(0) ++#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) ++#else ++#define MEMP_STATS_AVAIL(x, i, y) ++#define MEMP_STATS_INC(x, i) ++#define MEMP_STATS_DEC(x, i) ++#define MEMP_STATS_INC_USED(x, i) ++#define MEMP_STATS_DISPLAY(i) ++#endif ++ ++#if SYS_STATS ++#define SYS_STATS_INC(x) STATS_INC(sys.x) ++#define SYS_STATS_DEC(x) STATS_DEC(sys.x) ++#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) ++#else ++#define SYS_STATS_INC(x) ++#define SYS_STATS_DEC(x) ++#define SYS_STATS_DISPLAY() + #endif + + /* Display of statistics */ + #if LWIP_STATS_DISPLAY + void stats_display(void); ++void stats_display_proto(struct stats_proto *proto, char *name); ++void stats_display_igmp(struct stats_igmp *igmp); ++void stats_display_mem(struct stats_mem *mem, char *name); ++void stats_display_memp(struct stats_mem *mem, int index); ++void stats_display_sys(struct stats_sys *sys); + #else + #define stats_display() ++#define stats_display_proto(proto, name) ++#define stats_display_igmp(igmp) ++#define stats_display_mem(mem, name) ++#define stats_display_memp(mem, index) ++#define stats_display_sys(sys) + #endif /* LWIP_STATS_DISPLAY */ + + #ifdef __cplusplus +Index: src/include/lwip/tcpip.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/lwip/tcpip.h,v +retrieving revision 1.24 +retrieving revision 1.27 +diff -u -p -r1.24 -r1.27 +--- src/include/lwip/tcpip.h 12 Jan 2008 11:52:22 -0000 1.24 ++++ src/include/lwip/tcpip.h 27 Jun 2008 20:34:55 -0000 1.27 +@@ -83,7 +83,11 @@ err_t tcpip_netifapi_lock(struct netifap + #endif /* LWIP_NETIF_API */ + + err_t tcpip_callback_with_block(void (*f)(void *ctx), void *ctx, u8_t block); +-#define tcpip_callback(f,ctx) tcpip_callback_with_block(f,ctx,1) ++#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) ++ ++/* free pbufs or heap memory from another context without blocking */ ++err_t pbuf_free_callback(struct pbuf *p); ++err_t mem_free_callback(void *m); + + err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); + #define tcpip_untimeout(h, arg) tcpip_timeout(0xffffffff, h, arg) +Index: src/include/netif/loopif.h +=================================================================== +RCS file: /sources/lwip/lwip/src/include/netif/loopif.h,v +retrieving revision 1.7 +retrieving revision 1.9 +diff -u -p -r1.7 -r1.9 +--- src/include/netif/loopif.h 10 May 2007 10:59:20 -0000 1.7 ++++ src/include/netif/loopif.h 17 Jun 2008 20:12:22 -0000 1.9 +@@ -32,6 +32,7 @@ + #ifndef __NETIF_LOOPIF_H__ + #define __NETIF_LOOPIF_H__ + ++#include "lwip/opt.h" + #include "lwip/netif.h" + #include "lwip/err.h" + +@@ -39,9 +40,9 @@ + extern "C" { + #endif + +-#if !LWIP_LOOPIF_MULTITHREADING +-void loopif_poll(struct netif *netif); +-#endif ++#if !LWIP_NETIF_LOOPBACK_MULTITHREADING ++#define loopif_poll netif_poll ++#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + err_t loopif_init(struct netif *netif); + +Index: src/netif/etharp.c +=================================================================== +RCS file: /sources/lwip/lwip/src/netif/etharp.c,v +retrieving revision 1.145 +retrieving revision 1.148 +diff -u -p -r1.145 -r1.148 +--- src/netif/etharp.c 4 Mar 2008 13:41:24 -0000 1.145 ++++ src/netif/etharp.c 19 Jun 2008 16:40:59 -0000 1.148 +@@ -353,7 +353,7 @@ find_entry(struct ip_addr *ipaddr, u8_t + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets +- * 4) oldest pending entry without queued packets ++ * 4) oldest pending entry with queued packets + * + * { ETHARP_TRY_HARD is set at this point } + */ +@@ -1130,7 +1130,14 @@ ethernet_input(struct pbuf *p, struct ne + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; +- ++ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ++ ("ethernet_input: dest:%02x:%02x:%02x:%02x:%02x:%02x, src:%02x:%02x:%02x:%02x:%02x:%02x, type:%2hx\n", ++ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], ++ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], ++ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], ++ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], ++ (unsigned)htons(ethhdr->type))); ++ + switch (htons(ethhdr->type)) { + /* IP packet? */ + case ETHTYPE_IP: +@@ -1165,6 +1172,8 @@ ethernet_input(struct pbuf *p, struct ne + #endif /* PPPOE_SUPPORT */ + + default: ++ ETHARP_STATS_INC(etharp.proterr); ++ ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + p = NULL; + break; +Index: src/netif/loopif.c +=================================================================== +RCS file: /sources/lwip/lwip/src/netif/loopif.c,v +retrieving revision 1.26 +retrieving revision 1.27 +diff -u -p -r1.26 -r1.27 +--- src/netif/loopif.c 31 Aug 2007 10:14:09 -0000 1.26 ++++ src/netif/loopif.c 12 Jun 2008 20:10:10 -0000 1.27 +@@ -40,149 +40,8 @@ + #if LWIP_HAVE_LOOPIF + + #include "netif/loopif.h" +-#include "lwip/pbuf.h" + #include "lwip/snmp.h" + +-#include +- +-#if !LWIP_LOOPIF_MULTITHREADING +- +-#include "lwip/sys.h" +-#include "lwip/mem.h" +- +-/* helper struct for the linked list of pbufs */ +-struct loopif_private { +- struct pbuf *first; +- struct pbuf *last; +-}; +- +-/** +- * Call loopif_poll() in the main loop of your application. This is to prevent +- * reentering non-reentrant functions like tcp_input(). Packets passed to +- * loopif_output() are put on a list that is passed to netif->input() by +- * loopif_poll(). +- * +- * @param netif the lwip network interface structure for this loopif +- */ +-void +-loopif_poll(struct netif *netif) +-{ +- SYS_ARCH_DECL_PROTECT(lev); +- struct pbuf *in, *in_end; +- struct loopif_private *priv = (struct loopif_private*)netif->state; +- +- LWIP_ERROR("priv != NULL", (priv != NULL), return;); +- +- do { +- /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ +- SYS_ARCH_PROTECT(lev); +- in = priv->first; +- if(in) { +- in_end = in; +- while(in_end->len != in_end->tot_len) { +- LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); +- in_end = in_end->next; +- } +- /* 'in_end' now points to the last pbuf from 'in' */ +- if(in_end == priv->last) { +- /* this was the last pbuf in the list */ +- priv->first = priv->last = NULL; +- } else { +- /* pop the pbuf off the list */ +- priv->first = in_end->next; +- LWIP_ASSERT("should not be null since first != last!", priv->first != NULL); +- } +- } +- SYS_ARCH_UNPROTECT(lev); +- +- if(in != NULL) { +- if(in_end->next != NULL) { +- /* De-queue the pbuf from its successors on the 'priv' list. */ +- in_end->next = NULL; +- } +- if(netif->input(in, netif) != ERR_OK) { +- pbuf_free(in); +- } +- /* Don't reference the packet any more! */ +- in = NULL; +- in_end = NULL; +- } +- /* go on while there is a packet on the list */ +- } while(priv->first != NULL); +-} +-#endif /* LWIP_LOOPIF_MULTITHREADING */ +- +-/** +- * Send an IP packet over the loopback interface. +- * The pbuf is simply copied and handed back to netif->input. +- * In multithreaded mode, this is done directly since netif->input must put +- * the packet on a queue. +- * In callback mode, the packet is put on an internal queue and is fed to +- * netif->input by loopif_poll(). +- * +- * @param netif the lwip network interface structure for this loopif +- * @param p the (IP) packet to 'send' +- * @param ipaddr the ip address to send the packet to (not used for loopif) +- * @return ERR_OK if the packet has been sent +- * ERR_MEM if the pbuf used to copy the packet couldn't be allocated +- */ +-static err_t +-loopif_output(struct netif *netif, struct pbuf *p, +- struct ip_addr *ipaddr) +-{ +-#if !LWIP_LOOPIF_MULTITHREADING +- SYS_ARCH_DECL_PROTECT(lev); +- struct loopif_private *priv; +- struct pbuf *last; +-#endif /* LWIP_LOOPIF_MULTITHREADING */ +- struct pbuf *r; +- err_t err; +- +- LWIP_UNUSED_ARG(ipaddr); +- +- /* Allocate a new pbuf */ +- r = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); +- if (r == NULL) { +- return ERR_MEM; +- } +- +- /* Copy the whole pbuf queue p into the single pbuf r */ +- if ((err = pbuf_copy(r, p)) != ERR_OK) { +- pbuf_free(r); +- r = NULL; +- return err; +- } +- +-#if LWIP_LOOPIF_MULTITHREADING +- /* Multithreading environment, netif->input() is supposed to put the packet +- into a mailbox, so we can safely call it here without risking to re-enter +- functions that are not reentrant (TCP!!!) */ +- if(netif->input(r, netif) != ERR_OK) { +- pbuf_free(r); +- r = NULL; +- } +-#else /* LWIP_LOOPIF_MULTITHREADING */ +- /* Raw API without threads: put the packet on a linked list which gets emptied +- through calling loopif_poll(). */ +- priv = (struct loopif_private*)netif->state; +- +- /* let last point to the last pbuf in chain r */ +- for (last = r; last->next != NULL; last = last->next); +- SYS_ARCH_PROTECT(lev); +- if(priv->first != NULL) { +- LWIP_ASSERT("if first != NULL, last must also be != NULL", priv->last != NULL); +- priv->last->next = r; +- priv->last = last; +- } else { +- priv->first = r; +- priv->last = last; +- } +- SYS_ARCH_UNPROTECT(lev); +-#endif /* LWIP_LOOPIF_MULTITHREADING */ +- +- return ERR_OK; +-} +- + /** + * Initialize a lwip network interface structure for a loopback interface + * +@@ -193,16 +52,6 @@ loopif_output(struct netif *netif, struc + err_t + loopif_init(struct netif *netif) + { +-#if !LWIP_LOOPIF_MULTITHREADING +- struct loopif_private *priv; +- +- priv = (struct loopif_private*)mem_malloc(sizeof(struct loopif_private)); +- if(priv == NULL) +- return ERR_MEM; +- priv->first = priv->last = NULL; +- netif->state = priv; +-#endif /* LWIP_LOOPIF_MULTITHREADING */ +- + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ +@@ -210,7 +59,7 @@ loopif_init(struct netif *netif) + + netif->name[0] = 'l'; + netif->name[1] = 'o'; +- netif->output = loopif_output; ++ netif->output = netif_loop_output; + return ERR_OK; + } + +Index: src/netif/slipif.c +=================================================================== +RCS file: /sources/lwip/lwip/src/netif/slipif.c,v +retrieving revision 1.29 +retrieving revision 1.30 +diff -u -p -r1.29 -r1.30 +--- src/netif/slipif.c 30 Nov 2007 17:22:21 -0000 1.29 ++++ src/netif/slipif.c 17 Jun 2008 20:14:05 -0000 1.30 +@@ -44,6 +44,9 @@ + + #include "netif/slipif.h" + #include "lwip/opt.h" ++ ++#if LWIP_HAVE_SLIPIF ++ + #include "lwip/def.h" + #include "lwip/pbuf.h" + #include "lwip/sys.h" +@@ -273,3 +276,4 @@ slipif_init(struct netif *netif) + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop, netif, SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); + return ERR_OK; + } ++#endif /* LWIP_HAVE_SLIPIF */ diff --git a/stubdom/newlib.patch b/stubdom/newlib.patch new file mode 100644 index 0000000..733dc13 --- /dev/null +++ b/stubdom/newlib.patch @@ -0,0 +1,749 @@ +There is a mix between longs and long longs. + +Index: newlib/libc/include/inttypes.h +=================================================================== +RCS file: /cvs/src/src/newlib/libc/include/inttypes.h,v +retrieving revision 1.3 +diff -u -p -r1.3 inttypes.h +--- newlib/libc/include/inttypes.h 16 Dec 2005 19:03:12 -0000 1.3 ++++ newlib/libc/include/inttypes.h 8 Nov 2007 16:32:44 -0000 +@@ -163,12 +163,12 @@ + + + /* 64-bit types */ +-#if __have_longlong64 +-#define __PRI64(x) __STRINGIFY(ll##x) +-#define __SCN64(x) __STRINGIFY(ll##x) +-#elif __have_long64 ++#if __have_long64 + #define __PRI64(x) __STRINGIFY(l##x) + #define __SCN64(x) __STRINGIFY(l##x) ++#elif __have_longlong64 ++#define __PRI64(x) __STRINGIFY(ll##x) ++#define __SCN64(x) __STRINGIFY(ll##x) + #else + #define __PRI64(x) __STRINGIFY(x) + #define __SCN64(x) __STRINGIFY(x) +@@ -217,12 +217,12 @@ + #endif + + /* max-bit types */ +-#if __have_longlong64 +-#define __PRIMAX(x) __STRINGIFY(ll##x) +-#define __SCNMAX(x) __STRINGIFY(ll##x) +-#elif __have_long64 ++#if __have_long64 + #define __PRIMAX(x) __STRINGIFY(l##x) + #define __SCNMAX(x) __STRINGIFY(l##x) ++#elif __have_longlong64 ++#define __PRIMAX(x) __STRINGIFY(ll##x) ++#define __SCNMAX(x) __STRINGIFY(ll##x) + #else + #define __PRIMAX(x) __STRINGIFY(x) + #define __SCNMAX(x) __STRINGIFY(x) +@@ -242,12 +242,12 @@ + #define SCNxMAX __SCNMAX(x) + + /* ptr types */ +-#if __have_longlong64 +-#define __PRIPTR(x) __STRINGIFY(ll##x) +-#define __SCNPTR(x) __STRINGIFY(ll##x) +-#elif __have_long64 ++#if __have_long64 + #define __PRIPTR(x) __STRINGIFY(l##x) + #define __SCNPTR(x) __STRINGIFY(l##x) ++#elif __have_longlong64 ++#define __PRIPTR(x) __STRINGIFY(ll##x) ++#define __SCNPTR(x) __STRINGIFY(ll##x) + #else + #define __PRIPTR(x) __STRINGIFY(x) + #define __SCNPTR(x) __STRINGIFY(x) + +We don't want u?int32_t to be long as our code assume in a lot of places to be +int. + +Index: newlib/libc/include/stdint.h +=================================================================== +RCS file: /cvs/src/src/newlib/libc/include/stdint.h,v +retrieving revision 1.10 +diff -u -p -r1.10 stdint.h +--- newlib/libc/include/stdint.h 16 Aug 2006 21:39:43 -0000 1.10 ++++ newlib/libc/include/stdint.h 12 Feb 2008 13:07:52 -0000 +@@ -38,7 +38,7 @@ extern "C" { + #if __STDINT_EXP(LONG_MAX) > 0x7fffffff + #define __have_long64 1 + #elif __STDINT_EXP(LONG_MAX) == 0x7fffffff && !defined(__SPU__) +-#define __have_long32 1 ++/* #define __have_long32 1 */ + #endif + + #if __STDINT_EXP(SCHAR_MAX) == 0x7f + +Define the basic ia64 jump buffer + +Index: newlib/libc/include/machine/setjmp.h +=================================================================== +RCS file: /cvs/src/src/newlib/libc/include/machine/setjmp.h,v +retrieving revision 1.34 +diff -u -p -r1.34 setjmp.h +--- newlib/libc/include/machine/setjmp.h 7 Nov 2007 21:42:24 -0000 1.34 ++++ newlib/libc/include/machine/setjmp.h 11 Jan 2008 18:10:43 -0000 +@@ -72,6 +72,11 @@ _BEGIN_STD_C + #define _JBLEN 8 + #endif + ++#ifdef __ia64__ ++#define _JBTYPE long ++#define _JBLEN 70 ++#endif ++ + #ifdef __i960__ + #define _JBLEN 35 + #endif + +In mini-os we use a dynamic reentrency buffer. + +Index: newlib/libc/include/sys/config.h +=================================================================== +RCS file: /cvs/src/src/newlib/libc/include/sys/config.h,v +retrieving revision 1.47 +diff -u -p -r1.47 config.h +--- newlib/libc/include/sys/config.h 15 Mar 2007 21:32:12 -0000 1.47 ++++ newlib/libc/include/sys/config.h 8 Nov 2007 16:32:44 -0000 +@@ -71,6 +71,10 @@ + #endif + #endif + ++#ifndef __DYNAMIC_REENT__ ++#define __DYNAMIC_REENT__ ++#endif ++ + #ifdef __mn10200__ + #define __SMALL_BITFIELDS + #endif + +Dynamic pointer to our reentrancy zone + +Index: newlib/libc/reent/getreent.c +=================================================================== +RCS file: /cvs/src/src/newlib/libc/reent/getreent.c,v +retrieving revision 1.2 +diff -u -p -r1.2 getreent.c +--- newlib/libc/reent/getreent.c 7 Sep 2007 00:45:55 -0000 1.2 ++++ newlib/libc/reent/getreent.c 8 Nov 2007 16:32:44 -0000 +@@ -3,12 +3,20 @@ + #include <_ansi.h> + #include + ++#define weak_alias(name, aliasname) \ ++ extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); ++ + #ifdef __getreent + #undef __getreent + #endif ++#ifdef __libc_getreent ++#undef __libc_getreent ++#endif + + struct _reent * +-_DEFUN_VOID(__getreent) ++__libc_getreent (void) + { + return _impure_ptr; + } ++weak_alias(__libc_getreent,__getreent) ++ + +We can't provide a red zone in mini-os. + +Index: newlib/libc/machine/x86_64/memcpy.S +=================================================================== +RCS file: /cvs/src/src/newlib/libc/machine/x86_64/memcpy.S,v +retrieving revision 1.1 +diff -u -p -r1.1 memcpy.S +--- newlib/libc/machine/x86_64/memcpy.S 28 Aug 2007 21:56:49 -0000 1.1 ++++ newlib/libc/machine/x86_64/memcpy.S 8 Nov 2007 16:32:44 -0000 +@@ -30,10 +30,18 @@ quadword_aligned: + cmpq $256, rdx + jb quadword_copy + ++#if 1 ++ subq $32, rsp ++ movq rax, 24 (rsp) ++ movq r12, 16 (rsp) ++ movq r13, 8 (rsp) ++ movq r14, 0 (rsp) ++#else + movq rax, -8 (rsp) + movq r12, -16 (rsp) + movq r13, -24 (rsp) + movq r14, -32 (rsp) ++#endif + + movq rdx, rcx /* Copy 128 bytes at a time with minimum cache polution */ + shrq $7, rcx +@@ -89,10 +97,18 @@ loop: + movq rdx, rcx + andq $127, rcx + rep movsb ++#if 1 ++ movq 24 (rsp), rax ++ movq 16 (rsp), r12 ++ movq 8 (rsp), r13 ++ movq 0 (rsp), r14 ++ addq $32, rsp ++#else + movq -8 (rsp), rax + movq -16 (rsp), r12 + movq -24 (rsp), r13 + movq -32 (rsp), r14 ++#endif + ret + + +--- newlib/libc/machine/x86_64/x86_64mach.h.orig 2008-07-11 14:57:23.062269000 +0100 ++++ newlib/libc/machine/x86_64/x86_64mach.h 2008-07-11 14:58:01.262503000 +0100 +@@ -22,81 +22,81 @@ + + #define REG(x) CONCAT1(__REG_PREFIX__, x) + +-#define rax REG(rax) +-#define rbx REG(rbx) +-#define rcx REG(rcx) +-#define rdx REG(rdx) +-#define rsi REG(rsi) +-#define rdi REG(rdi) +-#define rbp REG(rbp) +-#define rsp REG(rsp) +- +-#define r8 REG(r8) +-#define r9 REG(r9) +-#define r10 REG(r10) +-#define r11 REG(r11) +-#define r12 REG(r12) +-#define r13 REG(r13) +-#define r14 REG(r14) +-#define r15 REG(r15) +- +-#define eax REG(eax) +-#define ebx REG(ebx) +-#define ecx REG(ecx) +-#define edx REG(edx) +-#define esi REG(esi) +-#define edi REG(edi) +-#define ebp REG(ebp) +-#define esp REG(esp) +- +-#define st0 REG(st) +-#define st1 REG(st(1)) +-#define st2 REG(st(2)) +-#define st3 REG(st(3)) +-#define st4 REG(st(4)) +-#define st5 REG(st(5)) +-#define st6 REG(st(6)) +-#define st7 REG(st(7)) +- +-#define ax REG(ax) +-#define bx REG(bx) +-#define cx REG(cx) +-#define dx REG(dx) +- +-#define ah REG(ah) +-#define bh REG(bh) +-#define ch REG(ch) +-#define dh REG(dh) +- +-#define al REG(al) +-#define bl REG(bl) +-#define cl REG(cl) +-#define dl REG(dl) +- +-#define sil REG(sil) +- +-#define mm1 REG(mm1) +-#define mm2 REG(mm2) +-#define mm3 REG(mm3) +-#define mm4 REG(mm4) +-#define mm5 REG(mm5) +-#define mm6 REG(mm6) +-#define mm7 REG(mm7) +- +-#define xmm0 REG(xmm0) +-#define xmm1 REG(xmm1) +-#define xmm2 REG(xmm2) +-#define xmm3 REG(xmm3) +-#define xmm4 REG(xmm4) +-#define xmm5 REG(xmm5) +-#define xmm6 REG(xmm6) +-#define xmm7 REG(xmm7) +- +-#define cr0 REG(cr0) +-#define cr1 REG(cr1) +-#define cr2 REG(cr2) +-#define cr3 REG(cr3) +-#define cr4 REG(cr4) ++#define rax %rax ++#define rbx %rbx ++#define rcx %rcx ++#define rdx %rdx ++#define rsi %rsi ++#define rdi %rdi ++#define rbp %rbp ++#define rsp %rsp ++ ++#define r8 %r8 ++#define r9 %r9 ++#define r10 %r10 ++#define r11 %r11 ++#define r12 %r12 ++#define r13 %r13 ++#define r14 %r14 ++#define r15 %r15 ++ ++#define eax %eax ++#define ebx %ebx ++#define ecx %ecx ++#define edx %edx ++#define esi %esi ++#define edi %edi ++#define ebp %ebp ++#define esp %esp ++ ++#define st0 %st ++#define st1 %st(1) ++#define st2 %st(2) ++#define st3 %st(3) ++#define st4 %st(4) ++#define st5 %st(5) ++#define st6 %st(6) ++#define st7 %st(7) ++ ++#define ax %ax ++#define bx %bx ++#define cx %cx ++#define dx %dx ++ ++#define ah %ah ++#define bh %bh ++#define ch %ch ++#define dh %dh ++ ++#define al %al ++#define bl %bl ++#define cl %cl ++#define dl %dl ++ ++#define sil %sil ++ ++#define mm1 %mm1 ++#define mm2 %mm2 ++#define mm3 %mm3 ++#define mm4 %mm4 ++#define mm5 %mm5 ++#define mm6 %mm6 ++#define mm7 %mm7 ++ ++#define xmm0 %xmm0 ++#define xmm1 %xmm1 ++#define xmm2 %xmm2 ++#define xmm3 %xmm3 ++#define xmm4 %xmm4 ++#define xmm5 %xmm5 ++#define xmm6 %xmm6 ++#define xmm7 %xmm7 ++ ++#define cr0 %cr0 ++#define cr1 %cr1 ++#define cr2 %cr2 ++#define cr3 %cr3 ++#define cr4 %cr4 + + #ifdef _I386MACH_NEED_SOTYPE_FUNCTION + #define SOTYPE_FUNCTION(sym) .type SYM(sym),@function +--- newlib/libc/machine/x86_64/memcpy.S.orig 2008-07-11 15:12:27.494693000 +0100 ++++ newlib/libc/machine/x86_64/memcpy.S 2008-07-11 15:12:29.448706000 +0100 +@@ -60,14 +60,14 @@ + movq 48 (rsi), r13 + movq 56 (rsi), r14 + +- movntiq rax, (rdi) +- movntiq r8 , 8 (rdi) +- movntiq r9 , 16 (rdi) +- movntiq r10, 24 (rdi) +- movntiq r11, 32 (rdi) +- movntiq r12, 40 (rdi) +- movntiq r13, 48 (rdi) +- movntiq r14, 56 (rdi) ++ movnti rax, (rdi) ++ movnti r8 , 8 (rdi) ++ movnti r9 , 16 (rdi) ++ movnti r10, 24 (rdi) ++ movnti r11, 32 (rdi) ++ movnti r12, 40 (rdi) ++ movnti r13, 48 (rdi) ++ movnti r14, 56 (rdi) + + movq 64 (rsi), rax + movq 72 (rsi), r8 +@@ -78,14 +78,14 @@ + movq 112 (rsi), r13 + movq 120 (rsi), r14 + +- movntiq rax, 64 (rdi) +- movntiq r8 , 72 (rdi) +- movntiq r9 , 80 (rdi) +- movntiq r10, 88 (rdi) +- movntiq r11, 96 (rdi) +- movntiq r12, 104 (rdi) +- movntiq r13, 112 (rdi) +- movntiq r14, 120 (rdi) ++ movnti rax, 64 (rdi) ++ movnti r8 , 72 (rdi) ++ movnti r9 , 80 (rdi) ++ movnti r10, 88 (rdi) ++ movnti r11, 96 (rdi) ++ movnti r12, 104 (rdi) ++ movnti r13, 112 (rdi) ++ movnti r14, 120 (rdi) + + leaq 128 (rsi), rsi + leaq 128 (rdi), rdi +--- newlib/libc/machine/i386/i386mach.h 2000-08-28 18:50:06.000000000 +0100 ++++ newlib/libc/machine/i386/i386mach.h 2008-07-11 15:17:13.874409000 +0100 +@@ -27,46 +27,46 @@ + + #define REG(x) CONCAT1(__REG_PREFIX__, x) + +-#define eax REG(eax) +-#define ebx REG(ebx) +-#define ecx REG(ecx) +-#define edx REG(edx) +-#define esi REG(esi) +-#define edi REG(edi) +-#define ebp REG(ebp) +-#define esp REG(esp) +- +-#define st0 REG(st) +-#define st1 REG(st(1)) +-#define st2 REG(st(2)) +-#define st3 REG(st(3)) +-#define st4 REG(st(4)) +-#define st5 REG(st(5)) +-#define st6 REG(st(6)) +-#define st7 REG(st(7)) +- +-#define ax REG(ax) +-#define bx REG(bx) +-#define cx REG(cx) +-#define dx REG(dx) +- +-#define ah REG(ah) +-#define bh REG(bh) +-#define ch REG(ch) +-#define dh REG(dh) +- +-#define al REG(al) +-#define bl REG(bl) +-#define cl REG(cl) +-#define dl REG(dl) +- +-#define mm1 REG(mm1) +-#define mm2 REG(mm2) +-#define mm3 REG(mm3) +-#define mm4 REG(mm4) +-#define mm5 REG(mm5) +-#define mm6 REG(mm6) +-#define mm7 REG(mm7) ++#define eax %eax ++#define ebx %ebx ++#define ecx %ecx ++#define edx %edx ++#define esi %esi ++#define edi %edi ++#define ebp %ebp ++#define esp %esp ++ ++#define st0 %st ++#define st1 %st(1) ++#define st2 %st(2) ++#define st3 %st(3) ++#define st4 %st(4) ++#define st5 %st(5) ++#define st6 %st(6) ++#define st7 %st(7) ++ ++#define ax %ax ++#define bx %bx ++#define cx %cx ++#define dx %dx ++ ++#define ah %ah ++#define bh %bh ++#define ch %ch ++#define dh %dh ++ ++#define al %al ++#define bl %bl ++#define cl %cl ++#define dl %dl ++ ++#define mm1 %mm1 ++#define mm2 %mm2 ++#define mm3 %mm3 ++#define mm4 %mm4 ++#define mm5 %mm5 ++#define mm6 %mm6 ++#define mm7 %mm7 + + #ifdef _I386MACH_NEED_SOTYPE_FUNCTION + #define SOTYPE_FUNCTION(sym) .type SYM(sym),@function +--- newlib/libc/machine/x86_64/memset.S 2007-08-28 22:56:49.000000000 +0100 ++++ newlib/libc/machine/x86_64/memset.S 2008-07-11 15:16:59.098320000 +0100 +@@ -40,22 +40,22 @@ + + .p2align 4 + loop: +- movntiq rax, (rdi) +- movntiq rax, 8 (rdi) +- movntiq rax, 16 (rdi) +- movntiq rax, 24 (rdi) +- movntiq rax, 32 (rdi) +- movntiq rax, 40 (rdi) +- movntiq rax, 48 (rdi) +- movntiq rax, 56 (rdi) +- movntiq rax, 64 (rdi) +- movntiq rax, 72 (rdi) +- movntiq rax, 80 (rdi) +- movntiq rax, 88 (rdi) +- movntiq rax, 96 (rdi) +- movntiq rax, 104 (rdi) +- movntiq rax, 112 (rdi) +- movntiq rax, 120 (rdi) ++ movnti rax, (rdi) ++ movnti rax, 8 (rdi) ++ movnti rax, 16 (rdi) ++ movnti rax, 24 (rdi) ++ movnti rax, 32 (rdi) ++ movnti rax, 40 (rdi) ++ movnti rax, 48 (rdi) ++ movnti rax, 56 (rdi) ++ movnti rax, 64 (rdi) ++ movnti rax, 72 (rdi) ++ movnti rax, 80 (rdi) ++ movnti rax, 88 (rdi) ++ movnti rax, 96 (rdi) ++ movnti rax, 104 (rdi) ++ movnti rax, 112 (rdi) ++ movnti rax, 120 (rdi) + + leaq 128 (rdi), rdi + +--- newlib/libm/machine/i386/i386mach.h.orig 2008-07-11 15:30:37.367227000 +0100 ++++ newlib/libm/machine/i386/i386mach.h 2008-07-11 15:30:55.232337000 +0100 +@@ -27,46 +27,46 @@ + + #define REG(x) CONCAT1(__REG_PREFIX__, x) + +-#define eax REG(eax) +-#define ebx REG(ebx) +-#define ecx REG(ecx) +-#define edx REG(edx) +-#define esi REG(esi) +-#define edi REG(edi) +-#define ebp REG(ebp) +-#define esp REG(esp) +- +-#define st0 REG(st) +-#define st1 REG(st(1)) +-#define st2 REG(st(2)) +-#define st3 REG(st(3)) +-#define st4 REG(st(4)) +-#define st5 REG(st(5)) +-#define st6 REG(st(6)) +-#define st7 REG(st(7)) +- +-#define ax REG(ax) +-#define bx REG(bx) +-#define cx REG(cx) +-#define dx REG(dx) +- +-#define ah REG(ah) +-#define bh REG(bh) +-#define ch REG(ch) +-#define dh REG(dh) +- +-#define al REG(al) +-#define bl REG(bl) +-#define cl REG(cl) +-#define dl REG(dl) +- +-#define mm1 REG(mm1) +-#define mm2 REG(mm2) +-#define mm3 REG(mm3) +-#define mm4 REG(mm4) +-#define mm5 REG(mm5) +-#define mm6 REG(mm6) +-#define mm7 REG(mm7) ++#define eax %eax ++#define ebx %ebx ++#define ecx %ecx ++#define edx %edx ++#define esi %esi ++#define edi %edi ++#define ebp %ebp ++#define esp %esp ++ ++#define st0 %st ++#define st1 %st(1) ++#define st2 %st(2) ++#define st3 %st(3) ++#define st4 %st(4) ++#define st5 %st(5) ++#define st6 %st(6) ++#define st7 %st(7) ++ ++#define ax %ax ++#define bx %bx ++#define cx %cx ++#define dx %dx ++ ++#define ah %ah ++#define bh %bh ++#define ch %ch ++#define dh %dh ++ ++#define al %al ++#define bl %bl ++#define cl %cl ++#define dl %dl ++ ++#define mm1 %mm1 ++#define mm2 %mm2 ++#define mm3 %mm3 ++#define mm4 %mm4 ++#define mm5 %mm5 ++#define mm6 %mm6 ++#define mm7 %mm7 + + #ifdef _I386MACH_NEED_SOTYPE_FUNCTION + #define SOTYPE_FUNCTION(sym) .type SYM(sym),@function + + +We want to have a 64bit offsets libc even on 32bit platforms. + +--- ./newlib/configure.host.orig 2008-08-07 16:01:17.801946000 +0100 ++++ ./newlib/configure.host 2008-08-07 16:01:34.181064000 +0100 +@@ -317,6 +317,8 @@ + oext=lo + lpfx= + aext=la ;; ++ i[34567]86-xen-elf) ++ stdio64_dir=stdio64 ;; + *) ;; #shared library not supported for ${host} + esac + +--- newlib/libc/include/sys/_types.h.orig 2008-08-07 15:22:44.925008000 +0100 ++++ newlib/libc/include/sys/_types.h 2008-08-07 15:22:50.824044000 +0100 +@@ -13,8 +13,12 @@ + #include + + #ifndef __off_t_defined ++#ifdef __MINIOS__ ++typedef long long _off_t; ++#else + typedef long _off_t; + #endif ++#endif + + #if defined(__rtems__) + /* device numbers are 32-bit major and and 32-bit minor */ +--- ./newlib/libc/include/sys/config.h.orig 2008-08-07 14:43:25.915866000 +0100 ++++ ./newlib/libc/include/sys/config.h 2008-08-07 14:44:13.508154000 +0100 +@@ -69,6 +69,10 @@ + /* we use some glibc header files so turn on glibc large file feature */ + #define _LARGEFILE64_SOURCE 1 + #endif ++#ifdef __MINIOS__ ++#define __LARGE64_FILES 1 ++#define _LARGEFILE64_SOURCE 1 ++#endif + #endif + + #ifndef __DYNAMIC_REENT__ +--- ./newlib/libc/include/sys/_default_fcntl.h.orig 2008-08-07 15:08:22.377836000 +0100 ++++ ./newlib/libc/include/sys/_default_fcntl.h 2008-08-07 15:08:31.651890000 +0100 +@@ -170,7 +170,11 @@ + /* Provide _ prototypes for functions provided by some versions + of newlib. */ + #ifdef _COMPILING_NEWLIB +-extern int _open _PARAMS ((const char *, int, ...)); ++extern int _open _PARAMS ((const char *, int, ...)) ++#ifdef __MINIOS__ ++ asm("open64") ++#endif ++ ; + extern int _fcntl _PARAMS ((int, int, ...)); + #ifdef __LARGE64_FILES + extern int _open64 _PARAMS ((const char *, int, ...)); +--- ./newlib/libc/include/sys/unistd.h.orig 2008-08-07 15:09:36.449280000 +0100 ++++ ./newlib/libc/include/sys/unistd.h 2008-08-07 15:09:51.210370000 +0100 +@@ -101,7 +101,11 @@ + int _EXFUN(link, (const char *__path1, const char *__path2 )); + int _EXFUN(nice, (int __nice_value )); + #if !defined(__INSIDE_CYGWIN__) +-off_t _EXFUN(lseek, (int __fildes, off_t __offset, int __whence )); ++off_t _EXFUN(lseek, (int __fildes, off_t __offset, int __whence )) ++#ifdef __MINIOS__ ++ asm("lseek64") ++#endif ++ ; + #endif + #if defined(__SPU__) + #define F_ULOCK 0 +--- ./newlib/libc/include/sys/stat.h.orig 2008-08-07 16:08:50.495116000 +0100 ++++ ./newlib/libc/include/sys/stat.h 2008-08-07 16:10:21.799753000 +0100 +@@ -49,6 +49,9 @@ + long st_spare4[2]; + #endif + }; ++#ifdef __MINIOS__ ++#define stat64 stat ++#endif + #endif + + #define _IFMT 0170000 /* type of file */ +@@ -132,7 +135,11 @@ + /* Provide prototypes for most of the _ names that are + provided in newlib for some compilers. */ + #ifdef _COMPILING_NEWLIB +-int _EXFUN(_fstat,( int __fd, struct stat *__sbuf )); ++int _EXFUN(_fstat,( int __fd, struct stat *__sbuf )) ++#ifdef __MINIOS__ ++ asm("fstat64") ++#endif ++ ; + int _EXFUN(_stat,( const char *__path, struct stat *__sbuf )); + #ifdef __LARGE64_FILES + struct stat64; +--- ./newlib/libc/include/_syslist.h.orig 2008-08-07 16:24:19.122605000 +0100 ++++ ./newlib/libc/include/_syslist.h 2008-08-07 16:24:21.548628000 +0100 +@@ -14,6 +14,7 @@ + #define _kill kill + #define _link link + #define _lseek lseek ++#define _lseek64 lseek64 + #define _open open + #define _read read + #define _sbrk sbrk +--- newlib/libc/include/reent.h.orig 2008-08-07 16:28:49.846502000 +0100 ++++ newlib/libc/include/reent.h 2008-08-07 16:29:02.096586000 +0100 +@@ -87,6 +87,9 @@ + #if defined(__CYGWIN__) && defined(_COMPILING_NEWLIB) + #define stat64 __stat64 + #endif ++#if defined(__MINIOS__) ++#define stat64 stat ++#endif + + struct stat64; + diff --git a/stubdom/pciutils.patch b/stubdom/pciutils.patch new file mode 100644 index 0000000..189b7ff --- /dev/null +++ b/stubdom/pciutils.patch @@ -0,0 +1,299 @@ +diff -urN pciutils-2.2.9.orig/lib/access.c pciutils-2.2.9/lib/access.c +--- pciutils-2.2.9.orig/lib/access.c 2007-02-06 11:59:43.000000000 +0000 ++++ pciutils-2.2.9/lib/access.c 2008-06-30 19:07:09.713187000 +0100 +@@ -57,6 +57,11 @@ + #else + NULL, + #endif ++#ifdef PCI_OS_MINIOS ++ &pm_minios, ++#else ++ NULL, ++#endif + }; + + struct pci_access * +--- pciutils-2.2.9.orig/lib/pci.h 2006-09-09 13:46:06.000000000 +0100 ++++ pciutils-2.2.9/lib/pci.h 2008-06-30 18:56:15.350111000 +0100 +@@ -33,6 +33,7 @@ + PCI_ACCESS_NBSD_LIBPCI, /* NetBSD libpci */ + PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */ + PCI_ACCESS_DUMP, /* Dump file (params: filename) */ ++ PCI_ACCESS_MINIOS, /* MiniOS */ + PCI_ACCESS_MAX + }; + +@@ -63,6 +64,7 @@ + int fd_rw; /* proc: fd opened read-write */ + struct pci_dev *cached_dev; /* proc: device the fd is for */ + int fd_pos; /* proc: current position */ ++ void *minios; + }; + + /* Initialize PCI access */ +--- pciutils-2.2.9.orig/lib/internal.h 2006-09-09 11:52:47.000000000 +0100 ++++ pciutils-2.2.9/lib/internal.h 2008-07-01 10:46:24.968202000 +0100 +@@ -37,4 +37,4 @@ + + extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc, + pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device, +- pm_dump, pm_linux_sysfs; ++ pm_dump, pm_linux_sysfs, pm_minios; +--- pciutils-2.2.9.orig/lib/Makefile 2007-10-19 13:41:34.000000000 +0100 ++++ pciutils-2.2.9/lib/Makefile 2008-07-01 12:13:14.400525000 +0100 +@@ -46,6 +46,12 @@ + PCILIB=libpciutils.a + endif + ++ifdef PCI_OS_MINIOS ++XEN_ROOT=../../.. ++include $(XEN_ROOT)/Config.mk ++OBJS += minios.o ++endif ++ + all: $(PCILIB) $(PCILIBPC) + + $(PCILIB): $(OBJS) +--- pciutils-2.2.9.orig/lib/types.h 2007-09-03 09:44:15.000000000 +0100 ++++ pciutils-2.2.9/lib/types.h 2008-07-01 12:17:08.396156000 +0100 +@@ -17,9 +17,13 @@ + typedef DWORD u32; + #elif defined(PCI_HAVE_STDINT_H) + #include ++#ifdef PCI_OS_MINIOS ++#include ++#else + typedef uint8_t u8; + typedef uint16_t u16; + typedef uint32_t u32; ++#endif + #else + typedef u_int8_t u8; + typedef u_int16_t u16; +--- pciutils-2.2.9.orig/lib/minios.c 1970-01-01 01:00:00.000000000 +0100 ++++ pciutils-2.2.9/lib/minios.c 2008-07-01 12:31:40.554260000 +0100 +@@ -0,0 +1,113 @@ ++/* ++ * The PCI Library -- MiniOS PCI frontend access ++ * ++ * Samuel Thibault , 2008 ++ * ++ * Can be freely distributed and used under the terms of the GNU GPL. ++ */ ++ ++#include ++#include ++#include ++#include "internal.h" ++ ++static int ++minios_detect(struct pci_access *a) ++{ ++ return 1; ++} ++ ++static void ++minios_init(struct pci_access *a) ++{ ++ a->minios = init_pcifront(NULL); ++ if (!a->minios) ++ a->warning("minios_init open failed"); ++} ++ ++static void ++minios_cleanup(struct pci_access *a) ++{ ++ if (a->minios) ++ shutdown_pcifront(a->minios); ++} ++ ++static void ++minios_scan(struct pci_access *a) ++{ ++ if (!a->minios) ++ return; ++ ++ void func(unsigned int domain, unsigned int bus, unsigned int slot, unsigned int fun) ++ { ++ struct pci_dev *d = pci_alloc_dev(a); ++ ++ d->domain = domain; ++ d->bus = bus; ++ d->dev = slot; ++ d->func = fun; ++ ++ pci_link_dev(a, d); ++ } ++ ++ pcifront_scan(a->minios, func); ++} ++ ++static int ++minios_read(struct pci_dev *d, int pos, byte *buf, int len) ++{ ++ unsigned int val; ++ switch (len) { ++ case 1: ++ if (pcifront_conf_read(d->access->minios, d->domain, d->bus, d->dev, d->func, pos, len, &val)) ++ return 0; ++ * buf = val; ++ return 1; ++ case 2: ++ if (pcifront_conf_read(d->access->minios, d->domain, d->bus, d->dev, d->func, pos, len, &val)) ++ return 0; ++ *(u16 *) buf = cpu_to_le16((u16) val); ++ return 1; ++ case 4: ++ if (pcifront_conf_read(d->access->minios, d->domain, d->bus, d->dev, d->func, pos, len, &val)) ++ return 0; ++ *(u32 *) buf = cpu_to_le32((u32) val); ++ return 1; ++ default: ++ return pci_generic_block_read(d, pos, buf, len); ++ } ++} ++ ++static int ++minios_write(struct pci_dev *d, int pos, byte *buf, int len) ++{ ++ unsigned int val; ++ switch (len) { ++ case 1: ++ val = * buf; ++ break; ++ case 2: ++ val = le16_to_cpu(*(u16 *) buf); ++ break; ++ case 4: ++ val = le32_to_cpu(*(u32 *) buf); ++ break; ++ default: ++ return pci_generic_block_write(d, pos, buf, len); ++ } ++ return !pcifront_conf_write(d->access->minios, d->domain, d->bus, d->dev, d->func, pos, len, val); ++} ++ ++struct pci_methods pm_minios = { ++ "MiniOS-device", ++ NULL, /* config */ ++ minios_detect, ++ minios_init, ++ minios_cleanup, ++ minios_scan, ++ pci_generic_fill_info, ++ minios_read, ++ minios_write, ++ NULL, /* dev_init */ ++ NULL /* dev_cleanup */ ++}; +--- pciutils-2.2.9/lib/generic.c 2007-02-06 12:00:05.000000000 +0000 ++++ pciutils-2.2.9-mine/lib/generic.c 2008-07-01 19:13:52.289949000 +0100 +@@ -74,6 +74,19 @@ + pci_generic_scan_bus(a, busmap, 0); + } + ++static u32 pci_size(u32 base, u32 maxbase, u32 mask) ++{ ++ u32 size = mask & maxbase; ++ if (!size) ++ return 0; ++ size = (size & ~(size-1)) - 1; ++ ++ if (base == maxbase && ((base | size) & mask) != mask) ++ return 0; ++ ++ return size + 1; ++} ++ + int + pci_generic_fill_info(struct pci_dev *d, int flags) + { +@@ -114,23 +127,61 @@ + if (!x || x == (u32) ~0) + continue; + if ((x & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) +- d->base_addr[i] = x; +- else ++ { ++ d->base_addr[i] = x & PCI_BASE_ADDRESS_IO_MASK; ++ if (flags & PCI_FILL_SIZES) ++ { ++ u32 size; ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + i*4, ~0); ++ d->size[i] = pci_size(x, pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4), PCI_BASE_ADDRESS_IO_MASK); ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + i*4, x); ++ } ++ } ++ else + { + if ((x & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64) +- d->base_addr[i] = x; ++ { ++ d->base_addr[i] = x & PCI_BASE_ADDRESS_MEM_MASK; ++ if (flags & PCI_FILL_SIZES) ++ { ++ u32 size; ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + i*4, ~0); ++ d->size[i] = pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4); ++ d->size[i] = pci_size(x, pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4), PCI_BASE_ADDRESS_MEM_MASK); ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + i*4, x); ++ } ++ } + else if (i >= cnt-1) + a->warning("%04x:%02x:%02x.%d: Invalid 64-bit address seen for BAR %d.", d->domain, d->bus, d->dev, d->func, i); + else + { + u32 y = pci_read_long(d, PCI_BASE_ADDRESS_0 + (++i)*4); + #ifdef PCI_HAVE_64BIT_ADDRESS +- d->base_addr[i-1] = x | (((pciaddr_t) y) << 32); ++ d->base_addr[i-1] = (x | (((pciaddr_t) y) << 32)) & PCI_BASE_ADDRESS_MEM_MASK; ++ if (flags & PCI_FILL_SIZES) ++ { ++ u32 size; ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + (i-1)*4, ~0); ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + i*4, ~0); ++ d->size[i-1] = pci_size(y, pci_read_long(d, PCI_BASE_ADDRESS_0 + (i-1)*4) | ++ pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4), 0xffffffff ); ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + (i-1)*4, x); ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + i*4, y); ++ } + #else + if (y) + a->warning("%04x:%02x:%02x.%d 64-bit device address ignored.", d->domain, d->bus, d->dev, d->func); + else +- d->base_addr[i-1] = x; ++ { ++ d->base_addr[i-1] = x & PCI_BASE_ADDRESS_MEM_MASK; ++ if (flags & PCI_FILL_SIZES) ++ { ++ u32 size; ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + (i-1)*4, ~0); ++ d->size[i-1] = pci_size(x, pci_read_long(d, PCI_BASE_ADDRESS_0 + (i-1)*4), PCI_BASE_ADDRESS_MEM_MASK); ++ pci_write_long(d, PCI_BASE_ADDRESS_0 + (i-1)*4, x); ++ } ++ } + #endif + } + } +@@ -154,10 +205,19 @@ + { + u32 u = pci_read_long(d, reg); + if (u != 0xffffffff) +- d->rom_base_addr = u; ++ { ++ d->rom_base_addr = u; ++ if (flags & PCI_FILL_SIZES) ++ { ++ u32 size; ++ pci_write_long(d, reg, ~0); ++ d->rom_size = pci_read_long(d, reg); ++ pci_write_long(d, reg, u); ++ } ++ } + } + } +- return flags & ~PCI_FILL_SIZES; ++ return flags; + } + + static int diff --git a/stubdom/stubdom-dm b/stubdom/stubdom-dm new file mode 100644 index 0000000..a800bc4 --- /dev/null +++ b/stubdom/stubdom-dm @@ -0,0 +1,101 @@ +#!/bin/bash +# +# Copyright 2007-2008 Samuel Thibault +# +# dm script around stubdomains. +# + +# To fit xterms nicely +height=339 + +# Parse arguments + +domid= +domname= +vncviewer=0 +vncpid= +extra= +while [ "$#" -gt 0 ]; +do + if [ "$#" -ge 2 ]; + then + case "$1" in + -d) + domid=$2; + extra="$extra -d $domid"; + shift + ;; + -domain-name) + domname=$2; + shift + ;; + -vnc) + ip=${2%:*}; + vnc_port=${2#*:}; + shift + ;; + -loadvm) + extra="$extra -loadvm $2"; + shift + ;; + esac + fi + case "$1" in + -vncviewer) vncviewer=1 ;; + esac + shift +done + +[ -z "$domid" ] && ( echo "couldn't find domain ID" ; exit 1 ) +[ -z "$domname" ] && ( echo "couldn't find domain name" ; exit 1 ) + +# Termination handler + +term() { + kill %1 + ( + [ -n "$vncpid" ] && kill -9 $vncpid + xm destroy $domname-dm + #xm destroy $domname + ) & + # We need to exit immediately so as to let xend do the commands above + exit 0 +} + +trap term SIGHUP + +############ +# stubdomain +# Wait for any previous stubdom to terminate +while xm list | grep $domname-dm +do + sleep 1 +done + +creation="xm create -c $domname-dm target=$domid memory=32 extra=\"$extra\"" + +(while true ; do sleep 60 ; done) | /bin/sh -c "$creation" & +#xterm -geometry +0+0 -e /bin/sh -c "$creation ; echo ; echo press ENTER to shut down ; read" & +consolepid=$! + + +########### +# vncviewer +if [ "$vncviewer" = 1 ] +then + # Wait for vnc server to appear + while ! vnc_port=`xenstore-read /local/domain/$domid/console/vnc-port` + do + # Check that the stubdom job is still alive + kill -0 $consolepid || term + sleep 1 + done + + vncviewer $ip:$vnc_port & + vncpid=$! +fi + +# wait for SIGHUP or stubdom termination +wait $consolepid + +term diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..4d27b26 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,101 @@ +XEN_ROOT = ../ +include $(XEN_ROOT)/tools/Rules.mk + +SUBDIRS-y := +SUBDIRS-y += check +SUBDIRS-y += include +SUBDIRS-y += libxc +SUBDIRS-y += flask +SUBDIRS-y += xenstore +SUBDIRS-y += misc +SUBDIRS-y += examples +SUBDIRS-y += xentrace +SUBDIRS-$(CONFIG_XCUTILS) += xcutils +SUBDIRS-$(CONFIG_X86) += firmware +SUBDIRS-$(ACM_SECURITY) += security +SUBDIRS-y += console +SUBDIRS-y += xenmon +SUBDIRS-$(VTPM_TOOLS) += vtpm_manager +SUBDIRS-$(VTPM_TOOLS) += vtpm +SUBDIRS-y += xenstat +SUBDIRS-y += libaio +SUBDIRS-y += blktap +SUBDIRS-y += libfsimage +SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen +SUBDIRS-y += fs-back +SUBDIRS-$(CONFIG_IOEMU) += ioemu-dir + +# These don't cross-compile +ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) +SUBDIRS-$(PYTHON_TOOLS) += python +SUBDIRS-$(PYTHON_TOOLS) += pygrub +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: all +all: subdirs-all + +.PHONY: install +install: subdirs-install + $(INSTALL_DIR) $(DESTDIR)/var/xen/dump + $(INSTALL_DIR) $(DESTDIR)/var/log/xen + $(INSTALL_DIR) $(DESTDIR)/var/lib/xen + +.PHONY: clean distclean +clean distclean: subdirs-clean + +ifneq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) +IOEMU_CONFIGURE_CROSS ?= --cpu=$(XEN_TARGET_ARCH) \ + --cross-prefix=$(CROSS_COMPILE) \ + --interp-prefix=$(CROSS_SYS_ROOT) +endif + +ioemu/config-host.mak: + cd ioemu && XEN_TARGET_ARCH=$(XEN_TARGET_ARCH) sh configure \ + --prefix=$(PREFIX) $(IOEMU_CONFIGURE_CROSS) + +subdir-all-ioemu subdir-install-ioemu: ioemu/config-host.mak + +subdir-clean-ioemu: + $(MAKE) -C ioemu distclean + +ioemu-dir-find: + set -ex; \ + if test -d $(CONFIG_QEMU); then \ + rm -f ioemu-dir; \ + ln -sf $(CONFIG_QEMU) ioemu-dir; \ + else \ + if [ ! -d ioemu-remote ]; then \ + rm -rf ioemu-remote ioemu-remote.tmp; \ + mkdir ioemu-remote.tmp; rmdir ioemu-remote.tmp; \ + $(GIT) clone $(CONFIG_QEMU) ioemu-remote.tmp; \ + if [ "$(QEMU_TAG)" ]; then \ + cd ioemu-remote.tmp; \ + $(GIT) branch -D dummy >/dev/null 2>&1 ||:; \ + $(GIT) checkout -b dummy $(QEMU_TAG); \ + cd ..; \ + fi; \ + mv ioemu-remote.tmp ioemu-remote; \ + fi; \ + rm -f ioemu-dir; \ + ln -sf ioemu-remote ioemu-dir; \ + fi + set -e; \ + $(absolutify_xen_root); \ + cd ioemu-dir; \ + ./xen-setup $(IOEMU_CONFIGURE_CROSS) + +subdir-all-ioemu-dir subdir-install-ioemu-dir: ioemu-dir-find + +subdir-clean-ioemu-dir: + set -e; if test -d ioemu-dir/.; then \ + $(absolutify_xen_root); \ + $(MAKE) -C ioemu-dir clean; \ + fi diff --git a/tools/Rules.mk b/tools/Rules.mk new file mode 100644 index 0000000..a477c82 --- /dev/null +++ b/tools/Rules.mk @@ -0,0 +1,62 @@ +# -*- mode: Makefile; -*- + +# `all' is the default target +all: + +include $(XEN_ROOT)/Config.mk + +export _INSTALL := $(INSTALL) +INSTALL = $(XEN_ROOT)/tools/cross-install + +XEN_INCLUDE = $(XEN_ROOT)/tools/include +XEN_XC = $(XEN_ROOT)/tools/python/xen/lowlevel/xc +XEN_LIBXC = $(XEN_ROOT)/tools/libxc +XEN_XENSTORE = $(XEN_ROOT)/tools/xenstore +XEN_LIBXENSTAT = $(XEN_ROOT)/tools/xenstat/libxenstat/src + +CFLAGS_include = -I$(XEN_INCLUDE) + +CFLAGS_libxenctrl = -I$(XEN_LIBXC) $(CFLAGS_include) +LDFLAGS_libxenctrl = -L$(XEN_LIBXC) -lxenctrl + +CFLAGS_libxenguest = -I$(XEN_LIBXC) $(CFLAGS_include) +LDFLAGS_libxenguest = -L$(XEN_LIBXC) -lxenguest + +CFLAGS_libxenstore = -I$(XEN_XENSTORE) $(CFLAGS_include) +LDFLAGS_libxenstore = -L$(XEN_XENSTORE) -lxenstore + +X11_LDPATH = -L/usr/X11R6/$(LIBLEAFDIR) + +CFLAGS += -D__XEN_TOOLS__ + +# Enable implicit LFS support *and* explicit LFS names. +CFLAGS += $(shell getconf LFS_CFLAGS) +CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE +LDFLAGS += $(shell getconf LFS_LDFLAGS) + +# 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) + +# Require GCC v3.4+ (to avoid issues with alignment constraints in Xen headers) +check-$(CONFIG_X86) = $(call cc-ver-check,CC,0x030400,\ + "Xen requires at least gcc-3.4") +$(eval $(check-y)) + +%.opic: %.c + $(CC) $(CPPFLAGS) -DPIC $(CFLAGS) -fPIC -c -o $@ $< + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +%.o: %.cc + $(CC) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +subdirs-all subdirs-clean subdirs-install: .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-%-$*,%,$@) + diff --git a/tools/blktap/Makefile b/tools/blktap/Makefile new file mode 100644 index 0000000..21c9556 --- /dev/null +++ b/tools/blktap/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +SUBDIRS-y := +SUBDIRS-y += lib +SUBDIRS-y += drivers + +.PHONY: all clean install +all clean install: %: subdirs-% + +install: + $(INSTALL_DIR) $(DESTDIR)$(DOCDIR) + $(INSTALL_DATA) README $(DESTDIR)$(DOCDIR)/README.blktap diff --git a/tools/blktap/README b/tools/blktap/README new file mode 100644 index 0000000..5e41080 --- /dev/null +++ b/tools/blktap/README @@ -0,0 +1,122 @@ +Blktap Userspace Tools + Library +================================ + +Andrew Warfield and Julian Chesterfield +16th June 2006 + +{firstname.lastname}@cl.cam.ac.uk + +The blktap userspace toolkit provides a user-level disk I/O +interface. The blktap mechanism involves a kernel driver that acts +similarly to the existing Xen/Linux blkback driver, and a set of +associated user-level libraries. Using these tools, blktap allows +virtual block devices presented to VMs to be implemented in userspace +and to be backed by raw partitions, files, network, etc. + +The key benefit of blktap is that it makes it easy and fast to write +arbitrary block backends, and that these user-level backends actually +perform very well. Specifically: + +- Metadata disk formats such as Copy-on-Write, encrypted disks, sparse + formats and other compression features can be easily implemented. + +- Accessing file-based images from userspace avoids problems related + to flushing dirty pages which are present in the Linux loopback + driver. (Specifically, doing a large number of writes to an + NFS-backed image don't result in the OOM killer going berserk.) + +- Per-disk handler processes enable easier userspace policing of block + resources, and process-granularity QoS techniques (disk scheduling + and related tools) may be trivially applied to block devices. + +- It's very easy to take advantage of userspace facilities such as + networking libraries, compression utilities, peer-to-peer + file-sharing systems and so on to build more complex block backends. + +- Crashes are contained -- incremental development/debugging is very + fast. + +How it works (in one paragraph): + +Working in conjunction with the kernel blktap driver, all disk I/O +requests from VMs are passed to the userspace deamon (using a shared +memory interface) through a character device. Each active disk is +mapped to an individual device node, allowing per-disk processes to +implement individual block devices where desired. The userspace +drivers are implemented using asynchronous (Linux libaio), +O_DIRECT-based calls to preserve the unbuffered, batched and +asynchronous request dispatch achieved with the existing blkback +code. We provide a simple, asynchronous virtual disk interface that +makes it quite easy to add new disk implementations. + +As of June 2006 the current supported disk formats are: + + - Raw Images (both on partitions and in image files) + - File-backed Qcow disks + - Standalone sparse Qcow disks + - Fast shareable RAM disk between VMs (requires some form of cluster-based + filesystem support e.g. OCFS2 in the guest kernel) + - Some VMDK images - your mileage may vary + +Raw and QCow images have asynchronous backends and so should perform +fairly well. VMDK is based directly on the qemu vmdk driver, which is +synchronous (a.k.a. slow). + +Build and Installation Instructions +=================================== + +Make to configure the blktap backend driver in your dom0 kernel. It +will cooperate fine with the existing backend driver, so you can +experiment with tap disks without breaking existing VM configs. + +To build the tools separately, "make && make install" in +tools/blktap. + + +Using the Tools +=============== + +Prepare the image for booting. For qcow files use the qcow utilities +installed earlier. e.g. qcow-create generates a blank standalone image +or a file-backed CoW image. img2qcow takes an existing image or +partition and creates a sparse, standalone qcow-based file. + +The userspace disk agent is configured to start automatically via xend +(alternatively you can start it manually => 'blktapctrl') + +Customise the VM config file to use the 'tap' handler, followed by the +driver type. e.g. for a raw image such as a file or partition: + +disk = ['tap:aio:,sda1,w'] + +e.g. for a qcow image: + +disk = ['tap:qcow:,sda1,w'] + + +Mounting images in Dom0 using the blktap driver +=============================================== +Tap (and blkback) disks are also mountable in Dom0 without requiring an +active VM to attach. You will need to build a xenlinux Dom0 kernel that +includes the blkfront driver (e.g. the default 'make world' or +'make kernels' build. Simply use the xm command-line tool to activate +the backend disks, and blkfront will generate a virtual block device that +can be accessed in the same way as a loop device or partition: + +e.g. for a raw image file that would normally be mounted using +the loopback driver (such as 'mount -o loop /mnt/disk'), do the +following: + +xm block-attach 0 tap:aio: /dev/xvda1 w 0 +mount /dev/xvda1 /mnt/disk <--- don't use loop driver + +In this way, you can use any of the userspace device-type drivers built +with the blktap userspace toolkit to open and mount disks such as qcow +or vmdk images: + +xm block-attach 0 tap:qcow: /dev/xvda1 w 0 +mount /dev/xvda1 /mnt/disk + + + + diff --git a/tools/blktap/drivers/Makefile b/tools/blktap/drivers/Makefile new file mode 100644 index 0000000..7c3e088 --- /dev/null +++ b/tools/blktap/drivers/Makefile @@ -0,0 +1,66 @@ +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +IBIN = blktapctrl tapdisk +QCOW_UTIL = img2qcow qcow2raw qcow-create +LIBAIO_DIR = ../../libaio/src + +CFLAGS += -Werror +CFLAGS += -Wno-unused +CFLAGS += -I../lib +CFLAGS += $(CFLAGS_libxenctrl) +CFLAGS += $(CFLAGS_libxenstore) +CFLAGS += -I $(LIBAIO_DIR) +CFLAGS += -D_GNU_SOURCE + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +ifeq ($(shell . ./check_gcrypt),"yes") +CFLAGS += -DUSE_GCRYPT +CRYPT_LIB := -lgcrypt +else +CRYPT_LIB := -lcrypto +$(warning *** libgcrypt not installed: falling back to libcrypto ***) +endif + +LDFLAGS_blktapctrl := $(LDFLAGS_libxenctrl) $(LDFLAGS_libxenstore) -L../lib -lblktap +LDFLAGS_img := $(LIBAIO_DIR)/libaio.a $(CRYPT_LIB) -lpthread -lz + +BLK-OBJS-y := block-aio.o +BLK-OBJS-y += block-sync.o +BLK-OBJS-y += block-vmdk.o +BLK-OBJS-y += block-ram.o +BLK-OBJS-y += block-qcow.o +BLK-OBJS-y += block-qcow2.o +BLK-OBJS-y += aes.o +BLK-OBJS-y += tapaio.o +BLK-OBJS-$(CONFIG_Linux) += blk_linux.o + +BLKTAB-OBJS-y := blktapctrl.o +BLKTAB-OBJS-$(CONFIG_Linux) += blktapctrl_linux.o + +all: $(IBIN) qcow-util + +blktapctrl: $(BLKTAB-OBJS-y) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDFLAGS_blktapctrl) + +tapdisk: tapdisk.o $(BLK-OBJS-y) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDFLAGS_img) + +.PHONY: qcow-util +qcow-util: img2qcow qcow2raw qcow-create + +img2qcow qcow2raw qcow-create: %: %.o $(BLK-OBJS-y) + $(CC) $(CFLAGS) -o $* $^ $(LDFLAGS) $(LDFLAGS_img) + +install: all + $(INSTALL_PROG) $(IBIN) $(QCOW_UTIL) $(VHD_UTIL) $(DESTDIR)$(SBINDIR) + +clean: + rm -rf *.o *~ $(DEPS) xen TAGS $(IBIN) $(LIB) $(QCOW_UTIL) $(VHD_UTIL) + +.PHONY: clean install + +-include $(DEPS) diff --git a/tools/blktap/drivers/aes.c b/tools/blktap/drivers/aes.c new file mode 100644 index 0000000..4d83fac --- /dev/null +++ b/tools/blktap/drivers/aes.c @@ -0,0 +1,1319 @@ +/** + * + * aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project. + */ +/* + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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 "vl.h" +#include +#include +#include "aes.h" + +//#define NDEBUG +#include + +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +#define MAXKC (256/32) +#define MAXKB (256/8) +#define MAXNR 14 + +/* This controls loop-unrolling in aes_core.c */ +#undef FULL_UNROLL +# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +/** + * Expand the cipher key into the encryption key schedule. + */ +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) { + + u32 *rk; + int i = 0; + u32 temp; + + if (!userKey || !key) + return -1; + if (bits != 128 && bits != 192 && bits != 256) + return -2; + + rk = key->rd_key; + + if (bits==128) + key->rounds = 10; + else if (bits==192) + key->rounds = 12; + else + key->rounds = 14; + + rk[0] = GETU32(userKey ); + rk[1] = GETU32(userKey + 4); + rk[2] = GETU32(userKey + 8); + rk[3] = GETU32(userKey + 12); + if (bits == 128) { + while (1) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 0; + } + rk += 4; + } + } + rk[4] = GETU32(userKey + 16); + rk[5] = GETU32(userKey + 20); + if (bits == 192) { + while (1) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 0; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(userKey + 24); + rk[7] = GETU32(userKey + 28); + if (bits == 256) { + while (1) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 0; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + */ +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) { + + u32 *rk; + int i, j, status; + u32 temp; + + /* first, start with an encryption schedule */ + status = AES_set_encrypt_key(userKey, bits, key); + if (status < 0) + return status; + + rk = key->rd_key; + + /* invert the order of the round keys: */ + for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < (key->rounds); i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } + return 0; +} + +#ifndef AES_ASM +/* + * Encrypt a single block + * in and out can overlap + */ +void AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key) { + + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (key->rounds > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (key->rounds > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +/* + * Decrypt a single block + * in and out can overlap + */ +void AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key) { + + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (key->rounds > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (key->rounds > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +#endif /* AES_ASM */ + +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc) +{ + + unsigned long n; + unsigned long len = length; + unsigned char tmp[AES_BLOCK_SIZE]; + + assert(in && out && key && ivec); + + if (enc) { + while (len >= AES_BLOCK_SIZE) { + for(n=0; n < AES_BLOCK_SIZE; ++n) + tmp[n] = in[n] ^ ivec[n]; + AES_encrypt(tmp, out, key); + memcpy(ivec, out, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (len) { + for(n=0; n < len; ++n) + tmp[n] = in[n] ^ ivec[n]; + for(n=len; n < AES_BLOCK_SIZE; ++n) + tmp[n] = ivec[n]; + AES_encrypt(tmp, tmp, key); + memcpy(out, tmp, AES_BLOCK_SIZE); + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } else { + while (len >= AES_BLOCK_SIZE) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(in, out, key); + for(n=0; n < AES_BLOCK_SIZE; ++n) + out[n] ^= ivec[n]; + memcpy(ivec, tmp, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (len) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, tmp, key); + for(n=0; n < len; ++n) + out[n] = tmp[n] ^ ivec[n]; + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } +} diff --git a/tools/blktap/drivers/aes.h b/tools/blktap/drivers/aes.h new file mode 100644 index 0000000..9fb54a9 --- /dev/null +++ b/tools/blktap/drivers/aes.h @@ -0,0 +1,28 @@ +#ifndef QEMU_AES_H +#define QEMU_AES_H + +#include + +#define AES_MAXNR 14 +#define AES_BLOCK_SIZE 16 + +struct aes_key_st { + uint32_t rd_key[4 *(AES_MAXNR + 1)]; + int rounds; +}; +typedef struct aes_key_st AES_KEY; + +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); + +void AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); +void AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc); + +#endif diff --git a/tools/blktap/drivers/blk.h b/tools/blktap/drivers/blk.h new file mode 100644 index 0000000..1cdc980 --- /dev/null +++ b/tools/blktap/drivers/blk.h @@ -0,0 +1,3 @@ + +int blk_getimagesize(int fd, uint64_t *size); +int blk_getsectorsize(int fd, uint64_t *sector_size); diff --git a/tools/blktap/drivers/blk_linux.c b/tools/blktap/drivers/blk_linux.c new file mode 100644 index 0000000..f1b14bd --- /dev/null +++ b/tools/blktap/drivers/blk_linux.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include "tapdisk.h" +#include "blk.h" + +int blk_getimagesize(int fd, uint64_t *size) +{ + int rc; + + *size = 0; + rc = ioctl(fd, BLKGETSIZE, size); + if (rc) { + DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image"); + return -EINVAL; + } + + return 0; +} + +int blk_getsectorsize(int fd, uint64_t *sector_size) +{ +#if defined(BLKSSZGET) + int rc; + + *sector_size = DEFAULT_SECTOR_SIZE; + rc = ioctl(fd, BLKSSZGET, sector_size); + if (rc) { + DPRINTF("ERR: BLKSSZGET failed. Falling back to use default sector size"); + *sector_size = DEFAULT_SECTOR_SIZE; + } + + if (*sector_size != DEFAULT_SECTOR_SIZE) + DPRINTF("Note: sector size is %"PRIu64" (not %u)\n", + *sector_size, DEFAULT_SECTOR_SIZE); +#else + *sector_size = DEFAULT_SECTOR_SIZE; +#endif + + return 0; +} + diff --git a/tools/blktap/drivers/blktapctrl.c b/tools/blktap/drivers/blktapctrl.c new file mode 100644 index 0000000..b8a872b --- /dev/null +++ b/tools/blktap/drivers/blktapctrl.c @@ -0,0 +1,877 @@ +/* + * blktapctrl.c + * + * userspace controller for the blktap disks. + * As requests for new block devices arrive, + * the controller spawns off a separate process + * per-disk. + * + * + * Copyright (c) 2005 Julian Chesterfield and Andrew Warfield. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "blktaplib.h" +#include "blktapctrl.h" +#include "tapdisk.h" +#include "list.h" +#include "xs_api.h" /* for xs_fire_next_watch() */ + +#define PIDFILE "/var/run/blktapctrl.pid" + +#define NUM_POLL_FDS 2 +#define MSG_SIZE 4096 +#define MAX_TIMEOUT 10 +#define MAX_RAND_VAL 0xFFFF +#define MAX_ATTEMPTS 10 + +int run = 1; +int max_timeout = MAX_TIMEOUT; +int ctlfd = 0; + +int blktap_major; + +static int open_ctrl_socket(char *devname); +static int write_msg(int fd, int msgtype, void *ptr, void *ptr2); +static int read_msg(int fd, int msgtype, void *ptr); +static driver_list_entry_t *active_disks[MAX_DISK_TYPES]; + + +static unsigned long long tapdisk_get_size(blkif_t *blkif) +{ + image_t *img = (image_t *)blkif->prv; + return img->size; +} + +static unsigned long tapdisk_get_secsize(blkif_t *blkif) +{ + image_t *img = (image_t *)blkif->prv; + return img->secsize; +} + +static unsigned int tapdisk_get_info(blkif_t *blkif) +{ + image_t *img = (image_t *)blkif->prv; + return img->info; +} + +struct blkif_ops tapdisk_ops = { + .get_size = tapdisk_get_size, + .get_secsize = tapdisk_get_secsize, + .get_info = tapdisk_get_info, +}; + + +static void init_driver_list(void) +{ + int i; + + for (i = 0; i < MAX_DISK_TYPES; i++) + active_disks[i] = NULL; + return; +} + +static void init_rng(void) +{ + static uint32_t seed; + struct timeval tv; + + gettimeofday(&tv, NULL); + seed = tv.tv_usec; + srand48(seed); + return; +} + +static int get_tapdisk_pid(blkif_t *blkif) +{ + int ret; + + if ((ret = write_msg(blkif->fds[WRITE], CTLMSG_PID, blkif, NULL)) + <= 0) { + DPRINTF("Write_msg failed - CTLMSG_PID(%d)\n", ret); + return -EINVAL; + } + + if ((ret = read_msg(blkif->fds[READ], CTLMSG_PID_RSP, blkif)) + <= 0) { + DPRINTF("Read_msg failure - CTLMSG_PID(%d)\n", ret); + return -EINVAL; + } + return 1; +} + +/* Look up the disk specified by path: + * if found, dev points to the device string in the path + * type is the tapdisk driver type id + * blkif is the existing interface if this is a shared driver + * and NULL otherwise. + * return 0 on success, -1 on error. + */ + +static int test_path(char *path, char **dev, int *type, blkif_t **blkif) +{ + char *ptr, handle[10]; + int i, size, found = 0; + size_t handle_len; + + size = sizeof(dtypes)/sizeof(disk_info_t *); + *type = MAX_DISK_TYPES + 1; + *blkif = NULL; + + if ( (ptr = strstr(path, ":"))!=NULL) { + handle_len = (ptr - path); + memcpy(handle, path, handle_len); + *dev = ptr + 1; + ptr = handle + handle_len; + *ptr = '\0'; + DPRINTF("Detected handle: [%s]\n",handle); + + for (i = 0; i < size; i++) { + if ((strlen(dtypes[i]->handle) == handle_len) && + strncmp(handle, dtypes[i]->handle, + handle_len) == 0) { + found = 1; + } + + if (found) { + *type = dtypes[i]->idnum; + + if (dtypes[i]->single_handler == 1) { + /* Check whether tapdisk process + already exists */ + if (active_disks[dtypes[i]->idnum] == NULL) + *blkif = NULL; + else + *blkif = active_disks[dtypes[i] + ->idnum]->blkif; + } + return 0; + } + } + } + + /* Fall-through case, we didn't find a disk driver. */ + DPRINTF("Unknown blktap disk type [%s]!\n",handle); + *dev = NULL; + return -1; +} + + +static void add_disktype(blkif_t *blkif, int type) +{ + driver_list_entry_t *entry, **pprev; + + if (type > MAX_DISK_TYPES) + return; + + entry = malloc(sizeof(driver_list_entry_t)); + entry->blkif = blkif; + entry->next = NULL; + + pprev = &active_disks[type]; + while (*pprev != NULL) + pprev = &(*pprev)->next; + + *pprev = entry; + entry->pprev = pprev; +} + +static int del_disktype(blkif_t *blkif) +{ + driver_list_entry_t *entry, **pprev; + int type = blkif->drivertype, count = 0, close = 0; + + if (type > MAX_DISK_TYPES) + return 1; + + pprev = &active_disks[type]; + while ((*pprev != NULL) && ((*pprev)->blkif != blkif)) + pprev = &(*pprev)->next; + + if ((entry = *pprev) == NULL) { + DPRINTF("DEL_DISKTYPE: No match\n"); + return 1; + } + + *pprev = entry->next; + if (entry->next) + entry->next->pprev = pprev; + + DPRINTF("DEL_DISKTYPE: Freeing entry\n"); + free(entry); + + /* Caller should close() if no single controller, or list is empty. */ + return (!dtypes[type]->single_handler || (active_disks[type] == NULL)); +} + +static int write_msg(int fd, int msgtype, void *ptr, void *ptr2) +{ + blkif_t *blkif; + blkif_info_t *blk; + msg_hdr_t *msg; + msg_newdev_t *msg_dev; + char *p, *buf, *path; + int msglen, len, ret; + fd_set writefds; + struct timeval timeout; + image_t *image, *img; + uint32_t seed; + + blkif = (blkif_t *)ptr; + blk = blkif->info; + image = blkif->prv; + len = 0; + + switch (msgtype) + { + case CTLMSG_PARAMS: + path = (char *)ptr2; + DPRINTF("Write_msg called: CTLMSG_PARAMS, sending [%s, %s]\n", + blk->params, path); + + msglen = sizeof(msg_hdr_t) + strlen(path) + 1; + buf = malloc(msglen); + + /*Assign header fields*/ + msg = (msg_hdr_t *)buf; + msg->type = CTLMSG_PARAMS; + msg->len = msglen; + msg->drivertype = blkif->drivertype; + msg->readonly = blkif->readonly; + + gettimeofday(&timeout, NULL); + msg->cookie = blkif->cookie; + DPRINTF("Generated cookie, %d\n",blkif->cookie); + + /*Copy blk->params to msg*/ + p = buf + sizeof(msg_hdr_t); + memcpy(p, path, strlen(path) + 1); + + break; + + case CTLMSG_NEWDEV: + DPRINTF("Write_msg called: CTLMSG_NEWDEV\n"); + + msglen = sizeof(msg_hdr_t) + sizeof(msg_newdev_t); + buf = malloc(msglen); + + /*Assign header fields*/ + msg = (msg_hdr_t *)buf; + msg->type = CTLMSG_NEWDEV; + msg->len = msglen; + msg->drivertype = blkif->drivertype; + msg->cookie = blkif->cookie; + + msg_dev = (msg_newdev_t *)(buf + sizeof(msg_hdr_t)); + msg_dev->devnum = blkif->minor; + msg_dev->domid = blkif->domid; + + break; + + case CTLMSG_CLOSE: + DPRINTF("Write_msg called: CTLMSG_CLOSE\n"); + + msglen = sizeof(msg_hdr_t); + buf = malloc(msglen); + + /*Assign header fields*/ + msg = (msg_hdr_t *)buf; + msg->type = CTLMSG_CLOSE; + msg->len = msglen; + msg->drivertype = blkif->drivertype; + msg->cookie = blkif->cookie; + + break; + + case CTLMSG_PID: + DPRINTF("Write_msg called: CTLMSG_PID\n"); + + msglen = sizeof(msg_hdr_t); + buf = malloc(msglen); + + /*Assign header fields*/ + msg = (msg_hdr_t *)buf; + msg->type = CTLMSG_PID; + msg->len = msglen; + msg->drivertype = blkif->drivertype; + msg->cookie = blkif->cookie; + + break; + + default: + return -1; + } + + /*Now send the message*/ + ret = 0; + FD_ZERO(&writefds); + FD_SET(fd,&writefds); + timeout.tv_sec = max_timeout; /*Wait for up to max_timeout seconds*/ + timeout.tv_usec = 0; + if (select(fd+1, (fd_set *) 0, &writefds, + (fd_set *) 0, &timeout) > 0) { + len = write(fd, buf, msglen); + if (len == -1) DPRINTF("Write failed: (%d)\n",errno); + } + free(buf); + + return len; +} + +static int read_msg(int fd, int msgtype, void *ptr) +{ + blkif_t *blkif; + blkif_info_t *blk; + msg_hdr_t *msg; + msg_pid_t *msg_pid; + char *p, *buf; + int msglen = MSG_SIZE, len, ret; + fd_set readfds; + struct timeval timeout; + image_t *image, *img; + + + blkif = (blkif_t *)ptr; + blk = blkif->info; + image = blkif->prv; + + buf = malloc(MSG_SIZE); + + ret = 0; + FD_ZERO(&readfds); + FD_SET(fd,&readfds); + timeout.tv_sec = max_timeout; /*Wait for up to max_timeout seconds*/ + timeout.tv_usec = 0; + if (select(fd+1, &readfds, (fd_set *) 0, + (fd_set *) 0, &timeout) > 0) { + ret = read(fd, buf, msglen); + } + if (ret > 0) { + msg = (msg_hdr_t *)buf; + switch (msg->type) + { + case CTLMSG_IMG: + img = (image_t *)(buf + sizeof(msg_hdr_t)); + image->size = img->size; + image->secsize = img->secsize; + image->info = img->info; + + DPRINTF("Received CTLMSG_IMG: %llu, %lu, %u\n", + image->size, image->secsize, image->info); + if(msgtype != CTLMSG_IMG) ret = 0; + break; + + case CTLMSG_IMG_FAIL: + DPRINTF("Received CTLMSG_IMG_FAIL, " + "unable to open image\n"); + ret = 0; + break; + + case CTLMSG_NEWDEV_RSP: + DPRINTF("Received CTLMSG_NEWDEV_RSP\n"); + if(msgtype != CTLMSG_NEWDEV_RSP) ret = 0; + break; + + case CTLMSG_NEWDEV_FAIL: + DPRINTF("Received CTLMSG_NEWDEV_FAIL\n"); + ret = 0; + break; + + case CTLMSG_CLOSE_RSP: + DPRINTF("Received CTLMSG_CLOSE_RSP\n"); + if (msgtype != CTLMSG_CLOSE_RSP) ret = 0; + break; + + case CTLMSG_PID_RSP: + DPRINTF("Received CTLMSG_PID_RSP\n"); + if (msgtype != CTLMSG_PID_RSP) ret = 0; + else { + msg_pid = (msg_pid_t *) + (buf + sizeof(msg_hdr_t)); + blkif->tappid = msg_pid->pid; + DPRINTF("\tPID: [%d]\n",blkif->tappid); + } + break; + default: + DPRINTF("UNKNOWN MESSAGE TYPE RECEIVED\n"); + ret = 0; + break; + } + } + + free(buf); + + return ret; + +} + +static int launch_tapdisk_provider(char **argv) +{ + pid_t child; + + if ((child = fork()) < 0) + return -1; + + if (!child) { + int i; + for (i = 0 ; i < sysconf(_SC_OPEN_MAX) ; i++) + if (i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO) + close(i); + + execvp(argv[0], argv); + DPRINTF("execvp failed: %d (%s)\n", errno, strerror(errno)); + DPRINTF("PATH = %s\n", getenv("PATH")); + _exit(1); + } else { + pid_t got; + do { + got = waitpid(child, NULL, 0); + } while (got != child); + } + return child; +} + +static int launch_tapdisk(char *wrctldev, char *rdctldev) +{ + char *argv[] = { "tapdisk", wrctldev, rdctldev, NULL }; + + if (launch_tapdisk_provider(argv) < 0) + return -1; + + return 0; +} + +static int launch_tapdisk_ioemu(void) +{ + char *argv[] = { "tapdisk-ioemu", NULL }; + return launch_tapdisk_provider(argv); +} + +/* + * Connect to an ioemu based disk provider (qemu-dm or tapdisk-ioemu) + * + * If the domain has a device model, connect to qemu-dm through the + * domain specific pipe. Otherwise use a single tapdisk-ioemu instance + * which is represented by domid 0 and provides access for Dom0 and + * all DomUs without device model. + */ +static int connect_qemu(blkif_t *blkif, int domid) +{ + char *rdctldev, *wrctldev; + + static int tapdisk_ioemu_pid = 0; + static int dom0_readfd = 0; + static int dom0_writefd = 0; + + if (asprintf(&rdctldev, BLKTAP_CTRL_DIR "/qemu-read-%d", domid) < 0) + return -1; + + if (asprintf(&wrctldev, BLKTAP_CTRL_DIR "/qemu-write-%d", domid) < 0) { + free(rdctldev); + return -1; + } + + DPRINTF("Using qemu blktap pipe: %s\n", rdctldev); + + if (domid == 0) { + /* + * tapdisk-ioemu exits as soon as the last image is + * disconnected. Check if it is still running. + */ + if (tapdisk_ioemu_pid == 0 || kill(tapdisk_ioemu_pid, 0)) { + /* No device model and tapdisk-ioemu doesn't run yet */ + DPRINTF("Launching tapdisk-ioemu\n"); + tapdisk_ioemu_pid = launch_tapdisk_ioemu(); + + dom0_readfd = open_ctrl_socket(wrctldev); + dom0_writefd = open_ctrl_socket(rdctldev); + } + + DPRINTF("Using tapdisk-ioemu connection\n"); + blkif->fds[READ] = dom0_readfd; + blkif->fds[WRITE] = dom0_writefd; + } else if (access(rdctldev, R_OK | W_OK) == 0) { + /* Use existing pipe to the device model */ + DPRINTF("Using qemu-dm connection\n"); + blkif->fds[READ] = open_ctrl_socket(wrctldev); + blkif->fds[WRITE] = open_ctrl_socket(rdctldev); + } else { + /* No device model => try with tapdisk-ioemu */ + DPRINTF("No device model\n"); + connect_qemu(blkif, 0); + } + + free(rdctldev); + free(wrctldev); + + if (blkif->fds[READ] == -1 || blkif->fds[WRITE] == -1) + return -1; + + DPRINTF("Attached to qemu blktap pipes\n"); + return 0; +} + +/* Launch tapdisk instance */ +static int connect_tapdisk(blkif_t *blkif, int minor) +{ + char *rdctldev = NULL, *wrctldev = NULL; + int ret = -1; + + DPRINTF("tapdisk process does not exist:\n"); + + if (asprintf(&rdctldev, + "%s/tapctrlread%d", BLKTAP_CTRL_DIR, minor) == -1) + goto fail; + + if (asprintf(&wrctldev, + "%s/tapctrlwrite%d", BLKTAP_CTRL_DIR, minor) == -1) + goto fail; + + blkif->fds[READ] = open_ctrl_socket(rdctldev); + blkif->fds[WRITE] = open_ctrl_socket(wrctldev); + + if (blkif->fds[READ] == -1 || blkif->fds[WRITE] == -1) + goto fail; + + /*launch the new process*/ + DPRINTF("Launching process, CMDLINE [tapdisk %s %s]\n", + wrctldev, rdctldev); + + if (launch_tapdisk(wrctldev, rdctldev) == -1) { + DPRINTF("Unable to fork, cmdline: [tapdisk %s %s]\n", + wrctldev, rdctldev); + goto fail; + } + + ret = 0; + +fail: + if (rdctldev) + free(rdctldev); + + if (wrctldev) + free(wrctldev); + + return ret; +} + +static int blktapctrl_new_blkif(blkif_t *blkif) +{ + blkif_info_t *blk; + int major, minor, fd_read, fd_write, type, new; + char *rdctldev, *wrctldev, *ptr; + image_t *image; + blkif_t *exist = NULL; + static uint16_t next_cookie = 0; + + DPRINTF("Received a poll for a new vbd\n"); + if ( ((blk=blkif->info) != NULL) && (blk->params != NULL) ) { + if (blktap_interface_create(ctlfd, &major, &minor, blkif) < 0) + return -1; + + if (test_path(blk->params, &ptr, &type, &exist) != 0) { + DPRINTF("Error in blktap device string(%s).\n", + blk->params); + goto fail; + } + blkif->drivertype = type; + blkif->cookie = next_cookie++; + + if (!exist) { + if (type == DISK_TYPE_IOEMU) { + if (connect_qemu(blkif, blkif->domid)) + goto fail; + } else { + if (connect_tapdisk(blkif, minor)) + goto fail; + } + + } else { + DPRINTF("Process exists!\n"); + blkif->fds[READ] = exist->fds[READ]; + blkif->fds[WRITE] = exist->fds[WRITE]; + } + + add_disktype(blkif, type); + blkif->major = major; + blkif->minor = minor; + + image = (image_t *)malloc(sizeof(image_t)); + blkif->prv = (void *)image; + blkif->ops = &tapdisk_ops; + + /*Retrieve the PID of the new process*/ + if (get_tapdisk_pid(blkif) <= 0) { + DPRINTF("Unable to contact disk process\n"); + goto fail; + } + + /* Both of the following read and write calls will block up to + * max_timeout val*/ + if (write_msg(blkif->fds[WRITE], CTLMSG_PARAMS, blkif, ptr) + <= 0) { + DPRINTF("Write_msg failed - CTLMSG_PARAMS\n"); + goto fail; + } + + if (read_msg(blkif->fds[READ], CTLMSG_IMG, blkif) <= 0) { + DPRINTF("Read_msg failure - CTLMSG_IMG\n"); + goto fail; + } + + } else return -1; + + return 0; +fail: + ioctl(ctlfd, BLKTAP_IOCTL_FREEINTF, minor); + return -EINVAL; +} + +static int map_new_blktapctrl(blkif_t *blkif) +{ + DPRINTF("Received a poll for a new devmap\n"); + if (write_msg(blkif->fds[WRITE], CTLMSG_NEWDEV, blkif, NULL) <= 0) { + DPRINTF("Write_msg failed - CTLMSG_NEWDEV\n"); + return -EINVAL; + } + + if (read_msg(blkif->fds[READ], CTLMSG_NEWDEV_RSP, blkif) <= 0) { + DPRINTF("Read_msg failed - CTLMSG_NEWDEV_RSP\n"); + return -EINVAL; + } + DPRINTF("Exiting map_new_blktapctrl\n"); + + return blkif->minor - 1; +} + +static int unmap_blktapctrl(blkif_t *blkif) +{ + DPRINTF("Unmapping vbd\n"); + + if (write_msg(blkif->fds[WRITE], CTLMSG_CLOSE, blkif, NULL) <= 0) { + DPRINTF("Write_msg failed - CTLMSG_CLOSE\n"); + return -EINVAL; + } + + if (del_disktype(blkif)) { + close(blkif->fds[WRITE]); + close(blkif->fds[READ]); + } + + return 0; +} + +int open_ctrl_socket(char *devname) +{ + int ret; + int ipc_fd; + fd_set socks; + struct timeval timeout; + + if (mkdir(BLKTAP_CTRL_DIR, 0755) == 0) + DPRINTF("Created %s directory\n", BLKTAP_CTRL_DIR); + ret = mkfifo(devname,S_IRWXU|S_IRWXG|S_IRWXO); + if ( (ret != 0) && (errno != EEXIST) ) { + DPRINTF("ERROR: pipe failed (%d)\n", errno); + exit(0); + } + + ipc_fd = open(devname,O_RDWR|O_NONBLOCK); + + if (ipc_fd < 0) { + DPRINTF("FD open failed\n"); + return -1; + } + + return ipc_fd; +} + +static void print_drivers(void) +{ + int i, size; + + size = sizeof(dtypes)/sizeof(disk_info_t *); + DPRINTF("blktapctrl: v1.0.0\n"); + for (i = 0; i < size; i++) + DPRINTF("Found driver: [%s]\n",dtypes[i]->name); +} + +static void write_pidfile(long pid) +{ + char buf[100]; + int len; + int fd; + int flags; + + fd = open(PIDFILE, O_RDWR | O_CREAT, 0600); + if (fd == -1) { + DPRINTF("Opening pid file failed (%d)\n", errno); + exit(1); + } + + /* We exit silently if daemon already running. */ + if (lockf(fd, F_TLOCK, 0) == -1) + exit(0); + + /* Set FD_CLOEXEC, so that tapdisk doesn't get this file + descriptor. */ + if ((flags = fcntl(fd, F_GETFD)) == -1) { + DPRINTF("F_GETFD failed (%d)\n", errno); + exit(1); + } + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) { + DPRINTF("F_SETFD failed (%d)\n", errno); + exit(1); + } + + len = snprintf(buf, sizeof(buf), "%ld\n", pid); + if (write(fd, buf, len) != len) { + DPRINTF("Writing pid file failed (%d)\n", errno); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + char *devname; + tapdev_info_t *ctlinfo; + int tap_pfd, store_pfd, xs_fd, ret, timeout, pfd_count, count=0; + struct xs_handle *h; + struct pollfd pfd[NUM_POLL_FDS]; + pid_t process; + char buf[128]; + + __init_blkif(); + snprintf(buf, sizeof(buf), "BLKTAPCTRL[%d]", getpid()); + openlog(buf, LOG_CONS|LOG_ODELAY, LOG_DAEMON); + if (daemon(0,0)) { + DPRINTF("daemon failed (%d)\n", errno); + goto open_failed; + } + + print_drivers(); + init_driver_list(); + init_rng(); + + register_new_blkif_hook(blktapctrl_new_blkif); + register_new_devmap_hook(map_new_blktapctrl); + register_new_unmap_hook(unmap_blktapctrl); + + ctlfd = blktap_interface_open(); + if (ctlfd < 0) { + DPRINTF("couldn't open blktap interface\n"); + goto open_failed; + } + + retry: + /* Set up store connection and watch. */ + h = xs_daemon_open(); + if (h == NULL) { + DPRINTF("xs_daemon_open failed -- " + "is xenstore running?\n"); + if (count < MAX_ATTEMPTS) { + count++; + sleep(2); + goto retry; + } else goto open_failed; + } + + ret = setup_probe_watch(h); + if (ret != 0) { + DPRINTF("Failed adding device probewatch\n"); + xs_daemon_close(h); + goto open_failed; + } + + ioctl(ctlfd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE ); + + process = getpid(); + write_pidfile(process); + ret = ioctl(ctlfd, BLKTAP_IOCTL_SENDPID, process ); + + /*Static pollhooks*/ + pfd_count = 0; + tap_pfd = pfd_count++; + pfd[tap_pfd].fd = ctlfd; + pfd[tap_pfd].events = POLLIN; + + store_pfd = pfd_count++; + pfd[store_pfd].fd = xs_fileno(h); + pfd[store_pfd].events = POLLIN; + + while (run) { + timeout = 1000; /*Milliseconds*/ + ret = poll(pfd, pfd_count, timeout); + + if (ret > 0) { + if (pfd[store_pfd].revents) { + ret = xs_fire_next_watch(h); + } + } + } + + xs_daemon_close(h); + ioctl(ctlfd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_PASSTHROUGH ); + close(ctlfd); + closelog(); + + return 0; + + open_failed: + DPRINTF("Unable to start blktapctrl\n"); + closelog(); + return -1; +} + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/tools/blktap/drivers/blktapctrl.h b/tools/blktap/drivers/blktapctrl.h new file mode 100644 index 0000000..4512807 --- /dev/null +++ b/tools/blktap/drivers/blktapctrl.h @@ -0,0 +1,36 @@ +/* blktapctrl.h + * + * controller image utils. + * + * (c) 2004-6 Andrew Warfield and Julian Chesterfield + * + * 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. + */ + + +int blktap_interface_open(void); + +int blktap_interface_create(int ctlfd, int *major, int *minor, blkif_t *blkif); + diff --git a/tools/blktap/drivers/blktapctrl_linux.c b/tools/blktap/drivers/blktapctrl_linux.c new file mode 100644 index 0000000..7977873 --- /dev/null +++ b/tools/blktap/drivers/blktapctrl_linux.c @@ -0,0 +1,109 @@ + +#include +#include +#include +#include + +#include "tapdisk.h" +#include "blktaplib.h" +#include "blktapctrl.h" + +static void make_blktap_dev(char *devname, int major, int minor) +{ + struct stat st; + + if (lstat(devname, &st) != 0) { + /*Need to create device*/ + if (mkdir(BLKTAP_DEV_DIR, 0755) == 0) + DPRINTF("Created %s directory\n",BLKTAP_DEV_DIR); + if (mknod(devname, S_IFCHR|0600, + makedev(major, minor)) == 0) + DPRINTF("Created %s device\n",devname); + } else { + DPRINTF("%s device already exists\n",devname); + /* it already exists, but is it the same major number */ + if (((st.st_rdev>>8) & 0xff) != major) { + DPRINTF("%s has old major %d\n", + devname, + (unsigned int)((st.st_rdev >> 8) & 0xff)); + /* only try again if we succed in deleting it */ + if (!unlink(devname)) + make_blktap_dev(devname, major, minor); + } + } +} + +int blktap_interface_create(int ctlfd, int *major, int *minor, blkif_t *blkif) +{ + domid_translate_t tr; + domid_translate_ext_t tr_ext; + int ret; + char *devname; + + if (blkif->be_id >= (1<<28)) { + /* new-style backend-id, so use the extended structure */ + tr_ext.domid = blkif->domid; + tr_ext.busid = blkif->be_id; + ret = ioctl(ctlfd, BLKTAP_IOCTL_NEWINTF_EXT, &tr_ext); + DPRINTF("Sent domid %d and be_id %d\n", tr_ext.domid, + tr_ext.busid); + } + else { + /* old-style backend-id; use the old structure */ + tr.domid = blkif->domid; + tr.busid = (unsigned short)blkif->be_id; + ret = ioctl(ctlfd, BLKTAP_IOCTL_NEWINTF, tr); + DPRINTF("Sent domid %d and be_id %d\n", tr.domid, tr.busid); + } + + if ( (ret <= 0)||(ret > MAX_TAP_DEV) ) { + DPRINTF("Incorrect Dev ID [%d]\n",ret); + return -1; + } + + *minor = ret; + *major = ioctl(ctlfd, BLKTAP_IOCTL_MAJOR, ret ); + if (*major < 0) { + DPRINTF("Incorrect Major ID [%d]\n",*major); + return -1; + } + + if (asprintf(&devname,"%s/%s%d",BLKTAP_DEV_DIR, BLKTAP_DEV_NAME, *minor) == -1) + return -1; + make_blktap_dev(devname,*major,*minor); + DPRINTF("Received device id %d and major %d\n", + *minor, *major); + return 0; +} + + +int blktap_interface_open(void) +{ + char *devname; + int ret; + int ctlfd; + + /* Attach to blktap0 */ + if (asprintf(&devname,"%s/%s0", BLKTAP_DEV_DIR, BLKTAP_DEV_NAME) == -1) + goto open_failed; + + ret = xc_find_device_number("blktap0"); + if (ret < 0) { + DPRINTF("couldn't find device number for 'blktap0'\n"); + goto open_failed; + } + + blktap_major = major(ret); + make_blktap_dev(devname,blktap_major, 0); + + ctlfd = open(devname, O_RDWR); + if (ctlfd == -1) { + DPRINTF("blktap0 open failed\n"); + goto open_failed; + } + + return ctlfd; + +open_failed: + return -1; +} diff --git a/tools/blktap/drivers/block-aio.c b/tools/blktap/drivers/block-aio.c new file mode 100644 index 0000000..98727f4 --- /dev/null +++ b/tools/blktap/drivers/block-aio.c @@ -0,0 +1,259 @@ +/* block-aio.c + * + * libaio-based raw disk implementation. + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + * NB: This code is not thread-safe. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tapdisk.h" +#include "tapaio.h" +#include "blk.h" + +#define MAX_AIO_REQS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ) + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +struct tdaio_state { + int fd; + tap_aio_context_t aio; +}; + + +/*Get Image size, secsize*/ +static int get_image_info(struct td_state *s, int fd) +{ + int ret; + long size; + unsigned long total_size; + struct statvfs statBuf; + struct stat stat; + + ret = fstat(fd, &stat); + if (ret != 0) { + DPRINTF("ERROR: fstat failed, Couldn't stat image"); + return -EINVAL; + } + + if (S_ISBLK(stat.st_mode)) { + /*Accessing block device directly*/ + if (blk_getimagesize(fd, &s->size) != 0) + return -EINVAL; + + DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + + /*Get the sector size*/ + if (blk_getsectorsize(fd, &s->sector_size) != 0) + s->sector_size = DEFAULT_SECTOR_SIZE; + + } else { + /*Local file? try fstat instead*/ + s->size = (stat.st_size >> SECTOR_SHIFT); + s->sector_size = DEFAULT_SECTOR_SIZE; + DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + } + + if (s->size == 0) { + s->size =((uint64_t) 16836057); + s->sector_size = DEFAULT_SECTOR_SIZE; + } + s->info = 0; + + return 0; +} + +static inline void init_fds(struct disk_driver *dd) +{ + int i; + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + + for(i = 0; i < MAX_IOFD; i++) + dd->io_fd[i] = 0; + + dd->io_fd[0] = prv->aio.aio_ctx.pollfd; +} + +/* Open the disk file and initialize aio state. */ +static int tdaio_open (struct disk_driver *dd, const char *name, td_flag_t flags) +{ + int i, fd, ret = 0, o_flags; + struct td_state *s = dd->td_state; + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + + DPRINTF("block-aio open('%s')", name); + + /* Initialize AIO */ + ret = tap_aio_init(&prv->aio, 0, MAX_AIO_REQS); + if (ret != 0) + return ret; + + /* Open the file */ + o_flags = O_DIRECT | O_LARGEFILE | + ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); + fd = open(name, o_flags); + + if ( (fd == -1) && (errno == EINVAL) ) { + + /* Maybe O_DIRECT isn't supported. */ + o_flags &= ~O_DIRECT; + fd = open(name, o_flags); + if (fd != -1) DPRINTF("WARNING: Accessing image without" + "O_DIRECT! (%s)\n", name); + + } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name); + + if (fd == -1) { + DPRINTF("Unable to open [%s] (%d)!\n", name, 0 - errno); + ret = 0 - errno; + goto done; + } + + prv->fd = fd; + + init_fds(dd); + ret = get_image_info(s, fd); + +done: + return ret; +} + +static int tdaio_queue_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct td_state *s = dd->td_state; + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + int size = nb_sectors * s->sector_size; + uint64_t offset = sector * (uint64_t)s->sector_size; + + return tap_aio_read(&prv->aio, prv->fd, size, offset, buf, + cb, id, sector, private); +} + +static int tdaio_queue_write(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct td_state *s = dd->td_state; + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + int size = nb_sectors * s->sector_size; + uint64_t offset = sector * (uint64_t)s->sector_size; + + return tap_aio_write(&prv->aio, prv->fd, size, offset, buf, + cb, id, sector, private); +} + +static int tdaio_submit(struct disk_driver *dd) +{ + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + + return tap_aio_submit(&prv->aio); +} + +static int tdaio_close(struct disk_driver *dd) +{ + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + + io_destroy(prv->aio.aio_ctx.aio_ctx); + close(prv->fd); + + return 0; +} + +static int tdaio_do_callbacks(struct disk_driver *dd, int sid) +{ + int i, nr_events, rsp = 0; + struct io_event *ep; + struct tdaio_state *prv = (struct tdaio_state *)dd->private; + + nr_events = tap_aio_get_events(&prv->aio.aio_ctx); +repeat: + for (ep = prv->aio.aio_events, i = nr_events; i-- > 0; ep++) { + struct iocb *io = ep->obj; + struct pending_aio *pio; + + pio = &prv->aio.pending_aio[(long)io->data]; + rsp += pio->cb(dd, ep->res == io->u.c.nbytes ? 0 : 1, + pio->sector, io->u.c.nbytes >> 9, + pio->id, pio->private); + + prv->aio.iocb_free[prv->aio.iocb_free_count++] = io; + } + + if (nr_events) { + nr_events = tap_aio_more_events(&prv->aio.aio_ctx); + goto repeat; + } + + tap_aio_continue(&prv->aio.aio_ctx); + + return rsp; +} + +static int tdaio_get_parent_id(struct disk_driver *dd, struct disk_id *id) +{ + return TD_NO_PARENT; +} + +static int tdaio_validate_parent(struct disk_driver *dd, + struct disk_driver *parent, td_flag_t flags) +{ + return -EINVAL; +} + +struct tap_disk tapdisk_aio = { + .disk_type = "tapdisk_aio", + .private_data_size = sizeof(struct tdaio_state), + .td_open = tdaio_open, + .td_queue_read = tdaio_queue_read, + .td_queue_write = tdaio_queue_write, + .td_submit = tdaio_submit, + .td_close = tdaio_close, + .td_do_callbacks = tdaio_do_callbacks, + .td_get_parent_id = tdaio_get_parent_id, + .td_validate_parent = tdaio_validate_parent +}; diff --git a/tools/blktap/drivers/block-qcow.c b/tools/blktap/drivers/block-qcow.c new file mode 100644 index 0000000..51a2e32 --- /dev/null +++ b/tools/blktap/drivers/block-qcow.c @@ -0,0 +1,1430 @@ +/* block-qcow.c + * + * Asynchronous Qemu copy-on-write disk implementation. + * Code based on the Qemu implementation + * (see copyright notice below) + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + */ + +/* + * Block driver for the QCOW format + * + * Copyright (c) 2004 Fabrice Bellard + * + * 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: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bswap.h" +#include "aes.h" +#include "tapdisk.h" +#include "tapaio.h" +#include "blk.h" + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#if 1 +#define ASSERT(_p) \ + if ( !(_p) ) { DPRINTF("Assertion '%s' failed, line %d, file %s", #_p , \ + __LINE__, __FILE__); *(int*)0=0; } +#else +#define ASSERT(_p) ((void)0) +#endif + +#define ROUNDUP(l, s) \ +({ \ + (uint64_t)( \ + (l + (s - 1)) - ((l + (s - 1)) % s)); \ +}) + +#undef IOCB_IDX +#define IOCB_IDX(_s, _io) ((_io) - (_s)->iocb_list) + +#define ZERO_TEST(_b) (_b | 0x00) + +/**************************************************************/ +/* QEMU COW block driver with compression and encryption support */ + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) +#define XEN_MAGIC (('X' << 24) | ('E' << 16) | ('N' << 8) | 0xfb) +#define QCOW_VERSION 1 + +#define QCOW_CRYPT_NONE 0x00 +#define QCOW_CRYPT_AES 0x01 + +#define QCOW_OFLAG_COMPRESSED (1LL << 63) +#define SPARSE_FILE 0x01 +#define EXTHDR_L1_BIG_ENDIAN 0x02 + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +typedef struct QCowHeader { + uint32_t magic; + uint32_t version; + uint64_t backing_file_offset; + uint32_t backing_file_size; + uint32_t mtime; + uint64_t size; /* in bytes */ + uint8_t cluster_bits; + uint8_t l2_bits; + uint32_t crypt_method; + uint64_t l1_table_offset; +} QCowHeader; + +/*Extended header for Xen enhancements*/ +typedef struct QCowHeader_ext { + uint32_t xmagic; + uint32_t cksum; + uint32_t min_cluster_alloc; + uint32_t flags; +} QCowHeader_ext; + +#define L2_CACHE_SIZE 16 /*Fixed allocation in Qemu*/ + +struct tdqcow_state { + int fd; /*Main Qcow file descriptor */ + uint64_t fd_end; /*Store a local record of file length */ + char *name; /*Record of the filename*/ + uint32_t backing_file_size; + uint64_t backing_file_offset; + int encrypted; /*File contents are encrypted or plain*/ + int cluster_bits; /*Determines length of cluster as + *indicated by file hdr*/ + int cluster_size; /*Length of cluster*/ + int cluster_sectors; /*Number of sectors per cluster*/ + int cluster_alloc; /*Blktap fix for allocating full + *extents*/ + int min_cluster_alloc; /*Blktap historical extent alloc*/ + int sparse; /*Indicates whether to preserve sparseness*/ + int l2_bits; /*Size of L2 table entry*/ + int l2_size; /*Full table size*/ + int l1_size; /*L1 table size*/ + uint64_t cluster_offset_mask; + uint64_t l1_table_offset; /*L1 table offset from beginning of + *file*/ + uint64_t *l1_table; /*L1 table entries*/ + uint64_t *l2_cache; /*We maintain a cache of size + *L2_CACHE_SIZE of most read entries*/ + uint64_t l2_cache_offsets[L2_CACHE_SIZE]; /*L2 cache entries*/ + uint32_t l2_cache_counts[L2_CACHE_SIZE]; /*Cache access record*/ + uint8_t *cluster_cache; + uint8_t *cluster_data; + uint64_t cluster_cache_offset; /**/ + uint32_t crypt_method; /*current crypt method, 0 if no + *key yet */ + uint32_t crypt_method_header; /**/ + AES_KEY aes_encrypt_key; /*AES key*/ + AES_KEY aes_decrypt_key; /*AES key*/ + + /* libaio state */ + tap_aio_context_t aio; +}; + +static int decompress_cluster(struct tdqcow_state *s, uint64_t cluster_offset); + +#ifdef USE_GCRYPT + +#include + +static uint32_t gen_cksum(char *ptr, int len) +{ + int i; + uint32_t md[4]; + + /* Convert L1 table to big endian */ + for(i = 0; i < len / sizeof(uint64_t); i++) { + cpu_to_be64s(&((uint64_t*) ptr)[i]); + } + + /* Generate checksum */ + gcry_md_hash_buffer(GCRY_MD_MD5, md, ptr, len); + + /* Convert L1 table back to native endianess */ + for(i = 0; i < len / sizeof(uint64_t); i++) { + be64_to_cpus(&((uint64_t*) ptr)[i]); + } + + return md[0]; +} + +#else /* use libcrypto */ + +#include + +static uint32_t gen_cksum(char *ptr, int len) +{ + int i; + unsigned char *md; + uint32_t ret; + + md = malloc(MD5_DIGEST_LENGTH); + if(!md) return 0; + + /* Convert L1 table to big endian */ + for(i = 0; i < len / sizeof(uint64_t); i++) { + cpu_to_be64s(&((uint64_t*) ptr)[i]); + } + + /* Generate checksum */ + if (MD5((unsigned char *)ptr, len, md) != md) + ret = 0; + else + memcpy(&ret, md, sizeof(uint32_t)); + + /* Convert L1 table back to native endianess */ + for(i = 0; i < len / sizeof(uint64_t); i++) { + be64_to_cpus(&((uint64_t*) ptr)[i]); + } + + free(md); + return ret; +} + +#endif + +static int get_filesize(char *filename, uint64_t *size, struct stat *st) +{ + int fd; + QCowHeader header; + + /*Set to the backing file size*/ + fd = open(filename, O_RDONLY); + if (fd < 0) + return -1; + if (read(fd, &header, sizeof(header)) < sizeof(header)) { + close(fd); + return -1; + } + close(fd); + + be32_to_cpus(&header.magic); + be64_to_cpus(&header.size); + if (header.magic == QCOW_MAGIC) { + *size = header.size >> SECTOR_SHIFT; + return 0; + } + + if(S_ISBLK(st->st_mode)) { + fd = open(filename, O_RDONLY); + if (fd < 0) + return -1; + if (blk_getimagesize(fd, size) != 0) { + close(fd); + return -1; + } + close(fd); + } else *size = (st->st_size >> SECTOR_SHIFT); + return 0; +} + +static int qcow_set_key(struct tdqcow_state *s, const char *key) +{ + uint8_t keybuf[16]; + int len, i; + + memset(keybuf, 0, 16); + len = strlen(key); + if (len > 16) + len = 16; + /* XXX: we could compress the chars to 7 bits to increase + entropy */ + for (i = 0; i < len; i++) { + keybuf[i] = key[i]; + } + s->crypt_method = s->crypt_method_header; + + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + return -1; + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + return -1; +#if 0 + /* test */ + { + uint8_t in[16]; + uint8_t out[16]; + uint8_t tmp[16]; + for (i=0; i<16; i++) + in[i] = i; + AES_encrypt(in, tmp, &s->aes_encrypt_key); + AES_decrypt(tmp, out, &s->aes_decrypt_key); + for (i = 0; i < 16; i++) + DPRINTF(" %02x", tmp[i]); + DPRINTF("\n"); + for (i = 0; i < 16; i++) + DPRINTF(" %02x", out[i]); + DPRINTF("\n"); + } +#endif + return 0; +} + +/* + * The crypt function is compatible with the linux cryptoloop + * algorithm for < 4 GB images. NOTE: out_buf == in_buf is + * supported . + */ +static void encrypt_sectors(struct tdqcow_state *s, int64_t sector_num, + uint8_t *out_buf, const uint8_t *in_buf, + int nb_sectors, int enc, + const AES_KEY *key) +{ + union { + uint64_t ll[2]; + uint8_t b[16]; + } ivec; + int i; + + for (i = 0; i < nb_sectors; i++) { + ivec.ll[0] = cpu_to_le64(sector_num); + ivec.ll[1] = 0; + AES_cbc_encrypt(in_buf, out_buf, 512, key, + ivec.b, enc); + sector_num++; + in_buf += 512; + out_buf += 512; + } +} + +static int qtruncate(int fd, off_t length, int sparse) +{ + int ret, i; + int current = 0, rem = 0; + uint64_t sectors; + struct stat st; + char *buf; + + /* If length is greater than the current file len + * we synchronously write zeroes to the end of the + * file, otherwise we truncate the length down + */ + ret = fstat(fd, &st); + if (ret == -1) + return -1; + if (S_ISBLK(st.st_mode)) + return 0; + + sectors = (length + DEFAULT_SECTOR_SIZE - 1)/DEFAULT_SECTOR_SIZE; + current = (st.st_size + DEFAULT_SECTOR_SIZE - 1)/DEFAULT_SECTOR_SIZE; + rem = st.st_size % DEFAULT_SECTOR_SIZE; + + /* If we are extending this file, we write zeros to the end -- + * this tries to ensure that the extents allocated wind up being + * contiguous on disk. + */ + if(st.st_size < sectors * DEFAULT_SECTOR_SIZE) { + /*We are extending the file*/ + if ((ret = posix_memalign((void **)&buf, + 512, DEFAULT_SECTOR_SIZE))) { + DPRINTF("posix_memalign failed: %d\n", ret); + return -1; + } + memset(buf, 0x00, DEFAULT_SECTOR_SIZE); + if (lseek(fd, 0, SEEK_END)==-1) { + DPRINTF("Lseek EOF failed (%d), internal error\n", + errno); + free(buf); + return -1; + } + if (rem) { + ret = write(fd, buf, rem); + if (ret != rem) { + DPRINTF("write failed: ret = %d, err = %s\n", + ret, strerror(errno)); + free(buf); + return -1; + } + } + for (i = current; i < sectors; i++ ) { + ret = write(fd, buf, DEFAULT_SECTOR_SIZE); + if (ret != DEFAULT_SECTOR_SIZE) { + DPRINTF("write failed: ret = %d, err = %s\n", + ret, strerror(errno)); + free(buf); + return -1; + } + } + free(buf); + } else if(sparse && (st.st_size > sectors * DEFAULT_SECTOR_SIZE)) + if (ftruncate(fd, (off_t)sectors * DEFAULT_SECTOR_SIZE)==-1) { + DPRINTF("Ftruncate failed (%s)\n", strerror(errno)); + return -1; + } + return 0; +} + + +/* 'allocate' is: + * + * 0 to not allocate. + * + * 1 to allocate a normal cluster (for sector indexes 'n_start' to + * 'n_end') + * + * 2 to allocate a compressed cluster of size + * 'compressed_size'. 'compressed_size' must be > 0 and < + * cluster_size + * + * return 0 if not allocated. + */ +static uint64_t get_cluster_offset(struct tdqcow_state *s, + uint64_t offset, int allocate, + int compressed_size, + int n_start, int n_end) +{ + int min_index, i, j, l1_index, l2_index, l2_sector, l1_sector; + char *tmp_ptr2, *l2_ptr, *l1_ptr; + uint64_t *tmp_ptr; + uint64_t l2_offset, *l2_table, cluster_offset, tmp; + uint32_t min_count; + int new_l2_table; + + /*Check L1 table for the extent offset*/ + l1_index = offset >> (s->l2_bits + s->cluster_bits); + l2_offset = s->l1_table[l1_index]; + new_l2_table = 0; + if (!l2_offset) { + if (!allocate) + return 0; + /* + * allocating a new l2 entry + extent + * at the end of the file, we must also + * update the L1 entry safely. + */ + l2_offset = s->fd_end; + + /* round to cluster size */ + l2_offset = (l2_offset + s->cluster_size - 1) + & ~(s->cluster_size - 1); + + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset; + tmp = cpu_to_be64(l2_offset); + + /*Truncate file for L2 table + *(initialised to zero in case we crash)*/ + if (qtruncate(s->fd, + l2_offset + (s->l2_size * sizeof(uint64_t)), + s->sparse) != 0) { + DPRINTF("ERROR truncating file\n"); + return 0; + } + s->fd_end = l2_offset + (s->l2_size * sizeof(uint64_t)); + + /*Update the L1 table entry on disk + * (for O_DIRECT we write 4KByte blocks)*/ + l1_sector = (l1_index * sizeof(uint64_t)) >> 12; + l1_ptr = (char *)s->l1_table + (l1_sector << 12); + + if (posix_memalign((void **)&tmp_ptr, 4096, 4096) != 0) { + DPRINTF("ERROR allocating memory for L1 table\n"); + } + memcpy(tmp_ptr, l1_ptr, 4096); + + /* Convert block to write to big endian */ + for(i = 0; i < 4096 / sizeof(uint64_t); i++) { + cpu_to_be64s(&tmp_ptr[i]); + } + + /* + * Issue non-asynchronous L1 write. + * For safety, we must ensure that + * entry is written before blocks. + */ + lseek(s->fd, s->l1_table_offset + (l1_sector << 12), SEEK_SET); + if (write(s->fd, tmp_ptr, 4096) != 4096) { + free(tmp_ptr); + return 0; + } + free(tmp_ptr); + + new_l2_table = 1; + goto cache_miss; + } else if (s->min_cluster_alloc == s->l2_size) { + /*Fast-track the request*/ + cluster_offset = l2_offset + (s->l2_size * sizeof(uint64_t)); + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + return cluster_offset + (l2_index * s->cluster_size); + } + + /*Check to see if L2 entry is already cached*/ + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for (j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } + } + l2_table = s->l2_cache + (i << s->l2_bits); + goto found; + } + } + +cache_miss: + /* not found: load a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (s->l2_cache_counts[i] < min_count) { + min_count = s->l2_cache_counts[i]; + min_index = i; + } + } + l2_table = s->l2_cache + (min_index << s->l2_bits); + + /*If extent pre-allocated, read table from disk, + *otherwise write new table to disk*/ + if (new_l2_table) { + /*Should we allocate the whole extent? Adjustable parameter.*/ + if (s->cluster_alloc == s->l2_size) { + cluster_offset = l2_offset + + (s->l2_size * sizeof(uint64_t)); + cluster_offset = (cluster_offset + s->cluster_size - 1) + & ~(s->cluster_size - 1); + if (qtruncate(s->fd, cluster_offset + + (s->cluster_size * s->l2_size), + s->sparse) != 0) { + DPRINTF("ERROR truncating file\n"); + return 0; + } + s->fd_end = cluster_offset + + (s->cluster_size * s->l2_size); + for (i = 0; i < s->l2_size; i++) { + l2_table[i] = cpu_to_be64(cluster_offset + + (i*s->cluster_size)); + } + } else memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); + + lseek(s->fd, l2_offset, SEEK_SET); + if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } else { + lseek(s->fd, l2_offset, SEEK_SET); + if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } + + /*Update the cache entries*/ + s->l2_cache_offsets[min_index] = l2_offset; + s->l2_cache_counts[min_index] = 1; + +found: + /*The extent is split into 's->l2_size' blocks of + *size 's->cluster_size'*/ + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + cluster_offset = be64_to_cpu(l2_table[l2_index]); + + if (!cluster_offset || + ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1) ) { + if (!allocate) + return 0; + + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && + (n_end - n_start) < s->cluster_sectors) { + /* cluster is already allocated but compressed, we must + decompress it in the case it is not completely + overwritten */ + if (decompress_cluster(s, cluster_offset) < 0) + return 0; + cluster_offset = lseek(s->fd, s->fd_end, SEEK_SET); + cluster_offset = (cluster_offset + s->cluster_size - 1) + & ~(s->cluster_size - 1); + /* write the cluster content - not asynchronous */ + lseek(s->fd, cluster_offset, SEEK_SET); + if (write(s->fd, s->cluster_cache, s->cluster_size) != + s->cluster_size) + return -1; + } else { + /* allocate a new cluster */ + cluster_offset = lseek(s->fd, s->fd_end, SEEK_SET); + if (allocate == 1) { + /* round to cluster size */ + cluster_offset = + (cluster_offset + s->cluster_size - 1) + & ~(s->cluster_size - 1); + if (qtruncate(s->fd, cluster_offset + + s->cluster_size, s->sparse)!=0) { + DPRINTF("ERROR truncating file\n"); + return 0; + } + s->fd_end = (cluster_offset + s->cluster_size); + /* if encrypted, we must initialize the cluster + content which won't be written */ + if (s->crypt_method && + (n_end - n_start) < s->cluster_sectors) { + uint64_t start_sect; + start_sect = (offset & + ~(s->cluster_size - 1)) + >> 9; + memset(s->cluster_data + 512, + 0xaa, 512); + for (i = 0; i < s->cluster_sectors;i++) + { + if (i < n_start || i >= n_end) + { + encrypt_sectors(s, start_sect + i, + s->cluster_data, + s->cluster_data + 512, 1, 1, + &s->aes_encrypt_key); + lseek(s->fd, cluster_offset + i * 512, SEEK_SET); + if (write(s->fd, s->cluster_data, 512) != 512) + return -1; + } + } + } + } else { + cluster_offset |= QCOW_OFLAG_COMPRESSED | + (uint64_t)compressed_size + << (63 - s->cluster_bits); + } + } + /* update L2 table */ + tmp = cpu_to_be64(cluster_offset); + l2_table[l2_index] = tmp; + + /*For IO_DIRECT we write 4KByte blocks*/ + l2_sector = (l2_index * sizeof(uint64_t)) >> 12; + l2_ptr = (char *)l2_table + (l2_sector << 12); + + if (posix_memalign((void **)&tmp_ptr2, 4096, 4096) != 0) { + DPRINTF("ERROR allocating memory for L1 table\n"); + } + memcpy(tmp_ptr2, l2_ptr, 4096); + lseek(s->fd, l2_offset + (l2_sector << 12), SEEK_SET); + if (write(s->fd, tmp_ptr2, 4096) != 4096) { + free(tmp_ptr2); + return -1; + } + free(tmp_ptr2); + } + return cluster_offset; +} + +static void init_cluster_cache(struct disk_driver *dd) +{ + struct td_state *bs = dd->td_state; + struct tdqcow_state *s = (struct tdqcow_state *)dd->private; + uint32_t count = 0; + int i, cluster_entries; + + cluster_entries = s->cluster_size / 512; + DPRINTF("Initialising Cluster cache, %d sectors per cluster (%d cluster size)\n", + cluster_entries, s->cluster_size); + + for (i = 0; i < bs->size; i += cluster_entries) { + if (get_cluster_offset(s, i << 9, 0, 0, 0, 1)) count++; + if (count >= L2_CACHE_SIZE) return; + } + DPRINTF("Finished cluster initialisation, added %d entries\n", count); + return; +} + +static int qcow_is_allocated(struct tdqcow_state *s, int64_t sector_num, + int nb_sectors, int *pnum) +{ + int index_in_cluster, n; + uint64_t cluster_offset; + + cluster_offset = get_cluster_offset(s, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + *pnum = n; + return (cluster_offset != 0); +} + +static int decompress_buffer(uint8_t *out_buf, int out_buf_size, + const uint8_t *buf, int buf_size) +{ + z_stream strm1, *strm = &strm1; + int ret, out_len; + + memset(strm, 0, sizeof(*strm)); + + strm->next_in = (uint8_t *)buf; + strm->avail_in = buf_size; + strm->next_out = out_buf; + strm->avail_out = out_buf_size; + + ret = inflateInit2(strm, -12); + if (ret != Z_OK) + return -1; + ret = inflate(strm, Z_FINISH); + out_len = strm->next_out - out_buf; + if ( (ret != Z_STREAM_END && ret != Z_BUF_ERROR) || + (out_len != out_buf_size) ) { + inflateEnd(strm); + return -1; + } + inflateEnd(strm); + return 0; +} + +static int decompress_cluster(struct tdqcow_state *s, uint64_t cluster_offset) +{ + int ret, csize; + uint64_t coffset; + + coffset = cluster_offset & s->cluster_offset_mask; + if (s->cluster_cache_offset != coffset) { + csize = cluster_offset >> (63 - s->cluster_bits); + csize &= (s->cluster_size - 1); + lseek(s->fd, coffset, SEEK_SET); + ret = read(s->fd, s->cluster_data, csize); + if (ret != csize) + return -1; + if (decompress_buffer(s->cluster_cache, s->cluster_size, + s->cluster_data, csize) < 0) { + return -1; + } + s->cluster_cache_offset = coffset; + } + return 0; +} + +static inline void init_fds(struct disk_driver *dd) +{ + int i; + struct tdqcow_state *s = (struct tdqcow_state *)dd->private; + + for(i = 0; i < MAX_IOFD; i++) + dd->io_fd[i] = 0; + + dd->io_fd[0] = s->aio.aio_ctx.pollfd; +} + +/* Open the disk file and initialize qcow state. */ +static int tdqcow_open (struct disk_driver *dd, const char *name, td_flag_t flags) +{ + int fd, len, i, shift, ret, size, l1_table_size, o_flags; + int max_aio_reqs; + struct td_state *bs = dd->td_state; + struct tdqcow_state *s = (struct tdqcow_state *)dd->private; + char *buf; + QCowHeader *header; + QCowHeader_ext *exthdr; + uint32_t cksum; + uint64_t final_cluster = 0; + + DPRINTF("QCOW: Opening %s\n",name); + + o_flags = O_DIRECT | O_LARGEFILE | + ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); + fd = open(name, o_flags); + if (fd < 0) { + DPRINTF("Unable to open %s (%d)\n",name,0 - errno); + return -1; + } + + s->fd = fd; + if (asprintf(&s->name,"%s", name) == -1) { + close(fd); + return -1; + } + + ASSERT(sizeof(QCowHeader) + sizeof(QCowHeader_ext) < 512); + + ret = posix_memalign((void **)&buf, 512, 512); + if (ret != 0) goto fail; + + if (read(fd, buf, 512) != 512) + goto fail; + + header = (QCowHeader *)buf; + be32_to_cpus(&header->magic); + be32_to_cpus(&header->version); + be64_to_cpus(&header->backing_file_offset); + be32_to_cpus(&header->backing_file_size); + be32_to_cpus(&header->mtime); + be64_to_cpus(&header->size); + be32_to_cpus(&header->crypt_method); + be64_to_cpus(&header->l1_table_offset); + + if (header->magic != QCOW_MAGIC) + goto fail; + + switch (header->version) { + case QCOW_VERSION: + break; + case 2: + close(fd); + dd->drv = &tapdisk_qcow2; + return dd->drv->td_open(dd, name, flags); + default: + goto fail; + } + + if (header->size <= 1 || header->cluster_bits < 9) + goto fail; + if (header->crypt_method > QCOW_CRYPT_AES) + goto fail; + s->crypt_method_header = header->crypt_method; + if (s->crypt_method_header) + s->encrypted = 1; + s->cluster_bits = header->cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + s->l2_bits = header->l2_bits; + s->l2_size = 1 << s->l2_bits; + s->cluster_alloc = s->l2_size; + bs->size = header->size / 512; + s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1; + s->backing_file_offset = header->backing_file_offset; + s->backing_file_size = header->backing_file_size; + + /* read the level 1 table */ + shift = s->cluster_bits + s->l2_bits; + s->l1_size = (header->size + (1LL << shift) - 1) >> shift; + + s->l1_table_offset = header->l1_table_offset; + + /*allocate a 4Kbyte multiple of memory*/ + l1_table_size = s->l1_size * sizeof(uint64_t); + if (l1_table_size % 4096 > 0) { + l1_table_size = ((l1_table_size >> 12) + 1) << 12; + } + ret = posix_memalign((void **)&s->l1_table, 4096, l1_table_size); + if (ret != 0) goto fail; + + memset(s->l1_table, 0x00, l1_table_size); + + DPRINTF("L1 Table offset detected: %llu, size %d (%d)\n", + (long long)s->l1_table_offset, + (int) (s->l1_size * sizeof(uint64_t)), + l1_table_size); + + lseek(fd, s->l1_table_offset, SEEK_SET); + if (read(fd, s->l1_table, l1_table_size) != l1_table_size) + goto fail; + + for(i = 0; i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + //DPRINTF("L1[%d] => %llu\n", i, s->l1_table[i]); + if (s->l1_table[i] > final_cluster) + final_cluster = s->l1_table[i]; + } + + /* alloc L2 cache */ + size = s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t); + ret = posix_memalign((void **)&s->l2_cache, 4096, size); + if(ret != 0) goto fail; + + size = s->cluster_size; + ret = posix_memalign((void **)&s->cluster_cache, 4096, size); + if(ret != 0) goto fail; + + ret = posix_memalign((void **)&s->cluster_data, 4096, size); + if(ret != 0) goto fail; + s->cluster_cache_offset = -1; + + if (s->backing_file_offset != 0) + s->cluster_alloc = 1; /*Cannot use pre-alloc*/ + + bs->sector_size = 512; + bs->info = 0; + + /*Detect min_cluster_alloc*/ + s->min_cluster_alloc = 1; /*Default*/ + if (s->backing_file_offset == 0 && s->l1_table_offset % 4096 == 0) { + /*We test to see if the xen magic # exists*/ + exthdr = (QCowHeader_ext *)(buf + sizeof(QCowHeader)); + be32_to_cpus(&exthdr->xmagic); + if(exthdr->xmagic != XEN_MAGIC) + goto end_xenhdr; + + /* Try to detect old tapdisk images. They have to be fixed because + * they don't use big endian but native endianess for the L1 table */ + if ((exthdr->flags & EXTHDR_L1_BIG_ENDIAN) == 0) { + + /* + The image is broken. Fix it. The L1 table has already been + byte-swapped, so we can write it to the image file as it is + currently in memory. Then swap it back to native endianess + for operation. + */ + + DPRINTF("qcow: Converting image to big endian L1 table\n"); + + lseek(fd, s->l1_table_offset, SEEK_SET); + if (write(fd, s->l1_table, l1_table_size) != l1_table_size) { + DPRINTF("qcow: Failed to write new L1 table\n"); + goto fail; + } + + for(i = 0;i < s->l1_size; i++) { + cpu_to_be64s(&s->l1_table[i]); + } + + /* Write the big endian flag to the extended header */ + exthdr->flags |= EXTHDR_L1_BIG_ENDIAN; + + if (write(fd, buf, 512) != 512) { + DPRINTF("qcow: Failed to write extended header\n"); + goto fail; + } + } + + /*Finally check the L1 table cksum*/ + be32_to_cpus(&exthdr->cksum); + cksum = gen_cksum((char *)s->l1_table, + s->l1_size * sizeof(uint64_t)); + if(exthdr->cksum != cksum) + goto end_xenhdr; + + be32_to_cpus(&exthdr->min_cluster_alloc); + be32_to_cpus(&exthdr->flags); + s->sparse = (exthdr->flags & SPARSE_FILE); + s->min_cluster_alloc = exthdr->min_cluster_alloc; + } + + end_xenhdr: + + /* A segment (i.e. a page) can span multiple clusters */ + max_aio_reqs = ((getpagesize() / s->cluster_size) + 1) * + MAX_SEGMENTS_PER_REQ * MAX_REQUESTS; + + if (tap_aio_init(&s->aio, bs->size, max_aio_reqs)!=0) { + DPRINTF("Unable to initialise AIO state\n"); + tap_aio_free(&s->aio); + goto fail; + } + init_fds(dd); + + if (!final_cluster) + s->fd_end = s->l1_table_offset + l1_table_size; + else { + s->fd_end = lseek(fd, 0, SEEK_END); + if (s->fd_end == (off_t)-1) + goto fail; + } + + return 0; + +fail: + DPRINTF("QCOW Open failed\n"); + tap_aio_free(&s->aio); + free(s->l1_table); + free(s->l2_cache); + free(s->cluster_cache); + free(s->cluster_data); + close(fd); + return -1; +} + +static int tdqcow_queue_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct tdqcow_state *s = (struct tdqcow_state *)dd->private; + int ret = 0, index_in_cluster, n, i, rsp = 0; + uint64_t cluster_offset, sec, nr_secs; + + sec = sector; + nr_secs = nb_sectors; + + /*Check we can get a lock*/ + for (i = 0; i < nb_sectors; i++) + if (!tap_aio_can_lock(&s->aio, sector + i)) + return cb(dd, -EBUSY, sector, nb_sectors, id, private); + + /*We store a local record of the request*/ + while (nb_sectors > 0) { + cluster_offset = + get_cluster_offset(s, sector << 9, 0, 0, 0, 0); + index_in_cluster = sector & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + + if (s->aio.iocb_free_count == 0 || !tap_aio_lock(&s->aio, sector)) + return cb(dd, -EBUSY, sector, nb_sectors, id, private); + + if(!cluster_offset) { + tap_aio_unlock(&s->aio, sector); + ret = cb(dd, BLK_NOT_ALLOCATED, + sector, n, id, private); + if (ret == -EBUSY) { + /* mark remainder of request + * as busy and try again later */ + return cb(dd, -EBUSY, sector + n, + nb_sectors - n, id, private); + } else + rsp += ret; + } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + tap_aio_unlock(&s->aio, sector); + if (decompress_cluster(s, cluster_offset) < 0) { + rsp += cb(dd, -EIO, sector, + nb_sectors, id, private); + goto done; + } + memcpy(buf, s->cluster_cache + index_in_cluster * 512, + 512 * n); + rsp += cb(dd, 0, sector, n, id, private); + } else { + tap_aio_read(&s->aio, s->fd, n * 512, + (cluster_offset + index_in_cluster * 512), + buf, cb, id, sector, private); + } + nb_sectors -= n; + sector += n; + buf += n * 512; + } +done: + return rsp; +} + +static int tdqcow_queue_write(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct tdqcow_state *s = (struct tdqcow_state *)dd->private; + int ret = 0, index_in_cluster, n, i; + uint64_t cluster_offset, sec, nr_secs; + + sec = sector; + nr_secs = nb_sectors; + + /*Check we can get a lock*/ + for (i = 0; i < nb_sectors; i++) + if (!tap_aio_can_lock(&s->aio, sector + i)) + return cb(dd, -EBUSY, sector, nb_sectors, id, private); + + /*We store a local record of the request*/ + while (nb_sectors > 0) { + index_in_cluster = sector & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + + if (s->aio.iocb_free_count == 0 || !tap_aio_lock(&s->aio, sector)) + return cb(dd, -EBUSY, sector, nb_sectors, id, private); + + cluster_offset = get_cluster_offset(s, sector << 9, 1, 0, + index_in_cluster, + index_in_cluster+n); + if (!cluster_offset) { + DPRINTF("Ooops, no write cluster offset!\n"); + tap_aio_unlock(&s->aio, sector); + return cb(dd, -EIO, sector, nb_sectors, id, private); + } + + if (s->crypt_method) { + encrypt_sectors(s, sector, s->cluster_data, + (unsigned char *)buf, n, 1, + &s->aes_encrypt_key); + tap_aio_write(&s->aio, s->fd, n * 512, + (cluster_offset + index_in_cluster*512), + (char *)s->cluster_data, cb, id, sector, + private); + } else { + tap_aio_write(&s->aio, s->fd, n * 512, + (cluster_offset + index_in_cluster*512), + buf, cb, id, sector, private); + } + + nb_sectors -= n; + sector += n; + buf += n * 512; + } + s->cluster_cache_offset = -1; /* disable compressed cache */ + + return 0; +} + +static int tdqcow_submit(struct disk_driver *dd) +{ + struct tdqcow_state *prv = (struct tdqcow_state *)dd->private; + + return tap_aio_submit(&prv->aio); +} + +static int tdqcow_close(struct disk_driver *dd) +{ + struct tdqcow_state *s = (struct tdqcow_state *)dd->private; + uint32_t cksum, out; + int fd, offset; + + /*Update the hdr cksum*/ + if(s->min_cluster_alloc == s->l2_size) { + cksum = gen_cksum((char *)s->l1_table, s->l1_size * sizeof(uint64_t)); + printf("Writing cksum: %d",cksum); + fd = open(s->name, O_WRONLY | O_LARGEFILE); /*Open without O_DIRECT*/ + offset = sizeof(QCowHeader) + sizeof(uint32_t); + lseek(fd, offset, SEEK_SET); + out = cpu_to_be32(cksum); + if (write(fd, &out, sizeof(uint32_t))) ; + close(fd); + } + + io_destroy(s->aio.aio_ctx.aio_ctx); + free(s->name); + free(s->l1_table); + free(s->l2_cache); + free(s->cluster_cache); + free(s->cluster_data); + close(s->fd); + return 0; +} + +static int tdqcow_do_callbacks(struct disk_driver *dd, int sid) +{ + int ret, i, nr_events, rsp = 0,*ptr; + struct io_event *ep; + struct tdqcow_state *prv = (struct tdqcow_state *)dd->private; + + if (sid > MAX_IOFD) return 1; + + nr_events = tap_aio_get_events(&prv->aio.aio_ctx); +repeat: + for (ep = prv->aio.aio_events, i = nr_events; i-- > 0; ep++) { + struct iocb *io = ep->obj; + struct pending_aio *pio; + + pio = &prv->aio.pending_aio[(long)io->data]; + + tap_aio_unlock(&prv->aio, pio->sector); + + if (prv->crypt_method) + encrypt_sectors(prv, pio->sector, + (unsigned char *)pio->buf, + (unsigned char *)pio->buf, + pio->nb_sectors, 0, + &prv->aes_decrypt_key); + + rsp += pio->cb(dd, ep->res == io->u.c.nbytes ? 0 : 1, + pio->sector, pio->nb_sectors, + pio->id, pio->private); + + prv->aio.iocb_free[prv->aio.iocb_free_count++] = io; + } + + if (nr_events) { + nr_events = tap_aio_more_events(&prv->aio.aio_ctx); + goto repeat; + } + + tap_aio_continue(&prv->aio.aio_ctx); + + return rsp; +} + +int qcow_create(const char *filename, uint64_t total_size, + const char *backing_file, int sparse) +{ + int fd, header_size, backing_filename_len, l1_size, i; + int shift, length, adjust, flags = 0, ret = 0; + QCowHeader header; + QCowHeader_ext exthdr; + char backing_filename[PATH_MAX], *ptr; + uint64_t tmp, size, total_length; + struct stat st; + + DPRINTF("Qcow_create: size %llu\n",(long long unsigned)total_size); + + fd = open(filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -1; + + memset(&header, 0, sizeof(header)); + header.magic = cpu_to_be32(QCOW_MAGIC); + header.version = cpu_to_be32(QCOW_VERSION); + + /*Create extended header fields*/ + exthdr.xmagic = cpu_to_be32(XEN_MAGIC); + + header_size = sizeof(header) + sizeof(QCowHeader_ext); + backing_filename_len = 0; + size = (total_size >> SECTOR_SHIFT); + if (backing_file) { + if (strcmp(backing_file, "fat:")) { + const char *p; + /* XXX: this is a hack: we do not attempt to + *check for URL like syntax */ + p = strchr(backing_file, ':'); + if (p && (p - backing_file) >= 2) { + /* URL like but exclude "c:" like filenames */ + strncpy(backing_filename, backing_file, + sizeof(backing_filename)); + } else { + if (realpath(backing_file, backing_filename) == NULL || + stat(backing_filename, &st) != 0) { + return -1; + } + } + header.backing_file_offset = cpu_to_be64(header_size); + backing_filename_len = strlen(backing_filename); + header.backing_file_size = cpu_to_be32( + backing_filename_len); + header_size += backing_filename_len; + + /*Set to the backing file size*/ + if(get_filesize(backing_filename, &size, &st)) { + return -1; + } + DPRINTF("Backing file size detected: %lld sectors" + "(total %lld [%lld MB])\n", + (long long)size, + (long long)(size << SECTOR_SHIFT), + (long long)(size >> 11)); + } else { + backing_file = NULL; + DPRINTF("Setting file size: %lld (total %lld)\n", + (long long) total_size, + (long long) (total_size << SECTOR_SHIFT)); + } + header.mtime = cpu_to_be32(st.st_mtime); + header.cluster_bits = 9; /* 512 byte cluster to avoid copying + unmodifyed sectors */ + header.l2_bits = 12; /* 32 KB L2 tables */ + exthdr.min_cluster_alloc = cpu_to_be32(1); + } else { + DPRINTF("Setting file size: %lld sectors" + "(total %lld [%lld MB])\n", + (long long) size, + (long long) (size << SECTOR_SHIFT), + (long long) (size >> 11)); + header.cluster_bits = 12; /* 4 KB clusters */ + header.l2_bits = 9; /* 4 KB L2 tables */ + exthdr.min_cluster_alloc = cpu_to_be32(1 << 9); + } + /*Set the header size value*/ + header.size = cpu_to_be64(size * 512); + + header_size = (header_size + 7) & ~7; + if (header_size % 4096 > 0) { + header_size = ((header_size >> 12) + 1) << 12; + } + + shift = header.cluster_bits + header.l2_bits; + l1_size = ((size * 512) + (1LL << shift) - 1) >> shift; + + header.l1_table_offset = cpu_to_be64(header_size); + DPRINTF("L1 Table offset: %d, size %d\n", + header_size, + (int)(l1_size * sizeof(uint64_t))); + header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); + + ptr = calloc(1, l1_size * sizeof(uint64_t)); + exthdr.cksum = cpu_to_be32(gen_cksum(ptr, l1_size * sizeof(uint64_t))); + printf("Created cksum: %d\n",exthdr.cksum); + free(ptr); + + /*adjust file length to system page size boundary*/ + length = ROUNDUP(header_size + (l1_size * sizeof(uint64_t)), + getpagesize()); + if (qtruncate(fd, length, 0)!=0) { + DPRINTF("ERROR truncating file\n"); + return -1; + } + + if (sparse == 0) { + /*Filesize is length+l1_size*(1 << s->l2_bits)+(size*512)*/ + total_length = length + (l1_size * (1 << 9)) + (size * 512); + if (qtruncate(fd, total_length, 0)!=0) { + DPRINTF("ERROR truncating file\n"); + return -1; + } + printf("File truncated to length %"PRIu64"\n",total_length); + } else + flags = SPARSE_FILE; + + exthdr.flags = cpu_to_be32(flags); + + /* write all the data */ + lseek(fd, 0, SEEK_SET); + ret += write(fd, &header, sizeof(header)); + ret += write(fd, &exthdr, sizeof(exthdr)); + if (backing_file) + ret += write(fd, backing_filename, backing_filename_len); + + lseek(fd, header_size, SEEK_SET); + tmp = 0; + for (i = 0;i < l1_size; i++) { + ret += write(fd, &tmp, sizeof(tmp)); + } + + close(fd); + + return 0; +} + +static int qcow_make_empty(struct tdqcow_state *s) +{ + uint32_t l1_length = s->l1_size * sizeof(uint64_t); + + memset(s->l1_table, 0, l1_length); + lseek(s->fd, s->l1_table_offset, SEEK_SET); + if (write(s->fd, s->l1_table, l1_length) < 0) + return -1; + if (qtruncate(s->fd, s->l1_table_offset + l1_length, s->sparse)!=0) { + DPRINTF("ERROR truncating file\n"); + return -1; + } + + memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); + + return 0; +} + +static int qcow_get_cluster_size(struct tdqcow_state *s) +{ + return s->cluster_size; +} + +/* XXX: put compressed sectors first, then all the cluster aligned + tables to avoid losing bytes in alignment */ +static int qcow_compress_cluster(struct tdqcow_state *s, int64_t sector_num, + const uint8_t *buf) +{ + z_stream strm; + int ret, out_len; + uint8_t *out_buf; + uint64_t cluster_offset; + + out_buf = malloc(s->cluster_size + (s->cluster_size / 1000) + 128); + if (!out_buf) + return -1; + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, -12, + 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + free(out_buf); + return -1; + } + + strm.avail_in = s->cluster_size; + strm.next_in = (uint8_t *)buf; + strm.avail_out = s->cluster_size; + strm.next_out = out_buf; + + ret = deflate(&strm, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK) { + free(out_buf); + deflateEnd(&strm); + return -1; + } + out_len = strm.next_out - out_buf; + + deflateEnd(&strm); + + if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + /* could not compress: write normal cluster */ + //tdqcow_queue_write(bs, sector_num, buf, s->cluster_sectors); + } else { + cluster_offset = get_cluster_offset(s, sector_num << 9, 2, + out_len, 0, 0); + cluster_offset &= s->cluster_offset_mask; + lseek(s->fd, cluster_offset, SEEK_SET); + if (write(s->fd, out_buf, out_len) != out_len) { + free(out_buf); + return -1; + } + } + + free(out_buf); + return 0; +} + +static int tdqcow_get_parent_id(struct disk_driver *dd, struct disk_id *id) +{ + off_t off; + char *buf, *filename; + int len, secs, err = -EINVAL; + struct tdqcow_state *child = (struct tdqcow_state *)dd->private; + + if (!child->backing_file_offset) + return TD_NO_PARENT; + + /* read the backing file name */ + len = child->backing_file_size; + off = child->backing_file_offset - (child->backing_file_offset % 512); + secs = (len + (child->backing_file_offset - off) + 511) >> 9; + + if (posix_memalign((void **)&buf, 512, secs << 9)) + return -1; + + if (lseek(child->fd, off, SEEK_SET) == (off_t)-1) + goto out; + + if (read(child->fd, buf, secs << 9) != secs << 9) + goto out; + filename = buf + (child->backing_file_offset - off); + filename[len] = '\0'; + + id->name = strdup(filename); + id->drivertype = DISK_TYPE_QCOW; + err = 0; + out: + free(buf); + return err; +} + +static int tdqcow_validate_parent(struct disk_driver *child, + struct disk_driver *parent, td_flag_t flags) +{ + struct stat stats; + uint64_t psize, csize; + struct tdqcow_state *c = (struct tdqcow_state *)child->private; + struct tdqcow_state *p = (struct tdqcow_state *)parent->private; + + if (stat(p->name, &stats)) + return -EINVAL; + if (get_filesize(p->name, &psize, &stats)) + return -EINVAL; + + if (stat(c->name, &stats)) + return -EINVAL; + if (get_filesize(c->name, &csize, &stats)) + return -EINVAL; + + if (csize != psize) + return -EINVAL; + + return 0; +} + +struct tap_disk tapdisk_qcow = { + .disk_type = "tapdisk_qcow", + .private_data_size = sizeof(struct tdqcow_state), + .td_open = tdqcow_open, + .td_queue_read = tdqcow_queue_read, + .td_queue_write = tdqcow_queue_write, + .td_submit = tdqcow_submit, + .td_close = tdqcow_close, + .td_do_callbacks = tdqcow_do_callbacks, + .td_get_parent_id = tdqcow_get_parent_id, + .td_validate_parent = tdqcow_validate_parent +}; diff --git a/tools/blktap/drivers/block-qcow2.c b/tools/blktap/drivers/block-qcow2.c new file mode 100644 index 0000000..fe28a2e --- /dev/null +++ b/tools/blktap/drivers/block-qcow2.c @@ -0,0 +1,1955 @@ +/* + * Block driver for the QCOW version 2 format + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * 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 "aes.h" +#include +#include +#include +#include +#include +#include + +#include "tapdisk.h" +#include "tapaio.h" +#include "bswap.h" + +#define USE_AIO + +#define qemu_malloc malloc +#define qemu_mallocz(size) calloc(1, size) +#define qemu_free free + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#define BLOCK_FLAG_ENCRYPT 1 + +/* + Differences with QCOW: + + - Support for multiple incremental snapshots. + - Memory management by reference counts. + - Clusters which have a reference count of one have the bit + QCOW_OFLAG_COPIED to optimize write performance. + - Size of compressed clusters is stored in sectors to reduce bit usage + in the cluster offsets. + - Support for storing additional data (such as the VM state) in the + snapshots. + - If a backing store is used, the cluster size is not constrained + (could be backported to QCOW). + - L2 tables have always a size of one cluster. +*/ + +//#define DEBUG_ALLOC +//#define DEBUG_ALLOC2 + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) +#define QCOW_VERSION 2 + +#define QCOW_CRYPT_NONE 0 +#define QCOW_CRYPT_AES 1 + +/* indicate that the refcount of the referenced cluster is exactly one. */ +#define QCOW_OFLAG_COPIED (1LL << 63) +/* indicate that the cluster is compressed (they never have the copied flag) */ +#define QCOW_OFLAG_COMPRESSED (1LL << 62) + +#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */ + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +typedef struct QCowHeader { + uint32_t magic; + uint32_t version; + uint64_t backing_file_offset; + uint32_t backing_file_size; + uint32_t cluster_bits; + uint64_t size; /* in bytes */ + + uint32_t crypt_method; + uint32_t l1_size; /* XXX: save number of clusters instead ? */ + uint64_t l1_table_offset; + uint64_t refcount_table_offset; + uint32_t refcount_table_clusters; + uint32_t nb_snapshots; + uint64_t snapshots_offset; +} QCowHeader; + +typedef struct __attribute__((packed)) QCowSnapshotHeader { + /* header is 8 byte aligned */ + uint64_t l1_table_offset; + + uint32_t l1_size; + uint16_t id_str_size; + uint16_t name_size; + + uint32_t date_sec; + uint32_t date_nsec; + + uint64_t vm_clock_nsec; + + uint32_t vm_state_size; + uint32_t extra_data_size; /* for extension */ + /* extra data follows */ + /* id_str follows */ + /* name follows */ +} QCowSnapshotHeader; + +#define L2_CACHE_SIZE 16 + +typedef struct QCowSnapshot { + uint64_t l1_table_offset; + uint32_t l1_size; + char *id_str; + char *name; + uint32_t vm_state_size; + uint32_t date_sec; + uint32_t date_nsec; + uint64_t vm_clock_nsec; +} QCowSnapshot; + +typedef struct BDRVQcowState { + + /* blktap additions */ + int fd; + int poll_pipe[2]; /* dummy fd for polling on */ + char* name; + int encrypted; + char backing_file[1024]; + struct disk_driver* backing_hd; + + int64_t total_sectors; + + tap_aio_context_t async; + + /* Original qemu variables */ + int cluster_bits; + int cluster_size; + int cluster_sectors; + int l2_bits; + int l2_size; + int l1_size; + int l1_vm_state_index; + int csize_shift; + int csize_mask; + uint64_t cluster_offset_mask; + uint64_t l1_table_offset; + uint64_t *l1_table; + uint64_t *l2_cache; + uint64_t l2_cache_offsets[L2_CACHE_SIZE]; + uint32_t l2_cache_counts[L2_CACHE_SIZE]; + uint8_t *cluster_cache; + uint8_t *cluster_data; + uint64_t cluster_cache_offset; + + uint64_t *refcount_table; + uint64_t refcount_table_offset; + uint32_t refcount_table_size; + uint64_t refcount_block_cache_offset; + uint16_t *refcount_block_cache; + int64_t free_cluster_index; + int64_t free_byte_offset; + + uint32_t crypt_method; /* current crypt method, 0 if no key yet */ + uint32_t crypt_method_header; + AES_KEY aes_encrypt_key; + AES_KEY aes_decrypt_key; + uint64_t snapshots_offset; + int snapshots_size; + int nb_snapshots; + QCowSnapshot *snapshots; +} BDRVQcowState; + +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); +static int qcow_read(struct disk_driver *bs, uint64_t sector_num, + uint8_t *buf, int nb_sectors); + +static int qcow_read_snapshots(struct disk_driver *bs); +static void qcow_free_snapshots(struct disk_driver *bs); + +static int refcount_init(struct disk_driver *bs); +static void refcount_close(struct disk_driver *bs); +static int get_refcount(struct disk_driver *bs, int64_t cluster_index); +static int update_cluster_refcount(struct disk_driver *bs, + int64_t cluster_index, + int addend); +static void update_refcount(struct disk_driver *bs, + int64_t offset, int64_t length, + int addend); +static int64_t alloc_clusters(struct disk_driver *bs, int64_t size); +static int64_t alloc_bytes(struct disk_driver *bs, int size); +static void free_clusters(struct disk_driver *bs, + int64_t offset, int64_t size); +#ifdef DEBUG_ALLOC +static void check_refcounts(struct disk_driver *bs); +#endif + +static int qcow_sync_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *prv); + +/** + * Read with byte offsets + */ +static int bdrv_pread(int fd, int64_t offset, void *buf, int count) +{ + int ret; + + if (lseek(fd, offset, SEEK_SET) == -1) { + DPRINTF("bdrv_pread failed seek (%#"PRIx64").\n", offset); + return -1; + } + + ret = read(fd, buf, count); + if (ret < 0) { + if (lseek(fd, 0, SEEK_END) >= offset) { + DPRINTF("bdrv_pread read failed (%#"PRIx64", END = %#"PRIx64").\n", + offset, lseek(fd, 0, SEEK_END)); + return -1; + } + + /* Read beyond end of file. Reading zeros. */ + memset(buf, 0, count); + ret = count; + } else if (ret < count) { + /* Read beyond end of file. Filling up with zeros. */ + memset(buf + ret, 0, count - ret); + ret = count; + } + return ret; +} + +/** + * Write with byte offsets + */ +static int bdrv_pwrite(int fd, int64_t offset, const void *buf, int count) +{ + if (lseek(fd, offset, SEEK_SET) == -1) { + DPRINTF("bdrv_pwrite failed seek (%#"PRIx64").\n", offset); + return -1; + } + + return write(fd, buf, count); +} + + +/** + * Read with sector offsets + */ +static int bdrv_read(int fd, int64_t offset, void *buf, int count) +{ + return bdrv_pread(fd, 512 * offset, buf, 512 * count); +} + +/** + * Write with sector offsets + */ +static int bdrv_write(int fd, int64_t offset, const void *buf, int count) +{ + return bdrv_pwrite(fd, 512 * offset, buf, count); +} + + +static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const QCowHeader *cow_header = (const void *)buf; + + if (buf_size >= sizeof(QCowHeader) && + be32_to_cpu(cow_header->magic) == QCOW_MAGIC && + be32_to_cpu(cow_header->version) == QCOW_VERSION) + return 100; + else + return 0; +} + +static int qcow_open(struct disk_driver *bs, const char *filename, td_flag_t flags) +{ + BDRVQcowState *s = bs->private; + int len, i, shift, ret, max_aio_reqs; + QCowHeader header; + + int fd, o_flags; + + o_flags = O_LARGEFILE | ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); + + DPRINTF("Opening %s\n", filename); + fd = open(filename, o_flags); + if (fd < 0) { + DPRINTF("Unable to open %s (%d)\n", filename, 0 - errno); + return -1; + } + + s->fd = fd; + if (asprintf(&s->name,"%s", filename) == -1) { + close(fd); + return -1; + } + + ret = read(fd, &header, sizeof(header)); + if (ret != sizeof(header)) { + DPRINTF(" ret = %d, errno = %d\n", ret, errno); + goto fail; + } + + be32_to_cpus(&header.magic); + be32_to_cpus(&header.version); + be64_to_cpus(&header.backing_file_offset); + be32_to_cpus(&header.backing_file_size); + be64_to_cpus(&header.size); + be32_to_cpus(&header.cluster_bits); + be32_to_cpus(&header.crypt_method); + be64_to_cpus(&header.l1_table_offset); + be32_to_cpus(&header.l1_size); + be64_to_cpus(&header.refcount_table_offset); + be32_to_cpus(&header.refcount_table_clusters); + be64_to_cpus(&header.snapshots_offset); + be32_to_cpus(&header.nb_snapshots); + + if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) + goto fail; + + if (header.size <= 1 || + header.cluster_bits < 9 || + header.cluster_bits > 16) + goto fail; + + s->crypt_method = 0; + if (header.crypt_method > QCOW_CRYPT_AES) + goto fail; + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) + s->encrypted = 1; + s->cluster_bits = header.cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ + s->l2_size = 1 << s->l2_bits; + s->total_sectors = header.size / 512; + s->csize_shift = (62 - (s->cluster_bits - 8)); + s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; + s->cluster_offset_mask = (1LL << s->csize_shift) - 1; + s->refcount_table_offset = header.refcount_table_offset; + s->refcount_table_size = + header.refcount_table_clusters << (s->cluster_bits - 3); + + s->snapshots_offset = header.snapshots_offset; + s->nb_snapshots = header.nb_snapshots; + +// DPRINTF("-- cluster_bits/size/sectors = %d/%d/%d\n", +// s->cluster_bits, s->cluster_size, s->cluster_sectors); +// DPRINTF("-- l2_bits/sizes = %d/%d\n", +// s->l2_bits, s->l2_size); + + /* Set sector size and number */ + bs->td_state->sector_size = 512; + bs->td_state->size = header.size / 512; + bs->td_state->info = 0; + + /* read the level 1 table */ + s->l1_size = header.l1_size; + shift = s->cluster_bits + s->l2_bits; + s->l1_vm_state_index = (header.size + (1LL << shift) - 1) >> shift; + /* the L1 table must contain at least enough entries to put + header.size bytes */ + if (s->l1_size < s->l1_vm_state_index) { + DPRINTF("L1 table tooo small\n"); + goto fail; + } + s->l1_table_offset = header.l1_table_offset; + + s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); + if (!s->l1_table) + goto fail; + + + if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1) + goto fail; + + if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) != + s->l1_size * sizeof(uint64_t)) { + + DPRINTF("Could not read L1 table\n"); + goto fail; + } + + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + /* alloc L2 cache */ + s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + if (!s->l2_cache) + goto fail; + s->cluster_cache = qemu_malloc(s->cluster_size); + if (!s->cluster_cache) + goto fail; + /* one more sector for decompressed data alignment */ + s->cluster_data = qemu_malloc(s->cluster_size + 512); + if (!s->cluster_data) + goto fail; + s->cluster_cache_offset = -1; + + if (refcount_init(bs) < 0) + goto fail; + + /* read the backing file name */ + s->backing_file[0] = '\0'; + if (header.backing_file_offset != 0) { + len = header.backing_file_size; + if (len > 1023) + len = 1023; + + if (lseek(fd, header.backing_file_offset, SEEK_SET) == -1) { + DPRINTF("Could not lseek to %#"PRIx64"\n", header.backing_file_offset); + goto fail; + } + + if (read(fd, s->backing_file, len) != len) { + DPRINTF("Could not read %#x bytes from %#"PRIx64": %s\n", + len, header.backing_file_offset, + strerror(errno)); + goto fail; + } + + s->backing_file[len] = '\0'; + } + +#if 0 + s->backing_hd = NULL; + if (qcow_read_snapshots(bs) < 0) { + DPRINTF("Could not read backing files\n"); + goto fail; + } +#endif + +#ifdef DEBUG_ALLOC + check_refcounts(bs); +#endif + + /* Initialize fds */ + for(i = 0; i < MAX_IOFD; i++) + bs->io_fd[i] = 0; + +#ifdef USE_AIO + /* Initialize AIO */ + + /* A segment (i.e. a page) can span multiple clusters */ + max_aio_reqs = ((getpagesize() / s->cluster_size) + 1) * + MAX_SEGMENTS_PER_REQ * MAX_REQUESTS; + + if (tap_aio_init(&s->async, bs->td_state->size, max_aio_reqs)) { + DPRINTF("Unable to initialise AIO state\n"); + tap_aio_free(&s->async); + goto fail; + } + + bs->io_fd[0] = s->async.aio_ctx.pollfd; +#else + /* Synchronous IO */ + if (pipe(s->poll_pipe)) + goto fail; + + bs->io_fd[0] = s->poll_pipe[0]; +#endif + + return 0; + + fail: + DPRINTF("qcow_open failed\n"); + +#ifdef USE_AIO + tap_aio_free(&s->async); +#endif + + qcow_free_snapshots(bs); + refcount_close(bs); + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + qemu_free(s->cluster_cache); + qemu_free(s->cluster_data); + close(fd); + return -1; +} + +static int qcow_set_key(struct disk_driver *bs, const char *key) +{ + BDRVQcowState *s = bs->private; + uint8_t keybuf[16]; + int len, i; + + memset(keybuf, 0, 16); + len = strlen(key); + if (len > 16) + len = 16; + /* XXX: we could compress the chars to 7 bits to increase + entropy */ + for(i = 0;i < len;i++) { + keybuf[i] = key[i]; + } + s->crypt_method = s->crypt_method_header; + + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + return -1; + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + return -1; +#if 0 + /* test */ + { + uint8_t in[16]; + uint8_t out[16]; + uint8_t tmp[16]; + for(i=0;i<16;i++) + in[i] = i; + AES_encrypt(in, tmp, &s->aes_encrypt_key); + AES_decrypt(tmp, out, &s->aes_decrypt_key); + for(i = 0; i < 16; i++) + printf(" %02x", tmp[i]); + printf("\n"); + for(i = 0; i < 16; i++) + printf(" %02x", out[i]); + printf("\n"); + } +#endif + return 0; +} + +/* The crypt function is compatible with the linux cryptoloop + algorithm for < 4 GB images. NOTE: out_buf == in_buf is + supported */ +static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, + uint8_t *out_buf, const uint8_t *in_buf, + int nb_sectors, int enc, + const AES_KEY *key) +{ + union { + uint64_t ll[2]; + uint8_t b[16]; + } ivec; + int i; + + for(i = 0; i < nb_sectors; i++) { + ivec.ll[0] = cpu_to_le64(sector_num); + ivec.ll[1] = 0; + AES_cbc_encrypt(in_buf, out_buf, 512, key, + ivec.b, enc); + sector_num++; + in_buf += 512; + out_buf += 512; + } +} + +static int copy_sectors(struct disk_driver *bs, uint64_t start_sect, + uint64_t cluster_offset, int n_start, int n_end) +{ + BDRVQcowState *s = bs->private; + int n, ret; + + n = n_end - n_start; + if (n <= 0) + return 0; + + ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n); + + if (ret < 0) + return ret; + if (s->crypt_method) { + encrypt_sectors(s, start_sect + n_start, + s->cluster_data, + s->cluster_data, n, 1, + &s->aes_encrypt_key); + } + + + ret = bdrv_pwrite(s->fd, cluster_offset + 512*n_start, s->cluster_data, n*512); + + if (ret < 0) + return ret; + return 0; +} + +static void l2_cache_reset(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + + memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); +} + +static inline int l2_cache_new_entry(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + uint32_t min_count; + int min_index, i; + + /* find a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (s->l2_cache_counts[i] < min_count) { + min_count = s->l2_cache_counts[i]; + min_index = i; + } + } + return min_index; +} + +static int64_t align_offset(int64_t offset, int n) +{ + offset = (offset + n - 1) & ~(n - 1); + return offset; +} + +static int grow_l1_table(struct disk_driver *bs, int min_size) +{ + BDRVQcowState *s = bs->private; + int new_l1_size, new_l1_size2, ret, i; + uint64_t *new_l1_table; + uint64_t new_l1_table_offset; + uint64_t data64; + uint32_t data32; + + new_l1_size = s->l1_size; + if (min_size <= new_l1_size) + return 0; + while (min_size > new_l1_size) { + new_l1_size = (new_l1_size * 3 + 1) / 2; + } + +#ifdef DEBUG_ALLOC2 + DPRINTF("grow l1_table from %d to %d\n", s->l1_size, new_l1_size); +#endif + + new_l1_size2 = sizeof(uint64_t) * new_l1_size; + new_l1_table = qemu_mallocz(new_l1_size2); + if (!new_l1_table) + return -ENOMEM; + memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); + + /* write new table (align to cluster) */ + new_l1_table_offset = alloc_clusters(bs, new_l1_size2); + + for(i = 0; i < s->l1_size; i++) + new_l1_table[i] = cpu_to_be64(new_l1_table[i]); + + + if (lseek(s->fd, new_l1_table_offset, SEEK_SET) == -1) + goto fail; + + ret = write(s->fd, new_l1_table, new_l1_size2); + if (ret != new_l1_size2) + goto fail; + + + for(i = 0; i < s->l1_size; i++) + new_l1_table[i] = be64_to_cpu(new_l1_table[i]); + + /* set new table */ + data64 = cpu_to_be64(new_l1_table_offset); + + if (lseek(s->fd, offsetof(QCowHeader, l1_table_offset), SEEK_SET) == -1) + goto fail; + + if (write(s->fd, &data64, sizeof(data64)) != sizeof(data64)) + goto fail; + + data32 = cpu_to_be32(new_l1_size); + + if (bdrv_pwrite(s->fd, offsetof(QCowHeader, l1_size), + &data32, sizeof(data32)) != sizeof(data32)) + goto fail; + qemu_free(s->l1_table); + free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t)); + s->l1_table_offset = new_l1_table_offset; + s->l1_table = new_l1_table; + s->l1_size = new_l1_size; + return 0; + fail: + qemu_free(s->l1_table); + return -EIO; +} + +/* 'allocate' is: + * + * 0 not to allocate. + * + * 1 to allocate a normal cluster (for sector indexes 'n_start' to + * 'n_end') + * + * 2 to allocate a compressed cluster of size + * 'compressed_size'. 'compressed_size' must be > 0 and < + * cluster_size + * + * return 0 if not allocated. + */ +static uint64_t get_cluster_offset(struct disk_driver *bs, + uint64_t offset, int allocate, + int compressed_size, + int n_start, int n_end) +{ + BDRVQcowState *s = bs->private; + int min_index, i, j, l1_index, l2_index, ret; + uint64_t l2_offset, *l2_table, cluster_offset, tmp, old_l2_offset; + + l1_index = offset >> (s->l2_bits + s->cluster_bits); + if (l1_index >= s->l1_size) { + /* outside l1 table is allowed: we grow the table if needed */ + if (!allocate) + return 0; + + if (grow_l1_table(bs, l1_index + 1) < 0) { + DPRINTF("Could not grow L1 table"); + return 0; + } + } + + l2_offset = s->l1_table[l1_index]; + if (!l2_offset) { + if (!allocate) + return 0; + + l2_allocate: + old_l2_offset = l2_offset; + /* allocate a new l2 entry */ + l2_offset = alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); + + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; + tmp = cpu_to_be64(l2_offset | QCOW_OFLAG_COPIED); + if (bdrv_pwrite(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), + &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + min_index = l2_cache_new_entry(bs); + l2_table = s->l2_cache + (min_index << s->l2_bits); + + if (old_l2_offset == 0) { + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); + } else { + if (bdrv_pread(s->fd, old_l2_offset, + l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } + if (bdrv_pwrite(s->fd, l2_offset, + l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } else { + if (!(l2_offset & QCOW_OFLAG_COPIED)) { + if (allocate) { + free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); + goto l2_allocate; + } + } else { + l2_offset &= ~QCOW_OFLAG_COPIED; + } + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } + } + l2_table = s->l2_cache + (i << s->l2_bits); + goto found; + } + } + /* not found: load a new entry in the least used one */ + min_index = l2_cache_new_entry(bs); + l2_table = s->l2_cache + (min_index << s->l2_bits); + + if (bdrv_pread(s->fd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + { + DPRINTF("Could not read L2 table"); + return 0; + } + } + s->l2_cache_offsets[min_index] = l2_offset; + s->l2_cache_counts[min_index] = 1; +found: + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + + cluster_offset = be64_to_cpu(l2_table[l2_index]); + if (!cluster_offset) { + if (!allocate) { + return cluster_offset; + } + } else if (!(cluster_offset & QCOW_OFLAG_COPIED)) { + if (!allocate) + return cluster_offset; + /* free the cluster */ + if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + int nb_csectors; + nb_csectors = ((cluster_offset >> s->csize_shift) & + s->csize_mask) + 1; + free_clusters(bs, (cluster_offset & s->cluster_offset_mask) & ~511, + nb_csectors * 512); + } else { + free_clusters(bs, cluster_offset, s->cluster_size); + } + } else { + cluster_offset &= ~QCOW_OFLAG_COPIED; + return cluster_offset; + } + if (allocate == 1) { + /* allocate a new cluster */ + cluster_offset = alloc_clusters(bs, s->cluster_size); + + /* we must initialize the cluster content which won't be + written */ + if ((n_end - n_start) < s->cluster_sectors) { + uint64_t start_sect; + + start_sect = (offset & ~(s->cluster_size - 1)) >> 9; + ret = copy_sectors(bs, start_sect, + cluster_offset, 0, n_start); + if (ret < 0) + return 0; + ret = copy_sectors(bs, start_sect, + cluster_offset, n_end, s->cluster_sectors); + if (ret < 0) + return 0; + } + tmp = cpu_to_be64(cluster_offset | QCOW_OFLAG_COPIED); + } else { + int nb_csectors; + cluster_offset = alloc_bytes(bs, compressed_size); + nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) - + (cluster_offset >> 9); + cluster_offset |= QCOW_OFLAG_COMPRESSED | + ((uint64_t)nb_csectors << s->csize_shift); + /* compressed clusters never have the copied flag */ + tmp = cpu_to_be64(cluster_offset); + } + /* update L2 table */ + l2_table[l2_index] = tmp; + + if (bdrv_pwrite(s->fd, l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + return cluster_offset; +} + +static int qcow_is_allocated(struct disk_driver *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVQcowState *s = bs->private; + int index_in_cluster, n; + uint64_t cluster_offset; + + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + *pnum = n; + return (cluster_offset != 0); +} + +static int decompress_buffer(uint8_t *out_buf, int out_buf_size, + const uint8_t *buf, int buf_size) +{ + z_stream strm1, *strm = &strm1; + int ret, out_len; + + memset(strm, 0, sizeof(*strm)); + + strm->next_in = (uint8_t *)buf; + strm->avail_in = buf_size; + strm->next_out = out_buf; + strm->avail_out = out_buf_size; + + ret = inflateInit2(strm, -12); + if (ret != Z_OK) + return -1; + ret = inflate(strm, Z_FINISH); + out_len = strm->next_out - out_buf; + if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || + out_len != out_buf_size) { + inflateEnd(strm); + return -1; + } + inflateEnd(strm); + return 0; +} + +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) +{ + int ret, csize, nb_csectors, sector_offset; + uint64_t coffset; + + coffset = cluster_offset & s->cluster_offset_mask; + if (s->cluster_cache_offset != coffset) { + nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; + sector_offset = coffset & 511; + csize = nb_csectors * 512 - sector_offset; + ret = bdrv_read(s->fd, coffset >> 9, s->cluster_data, nb_csectors); + if (ret < 0) { + return -1; + } + if (decompress_buffer(s->cluster_cache, s->cluster_size, + s->cluster_data + sector_offset, csize) < 0) { + return -1; + } + s->cluster_cache_offset = coffset; + } + return 0; +} + +/* handle reading after the end of the backing file */ +static int backing_read1(struct disk_driver *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors) +{ + int n1; + BDRVQcowState* s = bs->private; + + if ((sector_num + nb_sectors) <= s->total_sectors) + return nb_sectors; + if (sector_num >= s->total_sectors) + n1 = 0; + else + n1 = s->total_sectors - sector_num; + memset(buf + n1 * 512, 0, 512 * (nb_sectors - n1)); + return n1; +} + +/** + * Reads a number of sectors from the image (synchronous) + */ +static int qcow_read(struct disk_driver *bs, uint64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->private; + int ret, index_in_cluster, n, n1; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + if (!cluster_offset) { + + if (bs->next) { + + /* Read from backing file */ + struct disk_driver *parent = bs->next; + + ret = qcow_sync_read(parent, sector_num, + nb_sectors, (char*) buf, NULL, 0, NULL); + +#if 0 + /* read from the base image */ + n1 = backing_read1(s->backing_hd, sector_num, buf, n); + if (n1 > 0) { + ret = bdrv_read(((BDRVQcowState*) s->backing_hd)->fd, sector_num, buf, n1); + if (ret < 0) { + DPRINTF("read from backing file failed: ret = %d; errno = %d\n", ret, errno); + return -1; + } + } +#endif + } else { + memset(buf, 0, 512 * n); + } + } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + if (decompress_cluster(s, cluster_offset) < 0) { + DPRINTF("read/decompression failed: errno = %d\n", errno); + return -1; + } + memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); + } else { + ret = bdrv_pread(s->fd, cluster_offset + index_in_cluster * 512, buf, n * 512); + if (ret != n * 512) { + DPRINTF("read failed: ret = %d != n * 512 = %d; errno = %d\n", ret, n * 512, errno); + DPRINTF(" cluster_offset = %"PRIx64", index = %d; sector_num = %"PRId64"", cluster_offset, index_in_cluster, sector_num); + return -1; + } + + if (s->crypt_method) { + encrypt_sectors(s, sector_num, buf, buf, n, 0, + &s->aes_decrypt_key); + } + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +/** + * Writes a number of sectors to the image (synchronous) + */ +static int qcow_write(struct disk_driver *bs, uint64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->private; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, + index_in_cluster, + index_in_cluster + n); + if (!cluster_offset) { + DPRINTF("qcow_write: cluster_offset == 0\n"); + DPRINTF(" index = %d; sector_num = %"PRId64"\n", + index_in_cluster, sector_num); + return -1; + } + + if (s->crypt_method) { + encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, + &s->aes_encrypt_key); + ret = bdrv_pwrite(s->fd, cluster_offset + index_in_cluster * 512, + s->cluster_data, n * 512); + } else { + ret = bdrv_pwrite(s->fd, cluster_offset + index_in_cluster * 512, buf, n * 512); + } + if (ret != n * 512) { + DPRINTF("write failed: ret = %d != n * 512 = %d; errno = %d\n", ret, n * 512, errno); + DPRINTF(" cluster_offset = %"PRIx64", index = %d; sector_num = %"PRId64"\n", cluster_offset, index_in_cluster, sector_num); + return -1; + } + + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + s->cluster_cache_offset = -1; /* disable compressed cache */ + return 0; +} + + + +#ifdef USE_AIO + +/* + * QCOW2 specific AIO functions + */ + +static int qcow_queue_read(struct disk_driver *bs, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + BDRVQcowState *s = bs->private; + int i, index_in_cluster, n, ret; + int rsp = 0; + uint64_t cluster_offset; + + /*Check we can get a lock*/ + for (i = 0; i < nb_sectors; i++) + if (!tap_aio_can_lock(&s->async, sector + i)) + return cb(bs, -EBUSY, sector, nb_sectors, id, private); + + while (nb_sectors > 0) { + + cluster_offset = get_cluster_offset(bs, sector << 9, 0, 0, 0, 0); + + index_in_cluster = sector & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + + if (s->async.iocb_free_count == 0 || !tap_aio_lock(&s->async, sector)) + return cb(bs, -EBUSY, sector, nb_sectors, id, private); + + if (!cluster_offset) { + + /* The requested sector is not allocated */ + tap_aio_unlock(&s->async, sector); + ret = cb(bs, BLK_NOT_ALLOCATED, + sector, n, id, private); + if (ret == -EBUSY) { + /* mark remainder of request + * as busy and try again later */ + return cb(bs, -EBUSY, sector + n, + nb_sectors - n, id, private); + } else { + rsp += ret; + } + + } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + + /* sync read for compressed clusters */ + tap_aio_unlock(&s->async, sector); + if (decompress_cluster(s, cluster_offset) < 0) { + rsp += cb(bs, -EIO, sector, nb_sectors, id, private); + goto done; + } + memcpy(buf, s->cluster_cache + index_in_cluster * 512, + 512 * n); + rsp += cb(bs, 0, sector, n, id, private); + + } else { + + /* async read */ + tap_aio_read(&s->async, s->fd, n * 512, + (cluster_offset + index_in_cluster * 512), + buf, cb, id, sector, private); + } + + /* Prepare for next sector to read */ + nb_sectors -= n; + sector += n; + buf += n * 512; + } + +done: + return rsp; + +} + +static int qcow_queue_write(struct disk_driver *bs, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + BDRVQcowState *s = bs->private; + int i, n, index_in_cluster; + uint64_t cluster_offset; + const uint8_t *src_buf; + + + /*Check we can get a lock*/ + for (i = 0; i < nb_sectors; i++) + if (!tap_aio_can_lock(&s->async, sector + i)) + return cb(bs, -EBUSY, sector, nb_sectors, id, private); + + + while (nb_sectors > 0) { + + index_in_cluster = sector & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + + if (s->async.iocb_free_count == 0 || !tap_aio_lock(&s->async, sector)) + return cb(bs, -EBUSY, sector, nb_sectors, id, private); + + + cluster_offset = get_cluster_offset(bs, sector << 9, 1, 0, + index_in_cluster, + index_in_cluster+n); + + if (!cluster_offset) { + DPRINTF("Ooops, no write cluster offset!\n"); + tap_aio_unlock(&s->async, sector); + return cb(bs, -EIO, sector, nb_sectors, id, private); + } + + + // TODO Encryption + + tap_aio_write(&s->async, s->fd, n * 512, + (cluster_offset + index_in_cluster*512), + buf, cb, id, sector, private); + + /* Prepare for next sector to write */ + nb_sectors -= n; + sector += n; + buf += n * 512; + } + + + s->cluster_cache_offset = -1; /* disable compressed cache */ + + return 0; +} + + +#endif /* USE_AIO */ + + +static int qcow_close(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + +#ifdef USE_AIO + io_destroy(s->async.aio_ctx.aio_ctx); + tap_aio_free(&s->async); +#else + close(s->poll_pipe[0]); + close(s->poll_pipe[1]); +#endif + + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + qemu_free(s->cluster_cache); + qemu_free(s->cluster_data); + refcount_close(bs); + return close(s->fd); +} + +/* XXX: use std qcow open function ? */ +typedef struct QCowCreateState { + int cluster_size; + int cluster_bits; + uint16_t *refcount_block; + uint64_t *refcount_table; + int64_t l1_table_offset; + int64_t refcount_table_offset; + int64_t refcount_block_offset; +} QCowCreateState; + +static void create_refcount_update(QCowCreateState *s, + int64_t offset, int64_t size) +{ + int refcount; + int64_t start, last, cluster_offset; + uint16_t *p; + + start = offset & ~(s->cluster_size - 1); + last = (offset + size - 1) & ~(s->cluster_size - 1); + for(cluster_offset = start; cluster_offset <= last; + cluster_offset += s->cluster_size) { + p = &s->refcount_block[cluster_offset >> s->cluster_bits]; + refcount = be16_to_cpu(*p); + refcount++; + *p = cpu_to_be16(refcount); + } +} + +static int qcow_submit(struct disk_driver *bs) +{ + struct BDRVQcowState *s = (struct BDRVQcowState*) bs->private; + + fsync(s->fd); + return tap_aio_submit(&s->async); +} + + +/*********************************************************/ +/* snapshot support */ + + +static void qcow_free_snapshots(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + int i; + + for(i = 0; i < s->nb_snapshots; i++) { + qemu_free(s->snapshots[i].name); + qemu_free(s->snapshots[i].id_str); + } + qemu_free(s->snapshots); + s->snapshots = NULL; + s->nb_snapshots = 0; +} + +static int qcow_read_snapshots(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + QCowSnapshotHeader h; + QCowSnapshot *sn; + int i, id_str_size, name_size; + int64_t offset; + uint32_t extra_data_size; + + offset = s->snapshots_offset; + s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); + if (!s->snapshots) + goto fail; + for(i = 0; i < s->nb_snapshots; i++) { + offset = align_offset(offset, 8); + if (bdrv_pread(s->fd, offset, &h, sizeof(h)) != sizeof(h)) + goto fail; + offset += sizeof(h); + sn = s->snapshots + i; + sn->l1_table_offset = be64_to_cpu(h.l1_table_offset); + sn->l1_size = be32_to_cpu(h.l1_size); + sn->vm_state_size = be32_to_cpu(h.vm_state_size); + sn->date_sec = be32_to_cpu(h.date_sec); + sn->date_nsec = be32_to_cpu(h.date_nsec); + sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec); + extra_data_size = be32_to_cpu(h.extra_data_size); + + id_str_size = be16_to_cpu(h.id_str_size); + name_size = be16_to_cpu(h.name_size); + + offset += extra_data_size; + + sn->id_str = qemu_malloc(id_str_size + 1); + if (!sn->id_str) + goto fail; + if (bdrv_pread(s->fd, offset, sn->id_str, id_str_size) != id_str_size) + goto fail; + offset += id_str_size; + sn->id_str[id_str_size] = '\0'; + + sn->name = qemu_malloc(name_size + 1); + if (!sn->name) + goto fail; + if (bdrv_pread(s->fd, offset, sn->name, name_size) != name_size) + goto fail; + offset += name_size; + sn->name[name_size] = '\0'; + } + s->snapshots_size = offset - s->snapshots_offset; + return 0; +fail: + qcow_free_snapshots(bs); + return -1; +} + + +/*********************************************************/ +/* refcount handling */ + +static int refcount_init(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + int ret, refcount_table_size2, i; + + s->refcount_block_cache = qemu_malloc(s->cluster_size); + if (!s->refcount_block_cache) + goto fail; + refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); + s->refcount_table = qemu_malloc(refcount_table_size2); + if (!s->refcount_table) + goto fail; + if (s->refcount_table_size > 0) { + ret = bdrv_pread(s->fd, s->refcount_table_offset, + s->refcount_table, refcount_table_size2); + if (ret != refcount_table_size2) + goto fail; + for(i = 0; i < s->refcount_table_size; i++) + be64_to_cpus(&s->refcount_table[i]); + } + return 0; + fail: + return -ENOMEM; +} + +static void refcount_close(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + qemu_free(s->refcount_block_cache); + qemu_free(s->refcount_table); +} + + +static int load_refcount_block(struct disk_driver *bs, + int64_t refcount_block_offset) +{ + BDRVQcowState *s = bs->private; + int ret; + ret = bdrv_pread(s->fd, refcount_block_offset, s->refcount_block_cache, + s->cluster_size); + if (ret != s->cluster_size) + return -EIO; + s->refcount_block_cache_offset = refcount_block_offset; + return 0; +} + +static int get_refcount(struct disk_driver *bs, int64_t cluster_index) +{ + BDRVQcowState *s = bs->private; + int refcount_table_index, block_index; + int64_t refcount_block_offset; + + refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); + if (refcount_table_index >= s->refcount_table_size) + return 0; + refcount_block_offset = s->refcount_table[refcount_table_index]; + if (!refcount_block_offset) + return 0; + if (refcount_block_offset != s->refcount_block_cache_offset) { + /* better than nothing: return allocated if read error */ + if (load_refcount_block(bs, refcount_block_offset) < 0) + return 1; + } + block_index = cluster_index & + ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); + return be16_to_cpu(s->refcount_block_cache[block_index]); +} + +/* return < 0 if error */ +static int64_t alloc_clusters_noref(struct disk_driver *bs, int64_t size) +{ + BDRVQcowState *s = bs->private; + int i, nb_clusters; + + nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits; + for(;;) { + if (get_refcount(bs, s->free_cluster_index) == 0) { + s->free_cluster_index++; + for(i = 1; i < nb_clusters; i++) { + if (get_refcount(bs, s->free_cluster_index) != 0) + goto not_found; + s->free_cluster_index++; + } + +#ifdef DEBUG_ALLOC2 + DPRINTF("alloc_clusters: size=%ld -> %ld\n", + size, + (s->free_cluster_index - nb_clusters) << s->cluster_bits); +#endif + + return (s->free_cluster_index - nb_clusters) << s->cluster_bits; + } else { + not_found: + s->free_cluster_index++; + } + } +} + +static int64_t alloc_clusters(struct disk_driver *bs, int64_t size) +{ + int64_t offset; + + offset = alloc_clusters_noref(bs, size); + update_refcount(bs, offset, size, 1); + return offset; +} + +/* only used to allocate compressed sectors. We try to allocate + contiguous sectors. size must be <= cluster_size */ +static int64_t alloc_bytes(struct disk_driver *bs, int size) +{ + BDRVQcowState *s = bs->private; + int64_t offset, cluster_offset; + int free_in_cluster; + + assert(size > 0 && size <= s->cluster_size); + if (s->free_byte_offset == 0) { + s->free_byte_offset = alloc_clusters(bs, s->cluster_size); + } +redo: + free_in_cluster = s->cluster_size - + (s->free_byte_offset & (s->cluster_size - 1)); + if (size <= free_in_cluster) { + /* enough space in current cluster */ + offset = s->free_byte_offset; + s->free_byte_offset += size; + free_in_cluster -= size; + if (free_in_cluster == 0) + s->free_byte_offset = 0; + if ((offset & (s->cluster_size - 1)) != 0) + update_cluster_refcount(bs, offset >> s->cluster_bits, 1); + } else { + offset = alloc_clusters(bs, s->cluster_size); + cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1); + if ((cluster_offset + s->cluster_size) == offset) { + /* we are lucky: contiguous data */ + offset = s->free_byte_offset; + update_cluster_refcount(bs, offset >> s->cluster_bits, 1); + s->free_byte_offset += size; + } else { + s->free_byte_offset = offset; + goto redo; + } + } + return offset; +} + +static void free_clusters(struct disk_driver *bs, + int64_t offset, int64_t size) +{ + update_refcount(bs, offset, size, -1); +} + +static int grow_refcount_table(struct disk_driver *bs, int min_size) +{ + BDRVQcowState *s = bs->private; + int new_table_size, new_table_size2, refcount_table_clusters, i, ret; + uint64_t *new_table; + int64_t table_offset; + uint64_t data64; + uint32_t data32; + int old_table_size; + int64_t old_table_offset; + + if (min_size <= s->refcount_table_size) + return 0; + + /* compute new table size */ + refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); + for(;;) { + if (refcount_table_clusters == 0) { + refcount_table_clusters = 1; + } else { + refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2; + } + new_table_size = refcount_table_clusters << (s->cluster_bits - 3); + if (min_size <= new_table_size) + break; + } + +#ifdef DEBUG_ALLOC2 + printf("grow_refcount_table from %d to %d\n", + s->refcount_table_size, + new_table_size); +#endif + new_table_size2 = new_table_size * sizeof(uint64_t); + new_table = qemu_mallocz(new_table_size2); + if (!new_table) + return -ENOMEM; + memcpy(new_table, s->refcount_table, + s->refcount_table_size * sizeof(uint64_t)); + for(i = 0; i < s->refcount_table_size; i++) + cpu_to_be64s(&new_table[i]); + /* Note: we cannot update the refcount now to avoid recursion */ + table_offset = alloc_clusters_noref(bs, new_table_size2); + ret = bdrv_pwrite(s->fd, table_offset, new_table, new_table_size2); + if (ret != new_table_size2) + goto fail; + for(i = 0; i < s->refcount_table_size; i++) + be64_to_cpus(&new_table[i]); + + data64 = cpu_to_be64(table_offset); + if (bdrv_pwrite(s->fd, offsetof(QCowHeader, refcount_table_offset), + &data64, sizeof(data64)) != sizeof(data64)) + goto fail; + data32 = cpu_to_be32(refcount_table_clusters); + if (bdrv_pwrite(s->fd, offsetof(QCowHeader, refcount_table_clusters), + &data32, sizeof(data32)) != sizeof(data32)) + goto fail; + qemu_free(s->refcount_table); + old_table_offset = s->refcount_table_offset; + old_table_size = s->refcount_table_size; + s->refcount_table = new_table; + s->refcount_table_size = new_table_size; + s->refcount_table_offset = table_offset; + + update_refcount(bs, table_offset, new_table_size2, 1); + free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t)); + return 0; + fail: + free_clusters(bs, table_offset, new_table_size2); + qemu_free(new_table); + return -EIO; +} + +/* addend must be 1 or -1 */ +/* XXX: cache several refcount block clusters ? */ +static int update_cluster_refcount(struct disk_driver *bs, + int64_t cluster_index, + int addend) +{ + BDRVQcowState *s = bs->private; + int64_t offset, refcount_block_offset; + int ret, refcount_table_index, block_index, refcount; + uint64_t data64; + + refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); + if (refcount_table_index >= s->refcount_table_size) { + if (addend < 0) + return -EINVAL; + ret = grow_refcount_table(bs, refcount_table_index + 1); + if (ret < 0) + return ret; + } + refcount_block_offset = s->refcount_table[refcount_table_index]; + if (!refcount_block_offset) { + if (addend < 0) + return -EINVAL; + /* create a new refcount block */ + /* Note: we cannot update the refcount now to avoid recursion */ + offset = alloc_clusters_noref(bs, s->cluster_size); + memset(s->refcount_block_cache, 0, s->cluster_size); + ret = bdrv_pwrite(s->fd, offset, s->refcount_block_cache, s->cluster_size); + if (ret != s->cluster_size) + return -EINVAL; + s->refcount_table[refcount_table_index] = offset; + data64 = cpu_to_be64(offset); + ret = bdrv_pwrite(s->fd, s->refcount_table_offset + + refcount_table_index * sizeof(uint64_t), + &data64, sizeof(data64)); + if (ret != sizeof(data64)) + return -EINVAL; + + refcount_block_offset = offset; + s->refcount_block_cache_offset = offset; + update_refcount(bs, offset, s->cluster_size, 1); + } else { + if (refcount_block_offset != s->refcount_block_cache_offset) { + if (load_refcount_block(bs, refcount_block_offset) < 0) + return -EIO; + } + } + /* we can update the count and save it */ + block_index = cluster_index & + ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); + refcount = be16_to_cpu(s->refcount_block_cache[block_index]); + refcount += addend; + if (refcount < 0 || refcount > 0xffff) + return -EINVAL; + if (refcount == 0 && cluster_index < s->free_cluster_index) { + s->free_cluster_index = cluster_index; + } + s->refcount_block_cache[block_index] = cpu_to_be16(refcount); + if (bdrv_pwrite(s->fd, + refcount_block_offset + (block_index << REFCOUNT_SHIFT), + &s->refcount_block_cache[block_index], 2) != 2) + return -EIO; + return refcount; +} + +static void update_refcount(struct disk_driver *bs, + int64_t offset, int64_t length, + int addend) +{ + BDRVQcowState *s = bs->private; + int64_t start, last, cluster_offset; + +#ifdef DEBUG_ALLOC2 + printf("update_refcount: offset=%lld size=%lld addend=%d\n", + offset, length, addend); +#endif + if (length <= 0) + return; + start = offset & ~(s->cluster_size - 1); + last = (offset + length - 1) & ~(s->cluster_size - 1); + for(cluster_offset = start; cluster_offset <= last; + cluster_offset += s->cluster_size) { + update_cluster_refcount(bs, cluster_offset >> s->cluster_bits, addend); + } +} + +#ifdef DEBUG_ALLOC +static void inc_refcounts(struct disk_driver *bs, + uint16_t *refcount_table, + int refcount_table_size, + int64_t offset, int64_t size) +{ + BDRVQcowState *s = bs->private; + int64_t start, last, cluster_offset; + int k; + + if (size <= 0) + return; + + start = offset & ~(s->cluster_size - 1); + last = (offset + size - 1) & ~(s->cluster_size - 1); + for(cluster_offset = start; cluster_offset <= last; + cluster_offset += s->cluster_size) { + k = cluster_offset >> s->cluster_bits; + if (k < 0 || k >= refcount_table_size) { + printf("ERROR: invalid cluster offset=0x%llx\n", cluster_offset); + } else { + if (++refcount_table[k] == 0) { + printf("ERROR: overflow cluster offset=0x%llx\n", cluster_offset); + } + } + } +} + +static int check_refcounts_l1(struct disk_driver *bs, + uint16_t *refcount_table, + int refcount_table_size, + int64_t l1_table_offset, int l1_size, + int check_copied) +{ + BDRVQcowState *s = bs->private; + uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; + int l2_size, i, j, nb_csectors, refcount; + + l2_table = NULL; + l1_size2 = l1_size * sizeof(uint64_t); + + inc_refcounts(bs, refcount_table, refcount_table_size, + l1_table_offset, l1_size2); + + l1_table = qemu_malloc(l1_size2); + if (!l1_table) + goto fail; + if (bdrv_pread(s->fd, l1_table_offset, + l1_table, l1_size2) != l1_size2) + goto fail; + for(i = 0;i < l1_size; i++) + be64_to_cpus(&l1_table[i]); + + l2_size = s->l2_size * sizeof(uint64_t); + l2_table = qemu_malloc(l2_size); + if (!l2_table) + goto fail; + for(i = 0; i < l1_size; i++) { + l2_offset = l1_table[i]; + if (l2_offset) { + if (check_copied) { + refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); + if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { + printf("ERROR OFLAG_COPIED: l2_offset=%llx refcount=%d\n", + l2_offset, refcount); + } + } + l2_offset &= ~QCOW_OFLAG_COPIED; + if (bdrv_pread(s->fd, l2_offset, l2_table, l2_size) != l2_size) + goto fail; + for(j = 0; j < s->l2_size; j++) { + offset = be64_to_cpu(l2_table[j]); + if (offset != 0) { + if (offset & QCOW_OFLAG_COMPRESSED) { + if (offset & QCOW_OFLAG_COPIED) { + printf("ERROR: cluster %lld: copied flag must never be set for compressed clusters\n", + offset >> s->cluster_bits); + offset &= ~QCOW_OFLAG_COPIED; + } + nb_csectors = ((offset >> s->csize_shift) & + s->csize_mask) + 1; + offset &= s->cluster_offset_mask; + inc_refcounts(bs, refcount_table, + refcount_table_size, + offset & ~511, nb_csectors * 512); + } else { + if (check_copied) { + refcount = get_refcount(bs, (offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); + if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { + printf("ERROR OFLAG_COPIED: offset=%llx refcount=%d\n", + offset, refcount); + } + } + offset &= ~QCOW_OFLAG_COPIED; + inc_refcounts(bs, refcount_table, + refcount_table_size, + offset, s->cluster_size); + } + } + } + inc_refcounts(bs, refcount_table, + refcount_table_size, + l2_offset, + s->cluster_size); + } + } + qemu_free(l1_table); + qemu_free(l2_table); + return 0; + fail: + printf("ERROR: I/O error in check_refcounts_l1\n"); + qemu_free(l1_table); + qemu_free(l2_table); + return -EIO; +} + +static void check_refcounts(struct disk_driver *bs) +{ + BDRVQcowState *s = bs->private; + int64_t size; + int nb_clusters, refcount1, refcount2, i; + QCowSnapshot *sn; + uint16_t *refcount_table; + + size = bdrv_getlength(s->fd); + nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits; + refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); + + /* header */ + inc_refcounts(bs, refcount_table, nb_clusters, + 0, s->cluster_size); + + check_refcounts_l1(bs, refcount_table, nb_clusters, + s->l1_table_offset, s->l1_size, 1); + + /* snapshots */ + for(i = 0; i < s->nb_snapshots; i++) { + sn = s->snapshots + i; + check_refcounts_l1(bs, refcount_table, nb_clusters, + sn->l1_table_offset, sn->l1_size, 0); + } + inc_refcounts(bs, refcount_table, nb_clusters, + s->snapshots_offset, s->snapshots_size); + + /* refcount data */ + inc_refcounts(bs, refcount_table, nb_clusters, + s->refcount_table_offset, + s->refcount_table_size * sizeof(uint64_t)); + + for(i = 0; i < s->refcount_table_size; i++) { + int64_t offset; + offset = s->refcount_table[i]; + if (offset != 0) { + inc_refcounts(bs, refcount_table, nb_clusters, + offset, s->cluster_size); + } + } + + /* compare ref counts */ + for(i = 0; i < nb_clusters; i++) { + refcount1 = get_refcount(bs, i); + refcount2 = refcount_table[i]; + if (refcount1 != refcount2) + printf("ERROR cluster %d refcount=%d reference=%d\n", + i, refcount1, refcount2); + } + + qemu_free(refcount_table); +} +#endif + + +/** + * Wrapper for synchronous read. + * This function is called when not using AIO at all (#undef USE_AIO) or + * for accessing the backing file. + */ +static int qcow_sync_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *prv) +{ + int ret = qcow_read(dd, sector, (uint8_t*) buf, nb_sectors); + + if (cb != NULL) { + return cb(dd, (ret < 0) ? ret : 0, sector, nb_sectors, id, prv); + } else { + return ret; + } +} + +#ifndef USE_AIO +/** + * Wrapper for synchronous write + */ +static int qcow_sync_write(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *prv) +{ + int ret = qcow_write(dd, sector, (uint8_t*) buf, nb_sectors); + + return cb(dd, (ret < 0) ? ret : 0, sector, nb_sectors, id, prv); +} +#endif + + + +#ifndef USE_AIO + +static int qcow_do_callbacks(struct disk_driver *dd, int sid) +{ + return 1; +} + +#else + +static int qcow_do_callbacks(struct disk_driver *dd, int sid) +{ + int ret, i, nr_events, rsp = 0,*ptr; + struct io_event *ep; + struct BDRVQcowState *prv = (struct BDRVQcowState*)dd->private; + + if (sid > MAX_IOFD) return 1; + + nr_events = tap_aio_get_events(&prv->async.aio_ctx); + +repeat: + for (ep = prv->async.aio_events, i = nr_events; i-- > 0; ep++) { + struct iocb *io = ep->obj; + struct pending_aio *pio; + + pio = &prv->async.pending_aio[(long)io->data]; + + tap_aio_unlock(&prv->async, pio->sector); + + if (prv->crypt_method) + encrypt_sectors(prv, pio->sector, + (unsigned char *)pio->buf, + (unsigned char *)pio->buf, + pio->nb_sectors, 0, + &prv->aes_decrypt_key); + + rsp += pio->cb(dd, ep->res == io->u.c.nbytes ? 0 : 1, + pio->sector, pio->nb_sectors, + pio->id, pio->private); + + prv->async.iocb_free[prv->async.iocb_free_count++] = io; + } + + if (nr_events) { + nr_events = tap_aio_more_events(&prv->async.aio_ctx); + goto repeat; + } + + tap_aio_continue(&prv->async.aio_ctx); + + return rsp; +} + +#endif + +/** + * @return + * 0 if parent id successfully retrieved; + * TD_NO_PARENT if no parent exists; + * -errno on error + */ +static int qcow_get_parent_id(struct disk_driver *dd, struct disk_id *id) +{ + struct BDRVQcowState* s = (struct BDRVQcowState*) dd->private; + + if (s->backing_file[0] == '\0') + return TD_NO_PARENT; + + id->name = strdup(s->backing_file); + id->drivertype = DISK_TYPE_QCOW2; + + return 0; +} + +static int qcow_validate_parent(struct disk_driver *child, + struct disk_driver *parent, td_flag_t flags) +{ + struct BDRVQcowState *cs = (struct BDRVQcowState*) child->private; + struct BDRVQcowState *ps = (struct BDRVQcowState*) parent->private; + + if (ps->total_sectors != cs->total_sectors) { + DPRINTF("qcow_validate_parent(): %#"PRIx64" != %#"PRIx64"\n", + ps->total_sectors, cs->total_sectors); + return -EINVAL; + } + + return 0; +} + +struct tap_disk tapdisk_qcow2 = { + "qcow2", + sizeof(BDRVQcowState), + qcow_open, +#ifdef USE_AIO + qcow_queue_read, + qcow_queue_write, +#else + qcow_sync_read, + qcow_sync_write, +#endif + qcow_submit, + qcow_close, + qcow_do_callbacks, + qcow_get_parent_id, + qcow_validate_parent +}; diff --git a/tools/blktap/drivers/block-ram.c b/tools/blktap/drivers/block-ram.c new file mode 100644 index 0000000..836a68e --- /dev/null +++ b/tools/blktap/drivers/block-ram.c @@ -0,0 +1,295 @@ +/* block-ram.c + * + * Fast Ramdisk implementation. + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tapdisk.h" +#include "blk.h" + +#define MAX_DISK_SIZE 1024000 /*500MB disk limit*/ + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +char *img; +long int disksector_size; +long int disksize; +long int diskinfo; +static int connections = 0; + +struct tdram_state { + int fd; + int poll_pipe[2]; /* dummy fd for polling on */ +}; + +/*Get Image size, secsize*/ +static int get_image_info(struct td_state *s, int fd) +{ + int ret; + long size; + unsigned long total_size; + struct statvfs statBuf; + struct stat stat; + + ret = fstat(fd, &stat); + if (ret != 0) { + DPRINTF("ERROR: fstat failed, Couldn't stat image"); + return -EINVAL; + } + + if (S_ISBLK(stat.st_mode)) { + /*Accessing block device directly*/ + if (blk_getimagesize(fd, &s->size) != 0) + return -EINVAL; + + DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + + /*Get the sector size*/ + if (blk_getsectorsize(fd, &s->sector_size) != 0) + s->sector_size = DEFAULT_SECTOR_SIZE; + + } else { + /*Local file? try fstat instead*/ + s->size = (stat.st_size >> SECTOR_SHIFT); + s->sector_size = DEFAULT_SECTOR_SIZE; + DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + } + + if (s->size == 0) { + s->size =((uint64_t) MAX_DISK_SIZE); + s->sector_size = DEFAULT_SECTOR_SIZE; + } + s->info = 0; + + /*Store variables locally*/ + disksector_size = s->sector_size; + disksize = s->size; + diskinfo = s->info; + DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n", + s->sector_size); + + return 0; +} + +static inline void init_fds(struct disk_driver *dd) +{ + int i; + struct tdram_state *prv = (struct tdram_state *)dd->private; + + for(i =0 ; i < MAX_IOFD; i++) + dd->io_fd[i] = 0; + + dd->io_fd[0] = prv->poll_pipe[0]; +} + +/* Open the disk file and initialize ram state. */ +static int tdram_open (struct disk_driver *dd, const char *name, td_flag_t flags) +{ + char *p; + uint64_t size; + int i, fd, ret = 0, count = 0, o_flags; + struct td_state *s = dd->td_state; + struct tdram_state *prv = (struct tdram_state *)dd->private; + + connections++; + + /* set up a pipe so that we can hand back a poll fd that won't fire.*/ + ret = pipe(prv->poll_pipe); + if (ret != 0) + return (0 - errno); + + if (connections > 1) { + s->sector_size = disksector_size; + s->size = disksize; + s->info = diskinfo; + DPRINTF("Image already open, returning parameters:\n"); + DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n", + s->sector_size); + + prv->fd = -1; + goto done; + } + + /* Open the file */ + o_flags = O_DIRECT | O_LARGEFILE | + ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); + fd = open(name, o_flags); + + if ((fd == -1) && (errno == EINVAL)) { + + /* Maybe O_DIRECT isn't supported. */ + o_flags &= ~O_DIRECT; + fd = open(name, o_flags); + if (fd != -1) DPRINTF("WARNING: Accessing image without" + "O_DIRECT! (%s)\n", name); + + } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name); + + if (fd == -1) { + DPRINTF("Unable to open [%s]!\n",name); + ret = 0 - errno; + goto done; + } + + prv->fd = fd; + + ret = get_image_info(s, fd); + size = MAX_DISK_SIZE; + + if (s->size > size) { + DPRINTF("Disk exceeds limit, must be less than [%d]MB", + (MAX_DISK_SIZE<>20); + return -ENOMEM; + } + + /*Read the image into memory*/ + p = img = malloc(s->size << SECTOR_SHIFT); + if (img == NULL) { + DPRINTF("Mem malloc failed\n"); + return -1; + } + DPRINTF("Reading %llu bytes.......",(long long unsigned)s->size << SECTOR_SHIFT); + + for (i = 0; i < s->size; i++) { + ret = read(prv->fd, p, s->sector_size); + if (ret != s->sector_size) { + ret = 0 - errno; + break; + } else { + count += ret; + p = img + count; + } + } + DPRINTF("[%d]\n",count); + if (count != s->size << SECTOR_SHIFT) { + ret = -1; + } else { + ret = 0; + } + + init_fds(dd); +done: + return ret; +} + +static int tdram_queue_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct td_state *s = dd->td_state; + struct tdram_state *prv = (struct tdram_state *)dd->private; + int size = nb_sectors * s->sector_size; + uint64_t offset = sector * (uint64_t)s->sector_size; + + memcpy(buf, img + offset, size); + + return cb(dd, 0, sector, nb_sectors, id, private); +} + +static int tdram_queue_write(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct td_state *s = dd->td_state; + struct tdram_state *prv = (struct tdram_state *)dd->private; + int size = nb_sectors * s->sector_size; + uint64_t offset = sector * (uint64_t)s->sector_size; + + /* We assume that write access is controlled + * at a higher level for multiple disks */ + memcpy(img + offset, buf, size); + + return cb(dd, 0, sector, nb_sectors, id, private); +} + +static int tdram_submit(struct disk_driver *dd) +{ + return 0; +} + +static int tdram_close(struct disk_driver *dd) +{ + struct tdram_state *prv = (struct tdram_state *)dd->private; + + connections--; + + return 0; +} + +static int tdram_do_callbacks(struct disk_driver *dd, int sid) +{ + /* always ask for a kick */ + return 1; +} + +static int tdram_get_parent_id(struct disk_driver *dd, struct disk_id *id) +{ + return TD_NO_PARENT; +} + +static int tdram_validate_parent(struct disk_driver *dd, + struct disk_driver *parent, td_flag_t flags) +{ + return -EINVAL; +} + +struct tap_disk tapdisk_ram = { + .disk_type = "tapdisk_ram", + .private_data_size = sizeof(struct tdram_state), + .td_open = tdram_open, + .td_queue_read = tdram_queue_read, + .td_queue_write = tdram_queue_write, + .td_submit = tdram_submit, + .td_close = tdram_close, + .td_do_callbacks = tdram_do_callbacks, + .td_get_parent_id = tdram_get_parent_id, + .td_validate_parent = tdram_validate_parent +}; diff --git a/tools/blktap/drivers/block-sync.c b/tools/blktap/drivers/block-sync.c new file mode 100644 index 0000000..dde4538 --- /dev/null +++ b/tools/blktap/drivers/block-sync.c @@ -0,0 +1,242 @@ +/* block-sync.c + * + * simple slow synchronous raw disk implementation. + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "tapdisk.h" +#include "blk.h" + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +struct tdsync_state { + int fd; + int poll_pipe[2]; /* dummy fd for polling on */ +}; + +/*Get Image size, secsize*/ +static int get_image_info(struct td_state *s, int fd) +{ + int ret; + long size; + unsigned long total_size; + struct statvfs statBuf; + struct stat stat; + + ret = fstat(fd, &stat); + if (ret != 0) { + DPRINTF("ERROR: fstat failed, Couldn't stat image"); + return -EINVAL; + } + + if (S_ISBLK(stat.st_mode)) { + /*Accessing block device directly*/ + if (blk_getimagesize(fd, &s->size) != 0) + return -EINVAL; + + DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + + /*Get the sector size*/ + if (blk_getsectorsize(fd, &s->sector_size) != 0) + s->sector_size = DEFAULT_SECTOR_SIZE; + + } else { + /*Local file? try fstat instead*/ + s->size = (stat.st_size >> SECTOR_SHIFT); + s->sector_size = DEFAULT_SECTOR_SIZE; + DPRINTF("Image size: \n\tpre sector_shift [%lluu]\n\tpost " + "sector_shift [%lluu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + } + + if (s->size == 0) + return -EINVAL; + + s->info = 0; + + return 0; +} + +static inline void init_fds(struct disk_driver *dd) +{ + int i; + struct tdsync_state *prv = (struct tdsync_state *)dd->private; + + for(i = 0; i < MAX_IOFD; i++) + dd->io_fd[i] = 0; + + dd->io_fd[0] = prv->poll_pipe[0]; +} + +/* Open the disk file and initialize aio state. */ +static int tdsync_open (struct disk_driver *dd, const char *name, td_flag_t flags) +{ + int i, fd, ret = 0, o_flags; + struct td_state *s = dd->td_state; + struct tdsync_state *prv = (struct tdsync_state *)dd->private; + + /* set up a pipe so that we can hand back a poll fd that won't fire.*/ + ret = pipe(prv->poll_pipe); + if (ret != 0) + return (0 - errno); + + /* Open the file */ + o_flags = O_DIRECT | O_LARGEFILE | + ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); + fd = open(name, o_flags); + + if ( (fd == -1) && (errno == EINVAL) ) { + + /* Maybe O_DIRECT isn't supported. */ + o_flags &= ~O_DIRECT; + fd = open(name, o_flags); + if (fd != -1) DPRINTF("WARNING: Accessing image without" + "O_DIRECT! (%s)\n", name); + + } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name); + + if (fd == -1) { + DPRINTF("Unable to open [%s]!\n",name); + ret = 0 - errno; + goto done; + } + + prv->fd = fd; + + init_fds(dd); + ret = get_image_info(s, fd); +done: + return ret; +} + +static int tdsync_queue_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct td_state *s = dd->td_state; + struct tdsync_state *prv = (struct tdsync_state *)dd->private; + int size = nb_sectors * s->sector_size; + uint64_t offset = sector * (uint64_t)s->sector_size; + int ret; + + ret = lseek(prv->fd, offset, SEEK_SET); + if (ret != (off_t)-1) { + ret = read(prv->fd, buf, size); + if (ret != size) { + ret = 0 - errno; + } else { + ret = 1; + } + } else ret = 0 - errno; + + return cb(dd, (ret < 0) ? ret: 0, sector, nb_sectors, id, private); +} + +static int tdsync_queue_write(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct td_state *s = dd->td_state; + struct tdsync_state *prv = (struct tdsync_state *)dd->private; + int size = nb_sectors * s->sector_size; + uint64_t offset = sector * (uint64_t)s->sector_size; + int ret = 0; + + ret = lseek(prv->fd, offset, SEEK_SET); + if (ret != (off_t)-1) { + ret = write(prv->fd, buf, size); + if (ret != size) { + ret = 0 - errno; + } else { + ret = 1; + } + } else ret = 0 - errno; + + return cb(dd, (ret < 0) ? ret : 0, sector, nb_sectors, id, private); +} + +static int tdsync_submit(struct disk_driver *dd) +{ + return 0; +} + +static int tdsync_close(struct disk_driver *dd) +{ + struct tdsync_state *prv = (struct tdsync_state *)dd->private; + + close(prv->fd); + close(prv->poll_pipe[0]); + close(prv->poll_pipe[1]); + + return 0; +} + +static int tdsync_do_callbacks(struct disk_driver *dd, int sid) +{ + /* always ask for a kick */ + return 1; +} + +static int tdsync_get_parent_id(struct disk_driver *dd, struct disk_id *id) +{ + return TD_NO_PARENT; +} + +static int tdsync_validate_parent(struct disk_driver *dd, + struct disk_driver *parent, td_flag_t flags) +{ + return -EINVAL; +} + +struct tap_disk tapdisk_sync = { + .disk_type = "tapdisk_sync", + .private_data_size = sizeof(struct tdsync_state), + .td_open = tdsync_open, + .td_queue_read = tdsync_queue_read, + .td_queue_write = tdsync_queue_write, + .td_submit = tdsync_submit, + .td_close = tdsync_close, + .td_do_callbacks = tdsync_do_callbacks, + .td_get_parent_id = tdsync_get_parent_id, + .td_validate_parent = tdsync_validate_parent +}; diff --git a/tools/blktap/drivers/block-vmdk.c b/tools/blktap/drivers/block-vmdk.c new file mode 100644 index 0000000..4d16965 --- /dev/null +++ b/tools/blktap/drivers/block-vmdk.c @@ -0,0 +1,428 @@ +/* block-vmdk.c + * + * VMware Disk format implementation. + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + * This is largely the same as the vmdk driver in Qemu, I've just twisted it + * to match our interfaces. The original (BSDish) Copyright message appears + * below: + */ + +/* + * Block driver for the VMDK format + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2005 Filip Navara + * + * 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 "tapdisk.h" +#include "bswap.h" + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#define safer_free(_x) \ + do { \ + if (NULL != _x) { \ + free(_x); \ + (_x) = NULL; \ + } \ + } while (0) ; + +#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') +#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') + +typedef struct { + uint32_t version; + uint32_t flags; + uint32_t disk_sectors; + uint32_t granularity; + uint32_t l1dir_offset; + uint32_t l1dir_size; + uint32_t file_sectors; + uint32_t cylinders; + uint32_t heads; + uint32_t sectors_per_track; +} VMDK3Header; + +typedef struct { + uint32_t version; + uint32_t flags; + int64_t capacity; + int64_t granularity; + int64_t desc_offset; + int64_t desc_size; + int32_t num_gtes_per_gte; + int64_t rgd_offset; + int64_t gd_offset; + int64_t grain_offset; + char filler[1]; + char check_bytes[4]; +} __attribute__((packed)) VMDK4Header; + +#define L2_CACHE_SIZE 16 + +struct tdvmdk_state { + int fd; + int poll_pipe[2]; /* dummy fd for polling on */ + + unsigned int l1_size; + int64_t l1_table_offset; + int64_t l1_backup_table_offset; + uint32_t l1_entry_sectors; + unsigned int l2_size; + + uint32_t *l1_table; + uint32_t *l1_backup_table; + uint32_t *l2_cache; + uint32_t l2_cache_offsets[L2_CACHE_SIZE]; + uint32_t l2_cache_counts[L2_CACHE_SIZE]; + + unsigned int cluster_sectors; +}; + +static inline void init_fds(struct disk_driver *dd) +{ + int i; + struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private; + + for (i = 0; i < MAX_IOFD; i++) + dd->io_fd[i] = 0; + + dd->io_fd[0] = prv->poll_pipe[0]; +} + +/* Open the disk file and initialize aio state. */ +static int tdvmdk_open (struct disk_driver *dd, + const char *name, td_flag_t flags) +{ + int ret, fd; + int l1_size, i, o_flags; + uint32_t magic; + struct td_state *s = dd->td_state; + struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private; + + /* set up a pipe so that we can hand back a poll fd that won't fire.*/ + ret = pipe(prv->poll_pipe); + if (ret != 0) + return -1; + + /* Open the file */ + o_flags = O_DIRECT | O_LARGEFILE | + ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); + fd = open(name, o_flags); + + if ( (fd == -1) && (errno == EINVAL) ) { + + /* Maybe O_DIRECT isn't supported. */ + o_flags &= ~O_DIRECT; + fd = open(name, o_flags); + if (fd != -1) DPRINTF("WARNING: Accessing image without" + "O_DIRECT! (%s)\n", name); + + } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name); + + if (fd == -1) { + DPRINTF("Unable to open [%s]!\n",name); + ret = 0 - errno; + return -1; + } + + prv->fd = fd; + + /* Grok the vmdk header. */ + if ((ret = read(fd, &magic, sizeof(magic))) != sizeof(magic)) + goto fail; + magic = be32_to_cpu(magic); + if (magic == VMDK3_MAGIC) { + VMDK3Header header; + if (read(fd, &header, sizeof(header)) != + sizeof(header)) + goto fail; + prv->cluster_sectors = le32_to_cpu(header.granularity); + prv->l2_size = 1 << 9; + prv->l1_size = 1 << 6; + s->size = le32_to_cpu(header.disk_sectors); + prv->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; + prv->l1_backup_table_offset = 0; + prv->l1_entry_sectors = prv->l2_size * prv->cluster_sectors; + } else if (magic == VMDK4_MAGIC) { + VMDK4Header header; + + if (read(fd, &header, sizeof(header)) != sizeof(header)) + goto fail; + s->size = le32_to_cpu(header.capacity); + prv->cluster_sectors = le32_to_cpu(header.granularity); + prv->l2_size = le32_to_cpu(header.num_gtes_per_gte); + prv->l1_entry_sectors = prv->l2_size * prv->cluster_sectors; + if (prv->l1_entry_sectors <= 0) + goto fail; + prv->l1_size = (s->size + prv->l1_entry_sectors - 1) + / prv->l1_entry_sectors; + prv->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; + prv->l1_backup_table_offset = + le64_to_cpu(header.gd_offset) << 9; + } else { + goto fail; + } + /* read the L1 table */ + l1_size = prv->l1_size * sizeof(uint32_t); + prv->l1_table = malloc(l1_size); + if (!prv->l1_table) + goto fail; + if (lseek(fd, prv->l1_table_offset, SEEK_SET) == -1) + goto fail; + if (read(fd, prv->l1_table, l1_size) != l1_size) + goto fail; + for (i = 0; i < prv->l1_size; i++) { + le32_to_cpus(&prv->l1_table[i]); + } + + if (prv->l1_backup_table_offset) { + prv->l1_backup_table = malloc(l1_size); + if (!prv->l1_backup_table) + goto fail; + if (lseek(fd, prv->l1_backup_table_offset, SEEK_SET) == -1) + goto fail; + if (read(fd, prv->l1_backup_table, l1_size) != l1_size) + goto fail; + for(i = 0; i < prv->l1_size; i++) { + le32_to_cpus(&prv->l1_backup_table[i]); + } + } + + prv->l2_cache = malloc(prv->l2_size * L2_CACHE_SIZE *sizeof(uint32_t)); + if (!prv->l2_cache) + goto fail; + prv->fd = fd; + init_fds(dd); + DPRINTF("VMDK File opened successfully\n"); + return 0; + +fail: + DPRINTF("VMDK File open failed.\n"); + safer_free(prv->l1_backup_table); + free(prv->l1_table); + free(prv->l2_cache); + close(fd); + return -1; +} + +static uint64_t get_cluster_offset(struct tdvmdk_state *prv, + uint64_t offset, int allocate) +{ + unsigned int l1_index, l2_offset, l2_index; + int min_index, i, j; + uint32_t min_count, *l2_table, tmp; + uint64_t cluster_offset; + + l1_index = (offset >> 9) / prv->l1_entry_sectors; + if (l1_index >= prv->l1_size) + return 0; + l2_offset = prv->l1_table[l1_index]; + if (!l2_offset) + return 0; + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == prv->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++prv->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + prv->l2_cache_counts[j] >>= 1; + } + } + l2_table = prv->l2_cache + (i * prv->l2_size); + goto found; + } + } + /* not found: load a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (prv->l2_cache_counts[i] < min_count) { + min_count = prv->l2_cache_counts[i]; + min_index = i; + } + } + l2_table = prv->l2_cache + (min_index * prv->l2_size); + lseek(prv->fd, (int64_t)l2_offset * 512, SEEK_SET); + if (read(prv->fd, l2_table, prv->l2_size * sizeof(uint32_t)) != + prv->l2_size * sizeof(uint32_t)) + return 0; + prv->l2_cache_offsets[min_index] = l2_offset; + prv->l2_cache_counts[min_index] = 1; + found: + l2_index = ((offset >> 9) / prv->cluster_sectors) % prv->l2_size; + cluster_offset = le32_to_cpu(l2_table[l2_index]); + if (!cluster_offset) { + if (!allocate) + return 0; + cluster_offset = lseek(prv->fd, 0, SEEK_END); + if (ftruncate(prv->fd, cluster_offset + + (prv->cluster_sectors << 9))) + return 0; + cluster_offset >>= 9; + /* update L2 table */ + tmp = cpu_to_le32(cluster_offset); + l2_table[l2_index] = tmp; + lseek(prv->fd, ((int64_t)l2_offset * 512) + + (l2_index * sizeof(tmp)), SEEK_SET); + if (write(prv->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + /* update backup L2 table */ + if (prv->l1_backup_table_offset != 0) { + l2_offset = prv->l1_backup_table[l1_index]; + lseek(prv->fd, ((int64_t)l2_offset * 512) + + (l2_index * sizeof(tmp)), SEEK_SET); + if (write(prv->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + } + } + cluster_offset <<= 9; + return cluster_offset; +} + +static int tdvmdk_queue_read(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private; + int index_in_cluster, n; + uint64_t cluster_offset; + int ret = 0; + + while (nb_sectors > 0) { + cluster_offset = get_cluster_offset(prv, sector << 9, 0); + index_in_cluster = sector % prv->cluster_sectors; + n = prv->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + if (!cluster_offset) { + memset(buf, 0, 512 * n); + } else { + lseek(prv->fd, cluster_offset + index_in_cluster * 512, + SEEK_SET); + ret = read(prv->fd, buf, n * 512); + if (ret != n * 512) { + ret = -1; + goto done; + } + } + nb_sectors -= n; + sector += n; + buf += n * 512; + } +done: + return cb(dd, ret == -1 ? -1 : 0, sector, nb_sectors, id, private); +} + +static int tdvmdk_queue_write(struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *private) +{ + struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private; + int index_in_cluster, n; + uint64_t cluster_offset; + int ret = 0; + + while (nb_sectors > 0) { + index_in_cluster = sector & (prv->cluster_sectors - 1); + n = prv->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + cluster_offset = get_cluster_offset(prv, sector << 9, 1); + if (!cluster_offset) { + ret = -1; + goto done; + } + lseek(prv->fd, cluster_offset + index_in_cluster * 512, + SEEK_SET); + ret = write(prv->fd, buf, n * 512); + if (ret != n * 512) { + ret = -1; + goto done; + } + nb_sectors -= n; + sector += n; + buf += n * 512; + } +done: + return cb(dd, ret == -1 ? -1 : 0, sector, nb_sectors, id, private); +} + +static int tdvmdk_submit(struct disk_driver *dd) +{ + return 0; +} + +static int tdvmdk_close(struct disk_driver *dd) +{ + struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private; + + safer_free(prv->l1_table); + safer_free(prv->l1_backup_table); + safer_free(prv->l2_cache); + close(prv->fd); + close(prv->poll_pipe[0]); + close(prv->poll_pipe[1]); + return 0; +} + +static int tdvmdk_do_callbacks(struct disk_driver *dd, int sid) +{ + /* always ask for a kick */ + return 1; +} + +static int tdvmdk_get_parent_id(struct disk_driver *dd, struct disk_id *id) +{ + return TD_NO_PARENT; +} + +static int tdvmdk_validate_parent(struct disk_driver *dd, + struct disk_driver *parent, td_flag_t flags) +{ + return -EINVAL; +} + +struct tap_disk tapdisk_vmdk = { + .disk_type = "tapdisk_vmdk", + .private_data_size = sizeof(struct tdvmdk_state), + .td_open = tdvmdk_open, + .td_queue_read = tdvmdk_queue_read, + .td_queue_write = tdvmdk_queue_write, + .td_submit = tdvmdk_submit, + .td_close = tdvmdk_close, + .td_do_callbacks = tdvmdk_do_callbacks, + .td_get_parent_id = tdvmdk_get_parent_id, + .td_validate_parent = tdvmdk_validate_parent +}; diff --git a/tools/blktap/drivers/bswap.h b/tools/blktap/drivers/bswap.h new file mode 100644 index 0000000..45016b9 --- /dev/null +++ b/tools/blktap/drivers/bswap.h @@ -0,0 +1,214 @@ +#ifndef BSWAP_H +#define BSWAP_H + +//#include "config-host.h" + +#include + +#if defined(__NetBSD__) +#include +#include +#elif defined(__OpenBSD__) +#include +#define bswap_16(x) swap16(x) +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) +#else + +#ifdef HAVE_BYTESWAP_H +#include +#else + +#define bswap_16(x) \ +({ \ + uint16_t __x = (x); \ + ((uint16_t)( \ + (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \ +}) + +#define bswap_32(x) \ +({ \ + uint32_t __x = (x); \ + ((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ +}) + +#define bswap_64(x) \ +({ \ + uint64_t __x = (x); \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ +}) + +#endif /* !HAVE_BYTESWAP_H */ + +static inline uint16_t bswap16(uint16_t x) +{ + return bswap_16(x); +} + +static inline uint32_t bswap32(uint32_t x) +{ + return bswap_32(x); +} + +static inline uint64_t bswap64(uint64_t x) +{ + return bswap_64(x); +} + +static inline void bswap16s(uint16_t *s) +{ + *s = bswap16(*s); +} + +static inline void bswap32s(uint32_t *s) +{ + *s = bswap32(*s); +} + +static inline void bswap64s(uint64_t *s) +{ + *s = bswap64(*s); +} + +#endif + +#if defined(WORDS_BIGENDIAN) +#define be_bswap(v, size) (v) +#define le_bswap(v, size) bswap ## size(v) +#define be_bswaps(v, size) +#define le_bswaps(p, size) *p = bswap ## size(*p); +#else +#define le_bswap(v, size) (v) +#define be_bswap(v, size) bswap ## size(v) +#define le_bswaps(v, size) +#define be_bswaps(p, size) *p = bswap ## size(*p); +#endif + +#define CPU_CONVERT(endian, size, type)\ +static inline type endian ## size ## _to_cpu(type v)\ +{\ + return endian ## _bswap(v, size);\ +}\ +\ +static inline type cpu_to_ ## endian ## size(type v)\ +{\ + return endian ## _bswap(v, size);\ +}\ +\ +static inline void endian ## size ## _to_cpus(type *p)\ +{\ + endian ## _bswaps(p, size)\ +}\ +\ +static inline void cpu_to_ ## endian ## size ## s(type *p)\ +{\ + endian ## _bswaps(p, size)\ +}\ +\ +static inline type endian ## size ## _to_cpup(const type *p)\ +{\ + return endian ## size ## _to_cpu(*p);\ +}\ +\ +static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\ +{\ + *p = cpu_to_ ## endian ## size(v);\ +} + +CPU_CONVERT(be, 16, uint16_t) +CPU_CONVERT(be, 32, uint32_t) +CPU_CONVERT(be, 64, uint64_t) + +CPU_CONVERT(le, 16, uint16_t) +CPU_CONVERT(le, 32, uint32_t) +CPU_CONVERT(le, 64, uint64_t) + +/* unaligned versions (optimized for frequent unaligned accesses)*/ + +#if defined(__i386__) || defined(__powerpc__) + +#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v) +#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v) +#define le16_to_cpupu(p) le16_to_cpup(p) +#define le32_to_cpupu(p) le32_to_cpup(p) + +#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) +#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) + +#else + +static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v; + p1[1] = v >> 8; +} + +static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v; + p1[1] = v >> 8; + p1[2] = v >> 16; + p1[3] = v >> 24; +} + +static inline uint16_t le16_to_cpupu(const uint16_t *p) +{ + const uint8_t *p1 = (const uint8_t *)p; + return p1[0] | (p1[1] << 8); +} + +static inline uint32_t le32_to_cpupu(const uint32_t *p) +{ + const uint8_t *p1 = (const uint8_t *)p; + return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24); +} + +static inline void cpu_to_be16wu(uint16_t *p, uint16_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 8; + p1[1] = v; +} + +static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 24; + p1[1] = v >> 16; + p1[2] = v >> 8; + p1[3] = v; +} + +#endif + +#ifdef WORDS_BIGENDIAN +#define cpu_to_32wu cpu_to_be32wu +#else +#define cpu_to_32wu cpu_to_le32wu +#endif + +#undef le_bswap +#undef be_bswap +#undef le_bswaps +#undef be_bswaps + +#endif /* BSWAP_H */ diff --git a/tools/blktap/drivers/check_gcrypt b/tools/blktap/drivers/check_gcrypt new file mode 100644 index 0000000..154ba24 --- /dev/null +++ b/tools/blktap/drivers/check_gcrypt @@ -0,0 +1,14 @@ +#!/bin/sh + +cat > .gcrypt.c << EOF +#include +int main(void) { return 0; } +EOF + +if $1 -o .gcrypt .gcrypt.c -lgcrypt 2>/dev/null ; then + echo "yes" +else + echo "no" +fi + +rm -f .gcrypt* diff --git a/tools/blktap/drivers/img2qcow.c b/tools/blktap/drivers/img2qcow.c new file mode 100644 index 0000000..6b4fa70 --- /dev/null +++ b/tools/blktap/drivers/img2qcow.c @@ -0,0 +1,282 @@ +/* img2qcow.c + * + * Generates a qcow format disk and fills it from an existing image. + * + * (c) 2006 Julian Chesterfield and Andrew Warfield + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tapdisk.h" +#include "blk.h" + +#if 1 +#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a ) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + + +#define TAPDISK 1 +#define BLOCK_PROCESSSZ 4096 + +static int maxfds, *io_fd, running = 1, complete = 0; +static int returned_events = 0, submit_events = 0; +static uint64_t prev = 0; +static char output[25]; + +static void print_bytes(void *ptr, int length) +{ + int i,k; + unsigned char *p = ptr; + + DFPRINTF("Buf dump, length %d:\n",length); + for (k = 0; k < length; k++) { + DFPRINTF("%x",*p); + *p++; + if(k % 16 == 0) DFPRINTF("\n"); + else if(k % 2 == 0) DFPRINTF(" "); + } + DFPRINTF("\n"); + return; +} + +static void debug_output(uint64_t progress, uint64_t size) +{ + uint64_t blocks = size/20; + + /*Output progress every 5% */ + if (progress/blocks > prev) { + memcpy(output+prev+1,"=>",2); + prev++; + DFPRINTF("\r%s %llu%%", output, + (long long)(prev-1)*5); + } + return; +} + +static inline void LOCAL_FD_SET(fd_set *readfds) +{ + FD_SET(io_fd[0], readfds); + maxfds = io_fd[0] + 1; + + return; +} + +static int get_image_info(struct td_state *s, int fd) +{ + int ret; + long size; + unsigned long total_size; + struct statvfs statBuf; + struct stat stat; + + ret = fstat(fd, &stat); + if (ret != 0) { + DFPRINTF("ERROR: fstat failed, Couldn't stat image"); + return -EINVAL; + } + + if (S_ISBLK(stat.st_mode)) { + /*Accessing block device directly*/ + if (blk_getimagesize(fd, &s->size) != 0) + return -EINVAL; + + DFPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " + "sector_shift [%llu]\n", + (long long unsigned)(s->size << SECTOR_SHIFT), + (long long unsigned)s->size); + + /*Get the sector size*/ + if (blk_getsectorsize(fd, &s->sector_size) != 0) + s->sector_size = DEFAULT_SECTOR_SIZE; + + } else { + /*Local file? try fstat instead*/ + s->size = (stat.st_size >> SECTOR_SHIFT); + s->sector_size = DEFAULT_SECTOR_SIZE; + DFPRINTF("Image size: [%llu]\n", + (long long unsigned)s->size); + } + + return 0; +} + +static int send_responses(struct disk_driver *dd, int res, uint64_t sec, + int nr_secs, int idx, void *private) +{ + if (res < 0) DFPRINTF("AIO FAILURE: res [%d]!\n",res); + + returned_events++; + + free(private); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct disk_driver dd; + struct td_state *s; + int ret = -1, fd, len; + fd_set readfds; + struct timeval timeout; + uint64_t i; + char *buf; + + if (argc != 3) { + fprintf(stderr, "Qcow-utils: v1.0.0\n"); + fprintf(stderr, "usage: %s \n", + argv[0]); + exit(-1); + } + + s = malloc(sizeof(struct td_state)); + + /*Open image*/ + fd = open(argv[2], O_RDONLY | O_LARGEFILE); + + if (fd == -1) { + DFPRINTF("Unable to open [%s], (err %d)!\n",argv[2],0 - errno); + exit(-1); + } + + get_image_info(s, fd); + + /*Create qcow file*/ + ret = qcow_create(argv[1],s->size<size); + + dd.td_state = s; + dd.drv = &tapdisk_qcow; + dd.private = malloc(dd.drv->private_data_size); + + /*Open qcow file*/ + if (dd.drv->td_open(&dd, argv[1], 0)!=0) { + DFPRINTF("Unable to open Qcow file [%s]\n",argv[1]); + exit(-1); + } + + io_fd = dd.io_fd; + + /*Initialise the output string*/ + memset(output,0x20,25); + output[0] = '['; + output[22] = ']'; + output[23] = '\0'; + DFPRINTF("%s",output); + + i = 0; + while (running) { + timeout.tv_sec = 0; + + if (!complete) { + /*Read sector from image*/ + if (lseek(fd, i, SEEK_SET) == (off_t)-1) { + DFPRINTF("Unable to access file offset %llu\n", + (long long)i); + exit(-1); + } + + if( (ret = posix_memalign((void **)&buf, + BLOCK_PROCESSSZ, + BLOCK_PROCESSSZ)) != 0) { + DFPRINTF("Unable to read memalign buf (%d)\n",ret); + exit(-1); + } + + /*We attempt to read 4k sized blocks*/ + len = read(fd, buf, BLOCK_PROCESSSZ); + if (len < 512) { + DFPRINTF("Unable to read sector %llu\n", + (long long unsigned) (i >> 9)); + complete = 1; + continue; + } + + if (len % 512) { + len = (len >> 9) << 9; + } + + ret = dd.drv->td_queue_write(&dd, i >> 9, + len >> 9, buf, + send_responses, 0, buf); + + if (!ret) submit_events++; + + if (ret < 0) { + DFPRINTF("UNABLE TO WRITE block [%llu]\n", + (long long unsigned) (i >> 9)); + } else i += len; + + if (i >> 9 == s->size) complete = 1; + + debug_output(i,s->size << 9); + + if ((submit_events % 10 == 0) || complete) + dd.drv->td_submit(&dd); + timeout.tv_usec = 0; + + } else { + timeout.tv_usec = 1000; + if (!submit_events) running = 0; + } + + + /*Check AIO FD*/ + LOCAL_FD_SET(&readfds); + ret = select(maxfds + 1, &readfds, (fd_set *) 0, + (fd_set *) 0, &timeout); + + if (ret > 0) dd.drv->td_do_callbacks(&dd, 0); + if (complete && (returned_events == submit_events)) + running = 0; + } + memcpy(output+prev+1,"=",1); + DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output); + dd.drv->td_close(&dd); + free(dd.private); + free(s); + + return 0; +} diff --git a/tools/blktap/drivers/qcow-create.c b/tools/blktap/drivers/qcow-create.c new file mode 100644 index 0000000..5ff5064 --- /dev/null +++ b/tools/blktap/drivers/qcow-create.c @@ -0,0 +1,120 @@ +/* qcow-create.c + * + * Generates a qcow format disk. + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tapdisk.h" + +#if 1 +#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a ) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif + +#define MAX_NAME_LEN 1000 + +static void help(void) +{ + fprintf(stderr, "Qcow-utils: v1.0.0\n"); + fprintf(stderr, + "usage: qcow-create [-h help] [-r reserve] " + "[]\n"); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int ret = -1, c, backed = 0; + int sparse = 1; + uint64_t size; + char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN]; + + for(;;) { + c = getopt(argc, argv, "hr"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + exit(0); + break; + case 'r': + sparse = 0; + break; + default: + fprintf(stderr, "Unknown option\n"); + help(); + } + } + + printf("Optind %d, argc %d\n", optind, argc); + if ( !(optind == (argc - 2) || optind == (argc - 3)) ) + help(); + + size = atoi(argv[optind++]); + size = size << 20; + + if (snprintf(filename, MAX_NAME_LEN, "%s",argv[optind++]) >= + MAX_NAME_LEN) { + fprintf(stderr,"Device name too long\n"); + exit(-1); + } + + if (optind != argc) { + /*Backing file argument*/ + backed = 1; + if (snprintf(bfilename, MAX_NAME_LEN, "%s",argv[optind++]) >= + MAX_NAME_LEN) { + fprintf(stderr,"Device name too long\n"); + exit(-1); + } + } + + DFPRINTF("Creating file size %llu, name %s\n",(long long unsigned)size, filename); + if (!backed) + ret = qcow_create(filename,size,NULL,sparse); + else + ret = qcow_create(filename,size,bfilename,sparse); + + if (ret < 0) + DPRINTF("Unable to create QCOW file\n"); + else + DPRINTF("QCOW file successfully created\n"); + + return 0; +} diff --git a/tools/blktap/drivers/qcow2raw.c b/tools/blktap/drivers/qcow2raw.c new file mode 100644 index 0000000..0fa88c1 --- /dev/null +++ b/tools/blktap/drivers/qcow2raw.c @@ -0,0 +1,348 @@ +/* qcow2raw.c + * + * Generates raw image data from an existing qcow image + * + * (c) 2006 Julian Chesterfield and Andrew Warfield + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tapdisk.h" +#include "blk.h" + +#if 1 +#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a ) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif + + +/* *BSD has no O_LARGEFILE */ +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#define TAPDISK 1 +#define BLOCK_PROCESSSZ 4096 + +static int maxfds, *qcowio_fd, *aio_fd, running = 1, complete = 0; +static int returned_read_events = 0, returned_write_events = 0; +static int submit_events = 0; +static uint32_t read_idx = 0, write_idx = 0; +struct disk_driver ddqcow, ddaio; +static uint64_t prev = 0, written = 0; +static char output[25]; + +static void print_bytes(void *ptr, int length) +{ + int i,k; + unsigned char *p = ptr; + + DFPRINTF("Buf dump, length %d:\n",length); + for (k = 0; k < length; k++) { + DFPRINTF("%x",*p); + *p++; + if (k % 16 == 0) DFPRINTF("\n"); + else if (k % 2 == 0) DFPRINTF(" "); + } + DFPRINTF("\n"); + return; +} + +static void debug_output(uint64_t progress, uint64_t size) +{ + /*Output progress every 5% */ + uint64_t blocks = size/20; + + if (progress/blocks > prev) { + memcpy(output+prev+1,"=>",2); + prev++; + DFPRINTF("\r%s %llu%%", + output, (long long)((prev-1)*5)); + } + return; +} + +static inline void LOCAL_FD_SET(fd_set *readfds) +{ + FD_SET(qcowio_fd[0], readfds); + FD_SET(aio_fd[0], readfds); + + maxfds = (qcowio_fd[0] > aio_fd[0] ? qcowio_fd[0] : aio_fd[0]) + 1; + + return; +} + +static int send_write_responses(struct disk_driver *dd, int res, uint64_t sec, + int nr_secs, int idx, void *private) +{ + if (res < 0) { + DFPRINTF("AIO FAILURE: res [%d]!\n",res); + return 0; + } + written += BLOCK_PROCESSSZ; + returned_write_events++; + write_idx = idx; + + debug_output(written, dd->td_state->size << 9); + free(private); + return 0; +} + +static int send_read_responses(struct disk_driver *dd, int res, uint64_t sec, + int nr_secs, int idx, void *private) +{ + int ret; + + if (res < 0) DFPRINTF("AIO FAILURE: res [%d]!\n",res); + + returned_read_events++; + read_idx = idx; + + ret = ddaio.drv->td_queue_write(&ddaio, idx, BLOCK_PROCESSSZ>>9, private, + send_write_responses, idx, private); + if (ret != 0) { + DFPRINTF("ERROR in submitting queue write!\n"); + return 0; + } + + if ( (returned_read_events == submit_events) || + (returned_read_events % 10 == 0) ) { + ddaio.drv->td_submit(&ddaio); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = -1, fd, len,input; + uint64_t size; + fd_set readfds; + struct timeval timeout; + uint64_t i; + char *buf; + struct stat finfo; + + if (argc != 3) { + fprintf(stderr, "Qcow-utils: v1.0.0\n"); + fprintf(stderr, "usage: %s " + "\n", + argv[0]); + exit(-1); + } + + ddqcow.td_state = malloc(sizeof(struct td_state)); + ddaio.td_state = malloc(sizeof(struct td_state)); + + /*Open qcow source file*/ + ddqcow.drv = &tapdisk_qcow; + ddqcow.private = malloc(ddqcow.drv->private_data_size); + + if (ddqcow.drv->td_open(&ddqcow, argv[2], TD_RDONLY)!=0) { + DFPRINTF("Unable to open Qcow file [%s]\n",argv[2]); + exit(-1); + } else DFPRINTF("QCOW file opened, size %llu\n", + (long long unsigned)ddqcow.td_state->size); + + qcowio_fd = ddqcow.io_fd; + + /*Setup aio destination file*/ + ret = stat(argv[1],&finfo); + if (ret == -1) { + /*Check errno*/ + switch(errno) { + case ENOENT: + /*File doesn't exist, create*/ + fd = open(argv[1], + O_RDWR | O_LARGEFILE | O_CREAT, 0644); + if (fd < 0) { + DFPRINTF("ERROR creating file [%s] " + "(errno %d)\n", + argv[1], 0 - errno); + exit(-1); + } + if (ftruncate(fd, (off_t)ddqcow.td_state->size<<9) < 0) { + DFPRINTF("Unable to create file " + "[%s] of size %llu (errno %d). " + "Exiting...\n", + argv[1], + (long long unsigned)ddqcow.td_state->size<<9, + 0 - errno); + close(fd); + exit(-1); + } + close(fd); + break; + case ENXIO: + DFPRINTF("ERROR Device [%s] does not exist\n",argv[1]); + exit(-1); + default: + DFPRINTF("An error occurred opening Device [%s] " + "(errno %d)\n", + argv[1], 0 - errno); + exit(-1); + } + } else { + fprintf(stderr, "WARNING: All existing data in " + "%s will be overwritten.\nDo you wish to continue? " + "(y or n) ", + argv[1]); + if (getchar() != 'y') { + DFPRINTF("Exiting...\n"); + exit(-1); + } + + /*TODO - Test the existing file or device for adequate space*/ + fd = open(argv[1], O_RDWR | O_LARGEFILE); + if (fd < 0) { + DFPRINTF("ERROR: opening file [%s] (errno %d)\n", + argv[1], 0 - errno); + exit(-1); + } + + if (S_ISBLK(finfo.st_mode)) { + if (blk_getimagesize(fd, &size) != 0) { + close(fd); + return -1; + } + + if (size < ddqcow.td_state->size<<9) { + DFPRINTF("ERROR: Not enough space on device " + "%s (%"PRIu64" bytes available, " + "%llu bytes required\n", + argv[1], size, + (long long unsigned)ddqcow.td_state->size<<9); + close(fd); + exit(-1); + } + } else { + if (ftruncate(fd, (off_t)ddqcow.td_state->size<<9) < 0) { + DFPRINTF("Unable to create file " + "[%s] of size %llu (errno %d). " + "Exiting...\n", + argv[1], + (long long unsigned)ddqcow.td_state->size<<9, + 0 - errno); + close(fd); + exit(-1); + } else DFPRINTF("File [%s] truncated to length %llu " + "(%llu)\n", + argv[1], + (long long unsigned)ddqcow.td_state->size<<9, + (long long unsigned)ddqcow.td_state->size); + } + close(fd); + } + + /*Open aio destination file*/ + ddaio.drv = &tapdisk_aio; + ddaio.private = malloc(ddaio.drv->private_data_size); + + if (ddaio.drv->td_open(&ddaio, argv[1], 0)!=0) { + DFPRINTF("Unable to open Qcow file [%s]\n", argv[1]); + exit(-1); + } + + aio_fd = ddaio.io_fd; + + /*Initialise the output string*/ + memset(output,0x20,25); + output[0] = '['; + output[22] = ']'; + output[23] = '\0'; + DFPRINTF("%s",output); + + i = 0; + while (running) { + timeout.tv_sec = 0; + + if (!complete) { + /*Read Pages from qcow image*/ + if ( (ret = posix_memalign((void **)&buf, + BLOCK_PROCESSSZ, + BLOCK_PROCESSSZ)) + != 0) { + DFPRINTF("Unable to alloc memory (%d)\n",ret); + exit(-1); + } + + /*Attempt to read 4k sized blocks*/ + submit_events++; + ret = ddqcow.drv->td_queue_read(&ddqcow, i>>9, + BLOCK_PROCESSSZ>>9, buf, + send_read_responses, i>>9, buf); + + if (ret < 0) { + DFPRINTF("UNABLE TO READ block [%llu]\n", + (long long unsigned)i); + exit(-1); + } else { + i += BLOCK_PROCESSSZ; + } + + if (i >= ddqcow.td_state->size<<9) { + complete = 1; + } + + if ((submit_events % 10 == 0) || complete) + ddqcow.drv->td_submit(&ddqcow); + timeout.tv_usec = 0; + + } else { + timeout.tv_usec = 1000; + if (!submit_events) running = 0; + } + + + /*Check AIO FD*/ + LOCAL_FD_SET(&readfds); + ret = select(maxfds + 1, &readfds, (fd_set *) 0, + (fd_set *) 0, &timeout); + + if (ret > 0) { + if (FD_ISSET(qcowio_fd[0], &readfds)) + ddqcow.drv->td_do_callbacks(&ddqcow, 0); + if (FD_ISSET(aio_fd[0], &readfds)) + ddaio.drv->td_do_callbacks(&ddaio, 0); + } + if (complete && (returned_write_events == submit_events)) + running = 0; + } + memcpy(output+prev+1,"=",1); + DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output); + + return 0; +} diff --git a/tools/blktap/drivers/tapaio.c b/tools/blktap/drivers/tapaio.c new file mode 100644 index 0000000..140c44a --- /dev/null +++ b/tools/blktap/drivers/tapaio.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2006 Andrew Warfield and Julian Chesterfield + * Copyright (c) 2007 Red Hat, Inc. + * + * 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. + */ + +#include "tapaio.h" +#include "tapdisk.h" +#include +#include +#include +#include + +/** + * We used a kernel patch to return an fd associated with the AIO context + * so that we can concurrently poll on synchronous and async descriptors. + * This is signalled by passing 1 as the io context to io_setup. + */ +#define REQUEST_ASYNC_FD 1 + +/* + * If we don't have any way to do epoll on aio events in a normal kernel, + * wait for aio events in a separate thread and return completion status + * that via a pipe that can be waited on normally. + * + * To keep locking problems between the completion thread and the submit + * thread to a minimum, there's a handshake which allows only one thread + * to be doing work on the completion queue at a time: + * + * 1) main thread sends completion thread a command via the command pipe; + * 2) completion thread waits for aio events and returns the number + * received on the completion pipe + * 3) main thread processes the received ctx->aio_events events + * 4) loop back to 1) to let the completion thread refill the aio_events + * buffer. + * + * This workaround needs to disappear once the kernel provides a single + * mechanism for waiting on both aio and normal fd wakeups. + */ +static void * +tap_aio_completion_thread(void *arg) +{ + tap_aio_internal_context_t *ctx = (tap_aio_internal_context_t *) arg; + int command; + int nr_events; + int rc; + + while (1) { + rc = read(ctx->command_fd[0], &command, sizeof(command)); + + do { + rc = io_getevents(ctx->aio_ctx, 1, + ctx->max_aio_events, ctx->aio_events, + NULL); + if (rc) { + nr_events = rc; + rc = write(ctx->completion_fd[1], &nr_events, + sizeof(nr_events)); + } + } while (!rc); + } + return NULL; +} + +void +tap_aio_continue(tap_aio_internal_context_t *ctx) +{ + int cmd = 0; + + if (!ctx->poll_in_thread) + return; + + if (write(ctx->command_fd[1], &cmd, sizeof(cmd)) < 0) + DPRINTF("Cannot write to command pipe\n"); +} + +static int +tap_aio_setup(tap_aio_internal_context_t *ctx, + struct io_event *aio_events, + int max_aio_events) +{ + int ret; + + ctx->aio_events = aio_events; + ctx->max_aio_events = max_aio_events; + ctx->poll_in_thread = 0; + + ctx->aio_ctx = (io_context_t) REQUEST_ASYNC_FD; + ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx); + if (ret < 0 && ret != -EINVAL) + return ret; + else if (ret > 0) { + ctx->pollfd = ret; + return ctx->pollfd; + } + + ctx->aio_ctx = (io_context_t) 0; + ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx); + if (ret < 0) + return ret; + + if ((ret = pipe(ctx->command_fd)) < 0) { + DPRINTF("Unable to create command pipe\n"); + return -1; + } + if ((ret = pipe(ctx->completion_fd)) < 0) { + DPRINTF("Unable to create completion pipe\n"); + return -1; + } + + if ((ret = pthread_create(&ctx->aio_thread, NULL, + tap_aio_completion_thread, ctx)) != 0) { + DPRINTF("Unable to create completion thread\n"); + return -1; + } + + ctx->pollfd = ctx->completion_fd[0]; + ctx->poll_in_thread = 1; + + tap_aio_continue(ctx); + + return 0; +} + +int +tap_aio_get_events(tap_aio_internal_context_t *ctx) +{ + int nr_events = 0; + + if (!ctx->poll_in_thread) + nr_events = io_getevents(ctx->aio_ctx, 1, + ctx->max_aio_events, ctx->aio_events, NULL); + else { + int r; + r = read(ctx->completion_fd[0], &nr_events, sizeof(nr_events)); + if (r < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + /* This is pretty bad, we'll probably spin */ + DPRINTF("Aargh, read completion_fd failed: %s", + strerror(errno)); + } else if (r != sizeof(nr_events)) { + /* Should never happen because sizeof(nr_events) + * fits in the guaranteed atomic pipe write size. + * Blundering on is slightly nicer than asserting */ + DPRINTF("Aargh, read completion_fd short read %d", r); + } + } + + return nr_events; +} + +int tap_aio_more_events(tap_aio_internal_context_t *ctx) +{ + return io_getevents(ctx->aio_ctx, 0, + ctx->max_aio_events, ctx->aio_events, NULL); +} + +int tap_aio_init(tap_aio_context_t *ctx, uint64_t sectors, + int max_aio_reqs) +{ + int i, ret; + long ioidx; + + ctx->iocb_list = NULL; + ctx->pending_aio = NULL; + ctx->aio_events = NULL; + ctx->iocb_free = NULL; + ctx->iocb_queue = NULL; + + /*Initialize Locking bitmap*/ + ctx->sector_lock = calloc(1, sectors); + + if (!ctx->sector_lock) { + DPRINTF("Failed to allocate sector lock\n"); + goto fail; + } + + + /* Initialize AIO */ + ctx->max_aio_reqs = max_aio_reqs; + ctx->iocb_free_count = ctx->max_aio_reqs; + ctx->iocb_queued = 0; + + if (!(ctx->iocb_list = malloc(sizeof(struct iocb) * ctx->max_aio_reqs)) || + !(ctx->pending_aio = malloc(sizeof(struct pending_aio) * ctx->max_aio_reqs)) || + !(ctx->aio_events = malloc(sizeof(struct io_event) * ctx->max_aio_reqs)) || + !(ctx->iocb_free = malloc(sizeof(struct iocb *) * ctx->max_aio_reqs)) || + !(ctx->iocb_queue = malloc(sizeof(struct iocb *) * ctx->max_aio_reqs))) + { + DPRINTF("Failed to allocate AIO structs (max_aio_reqs = %d)\n", + ctx->max_aio_reqs); + goto fail; + } + + ret = tap_aio_setup(&ctx->aio_ctx, ctx->aio_events, ctx->max_aio_reqs); + if (ret < 0) { + if (ret == -EAGAIN) { + DPRINTF("Couldn't setup AIO context. If you are " + "trying to concurrently use a large number " + "of blktap-based disks, you may need to " + "increase the system-wide aio request limit. " + "(e.g. 'echo echo 1048576 > /proc/sys/fs/" + "aio-max-nr')\n"); + } else { + DPRINTF("Couldn't setup AIO context.\n"); + } + goto fail; + } + + for (i=0;imax_aio_reqs;i++) + ctx->iocb_free[i] = &ctx->iocb_list[i]; + + DPRINTF("AIO state initialised\n"); + + return 0; + +fail: + return -1; +} + +void tap_aio_free(tap_aio_context_t *ctx) +{ + if (ctx->sector_lock) + free(ctx->sector_lock); + if (ctx->iocb_list) + free(ctx->iocb_list); + if (ctx->pending_aio) + free(ctx->pending_aio); + if (ctx->aio_events) + free(ctx->aio_events); + if (ctx->iocb_free) + free(ctx->iocb_free); + if (ctx->iocb_queue) + free(ctx->iocb_queue); +} + +/*TODO: Fix sector span!*/ +int tap_aio_can_lock(tap_aio_context_t *ctx, uint64_t sector) +{ + return (ctx->sector_lock[sector] ? 0 : 1); +} + +int tap_aio_lock(tap_aio_context_t *ctx, uint64_t sector) +{ + return ++ctx->sector_lock[sector]; +} + +void tap_aio_unlock(tap_aio_context_t *ctx, uint64_t sector) +{ + if (!ctx->sector_lock[sector]) return; + + --ctx->sector_lock[sector]; + return; +} + + +int tap_aio_read(tap_aio_context_t *ctx, int fd, int size, + uint64_t offset, char *buf, td_callback_t cb, + int id, uint64_t sector, void *private) +{ + struct iocb *io; + struct pending_aio *pio; + long ioidx; + + if (ctx->iocb_free_count == 0) + return -ENOMEM; + + io = ctx->iocb_free[--ctx->iocb_free_count]; + + ioidx = IOCB_IDX(ctx, io); + pio = &ctx->pending_aio[ioidx]; + pio->cb = cb; + pio->id = id; + pio->private = private; + pio->nb_sectors = size/512; + pio->buf = buf; + pio->sector = sector; + + io_prep_pread(io, fd, buf, size, offset); + io->data = (void *)ioidx; + + ctx->iocb_queue[ctx->iocb_queued++] = io; + + return 0; +} + +int tap_aio_write(tap_aio_context_t *ctx, int fd, int size, + uint64_t offset, char *buf, td_callback_t cb, + int id, uint64_t sector, void *private) +{ + struct iocb *io; + struct pending_aio *pio; + long ioidx; + + if (ctx->iocb_free_count == 0) + return -ENOMEM; + + io = ctx->iocb_free[--ctx->iocb_free_count]; + + ioidx = IOCB_IDX(ctx, io); + pio = &ctx->pending_aio[ioidx]; + pio->cb = cb; + pio->id = id; + pio->private = private; + pio->nb_sectors = size/512; + pio->buf = buf; + pio->sector = sector; + + io_prep_pwrite(io, fd, buf, size, offset); + io->data = (void *)ioidx; + + ctx->iocb_queue[ctx->iocb_queued++] = io; + + return 0; +} + +int tap_aio_submit(tap_aio_context_t *ctx) +{ + int ret; + + if (!ctx->iocb_queued) + return 0; + + ret = io_submit(ctx->aio_ctx.aio_ctx, ctx->iocb_queued, ctx->iocb_queue); + + /* XXX: TODO: Handle error conditions here. */ + + /* Success case: */ + ctx->iocb_queued = 0; + + return 0; +} + diff --git a/tools/blktap/drivers/tapaio.h b/tools/blktap/drivers/tapaio.h new file mode 100644 index 0000000..27d3881 --- /dev/null +++ b/tools/blktap/drivers/tapaio.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2006 Andrew Warfield and Julian Chesterfield + * Copyright (c) 2007 Red Hat, Inc. + * + * 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 __TAPAIO_H__ +#define __TAPAIO_H__ + +#include +#include +#include + +#include "tapdisk.h" + +#define IOCB_IDX(_ctx, _io) ((_io) - (_ctx)->iocb_list) + +struct tap_aio_internal_context { + io_context_t aio_ctx; + + struct io_event *aio_events; + int max_aio_events; + + pthread_t aio_thread; + int command_fd[2]; + int completion_fd[2]; + int pollfd; + unsigned int poll_in_thread : 1; +}; + + +typedef struct tap_aio_internal_context tap_aio_internal_context_t; + + +struct pending_aio { + td_callback_t cb; + int id; + void *private; + int nb_sectors; + char *buf; + uint64_t sector; +}; + + +struct tap_aio_context { + tap_aio_internal_context_t aio_ctx; + + int max_aio_reqs; + struct iocb *iocb_list; + struct iocb **iocb_free; + struct pending_aio *pending_aio; + int iocb_free_count; + struct iocb **iocb_queue; + int iocb_queued; + struct io_event *aio_events; + + /* Locking bitmap for AIO reads/writes */ + uint8_t *sector_lock; +}; + +typedef struct tap_aio_context tap_aio_context_t; + +void tap_aio_continue (tap_aio_internal_context_t *ctx); +int tap_aio_get_events (tap_aio_internal_context_t *ctx); +int tap_aio_more_events(tap_aio_internal_context_t *ctx); + + +int tap_aio_init(tap_aio_context_t *ctx, uint64_t sectors, + int max_aio_reqs); +void tap_aio_free(tap_aio_context_t *ctx); + +int tap_aio_can_lock(tap_aio_context_t *ctx, uint64_t sector); +int tap_aio_lock(tap_aio_context_t *ctx, uint64_t sector); +void tap_aio_unlock(tap_aio_context_t *ctx, uint64_t sector); + + +int tap_aio_read(tap_aio_context_t *ctx, int fd, int size, + uint64_t offset, char *buf, td_callback_t cb, + int id, uint64_t sector, void *private); +int tap_aio_write(tap_aio_context_t *ctx, int fd, int size, + uint64_t offset, char *buf, td_callback_t cb, + int id, uint64_t sector, void *private); +int tap_aio_submit(tap_aio_context_t *ctx); + +#endif /* __TAPAIO_H__ */ diff --git a/tools/blktap/drivers/tapdisk.c b/tools/blktap/drivers/tapdisk.c new file mode 100644 index 0000000..19cd777 --- /dev/null +++ b/tools/blktap/drivers/tapdisk.c @@ -0,0 +1,872 @@ +/* tapdisk.c + * + * separate disk process, spawned by blktapctrl. Inherits code from driver + * plugins + * + * Copyright (c) 2005 Julian Chesterfield and Andrew Warfield. + * + */ + +#define MSG_SIZE 4096 +#define TAPDISK + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blktaplib.h" +#include "tapdisk.h" + +#if 1 +#define ASSERT(_p) \ + if ( !(_p) ) { DPRINTF("Assertion '%s' failed, line %d, file %s", #_p , \ + __LINE__, __FILE__); *(int*)0=0; } +#else +#define ASSERT(_p) ((void)0) +#endif + +#define INPUT 0 +#define OUTPUT 1 + +static int maxfds, fds[2], run = 1; + +static pid_t process; +int connected_disks = 0; +fd_list_entry_t *fd_start = NULL; + +int do_cow_read(struct disk_driver *dd, blkif_request_t *req, + int sidx, uint64_t sector, int nr_secs); + +#define td_for_each_disk(tds, drv) \ + for (drv = tds->disks; drv != NULL; drv = drv->next) + +static void usage(void) +{ + fprintf(stderr, "blktap-utils: v1.0.0\n"); + fprintf(stderr, "usage: tapdisk \n"); + exit(-1); +} + +static void daemonize(void) +{ + int i; + + if (getppid()==1) return; /* already a daemon */ + if (fork() != 0) exit(0); + +#if 0 + /*Set new program session ID and close all descriptors*/ + setsid(); + for (i = getdtablesize(); i >= 0; --i) close(i); + + /*Send all I/O to /dev/null */ + i = open("/dev/null",O_RDWR); + dup(i); + dup(i); +#endif + return; +} + +static void free_driver(struct disk_driver *d) +{ + if (d->name) + free(d->name); + if (d->private) + free(d->private); + free(d); +} + +static void unmap_disk(struct td_state *s) +{ + tapdev_info_t *info = s->ring_info; + struct disk_driver *dd, *tmp; + fd_list_entry_t *entry; + + dd = s->disks; + while (dd) { + tmp = dd->next; + dd->drv->td_close(dd); + free_driver(dd); + dd = tmp; + } + + if (info != NULL && info->mem > 0) + munmap(info->mem, getpagesize() * BLKTAP_MMAP_REGION_SIZE); + + entry = s->fd_entry; + *entry->pprev = entry->next; + if (entry->next) + entry->next->pprev = entry->pprev; + + close(info->fd); + + free(s->fd_entry); + free(s->blkif); + free(s->ring_info); + free(s); + + return; +} + +static void sig_handler(int sig) +{ + /*Received signal to close. If no disks are active, we close app.*/ + + if (connected_disks < 1) run = 0; +} + +static inline int LOCAL_FD_SET(fd_set *readfds) +{ + fd_list_entry_t *ptr; + struct disk_driver *dd; + + ptr = fd_start; + while (ptr != NULL) { + if (ptr->tap_fd) { + FD_SET(ptr->tap_fd, readfds); + td_for_each_disk(ptr->s, dd) { + if (dd->io_fd[READ]) + FD_SET(dd->io_fd[READ], readfds); + maxfds = (dd->io_fd[READ] > maxfds ? + dd->io_fd[READ] : maxfds); + } + maxfds = (ptr->tap_fd > maxfds ? ptr->tap_fd : maxfds); + } + ptr = ptr->next; + } + + return 0; +} + +static inline fd_list_entry_t *add_fd_entry(int tap_fd, struct td_state *s) +{ + fd_list_entry_t **pprev, *entry; + int i; + + DPRINTF("Adding fd_list_entry\n"); + + /*Add to linked list*/ + s->fd_entry = entry = malloc(sizeof(fd_list_entry_t)); + entry->tap_fd = tap_fd; + entry->s = s; + entry->next = NULL; + + pprev = &fd_start; + while (*pprev != NULL) + pprev = &(*pprev)->next; + + *pprev = entry; + entry->pprev = pprev; + + return entry; +} + +static inline struct td_state *get_state(int cookie) +{ + fd_list_entry_t *ptr; + + ptr = fd_start; + while (ptr != NULL) { + if (ptr->cookie == cookie) return ptr->s; + ptr = ptr->next; + } + return NULL; +} + +static struct tap_disk *get_driver(int drivertype) +{ + /* blktapctrl has passed us the driver type */ + + return dtypes[drivertype]->drv; +} + +static struct td_state *state_init(void) +{ + int i; + struct td_state *s; + blkif_t *blkif; + + s = malloc(sizeof(struct td_state)); + blkif = s->blkif = malloc(sizeof(blkif_t)); + s->ring_info = calloc(1, sizeof(tapdev_info_t)); + + for (i = 0; i < MAX_REQUESTS; i++) { + blkif->pending_list[i].secs_pending = 0; + blkif->pending_list[i].submitting = 0; + } + + return s; +} + +static int map_new_dev(struct td_state *s, int minor) +{ + int tap_fd; + tapdev_info_t *info = s->ring_info; + char *devname; + fd_list_entry_t *ptr; + int page_size; + + if (asprintf(&devname,"%s/%s%d", BLKTAP_DEV_DIR, BLKTAP_DEV_NAME, minor) == -1) + return -1; + tap_fd = open(devname, O_RDWR); + if (tap_fd == -1) + { + DPRINTF("open failed on dev %s!",devname); + goto fail; + } + info->fd = tap_fd; + + /*Map the shared memory*/ + page_size = getpagesize(); + info->mem = mmap(0, page_size * BLKTAP_MMAP_REGION_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, info->fd, 0); + if ((long int)info->mem == -1) + { + DPRINTF("mmap failed on dev %s!\n",devname); + goto fail; + } + + /* assign the rings to the mapped memory */ + info->sring = (blkif_sring_t *)((unsigned long)info->mem); + BACK_RING_INIT(&info->fe_ring, info->sring, page_size); + + info->vstart = + (unsigned long)info->mem + (BLKTAP_RING_PAGES * page_size); + + ioctl(info->fd, BLKTAP_IOCTL_SENDPID, process ); + ioctl(info->fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE ); + free(devname); + + /*Update the fd entry*/ + ptr = fd_start; + while (ptr != NULL) { + if (s == ptr->s) { + ptr->tap_fd = tap_fd; + break; + } + ptr = ptr->next; + } + + return minor; + + fail: + free(devname); + return -1; +} + +static struct disk_driver *disk_init(struct td_state *s, + struct tap_disk *drv, + char *name, td_flag_t flags) +{ + struct disk_driver *dd; + + dd = calloc(1, sizeof(struct disk_driver)); + if (!dd) + return NULL; + + dd->private = malloc(drv->private_data_size); + if (!dd->private) { + free(dd); + return NULL; + } + + dd->drv = drv; + dd->td_state = s; + dd->name = name; + dd->flags = flags; + + return dd; +} + +static int open_disk(struct td_state *s, + struct tap_disk *drv, char *path, td_flag_t flags) +{ + int err; + char *dup; + td_flag_t pflags; + struct disk_id id; + struct disk_driver *d; + + dup = strdup(path); + if (!dup) + return -ENOMEM; + + memset(&id, 0, sizeof(struct disk_id)); + s->disks = d = disk_init(s, drv, dup, flags); + if (!d) + return -ENOMEM; + + err = drv->td_open(d, path, flags); + if (err) { + free_driver(d); + s->disks = NULL; + return -ENOMEM; + } + pflags = flags | TD_RDONLY; + + /* load backing files as necessary */ + while ((err = d->drv->td_get_parent_id(d, &id)) == 0) { + struct disk_driver *new; + + if (id.drivertype > MAX_DISK_TYPES || + !get_driver(id.drivertype) || !id.name) + goto fail; + + dup = strdup(id.name); + if (!dup) + goto fail; + + new = disk_init(s, get_driver(id.drivertype), dup, pflags); + if (!new) + goto fail; + + err = new->drv->td_open(new, new->name, pflags); + if (err) + goto fail; + + err = d->drv->td_validate_parent(d, new, 0); + if (err) { + d->next = new; + goto fail; + } + + d = d->next = new; + free(id.name); + } + + s->info |= ((flags & TD_RDONLY) ? VDISK_READONLY : 0); + + if (err >= 0) + return 0; + + fail: + DPRINTF("failed opening disk\n"); + if (id.name) + free(id.name); + d = s->disks; + while (d) { + struct disk_driver *tmp = d->next; + d->drv->td_close(d); + free_driver(d); + d = tmp; + } + s->disks = NULL; + return -1; +} + +static int read_msg(char *buf) +{ + int length, len, msglen, tap_fd, *io_fd; + char *ptr, *path; + image_t *img; + msg_hdr_t *msg; + msg_newdev_t *msg_dev; + msg_pid_t *msg_pid; + struct tap_disk *drv; + int ret = -1; + struct td_state *s = NULL; + fd_list_entry_t *entry; + + length = read(fds[READ], buf, MSG_SIZE); + + if (length > 0 && length >= sizeof(msg_hdr_t)) + { + msg = (msg_hdr_t *)buf; + DPRINTF("Tapdisk: Received msg, len %d, type %d, UID %d\n", + length,msg->type,msg->cookie); + + switch (msg->type) { + case CTLMSG_PARAMS: + ptr = buf + sizeof(msg_hdr_t); + len = (length - sizeof(msg_hdr_t)); + path = calloc(1, len); + + memcpy(path, ptr, len); + DPRINTF("Received CTLMSG_PARAMS: [%s]\n", path); + + /*Assign driver*/ + drv = get_driver(msg->drivertype); + if (drv == NULL) + goto params_done; + + DPRINTF("Loaded driver: name [%s], type [%d]\n", + drv->disk_type, msg->drivertype); + + /* Allocate the disk structs */ + s = state_init(); + if (s == NULL) + goto params_done; + + /*Open file*/ + ret = open_disk(s, drv, path, + ((msg->readonly) ? TD_RDONLY : 0)); + if (ret) + goto params_done; + + entry = add_fd_entry(0, s); + entry->cookie = msg->cookie; + DPRINTF("Entered cookie %d\n", entry->cookie); + + memset(buf, 0x00, MSG_SIZE); + + params_done: + if (ret == 0) { + msglen = sizeof(msg_hdr_t) + sizeof(image_t); + msg->type = CTLMSG_IMG; + img = (image_t *)(buf + sizeof(msg_hdr_t)); + img->size = s->size; + img->secsize = s->sector_size; + img->info = s->info; + } else { + msglen = sizeof(msg_hdr_t); + msg->type = CTLMSG_IMG_FAIL; + msg->len = msglen; + } + len = write(fds[WRITE], buf, msglen); + free(path); + return 1; + + case CTLMSG_NEWDEV: + msg_dev = (msg_newdev_t *)(buf + sizeof(msg_hdr_t)); + + s = get_state(msg->cookie); + DPRINTF("Retrieving state, cookie %d.....[%s]\n", + msg->cookie, (s == NULL ? "FAIL":"OK")); + if (s != NULL) { + ret = ((map_new_dev(s, msg_dev->devnum) + == msg_dev->devnum ? 0: -1)); + connected_disks++; + } + + memset(buf, 0x00, MSG_SIZE); + msglen = sizeof(msg_hdr_t); + msg->type = (ret == 0 ? CTLMSG_NEWDEV_RSP + : CTLMSG_NEWDEV_FAIL); + msg->len = msglen; + + len = write(fds[WRITE], buf, msglen); + return 1; + + case CTLMSG_CLOSE: + s = get_state(msg->cookie); + if (s) unmap_disk(s); + + connected_disks--; + sig_handler(SIGINT); + + return 1; + + case CTLMSG_PID: + memset(buf, 0x00, MSG_SIZE); + msglen = sizeof(msg_hdr_t) + sizeof(msg_pid_t); + msg->type = CTLMSG_PID_RSP; + msg->len = msglen; + + msg_pid = (msg_pid_t *)(buf + sizeof(msg_hdr_t)); + process = getpid(); + msg_pid->pid = process; + + len = write(fds[WRITE], buf, msglen); + return 1; + + default: + return 0; + } + } + return 0; +} + +static inline int write_rsp_to_ring(struct td_state *s, blkif_response_t *rsp) +{ + tapdev_info_t *info = s->ring_info; + blkif_response_t *rsp_d; + + rsp_d = RING_GET_RESPONSE(&info->fe_ring, info->fe_ring.rsp_prod_pvt); + memcpy(rsp_d, rsp, sizeof(blkif_response_t)); + info->fe_ring.rsp_prod_pvt++; + + return 0; +} + +static inline void kick_responses(struct td_state *s) +{ + tapdev_info_t *info = s->ring_info; + + if (info->fe_ring.rsp_prod_pvt != info->fe_ring.sring->rsp_prod) + { + RING_PUSH_RESPONSES(&info->fe_ring); + ioctl(info->fd, BLKTAP_IOCTL_KICK_FE); + } +} + +static void io_done(struct disk_driver *dd, int sid) +{ + struct tap_disk *drv = dd->drv; + + if (!run) return; /*We have received signal to close*/ + + if (sid > MAX_IOFD || drv->td_do_callbacks(dd, sid) > 0) + kick_responses(dd->td_state); + + return; +} + +static inline uint64_t +segment_start(blkif_request_t *req, int sidx) +{ + int i; + uint64_t start = req->sector_number; + + for (i = 0; i < sidx; i++) + start += (req->seg[i].last_sect - req->seg[i].first_sect + 1); + + return start; +} + +uint64_t sends, responds; +static int send_responses(struct disk_driver *dd, int res, + uint64_t sector, int nr_secs, int idx, void *private) +{ + pending_req_t *preq; + blkif_request_t *req; + int responses_queued = 0; + struct td_state *s = dd->td_state; + blkif_t *blkif = s->blkif; + int sidx = (int)(long)private, secs_done = nr_secs; + + if ( (idx > MAX_REQUESTS-1) ) + { + DPRINTF("invalid index returned(%u)!\n", idx); + return 0; + } + preq = &blkif->pending_list[idx]; + req = &preq->req; + + if (res == BLK_NOT_ALLOCATED) { + res = do_cow_read(dd, req, sidx, sector, nr_secs); + if (res >= 0) { + secs_done = res; + res = 0; + } else + secs_done = 0; + } + + preq->secs_pending -= secs_done; + + if (res == -EBUSY && preq->submitting) + return -EBUSY; /* propagate -EBUSY back to higher layers */ + if (res) + preq->status = BLKIF_RSP_ERROR; + + if (!preq->submitting && preq->secs_pending == 0) + { + blkif_request_t tmp; + blkif_response_t *rsp; + + tmp = preq->req; + rsp = (blkif_response_t *)req; + + rsp->id = tmp.id; + rsp->operation = tmp.operation; + rsp->status = preq->status; + + write_rsp_to_ring(s, rsp); + responses_queued++; + } + return responses_queued; +} + +int do_cow_read(struct disk_driver *dd, blkif_request_t *req, + int sidx, uint64_t sector, int nr_secs) +{ + char *page; + int ret, early; + uint64_t seg_start, seg_end; + struct td_state *s = dd->td_state; + tapdev_info_t *info = s->ring_info; + struct disk_driver *parent = dd->next; + + seg_start = segment_start(req, sidx); + seg_end = seg_start + req->seg[sidx].last_sect + 1; + + ASSERT(sector >= seg_start && sector + nr_secs <= seg_end); + + page = (char *)MMAP_VADDR(info->vstart, + (unsigned long)req->id, sidx); + page += (req->seg[sidx].first_sect << SECTOR_SHIFT); + page += ((sector - seg_start) << SECTOR_SHIFT); + + if (!parent) { + memset(page, 0, nr_secs << SECTOR_SHIFT); + return nr_secs; + } + + /* reissue request to backing file */ + ret = parent->drv->td_queue_read(parent, sector, nr_secs, + page, send_responses, + req->id, (void *)(long)sidx); + if (ret > 0) + parent->early += ret; + + return ((ret >= 0) ? 0 : ret); +} + +static void get_io_request(struct td_state *s) +{ + RING_IDX rp, rc, j, i; + blkif_request_t *req; + int idx, nsects, ret; + uint64_t sector_nr; + char *page; + int early = 0; /* count early completions */ + struct disk_driver *dd = s->disks; + struct tap_disk *drv = dd->drv; + blkif_t *blkif = s->blkif; + tapdev_info_t *info = s->ring_info; + int page_size = getpagesize(); + + if (!run) return; /*We have received signal to close*/ + + rp = info->fe_ring.sring->req_prod; + xen_rmb(); + for (j = info->fe_ring.req_cons; j != rp; j++) + { + int done = 0, start_seg = 0; + + req = NULL; + req = RING_GET_REQUEST(&info->fe_ring, j); + ++info->fe_ring.req_cons; + + if (req == NULL) continue; + + idx = req->id; + + if (info->busy.req) { + /* continue where we left off last time */ + ASSERT(info->busy.req == req); + start_seg = info->busy.seg_idx; + sector_nr = segment_start(req, start_seg); + info->busy.seg_idx = 0; + info->busy.req = NULL; + } else { + ASSERT(blkif->pending_list[idx].secs_pending == 0); + memcpy(&blkif->pending_list[idx].req, + req, sizeof(*req)); + blkif->pending_list[idx].status = BLKIF_RSP_OKAY; + blkif->pending_list[idx].submitting = 1; + sector_nr = req->sector_number; + } + + if ((dd->flags & TD_RDONLY) && + (req->operation == BLKIF_OP_WRITE)) { + blkif->pending_list[idx].status = BLKIF_RSP_ERROR; + goto send_response; + } + + for (i = start_seg; i < req->nr_segments; i++) { + nsects = req->seg[i].last_sect - + req->seg[i].first_sect + 1; + + if ((req->seg[i].last_sect >= page_size >> 9) || + (nsects <= 0)) + continue; + + page = (char *)MMAP_VADDR(info->vstart, + (unsigned long)req->id, i); + page += (req->seg[i].first_sect << SECTOR_SHIFT); + + if (sector_nr >= s->size) { + DPRINTF("Sector request failed:\n"); + DPRINTF("%s request, idx [%d,%d] size [%llu], " + "sector [%llu,%llu]\n", + (req->operation == BLKIF_OP_WRITE ? + "WRITE" : "READ"), + idx,i, + (long long unsigned) + nsects<pending_list[idx].secs_pending += nsects; + + switch (req->operation) + { + case BLKIF_OP_WRITE: + ret = drv->td_queue_write(dd, sector_nr, + nsects, page, + send_responses, + idx, (void *)(long)i); + if (ret > 0) dd->early += ret; + else if (ret == -EBUSY) { + /* put req back on queue */ + --info->fe_ring.req_cons; + info->busy.req = req; + info->busy.seg_idx = i; + goto out; + } + break; + case BLKIF_OP_READ: + ret = drv->td_queue_read(dd, sector_nr, + nsects, page, + send_responses, + idx, (void *)(long)i); + if (ret > 0) dd->early += ret; + else if (ret == -EBUSY) { + /* put req back on queue */ + --info->fe_ring.req_cons; + info->busy.req = req; + info->busy.seg_idx = i; + goto out; + } + break; + default: + DPRINTF("Unknown block operation\n"); + break; + } + sector_nr += nsects; + } + send_response: + blkif->pending_list[idx].submitting = 0; + /* force write_rsp_to_ring for synchronous case */ + if (blkif->pending_list[idx].secs_pending == 0) + dd->early += send_responses(dd, 0, 0, 0, idx, + (void *)(long)0); + } + + out: + /*Batch done*/ + td_for_each_disk(s, dd) { + dd->early += dd->drv->td_submit(dd); + if (dd->early > 0) { + io_done(dd, MAX_IOFD + 1); + dd->early = 0; + } + } + + return; +} + +int main(int argc, char *argv[]) +{ + int len, msglen, ret; + char *p, *buf; + fd_set readfds, writefds; + fd_list_entry_t *ptr; + struct td_state *s; + char openlogbuf[128]; + + if (argc != 3) usage(); + + daemonize(); + + snprintf(openlogbuf, sizeof(openlogbuf), "TAPDISK[%d]", getpid()); + openlog(openlogbuf, LOG_CONS|LOG_ODELAY, LOG_DAEMON); + /*Setup signal handlers*/ + signal (SIGBUS, sig_handler); + signal (SIGINT, sig_handler); + + /*Open the control channel*/ + fds[READ] = open(argv[1],O_RDWR|O_NONBLOCK); + fds[WRITE] = open(argv[2],O_RDWR|O_NONBLOCK); + + if ( (fds[READ] < 0) || (fds[WRITE] < 0) ) + { + DPRINTF("FD open failed [%d,%d]\n", fds[READ], fds[WRITE]); + exit(-1); + } + + buf = calloc(MSG_SIZE, 1); + + if (buf == NULL) + { + DPRINTF("ERROR: allocating memory.\n"); + exit(-1); + } + + while (run) + { + ret = 0; + FD_ZERO(&readfds); + FD_SET(fds[READ], &readfds); + maxfds = fds[READ]; + + /*Set all tap fds*/ + LOCAL_FD_SET(&readfds); + + /*Wait for incoming messages*/ + ret = select(maxfds + 1, &readfds, (fd_set *) 0, + (fd_set *) 0, NULL); + + if (ret > 0) + { + ptr = fd_start; + while (ptr != NULL) { + int progress_made = 0; + struct disk_driver *dd; + tapdev_info_t *info = ptr->s->ring_info; + + td_for_each_disk(ptr->s, dd) { + if (dd->io_fd[READ] && + FD_ISSET(dd->io_fd[READ], + &readfds)) { + io_done(dd, READ); + progress_made = 1; + } + } + + /* completed io from above may have + * queued new requests on chained disks */ + if (progress_made) { + td_for_each_disk(ptr->s, dd) { + dd->early += + dd->drv->td_submit(dd); + if (dd->early > 0) { + io_done(dd, + MAX_IOFD + 1); + dd->early = 0; + } + } + } + + if (FD_ISSET(ptr->tap_fd, &readfds) || + (info->busy.req && progress_made)) + get_io_request(ptr->s); + + ptr = ptr->next; + } + + if (FD_ISSET(fds[READ], &readfds)) + read_msg(buf); + } + } + free(buf); + close(fds[READ]); + close(fds[WRITE]); + + ptr = fd_start; + while (ptr != NULL) { + s = ptr->s; + unmap_disk(s); + close(ptr->tap_fd); + ptr = ptr->next; + } + closelog(); + + return 0; +} diff --git a/tools/blktap/drivers/tapdisk.h b/tools/blktap/drivers/tapdisk.h new file mode 100644 index 0000000..c8a2182 --- /dev/null +++ b/tools/blktap/drivers/tapdisk.h @@ -0,0 +1,269 @@ +/* tapdisk.h + * + * Generic disk interface for blktap-based image adapters. + * + * (c) 2006 Andrew Warfield and Julian Chesterfield + * + * Some notes on the tap_disk interface: + * + * tap_disk aims to provide a generic interface to easily implement new + * types of image accessors. The structure-of-function-calls is similar + * to disk interfaces used in qemu/denali/etc, with the significant + * difference being the expectation of asynchronous rather than synchronous + * I/O. The asynchronous interface is intended to allow lots of requests to + * be pipelined through a disk, without the disk requiring any of its own + * threads of control. As such, a batch of requests is delivered to the disk + * using: + * + * td_queue_[read,write]() + * + * and passing in a completion callback, which the disk is responsible for + * tracking. The end of a back is marked with a call to: + * + * td_submit() + * + * The disk implementation must provide a file handle, which is used to + * indicate that it needs to do work. tapdisk will add this file handle + * (returned from td_get_fd()) to it's poll set, and will call into the disk + * using td_do_callbacks() whenever there is data pending. + * + * Two disk implementations demonstrate how this interface may be used to + * implement disks with both asynchronous and synchronous calls. block-aio.c + * maps this interface down onto the linux libaio calls, while block-sync uses + * normal posix read/write. + * + * A few things to realize about the sync case, which doesn't need to defer + * io completions: + * + * - td_queue_[read,write]() call read/write directly, and then call the + * callback immediately. The MUST then return a value greater than 0 + * in order to tell tapdisk that requests have finished early, and to + * force responses to be kicked to the clents. + * + * - The fd used for poll is an otherwise unused pipe, which allows poll to + * be safely called without ever returning anything. + * + * NOTE: tapdisk uses the number of sectors submitted per request as a + * ref count. Plugins must use the callback function to communicate the + * completion--or error--of every sector submitted to them. + * + * td_get_parent_id returns: + * 0 if parent id successfully retrieved + * TD_NO_PARENT if no parent exists + * -errno on error + */ + +#ifndef TAPDISK_H_ +#define TAPDISK_H_ + +#include +#include +#include +#include "blktaplib.h" + +/*If enabled, log all debug messages to syslog*/ +#if 1 +#define DPRINTF(_f, _a...) syslog( LOG_DEBUG, __FILE__ ":%d: " _f , __LINE__, ## _a ) +#else +#define DPRINTF(_f, _a...) ((void)0) +#endif + +/* Things disks need to know about, these should probably be in a higher-level + * header. */ +#define MAX_SEGMENTS_PER_REQ 11 +#define SECTOR_SHIFT 9 +#define DEFAULT_SECTOR_SIZE 512 + +#define MAX_IOFD 2 + +#define BLK_NOT_ALLOCATED 99 +#define TD_NO_PARENT 1 + +typedef uint32_t td_flag_t; + +#define TD_RDONLY 1 + +struct td_state; +struct tap_disk; + +struct disk_id { + char *name; + int drivertype; +}; + +struct disk_driver { + int early; + char *name; + void *private; + td_flag_t flags; + int io_fd[MAX_IOFD]; + struct tap_disk *drv; + struct td_state *td_state; + struct disk_driver *next; +}; + +/* This structure represents the state of an active virtual disk. */ +struct td_state { + struct disk_driver *disks; + void *blkif; + void *image; + void *ring_info; + void *fd_entry; + uint64_t sector_size; + uint64_t size; + unsigned int info; +}; + +/* Prototype of the callback to activate as requests complete. */ +typedef int (*td_callback_t)(struct disk_driver *dd, int res, uint64_t sector, + int nb_sectors, int id, void *private); + +/* Structure describing the interface to a virtual disk implementation. */ +/* See note at the top of this file describing this interface. */ +struct tap_disk { + const char *disk_type; + int private_data_size; + int (*td_open) (struct disk_driver *dd, + const char *name, td_flag_t flags); + int (*td_queue_read) (struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *prv); + int (*td_queue_write) (struct disk_driver *dd, uint64_t sector, + int nb_sectors, char *buf, td_callback_t cb, + int id, void *prv); + int (*td_submit) (struct disk_driver *dd); + int (*td_close) (struct disk_driver *dd); + int (*td_do_callbacks) (struct disk_driver *dd, int sid); + int (*td_get_parent_id) (struct disk_driver *dd, struct disk_id *id); + int (*td_validate_parent)(struct disk_driver *dd, + struct disk_driver *p, td_flag_t flags); +}; + +typedef struct disk_info { + int idnum; + char name[50]; /* e.g. "RAMDISK" */ + char handle[10]; /* xend handle, e.g. 'ram' */ + int single_handler; /* is there a single controller for all */ + /* instances of disk type? */ +#ifdef TAPDISK + struct tap_disk *drv; +#endif +} disk_info_t; + +void debug_fe_ring(struct td_state *s); + +extern struct tap_disk tapdisk_aio; +extern struct tap_disk tapdisk_sync; +extern struct tap_disk tapdisk_vmdk; +extern struct tap_disk tapdisk_ram; +extern struct tap_disk tapdisk_qcow; +extern struct tap_disk tapdisk_qcow2; + +#define MAX_DISK_TYPES 20 + +#define DISK_TYPE_AIO 0 +#define DISK_TYPE_SYNC 1 +#define DISK_TYPE_VMDK 2 +#define DISK_TYPE_RAM 3 +#define DISK_TYPE_QCOW 4 +#define DISK_TYPE_QCOW2 5 +#define DISK_TYPE_IOEMU 6 + + +/*Define Individual Disk Parameters here */ +static disk_info_t aio_disk = { + DISK_TYPE_AIO, + "raw image (aio)", + "aio", + 0, +#ifdef TAPDISK + &tapdisk_aio, +#endif +}; + +static disk_info_t sync_disk = { + DISK_TYPE_SYNC, + "raw image (sync)", + "sync", + 0, +#ifdef TAPDISK + &tapdisk_sync, +#endif +}; + +static disk_info_t vmdk_disk = { + DISK_TYPE_VMDK, + "vmware image (vmdk)", + "vmdk", + 1, +#ifdef TAPDISK + &tapdisk_vmdk, +#endif +}; + +static disk_info_t ram_disk = { + DISK_TYPE_RAM, + "ramdisk image (ram)", + "ram", + 1, +#ifdef TAPDISK + &tapdisk_ram, +#endif +}; + +static disk_info_t qcow_disk = { + DISK_TYPE_QCOW, + "qcow disk (qcow)", + "qcow", + 0, +#ifdef TAPDISK + &tapdisk_qcow, +#endif +}; + +static disk_info_t qcow2_disk = { + DISK_TYPE_QCOW2, + "qcow2 disk (qcow2)", + "qcow2", + 0, +#ifdef TAPDISK + &tapdisk_qcow2, +#endif +}; + +static disk_info_t ioemu_disk = { + DISK_TYPE_IOEMU, + "ioemu disk", + "ioemu", + 1, +#ifdef TAPDISK + NULL +#endif +}; + +/*Main disk info array */ +static disk_info_t *dtypes[] = { + &aio_disk, + &sync_disk, + &vmdk_disk, + &ram_disk, + &qcow_disk, + &qcow2_disk, + &ioemu_disk, +}; + +typedef struct driver_list_entry { + struct blkif *blkif; + struct driver_list_entry **pprev, *next; +} driver_list_entry_t; + +typedef struct fd_list_entry { + int cookie; + int tap_fd; + struct td_state *s; + struct fd_list_entry **pprev, *next; +} fd_list_entry_t; + +int qcow_create(const char *filename, uint64_t total_size, + const char *backing_file, int flags); +#endif /*TAPDISK_H_*/ diff --git a/tools/blktap/lib/Makefile b/tools/blktap/lib/Makefile new file mode 100644 index 0000000..aedf7dc --- /dev/null +++ b/tools/blktap/lib/Makefile @@ -0,0 +1,62 @@ +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 3.0 +MINOR = 0 +SONAME = libblktap.so.$(MAJOR) + +CFLAGS += -I. +CFLAGS += $(CFLAGS_libxenctrl) +CFLAGS += $(CFLAGS_libxenstore) +LDFLAGS += $(LDFLAGS_libxenstore) + +SRCS := +SRCS += xenbus.c blkif.c xs_api.c + +CFLAGS += -Werror +CFLAGS += -Wno-unused +CFLAGS += -fPIC +# get asprintf(): +CFLAGS += -D _GNU_SOURCE + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +OBJS = $(SRCS:.c=.o) +OBJS_PIC = $(SRCS:.c=.opic) +IBINS := + +LIB = libblktap.a libblktap.so.$(MAJOR).$(MINOR) + +.PHONY: all +all: $(LIB) + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR) + $(INSTALL_DATA) $(LIB) $(DESTDIR)$(LIBDIR) + ln -sf libblktap.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libblktap.so.$(MAJOR) + ln -sf libblktap.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libblktap.so + $(INSTALL_DATA) blktaplib.h $(DESTDIR)$(INCLUDEDIR) + +.PHONY: clean +clean: + rm -rf *.a *.so* *.o *.opic *.rpm $(LIB) *~ $(DEPS) xen TAGS + +libblktap.so.$(MAJOR).$(MINOR): $(OBJS_PIC) + $(CC) $(CFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,$(SONAME) $(SHLIB_CFLAGS) \ + $(LDFLAGS) -o $@ $^ + ln -sf libblktap.so.$(MAJOR).$(MINOR) libblktap.so.$(MAJOR) + ln -sf libblktap.so.$(MAJOR) libblktap.so + +libblktap.a: $(OBJS) + $(AR) rc $@ $^ + +.PHONY: TAGS +TAGS: + etags -t $(SRCS) *.h + +-include $(DEPS) + diff --git a/tools/blktap/lib/blkif.c b/tools/blktap/lib/blkif.c new file mode 100644 index 0000000..9a19596 --- /dev/null +++ b/tools/blktap/lib/blkif.c @@ -0,0 +1,185 @@ +/* + * tools/blktap_user/blkif.c + * + * The blkif interface for blktap. A blkif describes an in-use virtual disk. + * (c) 2005 Andrew Warfield and Julian Chesterfield + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "blktaplib.h" + +#if 0 +#define DPRINTF(_f, _a...) printf ( _f , ## _a ) +#else +#define DPRINTF(_f, _a...) ((void)0) +#endif + +#define BLKIF_HASHSZ 1024 +#define BLKIF_HASH(_d,_h) (((int)(_d)^(int)(_h))&(BLKIF_HASHSZ-1)) + +static blkif_t *blkif_hash[BLKIF_HASHSZ]; + +blkif_t *blkif_find_by_handle(domid_t domid, unsigned int handle) +{ + blkif_t *blkif = blkif_hash[BLKIF_HASH(domid, handle)]; + while ( (blkif != NULL) && + ((blkif->domid != domid) || (blkif->handle != handle)) ) + blkif = blkif->hash_next; + return blkif; +} + +blkif_t *alloc_blkif(domid_t domid) +{ + blkif_t *blkif; + DPRINTF("Alloc_blkif called [%d]\n",domid); + blkif = (blkif_t *)malloc(sizeof(blkif_t)); + if (!blkif) + return NULL; + memset(blkif, 0, sizeof(*blkif)); + blkif->domid = domid; + blkif->devnum = -1; + return blkif; +} + +/*Controller callbacks*/ +static int (*new_devmap_hook)(blkif_t *blkif) = NULL; +void register_new_devmap_hook(int (*fn)(blkif_t *blkif)) +{ + new_devmap_hook = fn; +} + +static int (*new_unmap_hook)(blkif_t *blkif) = NULL; +void register_new_unmap_hook(int (*fn)(blkif_t *blkif)) +{ + new_unmap_hook = fn; +} + +static int (*new_blkif_hook)(blkif_t *blkif) = NULL; +void register_new_blkif_hook(int (*fn)(blkif_t *blkif)) +{ + new_blkif_hook = fn; +} + +int blkif_init(blkif_t *blkif, long int handle, long int pdev, + long int readonly) +{ + domid_t domid; + blkif_t **pblkif; + int devnum; + + if (blkif == NULL) + return -EINVAL; + + domid = blkif->domid; + blkif->handle = handle; + blkif->pdev = pdev; + blkif->readonly = readonly; + + /* + * Call out to the new_blkif_hook. + * The tap application should define this, + * and it should return having set blkif->ops + * + */ + if (new_blkif_hook == NULL) + { + DPRINTF("Probe detected a new blkif, but no new_blkif_hook!"); + return -1; + } + if (new_blkif_hook(blkif)!=0) { + DPRINTF("BLKIF: Image open failed\n"); + return -1; + } + + /* Now wire it in. */ + pblkif = &blkif_hash[BLKIF_HASH(domid, handle)]; + DPRINTF("Created hash entry: %d [%d,%ld]\n", + BLKIF_HASH(domid, handle), domid, handle); + + while ( *pblkif != NULL ) + { + if ( ((*pblkif)->domid == domid) && + ((*pblkif)->handle == handle) ) + { + DPRINTF("Could not create blkif: already exists\n"); + return -1; + } + pblkif = &(*pblkif)->hash_next; + } + blkif->hash_next = NULL; + *pblkif = blkif; + + if (new_devmap_hook == NULL) + { + DPRINTF("Probe setting up new blkif but no devmap hook!"); + return -1; + } + + devnum = new_devmap_hook(blkif); + if (devnum == -1) + return -1; + blkif->devnum = devnum; + + return 0; +} + +void free_blkif(blkif_t *blkif) +{ + blkif_t **pblkif, *curs; + image_t *image; + + pblkif = &blkif_hash[BLKIF_HASH(blkif->domid, blkif->handle)]; + while ( (curs = *pblkif) != NULL ) + { + if ( blkif == curs ) + { + *pblkif = curs->hash_next; + } + pblkif = &curs->hash_next; + } + if (blkif != NULL) { + if ((image=(image_t *)blkif->prv)!=NULL) { + free(blkif->prv); + } + if (blkif->info!=NULL) { + free(blkif->info); + } + if (new_unmap_hook != NULL) new_unmap_hook(blkif); + free(blkif); + } +} + +void __init_blkif(void) +{ + memset(blkif_hash, 0, sizeof(blkif_hash)); +} diff --git a/tools/blktap/lib/blktaplib.h b/tools/blktap/lib/blktaplib.h new file mode 100644 index 0000000..5511bf2 --- /dev/null +++ b/tools/blktap/lib/blktaplib.h @@ -0,0 +1,230 @@ +/* blktaplib.h + * + * Blktap library userspace code. + * + * (c) 2005 Andrew Warfield and Julian Chesterfield + * + * 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 __BLKTAPLIB_H__ +#define __BLKTAPLIB_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLK_RING_SIZE __RING_SIZE((blkif_sring_t *)0, XC_PAGE_SIZE) + +/* size of the extra VMA area to map in attached pages. */ +#define BLKTAP_VMA_PAGES BLK_RING_SIZE + +/* blktap IOCTLs: These must correspond with the blktap driver ioctls*/ +#define BLKTAP_IOCTL_KICK_FE 1 +#define BLKTAP_IOCTL_KICK_BE 2 +#define BLKTAP_IOCTL_SETMODE 3 +#define BLKTAP_IOCTL_SENDPID 4 +#define BLKTAP_IOCTL_NEWINTF 5 +#define BLKTAP_IOCTL_MINOR 6 +#define BLKTAP_IOCTL_MAJOR 7 +#define BLKTAP_QUERY_ALLOC_REQS 8 +#define BLKTAP_IOCTL_FREEINTF 9 +#define BLKTAP_IOCTL_NEWINTF_EXT 50 +#define BLKTAP_IOCTL_PRINT_IDXS 100 + +/* blktap switching modes: (Set with BLKTAP_IOCTL_SETMODE) */ +#define BLKTAP_MODE_PASSTHROUGH 0x00000000 /* default */ +#define BLKTAP_MODE_INTERCEPT_FE 0x00000001 +#define BLKTAP_MODE_INTERCEPT_BE 0x00000002 + +#define BLKTAP_MODE_INTERPOSE \ + (BLKTAP_MODE_INTERCEPT_FE | BLKTAP_MODE_INTERCEPT_BE) + +static inline int BLKTAP_MODE_VALID(unsigned long arg) +{ + return ( + ( arg == BLKTAP_MODE_PASSTHROUGH ) || + ( arg == BLKTAP_MODE_INTERCEPT_FE ) || + ( arg == BLKTAP_MODE_INTERPOSE ) ); +} + +#define MAX_REQUESTS BLK_RING_SIZE + +#define BLKTAP_IOCTL_KICK 1 +#define MAX_PENDING_REQS BLK_RING_SIZE +#define BLKTAP_DEV_DIR "/dev/xen" +#define BLKTAP_DEV_NAME "blktap" +#define BLKTAP_DEV_MINOR 0 +#define BLKTAP_CTRL_DIR "/var/run/tap" + +extern int blktap_major; + +#define BLKTAP_RING_PAGES 1 /* Front */ +#define BLKTAP_MMAP_REGION_SIZE (BLKTAP_RING_PAGES + MMAP_PAGES) + +struct blkif; + +typedef struct { + blkif_request_t req; + struct blkif *blkif; + int submitting; + int secs_pending; + int16_t status; +} pending_req_t; + +struct blkif_ops { + unsigned long long (*get_size)(struct blkif *blkif); + unsigned long (*get_secsize)(struct blkif *blkif); + unsigned int (*get_info)(struct blkif *blkif); +}; + +typedef struct blkif { + domid_t domid; + long int handle; + + long int pdev; + long int readonly; + + enum { DISCONNECTED, DISCONNECTING, CONNECTED } state; + + struct blkif_ops *ops; + struct blkif *hash_next; + + void *prv; /* device-specific data */ + void *info; /*Image parameter passing */ + pending_req_t pending_list[MAX_REQUESTS]; + int devnum; + int fds[2]; + int be_id; + int major; + int minor; + pid_t tappid; + int drivertype; + uint16_t cookie; +} blkif_t; + +typedef struct blkif_info { + char *params; +} blkif_info_t; + +void register_new_devmap_hook(int (*fn)(blkif_t *blkif)); +void register_new_unmap_hook(int (*fn)(blkif_t *blkif)); +void register_new_blkif_hook(int (*fn)(blkif_t *blkif)); +blkif_t *blkif_find_by_handle(domid_t domid, unsigned int handle); +blkif_t *alloc_blkif(domid_t domid); +int blkif_init(blkif_t *blkif, long int handle, long int pdev, + long int readonly); +void free_blkif(blkif_t *blkif); +void __init_blkif(void); + +typedef struct busy_state { + int seg_idx; + blkif_request_t *req; +} busy_state_t; + +typedef struct tapdev_info { + int fd; + char *mem; + blkif_sring_t *sring; + blkif_back_ring_t fe_ring; + unsigned long vstart; + blkif_t *blkif; + busy_state_t busy; +} tapdev_info_t; + +typedef struct domid_translate { + unsigned short domid; + unsigned short busid; +} domid_translate_t ; + +typedef struct domid_translate_ext { + unsigned short domid; + uint32_t busid; +} domid_translate_ext_t ; + +typedef struct image { + unsigned long long size; + unsigned long secsize; + unsigned int info; +} image_t; + +/* 16-byte message header, immediately followed by message payload. */ +typedef struct msg_hdr { + uint16_t type; + uint16_t len; + uint16_t drivertype; + uint16_t cookie; + uint8_t readonly; + uint8_t pad[7]; +} msg_hdr_t; + +typedef struct msg_newdev { + uint8_t devnum; + uint16_t domid; +} msg_newdev_t; + +typedef struct msg_pid { + pid_t pid; +} msg_pid_t; + +#define READ 0 +#define WRITE 1 + +/*Control Messages between manager and tapdev*/ +#define CTLMSG_PARAMS 1 +#define CTLMSG_IMG 2 +#define CTLMSG_IMG_FAIL 3 +#define CTLMSG_NEWDEV 4 +#define CTLMSG_NEWDEV_RSP 5 +#define CTLMSG_NEWDEV_FAIL 6 +#define CTLMSG_CLOSE 7 +#define CTLMSG_CLOSE_RSP 8 +#define CTLMSG_PID 9 +#define CTLMSG_PID_RSP 10 + +/* xenstore/xenbus: */ +#define DOMNAME "Domain-0" +int setup_probe_watch(struct xs_handle *h); + + +/* Abitrary values, must match the underlying driver... */ +#define MAX_TAP_DEV 100 + +/* Accessing attached data page mappings */ +#define MMAP_PAGES \ + (MAX_PENDING_REQS * BLKIF_MAX_SEGMENTS_PER_REQUEST) +#define MMAP_VADDR(_vstart,_req,_seg) \ + ((_vstart) + \ + ((_req) * BLKIF_MAX_SEGMENTS_PER_REQUEST * getpagesize()) + \ + ((_seg) * getpagesize())) + + +#endif /* __BLKTAPLIB_H__ */ diff --git a/tools/blktap/lib/list.h b/tools/blktap/lib/list.h new file mode 100644 index 0000000..c82242f --- /dev/null +++ b/tools/blktap/lib/list.h @@ -0,0 +1,59 @@ +/* + * list.h + * + * This is a subset of linux's list.h intended to be used in user-space. + * + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#ifdef LIST_HEAD +#undef LIST_HEAD +#endif + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +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) + +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; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) +#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)) + +#endif /* __LIST_H__ */ diff --git a/tools/blktap/lib/xenbus.c b/tools/blktap/lib/xenbus.c new file mode 100644 index 0000000..4fc56d6 --- /dev/null +++ b/tools/blktap/lib/xenbus.c @@ -0,0 +1,434 @@ +/* + * xenbus.c + * + * xenbus interface to the blocktap. + * + * this handles the top-half of integration with block devices through the + * store -- the tap driver negotiates the device channel etc, while the + * userland tap client needs to sort out the disk parameters etc. + * + * (c) 2005 Andrew Warfield and Julian Chesterfield + * + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blktaplib.h" +#include "list.h" +#include "xs_api.h" + +#if 0 +#define DPRINTF(_f, _a...) printf ( _f , ## _a ) +#else +#define DPRINTF(_f, _a...) ((void)0) +#endif + +struct backend_info +{ + /* our communications channel */ + blkif_t *blkif; + + long int frontend_id; + long int pdev; + long int readonly; + + char *backpath; + char *frontpath; + + struct list_head list; +}; + +static LIST_HEAD(belist); + +static int strsep_len(const char *str, char c, unsigned int len) +{ + unsigned int i; + + for (i = 0; str[i]; i++) + if (str[i] == c) { + if (len == 0) + return i; + len--; + } + return (len == 0) ? i : -ERANGE; +} + +static int get_be_id(const char *str) +{ + int len,end; + const char *ptr; + char *tptr, num[10]; + + len = strsep_len(str, '/', 6); + end = strlen(str); + if( (len < 0) || (end < 0) ) return -1; + + ptr = str + len + 1; + strncpy(num, ptr, end - len); + tptr = num + (end - (len + 1)); + *tptr = '\0'; + + return atoi(num); +} + +static struct backend_info *be_lookup_be(const char *bepath) +{ + struct backend_info *be; + + list_for_each_entry(be, &belist, list) + if (strcmp(bepath, be->backpath) == 0) + return be; + return (struct backend_info *)NULL; +} + +static int be_exists_be(const char *bepath) +{ + return (be_lookup_be(bepath) != NULL); +} + +static struct backend_info *be_lookup_fe(const char *fepath) +{ + struct backend_info *be; + + list_for_each_entry(be, &belist, list) + if (strcmp(fepath, be->frontpath) == 0) + return be; + return (struct backend_info *)NULL; +} + +static int backend_remove(struct xs_handle *h, struct backend_info *be) +{ + /* Unhook from be list. */ + list_del(&be->list); + + /* Free everything else. */ + if (be->blkif) { + DPRINTF("Freeing blkif dev [%d]\n",be->blkif->devnum); + free_blkif(be->blkif); + } + if (be->frontpath) + free(be->frontpath); + if (be->backpath) + free(be->backpath); + free(be); + return 0; +} + +static void ueblktap_setup(struct xs_handle *h, char *bepath) +{ + struct backend_info *be; + char *path = NULL, *p,*dev; + int len, er, deverr; + long int pdev = 0, handle; + blkif_info_t *blk; + + be = be_lookup_be(bepath); + if (be == NULL) + { + DPRINTF("ERROR: backend changed called for nonexistent " + "backend! (%s)\n", bepath); + goto fail; + } + + deverr = xs_gather(h, bepath, "physical-device", "%li", &pdev, NULL); + if (!deverr) { + DPRINTF("pdev set to %ld\n",pdev); + if (be->pdev && be->pdev != pdev) { + DPRINTF("changing physical-device not supported"); + goto fail; + } + be->pdev = pdev; + } + + /* Check to see if device is to be opened read-only. */ + deverr = xs_gather(h, bepath, "mode", NULL, &path, NULL); + if (deverr) { + DPRINTF("ERROR: could not find read/write mode\n"); + goto fail; + } else if (path[0] == 'r') + be->readonly = 1; + + if (be->blkif == NULL) { + /* Front end dir is a number, which is used as the handle. */ + p = strrchr(be->frontpath, '/') + 1; + handle = strtoul(p, NULL, 0); + + be->blkif = alloc_blkif(be->frontend_id); + if (be->blkif == NULL) + goto fail; + + be->blkif->be_id = get_be_id(bepath); + + /* Insert device specific info, */ + blk = malloc(sizeof(blkif_info_t)); + if (!blk) { + DPRINTF("Out of memory - blkif_info_t\n"); + goto fail; + } + er = xs_gather(h, bepath, "params", NULL, &blk->params, NULL); + if (er) + goto fail; + be->blkif->info = blk; + + if (deverr) { + /*Dev number was not available, try to set manually*/ + pdev = convert_dev_name_to_num(blk->params); + be->pdev = pdev; + } + + er = blkif_init(be->blkif, handle, be->pdev, be->readonly); + if (er != 0) { + DPRINTF("Unable to open device %s\n",blk->params); + goto fail; + } + + DPRINTF("[BECHG]: ADDED A NEW BLKIF (%s)\n", bepath); + } + + /* Supply the information about the device to xenstore */ + er = xs_printf(h, be->backpath, "sectors", "%llu", + be->blkif->ops->get_size(be->blkif)); + + if (er == 0) { + DPRINTF("ERROR: Failed writing sectors"); + goto fail; + } + + er = xs_printf(h, be->backpath, "sector-size", "%lu", + be->blkif->ops->get_secsize(be->blkif)); + + if (er == 0) { + DPRINTF("ERROR: Failed writing sector-size"); + goto fail; + } + + er = xs_printf(h, be->backpath, "info", "%u", + be->blkif->ops->get_info(be->blkif)); + + if (er == 0) { + DPRINTF("ERROR: Failed writing info"); + goto fail; + } + + be->blkif->state = CONNECTED; + DPRINTF("[SETUP] Complete\n\n"); + goto close; + +fail: + if ( (be != NULL) && (be->blkif != NULL) ) + backend_remove(h, be); +close: + if (path) + free(path); + return; +} + +/** + * Xenstore watch callback entry point. This code replaces the hotplug scripts, + * and as soon as the xenstore backend driver entries are created, this script + * gets called. + */ +static void ueblktap_probe(struct xs_handle *h, struct xenbus_watch *w, + const char *bepath_im) +{ + struct backend_info *be = NULL; + char *frontend = NULL, *bepath = NULL, *p; + int er, len; + blkif_t *blkif; + + + bepath = strdup(bepath_im); + + if (!bepath) { + DPRINTF("No path\n"); + return; + } + + /* + *asserts that xenstore structure is always 7 levels deep + *e.g. /local/domain/0/backend/vbd/1/2049 + */ + len = strsep_len(bepath, '/', 7); + if (len < 0) + goto free_be; + bepath[len] = '\0'; + + be = malloc(sizeof(*be)); + if (!be) { + DPRINTF("ERROR: allocating backend structure\n"); + goto free_be; + } + memset(be, 0, sizeof(*be)); + frontend = NULL; + + er = xs_gather(h, bepath, + "frontend-id", "%li", &be->frontend_id, + "frontend", NULL, &frontend, + NULL); + + if (er) { + /* + *Unable to find frontend entries, + *bus-id is no longer valid + */ + DPRINTF("ERROR: Frontend-id check failed, removing backend: " + "[%s]\n",bepath); + + /** + * BE info should already exist, + * free new mem and find old entry + */ + free(be); + be = be_lookup_be(bepath); + if ( (be != NULL) && (be->blkif != NULL) ) + backend_remove(h, be); + else goto free_be; + if (bepath) + free(bepath); + return; + } + + /* Are we already tracking this device? */ + if (be_exists_be(bepath)) + goto free_be; + + be->backpath = bepath; + be->frontpath = frontend; + + list_add(&be->list, &belist); + + DPRINTF("[PROBE]\tADDED NEW DEVICE (%s)\n", bepath); + DPRINTF("\tFRONTEND (%s),(%ld)\n", frontend,be->frontend_id); + + ueblktap_setup(h, bepath); + return; + + free_be: + if (frontend) + free(frontend); + if (bepath) + free(bepath); + if (be) + free(be); +} + +/** + *We set a general watch on the backend vbd directory + *ueblktap_probe is called for every update + *Our job is to monitor for new entries. As they + *are created, we initalise the state and attach a disk. + */ + +static int add_blockdevice_probe_watch(struct xs_handle *h, const char *domid) +{ + char *path; + struct xenbus_watch *vbd_watch; + + if (asprintf(&path, "/local/domain/%s/backend/tap", domid) == -1) + return -ENOMEM; + + vbd_watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch)); + if (!vbd_watch) { + DPRINTF("ERROR: unable to malloc vbd_watch [%s]\n", path); + return -EINVAL; + } + vbd_watch->node = path; + vbd_watch->callback = ueblktap_probe; + if (register_xenbus_watch(h, vbd_watch) != 0) { + DPRINTF("ERROR: adding vbd probe watch %s\n", path); + return -EINVAL; + } + return 0; +} + +/* Asynch callback to check for /local/domain//name */ +static void check_dom(struct xs_handle *h, struct xenbus_watch *w, + const char *bepath_im) +{ + char *domid; + + domid = get_dom_domid(h); + if (domid == NULL) + return; + + add_blockdevice_probe_watch(h, domid); + free(domid); + unregister_xenbus_watch(h, w); +} + +/* We must wait for xend to register /local/domain/ */ +static int watch_for_domid(struct xs_handle *h) +{ + struct xenbus_watch *domid_watch; + char *path = NULL; + + if (asprintf(&path, "/local/domain") == -1) + return -ENOMEM; + + domid_watch = malloc(sizeof(struct xenbus_watch)); + if (domid_watch == NULL) { + DPRINTF("ERROR: unable to malloc domid_watch [%s]\n", path); + return -EINVAL; + } + + domid_watch->node = path; + domid_watch->callback = check_dom; + + if (register_xenbus_watch(h, domid_watch) != 0) { + DPRINTF("ERROR: adding vbd probe watch %s\n", path); + return -EINVAL; + } + + DPRINTF("Set async watch for /local/domain\n"); + + return 0; +} + +int setup_probe_watch(struct xs_handle *h) +{ + char *domid; + int ret; + + domid = get_dom_domid(h); + if (domid == NULL) + return watch_for_domid(h); + + ret = add_blockdevice_probe_watch(h, domid); + free(domid); + return ret; +} diff --git a/tools/blktap/lib/xs_api.c b/tools/blktap/lib/xs_api.c new file mode 100644 index 0000000..93d1cb9 --- /dev/null +++ b/tools/blktap/lib/xs_api.c @@ -0,0 +1,360 @@ +/* + * xs_api.c + * + * blocktap interface functions to xenstore + * + * (c) 2005 Andrew Warfield and Julian Chesterfield + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blktaplib.h" +#include "list.h" +#include "xs_api.h" + +#if 0 +#define DPRINTF(_f, _a...) printf ( _f , ## _a ) +#else +#define DPRINTF(_f, _a...) ((void)0) +#endif + +static LIST_HEAD(watches); +#define BASE_DEV_VAL 2048 + +int xs_gather(struct xs_handle *xs, const char *dir, ...) +{ + va_list ap; + const char *name; + char *path, **e; + int ret = 0, num,i; + unsigned int len; + xs_transaction_t xth; + +again: + if ( (xth = xs_transaction_start(xs)) == XBT_NULL) { + DPRINTF("unable to start xs trasanction\n"); + ret = ENOMEM; + return ret; + } + + 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) + { + printf("allocation error in xs_gather!\n"); + ret = ENOMEM; + break; + } + + p = xs_read(xs, xth, path, &len); + + + 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); + + if (!xs_transaction_end(xs, xth, ret)) { + if (ret == 0 && errno == EAGAIN) + goto again; + else + ret = errno; + } + + return ret; +} + + +/* Single printf and write: returns -errno or 0. */ +int xs_printf(struct xs_handle *h, const char *dir, const char *node, + const char *fmt, ...) +{ + char *buf, *path; + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&buf, fmt, ap); + va_end(ap); + + if (ret == -1) + return ENOMEM; + if (asprintf(&path, "%s/%s", dir, node) == -1) { + free(buf); + return ENOMEM; + } + + ret = xs_write(h, XBT_NULL, path, buf, strlen(buf)); + + free(buf); + free(path); + + return ret; +} + + +int xs_exists(struct xs_handle *h, const char *path) +{ + char **d; + unsigned int num; + xs_transaction_t xth; + + if ( (xth = xs_transaction_start(h)) == XBT_NULL) { + printf("unable to start xs trasanction\n"); + return 0; + } + + d = xs_directory(h, xth, path, &num); + xs_transaction_end(h, xth, 0); + if (d == NULL) + return 0; + free(d); + return 1; +} + + + +/** + * This assumes that the domain name we are looking for is unique. + * Name parameter Domain-0 + */ +char *get_dom_domid(struct xs_handle *h) +{ + char **e, *val, *domid = NULL; + unsigned int num, len; + int i; + char *path; + xs_transaction_t xth; + + if ( (xth = xs_transaction_start(h)) == XBT_NULL) { + warn("unable to start xs trasanction\n"); + return NULL; + } + + e = xs_directory(h, xth, "/local/domain", &num); + if (e == NULL) + goto done; + + for (i = 0; (i < num) && (domid == NULL); i++) { + if (asprintf(&path, "/local/domain/%s/name", e[i]) == -1) + break; + val = xs_read(h, xth, path, &len); + free(path); + if (val == NULL) + continue; + + if (strcmp(val, DOMNAME) == 0) { + /* match! */ + if (asprintf(&path, "/local/domain/%s/domid", e[i]) == -1) { + free(val); + break; + } + domid = xs_read(h, xth, path, &len); + free(path); + } + free(val); + } +done: + xs_transaction_end(h, xth, 0); + if (e) + free(e); + return domid; +} + +int convert_dev_name_to_num(char *name) { + char *p, *ptr; + int majors[10] = {3,22,33,34,56,57,88,89,90,91}; + int maj,i,ret = 0; + char *p_sd = "/dev/sd"; + char *p_hd = "/dev/hd"; + char *p_xvd = "/dev/xvd"; + char *p_plx = "plx"; + char *alpha = "abcdefghijklmnop"; + + if (strstr(name, p_sd) != NULL) { + p = name + strlen(p_sd); + for(i = 0, ptr = alpha; i < strlen(alpha); i++) { + if(*ptr == *p) + break; + *ptr++; + } + *p++; + ret = BASE_DEV_VAL + (16*i) + atoi(p); + } else if (strstr(name, p_hd) != NULL) { + p = name + strlen(p_hd); + for (i = 0, ptr = alpha; i < strlen(alpha); i++) { + if(*ptr == *p) break; + *ptr++; + } + *p++; + ret = (majors[i/2]*256) + atoi(p); + + } else if (strstr(name, p_xvd) != NULL) { + p = name + strlen(p_xvd); + for(i = 0, ptr = alpha; i < strlen(alpha); i++) { + if(*ptr == *p) break; + *ptr++; + } + *p++; + ret = (202*256) + (16*i) + atoi(p); + + } else if (strstr(name, p_plx) != NULL) { + p = name + strlen(p_plx); + ret = atoi(p); + + } else { + DPRINTF("Unknown device type, setting to default.\n"); + ret = BASE_DEV_VAL; + } + + return ret; +} + +/** + * A little paranoia: we don't just trust token. + */ +static struct xenbus_watch *find_watch(const char *token) +{ + struct xenbus_watch *i, *cmp; + + cmp = (void *)strtoul(token, NULL, 16); + + list_for_each_entry(i, &watches, list) + if (i == cmp) + return i; + return NULL; +} + +/** + * Register callback to watch this node. + * like xs_watch, return 0 on failure + */ +int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch) +{ + /* Pointer in ascii is the token. */ + char token[sizeof(watch) * 2 + 1]; + + snprintf(token, sizeof(token), "%lX", (long)watch); + if (find_watch(token)) { + DPRINTF("watch collision!\n"); + return -EINVAL; + } + + if (!xs_watch(h, watch->node, token)) { + DPRINTF("unable to set watch!\n"); + return -EINVAL; + } + + list_add(&watch->list, &watches); + + return 0; +} + +int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch) +{ + char token[sizeof(watch) * 2 + 1]; + + snprintf(token, sizeof(token), "%lX", (long)watch); + if (!find_watch(token)) { + DPRINTF("no such watch!\n"); + return -EINVAL; + } + + if (!xs_unwatch(h, watch->node, token)) + DPRINTF("XENBUS Failed to release watch %s\n", + watch->node); + + list_del(&watch->list); + + return 0; +} + +/** + * Re-register callbacks to all watches. + */ +void reregister_xenbus_watches(struct xs_handle *h) +{ + struct xenbus_watch *watch; + char token[sizeof(watch) * 2 + 1]; + + list_for_each_entry(watch, &watches, list) { + snprintf(token, sizeof(token), "%lX", (long)watch); + xs_watch(h, watch->node, token); + } +} + +/** + * based on watch_thread() + */ +int xs_fire_next_watch(struct xs_handle *h) +{ + char **res; + char *token; + char *node = NULL; + struct xenbus_watch *w; + int er; + unsigned int num; + + res = xs_read_watch(h, &num); + if (res == NULL) + return -EAGAIN; /* in O_NONBLOCK, read_watch returns 0... */ + + node = res[XS_WATCH_PATH]; + token = res[XS_WATCH_TOKEN]; + + w = find_watch(token); + if (w) + w->callback(h, w, node); + + free(res); + + return 1; +} diff --git a/tools/blktap/lib/xs_api.h b/tools/blktap/lib/xs_api.h new file mode 100644 index 0000000..34430dc --- /dev/null +++ b/tools/blktap/lib/xs_api.h @@ -0,0 +1,50 @@ +/* + * xs_api.h + * + * (c) 2005 Andrew Warfield and Julian Chesterfield + * + * + * 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. + */ + +struct xenbus_watch +{ + struct list_head list; + char *node; + void (*callback)(struct xs_handle *h, + struct xenbus_watch *, + const char *node); +}; + +int xs_gather(struct xs_handle *xs, const char *dir, ...); +int xs_printf(struct xs_handle *h, const char *dir, const char *node, + const char *fmt, ...); +int xs_exists(struct xs_handle *h, const char *path); +char *get_dom_domid(struct xs_handle *h); +int convert_dev_name_to_num(char *name); +int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch); +int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch); +void reregister_xenbus_watches(struct xs_handle *h); +int xs_fire_next_watch(struct xs_handle *h); diff --git a/tools/check/Makefile b/tools/check/Makefile new file mode 100644 index 0000000..2604472 --- /dev/null +++ b/tools/check/Makefile @@ -0,0 +1,19 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +.PHONY: all install +all install: check-build + +# Check this machine is OK for building on. +.PHONY: check-build +check-build: + LIBXENAPI_BINDINGS=$(LIBXENAPI_BINDINGS) ACM_SECURITY=$(ACM_SECURITY) ./chk build + +# Check this machine is OK for installing on. +.PHONY: check-install +check-install: + LIBXENAPI_BINDINGS=$(LIBXENAPI_BINDINGS) ACM_SECURITY=$(ACM_SECURITY) ./chk install + +.PHONY: clean +clean: + ./chk clean diff --git a/tools/check/README b/tools/check/README new file mode 100644 index 0000000..e6f105b --- /dev/null +++ b/tools/check/README @@ -0,0 +1,20 @@ +Checks for the suitability of a machine for Xen build or install. +To check for build suitability use + + ./chk build + +To check for install suitability use + + ./chk install + +The chk script will run checks in this directory and print +the ones that failed. It prints nothing if checks succeed. +The chk script exits with 0 on success and 1 on failure. + +The chk script runs executable files in this directory whose +names begin with 'check_'. Files containing CHECK-BUILD +are run for the build check, and files containing CHECK-INSTALL +are run for the install check. + +Detailed output from the check scripts is in .chkbuild for build +and .chkinstall for install. \ No newline at end of file diff --git a/tools/check/check_brctl b/tools/check/check_brctl new file mode 100755 index 0000000..5b3f2b4 --- /dev/null +++ b/tools/check/check_brctl @@ -0,0 +1,13 @@ +#!/bin/sh +# CHECK-INSTALL + +. ./funcs.sh + +case $OS in +OpenBSD|NetBSD|FreeBSD) + has_or_fail brconfig ;; +Linux) + has_or_fail brctl ;; +*) + fail "unknown OS" ;; +esac diff --git a/tools/check/check_crypto_lib b/tools/check/check_crypto_lib new file mode 100755 index 0000000..af27da6 --- /dev/null +++ b/tools/check/check_crypto_lib @@ -0,0 +1,11 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +. ./funcs.sh + +case $OS in +FreeBSD|NetBSD|OpenBSD) + exit 0 ;; +esac + +has_lib libcrypto.so || fail "missing libcrypto.so" diff --git a/tools/check/check_curl b/tools/check/check_curl new file mode 100755 index 0000000..a001e7e --- /dev/null +++ b/tools/check/check_curl @@ -0,0 +1,13 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +. ./funcs.sh + +if [ "$LIBXENAPI_BINDINGS" != "y" ]; then + echo -n "unused, " + exit 0 +fi + +has_or_fail curl-config +curl_libs=`curl-config --libs` || fail "curl-config --libs failed" +test_link $curl_libs || fail "dependency libraries for curl are missing" diff --git a/tools/check/check_iproute b/tools/check/check_iproute new file mode 100755 index 0000000..2208944 --- /dev/null +++ b/tools/check/check_iproute @@ -0,0 +1,15 @@ +#!/bin/sh +# CHECK-INSTALL + +. ./funcs.sh + +PATH=/sbin:$PATH + +case $OS in +OpenBSD|NetBSD|FreeBSD) + has_or_fail ifconfig ;; +Linux) + has_or_fail ip ;; +*) + fail "unknown OS" ;; +esac diff --git a/tools/check/check_logging b/tools/check/check_logging new file mode 100755 index 0000000..d9f3ff8 --- /dev/null +++ b/tools/check/check_logging @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- mode: python; -*- + +import os +import sys + +def hline(): + print >>sys.stderr, "*" * 70 + +def msg(message): + print >>sys.stderr, "*" * 3, message + +def check_logging(): + """Check python logging is installed and raise an error if not. + Logging is standard from Python 2.3 on. + """ + try: + import logging + except ImportError: + hline() + msg("") + msg(" *** Python logging is not installed.") + msg(" *** Use 'make install-logging' at the xen root to install.") + msg(" *** ") + msg(" *** Alternatively download and install from") + msg(" *** http://www.red-dove.com/python_logging.html") + hline() + sys.exit(1) + +if __name__ == '__main__': + check_logging() diff --git a/tools/check/check_openssl_devel b/tools/check/check_openssl_devel new file mode 100755 index 0000000..e5ca03d --- /dev/null +++ b/tools/check/check_openssl_devel @@ -0,0 +1,6 @@ +#!/bin/sh +# CHECK-BUILD + +. ./funcs.sh + +has_header openssl/md5.h || fail "missing openssl headers" diff --git a/tools/check/check_python b/tools/check/check_python new file mode 100755 index 0000000..229809f --- /dev/null +++ b/tools/check/check_python @@ -0,0 +1,9 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +. ./funcs.sh + +python -c ' +import sys +sys.exit(sys.version_info[0] < 2 or sys.version_info[1] < 2) +' || fail "need python version >= 2.2" diff --git a/tools/check/check_python_devel b/tools/check/check_python_devel new file mode 100755 index 0000000..98d448f --- /dev/null +++ b/tools/check/check_python_devel @@ -0,0 +1,12 @@ +#!/bin/sh +# CHECK-BUILD + +. ./funcs.sh + +python -c ' +import os.path, sys +for p in sys.path: + if os.path.exists(p + "/config/Makefile"): + sys.exit(0) +sys.exit(1) +' || fail "can't find python devel files" diff --git a/tools/check/check_python_xml b/tools/check/check_python_xml new file mode 100755 index 0000000..6b65c46 --- /dev/null +++ b/tools/check/check_python_xml @@ -0,0 +1,7 @@ +#!/bin/sh +# CHECK-INSTALL + +. ./funcs.sh + +python -c 'import xml.dom.minidom' 2>/dev/null || \ +fail "can't import xml.dom.minidom" diff --git a/tools/check/check_udev b/tools/check/check_udev new file mode 100755 index 0000000..3deaa36 --- /dev/null +++ b/tools/check/check_udev @@ -0,0 +1,19 @@ +#!/bin/sh +# CHECK-INSTALL + +. ./funcs.sh + +case $OS in +OpenBSD|NetBSD|FreeBSD) + has_or_fail vnconfig + ;; +Linux) + has_or_fail udevinfo + [ "`udevinfo -V | awk '{print $NF}'`" -ge 59 ] 2>/dev/null || \ + has hotplug || \ + fail "udev is too old, upgrade to version 59 or later" + ;; +*) + fail "unknown OS" + ;; +esac diff --git a/tools/check/check_x11_devel b/tools/check/check_x11_devel new file mode 100755 index 0000000..aab7e42 --- /dev/null +++ b/tools/check/check_x11_devel @@ -0,0 +1,8 @@ +#!/bin/sh +# CHECK-BUILD + +. ./funcs.sh + +has_header X11/keysymdef.h || \ +has_header /usr/X11R6/include/X11/keysymdef.h || \ +fail "can't find X11 headers" diff --git a/tools/check/check_xgettext b/tools/check/check_xgettext new file mode 100755 index 0000000..7e9fa2a --- /dev/null +++ b/tools/check/check_xgettext @@ -0,0 +1,6 @@ +#!/bin/sh +# CHECK-BUILD + +. ./funcs.sh + +has_or_fail xgettext diff --git a/tools/check/check_xml2 b/tools/check/check_xml2 new file mode 100755 index 0000000..caa762c --- /dev/null +++ b/tools/check/check_xml2 @@ -0,0 +1,14 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +. ./funcs.sh + +if [ ! "$LIBXENAPI_BINDINGS" = "y" -a ! "$ACM_SECURITY" = "y" ] +then + echo -n "unused, " + exit 0 +fi + +has_or_fail xml2-config +xml2_libs=`xml2-config --libs` || fail "xml2-config --libs failed" +test_link $xml2_libs || fail "dependency libraries for xml2 are missing" diff --git a/tools/check/check_zlib_devel b/tools/check/check_zlib_devel new file mode 100755 index 0000000..2fc0165 --- /dev/null +++ b/tools/check/check_zlib_devel @@ -0,0 +1,6 @@ +#!/bin/sh +# CHECK-BUILD + +. ./funcs.sh + +has_header zlib.h || fail "can't find zlib headers" diff --git a/tools/check/check_zlib_lib b/tools/check/check_zlib_lib new file mode 100755 index 0000000..be8cf05 --- /dev/null +++ b/tools/check/check_zlib_lib @@ -0,0 +1,12 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +. ./funcs.sh + +case $OS in +FreeBSD|NetBSD|OpenBSD) + exit 0 + ;; +esac + +has_lib libz.so || fail "can't find zlib" diff --git a/tools/check/chk b/tools/check/chk new file mode 100755 index 0000000..e776cff --- /dev/null +++ b/tools/check/chk @@ -0,0 +1,63 @@ +#!/bin/sh + +func_usage () +{ + echo "Usage:" + echo " $0 [build|install|clean]" + echo + echo "Check suitability for Xen build or install." + echo "Exit with 0 if OK, 1 if not." + echo + echo "Calling with 'clean' removes generated files." + exit 1 +} + +PATH=$PATH:/sbin:/usr/sbin +OS=`uname -s` +export PATH OS + +if [ "$OS" = "SunOS" ]; then + exit 0 +fi + +case $1 in + build) + check="CHECK-BUILD" + ;; + install) + check="CHECK-INSTALL" + ;; + clean) + exit 0 + ;; + *) + func_usage + ;; +esac + +failed=0 + +echo "Xen ${check} " `date` +for f in check_* ; do + case $f in + *~) + continue + ;; + *) + ;; + esac + if ! [ -x $f ] ; then + continue + fi + if ! grep -Fq "$check" $f ; then + continue + fi + echo -n "Checking $f: " + if ./$f 2>&1 ; then + echo OK + else + failed=1 + fi +done + +exit ${failed} diff --git a/tools/check/funcs.sh b/tools/check/funcs.sh new file mode 100644 index 0000000..d3fb60a --- /dev/null +++ b/tools/check/funcs.sh @@ -0,0 +1,85 @@ +# has is the same as which, except it handles cross environments +has() { + if [ -z "$CROSS_COMPILE" ]; then + command which "$@" + return $? + fi + + check_sys_root || return 1 + + # subshell to prevent pollution of caller's IFS + ( + IFS=: + for p in $PATH; do + if [ -x "$CROSS_SYS_ROOT/$p/$1" ]; then + echo "$CROSS_SYS_ROOT/$p/$1" + return 0 + fi + done + return 1 + ) +} + +has_or_fail() { + has "$1" >/dev/null || fail "can't find $1" +} + +has_header() { + case $1 in + /*) ;; + *) set -- "/usr/include/$1" ;; + esac + + check_sys_root || return 1 + + test -r "$CROSS_SYS_ROOT$1" + return $? +} + +has_lib() { + check_sys_root || return 1 + + # subshell to prevent pollution of caller's environment + ( + PATH=/sbin:$PATH # for ldconfig + + # This relatively common in a sys-root; libs are installed but + # ldconfig hasn't run there, so ldconfig -p won't work. + if [ "$OS" = Linux -a ! -f "$CROSS_SYS_ROOT/etc/ld.so.cache" ]; then + echo "Please run ldconfig -r \"$CROSS_SYS_ROOT\" to generate ld.so.cache" + # fall through; ldconfig test below should fail + fi + ldconfig -p ${CROSS_SYS_ROOT+-r "$CROSS_SYS_ROOT"} | grep -Fq "$1" + return $? + ) +} + +test_link() { + # subshell to trap removal of tmpfile + ( + unset tmpfile + trap 'rm -f "$tmpfile"; exit' 0 1 2 15 + tmpfile=`mktemp` || return 1 + ld "$@" -o "$tmpfile" >/dev/null 2>&1 + return $? + ) +} + +# this function is used commonly above +check_sys_root() { + [ -z "$CROSS_COMPILE" ] && return 0 + if [ -z "$CROSS_SYS_ROOT" ]; then + echo "please set CROSS_SYS_ROOT in the environment" + return 1 + fi + if [ ! -d "$CROSS_SYS_ROOT" ]; then + echo "no sys-root found at $CROSS_SYS_ROOT" + return 1 + fi +} + +fail() { + echo + echo " *** `basename "$0"` FAILED${*+: $*}" + exit 1 +} diff --git a/tools/console/Makefile b/tools/console/Makefile new file mode 100644 index 0000000..10e909a --- /dev/null +++ b/tools/console/Makefile @@ -0,0 +1,35 @@ + +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -Werror + +CFLAGS += $(CFLAGS_libxenctrl) +CFLAGS += $(CFLAGS_libxenstore) +LDFLAGS += $(LDFLAGS_libxenctrl) +LDFLAGS += $(LDFLAGS_libxenstore) + +BIN = xenconsoled xenconsole + +.PHONY: all +all: $(BIN) + +.PHONY: clean +clean: + $(RM) *.a *.so *.o *.rpm $(BIN) + $(RM) client/*.o daemon/*.o + +xenconsoled: $(patsubst %.c,%.o,$(wildcard daemon/*.c)) + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) \ + $(UTIL_LIBS) $(SOCKET_LIBS) -lrt + +xenconsole: $(patsubst %.c,%.o,$(wildcard client/*.c)) + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) \ + $(UTIL_LIBS) $(SOCKET_LIBS) + +.PHONY: install +install: $(BIN) + $(INSTALL_DIR) $(DESTDIR)/$(SBINDIR) + $(INSTALL_PROG) xenconsoled $(DESTDIR)/$(SBINDIR) + $(INSTALL_DIR) $(DESTDIR)$(PRIVATE_BINDIR) + $(INSTALL_PROG) xenconsole $(DESTDIR)$(PRIVATE_BINDIR) diff --git a/tools/console/client/main.c b/tools/console/client/main.c new file mode 100644 index 0000000..509e44b --- /dev/null +++ b/tools/console/client/main.c @@ -0,0 +1,298 @@ +/*\ + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xs.h" + +#define ESCAPE_CHARACTER 0x1d + +static volatile sig_atomic_t received_signal = 0; + +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" + , program); +} + +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_fd = open(pty_path, O_RDWR | O_NOCTTY); + if (pty_fd == -1) + err(errno, "Could not open tty `%s'", + pty_path); + free(pty_path); + } + } + } while (pty_fd == -1 && (now = time(NULL)) < start + seconds); + 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, TCSAFLUSH, &new_term); +} + +static void restore_term(int fd, struct termios *old) +{ + tcsetattr(fd, TCSAFLUSH, old); +} + +static int console_loop(int fd, struct xs_handle *xs, char *pty_path) +{ + int ret, xs_fd = xs_fileno(xs), max_fd; + + do { + fd_set fds; + + FD_ZERO(&fds); + 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); + 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; +} + +int main(int argc, char **argv) +{ + struct termios attr; + int domid; + char *sopt = "h"; + int ch; + int opt_ind=0; + struct option lopt[] = { + { "help", 0, 0, 'h' }, + { 0 }, + + }; + char *path; + int spty, xsfd; + struct xs_handle *xs; + char *end; + + while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch(ch) { + case 'h': + usage(argv[0]); + exit(0); + break; + } + } + + if ((argc - optind) != 1) { + fprintf(stderr, "Invalid number of arguments\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); + + path = xs_get_domain_path(xs, domid); + if (path == NULL) + err(errno, "xs_get_domain_path()"); + path = realloc(path, strlen(path) + strlen("/console/tty") + 1); + if (path == NULL) + err(ENOMEM, "realloc"); + strcat(path, "/console/tty"); + + /* 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); + } + + /* 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); + init_term(STDIN_FILENO, &attr); + console_loop(spty, xs, path); + restore_term(STDIN_FILENO, &attr); + + free(path); + return 0; + } diff --git a/tools/console/daemon/io.c b/tools/console/daemon/io.c new file mode 100644 index 0000000..78aea83 --- /dev/null +++ b/tools/console/daemon/io.c @@ -0,0 +1,1118 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#include "utils.h" +#include "io.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#elif defined(__linux__) || defined(__Linux__) +#include +#endif +#if defined(__sun__) +#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 evtchn_port_or_error_t log_hv_evtchn = -1; +static int xc_handle = -1; +static int xce_handle = -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 slave_fd; + int log_fd; + bool is_dead; + struct buffer buffer; + struct domain *next; + char *conspath; + char *serialpath; + int use_consolepath; + int ring_ref; + evtchn_port_or_error_t local_port; + evtchn_port_or_error_t remote_port; + int xce_handle; + 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', sz); + 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; + xc_evtchn_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 > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + char *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - 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", + strlen("Logfile Opened"), + &log_time_hv_needts) < 0) { + dolog(LOG_ERR, "Failed to log opening timestamp " + "in %s: %d (%s)", logfile, errno, + strerror(errno)); + 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); + 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", + strlen("Logfile Opened"), + &log_time_guest_needts) < 0) { + dolog(LOG_ERR, "Failed to log opening timestamp " + "in %s: %d (%s)", logfile, errno, + strerror(errno)); + 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); + + cfmakeraw(&term); + + if (openpty(&dom->master_fd, &dom->slave_fd, NULL, &term, 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 ((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; + } + + if (dom->use_consolepath) { + 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/limit", dom->serialpath) != -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->serialpath) != -1; + if (!success) + goto out; + success = xs_write(xs, XBT_NULL, path, slave, strlen(slave)); + free(path); + if (!success) + goto out; + + if (dom->use_consolepath) { + 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 int domain_create_ring(struct domain *dom) +{ + int err, remote_port, ring_ref, rc; + char *type, path[PATH_MAX]; + + err = xs_gather(xs, dom->serialpath, + "ring-ref", "%u", &ring_ref, + "port", "%i", &remote_port, + NULL); + if (err) { + err = xs_gather(xs, dom->conspath, + "ring-ref", "%u", &ring_ref, + "port", "%i", &remote_port, + NULL); + if (err) + goto out; + dom->use_consolepath = 1; + } else + dom->use_consolepath = 0; + + snprintf(path, sizeof(path), "%s/type", + dom->use_consolepath ? dom->conspath: dom->serialpath); + type = xs_read(xs, XBT_NULL, path, NULL); + if (type && strcmp(type, "xenconsoled") != 0) { + free(type); + return 0; + } + free(type); + + if ((ring_ref == dom->ring_ref) && (remote_port == dom->remote_port)) + goto out; + + if (ring_ref != dom->ring_ref) { + if (dom->interface != NULL) + munmap(dom->interface, getpagesize()); + dom->interface = xc_map_foreign_range( + xc, dom->domid, getpagesize(), + PROT_READ|PROT_WRITE, + (unsigned long)ring_ref); + if (dom->interface == NULL) { + err = EINVAL; + goto out; + } + dom->ring_ref = ring_ref; + } + + dom->local_port = -1; + dom->remote_port = -1; + if (dom->xce_handle != -1) + xc_evtchn_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 = xc_evtchn_open(); + if (dom->xce_handle == -1) { + err = errno; + goto out; + } + + rc = xc_evtchn_bind_interdomain(dom->xce_handle, + dom->domid, remote_port); + + if (rc == -1) { + err = errno; + xc_evtchn_close(dom->xce_handle); + dom->xce_handle = -1; + goto out; + } + dom->local_port = rc; + dom->remote_port = remote_port; + + if (dom->master_fd == -1) { + if (!domain_create_tty(dom)) { + err = errno; + xc_evtchn_close(dom->xce_handle); + dom->xce_handle = -1; + dom->local_port = -1; + dom->remote_port = -1; + goto out; + } + } + + if (log_guest) + 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->serialpath, domid_str); + if (success) { + success = xs_watch(xs, dom->conspath, domid_str); + if (success) + domain_create_ring(dom); + else + xs_unwatch(xs, dom->serialpath, domid_str); + } + } else { + success = xs_unwatch(xs, dom->serialpath, domid_str); + 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 = (struct domain *)malloc(sizeof(struct domain)); + if (dom == NULL) { + dolog(LOG_ERR, "Out of memory %s:%s():L%d", + __FILE__, __FUNCTION__, __LINE__); + exit(ENOMEM); + } + + dom->domid = domid; + + dom->serialpath = xs_get_domain_path(xs, dom->domid); + s = realloc(dom->serialpath, strlen(dom->serialpath) + + strlen("/serial/0") + 1); + if (s == NULL) + goto out; + dom->serialpath = s; + strcat(dom->serialpath, "/serial/0"); + + 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->slave_fd = -1; + dom->log_fd = -1; + + dom->is_dead = false; + dom->buffer.data = 0; + dom->buffer.consumed = 0; + dom->buffer.size = 0; + dom->buffer.capacity = 0; + dom->buffer.max_capacity = 0; + dom->event_count = 0; + dom->next_period = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000) + RATE_LIMIT_PERIOD; + dom->next = NULL; + + dom->ring_ref = -1; + dom->local_port = -1; + dom->remote_port = -1; + dom->interface = NULL; + dom->xce_handle = -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->serialpath); + 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); + + free(d->buffer.data); + d->buffer.data = NULL; + + free(d->serialpath); + d->serialpath = 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); + if (d->interface != NULL) + munmap(d->interface, getpagesize()); + d->interface = NULL; + if (d->xce_handle != -1) + xc_evtchn_close(d->xce_handle); + d->xce_handle = -1; +} + +void enum_domains(void) +{ + int domid = 1; + xc_dominfo_t dominfo; + struct domain *dom; + + 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) + create_domain(dominfo.domid); + } + 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 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_close_tty(dom); + + if (domain_is_valid(dom->domid)) { + domain_create_tty(dom); + } else { + shutdown_domain(dom); + } + } 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; + xc_evtchn_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_close_tty(dom); + + if (domain_is_valid(dom->domid)) { + domain_create_tty(dom); + } else { + shutdown_domain(dom); + } + } else { + buffer_advance(&dom->buffer, len); + } +} + +static void handle_ring_read(struct domain *dom) +{ + evtchn_port_or_error_t port; + + if (dom->is_dead) + return; + + if ((port = xc_evtchn_pending(dom->xce_handle)) == -1) + return; + + dom->event_count++; + + buffer_append(dom); + + if (dom->event_count < RATE_LIMIT_ALLOWANCE) + (void)xc_evtchn_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(void) +{ + char buffer[1024*16]; + char *bufptr = buffer; + unsigned int size = sizeof(buffer); + static uint32_t index = 0; + evtchn_port_or_error_t port; + + if ((port = xc_evtchn_pending(xce_handle)) == -1) + return; + + if (xc_readconsolering(xc_handle, &bufptr, &size, 0, 1, &index) == 0 && size > 0) { + int logret; + 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)); + } + + (void)xc_evtchn_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(); + } +} + +void handle_io(void) +{ + fd_set readfds, writefds; + int ret; + + if (log_hv) { + xc_handle = xc_interface_open(); + if (xc_handle == -1) { + dolog(LOG_ERR, "Failed to open xc handle: %d (%s)", + errno, strerror(errno)); + goto out; + } + xce_handle = xc_evtchn_open(); + if (xce_handle == -1) { + 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 = xc_evtchn_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; + } + } + + for (;;) { + struct domain *d, *n; + int max_fd = -1; + struct timeval timeout; + struct timespec ts; + long long now, next_timeout = 0; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + FD_SET(xs_fileno(xs), &readfds); + max_fd = MAX(xs_fileno(xs), max_fd); + + if (log_hv) { + FD_SET(xc_evtchn_fd(xce_handle), &readfds); + max_fd = MAX(xc_evtchn_fd(xce_handle), max_fd); + } + + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) + return; + now = (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) { + /* Add 5ms of fuzz since select() often returns + a couple of ms sooner than requested. Without + the fuzz we typically do an extra spin in select() + with a 1/2 ms timeout every other iteration */ + if ((now+5) > d->next_period) { + d->next_period = now + RATE_LIMIT_PERIOD; + if (d->event_count >= RATE_LIMIT_ALLOWANCE) { + (void)xc_evtchn_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 != -1) { + if (discard_overflowed_data || + !d->buffer.max_capacity || + d->buffer.size < d->buffer.max_capacity) { + int evtchn_fd = xc_evtchn_fd(d->xce_handle); + FD_SET(evtchn_fd, &readfds); + max_fd = MAX(evtchn_fd, max_fd); + } + } + + if (d->master_fd != -1) { + if (!d->is_dead && ring_free_bytes(d)) + FD_SET(d->master_fd, &readfds); + + if (!buffer_empty(&d->buffer)) + FD_SET(d->master_fd, &writefds); + max_fd = MAX(d->master_fd, max_fd); + } + } + + /* If any domain has been rate limited, we need to work + out what timeout to supply to select */ + if (next_timeout) { + long long duration = (next_timeout - now); + if (duration <= 0) /* sanity check */ + duration = 1; + timeout.tv_sec = duration / 1000; + timeout.tv_usec = ((duration - (timeout.tv_sec * 1000)) + * 1000); + } + + ret = select(max_fd + 1, &readfds, &writefds, 0, + next_timeout ? &timeout : NULL); + + if (log_reload) { + handle_log_reload(); + log_reload = 0; + } + + /* Abort if select failed, except for EINTR cases + which indicate a possible log reload */ + if (ret == -1) { + if (errno == EINTR) + continue; + dolog(LOG_ERR, "Failure in select: %d (%s)", + errno, strerror(errno)); + break; + } + + if (log_hv && FD_ISSET(xc_evtchn_fd(xce_handle), &readfds)) + handle_hv_logs(); + + if (ret <= 0) + continue; + + if (FD_ISSET(xs_fileno(xs), &readfds)) + handle_xs(); + + for (d = dom_head; d; d = n) { + n = d->next; + if (d->event_count < RATE_LIMIT_ALLOWANCE) { + if (d->xce_handle != -1 && + FD_ISSET(xc_evtchn_fd(d->xce_handle), + &readfds)) + handle_ring_read(d); + } + + if (d->master_fd != -1 && FD_ISSET(d->master_fd, + &readfds)) + handle_tty_read(d); + + if (d->master_fd != -1 && FD_ISSET(d->master_fd, + &writefds)) + handle_tty_write(d); + + if (d->is_dead) + cleanup_domain(d); + } + } + + out: + if (log_hv_fd != -1) { + close(log_hv_fd); + log_hv_fd = -1; + } + if (xc_handle != -1) { + xc_interface_close(xc_handle); + xc_handle = -1; + } + if (xce_handle != -1) { + xc_evtchn_close(xce_handle); + xce_handle = -1; + } + 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: + */ diff --git a/tools/console/daemon/io.h b/tools/console/daemon/io.h new file mode 100644 index 0000000..8fa04b6 --- /dev/null +++ b/tools/console/daemon/io.h @@ -0,0 +1,27 @@ +/*\ + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ + +#ifndef CONSOLED_IO_H +#define CONSOLED_IO_H + +void enum_domains(void); +void handle_io(void); + +#endif diff --git a/tools/console/daemon/main.c b/tools/console/daemon/main.c new file mode 100644 index 0000000..c1529d0 --- /dev/null +++ b/tools/console/daemon/main.c @@ -0,0 +1,179 @@ +/*\ + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenctrl.h" + +#include "utils.h" +#include "io.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"); +} + +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_WARNING; + 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': + syslog_option |= LOG_PERROR; + syslog_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("/var/log/xen/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); + + if (!is_interactive) { + daemonize(pidfile ? pidfile : "/var/run/xenconsoled.pid"); + } + + if (!xen_setup()) + exit(1); + + enum_domains(); + + 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: + */ diff --git a/tools/console/daemon/utils.c b/tools/console/daemon/utils.c new file mode 100644 index 0000000..7b3cd19 --- /dev/null +++ b/tools/console/daemon/utils.c @@ -0,0 +1,144 @@ +/*\ + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenctrl.h" +#include "utils.h" + +struct xs_handle *xs; +int 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 (getppid() == 1) { + return; + } + + 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); +} + +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(); + if (xc == -1) { + 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 != -1) + xc_interface_close(xc); + return false; +} + diff --git a/tools/console/daemon/utils.h b/tools/console/daemon/utils.h new file mode 100644 index 0000000..44b3e2a --- /dev/null +++ b/tools/console/daemon/utils.h @@ -0,0 +1,46 @@ +/*\ + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ + +#ifndef CONSOLED_UTILS_H +#define CONSOLED_UTILS_H + +#include +#include +#include + +#include "xs.h" + +void daemonize(const char *pidfile); +bool xen_setup(void); + +extern struct xs_handle *xs; +extern int 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 diff --git a/tools/console/testsuite/Makefile b/tools/console/testsuite/Makefile new file mode 100644 index 0000000..71deb0a --- /dev/null +++ b/tools/console/testsuite/Makefile @@ -0,0 +1,14 @@ +XEN_ROOT = ../../.. +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 diff --git a/tools/console/testsuite/README b/tools/console/testsuite/README new file mode 100644 index 0000000..a799d6a --- /dev/null +++ b/tools/console/testsuite/README @@ -0,0 +1,29 @@ +ABOUT + +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. diff --git a/tools/console/testsuite/console-dom0.c b/tools/console/testsuite/console-dom0.c new file mode 100644 index 0000000..9c88c18 --- /dev/null +++ b/tools/console/testsuite/console-dom0.c @@ -0,0 +1,117 @@ +/* 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; +} diff --git a/tools/console/testsuite/console-domU.c b/tools/console/testsuite/console-domU.c new file mode 100644 index 0000000..3a9c508 --- /dev/null +++ b/tools/console/testsuite/console-domU.c @@ -0,0 +1,76 @@ +/* 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; +} diff --git a/tools/console/testsuite/procpipe.c b/tools/console/testsuite/procpipe.c new file mode 100644 index 0000000..fc9791d --- /dev/null +++ b/tools/console/testsuite/procpipe.c @@ -0,0 +1,133 @@ +/* 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; +} diff --git a/tools/cross-install b/tools/cross-install new file mode 100755 index 0000000..1177335 --- /dev/null +++ b/tools/cross-install @@ -0,0 +1,8 @@ +#!/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 "$@" diff --git a/tools/debugger/gdb/README b/tools/debugger/gdb/README new file mode 100644 index 0000000..8221798 --- /dev/null +++ b/tools/debugger/gdb/README @@ -0,0 +1,38 @@ + +DomU & HVM GDB server for 32-bit (PAE and non-PAE) and x86_64 systems +---------------------------------------------------- + +Lines marked below with [*] are optional, if you want full +source-level debugging of your kernel image. + +To build the GDB server: + 0. Build rest of the Xen first from the base directory + 1. Run ./gdbbuild from within this directory. + 2. Copy ./gdb-6.2.1-linux-i386-xen/gdb/gdbserver/gdbserver-xen + to your test machine. + +To build a debuggable guest domU kernel image: + 1. cd linux-2.6.xx-xenU + 2. make menuconfig + 3. From within the configurator, enable the following options: + # Kernel hacking -> Compile the kernel with debug info [*] + -> Compile the kernel with frame pointers + 4. (Re)build and (re)install your xenU kernel image. + +To debug a running guest: + 1. Use 'xm list' to discover its domain id ($domid). + 2. Run 'gdbserver-xen 127.0.0.1:9999 --attach $domid'. + 3. Run 'gdb /path/to/vmlinux-syms-2.6.xx-xenU'. + 4. From within the gdb client session: + # directory /path/to/linux-2.6.xx-xenU [*] + # target remote 127.0.0.1:9999 + # bt + # disass + +To debug a crashed domU guest: + 1. Add '(enable-dump yes)' to /etc/xen/xend-config.sxp before + starting xend. + 2. When the domain crashes, a core file is written to + '/var/xen/dump/..core'. + 3. Run 'gdbserver-xen 127.0.0.1:9999 --file '. + 4. Connect to the server as for a running guest. diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/Makefile.in b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/Makefile.in new file mode 100644 index 0000000..e470217 --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/Makefile.in @@ -0,0 +1,308 @@ +# Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, +# 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +# This file is part of GDB. + +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +host_alias = @host_alias@ +target_alias = @target_alias@ +program_transform_name = @program_transform_name@ +bindir = @bindir@ +libdir = @libdir@ +tooldir = $(libdir)/$(target_alias) + +datadir = @datadir@ +mandir = @mandir@ +man1dir = $(mandir)/man1 +man2dir = $(mandir)/man2 +man3dir = $(mandir)/man3 +man4dir = $(mandir)/man4 +man5dir = $(mandir)/man5 +man6dir = $(mandir)/man6 +man7dir = $(mandir)/man7 +man8dir = $(mandir)/man8 +man9dir = $(mandir)/man9 +infodir = @infodir@ +htmldir = $(prefix)/html +includedir = @includedir@ + +SHELL = /bin/sh + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +# Directory containing source files. Don't clean up the spacing, +# this exact string is matched for by the "configure" script. +srcdir = @srcdir@ +VPATH = @srcdir@ + +# It is also possible that you will need to add -I/usr/include/sys to the +# CFLAGS section if your system doesn't have fcntl.h in /usr/include (which +# is where it should be according to Posix). + +# Set this up with gcc if you have gnu ld and the loader will print out +# line numbers for undefinded refs. +#CC-LD=gcc -static +CC-LD=${CC} + +# Where is the "include" directory? Traditionally ../include or ./include +INCLUDE_DIR = ${srcdir}/../../include +INCLUDE_DEP = $$(INCLUDE_DIR) + +# Where are the BFD library? +BFD_DIR = ../../bfd +BFD = $(BFD_DIR)/libbfd.a +BFD_SRC = $(srcdir)/$(BFD_DIR) +BFD_CFLAGS = -I$(BFD_DIR) -I$(BFD_SRC) + +# Where is the source dir for the READLINE library? Traditionally in .. or . +# (For the binary library built from it, we use ${READLINE_DIR}${subdir}.) +READLINE_DIR = ${srcdir}/../readline +READLINE_DEP = $$(READLINE_DIR) + +# All the includes used for CFLAGS and for lint. +# -I. for config files. +# -I${srcdir} for our headers. +# -I$(srcdir)/../regformats for regdef.h. +INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../regformats -I$(INCLUDE_DIR) -I../../../../../libxc/ + +# M{H,T}_CFLAGS, if defined, has host- and target-dependent CFLAGS +# from the config/ directory. +GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS} +#PROFILE_CFLAGS = -pg + +WARN_CFLAGS = -Wall + +# CFLAGS is specifically reserved for setting from the command line +# when running make. I.E. "make CFLAGS=-Wmissing-prototypes". +CFLAGS = @CFLAGS@ + +# INTERNAL_CFLAGS is the aggregate of all other *CFLAGS macros. +INTERNAL_CFLAGS = $(WARN_CFLAGS) ${CFLAGS} ${GLOBAL_CFLAGS} \ + ${PROFILE_CFLAGS} ${INCLUDE_CFLAGS} ${BFD_CFLAGS} + +# LDFLAGS is specifically reserved for setting from the command line +# when running make. +LDFLAGS = @LDFLAGS@ + +# Perhaps should come from parent Makefile +VERSION = gdbserver-4.12.3 +DIST=gdb + +LINT=/usr/5bin/lint +LINTFLAGS= $(BFD_CFLAGS) + +# All source files that go into linking GDB remote server. + +SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c \ + $(srcdir)/mem-break.c $(srcdir)/proc-service.c $(srcdir)/regcache.c \ + $(srcdir)/remote-utils.c $(srcdir)/server.c $(srcdir)/target.c \ + $(srcdir)/thread-db.c $(srcdir)/utils.c \ + $(srcdir)/linux-arm-low.c $(srcdir)/linux-i386-low.c \ + $(srcdir)/i387-fp.c \ + $(srcdir)/linux-ia64-low.c $(srcdir)/linux-low.c \ + $(srcdir)/linux-m68k-low.c $(srcdir)/linux-mips-low.c \ + $(srcdir)/linux-ppc-low.c $(srcdir)/linux-s390-low.c \ + $(srcdir)/linux-sh-low.c $(srcdir)/linux-x86-64-low.c + +DEPFILES = @GDBSERVER_DEPFILES@ + +SOURCES = $(SFILES) +TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS} + +OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ + utils.o \ + mem-break.o \ + $(DEPFILES) +GDBSERVER_LIBS = @GDBSERVER_LIBS@ + +# Prevent Sun make from putting in the machine type. Setting +# TARGET_ARCH to nothing works for SunOS 3, 4.0, but not for 4.1. +.c.o: + ${CC} -c ${INTERNAL_CFLAGS} $< + +all: gdbserver-xen gdbreplay + +# Traditionally "install" depends on "all". But it may be useful +# not to; for example, if the user has made some trivial change to a +# source file and doesn't care about rebuilding or just wants to save the +# time it takes for make to check that all is up to date. +# install-only is intended to address that need. +install: all install-only +install-only: + n=`echo gdbserver-xen | sed '$(program_transform_name)'`; \ + if [ x$$n = x ]; then n=gdbserver-xen; else true; fi; \ + $(SHELL) $(srcdir)/../../mkinstalldirs $(DESTDIR)$(bindir); \ + $(INSTALL_PROGRAM) gdbserver-xen $(DESTDIR)$(bindir)/$$n; \ + $(SHELL) $(srcdir)/../../mkinstalldirs $(DESTDIR)$(man1dir); \ + $(INSTALL_DATA) $(srcdir)/gdbserver.1 $(DESTDIR)$(man1dir)/$$n.1 + +uninstall: force + n=`echo gdbserver-xen | sed '$(program_transform_name)'`; \ + if [ x$$n = x ]; then n=gdbserver-xen; else true; fi; \ + rm -f $(bindir)/$$n $(DESTDIR)$(man1dir)/$$n.1 + +installcheck: +check: +info dvi: +install-info: +html: +install-html: +clean-info: + +gdbserver-xen: $(OBS) ${ADD_DEPS} ${CDEPS} + rm -f gdbserver-xen + ${CC-LD} $(GLOBAL_CFLAGS) $(LDFLAGS) -o gdbserver-xen $(OBS) \ + $(GDBSERVER_LIBS) $(XM_CLIBS) + +gdbreplay: gdbreplay.o + rm -f gdbreplay + ${CC-LD} $(GLOBAL_CFLAGS) $(LDFLAGS) -o gdbreplay gdbreplay.o \ + $(XM_CLIBS) + +# Put the proper machine-specific files first, so M-. on a machine +# specific routine gets the one for the correct machine. +# The xyzzy stuff below deals with empty DEPFILES +TAGS: ${TAGFILES} + etags `find ${srcdir}/../config -name $(TM_FILE) -print` \ + `find ${srcdir}/../config -name ${XM_FILE} -print` \ + `find ${srcdir}/../config -name ${NAT_FILE} -print` \ + `for i in yzzy ${DEPFILES}; do \ + if [ x$$i != xyzzy ]; then \ + echo ${srcdir}/$$i | sed -e 's/\.o$$/\.c/' ; \ + fi; \ + done` \ + ${TAGFILES} +tags: TAGS + +clean: + rm -f *.o ${ADD_FILES} *~ + rm -f gdbserver gdbreplay core make.log + rm -f reg-arm.c reg-i386.c reg-ia64.c reg-m68k.c reg-mips.c + rm -f reg-ppc.c reg-sh.c reg-x86-64.c reg-i386-linux.c + +maintainer-clean realclean distclean: clean + rm -f nm.h tm.h xm.h config.status config.h stamp-h config.log + rm -f Makefile + +STAGESTUFF=${OBS} ${TSOBS} ${NTSOBS} ${ADD_FILES} init.c init.o version.c gdb + +config.h: stamp-h ; @true +stamp-h: config.in config.status + CONFIG_FILES="" $(SHELL) ./config.status + +Makefile: Makefile.in config.status + CONFIG_HEADERS="" $(SHELL) ./config.status + +config.status: configure configure.srv + $(SHELL) ./config.status --recheck + +force: + +version.c: Makefile + echo 'char *version = "$(VERSION)";' >version.c + +# GNU Make has an annoying habit of putting *all* the Makefile variables +# into the environment, unless you include this target as a circumvention. +# Rumor is that this will be fixed (and this target can be removed) +# in GNU Make 4.0. +.NOEXPORT: + +# GNU Make 3.63 has a different problem: it keeps tacking command line +# overrides onto the definition of $(MAKE). This variable setting +# will remove them. +MAKEOVERRIDES= + +gdb_proc_service_h = $(srcdir)/../gdb_proc_service.h $(srcdir)/../gregset.h +regdat_sh = $(srcdir)/../regformats/regdat.sh +regdef_h = $(srcdir)/../regformats/regdef.h +regcache_h = $(srcdir)/regcache.h +server_h = $(srcdir)/server.h $(regcache_h) config.h $(srcdir)/target.h \ + $(srcdir)/mem-break.h + +inferiors.o: inferiors.c $(server_h) +mem-break.o: mem-break.c $(server_h) +proc-service.o: proc-service.c $(server_h) $(gdb_proc_service_h) +regcache.o: regcache.c $(server_h) $(regdef_h) +remote-utils.o: remote-utils.c terminal.h $(server_h) +server.o: server.c $(server_h) +target.o: target.c $(server_h) +thread-db.o: thread-db.c $(server_h) $(gdb_proc_service_h) +utils.o: utils.c $(server_h) + +signals.o: ../signals/signals.c $(server_h) + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER + +i387-fp.o: i387-fp.c $(server_h) + +linux_low_h = $(srcdir)/linux-low.h + +linux-low.o: linux-low.c $(linux_low_h) $(server_h) + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< @USE_THREAD_DB@ + +linux-xen-low.o: linux-xen-low.c $(linux_low_h) $(server_h) + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< @USE_THREAD_DB@ + +linux-arm-low.o: linux-arm-low.c $(linux_low_h) $(server_h) +linux-i386-low.o: linux-i386-low.c $(linux_low_h) $(server_h) +linux-ia64-low.o: linux-ia64-low.c $(linux_low_h) $(server_h) +linux-mips-low.o: linux-mips-low.c $(linux_low_h) $(server_h) +linux-ppc-low.o: linux-ppc-low.c $(linux_low_h) $(server_h) +linux-s390-low.o: linux-s390-low.c $(linux_low_h) $(server_h) +linux-sh-low.o: linux-sh-low.c $(linux_low_h) $(server_h) +linux-x86-64-low.o: linux-x86-64-low.c $(linux_low_h) $(server_h) + +reg-arm.o : reg-arm.c $(regdef_h) +reg-arm.c : $(srcdir)/../regformats/reg-arm.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-arm.dat reg-arm.c +reg-i386.o : reg-i386.c $(regdef_h) +reg-i386.c : $(srcdir)/../regformats/reg-i386.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-i386.dat reg-i386.c +reg-i386-linux.o : reg-i386-linux.c $(regdef_h) +reg-i386-linux.c : $(srcdir)/../regformats/reg-i386-linux.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-i386-linux.dat reg-i386-linux.c +reg-ia64.o : reg-ia64.c $(regdef_h) +reg-ia64.c : $(srcdir)/../regformats/reg-ia64.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-ia64.dat reg-ia64.c +reg-m68k.o : reg-m68k.c $(regdef_h) +reg-m68k.c : $(srcdir)/../regformats/reg-m68k.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-m68k.dat reg-m68k.c +reg-mips.o : reg-mips.c $(regdef_h) +reg-mips.c : $(srcdir)/../regformats/reg-mips.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-mips.dat reg-mips.c +reg-ppc.o : reg-ppc.c $(regdef_h) +reg-ppc.c : $(srcdir)/../regformats/reg-ppc.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-ppc.dat reg-ppc.c +reg-s390.o : reg-s390.c $(regdef_h) +reg-s390.c : $(srcdir)/../regformats/reg-s390.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-s390.dat reg-s390.c +reg-s390x.o : reg-s390x.c $(regdef_h) +reg-s390x.c : $(srcdir)/../regformats/reg-s390x.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-s390x.dat reg-s390x.c +reg-sh.o : reg-sh.c $(regdef_h) +reg-sh.c : $(srcdir)/../regformats/reg-sh.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-sh.dat reg-sh.c +reg-x86-64.o : reg-x86-64.c $(regdef_h) +reg-x86-64.c : $(srcdir)/../regformats/reg-x86-64.dat $(regdat_sh) + sh $(regdat_sh) $(srcdir)/../regformats/reg-x86-64.dat reg-x86-64.c + +# This is the end of "Makefile.in". diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure new file mode 100755 index 0000000..74354ad --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure @@ -0,0 +1,4650 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.57. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +# 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 Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; 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 + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# 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 + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + 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 + + ;; + 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 + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # 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 sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="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="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="server.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP GDBSERVER_DEPFILES GDBSERVER_LIBS USE_THREAD_DB LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# 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. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +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 + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -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 | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$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 ;; + + -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 ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + 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 ;; + + -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_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=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 ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + 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'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +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 + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + 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 + + +# 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 its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + 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 + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# 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 this package 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 \`..'] + +_ACEOF + + cat <<_ACEOF +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] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --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] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_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] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +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 + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style 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 + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +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 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.57. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +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` +hostinfo = `(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=. + echo "PATH: $as_dir" +done + +} >&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_sep= +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=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$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 + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export 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: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core core.* *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >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 + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + 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. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +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 `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; 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,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +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 + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`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. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +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 + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers config.h:config.in" + + +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_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" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +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 + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + 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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out 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. +echo "$as_me:$LINENO: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; 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 | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$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 +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* 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; +} +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 +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +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_aux_dir= +for ac_dir in $srcdir $srcdir/.. $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 + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking target system type" >&5 +echo $ECHO_N "checking target system type... $ECHO_C" >&6 +if test "${ac_cv_target+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_target_alias=$target_alias +test "x$ac_cv_target_alias" = "x" && + ac_cv_target_alias=$ac_cv_host_alias +ac_cv_target=`$ac_config_sub $ac_cv_target_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_target" >&5 +echo "${ECHO_T}$ac_cv_target" >&6 +target=$ac_cv_target +target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +# 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" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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/* | \ + /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_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 + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +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. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$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' + + + +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 +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f 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 +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +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 + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f 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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#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)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +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=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + + + +for ac_header in sgtty.h termio.h termios.h sys/reg.h string.h proc_service.h sys/procfs.h thread_db.h linux/elf.h stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking whether strerror must be declared" >&5 +echo $ECHO_N "checking whether strerror must be declared... $ECHO_C" >&6 +if test "${bfd_cv_decl_needed_strerror+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +int +main () +{ +char *(*pfn) = (char *(*)) strerror + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bfd_cv_decl_needed_strerror=no +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bfd_cv_decl_needed_strerror=yes +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + +echo "$as_me:$LINENO: result: $bfd_cv_decl_needed_strerror" >&5 +echo "${ECHO_T}$bfd_cv_decl_needed_strerror" >&6 +if test $bfd_cv_decl_needed_strerror = yes; then + +cat >>confdefs.h <<\_ACEOF +#define NEED_DECLARATION_STRERROR 1 +_ACEOF + +fi + + +. ${srcdir}/configure.srv + +if test "${srv_linux_usrregs}" = "yes"; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_LINUX_USRREGS 1 +_ACEOF + +fi + +if test "${srv_linux_regsets}" = "yes"; then + echo "$as_me:$LINENO: checking for PTRACE_GETREGS" >&5 +echo $ECHO_N "checking for PTRACE_GETREGS... $ECHO_C" >&6 + if test "${gdbsrv_cv_have_ptrace_getregs+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +PTRACE_GETREGS; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gdbsrv_cv_have_ptrace_getregs=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gdbsrv_cv_have_ptrace_getregs=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + echo "$as_me:$LINENO: result: $gdbsrv_cv_have_ptrace_getregs" >&5 +echo "${ECHO_T}$gdbsrv_cv_have_ptrace_getregs" >&6 + if test "${gdbsrv_cv_have_ptrace_getregs}" = "yes"; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_LINUX_REGSETS 1 +_ACEOF + + fi + + echo "$as_me:$LINENO: checking for PTRACE_GETFPXREGS" >&5 +echo $ECHO_N "checking for PTRACE_GETFPXREGS... $ECHO_C" >&6 + if test "${gdbsrv_cv_have_ptrace_getfpxregs+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +PTRACE_GETFPXREGS; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gdbsrv_cv_have_ptrace_getfpxregs=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gdbsrv_cv_have_ptrace_getfpxregs=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + echo "$as_me:$LINENO: result: $gdbsrv_cv_have_ptrace_getfpxregs" >&5 +echo "${ECHO_T}$gdbsrv_cv_have_ptrace_getfpxregs" >&6 + if test "${gdbsrv_cv_have_ptrace_getfpxregs}" = "yes"; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_PTRACE_GETFPXREGS 1 +_ACEOF + + fi +fi + +if test "$ac_cv_header_sys_procfs_h" = yes; then + echo "$as_me:$LINENO: checking for lwpid_t in sys/procfs.h" >&5 +echo $ECHO_N "checking for lwpid_t in sys/procfs.h... $ECHO_C" >&6 + if test "${bfd_cv_have_sys_procfs_type_lwpid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _SYSCALL32 +#include +int +main () +{ +lwpid_t avar + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bfd_cv_have_sys_procfs_type_lwpid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bfd_cv_have_sys_procfs_type_lwpid_t=no + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + if test $bfd_cv_have_sys_procfs_type_lwpid_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LWPID_T 1 +_ACEOF + + fi + echo "$as_me:$LINENO: result: $bfd_cv_have_sys_procfs_type_lwpid_t" >&5 +echo "${ECHO_T}$bfd_cv_have_sys_procfs_type_lwpid_t" >&6 + + echo "$as_me:$LINENO: checking for psaddr_t in sys/procfs.h" >&5 +echo $ECHO_N "checking for psaddr_t in sys/procfs.h... $ECHO_C" >&6 + if test "${bfd_cv_have_sys_procfs_type_psaddr_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _SYSCALL32 +#include +int +main () +{ +psaddr_t avar + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bfd_cv_have_sys_procfs_type_psaddr_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bfd_cv_have_sys_procfs_type_psaddr_t=no + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + if test $bfd_cv_have_sys_procfs_type_psaddr_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PSADDR_T 1 +_ACEOF + + fi + echo "$as_me:$LINENO: result: $bfd_cv_have_sys_procfs_type_psaddr_t" >&5 +echo "${ECHO_T}$bfd_cv_have_sys_procfs_type_psaddr_t" >&6 + + echo "$as_me:$LINENO: checking for prgregset_t in sys/procfs.h" >&5 +echo $ECHO_N "checking for prgregset_t in sys/procfs.h... $ECHO_C" >&6 + if test "${bfd_cv_have_sys_procfs_type_prgregset_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _SYSCALL32 +#include +int +main () +{ +prgregset_t avar + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bfd_cv_have_sys_procfs_type_prgregset_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bfd_cv_have_sys_procfs_type_prgregset_t=no + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + if test $bfd_cv_have_sys_procfs_type_prgregset_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PRGREGSET_T 1 +_ACEOF + + fi + echo "$as_me:$LINENO: result: $bfd_cv_have_sys_procfs_type_prgregset_t" >&5 +echo "${ECHO_T}$bfd_cv_have_sys_procfs_type_prgregset_t" >&6 + + echo "$as_me:$LINENO: checking for prfpregset_t in sys/procfs.h" >&5 +echo $ECHO_N "checking for prfpregset_t in sys/procfs.h... $ECHO_C" >&6 + if test "${bfd_cv_have_sys_procfs_type_prfpregset_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _SYSCALL32 +#include +int +main () +{ +prfpregset_t avar + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bfd_cv_have_sys_procfs_type_prfpregset_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bfd_cv_have_sys_procfs_type_prfpregset_t=no + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PRFPREGSET_T 1 +_ACEOF + + fi + echo "$as_me:$LINENO: result: $bfd_cv_have_sys_procfs_type_prfpregset_t" >&5 +echo "${ECHO_T}$bfd_cv_have_sys_procfs_type_prfpregset_t" >&6 + + + + + if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then + echo "$as_me:$LINENO: checking whether prfpregset_t type is broken" >&5 +echo $ECHO_N "checking whether prfpregset_t type is broken... $ECHO_C" >&6 + if test "${gdb_cv_prfpregset_t_broken+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + gdb_cv_prfpregset_t_broken=yes +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + int main () + { + if (sizeof (prfpregset_t) == sizeof (void *)) + return 1; + return 0; + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gdb_cv_prfpregset_t_broken=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +gdb_cv_prfpregset_t_broken=yes +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi + + echo "$as_me:$LINENO: result: $gdb_cv_prfpregset_t_broken" >&5 +echo "${ECHO_T}$gdb_cv_prfpregset_t_broken" >&6 + if test $gdb_cv_prfpregset_t_broken = yes; then + cat >>confdefs.h <<\_ACEOF +#define PRFPREGSET_T_BROKEN 1 +_ACEOF + + fi + fi + + echo "$as_me:$LINENO: checking for elf_fpregset_t in sys/procfs.h" >&5 +echo $ECHO_N "checking for elf_fpregset_t in sys/procfs.h... $ECHO_C" >&6 + if test "${bfd_cv_have_sys_procfs_type_elf_fpregset_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _SYSCALL32 +#include +int +main () +{ +elf_fpregset_t avar + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + bfd_cv_have_sys_procfs_type_elf_fpregset_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +bfd_cv_have_sys_procfs_type_elf_fpregset_t=no + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi + + if test $bfd_cv_have_sys_procfs_type_elf_fpregset_t = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ELF_FPREGSET_T 1 +_ACEOF + + fi + echo "$as_me:$LINENO: result: $bfd_cv_have_sys_procfs_type_elf_fpregset_t" >&5 +echo "${ECHO_T}$bfd_cv_have_sys_procfs_type_elf_fpregset_t" >&6 + +fi + +srv_thread_depfiles= +srv_libs= +USE_THREAD_DB= + + +GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_thread_depfiles" +GDBSERVER_LIBS="$srv_libs -L../../../../../libxc/ -lxenctrl" + + + + + + ac_config_files="$ac_config_files Makefile" + ac_config_commands="$ac_config_commands default" +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, don't put newlines in cache variables' values. +# 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. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *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 \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + 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}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ 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[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $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} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; 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 + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# 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 + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + 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 + + ;; + 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 + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # 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 sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="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="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.57. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet 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 + +Configuration commands: +$config_commands + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.57, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +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=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + 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 +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "default" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h:config.in" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + 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 + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@target@,$target,;t t +s,@target_cpu@,$target_cpu,;t t +s,@target_vendor@,$target_vendor,;t t +s,@target_os@,$target_os,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@GDBSERVER_DEPFILES@,$GDBSERVER_DEPFILES,;t t +s,@GDBSERVER_LIBS@,$GDBSERVER_LIBS,;t t +s,@USE_THREAD_DB@,$USE_THREAD_DB,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # 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. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;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,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #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. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # 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. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + default ) case x$CONFIG_HEADERS in +xconfig.h:config.in) +echo > stamp-h ;; +esac + ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# 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 || { (exit 1); exit 1; } +fi + diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.in b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.in new file mode 100644 index 0000000..17edf9c --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.in @@ -0,0 +1,121 @@ +dnl Autoconf configure script for GDB server. +dnl Copyright 2000, 2002 Free Software Foundation, Inc. +dnl +dnl This file is part of GDB. +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(server.c) +AC_CONFIG_HEADER(config.h:config.in) + +AC_PROG_CC + +AC_CANONICAL_SYSTEM + +AC_PROG_INSTALL + +AC_HEADER_STDC + +AC_CHECK_HEADERS(sgtty.h termio.h termios.h sys/reg.h string.h dnl + proc_service.h sys/procfs.h thread_db.h linux/elf.h dnl + stdlib.h unistd.h) + +BFD_NEED_DECLARATION(strerror) + +. ${srcdir}/configure.srv + +if test "${srv_linux_usrregs}" = "yes"; then + AC_DEFINE(HAVE_LINUX_USRREGS) +fi + +if test "${srv_linux_regsets}" = "yes"; then + AC_MSG_CHECKING(for PTRACE_GETREGS) + AC_CACHE_VAL(gdbsrv_cv_have_ptrace_getregs, + [AC_TRY_COMPILE([#include ], + [PTRACE_GETREGS;], + [gdbsrv_cv_have_ptrace_getregs=yes], + [gdbsrv_cv_have_ptrace_getregs=no])]) + AC_MSG_RESULT($gdbsrv_cv_have_ptrace_getregs) + if test "${gdbsrv_cv_have_ptrace_getregs}" = "yes"; then + AC_DEFINE(HAVE_LINUX_REGSETS) + fi + + AC_MSG_CHECKING(for PTRACE_GETFPXREGS) + AC_CACHE_VAL(gdbsrv_cv_have_ptrace_getfpxregs, + [AC_TRY_COMPILE([#include ], + [PTRACE_GETFPXREGS;], + [gdbsrv_cv_have_ptrace_getfpxregs=yes], + [gdbsrv_cv_have_ptrace_getfpxregs=no])]) + AC_MSG_RESULT($gdbsrv_cv_have_ptrace_getfpxregs) + if test "${gdbsrv_cv_have_ptrace_getfpxregs}" = "yes"; then + AC_DEFINE(HAVE_PTRACE_GETFPXREGS) + fi +fi + +if test "$ac_cv_header_sys_procfs_h" = yes; then + BFD_HAVE_SYS_PROCFS_TYPE(lwpid_t) + BFD_HAVE_SYS_PROCFS_TYPE(psaddr_t) + BFD_HAVE_SYS_PROCFS_TYPE(prgregset_t) + BFD_HAVE_SYS_PROCFS_TYPE(prfpregset_t) + + dnl Check for broken prfpregset_t type + + dnl For Linux/i386, glibc 2.1.3 was released with a bogus + dnl prfpregset_t type (it's a typedef for the pointer to a struct + dnl instead of the struct itself). We detect this here, and work + dnl around it in gdb_proc_service.h. + + if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then + AC_MSG_CHECKING(whether prfpregset_t type is broken) + AC_CACHE_VAL(gdb_cv_prfpregset_t_broken, + [AC_TRY_RUN([#include + int main () + { + if (sizeof (prfpregset_t) == sizeof (void *)) + return 1; + return 0; + }], + gdb_cv_prfpregset_t_broken=no, + gdb_cv_prfpregset_t_broken=yes, + gdb_cv_prfpregset_t_broken=yes)]) + AC_MSG_RESULT($gdb_cv_prfpregset_t_broken) + if test $gdb_cv_prfpregset_t_broken = yes; then + AC_DEFINE(PRFPREGSET_T_BROKEN) + fi + fi + + BFD_HAVE_SYS_PROCFS_TYPE(elf_fpregset_t) +fi + +srv_thread_depfiles= +srv_libs= +USE_THREAD_DB= + + +GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_thread_depfiles" +GDBSERVER_LIBS="$srv_libs -L../../../../../libxc/ -lxenctrl" + +AC_SUBST(GDBSERVER_DEPFILES) +AC_SUBST(GDBSERVER_LIBS) +AC_SUBST(USE_THREAD_DB) + +AC_OUTPUT(Makefile, +[case x$CONFIG_HEADERS in +xconfig.h:config.in) +echo > stamp-h ;; +esac +]) diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.srv b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.srv new file mode 100644 index 0000000..fae0f3b --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.srv @@ -0,0 +1,75 @@ +# Mappings from configuration triplets to gdbserver build options. +# This is invoked from the autoconf-generated configure script, to +# produce the appropriate Makefile substitutions. + +# This file sets the following shell variables: +# srv_regobj The register protocol appropriate for this target. +# srv_tgtobj Any other target-specific modules appropriate +# for this target. +# +# In addition, on GNU/Linux the following shell variables will be set: +# srv_linux_regsets Set to "yes" if ptrace(PTRACE_GETREGS) and friends +# may be available on this platform; unset otherwise. +# They will only be used if defines +# PTRACE_GETREGS. +# srv_linux_usrregs Set to "yes" if we can get at registers via +# PTRACE_PEEKUSR / PTRACE_POKEUSR. + +# Input is taken from the "${target}" variable. + +case "${target}" in + arm*-*-linux*) srv_regobj=reg-arm.o + srv_tgtobj="linux-xen-low.o linux-arm-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; + i[34567]86-*-linux*) srv_regobj=reg-i386-linux.o + srv_tgtobj="linux-xen-low.o linux-i386-low.o i387-fp.o" + srv_linux_usrregs=yes + srv_linux_regsets=yes + srv_linux_thread_db=yes + ;; + ia64-*-linux*) srv_regobj=reg-ia64.o + srv_tgtobj="linux-low.o linux-ia64-low.o" + srv_linux_usrregs=yes + ;; + m68*-*-linux*) srv_regobj=reg-m68k.o + srv_tgtobj="linux-low.o linux-m68k-low.o" + srv_linux_usrregs=yes + ;; + mips*-*-linux*) srv_regobj=reg-mips.o + srv_tgtobj="linux-low.o linux-mips-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; + powerpc*-*-linux*) srv_regobj=reg-ppc.o + srv_tgtobj="linux-low.o linux-ppc-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; + s390-*-linux*) srv_regobj=reg-s390.o + srv_tgtobj="linux-low.o linux-s390-low.o" + srv_linux_usrregs=yes + ;; + s390x-*-linux*) srv_regobj=reg-s390x.o + srv_tgtobj="linux-low.o linux-s390-low.o" + srv_linux_usrregs=yes + ;; + sh*-*-linux*) srv_regobj=reg-sh.o + srv_tgtobj="linux-low.o linux-sh-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; + x86_64-*-linux*) srv_regobj=reg-x86-64.o + srv_tgtobj="linux-xen-low.o linux-x86-64-low.o i387-fp.o" + srv_linux_regsets=yes + ;; + xscale*-*-linux*) srv_regobj=reg-arm.o + srv_tgtobj="linux-low.o linux-arm-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; + *) echo "Error: target not supported by gdbserver." + exit 1 + ;; +esac diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/linux-xen-low.c b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/linux-xen-low.c new file mode 100644 index 0000000..8b0b6d9 --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/linux-xen-low.c @@ -0,0 +1,667 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + + This file is part of GDB. + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "server.h" +#include "linux-low.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRACE_ENTER /* printf("enter %s\n", __FUNCTION__) */ + +static int xc_handle; + +static inline int +curvcpuid() +{ + struct process_info *process; + if (current_inferior == NULL) + return 0; + process = get_thread_process(current_inferior); + return (process->thread_known ? process->tid : 0); + +} + +struct inferior_list all_processes; +static int current_domid; +static int expect_signal = 0; +static int signal_to_send = 0; +static void linux_resume (struct thread_resume *resume_info); +static void linux_set_inferior (void); + +int debug_threads; +int using_threads; +extern int isfile; + +struct pending_signals +{ + int signal; + struct pending_signals *prev; +}; + +#define PTRACE_ARG3_TYPE long +#define PTRACE_XFER_TYPE long + +static int use_regsets_p = 1; + + +#define pid_of(proc) ((proc)->head.id) + +/* FIXME: Delete eventually. */ +#define inferior_pid (pid_of (get_thread_process (current_inferior))) + +/* This function should only be called if the process got a SIGTRAP. + The SIGTRAP could mean several things. + + On i386, where decr_pc_after_break is non-zero: + If we were single-stepping this process using PTRACE_SINGLESTEP, + we will get only the one SIGTRAP (even if the instruction we + stepped over was a breakpoint). The value of $eip will be the + next instruction. + If we continue the process using PTRACE_CONT, we will get a + SIGTRAP when we hit a breakpoint. The value of $eip will be + the instruction after the breakpoint (i.e. needs to be + decremented). If we report the SIGTRAP to GDB, we must also + report the undecremented PC. If we cancel the SIGTRAP, we + must resume at the decremented PC. + + (Presumably, not yet tested) On a non-decr_pc_after_break machine + with hardware or kernel single-step: + If we single-step over a breakpoint instruction, our PC will + point at the following instruction. If we continue and hit a + breakpoint instruction, our PC will point at the breakpoint + instruction. */ +static CORE_ADDR +get_stop_pc (void) +{ + CORE_ADDR stop_pc = (*the_low_target.get_pc) (); + + if (get_thread_process (current_inferior)->stepping) + return stop_pc; + else + return stop_pc - the_low_target.decr_pc_after_break; +} + +static void * +add_process (int pid, long tid) +{ + struct process_info *process; + + process = (struct process_info *) malloc (sizeof (*process)); + memset (process, 0, sizeof (*process)); + + process->head.id = pid; + + process->tid = tid; + process->lwpid = tid; + + add_inferior_to_list (&all_processes, &process->head); + + return process; +} + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. */ + +static int +linux_create_inferior (char *program, char **allargs) +{ + + fprintf (stderr, "Cannot exec %s: %s.\n", program, + strerror (errno)); + fflush (stderr); + _exit (0177); + /* NOT REACHED */ + return -1; +} + +int +linux_attach (int domid) +{ + struct process_info *new_process; + current_domid = domid; + /* this is handled for all active vcpus in PTRACE_ATTACH via the thread_create_callback */ + new_process = (struct process_info *) add_process (domid, curvcpuid()); + /* Don't ignore the initial SIGSTOP if we just attached to this process. */ + /* vcpuid == 0 */ + add_thread (0, new_process); + new_process->stop_expected = 0; + + if (xc_ptrace (xc_handle, PTRACE_ATTACH, domid, 0, isfile) != 0) { + fprintf (stderr, "Cannot attach to domain %d: %s (%d)\n", domid, + strerror (errno), errno); + fflush (stderr); + if (!using_threads) + _exit (0177); + } + + return 0; +} + +/* Kill the inferior process. Make us have no inferior. */ + +static void +linux_kill_one_process (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct process_info *process = get_thread_process (thread); + xc_ptrace (xc_handle, PTRACE_KILL, pid_of (process), 0, 0); +} + + +static void +linux_kill (void) +{ + for_each_inferior (&all_threads, linux_kill_one_process); +} + +static void +linux_detach_one_process (struct inferior_list_entry *entry) +{ + + xc_ptrace (xc_handle, PTRACE_DETACH, current_domid, 0, 0); +} + + +static void +linux_detach (void) +{ + for_each_inferior (&all_threads, linux_detach_one_process); +} + +/* Return nonzero if the given thread is still alive. */ +static int +linux_thread_alive (int tid) +{ + if (find_inferior_id (&all_threads, tid) != NULL) + return 1; + else + return 0; +} + +/* Wait for process, returns status. */ + +static unsigned char +linux_wait (char *status) +{ + int w; + if (xc_waitdomain(xc_handle, current_domid, &w, 0)) + return -1; + + *status = 'T'; + if (expect_signal) + return expect_signal; + else + return SIGTRAP; + +} + +static void +linux_resume (struct thread_resume *resume_info) +{ + int step = resume_info->step; + TRACE_ENTER; + expect_signal = resume_info->sig; + for_each_inferior(&all_threads, regcache_invalidate_one); + if (debug_threads) + fprintf(stderr, "step: %d\n", step); + xc_ptrace (xc_handle, step ? PTRACE_SINGLESTEP : PTRACE_CONT, + resume_info->thread, 0, 0); + +} + + +static int +regsets_fetch_inferior_registers () +{ + struct regset_info *regset; + TRACE_ENTER; + regset = target_regsets; + + while (regset->size >= 0) + { + void *buf; + int res; + + if (regset->size == 0) + { + regset ++; + continue; + } + + buf = malloc (regset->size); + res = xc_ptrace (xc_handle, regset->get_request, + curvcpuid(), + 0, (PTRACE_XFER_TYPE)buf); + if (res < 0) + { + if (errno == EIO) + { + /* If we get EIO on the first regset, do not try regsets again. + If we get EIO on a later regset, disable that regset. */ + if (regset == target_regsets) + { + use_regsets_p = 0; + return -1; + } + else + { + regset->size = 0; + continue; + } + } + else + { + char s[256]; + sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%d", + inferior_pid); + perror (s); + } + } + regset->store_function (buf); + regset ++; + } + return 0; +} + +static int +regsets_store_inferior_registers () +{ + struct regset_info *regset; + TRACE_ENTER; + regset = target_regsets; + + while (regset->size >= 0) + { + void *buf; + int res; + + if (regset->size == 0) + { + regset ++; + continue; + } + + buf = malloc (regset->size); + regset->fill_function (buf); + res = xc_ptrace (xc_handle, regset->set_request, curvcpuid(), 0, (PTRACE_XFER_TYPE)buf); + if (res < 0) + { + if (errno == EIO) + { + /* If we get EIO on the first regset, do not try regsets again. + If we get EIO on a later regset, disable that regset. */ + if (regset == target_regsets) + { + use_regsets_p = 0; + return -1; + } + else + { + regset->size = 0; + continue; + } + } + else + { +#ifdef DEBUG + perror ("Warning: ptrace(regsets_store_inferior_registers)"); +#endif + } + } + regset ++; + free (buf); + } + return 0; +} + + + + +void +linux_fetch_registers (int regno) +{ + if (use_regsets_p) + { + if (regsets_fetch_inferior_registers () == 0) + return; + } + +} + +void +linux_store_registers (int regno) +{ + if (use_regsets_p) + { + if (regsets_store_inferior_registers () == 0) + return; + } +} + + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +static int +linux_read_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) + / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register PTRACE_XFER_TYPE *buffer + = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + + TRACE_ENTER; + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) + { + errno = 0; + buffer[i] = xc_ptrace (xc_handle, PTRACE_PEEKTEXT, curvcpuid(), (PTRACE_ARG3_TYPE) addr, 0); + if (errno) + return errno; + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), len); + + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +static int +linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len) +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + extern int errno; + + TRACE_ENTER; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = xc_ptrace (xc_handle, PTRACE_PEEKTEXT, curvcpuid(), + (PTRACE_ARG3_TYPE) addr, 0); + + if (count > 1) + { + buffer[count - 1] + = xc_ptrace (xc_handle, PTRACE_PEEKTEXT, curvcpuid(), + (PTRACE_ARG3_TYPE) (addr + (count - 1) + * sizeof (PTRACE_XFER_TYPE)), + 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), myaddr, len); + + /* Write the entire buffer. */ + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) + { + errno = 0; + xc_ptrace (xc_handle, PTRACE_POKETEXT, curvcpuid(), + (PTRACE_ARG3_TYPE) addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +static void +linux_look_up_symbols (void) +{ + if (using_threads) + return; + + using_threads = thread_db_init (); + +} + +static void +linux_send_signal (int signum) +{ + extern int signal_pid; + + TRACE_ENTER; + signal_to_send = signum; + psignal(signum, "need to send "); + if (cont_thread > 0) + { + struct process_info *process; + + process = get_thread_process (current_inferior); + kill (process->lwpid, signum); + } + else + kill (signal_pid, signum); +} + +/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET + to debugger memory starting at MYADDR. */ + +static int +linux_read_auxv (CORE_ADDR offset, char *myaddr, unsigned int len) +{ + char filename[PATH_MAX]; + int fd, n; + + TRACE_ENTER; + snprintf (filename, sizeof filename, "/proc/%d/auxv", inferior_pid); + + fd = open (filename, O_RDONLY); + if (fd < 0) + return -1; + + if (offset != (CORE_ADDR) 0 + && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset) + n = -1; + else + n = read (fd, myaddr, len); + + close (fd); + + return n; +} + + +static struct target_ops linux_xen_target_ops = { + linux_create_inferior, + linux_attach, + linux_kill, + linux_detach, + linux_thread_alive, + linux_resume, + linux_wait, + linux_fetch_registers, + linux_store_registers, + linux_read_memory, + linux_write_memory, + linux_look_up_symbols, + linux_send_signal, + linux_read_auxv, +}; + +static void +linux_init_signals () +{ + /* FIXME drow/2002-06-09: As above, we should check with LinuxThreads + to find what the cancel signal actually is. */ + signal (__SIGRTMIN+1, SIG_IGN); +} + +void +initialize_low (void) +{ + using_threads = 0; + xc_handle = xc_interface_open(); + set_target_ops (&linux_xen_target_ops); + set_breakpoint_data (the_low_target.breakpoint, + the_low_target.breakpoint_len); + init_registers (); + linux_init_signals (); + using_threads = thread_db_init (); + +} + + +static void +thread_create_callback(long vcpuid) +{ + struct thread_info *inferior; + struct process_info *process; + + /* If we are attaching to our first thread, things are a little + * different. + */ + if (all_threads.head == all_threads.tail) + { + inferior = (struct thread_info *) all_threads.head; + process = get_thread_process (inferior); + if (process->thread_known == 0) + { + /* Switch to indexing the threads list by TID. */ + change_inferior_id (&all_threads, vcpuid); + goto found; + } + } + if (debug_threads) + fprintf (stderr, "looking up thread %ld\n", + vcpuid); + inferior = (struct thread_info *) find_inferior_id (&all_threads, + vcpuid); + /* if vcpu alread registered - do nothing */ + if (inferior != NULL) + return; + + if (debug_threads) + fprintf (stderr, "Attaching to thread %ld\n", + vcpuid); + + process = add_process(current_domid, vcpuid); + + add_thread(vcpuid, process); + inferior = (struct thread_info *) find_inferior_id (&all_threads, + vcpuid); + if (inferior == NULL) + { + warning ("Could not attach to thread %ld\n", + vcpuid); + return; + } + + +found: + if (debug_threads) + fprintf (stderr, "notifying of new thread %ld\n", + vcpuid); + new_thread_notify (vcpuid); + + process->tid = vcpuid; + process->lwpid = vcpuid; + + process->thread_known = 1; +} + +static void +thread_death_callback(long vcpuid) +{ + if (debug_threads) + fprintf (stderr, "Buuurp...! CPU down event.\n"); +} + +int +thread_db_init(void) +{ + debug_threads = 0; + xc_register_event_handler(thread_create_callback, TD_CREATE); + xc_register_event_handler(thread_death_callback, TD_DEATH); + return 1; +} + +/* XXX GAG ME */ +static int breakpoint_found; +static void +set_breakpoint_inferior (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct thread_info *saved_inferior = current_inferior; + CORE_ADDR eip; + unsigned char buf[2] = {0, 0}; + current_inferior = thread; + if (!breakpoint_found) { + eip = get_stop_pc(); + linux_read_memory(eip, buf, 1); + if (buf[0] == 0xcc) { + breakpoint_found = 1; + return; + } + } else if (breakpoint_found == 2) { + if (get_thread_process (current_inferior)->stepping) { + printf("stepping\n"); + breakpoint_found = 1; + return; + } + } + current_inferior = saved_inferior; + + +} + +static void +linux_set_inferior (void) +{ + breakpoint_found = 0; + for_each_inferior (&all_threads, set_breakpoint_inferior); + if (!breakpoint_found) { + breakpoint_found = 2; + for_each_inferior (&all_threads, set_breakpoint_inferior); + } +} + diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c new file mode 100644 index 0000000..492aab4 --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c @@ -0,0 +1,676 @@ +/* Main code for remote server for GDB. + Copyright 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 + Free Software Foundation, Inc. + + This file is part of GDB. + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "server.h" +#include "linux-low.h" +#include +#include +#include +#include +#include +#include + +int cont_thread; +int general_thread; +int step_thread; +int thread_from_wait; +int old_thread_from_wait; +int extended_protocol; +int server_waiting; +int isfile = 0; + +jmp_buf toplevel; + +/* The PID of the originally created or attached inferior. Used to + send signals to the process when GDB sends us an asynchronous interrupt + (user hitting Control-C in the client), and to wait for the child to exit + when no longer debugging it. */ + +int signal_pid; + +static unsigned char +start_inferior (char *argv[], char *statusptr) +{ + signal (SIGTTOU, SIG_DFL); + signal (SIGTTIN, SIG_DFL); + + signal_pid = create_inferior (argv[0], argv); + + fprintf (stderr, "Process %s created; pid = %d\n", argv[0], + signal_pid); + + signal (SIGTTOU, SIG_IGN); + signal (SIGTTIN, SIG_IGN); + tcsetpgrp (fileno (stderr), signal_pid); + + /* Wait till we are at 1st instruction in program, return signal number. */ + return mywait (statusptr, 0); +} + +static int +attach_inferior (int pid, char *statusptr, unsigned char *sigptr) +{ + /* myattach should return -1 if attaching is unsupported, + 0 if it succeeded, and call error() otherwise. */ + + if (myattach (pid) != 0) + return -1; + + fprintf (stderr, "Attached; pid = %d\n", pid); + + /* FIXME - It may be that we should get the SIGNAL_PID from the + attach function, so that it can be the main thread instead of + whichever we were told to attach to. */ + signal_pid = pid; + + *sigptr = mywait (statusptr, 0); + + return 0; +} + +extern int remote_debug; + +/* Handle all of the extended 'q' packets. */ +void +handle_query (char *own_buf) +{ + static struct inferior_list_entry *thread_ptr; + + if (strcmp ("qSymbol::", own_buf) == 0) + { + if (the_target->look_up_symbols != NULL) + (*the_target->look_up_symbols) (); + + strcpy (own_buf, "OK"); + return; + } + if (strcmp ("qC", own_buf) == 0) + { + struct process_info *process; + if (current_inferior == NULL) + return; + process = get_thread_process(current_inferior); + sprintf(own_buf, "QC%x", process->thread_known ? process->tid : 0); + + } + if (strcmp ("qfThreadInfo", own_buf) == 0) + { + thread_ptr = all_threads.head; + sprintf (own_buf, "m%x", thread_ptr->id); + thread_ptr = thread_ptr->next; + return; + } + + if (strcmp ("qsThreadInfo", own_buf) == 0) + { + if (thread_ptr != NULL) + { + sprintf (own_buf, "m%x", thread_ptr->id); + thread_ptr = thread_ptr->next; + return; + } + else + { + sprintf (own_buf, "l"); + return; + } + } + + if (the_target->read_auxv != NULL + && strncmp ("qPart:auxv:read::", own_buf, 17) == 0) + { + char data[(PBUFSIZ - 1) / 2]; + CORE_ADDR ofs; + unsigned int len; + int n; + decode_m_packet (&own_buf[17], &ofs, &len); /* "OFS,LEN" */ + if (len > sizeof data) + len = sizeof data; + n = (*the_target->read_auxv) (ofs, data, len); + if (n == 0) + write_ok (own_buf); + else if (n < 0) + write_enn (own_buf); + else + convert_int_to_ascii (data, own_buf, n); + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; +} + +/* Parse vCont packets. */ +void +handle_v_cont (char *own_buf, char *status, unsigned char *signal) +{ + char *p, *q; + int n = 0, i = 0; + struct thread_resume *resume_info, default_action; + + /* Count the number of semicolons in the packet. There should be one + for every action. */ + p = &own_buf[5]; + while (p) + { + n++; + p++; + p = strchr (p, ';'); + } + /* Allocate room for one extra action, for the default remain-stopped + behavior; if no default action is in the list, we'll need the extra + slot. */ + resume_info = malloc ((n + 1) * sizeof (resume_info[0])); + + default_action.thread = -1; + default_action.leave_stopped = 1; + default_action.step = 0; + default_action.sig = 0; + + p = &own_buf[5]; + i = 0; + while (*p) + { + p++; + + resume_info[i].leave_stopped = 0; + + if (p[0] == 's' || p[0] == 'S') + resume_info[i].step = 1; + else if (p[0] == 'c' || p[0] == 'C') + resume_info[i].step = 0; + else + goto err; + + if (p[0] == 'S' || p[0] == 'C') + { + int sig; + sig = strtol (p + 1, &q, 16); + if (p == q) + goto err; + p = q; + + if (!target_signal_to_host_p (sig)) + goto err; + resume_info[i].sig = target_signal_to_host (sig); + } + else + { + resume_info[i].sig = 0; + p = p + 1; + } + + if (p[0] == 0) + { + resume_info[i].thread = -1; + default_action = resume_info[i]; + + /* Note: we don't increment i here, we'll overwrite this entry + the next time through. */ + } + else if (p[0] == ':') + { + resume_info[i].thread = strtol (p + 1, &q, 16); + if (p == q) + goto err; + p = q; + if (p[0] != ';' && p[0] != 0) + goto err; + + i++; + } + } + + resume_info[i] = default_action; + + /* Still used in occasional places in the backend. */ + if (n == 1 && resume_info[0].thread != -1) + cont_thread = resume_info[0].thread; + else + cont_thread = -1; + set_desired_inferior (0); + + (*the_target->resume) (resume_info); + + free (resume_info); + + *signal = mywait (status, 1); + prepare_resume_reply (own_buf, *status, *signal); + return; + +err: + /* No other way to report an error... */ + strcpy (own_buf, ""); + free (resume_info); + return; +} + +/* Handle all of the extended 'v' packets. */ +void +handle_v_requests (char *own_buf, char *status, unsigned char *signal) +{ + if (strncmp (own_buf, "vCont;", 6) == 0) + { + handle_v_cont (own_buf, status, signal); + return; + } + + if (strncmp (own_buf, "vCont?", 6) == 0) + { + strcpy (own_buf, "vCont;c;C;s;S"); + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; + return; +} + +void +handle_breakpoint_requests (char *own_buf, char *status, unsigned char *signal) +{ + /* Currently we only support software breakpoints */ + switch (own_buf[1]) { + case '0': /* software breakpoint, int3 based */ + own_buf[0] = '\0'; + break; + case '1': /* hardware breakpoint */ + default: + write_enn (own_buf); + break; + } +} + +void +myresume (int step, int sig) +{ + struct thread_resume resume_info[2]; + int n = 0; + + if (step || sig || cont_thread > 0) + { + resume_info[0].thread + = ((struct inferior_list_entry *) current_inferior)->id; + resume_info[0].step = step; + resume_info[0].sig = sig; + resume_info[0].leave_stopped = 0; + n++; + } + resume_info[n].thread = -1; + resume_info[n].step = 0; + resume_info[n].sig = 0; + resume_info[n].leave_stopped = (cont_thread > 0); + + (*the_target->resume) (resume_info); +} + +static int attached; + +static void +gdbserver_usage (void) +{ + error ("Usage:\tgdbserver COMM PROG [ARGS ...]\n" + "\tgdbserver COMM --attach PID\n" + "\tgdbserver COMM --file COREFILE\n" + "\n" + "COMM may either be a tty device (for serial debugging), or \n" + "HOST:PORT to listen for a TCP connection.\n"); +} + +extern control_c_pressed_flag; +#include + +void ctrl_c_handler(int signo) +{ + printf("Ctrl-C pressed: Quit from the attached gdb first\n"); + control_c_pressed_flag = 1; +} + +struct sigaction ctrl_c_sigaction = { .sa_handler = ctrl_c_handler }; +struct sigaction old_sigaction; + +int +main (int argc, char *argv[]) +{ + char ch, status, *own_buf, mem_buf[2000]; + int i = 0; + unsigned char signal; + unsigned int len; + CORE_ADDR mem_addr; + int bad_attach; + int pid; + char *arg_end; + + if (setjmp (toplevel)) + { + fprintf (stderr, "Exiting\n"); + exit (1); + } + + bad_attach = 0; + pid = 0; + attached = 0; + if (argc >= 3 && strcmp (argv[2], "--attach") == 0) + { + if (argc == 4 + && argv[3] != '\0' + && (pid = strtoul (argv[3], &arg_end, 10)) != 0 + && *arg_end == '\0') + { + ; + } + else + bad_attach = 1; + } + else if (argc >= 3 && strcmp (argv[2], "--file") == 0) + { + if (argc == 4 + && argv[3] != '\0') + { + if ((pid = open(argv[3], O_RDONLY)) <= 0) + bad_attach = 1; + else + isfile = 1; + } + else + bad_attach = 1; + } + + if (argc < 3 || bad_attach) + gdbserver_usage(); + + initialize_low (); + + own_buf = malloc (PBUFSIZ); + + if (pid == 0) + { + /* Wait till we are at first instruction in program. */ + signal = start_inferior (&argv[2], &status); + + /* We are now stopped at the first instruction of the target process */ + } + else + { + switch (attach_inferior (pid, &status, &signal)) + { + case -1: + error ("Attaching not supported on this target"); + break; + default: + attached = 1; + break; + } + } + + + while (1) + { + remote_open (argv[1]); + sigaction(SIGINT, &ctrl_c_sigaction, &old_sigaction); + + restart: + setjmp (toplevel); + while (getpkt (own_buf) > 0) + { + unsigned char sig; + i = 0; + ch = own_buf[i++]; + switch (ch) + { + case 'q': + handle_query (own_buf); + break; + case 'd': + remote_debug = !remote_debug; + break; + case 'D': + fprintf (stderr, "Detaching from inferior\n"); + detach_inferior (); + write_ok (own_buf); + putpkt (own_buf); + remote_close (); + + /* If we are attached, then we can exit. Otherwise, we need to + hang around doing nothing, until the child is gone. */ + if (!attached) + { + int status, ret; + + do { + ret = waitpid (signal_pid, &status, 0); + if (WIFEXITED (status) || WIFSIGNALED (status)) + break; + } while (ret != -1 || errno != ECHILD); + } + + exit (0); + + case '!': + if (attached == 0) + { + extended_protocol = 1; + prepare_resume_reply (own_buf, status, signal); + } + else + { + /* We can not use the extended protocol if we are + attached, because we can not restart the running + program. So return unrecognized. */ + own_buf[0] = '\0'; + } + break; + case '?': + prepare_resume_reply (own_buf, status, signal); + break; + case 'H': + switch (own_buf[1]) + { + case 'g': + general_thread = strtol (&own_buf[2], NULL, 16); + write_ok (own_buf); + set_desired_inferior (1); + break; + case 'c': + cont_thread = strtol (&own_buf[2], NULL, 16); + write_ok (own_buf); + break; + case 's': + step_thread = strtol (&own_buf[2], NULL, 16); + write_ok (own_buf); + break; + default: + /* Silently ignore it so that gdb can extend the protocol + without compatibility headaches. */ + own_buf[0] = '\0'; + break; + } + break; + case 'g': + set_desired_inferior (1); + registers_to_string (own_buf); + break; + case 'G': + set_desired_inferior (1); + registers_from_string (&own_buf[1]); + write_ok (own_buf); + break; + case 'm': + decode_m_packet (&own_buf[1], &mem_addr, &len); + if (read_inferior_memory (mem_addr, mem_buf, len) == 0) + convert_int_to_ascii (mem_buf, own_buf, len); + else + write_enn (own_buf); + break; + case 'M': + decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); + if (write_inferior_memory (mem_addr, mem_buf, len) == 0) + write_ok (own_buf); + else + write_enn (own_buf); + break; + case 'C': + convert_ascii_to_int (own_buf + 1, &sig, 1); + if (target_signal_to_host_p (sig)) + signal = target_signal_to_host (sig); + else + signal = 0; + set_desired_inferior (0); + myresume (0, signal); + signal = mywait (&status, 1); + prepare_resume_reply (own_buf, status, signal); + break; + case 'S': + convert_ascii_to_int (own_buf + 1, &sig, 1); + if (target_signal_to_host_p (sig)) + signal = target_signal_to_host (sig); + else + signal = 0; + set_desired_inferior (0); + myresume (1, signal); + signal = mywait (&status, 1); + prepare_resume_reply (own_buf, status, signal); + break; + case 'c': + set_desired_inferior (0); + myresume (0, 0); + signal = mywait (&status, 1); + prepare_resume_reply (own_buf, status, signal); + break; + case 's': + set_desired_inferior (0); + myresume (1, 0); + signal = mywait (&status, 1); + prepare_resume_reply (own_buf, status, signal); + break; + case 'k': + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + /* When using the extended protocol, we start up a new + debugging session. The traditional protocol will + exit instead. */ + if (extended_protocol) + { + write_ok (own_buf); + fprintf (stderr, "GDBserver restarting\n"); + + /* Wait till we are at 1st instruction in prog. */ + signal = start_inferior (&argv[2], &status); + goto restart; + break; + } + else + { + exit (0); + break; + } + case 'T': + if (mythread_alive (strtol (&own_buf[1], NULL, 16))) + write_ok (own_buf); + else + write_enn (own_buf); + break; + case 'R': + /* Restarting the inferior is only supported in the + extended protocol. */ + if (extended_protocol) + { + kill_inferior (); + write_ok (own_buf); + fprintf (stderr, "GDBserver restarting\n"); + + /* Wait till we are at 1st instruction in prog. */ + signal = start_inferior (&argv[2], &status); + goto restart; + break; + } + else + { + /* It is a request we don't understand. Respond with an + empty packet so that gdb knows that we don't support this + request. */ + own_buf[0] = '\0'; + break; + } + case 'v': + /* Extended (long) request. */ + handle_v_requests (own_buf, &status, &signal); + break; + case 'Z': + handle_breakpoint_requests (own_buf, &status, &signal); + break; + default: + /* It is a request we don't understand. Respond with an + empty packet so that gdb knows that we don't support this + request. */ + own_buf[0] = '\0'; + break; + } + + putpkt (own_buf); + + if (status == 'W') + fprintf (stderr, + "\nChild exited with status %d\n", signal); + if (status == 'X') + fprintf (stderr, "\nChild terminated with signal = 0x%x\n", + signal); + if (status == 'W' || status == 'X') + { + if (extended_protocol) + { + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + write_ok (own_buf); + fprintf (stderr, "GDBserver restarting\n"); + + /* Wait till we are at 1st instruction in prog. */ + signal = start_inferior (&argv[2], &status); + goto restart; + break; + } + else + { + fprintf (stderr, "GDBserver exiting\n"); + exit (0); + } + } + } + + /* We come here when getpkt fails. + + For the extended remote protocol we exit (and this is the only + way we gracefully exit!). + + For the traditional remote protocol close the connection, + and re-open it at the top of the loop. */ + detach_inferior (); + remote_close (); + if (extended_protocol) + exit (0); + else + fprintf (stderr, "Remote side has terminated connection. " + "GDBserver will reopen the connection.\n"); + sigaction(SIGINT, &old_sigaction, NULL); + } +} diff --git a/tools/debugger/gdb/gdb-6.2.1-xen-sparse/mkbuildtree b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/mkbuildtree new file mode 100755 index 0000000..6be1df1 --- /dev/null +++ b/tools/debugger/gdb/gdb-6.2.1-xen-sparse/mkbuildtree @@ -0,0 +1,115 @@ +#!/bin/bash + +# mkbuildtree +# +# Creates symbolic links in for the sparse tree +# in the current directory. + +# Script to determine the relative path between two directories. +# Copyright (c) D. J. Hawkey Jr. 2002 +# Fixed for Xen project by K. Fraser in 2003. +abs_to_rel () +{ + local CWD SRCPATH + + if [ "$1" != "/" -a "${1##*[^/]}" = "/" ]; then + SRCPATH=${1%?} + else + SRCPATH=$1 + fi + if [ "$2" != "/" -a "${2##*[^/]}" = "/" ]; then + DESTPATH=${2%?} + else + DESTPATH=$2 + fi + + CWD=$PWD + [ "${1%%[^/]*}" != "/" ] && cd $1 && SRCPATH=$PWD + [ "${2%%[^/]*}" != "/" ] && cd $2 && DESTPATH=$PWD + [ "$CWD" != "$PWD" ] && cd $CWD + + BASEPATH=$SRCPATH + + [ "$SRCPATH" = "$DESTPATH" ] && DESTPATH="." && return + [ "$SRCPATH" = "/" ] && DESTPATH=${DESTPATH#?} && return + + while [ "$BASEPATH/" != "${DESTPATH%${DESTPATH#$BASEPATH/}}" ]; do + BASEPATH=${BASEPATH%/*} + done + + SRCPATH=${SRCPATH#$BASEPATH} + DESTPATH=${DESTPATH#$BASEPATH} + DESTPATH=${DESTPATH#?} + while [ -n "$SRCPATH" ]; do + SRCPATH=${SRCPATH%/*} + DESTPATH="../$DESTPATH" + done + + [ -z "$BASEPATH" ] && BASEPATH="/" + [ "${DESTPATH##*[^/]}" = "/" ] && DESTPATH=${DESTPATH%?} +} + +# relative_lndir +# Creates a tree of symlinks in the current working directory that mirror +# real files in . should be relative to the current +# working directory. Symlinks in are ignored. Source-control files +# are ignored. +relative_lndir () +{ + local SYMLINK_DIR REAL_DIR pref i j + SYMLINK_DIR=$PWD + REAL_DIR=$1 + ( + cd $REAL_DIR + for i in `find . -type d | grep -v SCCS`; do + [ -d $SYMLINK_DIR/$i ] || mkdir -p $SYMLINK_DIR/$i + ( + cd $i + pref=`echo $i | sed -e 's#/[^/]*#../#g' -e 's#^\.##'` + for j in `find . -type f -o -type l -maxdepth 1`; do + ln -sf ${pref}${REAL_DIR}/$i/$j ${SYMLINK_DIR}/$i/$j + done + ) + done + ) +} + +[ "$1" == "" ] && { echo "Syntax: $0 "; exit 1; } + +# Get absolute path to the destination directory +pushd . >/dev/null +cd ${1} +AD=$PWD +popd >/dev/null + +# Get absolute path to the source directory +AS=`pwd` + +# Get name of sparse directory +SDN=$(basename $AS) + +# Get path to source, relative to destination +abs_to_rel ${AD} ${AS} +RS=$DESTPATH + +# We now work from the destination directory +cd ${AD} + +# Remove old symlinks +find sys -type l | while read f +do + case $(readlink $f) in + */$SDN/*) + rm -f $f + ;; + esac +done + +if [ -f ${AD}/BUILDING ]; then + # Create symlinks of files and directories which exist in the sparse source + (cd sys && relative_lndir ../${RS}/sys) +else + # Create symlinks of files and directories which exist in the sparse source + relative_lndir ${RS} + rm -f mkbuildtree +fi diff --git a/tools/debugger/gdb/gdbbuild b/tools/debugger/gdb/gdbbuild new file mode 100755 index 0000000..7d441da --- /dev/null +++ b/tools/debugger/gdb/gdbbuild @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e + +GDB_VERSION=6.2.1 + +[ "$GDB_MIRROR" ] || GDB_MIRROR="ftp://ftp.gnu.org/gnu/gdb/" + +rm -rf gdb-$GDB_VERSION gdb-$GDB_VERSION-linux-i386-xen +[ -a gdb-$GDB_VERSION.tar.bz2 ] || wget -c "$GDB_MIRROR/gdb-$GDB_VERSION.tar.bz2" +tar xjf gdb-$GDB_VERSION.tar.bz2 + +cd gdb-$GDB_VERSION-xen-sparse +bash ./mkbuildtree ../gdb-$GDB_VERSION + +cd .. +mkdir gdb-$GDB_VERSION-linux-i386-xen +cd gdb-$GDB_VERSION-linux-i386-xen +../gdb-$GDB_VERSION/configure + +# Use $MAKE if set, else use gmake if present, otherwise use make +if [ "$MAKE" ]; then + $MAKE +elif which gmake ; then + gmake -j4 +else + make -j4 +fi diff --git a/tools/debugger/xenitp/Makefile b/tools/debugger/xenitp/Makefile new file mode 100644 index 0000000..36cd0f4 --- /dev/null +++ b/tools/debugger/xenitp/Makefile @@ -0,0 +1,53 @@ +XEN_ROOT=../../.. +include $(XEN_ROOT)/tools/Rules.mk + +#CFLAGS += -Werror -g -O0 + +CFLAGS += $(CFLAGS_libxenctrl) + +HDRS = $(wildcard *.h) +OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) + +BIN = +LIBBIN = +SCRIPTS = +MAN1 = $(wildcard *.1) +MAN8 = $(wildcard *.8) + +ifeq ($(XEN_TARGET_ARCH),ia64) +LIBBIN += xenitp +endif + +.PHONY: all +all: build + +.PHONY: build +build: $(BIN) $(LIBBIN) + +.PHONY: install +install: build + $(INSTALL_DIR) $(DESTDIR)$(BINDIR) + [ -z "$(LIBBIN)" ] || $(INSTALL_DIR) $(DESTDIR)$(PRIVATE_BINDIR) + $(INSTALL_DIR) $(DESTDIR)$(MAN1DIR) + $(INSTALL_DIR) $(DESTDIR)$(MAN8DIR) + if [ "x$(SCRIPTS)" != "x" ]; then \ + $(INSTALL_PROG) $(BIN) $(SCRIPTS) $(DESTDIR)$(BINDIR); \ + fi + [ -z "$(LIBBIN)" ] || $(INSTALL_PROG) $(LIBBIN) $(DESTDIR)$(PRIVATE_BINDIR) + if [ "x$(MAN1)" != "x" ]; then \ + $(INSTALL_DATA) $(MAN1) $(DESTDIR)$(MAN1DIR); \ + fi + if [ "x$(MAN1)" != "x" ]; then \ + $(INSTALL_DATA) $(MAN8) $(DESTDIR)$(MAN8DIR); \ + fi + +.PHONY: clean +clean: + $(RM) *.a *.so *.o *.rpm $(BIN) $(LIBBIN) + +%: %.c $(HDRS) Makefile + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_libxenctrl) + +XENITP_OBJS=xenitp.o ia64-dis.o ia64-opc.o cpu-ia64-opc.o +xenitp: $(XENITP_OBJS) + $(CC) $(CFLAGS) -o $@ $(XENITP_OBJS) $(LDFLAGS_libxenctrl) diff --git a/tools/debugger/xenitp/README b/tools/debugger/xenitp/README new file mode 100644 index 0000000..0cd248e --- /dev/null +++ b/tools/debugger/xenitp/README @@ -0,0 +1,36 @@ +xenitp README +************* + + +Xenitp is a low-level, non-symbolic debugger. It only works on ia64. + +* Building xenitp +First do 'make tools' at the top level to build libxc and includes. Then +cd to tools/debugger/xenitp and do make. + +* Using xenitp +Usage is: xenitp DOM +DOM is a domain number. +'help' shows all the command available. +When the domain is running, C-c stops it. + +* Source origin +All these files come from binutils: +cpu-ia64-opc.c +dis-asm.h +ia64-asmtab.c +ia64-asmtab.h +ia64-dis.c +ia64-gen.c +ia64.h +ia64-opc-a.c +ia64-opc-b.c +ia64-opc.c +ia64-opc-d.c +ia64-opc-f.c +ia64-opc.h +ia64-opc-i.c +ia64-opc-m.c +ia64-opc-x.c + +xenitp.c is based on xenctxt.c diff --git a/tools/debugger/xenitp/cpu-ia64-opc.c b/tools/debugger/xenitp/cpu-ia64-opc.c new file mode 100644 index 0000000..317de45 --- /dev/null +++ b/tools/debugger/xenitp/cpu-ia64-opc.c @@ -0,0 +1,615 @@ +/* Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + +This file is part of BFD, the Binary File Descriptor library. + +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, write to the Free Software +Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Logically, this code should be part of libopcode but since some of + the operand insertion/extraction functions help bfd to implement + relocations, this code is included as part of cpu-ia64.c. This + avoids circular dependencies between libopcode and libbfd and also + obviates the need for applications to link in libopcode when all + they really want is libbfd. + + --davidm Mon Apr 13 22:14:02 1998 */ + +#include "ia64-opc.h" + +#define NELEMS(a) ((int) (sizeof (a) / sizeof ((a)[0]))) + +static const char* +ins_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) +{ + return "internal error---this shouldn't happen"; +} + +static const char* +ext_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) +{ + return "internal error---this shouldn't happen"; +} + +static const char* +ins_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char* +ext_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char* +ins_reg (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + if (value >= 1u << self->field[0].bits) + return "register number out of range"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_reg (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) + & ((1u << self->field[0].bits) - 1)); + return 0; +} + +static const char* +ins_immu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + ia64_insn new = 0; + int i; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + new |= ((value & ((((ia64_insn) 1) << self->field[i].bits) - 1)) + << self->field[i].shift); + value >>= self->field[i].bits; + } + if (value) + return "integer operand out of range"; + + *code |= new; + return 0; +} + +static const char* +ext_immu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + BFD_HOST_U_64_BIT value = 0; + int i, bits = 0, total = 0; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + bits = self->field[i].bits; + value |= ((code >> self->field[i].shift) + & ((((BFD_HOST_U_64_BIT) 1) << bits) - 1)) << total; + total += bits; + } + *valuep = value; + return 0; +} + +static const char* +ins_immu5b (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code) +{ + if (value < 32 || value > 63) + return "value must be between 32 and 63"; + return ins_immu (self, value - 32, code); +} + +static const char* +ext_immu5b (const struct ia64_operand *self, ia64_insn code, + ia64_insn *valuep) +{ + const char *result; + + result = ext_immu (self, code, valuep); + if (result) + return result; + + *valuep = *valuep + 32; + return 0; +} + +static const char* +ins_immus8 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + if (value & 0x7) + return "value not an integer multiple of 8"; + return ins_immu (self, value >> 3, code); +} + +static const char* +ext_immus8 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *result; + + result = ext_immu (self, code, valuep); + if (result) + return result; + + *valuep = *valuep << 3; + return 0; +} + +static const char* +ins_imms_scaled (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code, int scale) +{ + BFD_HOST_64_BIT svalue = value, sign_bit = 0; + ia64_insn new = 0; + int i; + + svalue >>= scale; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + new |= ((svalue & ((((ia64_insn) 1) << self->field[i].bits) - 1)) + << self->field[i].shift); + sign_bit = (svalue >> (self->field[i].bits - 1)) & 1; + svalue >>= self->field[i].bits; + } + if ((!sign_bit && svalue != 0) || (sign_bit && svalue != -1)) + return "integer operand out of range"; + + *code |= new; + return 0; +} + +static const char* +ext_imms_scaled (const struct ia64_operand *self, ia64_insn code, + ia64_insn *valuep, int scale) +{ + int i, bits = 0, total = 0; + BFD_HOST_64_BIT val = 0, sign; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + bits = self->field[i].bits; + val |= ((code >> self->field[i].shift) + & ((((BFD_HOST_U_64_BIT) 1) << bits) - 1)) << total; + total += bits; + } + /* sign extend: */ + sign = (BFD_HOST_64_BIT) 1 << (total - 1); + val = (val ^ sign) - sign; + + *valuep = (val << scale); + return 0; +} + +static const char* +ins_imms (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ins_immsu4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; + + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ext_imms (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 0); +} + +static const char* +ins_immsm1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ins_immsm1u4 (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code) +{ + value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; + + --value; + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ext_immsm1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *res = ext_imms_scaled (self, code, valuep, 0); + + ++*valuep; + return res; +} + +static const char* +ins_imms1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 1); +} + +static const char* +ext_imms1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 1); +} + +static const char* +ins_imms4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 4); +} + +static const char* +ext_imms4 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 4); +} + +static const char* +ins_imms16 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 16); +} + +static const char* +ext_imms16 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 16); +} + +static const char* +ins_cimmu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + ia64_insn mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + return ins_immu (self, value ^ mask, code); +} + +static const char* +ext_cimmu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *result; + ia64_insn mask; + + mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + result = ext_immu (self, code, valuep); + if (!result) + { + mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + *valuep ^= mask; + } + return result; +} + +static const char* +ins_cnt (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + if (value >= ((BFD_HOST_U_64_BIT) 1) << self->field[0].bits) + return "count out of range"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) + & ((((BFD_HOST_U_64_BIT) 1) << self->field[0].bits) - 1)) + 1; + return 0; +} + +static const char* +ins_cnt2b (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + + if (value > 2) + return "count must be in range 1..3"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt2b (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) & 0x3) + 1; + return 0; +} + +static const char* +ins_cnt2c (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + switch (value) + { + case 0: value = 0; break; + case 7: value = 1; break; + case 15: value = 2; break; + case 16: value = 3; break; + default: return "count must be 0, 7, 15, or 16"; + } + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt2c (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + ia64_insn value; + + value = (code >> self->field[0].shift) & 0x3; + switch (value) + { + case 0: value = 0; break; + case 1: value = 7; break; + case 2: value = 15; break; + case 3: value = 16; break; + } + *valuep = value; + return 0; +} + +static const char* +ins_inc3 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + BFD_HOST_64_BIT val = value; + BFD_HOST_U_64_BIT sign = 0; + + if (val < 0) + { + sign = 0x4; + value = -value; + } + switch (value) + { + case 1: value = 3; break; + case 4: value = 2; break; + case 8: value = 1; break; + case 16: value = 0; break; + default: return "count must be +/- 1, 4, 8, or 16"; + } + *code |= (sign | value) << self->field[0].shift; + return 0; +} + +static const char* +ext_inc3 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + BFD_HOST_64_BIT val; + int negate; + + val = (code >> self->field[0].shift) & 0x7; + negate = val & 0x4; + switch (val & 0x3) + { + case 0: val = 16; break; + case 1: val = 8; break; + case 2: val = 4; break; + case 3: val = 1; break; + } + if (negate) + val = -val; + + *valuep = val; + return 0; +} + +#define CST IA64_OPND_CLASS_CST +#define REG IA64_OPND_CLASS_REG +#define IND IA64_OPND_CLASS_IND +#define ABS IA64_OPND_CLASS_ABS +#define REL IA64_OPND_CLASS_REL + +#define SDEC IA64_OPND_FLAG_DECIMAL_SIGNED +#define UDEC IA64_OPND_FLAG_DECIMAL_UNSIGNED + +const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT] = + { + /* constants: */ + { CST, ins_const, ext_const, "NIL", {{ 0, 0}}, 0, "" }, + { CST, ins_const, ext_const, "ar.csd", {{ 0, 0}}, 0, "ar.csd" }, + { CST, ins_const, ext_const, "ar.ccv", {{ 0, 0}}, 0, "ar.ccv" }, + { CST, ins_const, ext_const, "ar.pfs", {{ 0, 0}}, 0, "ar.pfs" }, + { CST, ins_const, ext_const, "1", {{ 0, 0}}, 0, "1" }, + { CST, ins_const, ext_const, "8", {{ 0, 0}}, 0, "8" }, + { CST, ins_const, ext_const, "16", {{ 0, 0}}, 0, "16" }, + { CST, ins_const, ext_const, "r0", {{ 0, 0}}, 0, "r0" }, + { CST, ins_const, ext_const, "ip", {{ 0, 0}}, 0, "ip" }, + { CST, ins_const, ext_const, "pr", {{ 0, 0}}, 0, "pr" }, + { CST, ins_const, ext_const, "pr.rot", {{ 0, 0}}, 0, "pr.rot" }, + { CST, ins_const, ext_const, "psr", {{ 0, 0}}, 0, "psr" }, + { CST, ins_const, ext_const, "psr.l", {{ 0, 0}}, 0, "psr.l" }, + { CST, ins_const, ext_const, "psr.um", {{ 0, 0}}, 0, "psr.um" }, + + /* register operands: */ + { REG, ins_reg, ext_reg, "ar", {{ 7, 20}}, 0, /* AR3 */ + "an application register" }, + { REG, ins_reg, ext_reg, "b", {{ 3, 6}}, 0, /* B1 */ + "a branch register" }, + { REG, ins_reg, ext_reg, "b", {{ 3, 13}}, 0, /* B2 */ + "a branch register"}, + { REG, ins_reg, ext_reg, "cr", {{ 7, 20}}, 0, /* CR */ + "a control register"}, + { REG, ins_reg, ext_reg, "f", {{ 7, 6}}, 0, /* F1 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 13}}, 0, /* F2 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 20}}, 0, /* F3 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 27}}, 0, /* F4 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "p", {{ 6, 6}}, 0, /* P1 */ + "a predicate register" }, + { REG, ins_reg, ext_reg, "p", {{ 6, 27}}, 0, /* P2 */ + "a predicate register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 6}}, 0, /* R1 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 13}}, 0, /* R2 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 20}}, 0, /* R3 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 2, 20}}, 0, /* R3_2 */ + "a general register r0-r3" }, + + /* memory operands: */ + { IND, ins_reg, ext_reg, "", {{7, 20}}, 0, /* MR3 */ + "a memory address" }, + + /* indirect operands: */ + { IND, ins_reg, ext_reg, "cpuid", {{7, 20}}, 0, /* CPUID_R3 */ + "a cpuid register" }, + { IND, ins_reg, ext_reg, "dbr", {{7, 20}}, 0, /* DBR_R3 */ + "a dbr register" }, + { IND, ins_reg, ext_reg, "dtr", {{7, 20}}, 0, /* DTR_R3 */ + "a dtr register" }, + { IND, ins_reg, ext_reg, "itr", {{7, 20}}, 0, /* ITR_R3 */ + "an itr register" }, + { IND, ins_reg, ext_reg, "ibr", {{7, 20}}, 0, /* IBR_R3 */ + "an ibr register" }, + { IND, ins_reg, ext_reg, "msr", {{7, 20}}, 0, /* MSR_R3 */ + "an msr register" }, + { IND, ins_reg, ext_reg, "pkr", {{7, 20}}, 0, /* PKR_R3 */ + "a pkr register" }, + { IND, ins_reg, ext_reg, "pmc", {{7, 20}}, 0, /* PMC_R3 */ + "a pmc register" }, + { IND, ins_reg, ext_reg, "pmd", {{7, 20}}, 0, /* PMD_R3 */ + "a pmd register" }, + { IND, ins_reg, ext_reg, "rr", {{7, 20}}, 0, /* RR_R3 */ + "an rr register" }, + + /* immediate operands: */ + { ABS, ins_cimmu, ext_cimmu, 0, {{ 5, 20 }}, UDEC, /* CCNT5 */ + "a 5-bit count (0-31)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 2, 27 }}, UDEC, /* CNT2a */ + "a 2-bit count (1-4)" }, + { ABS, ins_cnt2b, ext_cnt2b, 0, {{ 2, 27 }}, UDEC, /* CNT2b */ + "a 2-bit count (1-3)" }, + { ABS, ins_cnt2c, ext_cnt2c, 0, {{ 2, 30 }}, UDEC, /* CNT2c */ + "a count (0, 7, 15, or 16)" }, + { ABS, ins_immu, ext_immu, 0, {{ 5, 14}}, UDEC, /* CNT5 */ + "a 5-bit count (0-31)" }, + { ABS, ins_immu, ext_immu, 0, {{ 6, 27}}, UDEC, /* CNT6 */ + "a 6-bit count (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 20}}, UDEC, /* CPOS6a */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 14}}, UDEC, /* CPOS6b */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 31}}, UDEC, /* CPOS6c */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_imms, ext_imms, 0, {{ 1, 36}}, SDEC, /* IMM1 */ + "a 1-bit integer (-1, 0)" }, + { ABS, ins_immu, ext_immu, 0, {{ 2, 13}}, UDEC, /* IMMU2 */ + "a 2-bit unsigned (0-3)" }, + { ABS, ins_immu5b, ext_immu5b, 0, {{ 5, 14}}, UDEC, /* IMMU5b */ + "a 5-bit unsigned (32 + (0-31))" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, 0, /* IMMU7a */ + "a 7-bit unsigned (0-127)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, 0, /* IMMU7b */ + "a 7-bit unsigned (0-127)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, UDEC, /* SOF */ + "a frame size (register count)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, UDEC, /* SOL */ + "a local register count" }, + { ABS, ins_immus8,ext_immus8,0, {{ 4, 27}}, UDEC, /* SOR */ + "a rotating register count (integer multiple of 8)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM8 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer (-128-127)" }, + { ABS, ins_immsu4, ext_imms, 0, /* IMM8U4 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit signed integer for 32-bit unsigned compare (-128-127)" }, + { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer (-127-128)" }, + { ABS, ins_immsm1u4, ext_immsm1, 0, /* IMM8M1U4 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer for 32-bit unsigned compare (-127-(-1),1-128,0x100000000)" }, + { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1U8 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer for 64-bit unsigned compare (-127-(-1),1-128,0x10000000000000000)" }, + { ABS, ins_immu, ext_immu, 0, {{ 2, 33}, { 7, 20}}, 0, /* IMMU9 */ + "a 9-bit unsigned (0-511)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM9a */ + {{ 7, 6}, { 1, 27}, { 1, 36}}, SDEC, + "a 9-bit integer (-256-255)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM9b */ + {{ 7, 13}, { 1, 27}, { 1, 36}}, SDEC, + "a 9-bit integer (-256-255)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM14 */ + {{ 7, 13}, { 6, 27}, { 1, 36}}, SDEC, + "a 14-bit integer (-8192-8191)" }, + { ABS, ins_imms1, ext_imms1, 0, /* IMM17 */ + {{ 7, 6}, { 8, 24}, { 1, 36}}, 0, + "a 17-bit integer (-65536-65535)" }, + { ABS, ins_immu, ext_immu, 0, {{20, 6}, { 1, 36}}, 0, /* IMMU21 */ + "a 21-bit unsigned" }, + { ABS, ins_imms, ext_imms, 0, /* IMM22 */ + {{ 7, 13}, { 9, 27}, { 5, 22}, { 1, 36}}, SDEC, + "a 22-bit signed integer" }, + { ABS, ins_immu, ext_immu, 0, /* IMMU24 */ + {{21, 6}, { 2, 31}, { 1, 36}}, 0, + "a 24-bit unsigned" }, + { ABS, ins_imms16,ext_imms16,0, {{27, 6}, { 1, 36}}, 0, /* IMM44 */ + "a 44-bit unsigned (least 16 bits ignored/zeroes)" }, + { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU62 */ + "a 62-bit unsigned" }, + { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU64 */ + "a 64-bit unsigned" }, + { ABS, ins_inc3, ext_inc3, 0, {{ 3, 13}}, SDEC, /* INC3 */ + "an increment (+/- 1, 4, 8, or 16)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 4, 27}}, UDEC, /* LEN4 */ + "a 4-bit length (1-16)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 6, 27}}, UDEC, /* LEN6 */ + "a 6-bit length (1-64)" }, + { ABS, ins_immu, ext_immu, 0, {{ 4, 20}}, 0, /* MBTYPE4 */ + "a mix type (@rev, @mix, @shuf, @alt, or @brcst)" }, + { ABS, ins_immu, ext_immu, 0, {{ 8, 20}}, 0, /* MBTYPE8 */ + "an 8-bit mix type" }, + { ABS, ins_immu, ext_immu, 0, {{ 6, 14}}, UDEC, /* POS6 */ + "a 6-bit bit pos (0-63)" }, + { REL, ins_imms4, ext_imms4, 0, {{ 7, 6}, { 2, 33}}, 0, /* TAG13 */ + "a branch tag" }, + { REL, ins_imms4, ext_imms4, 0, {{ 9, 24}}, 0, /* TAG13b */ + "a branch tag" }, + { REL, ins_imms4, ext_imms4, 0, {{20, 6}, { 1, 36}}, 0, /* TGT25 */ + "a branch target" }, + { REL, ins_imms4, ext_imms4, 0, /* TGT25b */ + {{ 7, 6}, {13, 20}, { 1, 36}}, 0, + "a branch target" }, + { REL, ins_imms4, ext_imms4, 0, {{20, 13}, { 1, 36}}, 0, /* TGT25c */ + "a branch target" }, + { REL, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* TGT64 */ + "a branch target" }, + + { ABS, ins_const, ext_const, 0, {{0, 0}}, 0, /* LDXMOV */ + "ldxmov target" }, + }; diff --git a/tools/debugger/xenitp/dis-asm.h b/tools/debugger/xenitp/dis-asm.h new file mode 100644 index 0000000..efb6eaf --- /dev/null +++ b/tools/debugger/xenitp/dis-asm.h @@ -0,0 +1,548 @@ +/* Interface between the opcode library and its callers. + + Copyright 1999, 2000, 2001, 2002, 2003, 2004, 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, 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, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. + + Written by Cygnus Support, 1993. + + The opcode library (libopcodes.a) provides instruction decoders for + a large variety of instruction sets, callable with an identical + interface, for making instruction-processing programs more independent + of the instruction set being processed. */ + +#ifndef DIS_ASM_H +#define DIS_ASM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#define PARAMS(x) x +typedef void *PTR; +typedef uint64_t bfd_vma; +typedef int64_t bfd_signed_vma; +typedef uint8_t bfd_byte; +typedef int bfd_boolean; +#define BFD_HOST_U_64_BIT unsigned long +#define BFD_HOST_64_BIT long +#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x) + +extern unsigned long bfd_getl64 (const bfd_byte *addr); + +#define BFD64 +#define ATTRIBUTE_FPTR_PRINTF_2 +#define ATTRIBUTE_UNUSED __attribute__((unused)) + +enum bfd_flavour { + bfd_target_unknown_flavour, + bfd_target_aout_flavour, + bfd_target_coff_flavour, + bfd_target_ecoff_flavour, + bfd_target_elf_flavour, + bfd_target_ieee_flavour, + bfd_target_nlm_flavour, + bfd_target_oasys_flavour, + bfd_target_tekhex_flavour, + bfd_target_srec_flavour, + bfd_target_ihex_flavour, + bfd_target_som_flavour, + bfd_target_os9k_flavour, + bfd_target_versados_flavour, + bfd_target_msdos_flavour, + bfd_target_evax_flavour +}; + +enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; + +enum bfd_architecture +{ + bfd_arch_unknown, /* File arch not known */ + bfd_arch_obscure, /* Arch known, not one of these */ + bfd_arch_m68k, /* Motorola 68xxx */ +#define bfd_mach_m68000 1 +#define bfd_mach_m68008 2 +#define bfd_mach_m68010 3 +#define bfd_mach_m68020 4 +#define bfd_mach_m68030 5 +#define bfd_mach_m68040 6 +#define bfd_mach_m68060 7 +#define bfd_mach_cpu32 8 +#define bfd_mach_mcf5200 9 +#define bfd_mach_mcf5206e 10 +#define bfd_mach_mcf5307 11 +#define bfd_mach_mcf5407 12 +#define bfd_mach_mcf528x 13 +#define bfd_mach_mcfv4e 14 +#define bfd_mach_mcf521x 15 +#define bfd_mach_mcf5249 16 +#define bfd_mach_mcf547x 17 +#define bfd_mach_mcf548x 18 + bfd_arch_vax, /* DEC Vax */ + bfd_arch_i960, /* Intel 960 */ + /* The order of the following is important. + lower number indicates a machine type that + only accepts a subset of the instructions + available to machines with higher numbers. + The exception is the "ca", which is + incompatible with all other machines except + "core". */ + +#define bfd_mach_i960_core 1 +#define bfd_mach_i960_ka_sa 2 +#define bfd_mach_i960_kb_sb 3 +#define bfd_mach_i960_mc 4 +#define bfd_mach_i960_xa 5 +#define bfd_mach_i960_ca 6 +#define bfd_mach_i960_jx 7 +#define bfd_mach_i960_hx 8 + + bfd_arch_a29k, /* AMD 29000 */ + bfd_arch_sparc, /* SPARC */ +#define bfd_mach_sparc 1 +/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */ +#define bfd_mach_sparc_sparclet 2 +#define bfd_mach_sparc_sparclite 3 +#define bfd_mach_sparc_v8plus 4 +#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */ +#define bfd_mach_sparc_sparclite_le 6 +#define bfd_mach_sparc_v9 7 +#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */ +#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */ +#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */ +/* Nonzero if MACH has the v9 instruction set. */ +#define bfd_mach_sparc_v9_p(mach) \ + ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \ + && (mach) != bfd_mach_sparc_sparclite_le) + bfd_arch_mips, /* MIPS Rxxxx */ +#define bfd_mach_mips3000 3000 +#define bfd_mach_mips3900 3900 +#define bfd_mach_mips4000 4000 +#define bfd_mach_mips4010 4010 +#define bfd_mach_mips4100 4100 +#define bfd_mach_mips4300 4300 +#define bfd_mach_mips4400 4400 +#define bfd_mach_mips4600 4600 +#define bfd_mach_mips4650 4650 +#define bfd_mach_mips5000 5000 +#define bfd_mach_mips6000 6000 +#define bfd_mach_mips8000 8000 +#define bfd_mach_mips10000 10000 +#define bfd_mach_mips16 16 + bfd_arch_i386, /* Intel 386 */ +#define bfd_mach_i386_i386 0 +#define bfd_mach_i386_i8086 1 +#define bfd_mach_i386_i386_intel_syntax 2 +#define bfd_mach_x86_64 3 +#define bfd_mach_x86_64_intel_syntax 4 + bfd_arch_we32k, /* AT&T WE32xxx */ + bfd_arch_tahoe, /* CCI/Harris Tahoe */ + bfd_arch_i860, /* Intel 860 */ + bfd_arch_romp, /* IBM ROMP PC/RT */ + bfd_arch_alliant, /* Alliant */ + bfd_arch_convex, /* Convex */ + bfd_arch_m88k, /* Motorola 88xxx */ + bfd_arch_pyramid, /* Pyramid Technology */ + bfd_arch_h8300, /* Hitachi H8/300 */ +#define bfd_mach_h8300 1 +#define bfd_mach_h8300h 2 +#define bfd_mach_h8300s 3 + bfd_arch_powerpc, /* PowerPC */ +#define bfd_mach_ppc 0 +#define bfd_mach_ppc64 1 +#define bfd_mach_ppc_403 403 +#define bfd_mach_ppc_403gc 4030 +#define bfd_mach_ppc_505 505 +#define bfd_mach_ppc_601 601 +#define bfd_mach_ppc_602 602 +#define bfd_mach_ppc_603 603 +#define bfd_mach_ppc_ec603e 6031 +#define bfd_mach_ppc_604 604 +#define bfd_mach_ppc_620 620 +#define bfd_mach_ppc_630 630 +#define bfd_mach_ppc_750 750 +#define bfd_mach_ppc_860 860 +#define bfd_mach_ppc_a35 35 +#define bfd_mach_ppc_rs64ii 642 +#define bfd_mach_ppc_rs64iii 643 +#define bfd_mach_ppc_7400 7400 + bfd_arch_rs6000, /* IBM RS/6000 */ + bfd_arch_hppa, /* HP PA RISC */ + bfd_arch_d10v, /* Mitsubishi D10V */ + bfd_arch_z8k, /* Zilog Z8000 */ +#define bfd_mach_z8001 1 +#define bfd_mach_z8002 2 + bfd_arch_h8500, /* Hitachi H8/500 */ + bfd_arch_sh, /* Hitachi SH */ +#define bfd_mach_sh 1 +#define bfd_mach_sh2 0x20 +#define bfd_mach_sh_dsp 0x2d +#define bfd_mach_sh2a 0x2a +#define bfd_mach_sh2a_nofpu 0x2b +#define bfd_mach_sh2e 0x2e +#define bfd_mach_sh3 0x30 +#define bfd_mach_sh3_nommu 0x31 +#define bfd_mach_sh3_dsp 0x3d +#define bfd_mach_sh3e 0x3e +#define bfd_mach_sh4 0x40 +#define bfd_mach_sh4_nofpu 0x41 +#define bfd_mach_sh4_nommu_nofpu 0x42 +#define bfd_mach_sh4a 0x4a +#define bfd_mach_sh4a_nofpu 0x4b +#define bfd_mach_sh4al_dsp 0x4d +#define bfd_mach_sh5 0x50 + bfd_arch_alpha, /* Dec Alpha */ +#define bfd_mach_alpha 1 + bfd_arch_arm, /* Advanced Risc Machines ARM */ +#define bfd_mach_arm_2 1 +#define bfd_mach_arm_2a 2 +#define bfd_mach_arm_3 3 +#define bfd_mach_arm_3M 4 +#define bfd_mach_arm_4 5 +#define bfd_mach_arm_4T 6 + bfd_arch_ns32k, /* National Semiconductors ns32000 */ + bfd_arch_w65, /* WDC 65816 */ + bfd_arch_tic30, /* Texas Instruments TMS320C30 */ + bfd_arch_v850, /* NEC V850 */ +#define bfd_mach_v850 0 + bfd_arch_arc, /* Argonaut RISC Core */ +#define bfd_mach_arc_base 0 + bfd_arch_m32r, /* Mitsubishi M32R/D */ +#define bfd_mach_m32r 0 /* backwards compatibility */ + bfd_arch_mn10200, /* Matsushita MN10200 */ + bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_last + }; + +typedef struct symbol_cache_entry +{ + const char *name; + union + { + PTR p; + bfd_vma i; + } udata; +} asymbol; + +typedef int (*fprintf_ftype) (void *, const char*, ...) ATTRIBUTE_FPTR_PRINTF_2; + +enum dis_insn_type { + dis_noninsn, /* Not a valid instruction */ + dis_nonbranch, /* Not a branch instruction */ + dis_branch, /* Unconditional branch */ + dis_condbranch, /* Conditional branch */ + dis_jsr, /* Jump to subroutine */ + dis_condjsr, /* Conditional jump to subroutine */ + dis_dref, /* Data reference instruction */ + dis_dref2 /* Two data references in instruction */ +}; + +/* This struct is passed into the instruction decoding routine, + and is passed back out into each callback. The various fields are used + for conveying information from your main routine into your callbacks, + for passing information into the instruction decoders (such as the + addresses of the callback functions), or for passing information + back from the instruction decoders to their callers. + + It must be initialized before it is first passed; this can be done + by hand, or using one of the initialization macros below. */ + +typedef struct disassemble_info { + fprintf_ftype fprintf_func; + void *stream; + void *application_data; + + /* Target description. We could replace this with a pointer to the bfd, + but that would require one. There currently isn't any such requirement + so to avoid introducing one we record these explicitly. */ + /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ + enum bfd_flavour flavour; + /* The bfd_arch value. */ + enum bfd_architecture arch; + /* The bfd_mach value. */ + unsigned long mach; + /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ + enum bfd_endian endian; + /* An arch/mach-specific bitmask of selected instruction subsets, mainly + for processors with run-time-switchable instruction sets. The default, + zero, means that there is no constraint. CGEN-based opcodes ports + may use ISA_foo masks. */ + void *insn_sets; + + /* Some targets need information about the current section to accurately + display insns. If this is NULL, the target disassembler function + will have to make its best guess. */ + //asection *section; + + /* An array of pointers to symbols either at the location being disassembled + or at the start of the function being disassembled. The array is sorted + so that the first symbol is intended to be the one used. The others are + present for any misc. purposes. This is not set reliably, but if it is + not NULL, it is correct. */ + asymbol **symbols; + /* Number of symbols in array. */ + int num_symbols; + + /* For use by the disassembler. + The top 16 bits are reserved for public use (and are documented here). + The bottom 16 bits are for the internal use of the disassembler. */ + unsigned long flags; +#define INSN_HAS_RELOC 0x80000000 + void *private_data; + + /* Function used to get bytes to disassemble. MEMADDR is the + address of the stuff to be disassembled, MYADDR is the address to + put the bytes in, and LENGTH is the number of bytes to read. + INFO is a pointer to this struct. + Returns an errno value or 0 for success. */ + int (*read_memory_func) + (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + struct disassemble_info *info); + + /* Function which should be called if we get an error that we can't + recover from. STATUS is the errno value from read_memory_func and + MEMADDR is the address that we were trying to read. INFO is a + pointer to this struct. */ + void (*memory_error_func) + (int status, bfd_vma memaddr, struct disassemble_info *info); + + /* Function called to print ADDR. */ + void (*print_address_func) + (bfd_vma addr, struct disassemble_info *info); + + /* Function called to determine if there is a symbol at the given ADDR. + If there is, the function returns 1, otherwise it returns 0. + This is used by ports which support an overlay manager where + the overlay number is held in the top part of an address. In + some circumstances we want to include the overlay number in the + address, (normally because there is a symbol associated with + that address), but sometimes we want to mask out the overlay bits. */ + int (* symbol_at_address_func) + (bfd_vma addr, struct disassemble_info * info); + + /* Function called to check if a SYMBOL is can be displayed to the user. + This is used by some ports that want to hide special symbols when + displaying debugging outout. */ + bfd_boolean (* symbol_is_valid) + (asymbol *, struct disassemble_info * info); + + /* These are for buffer_read_memory. */ + bfd_byte *buffer; + bfd_vma buffer_vma; + unsigned int buffer_length; + + /* This variable may be set by the instruction decoder. It suggests + the number of bytes objdump should display on a single line. If + the instruction decoder sets this, it should always set it to + the same value in order to get reasonable looking output. */ + int bytes_per_line; + + /* The next two variables control the way objdump displays the raw data. */ + /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ + /* output will look like this: + 00: 00000000 00000000 + with the chunks displayed according to "display_endian". */ + int bytes_per_chunk; + enum bfd_endian display_endian; + + /* Number of octets per incremented target address + Normally one, but some DSPs have byte sizes of 16 or 32 bits. */ + unsigned int octets_per_byte; + + /* The number of zeroes we want to see at the end of a section before we + start skipping them. */ + unsigned int skip_zeroes; + + /* The number of zeroes to skip at the end of a section. If the number + of zeroes at the end is between SKIP_ZEROES_AT_END and SKIP_ZEROES, + they will be disassembled. If there are fewer than + SKIP_ZEROES_AT_END, they will be skipped. This is a heuristic + attempt to avoid disassembling zeroes inserted by section + alignment. */ + unsigned int skip_zeroes_at_end; + + /* Whether the disassembler always needs the relocations. */ + bfd_boolean disassembler_needs_relocs; + + /* Results from instruction decoders. Not all decoders yet support + this information. This info is set each time an instruction is + decoded, and is only valid for the last such instruction. + + To determine whether this decoder supports this information, set + insn_info_valid to 0, decode an instruction, then check it. */ + + char insn_info_valid; /* Branch info has been set. */ + char branch_delay_insns; /* How many sequential insn's will run before + a branch takes effect. (0 = normal) */ + char data_size; /* Size of data reference in insn, in bytes */ + enum dis_insn_type insn_type; /* Type of instruction */ + bfd_vma target; /* Target address of branch or dref, if known; + zero if unknown. */ + bfd_vma target2; /* Second target address for dref2 */ + + /* Command line options specific to the target disassembler. */ + char * disassembler_options; + +} disassemble_info; + + +/* Standard disassemblers. Disassemble one instruction at the given + target address. Return number of octets processed. */ +typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); + +extern int print_insn_big_mips (bfd_vma, disassemble_info *); +extern int print_insn_little_mips (bfd_vma, disassemble_info *); +extern int print_insn_i386 (bfd_vma, disassemble_info *); +extern int print_insn_i386_att (bfd_vma, disassemble_info *); +extern int print_insn_i386_intel (bfd_vma, disassemble_info *); +extern int print_insn_ia64 (bfd_vma, disassemble_info *); +extern int print_insn_i370 (bfd_vma, disassemble_info *); +extern int print_insn_m68hc11 (bfd_vma, disassemble_info *); +extern int print_insn_m68hc12 (bfd_vma, disassemble_info *); +extern int print_insn_m68k (bfd_vma, disassemble_info *); +extern int print_insn_z80 (bfd_vma, disassemble_info *); +extern int print_insn_z8001 (bfd_vma, disassemble_info *); +extern int print_insn_z8002 (bfd_vma, disassemble_info *); +extern int print_insn_h8300 (bfd_vma, disassemble_info *); +extern int print_insn_h8300h (bfd_vma, disassemble_info *); +extern int print_insn_h8300s (bfd_vma, disassemble_info *); +extern int print_insn_h8500 (bfd_vma, disassemble_info *); +extern int print_insn_alpha (bfd_vma, disassemble_info *); +extern int print_insn_big_arm (bfd_vma, disassemble_info *); +extern int print_insn_little_arm (bfd_vma, disassemble_info *); +extern int print_insn_sparc (bfd_vma, disassemble_info *); +extern int print_insn_avr (bfd_vma, disassemble_info *); +extern int print_insn_bfin (bfd_vma, disassemble_info *); +extern int print_insn_d10v (bfd_vma, disassemble_info *); +extern int print_insn_d30v (bfd_vma, disassemble_info *); +extern int print_insn_dlx (bfd_vma, disassemble_info *); +extern int print_insn_fr30 (bfd_vma, disassemble_info *); +extern int print_insn_hppa (bfd_vma, disassemble_info *); +extern int print_insn_i860 (bfd_vma, disassemble_info *); +extern int print_insn_i960 (bfd_vma, disassemble_info *); +extern int print_insn_ip2k (bfd_vma, disassemble_info *); +extern int print_insn_m32r (bfd_vma, disassemble_info *); +extern int print_insn_m88k (bfd_vma, disassemble_info *); +extern int print_insn_maxq_little (bfd_vma, disassemble_info *); +extern int print_insn_maxq_big (bfd_vma, disassemble_info *); +extern int print_insn_mcore (bfd_vma, disassemble_info *); +extern int print_insn_mmix (bfd_vma, disassemble_info *); +extern int print_insn_mn10200 (bfd_vma, disassemble_info *); +extern int print_insn_mn10300 (bfd_vma, disassemble_info *); +extern int print_insn_mt (bfd_vma, disassemble_info *); +extern int print_insn_msp430 (bfd_vma, disassemble_info *); +extern int print_insn_ns32k (bfd_vma, disassemble_info *); +extern int print_insn_crx (bfd_vma, disassemble_info *); +extern int print_insn_openrisc (bfd_vma, disassemble_info *); +extern int print_insn_big_or32 (bfd_vma, disassemble_info *); +extern int print_insn_little_or32 (bfd_vma, disassemble_info *); +extern int print_insn_pdp11 (bfd_vma, disassemble_info *); +extern int print_insn_pj (bfd_vma, disassemble_info *); +extern int print_insn_big_powerpc (bfd_vma, disassemble_info *); +extern int print_insn_little_powerpc (bfd_vma, disassemble_info *); +extern int print_insn_rs6000 (bfd_vma, disassemble_info *); +extern int print_insn_s390 (bfd_vma, disassemble_info *); +extern int print_insn_sh (bfd_vma, disassemble_info *); +extern int print_insn_tic30 (bfd_vma, disassemble_info *); +extern int print_insn_tic4x (bfd_vma, disassemble_info *); +extern int print_insn_tic54x (bfd_vma, disassemble_info *); +extern int print_insn_tic80 (bfd_vma, disassemble_info *); +extern int print_insn_v850 (bfd_vma, disassemble_info *); +extern int print_insn_vax (bfd_vma, disassemble_info *); +extern int print_insn_w65 (bfd_vma, disassemble_info *); +extern int print_insn_xstormy16 (bfd_vma, disassemble_info *); +extern int print_insn_xtensa (bfd_vma, disassemble_info *); +extern int print_insn_sh64 (bfd_vma, disassemble_info *); +extern int print_insn_sh64x_media (bfd_vma, disassemble_info *); +extern int print_insn_frv (bfd_vma, disassemble_info *); +extern int print_insn_iq2000 (bfd_vma, disassemble_info *); +extern int print_insn_xc16x (bfd_vma, disassemble_info *); +extern int print_insn_m32c (bfd_vma, disassemble_info *); + +extern disassembler_ftype arc_get_disassembler (void *); +//extern disassembler_ftype cris_get_disassembler (bfd *); + +extern void print_mips_disassembler_options (FILE *); +extern void print_ppc_disassembler_options (FILE *); +extern void print_arm_disassembler_options (FILE *); +extern void parse_arm_disassembler_option (char *); +extern int get_arm_regname_num_options (void); +extern int set_arm_regname_option (int); +extern int get_arm_regnames (int, const char **, const char **, const char *const **); +extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *); + +/* Fetch the disassembler for a given BFD, if that support is available. */ +//extern disassembler_ftype disassembler (bfd *); + +/* Amend the disassemble_info structure as necessary for the target architecture. + Should only be called after initialising the info->arch field. */ +extern void disassemble_init_for_target (struct disassemble_info * info); + +/* Document any target specific options available from the disassembler. */ +extern void disassembler_usage (FILE *); + + +/* This block of definitions is for particular callers who read instructions + into a buffer before calling the instruction decoder. */ + +/* Here is a function which callers may wish to use for read_memory_func. + It gets bytes from a buffer. */ +extern int buffer_read_memory + (bfd_vma, bfd_byte *, unsigned int, struct disassemble_info *); + +/* This function goes with buffer_read_memory. + It prints a message using info->fprintf_func and info->stream. */ +extern void perror_memory (int, bfd_vma, struct disassemble_info *); + + +/* Just print the address in hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ +extern void generic_print_address + (bfd_vma, struct disassemble_info *); + +/* Always true. */ +extern int generic_symbol_at_address + (bfd_vma, struct disassemble_info *); + +/* Also always true. */ +extern bfd_boolean generic_symbol_is_valid + (asymbol *, struct disassemble_info *); + +/* Method to initialize a disassemble_info struct. This should be + called by all applications creating such a struct. */ +extern void init_disassemble_info (struct disassemble_info *info, void *stream, + fprintf_ftype fprintf_func); + +/* For compatibility with existing code. */ +#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ + init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) +#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ + init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) + + +#ifdef __cplusplus +} +#endif + +#endif /* ! defined (DIS_ASM_H) */ diff --git a/tools/debugger/xenitp/ia64-asmtab.c b/tools/debugger/xenitp/ia64-asmtab.c new file mode 100644 index 0000000..6350fd4 --- /dev/null +++ b/tools/debugger/xenitp/ia64-asmtab.c @@ -0,0 +1,8774 @@ +/* This file is automatically generated by ia64-gen. Do not edit! */ +static const char * const ia64_strings[] = { + "", "0", "1", "a", "acq", "add", "addl", "addp4", "adds", "alloc", "and", + "andcm", "b", "bias", "br", "break", "brl", "brp", "bsw", "c", "call", + "cexit", "chk", "cloop", "clr", "clrrrb", "cmp", "cmp4", "cmp8xchg16", + "cmpxchg1", "cmpxchg2", "cmpxchg4", "cmpxchg8", "cond", "cover", "ctop", + "czx1", "czx2", "d", "dep", "dpnt", "dptk", "e", "epc", "eq", "excl", + "exit", "exp", "extr", "f", "fabs", "fadd", "famax", "famin", "fand", + "fandcm", "fault", "fc", "fchkf", "fclass", "fclrf", "fcmp", "fcvt", + "fetchadd4", "fetchadd8", "few", "fill", "flushrs", "fma", "fmax", + "fmerge", "fmin", "fmix", "fmpy", "fms", "fneg", "fnegabs", "fnma", + "fnmpy", "fnorm", "for", "fpabs", "fpack", "fpamax", "fpamin", "fpcmp", + "fpcvt", "fpma", "fpmax", "fpmerge", "fpmin", "fpmpy", "fpms", "fpneg", + "fpnegabs", "fpnma", "fpnmpy", "fprcpa", "fprsqrta", "frcpa", "frsqrta", + "fselect", "fsetc", "fsub", "fswap", "fsxt", "fwb", "fx", "fxor", "fxu", + "g", "ga", "ge", "getf", "geu", "gt", "gtu", "h", "hint", "hu", "i", "ia", + "imp", "invala", "itc", "itr", "l", "ld1", "ld16", "ld2", "ld4", "ld8", + "ldf", "ldf8", "ldfd", "ldfe", "ldfp8", "ldfpd", "ldfps", "ldfs", "le", + "leu", "lfetch", "loadrs", "loop", "lr", "lt", "ltu", "lu", "m", "many", + "mf", "mix1", "mix2", "mix4", "mov", "movl", "mux1", "mux2", "nc", "ne", + "neq", "nge", "ngt", "nl", "nle", "nlt", "nm", "nop", "nr", "ns", "nt1", + "nt2", "nta", "nz", "or", "orcm", "ord", "pack2", "pack4", "padd1", + "padd2", "padd4", "pavg1", "pavg2", "pavgsub1", "pavgsub2", "pcmp1", + "pcmp2", "pcmp4", "pmax1", "pmax2", "pmin1", "pmin2", "pmpy2", "pmpyshr2", + "popcnt", "pr", "probe", "psad1", "pshl2", "pshl4", "pshladd2", "pshr2", + "pshr4", "pshradd2", "psub1", "psub2", "psub4", "ptc", "ptr", "r", "raz", + "rel", "ret", "rfi", "rsm", "rum", "rw", "s", "s0", "s1", "s2", "s3", + "sa", "se", "setf", "shl", "shladd", "shladdp4", "shr", "shrp", "sig", + "spill", "spnt", "sptk", "srlz", "ssm", "sss", "st1", "st16", "st2", + "st4", "st8", "stf", "stf8", "stfd", "stfe", "stfs", "sub", "sum", "sxt1", + "sxt2", "sxt4", "sync", "tak", "tbit", "tf", "thash", "tnat", "tpa", + "trunc", "ttag", "u", "unc", "unord", "unpack1", "unpack2", "unpack4", + "uss", "uus", "uuu", "vmsw", "w", "wexit", "wtop", "x", "xchg1", "xchg2", + "xchg4", "xchg8", "xf", "xma", "xmpy", "xor", "xuf", "z", "zxt1", "zxt2", + "zxt4", +}; + +static const struct ia64_dependency +dependencies[] = { + { "ALAT", 0, 0, 0, -1, NULL, }, + { "AR[BSP]", 26, 0, 2, 17, NULL, }, + { "AR[BSPSTORE]", 26, 0, 2, 18, NULL, }, + { "AR[CCV]", 26, 0, 2, 32, NULL, }, + { "AR[CFLG]", 26, 0, 2, 27, NULL, }, + { "AR[CSD]", 26, 0, 2, 25, NULL, }, + { "AR[EC]", 26, 0, 2, 66, NULL, }, + { "AR[EFLAG]", 26, 0, 2, 24, NULL, }, + { "AR[FCR]", 26, 0, 2, 21, NULL, }, + { "AR[FDR]", 26, 0, 2, 30, NULL, }, + { "AR[FIR]", 26, 0, 2, 29, NULL, }, + { "AR[FPSR].sf0.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf1.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf2.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf3.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].traps", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].rv", 30, 0, 2, -1, NULL, }, + { "AR[FSR]", 26, 0, 2, 28, NULL, }, + { "AR[ITC]", 26, 0, 2, 44, NULL, }, + { "AR[K%], % in 0 - 7", 1, 0, 2, -1, NULL, }, + { "AR[LC]", 26, 0, 2, 65, NULL, }, + { "AR[PFS]", 26, 0, 2, 64, NULL, }, + { "AR[PFS]", 26, 0, 2, 64, NULL, }, + { "AR[PFS]", 26, 0, 0, 64, NULL, }, + { "AR[RNAT]", 26, 0, 2, 19, NULL, }, + { "AR[RSC]", 26, 0, 2, 16, NULL, }, + { "AR[SSD]", 26, 0, 2, 26, NULL, }, + { "AR[UNAT]{%}, % in 0 - 63", 2, 0, 2, -1, NULL, }, + { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 0, 0, -1, NULL, }, + { "AR%, % in 48-63, 112-127", 4, 0, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 0, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 0, -1, NULL, }, + { "CPUID#", 7, 0, 5, -1, NULL, }, + { "CR[CMCV]", 27, 0, 3, 74, NULL, }, + { "CR[DCR]", 27, 0, 3, 0, NULL, }, + { "CR[EOI]", 27, 0, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI РCR67)\" on page 2:119", }, + { "CR[GPTA]", 27, 0, 3, 9, NULL, }, + { "CR[IFA]", 27, 0, 1, 20, NULL, }, + { "CR[IFA]", 27, 0, 3, 20, NULL, }, + { "CR[IFS]", 27, 0, 3, 23, NULL, }, + { "CR[IFS]", 27, 0, 1, 23, NULL, }, + { "CR[IFS]", 27, 0, 1, 23, NULL, }, + { "CR[IHA]", 27, 0, 3, 25, NULL, }, + { "CR[IIM]", 27, 0, 3, 24, NULL, }, + { "CR[IIP]", 27, 0, 3, 19, NULL, }, + { "CR[IIP]", 27, 0, 1, 19, NULL, }, + { "CR[IIPA]", 27, 0, 3, 22, NULL, }, + { "CR[IPSR]", 27, 0, 3, 16, NULL, }, + { "CR[IPSR]", 27, 0, 1, 16, NULL, }, + { "CR[IRR%], % in 0 - 3", 8, 0, 3, -1, NULL, }, + { "CR[ISR]", 27, 0, 3, 17, NULL, }, + { "CR[ITIR]", 27, 0, 3, 21, NULL, }, + { "CR[ITIR]", 27, 0, 1, 21, NULL, }, + { "CR[ITM]", 27, 0, 3, 1, NULL, }, + { "CR[ITV]", 27, 0, 3, 72, NULL, }, + { "CR[IVA]", 27, 0, 4, 2, NULL, }, + { "CR[IVR]", 27, 0, 7, 65, "SC Section 5.8.3.2, \"External Interrupt Vector Register (IVR РCR65)\" on page 2:118", }, + { "CR[LID]", 27, 0, 7, 64, "SC Section 5.8.3.1, \"Local ID (LID РCR64)\" on page 2:117", }, + { "CR[LRR%], % in 0 - 1", 9, 0, 3, -1, NULL, }, + { "CR[PMV]", 27, 0, 3, 73, NULL, }, + { "CR[PTA]", 27, 0, 3, 8, NULL, }, + { "CR[TPR]", 27, 0, 3, 66, NULL, }, + { "CR[TPR]", 27, 0, 7, 66, "SC Section 5.8.3.3, \"Task Priority Register (TPR РCR66)\" on page 2:119", }, + { "CR[TPR]", 27, 0, 1, 66, NULL, }, + { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 0, 0, -1, NULL, }, + { "DBR#", 11, 0, 2, -1, NULL, }, + { "DBR#", 11, 0, 3, -1, NULL, }, + { "DTC", 0, 0, 3, -1, NULL, }, + { "DTC", 0, 0, 2, -1, NULL, }, + { "DTC", 0, 0, 0, -1, NULL, }, + { "DTC", 0, 0, 2, -1, NULL, }, + { "DTC_LIMIT*", 0, 0, 2, -1, NULL, }, + { "DTR", 0, 0, 3, -1, NULL, }, + { "DTR", 0, 0, 2, -1, NULL, }, + { "DTR", 0, 0, 3, -1, NULL, }, + { "DTR", 0, 0, 0, -1, NULL, }, + { "DTR", 0, 0, 2, -1, NULL, }, + { "FR%, % in 0 - 1", 12, 0, 0, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 0, 2, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 0, 0, -1, NULL, }, + { "GR0", 14, 0, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 0, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 0, 2, -1, NULL, }, + { "IBR#", 16, 0, 2, -1, NULL, }, + { "InService*", 17, 0, 3, -1, NULL, }, + { "InService*", 17, 0, 2, -1, NULL, }, + { "InService*", 17, 0, 2, -1, NULL, }, + { "IP", 0, 0, 0, -1, NULL, }, + { "ITC", 0, 0, 4, -1, NULL, }, + { "ITC", 0, 0, 2, -1, NULL, }, + { "ITC", 0, 0, 0, -1, NULL, }, + { "ITC", 0, 0, 4, -1, NULL, }, + { "ITC", 0, 0, 2, -1, NULL, }, + { "ITC_LIMIT*", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 4, -1, NULL, }, + { "ITR", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 0, -1, NULL, }, + { "ITR", 0, 0, 4, -1, NULL, }, + { "memory", 0, 0, 0, -1, NULL, }, + { "MSR#", 18, 0, 5, -1, NULL, }, + { "PKR#", 19, 0, 3, -1, NULL, }, + { "PKR#", 19, 0, 0, -1, NULL, }, + { "PKR#", 19, 0, 2, -1, NULL, }, + { "PKR#", 19, 0, 2, -1, NULL, }, + { "PMC#", 20, 0, 2, -1, NULL, }, + { "PMC#", 20, 0, 7, -1, "SC Section 7.2.1, \"Generic Performance Counter Registers\" for PMC[0].fr on page 2:150", }, + { "PMD#", 21, 0, 2, -1, NULL, }, + { "PR0", 0, 0, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 0, -1, NULL, }, + { "PR63", 24, 0, 2, -1, NULL, }, + { "PR63", 24, 0, 2, -1, NULL, }, + { "PR63", 24, 0, 0, -1, NULL, }, + { "PSR.ac", 28, 0, 1, 3, NULL, }, + { "PSR.ac", 28, 0, 3, 3, NULL, }, + { "PSR.ac", 28, 0, 2, 3, NULL, }, + { "PSR.ac", 28, 0, 2, 3, NULL, }, + { "PSR.be", 28, 0, 1, 1, NULL, }, + { "PSR.be", 28, 0, 3, 1, NULL, }, + { "PSR.be", 28, 0, 2, 1, NULL, }, + { "PSR.be", 28, 0, 2, 1, NULL, }, + { "PSR.bn", 28, 0, 2, 44, NULL, }, + { "PSR.cpl", 28, 0, 1, 32, NULL, }, + { "PSR.cpl", 28, 0, 2, 32, NULL, }, + { "PSR.da", 28, 0, 2, 38, NULL, }, + { "PSR.db", 28, 0, 3, 24, NULL, }, + { "PSR.db", 28, 0, 2, 24, NULL, }, + { "PSR.db", 28, 0, 2, 24, NULL, }, + { "PSR.dd", 28, 0, 2, 39, NULL, }, + { "PSR.dfh", 28, 0, 3, 19, NULL, }, + { "PSR.dfh", 28, 0, 2, 19, NULL, }, + { "PSR.dfh", 28, 0, 2, 19, NULL, }, + { "PSR.dfl", 28, 0, 3, 18, NULL, }, + { "PSR.dfl", 28, 0, 2, 18, NULL, }, + { "PSR.dfl", 28, 0, 2, 18, NULL, }, + { "PSR.di", 28, 0, 3, 22, NULL, }, + { "PSR.di", 28, 0, 2, 22, NULL, }, + { "PSR.di", 28, 0, 2, 22, NULL, }, + { "PSR.dt", 28, 0, 3, 17, NULL, }, + { "PSR.dt", 28, 0, 2, 17, NULL, }, + { "PSR.dt", 28, 0, 2, 17, NULL, }, + { "PSR.ed", 28, 0, 2, 43, NULL, }, + { "PSR.i", 28, 0, 2, 14, NULL, }, + { "PSR.ia", 28, 0, 0, 14, NULL, }, + { "PSR.ic", 28, 0, 2, 13, NULL, }, + { "PSR.ic", 28, 0, 3, 13, NULL, }, + { "PSR.ic", 28, 0, 2, 13, NULL, }, + { "PSR.id", 28, 0, 0, 14, NULL, }, + { "PSR.is", 28, 0, 0, 14, NULL, }, + { "PSR.it", 28, 0, 2, 14, NULL, }, + { "PSR.lp", 28, 0, 2, 25, NULL, }, + { "PSR.lp", 28, 0, 3, 25, NULL, }, + { "PSR.lp", 28, 0, 2, 25, NULL, }, + { "PSR.mc", 28, 0, 2, 35, NULL, }, + { "PSR.mfh", 28, 0, 2, 5, NULL, }, + { "PSR.mfl", 28, 0, 2, 4, NULL, }, + { "PSR.pk", 28, 0, 3, 15, NULL, }, + { "PSR.pk", 28, 0, 2, 15, NULL, }, + { "PSR.pk", 28, 0, 2, 15, NULL, }, + { "PSR.pp", 28, 0, 2, 21, NULL, }, + { "PSR.ri", 28, 0, 0, 41, NULL, }, + { "PSR.rt", 28, 0, 2, 27, NULL, }, + { "PSR.rt", 28, 0, 3, 27, NULL, }, + { "PSR.rt", 28, 0, 2, 27, NULL, }, + { "PSR.si", 28, 0, 2, 23, NULL, }, + { "PSR.si", 28, 0, 3, 23, NULL, }, + { "PSR.si", 28, 0, 2, 23, NULL, }, + { "PSR.sp", 28, 0, 2, 20, NULL, }, + { "PSR.sp", 28, 0, 3, 20, NULL, }, + { "PSR.sp", 28, 0, 2, 20, NULL, }, + { "PSR.ss", 28, 0, 2, 40, NULL, }, + { "PSR.tb", 28, 0, 3, 26, NULL, }, + { "PSR.tb", 28, 0, 2, 26, NULL, }, + { "PSR.tb", 28, 0, 2, 26, NULL, }, + { "PSR.up", 28, 0, 2, 2, NULL, }, + { "PSR.vm", 28, 0, 1, 46, NULL, }, + { "PSR.vm", 28, 0, 2, 46, NULL, }, + { "RR#", 25, 0, 3, -1, NULL, }, + { "RR#", 25, 0, 2, -1, NULL, }, + { "RSE", 29, 0, 2, -1, NULL, }, + { "ALAT", 0, 1, 0, -1, NULL, }, + { "AR[BSP]", 26, 1, 2, 17, NULL, }, + { "AR[BSPSTORE]", 26, 1, 2, 18, NULL, }, + { "AR[CCV]", 26, 1, 2, 32, NULL, }, + { "AR[CFLG]", 26, 1, 2, 27, NULL, }, + { "AR[CSD]", 26, 1, 2, 25, NULL, }, + { "AR[EC]", 26, 1, 2, 66, NULL, }, + { "AR[EFLAG]", 26, 1, 2, 24, NULL, }, + { "AR[FCR]", 26, 1, 2, 21, NULL, }, + { "AR[FDR]", 26, 1, 2, 30, NULL, }, + { "AR[FIR]", 26, 1, 2, 29, NULL, }, + { "AR[FPSR].sf0.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].rv", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].traps", 30, 1, 2, -1, NULL, }, + { "AR[FSR]", 26, 1, 2, 28, NULL, }, + { "AR[ITC]", 26, 1, 2, 44, NULL, }, + { "AR[K%], % in 0 - 7", 1, 1, 2, -1, NULL, }, + { "AR[LC]", 26, 1, 2, 65, NULL, }, + { "AR[PFS]", 26, 1, 0, 64, NULL, }, + { "AR[PFS]", 26, 1, 2, 64, NULL, }, + { "AR[PFS]", 26, 1, 2, 64, NULL, }, + { "AR[RNAT]", 26, 1, 2, 19, NULL, }, + { "AR[RSC]", 26, 1, 2, 16, NULL, }, + { "AR[SSD]", 26, 1, 2, 26, NULL, }, + { "AR[UNAT]{%}, % in 0 - 63", 2, 1, 2, -1, NULL, }, + { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 1, 0, -1, NULL, }, + { "AR%, % in 48 - 63, 112-127", 4, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 0, -1, NULL, }, + { "CFM", 6, 1, 2, -1, NULL, }, + { "CPUID#", 7, 1, 0, -1, NULL, }, + { "CR[CMCV]", 27, 1, 2, 74, NULL, }, + { "CR[DCR]", 27, 1, 2, 0, NULL, }, + { "CR[EOI]", 27, 1, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI РCR67)\" on page 2:119", }, + { "CR[GPTA]", 27, 1, 2, 9, NULL, }, + { "CR[IFA]", 27, 1, 2, 20, NULL, }, + { "CR[IFS]", 27, 1, 2, 23, NULL, }, + { "CR[IHA]", 27, 1, 2, 25, NULL, }, + { "CR[IIM]", 27, 1, 2, 24, NULL, }, + { "CR[IIP]", 27, 1, 2, 19, NULL, }, + { "CR[IIPA]", 27, 1, 2, 22, NULL, }, + { "CR[IPSR]", 27, 1, 2, 16, NULL, }, + { "CR[IRR%], % in 0 - 3", 8, 1, 2, -1, NULL, }, + { "CR[ISR]", 27, 1, 2, 17, NULL, }, + { "CR[ITIR]", 27, 1, 2, 21, NULL, }, + { "CR[ITM]", 27, 1, 2, 1, NULL, }, + { "CR[ITV]", 27, 1, 2, 72, NULL, }, + { "CR[IVA]", 27, 1, 2, 2, NULL, }, + { "CR[IVR]", 27, 1, 7, 65, "SC", }, + { "CR[LID]", 27, 1, 7, 64, "SC", }, + { "CR[LRR%], % in 0 - 1", 9, 1, 2, -1, NULL, }, + { "CR[PMV]", 27, 1, 2, 73, NULL, }, + { "CR[PTA]", 27, 1, 2, 8, NULL, }, + { "CR[TPR]", 27, 1, 2, 66, NULL, }, + { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 1, 0, -1, NULL, }, + { "DBR#", 11, 1, 2, -1, NULL, }, + { "DTC", 0, 1, 0, -1, NULL, }, + { "DTC", 0, 1, 2, -1, NULL, }, + { "DTC", 0, 1, 2, -1, NULL, }, + { "DTC_LIMIT*", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 0, -1, NULL, }, + { "FR%, % in 0 - 1", 12, 1, 0, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 1, 2, -1, NULL, }, + { "GR0", 14, 1, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 1, 2, -1, NULL, }, + { "IBR#", 16, 1, 2, -1, NULL, }, + { "InService*", 17, 1, 7, -1, "SC", }, + { "IP", 0, 1, 0, -1, NULL, }, + { "ITC", 0, 1, 0, -1, NULL, }, + { "ITC", 0, 1, 2, -1, NULL, }, + { "ITC", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 0, -1, NULL, }, + { "memory", 0, 1, 0, -1, NULL, }, + { "MSR#", 18, 1, 7, -1, "SC", }, + { "PKR#", 19, 1, 0, -1, NULL, }, + { "PKR#", 19, 1, 0, -1, NULL, }, + { "PKR#", 19, 1, 2, -1, NULL, }, + { "PMC#", 20, 1, 2, -1, NULL, }, + { "PMD#", 21, 1, 2, -1, NULL, }, + { "PR0", 0, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, + { "PR63", 24, 1, 0, -1, NULL, }, + { "PR63", 24, 1, 0, -1, NULL, }, + { "PR63", 24, 1, 2, -1, NULL, }, + { "PR63", 24, 1, 2, -1, NULL, }, + { "PSR.ac", 28, 1, 2, 3, NULL, }, + { "PSR.be", 28, 1, 2, 1, NULL, }, + { "PSR.bn", 28, 1, 2, 44, NULL, }, + { "PSR.cpl", 28, 1, 2, 32, NULL, }, + { "PSR.da", 28, 1, 2, 38, NULL, }, + { "PSR.db", 28, 1, 2, 24, NULL, }, + { "PSR.dd", 28, 1, 2, 39, NULL, }, + { "PSR.dfh", 28, 1, 2, 19, NULL, }, + { "PSR.dfl", 28, 1, 2, 18, NULL, }, + { "PSR.di", 28, 1, 2, 22, NULL, }, + { "PSR.dt", 28, 1, 2, 17, NULL, }, + { "PSR.ed", 28, 1, 2, 43, NULL, }, + { "PSR.i", 28, 1, 2, 14, NULL, }, + { "PSR.ia", 28, 1, 2, 14, NULL, }, + { "PSR.ic", 28, 1, 2, 13, NULL, }, + { "PSR.id", 28, 1, 2, 14, NULL, }, + { "PSR.is", 28, 1, 2, 14, NULL, }, + { "PSR.it", 28, 1, 2, 14, NULL, }, + { "PSR.lp", 28, 1, 2, 25, NULL, }, + { "PSR.mc", 28, 1, 2, 35, NULL, }, + { "PSR.mfh", 28, 1, 0, 5, NULL, }, + { "PSR.mfh", 28, 1, 2, 5, NULL, }, + { "PSR.mfh", 28, 1, 2, 5, NULL, }, + { "PSR.mfl", 28, 1, 0, 4, NULL, }, + { "PSR.mfl", 28, 1, 2, 4, NULL, }, + { "PSR.mfl", 28, 1, 2, 4, NULL, }, + { "PSR.pk", 28, 1, 2, 15, NULL, }, + { "PSR.pp", 28, 1, 2, 21, NULL, }, + { "PSR.ri", 28, 1, 2, 41, NULL, }, + { "PSR.rt", 28, 1, 2, 27, NULL, }, + { "PSR.si", 28, 1, 2, 23, NULL, }, + { "PSR.sp", 28, 1, 2, 20, NULL, }, + { "PSR.ss", 28, 1, 2, 40, NULL, }, + { "PSR.tb", 28, 1, 2, 26, NULL, }, + { "PSR.up", 28, 1, 2, 2, NULL, }, + { "PSR.vm", 28, 1, 2, 46, NULL, }, + { "RR#", 25, 1, 2, -1, NULL, }, + { "RSE", 29, 1, 2, -1, NULL, }, + { "PR63", 24, 2, 6, -1, NULL, }, +}; + +static const unsigned short dep0[] = { + 97, 282, 2140, 2327, +}; + +static const unsigned short dep1[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2327, 4135, 20616, +}; + +static const unsigned short dep2[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2347, 2348, 2351, + 2352, 2355, 2356, +}; + +static const unsigned short dep3[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 20616, +}; + +static const unsigned short dep4[] = { + 97, 282, 22646, 22647, 22649, 22650, 22652, 22653, 22655, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep5[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 4135, 20616, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep6[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2347, 2349, + 2351, 2353, 2355, +}; + +static const unsigned short dep7[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2345, 2348, 2349, 2352, 2353, 2356, 4135, 20616, +}; + +static const unsigned short dep8[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2346, 2348, 2350, + 2352, 2354, 2356, +}; + +static const unsigned short dep9[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2346, 2347, 2350, 2351, 2354, 2355, 4135, 20616, +}; + +static const unsigned short dep10[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2346, 2347, + 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, +}; + +static const unsigned short dep11[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, + 4135, 20616, +}; + +static const unsigned short dep12[] = { + 97, 282, 2395, +}; + +static const unsigned short dep13[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2082, 2083, 2166, 2168, + 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep14[] = { + 97, 163, 282, 325, 2395, 28866, 29018, +}; + +static const unsigned short dep15[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 28, 29, 30, 31, 32, 33, 40, 41, 97, 150, 152, 158, 162, + 164, 175, 185, 186, 188, 282, 325, 2082, 2083, 2166, 2168, 2169, 2171, 2172, + 2174, 2175, 4135, 28866, 29018, +}; + +static const unsigned short dep16[] = { + 1, 6, 40, 97, 137, 196, 201, 241, 282, 312, 2395, 28866, 29018, +}; + +static const unsigned short dep17[] = { + 1, 25, 27, 38, 40, 41, 97, 158, 162, 164, 166, 167, 175, 185, 186, 188, 196, + 201, 241, 282, 312, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, + 4135, 28866, 29018, +}; + +static const unsigned short dep18[] = { + 1, 40, 51, 97, 196, 241, 248, 282, 28866, 29018, +}; + +static const unsigned short dep19[] = { + 1, 38, 40, 41, 97, 158, 160, 161, 162, 175, 185, 190, 191, 196, 241, 248, + 282, 4135, 28866, 29018, +}; + +static const unsigned short dep20[] = { + 40, 97, 241, 282, +}; + +static const unsigned short dep21[] = { + 97, 158, 162, 175, 185, 241, 282, +}; + +static const unsigned short dep22[] = { + 1, 40, 97, 131, 135, 136, 138, 139, 142, 143, 146, 149, 152, 155, 156, 157, + 158, 161, 162, 163, 164, 167, 168, 169, 170, 173, 174, 175, 178, 181, 184, + 185, 188, 189, 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, + 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 28866, 29018, +}; + +static const unsigned short dep23[] = { + 1, 38, 40, 41, 50, 51, 55, 58, 73, 97, 137, 138, 158, 162, 175, 185, 190, + 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, 334, 335, 336, + 337, 338, 339, 340, 341, 342, 343, 344, 4135, 28866, 29018, +}; + +static const unsigned short dep24[] = { + 97, 136, 282, 311, +}; + +static const unsigned short dep25[] = { + 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 311, +}; + +static const unsigned short dep26[] = { + 97, 137, 282, 312, +}; + +static const unsigned short dep27[] = { + 25, 26, 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 312, + +}; + +static const unsigned short dep28[] = { + 97, 190, 282, 344, +}; + +static const unsigned short dep29[] = { + 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 344, +}; + +static const unsigned short dep30[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, + 4135, +}; + +static const unsigned short dep31[] = { + 1, 25, 40, 97, 196, 228, 229, 241, 282, 2082, 2285, 2288, 2395, 28866, 29018, + +}; + +static const unsigned short dep32[] = { + 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228, + 230, 241, 282, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, + 2288, 4135, 28866, 29018, +}; + +static const unsigned short dep33[] = { + 97, 282, +}; + +static const unsigned short dep34[] = { + 97, 158, 162, 175, 185, 282, 2082, 2084, +}; + +static const unsigned short dep35[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2168, 2169, 2171, + 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep36[] = { + 6, 37, 38, 39, 97, 125, 126, 201, 241, 282, 307, 308, 2395, +}; + +static const unsigned short dep37[] = { + 6, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 241, 282, 307, + 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep38[] = { + 24, 97, 227, 282, 2395, +}; + +static const unsigned short dep39[] = { + 24, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 227, 282, 2166, 2168, 2169, + 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep40[] = { + 6, 24, 37, 38, 39, 97, 125, 126, 201, 227, 241, 282, 307, 308, 2395, +}; + +static const unsigned short dep41[] = { + 6, 24, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 227, 241, 282, + 307, 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep42[] = { + 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228, + 230, 241, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, 2288, 4135, + 28866, 29018, +}; + +static const unsigned short dep43[] = { + 97, 158, 162, 175, 185, 282, +}; + +static const unsigned short dep44[] = { + 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep45[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep46[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325, + 18601, 18602, 18761, 18762, 18764, 18765, 22646, 22647, 22648, 22650, 22651, + 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep47[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep48[] = { + 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep49[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep50[] = { + 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep51[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep52[] = { + 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep53[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep54[] = { + 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep55[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep56[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325, + 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep57[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, +}; + +static const unsigned short dep58[] = { + 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep59[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep60[] = { + 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep61[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep62[] = { + 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep63[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep64[] = { + 97, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep65[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, +}; + +static const unsigned short dep66[] = { + 11, 97, 206, 282, +}; + +static const unsigned short dep67[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 206, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep68[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep69[] = { + 12, 97, 207, 282, +}; + +static const unsigned short dep70[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 207, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep71[] = { + 13, 97, 208, 282, +}; + +static const unsigned short dep72[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 208, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep73[] = { + 14, 97, 209, 282, +}; + +static const unsigned short dep74[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 209, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep75[] = { + 15, 97, 211, 212, 282, +}; + +static const unsigned short dep76[] = { + 40, 41, 97, 158, 162, 175, 185, 211, 212, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep77[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep78[] = { + 16, 97, 214, 215, 282, +}; + +static const unsigned short dep79[] = { + 40, 41, 97, 158, 162, 175, 185, 214, 215, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep80[] = { + 17, 97, 217, 218, 282, +}; + +static const unsigned short dep81[] = { + 40, 41, 97, 158, 162, 175, 185, 217, 218, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep82[] = { + 18, 97, 220, 221, 282, +}; + +static const unsigned short dep83[] = { + 40, 41, 97, 158, 162, 175, 185, 220, 221, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep84[] = { + 15, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, + 2170, 2173, 4135, +}; + +static const unsigned short dep85[] = { + 15, 16, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep86[] = { + 15, 17, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep87[] = { + 15, 18, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep88[] = { + 15, 97, 210, 211, 282, +}; + +static const unsigned short dep89[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep90[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, +}; + +static const unsigned short dep91[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep92[] = { + 16, 97, 213, 214, 282, +}; + +static const unsigned short dep93[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep94[] = { + 17, 97, 216, 217, 282, +}; + +static const unsigned short dep95[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep96[] = { + 18, 97, 219, 220, 282, +}; + +static const unsigned short dep97[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep98[] = { + 15, 97, 210, 211, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep99[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep100[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2166, 2167, + 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep101[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533, +}; + +static const unsigned short dep102[] = { + 16, 97, 213, 214, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep103[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep104[] = { + 17, 97, 216, 217, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep105[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep106[] = { + 18, 97, 219, 220, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep107[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep108[] = { + 15, 97, 210, 211, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep109[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep110[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 22646, 22647, + 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, + 22836, +}; + +static const unsigned short dep111[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 4135, 16528, + 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep112[] = { + 16, 97, 213, 214, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep113[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep114[] = { + 17, 97, 216, 217, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep115[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep116[] = { + 18, 97, 219, 220, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep117[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep118[] = { + 97, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, + 2352, 2355, 2356, +}; + +static const unsigned short dep119[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533, + +}; + +static const unsigned short dep120[] = { + 97, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep121[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, + 22836, +}; + +static const unsigned short dep122[] = { + 19, 20, 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, + 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, + +}; + +static const unsigned short dep123[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep124[] = { + 97, 282, 2083, 2084, 2286, 2287, +}; + +static const unsigned short dep125[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2285, 2287, 4135, 20616, +}; + +static const unsigned short dep126[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2082, 2084, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep127[] = { + 97, 282, 14455, 14457, 14458, 14460, 14461, 14463, 14635, 14636, 14639, 14640, + 14643, 14644, +}; + +static const unsigned short dep128[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 4135, 14635, 14636, + 14639, 14640, 14643, 14644, 20616, 24694, 24695, 24698, 24701, +}; + +static const unsigned short dep129[] = { + 97, 122, 124, 125, 127, 282, 303, 304, 307, 308, +}; + +static const unsigned short dep130[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 303, 304, 307, 308, 4135, 24694, 24695, + 24698, 24701, +}; + +static const unsigned short dep131[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep132[] = { + 40, 41, 97, 119, 122, 125, 158, 162, 175, 185, 282, 2327, 4135, 20616, 24694, + +}; + +static const unsigned short dep133[] = { + 6, 24, 26, 27, 97, 201, 227, 230, 282, 2081, 2284, +}; + +static const unsigned short dep134[] = { + 40, 41, 97, 158, 162, 175, 185, 201, 227, 229, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2284, 4135, 20616, +}; + +static const unsigned short dep135[] = { + 6, 24, 25, 26, 40, 41, 97, 158, 162, 175, 185, 282, 2081, 2166, 2167, 2170, + 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep136[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2347, 2348, + 2351, 2352, 2355, 2356, 4135, +}; + +static const unsigned short dep137[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep138[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2348, + 2349, 2352, 2353, 2356, 4135, +}; + +static const unsigned short dep139[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2346, 2347, + 2350, 2351, 2354, 2355, 4135, +}; + +static const unsigned short dep140[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2346, + 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 4135, +}; + +static const unsigned short dep141[] = { + 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, 2170, 2173, + 4135, +}; + +static const unsigned short dep142[] = { + 0, 97, 195, 282, +}; + +static const unsigned short dep143[] = { + 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 195, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep144[] = { + 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep145[] = { + 2, 28, 97, 197, 231, 282, 28866, 29018, +}; + +static const unsigned short dep146[] = { + 1, 2, 28, 29, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 28866, 29018, + +}; + +static const unsigned short dep147[] = { + 1, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 4135, + 28866, 29018, +}; + +static const unsigned short dep148[] = { + 0, 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep149[] = { + 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 202, 203, 204, 205, 206, 207, + 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, 231, + 232, 233, 234, 282, 2071, 2081, 2274, 2284, 28866, 29018, +}; + +static const unsigned short dep150[] = { + 29, 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 196, 197, 198, 199, + 200, 202, 203, 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, + 220, 221, 222, 223, 224, 225, 231, 232, 233, 234, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2274, 2284, 4135, 20616, 28866, 29018, +}; + +static const unsigned short dep151[] = { + 97, 282, 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, + 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep152[] = { + 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 2166, 2167, 2170, 2173, 4135, + 14645, 14646, 14666, 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep153[] = { + 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, 14667, + 14669, 14670, 14679, +}; + +static const unsigned short dep154[] = { + 183, 184, 14645, 14646, 14666, 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep155[] = { + 97, 282, 14465, 14466, 14469, 14470, 14480, 14481, 14483, 14484, 14486, 14487, + 14489, 14490, 14493, 14495, 14496, 14505, 14506, 14507, 14508, 14510, 14515, + 14516, 14518, 14519, 14525, 14645, 14646, 14652, 14653, 14654, 14655, 14657, + 14659, 14666, 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, +}; + +static const unsigned short dep156[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170, + 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666, + 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, 34888, +}; + +static const unsigned short dep157[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170, + 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666, + 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, +}; + +static const unsigned short dep158[] = { + 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, 181, 185, 190, 191, + 282, 2071, 2081, 2166, 2167, 2170, 2173, 2327, 4135, 20616, 28866, +}; + +static const unsigned short dep159[] = { + 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, + 64, 65, 67, 69, 70, 71, 72, 73, 94, 96, 97, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, 263, 264, 265, 281, + 282, 2116, 2310, +}; + +static const unsigned short dep160[] = { + 40, 41, 96, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, + 263, 264, 265, 281, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2310, 4135, + 20616, +}; + +static const unsigned short dep161[] = { + 59, 95, 97, 254, 281, 282, 2140, 2327, +}; + +static const unsigned short dep162[] = { + 40, 41, 43, 44, 46, 48, 49, 51, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 66, + 67, 69, 70, 71, 94, 95, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, + 254, 281, 282, 2107, 2116, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep163[] = { + 2, 28, 41, 97, 197, 231, 241, 282, 2140, 2327, 28866, 29018, +}; + +static const unsigned short dep164[] = { + 2, 25, 26, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, + 241, 282, 2327, 4135, 20616, 28866, 29018, +}; + +static const unsigned short dep165[] = { + 97, 129, 130, 133, 134, 140, 141, 144, 145, 147, 148, 150, 151, 153, 154, + 157, 159, 160, 165, 166, 169, 170, 171, 172, 174, 176, 177, 179, 180, 182, + 183, 186, 187, 189, 282, 309, 310, 314, 316, 317, 318, 319, 321, 323, 327, + 330, 331, 333, 334, 335, 336, 338, 339, 340, 342, 343, +}; + +static const unsigned short dep166[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 309, 310, 314, 316, + 317, 318, 319, 321, 323, 327, 330, 331, 333, 334, 335, 336, 338, 339, 340, + 342, 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, 34888, +}; + +static const unsigned short dep167[] = { + 97, 128, 130, 132, 134, 169, 170, 189, 282, 309, 310, 330, 331, 333, 334, + 343, +}; + +static const unsigned short dep168[] = { + 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 309, 310, 330, 331, 333, 334, + 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep169[] = { + 40, 41, 97, 130, 131, 134, 135, 137, 138, 141, 142, 145, 146, 148, 149, 151, + 152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 167, 168, 169, 170, 172, + 173, 174, 175, 176, 178, 179, 181, 182, 184, 185, 187, 188, 189, 190, 191, + 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep170[] = { + 40, 41, 97, 130, 131, 134, 135, 158, 162, 169, 170, 175, 185, 189, 282, 2166, + 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep171[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 153, 155, 158, 162, 171, 173, + 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep172[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 139, 140, 142, 143, 153, 155, + 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep173[] = { + 77, 78, 97, 101, 102, 269, 270, 282, 284, 285, +}; + +static const unsigned short dep174[] = { + 40, 41, 47, 62, 78, 80, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, 175, + 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep175[] = { + 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep176[] = { + 97, 282, 12480, 12481, 12633, +}; + +static const unsigned short dep177[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 12633, 20616, +}; + +static const unsigned short dep178[] = { + 97, 282, 6219, 6220, 6411, +}; + +static const unsigned short dep179[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6411, 20616, +}; + +static const unsigned short dep180[] = { + 97, 282, 6237, 6424, +}; + +static const unsigned short dep181[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6424, 20616, +}; + +static const unsigned short dep182[] = { + 97, 282, 6255, 6256, 6257, 6258, 6435, 6437, 8484, +}; + +static const unsigned short dep183[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6258, 6436, 6437, 8304, 8483, 20616, +}; + +static const unsigned short dep184[] = { + 97, 282, 6259, 6260, 6438, +}; + +static const unsigned short dep185[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6438, 20616, +}; + +static const unsigned short dep186[] = { + 97, 282, 6261, 6439, +}; + +static const unsigned short dep187[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6439, 20616, +}; + +static const unsigned short dep188[] = { + 97, 282, 10350, 10530, +}; + +static const unsigned short dep189[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 10530, 20616, +}; + +static const unsigned short dep190[] = { + 77, 78, 82, 83, 97, 101, 102, 269, 270, 272, 273, 282, 284, 285, +}; + +static const unsigned short dep191[] = { + 40, 41, 47, 62, 78, 80, 83, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 272, 274, 282, 284, 285, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep192[] = { + 77, 78, 97, 101, 102, 104, 105, 269, 270, 282, 284, 285, 286, 287, +}; + +static const unsigned short dep193[] = { + 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 286, 287, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep194[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 12481, 20616, +}; + +static const unsigned short dep195[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6219, 20616, +}; + +static const unsigned short dep196[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6237, 20616, +}; + +static const unsigned short dep197[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6257, 8303, 20616, +}; + +static const unsigned short dep198[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6259, 20616, +}; + +static const unsigned short dep199[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 183, 184, 185, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6260, 6261, 20616, +}; + +static const unsigned short dep200[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2327, 4135, 10350, 20616, +}; + +static const unsigned short dep201[] = { + 40, 41, 97, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 2327, 4135, 6186, 20616, +}; + +static const unsigned short dep202[] = { + 77, 79, 80, 97, 98, 99, 100, 268, 269, 282, 283, 284, +}; + +static const unsigned short dep203[] = { + 40, 41, 78, 79, 83, 85, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, + 190, 191, 192, 268, 270, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep204[] = { + 77, 79, 80, 81, 97, 98, 99, 100, 103, 268, 269, 271, 282, 283, 284, +}; + +static const unsigned short dep205[] = { + 40, 41, 78, 79, 81, 83, 85, 97, 100, 102, 103, 104, 107, 137, 138, 158, 162, + 175, 185, 190, 191, 192, 268, 270, 271, 282, 283, 285, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep206[] = { + 77, 79, 80, 84, 85, 86, 97, 98, 99, 100, 268, 269, 274, 275, 282, 283, 284, + +}; + +static const unsigned short dep207[] = { + 40, 41, 78, 79, 83, 85, 97, 100, 102, 137, 138, 158, 162, 175, 185, 190, 191, + 192, 268, 270, 273, 275, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep208[] = { + 77, 79, 80, 97, 98, 99, 100, 106, 107, 108, 268, 269, 282, 283, 284, 287, + 288, +}; + +static const unsigned short dep209[] = { + 40, 41, 78, 79, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, 190, + 191, 192, 268, 270, 282, 283, 285, 286, 288, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep210[] = { + 40, 41, 46, 70, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep211[] = { + 40, 41, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep212[] = { + 40, 41, 70, 77, 82, 84, 97, 137, 138, 153, 155, 158, 162, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep213[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2135, 2136, 2137, 2138, + 2139, 2140, 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 20616, + +}; + +static const unsigned short dep214[] = { + 40, 41, 70, 77, 82, 84, 97, 153, 155, 158, 162, 175, 185, 192, 282, 2138, + 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep215[] = { + 40, 41, 78, 79, 97, 100, 137, 138, 158, 162, 175, 185, 190, 191, 268, 270, + 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep216[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep217[] = { + 5, 97, 200, 282, 2140, 2327, +}; + +static const unsigned short dep218[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 200, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep219[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep220[] = { + 0, 97, 195, 282, 2140, 2327, +}; + +static const unsigned short dep221[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep222[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep223[] = { + 31, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep224[] = { + 0, 97, 195, 282, 2327, 26715, +}; + +static const unsigned short dep225[] = { + 0, 97, 109, 195, 282, 289, +}; + +static const unsigned short dep226[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep227[] = { + 0, 5, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep228[] = { + 0, 31, 97, 109, 195, 234, 282, 289, +}; + +static const unsigned short dep229[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep230[] = { + 0, 97, 109, 195, 282, 289, 2140, 2327, +}; + +static const unsigned short dep231[] = { + 0, 3, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep232[] = { + 0, 3, 5, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep233[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep234[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2327, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep235[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep236[] = { + 0, 31, 97, 109, 195, 234, 282, 289, 2140, 2327, +}; + +static const unsigned short dep237[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep238[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, + 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep239[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep240[] = { + 0, 97, 195, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep241[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep242[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep243[] = { + 0, 97, 195, 282, 2137, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep244[] = { + 97, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep245[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, 4135, 16528, + 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep246[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep247[] = { + 0, 97, 195, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, + 18765, +}; + +static const unsigned short dep248[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep249[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, + 2327, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, + +}; + +static const unsigned short dep250[] = { + 0, 97, 195, 282, 2137, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, + 18765, +}; + +static const unsigned short dep251[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep252[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156, + 158, 162, 171, 173, 175, 185, 192, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep253[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156, + 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep254[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, + +}; + +static const unsigned short dep255[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 2327, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep256[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 26, 27, 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, + 223, 224, 225, 227, 230, 231, 232, 233, 234, 282, 2071, 2081, 2140, 2274, + 2284, 2327, 28866, 29018, +}; + +static const unsigned short dep257[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 25, 26, 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, + 181, 185, 190, 191, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, + 227, 229, 231, 232, 233, 234, 282, 2071, 2081, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 2274, 2284, 2327, 4135, 20616, 28866, 29018, +}; + +#define NELS(X) (sizeof(X)/sizeof(X[0])) +static const struct ia64_opcode_dependency +op_dependencies[] = { + { NELS(dep1), dep1, NELS(dep0), dep0, }, + { NELS(dep3), dep3, NELS(dep2), dep2, }, + { NELS(dep5), dep5, NELS(dep4), dep4, }, + { NELS(dep7), dep7, NELS(dep6), dep6, }, + { NELS(dep9), dep9, NELS(dep8), dep8, }, + { NELS(dep11), dep11, NELS(dep10), dep10, }, + { NELS(dep13), dep13, NELS(dep12), dep12, }, + { NELS(dep15), dep15, NELS(dep14), dep14, }, + { NELS(dep17), dep17, NELS(dep16), dep16, }, + { NELS(dep19), dep19, NELS(dep18), dep18, }, + { NELS(dep21), dep21, NELS(dep20), dep20, }, + { NELS(dep23), dep23, NELS(dep22), dep22, }, + { NELS(dep25), dep25, NELS(dep24), dep24, }, + { NELS(dep27), dep27, NELS(dep26), dep26, }, + { NELS(dep29), dep29, NELS(dep28), dep28, }, + { NELS(dep30), dep30, NELS(dep12), dep12, }, + { NELS(dep32), dep32, NELS(dep31), dep31, }, + { NELS(dep34), dep34, NELS(dep33), dep33, }, + { NELS(dep35), dep35, NELS(dep12), dep12, }, + { NELS(dep37), dep37, NELS(dep36), dep36, }, + { NELS(dep39), dep39, NELS(dep38), dep38, }, + { NELS(dep41), dep41, NELS(dep40), dep40, }, + { NELS(dep42), dep42, NELS(dep31), dep31, }, + { NELS(dep43), dep43, NELS(dep33), dep33, }, + { NELS(dep45), dep45, NELS(dep44), dep44, }, + { NELS(dep47), dep47, NELS(dep46), dep46, }, + { NELS(dep49), dep49, NELS(dep48), dep48, }, + { NELS(dep51), dep51, NELS(dep50), dep50, }, + { NELS(dep53), dep53, NELS(dep52), dep52, }, + { NELS(dep55), dep55, NELS(dep54), dep54, }, + { NELS(dep57), dep57, NELS(dep56), dep56, }, + { NELS(dep59), dep59, NELS(dep58), dep58, }, + { NELS(dep61), dep61, NELS(dep60), dep60, }, + { NELS(dep63), dep63, NELS(dep62), dep62, }, + { NELS(dep65), dep65, NELS(dep64), dep64, }, + { NELS(dep67), dep67, NELS(dep66), dep66, }, + { NELS(dep68), dep68, NELS(dep33), dep33, }, + { NELS(dep70), dep70, NELS(dep69), dep69, }, + { NELS(dep72), dep72, NELS(dep71), dep71, }, + { NELS(dep74), dep74, NELS(dep73), dep73, }, + { NELS(dep76), dep76, NELS(dep75), dep75, }, + { NELS(dep77), dep77, NELS(dep33), dep33, }, + { NELS(dep79), dep79, NELS(dep78), dep78, }, + { NELS(dep81), dep81, NELS(dep80), dep80, }, + { NELS(dep83), dep83, NELS(dep82), dep82, }, + { NELS(dep84), dep84, NELS(dep33), dep33, }, + { NELS(dep85), dep85, NELS(dep33), dep33, }, + { NELS(dep86), dep86, NELS(dep33), dep33, }, + { NELS(dep87), dep87, NELS(dep33), dep33, }, + { NELS(dep89), dep89, NELS(dep88), dep88, }, + { NELS(dep91), dep91, NELS(dep90), dep90, }, + { NELS(dep93), dep93, NELS(dep92), dep92, }, + { NELS(dep95), dep95, NELS(dep94), dep94, }, + { NELS(dep97), dep97, NELS(dep96), dep96, }, + { NELS(dep99), dep99, NELS(dep98), dep98, }, + { NELS(dep101), dep101, NELS(dep100), dep100, }, + { NELS(dep103), dep103, NELS(dep102), dep102, }, + { NELS(dep105), dep105, NELS(dep104), dep104, }, + { NELS(dep107), dep107, NELS(dep106), dep106, }, + { NELS(dep109), dep109, NELS(dep108), dep108, }, + { NELS(dep111), dep111, NELS(dep110), dep110, }, + { NELS(dep113), dep113, NELS(dep112), dep112, }, + { NELS(dep115), dep115, NELS(dep114), dep114, }, + { NELS(dep117), dep117, NELS(dep116), dep116, }, + { NELS(dep119), dep119, NELS(dep118), dep118, }, + { NELS(dep121), dep121, NELS(dep120), dep120, }, + { NELS(dep122), dep122, NELS(dep64), dep64, }, + { NELS(dep123), dep123, NELS(dep33), dep33, }, + { NELS(dep125), dep125, NELS(dep124), dep124, }, + { NELS(dep126), dep126, NELS(dep0), dep0, }, + { NELS(dep128), dep128, NELS(dep127), dep127, }, + { NELS(dep130), dep130, NELS(dep129), dep129, }, + { NELS(dep131), dep131, NELS(dep0), dep0, }, + { NELS(dep132), dep132, NELS(dep0), dep0, }, + { NELS(dep134), dep134, NELS(dep133), dep133, }, + { NELS(dep135), dep135, NELS(dep0), dep0, }, + { NELS(dep136), dep136, NELS(dep2), dep2, }, + { NELS(dep137), dep137, NELS(dep4), dep4, }, + { NELS(dep138), dep138, NELS(dep6), dep6, }, + { NELS(dep139), dep139, NELS(dep8), dep8, }, + { NELS(dep140), dep140, NELS(dep10), dep10, }, + { NELS(dep141), dep141, NELS(dep33), dep33, }, + { NELS(dep143), dep143, NELS(dep142), dep142, }, + { NELS(dep144), dep144, NELS(dep142), dep142, }, + { NELS(dep146), dep146, NELS(dep145), dep145, }, + { NELS(dep147), dep147, NELS(dep145), dep145, }, + { NELS(dep148), dep148, NELS(dep142), dep142, }, + { NELS(dep150), dep150, NELS(dep149), dep149, }, + { NELS(dep152), dep152, NELS(dep151), dep151, }, + { NELS(dep154), dep154, NELS(dep153), dep153, }, + { NELS(dep156), dep156, NELS(dep155), dep155, }, + { NELS(dep157), dep157, NELS(dep155), dep155, }, + { NELS(dep158), dep158, NELS(dep0), dep0, }, + { NELS(dep160), dep160, NELS(dep159), dep159, }, + { NELS(dep162), dep162, NELS(dep161), dep161, }, + { NELS(dep164), dep164, NELS(dep163), dep163, }, + { NELS(dep166), dep166, NELS(dep165), dep165, }, + { NELS(dep168), dep168, NELS(dep167), dep167, }, + { NELS(dep169), dep169, NELS(dep0), dep0, }, + { NELS(dep170), dep170, NELS(dep0), dep0, }, + { NELS(dep171), dep171, NELS(dep0), dep0, }, + { NELS(dep172), dep172, NELS(dep33), dep33, }, + { NELS(dep174), dep174, NELS(dep173), dep173, }, + { NELS(dep175), dep175, NELS(dep173), dep173, }, + { NELS(dep177), dep177, NELS(dep176), dep176, }, + { NELS(dep179), dep179, NELS(dep178), dep178, }, + { NELS(dep181), dep181, NELS(dep180), dep180, }, + { NELS(dep183), dep183, NELS(dep182), dep182, }, + { NELS(dep185), dep185, NELS(dep184), dep184, }, + { NELS(dep187), dep187, NELS(dep186), dep186, }, + { NELS(dep189), dep189, NELS(dep188), dep188, }, + { NELS(dep191), dep191, NELS(dep190), dep190, }, + { NELS(dep193), dep193, NELS(dep192), dep192, }, + { NELS(dep194), dep194, NELS(dep0), dep0, }, + { NELS(dep195), dep195, NELS(dep0), dep0, }, + { NELS(dep196), dep196, NELS(dep0), dep0, }, + { NELS(dep197), dep197, NELS(dep0), dep0, }, + { NELS(dep198), dep198, NELS(dep0), dep0, }, + { NELS(dep199), dep199, NELS(dep0), dep0, }, + { NELS(dep200), dep200, NELS(dep0), dep0, }, + { NELS(dep201), dep201, NELS(dep0), dep0, }, + { NELS(dep203), dep203, NELS(dep202), dep202, }, + { NELS(dep205), dep205, NELS(dep204), dep204, }, + { NELS(dep207), dep207, NELS(dep206), dep206, }, + { NELS(dep209), dep209, NELS(dep208), dep208, }, + { NELS(dep210), dep210, NELS(dep0), dep0, }, + { NELS(dep211), dep211, NELS(dep0), dep0, }, + { NELS(dep212), dep212, NELS(dep0), dep0, }, + { NELS(dep213), dep213, NELS(dep33), dep33, }, + { NELS(dep214), dep214, NELS(dep33), dep33, }, + { NELS(dep215), dep215, NELS(dep202), dep202, }, + { NELS(dep216), dep216, NELS(dep0), dep0, }, + { NELS(dep218), dep218, NELS(dep217), dep217, }, + { NELS(dep219), dep219, NELS(dep0), dep0, }, + { NELS(dep221), dep221, NELS(dep220), dep220, }, + { NELS(dep222), dep222, NELS(dep220), dep220, }, + { NELS(dep223), dep223, NELS(dep0), dep0, }, + { NELS(dep221), dep221, NELS(dep224), dep224, }, + { NELS(dep226), dep226, NELS(dep225), dep225, }, + { NELS(dep227), dep227, NELS(dep225), dep225, }, + { NELS(dep229), dep229, NELS(dep228), dep228, }, + { NELS(dep231), dep231, NELS(dep230), dep230, }, + { NELS(dep232), dep232, NELS(dep230), dep230, }, + { NELS(dep233), dep233, NELS(dep230), dep230, }, + { NELS(dep234), dep234, NELS(dep0), dep0, }, + { NELS(dep235), dep235, NELS(dep230), dep230, }, + { NELS(dep237), dep237, NELS(dep236), dep236, }, + { NELS(dep238), dep238, NELS(dep64), dep64, }, + { NELS(dep239), dep239, NELS(dep64), dep64, }, + { NELS(dep241), dep241, NELS(dep240), dep240, }, + { NELS(dep242), dep242, NELS(dep240), dep240, }, + { NELS(dep241), dep241, NELS(dep243), dep243, }, + { NELS(dep245), dep245, NELS(dep244), dep244, }, + { NELS(dep246), dep246, NELS(dep244), dep244, }, + { NELS(dep248), dep248, NELS(dep247), dep247, }, + { NELS(dep249), dep249, NELS(dep247), dep247, }, + { NELS(dep248), dep248, NELS(dep250), dep250, }, + { NELS(dep251), dep251, NELS(dep225), dep225, }, + { NELS(dep252), dep252, NELS(dep33), dep33, }, + { NELS(dep253), dep253, NELS(dep0), dep0, }, + { NELS(dep254), dep254, NELS(dep64), dep64, }, + { NELS(dep255), dep255, NELS(dep230), dep230, }, + { 0, NULL, 0, NULL, }, + { NELS(dep257), dep257, NELS(dep256), dep256, }, +}; + +static const struct ia64_completer_table +completer_table[] = { + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 95 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 95 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 594, -1, 0, 1, 6 }, + { 0x0, 0x0, 0, 657, -1, 0, 1, 18 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, 756, -1, 0, 1, 18 }, + { 0x0, 0x0, 0, 2198, -1, 0, 1, 10 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 9 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 13 }, + { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 2406, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 1140, -1, 0, 1, 129 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 45 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 84 }, + { 0x0, 0x0, 0, 2246, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2473, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2250, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 2252, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2482, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2485, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2507, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2510, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 36 }, + { 0x0, 0x0, 0, 2518, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 1409, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 83 }, + { 0x0, 0x0, 0, 1457, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1466, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1475, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1477, -1, 0, 1, 132 }, + { 0x0, 0x0, 0, 1479, -1, 0, 1, 132 }, + { 0x0, 0x0, 0, 1488, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1497, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1506, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1515, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1524, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1533, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1543, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1553, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1563, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1572, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1578, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1584, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1590, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1596, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1602, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1608, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1614, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1620, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1626, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1632, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1638, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1644, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1650, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1656, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1662, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1668, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1674, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1678, -1, 0, 1, 158 }, + { 0x0, 0x0, 0, 1682, -1, 0, 1, 159 }, + { 0x0, 0x0, 0, 1686, -1, 0, 1, 159 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 85 }, + { 0x0, 0x0, 0, 258, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 68 }, + { 0x1, 0x1, 0, 1166, -1, 20, 1, 68 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 69 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 71 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 73 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 93 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 94 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 96 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 97 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 98 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 99 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 104 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 105 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 106 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 107 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 108 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 109 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 110 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 113 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 114 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 115 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 116 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 117 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 118 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 119 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 120 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2858, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2859, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2210, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2211, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2873, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2874, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2875, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2876, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2877, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2860, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2861, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 11 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 91 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 89 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, + { 0x0, 0x0, 0, 2879, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 90 }, + { 0x0, 0x0, 0, 1966, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1968, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1970, -1, 0, 1, 139 }, + { 0x0, 0x0, 0, 1972, -1, 0, 1, 139 }, + { 0x0, 0x0, 0, 1974, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1976, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1978, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1980, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1983, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1986, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1989, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1990, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1991, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1992, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1993, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1994, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1995, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1996, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 88 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 127 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 125 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 127 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 126 }, + { 0x0, 0x0, 0, 1687, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1688, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1689, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1690, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 1, 224, -1, 0, 1, 12 }, + { 0x0, 0x0, 1, 225, -1, 0, 1, 14 }, + { 0x1, 0x1, 2, -1, -1, 27, 1, 12 }, + { 0x1, 0x1, 2, -1, -1, 27, 1, 14 }, + { 0x0, 0x0, 3, -1, 1340, 0, 0, -1 }, + { 0x0, 0x0, 3, -1, 1341, 0, 0, -1 }, + { 0x1, 0x1, 3, 2749, 1450, 33, 1, 134 }, + { 0x1, 0x1, 3, 2750, 1459, 33, 1, 134 }, + { 0x1, 0x1, 3, 2751, 1468, 33, 1, 134 }, + { 0x1, 0x1, 3, 2752, 1481, 33, 1, 134 }, + { 0x1, 0x1, 3, 2753, 1490, 33, 1, 134 }, + { 0x1, 0x1, 3, 2754, 1499, 33, 1, 134 }, + { 0x1, 0x1, 3, 2755, 1508, 33, 1, 134 }, + { 0x1, 0x1, 3, 2756, 1517, 33, 1, 134 }, + { 0x1, 0x1, 3, 2757, 1526, 33, 1, 134 }, + { 0x1, 0x1, 3, 2758, 1535, 33, 1, 134 }, + { 0x1, 0x1, 3, 2759, 1545, 33, 1, 134 }, + { 0x1, 0x1, 3, 2760, 1555, 33, 1, 134 }, + { 0x1, 0x1, 3, 2761, 1568, 33, 1, 149 }, + { 0x1, 0x1, 3, 2762, 1574, 33, 1, 154 }, + { 0x1, 0x1, 3, 2763, 1580, 33, 1, 154 }, + { 0x1, 0x1, 3, 2764, 1586, 33, 1, 149 }, + { 0x1, 0x1, 3, 2765, 1592, 33, 1, 154 }, + { 0x1, 0x1, 3, 2766, 1598, 33, 1, 154 }, + { 0x1, 0x1, 3, 2767, 1604, 33, 1, 149 }, + { 0x1, 0x1, 3, 2768, 1610, 33, 1, 154 }, + { 0x1, 0x1, 3, 2769, 1616, 33, 1, 154 }, + { 0x1, 0x1, 3, 2770, 1622, 33, 1, 149 }, + { 0x1, 0x1, 3, 2771, 1628, 33, 1, 154 }, + { 0x1, 0x1, 3, 2772, 1634, 33, 1, 149 }, + { 0x1, 0x1, 3, 2773, 1640, 33, 1, 154 }, + { 0x1, 0x1, 3, 2774, 1646, 33, 1, 149 }, + { 0x1, 0x1, 3, 2775, 1652, 33, 1, 154 }, + { 0x1, 0x1, 3, 2776, 1658, 33, 1, 149 }, + { 0x1, 0x1, 3, 2777, 1664, 33, 1, 154 }, + { 0x1, 0x1, 3, 2778, 1670, 33, 1, 154 }, + { 0x1, 0x1, 3, -1, -1, 27, 1, 41 }, + { 0x0, 0x0, 4, 2212, 1425, 0, 1, 142 }, + { 0x0, 0x0, 4, 2213, 1427, 0, 1, 142 }, + { 0x0, 0x0, 4, 2214, 1429, 0, 1, 141 }, + { 0x0, 0x0, 4, 2215, 1431, 0, 1, 141 }, + { 0x0, 0x0, 4, 2216, 1433, 0, 1, 141 }, + { 0x0, 0x0, 4, 2217, 1435, 0, 1, 141 }, + { 0x0, 0x0, 4, 2218, 1437, 0, 1, 141 }, + { 0x0, 0x0, 4, 2219, 1439, 0, 1, 141 }, + { 0x0, 0x0, 4, 2220, 1441, 0, 1, 141 }, + { 0x0, 0x0, 4, 2221, 1443, 0, 1, 141 }, + { 0x0, 0x0, 4, 2222, 1445, 0, 1, 143 }, + { 0x0, 0x0, 4, 2223, 1447, 0, 1, 143 }, + { 0x1, 0x1, 4, -1, 1454, 33, 1, 137 }, + { 0x5, 0x5, 4, 552, 1453, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1463, 33, 1, 137 }, + { 0x5, 0x5, 4, 553, 1462, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1472, 33, 1, 137 }, + { 0x5, 0x5, 4, 554, 1471, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1476, 32, 1, 132 }, + { 0x1, 0x1, 4, -1, 1478, 32, 1, 132 }, + { 0x1, 0x1, 4, -1, 1485, 33, 1, 137 }, + { 0x5, 0x5, 4, 555, 1484, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1494, 33, 1, 137 }, + { 0x5, 0x5, 4, 556, 1493, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1503, 33, 1, 137 }, + { 0x5, 0x5, 4, 557, 1502, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1512, 33, 1, 137 }, + { 0x5, 0x5, 4, 558, 1511, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1521, 33, 1, 137 }, + { 0x5, 0x5, 4, 559, 1520, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1530, 33, 1, 137 }, + { 0x5, 0x5, 4, 560, 1529, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1540, 33, 1, 137 }, + { 0x5, 0x5, 4, 1036, 1538, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1550, 33, 1, 137 }, + { 0x5, 0x5, 4, 1037, 1548, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1560, 33, 1, 137 }, + { 0x5, 0x5, 4, 1038, 1558, 32, 1, 131 }, + { 0x1, 0x21, 10, 2013, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2014, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 420, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2074, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2075, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2076, 0, 0, -1 }, + { 0x0, 0x0, 10, 2017, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2018, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2019, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2020, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 430, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2080, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 434, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2082, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 438, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2084, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 442, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2086, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 446, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2088, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 450, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2090, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 2033, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2034, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 460, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2096, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2097, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2098, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2101, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2102, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2103, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2104, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2105, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2106, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2107, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2108, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2109, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2110, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2111, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2112, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2113, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2114, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2115, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2116, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2117, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2118, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2119, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2120, 0, 0, -1 }, + { 0x1, 0x21, 10, 2037, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2038, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 468, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2122, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2123, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2124, 0, 0, -1 }, + { 0x0, 0x0, 10, 2041, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2042, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2043, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2044, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 478, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2128, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 482, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2130, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 486, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2132, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 490, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2134, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 494, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2136, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 498, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2138, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 2057, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2058, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 508, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2144, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2145, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2146, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2149, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2150, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2151, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2152, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2153, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2154, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2155, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2156, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2157, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2158, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2159, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2160, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2161, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2162, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2163, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2164, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2165, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2166, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2167, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2168, 0, 0, -1 }, + { 0x1, 0x1, 10, 2061, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2062, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2063, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2064, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2169, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2171, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2173, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2175, 0, 0, -1 }, + { 0x1, 0x1, 10, 2065, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 10, 2066, -1, 12, 1, 78 }, + { 0x1, 0x1, 10, 2067, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 10, 2068, -1, 12, 1, 78 }, + { 0x0, 0x0, 10, -1, 2177, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2179, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2181, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2183, 0, 0, -1 }, + { 0x1, 0x1, 10, 2069, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2070, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2071, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2072, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2185, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2187, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2189, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2191, 0, 0, -1 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2015, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 300, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2077, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2078, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2021, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 308, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2023, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 310, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2025, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 312, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2027, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 314, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2029, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 316, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2031, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 318, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2091, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2092, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2093, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 11, 2094, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2035, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 322, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2099, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2100, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2039, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 348, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2125, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2126, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2045, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 356, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2047, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 358, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2049, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 360, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2051, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 362, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2053, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 364, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2055, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 366, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2139, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2140, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2141, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 11, 2142, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2059, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 370, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2147, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2148, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, 2170, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2172, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2174, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2176, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, 2178, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 11, 2180, -1, 12, 1, 78 }, + { 0x1, 0x1, 11, 2182, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 11, 2184, -1, 12, 1, 78 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, 2186, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2188, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2190, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2192, -1, 12, 1, 3 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x1, 0x1, 13, 272, 1452, 34, 1, 131 }, + { 0x1, 0x1, 13, 274, 1461, 34, 1, 131 }, + { 0x1, 0x1, 13, 276, 1470, 34, 1, 131 }, + { 0x1, 0x1, 13, 280, 1483, 34, 1, 131 }, + { 0x1, 0x1, 13, 282, 1492, 34, 1, 131 }, + { 0x1, 0x1, 13, 284, 1501, 34, 1, 131 }, + { 0x1, 0x1, 13, 286, 1510, 34, 1, 131 }, + { 0x1, 0x1, 13, 288, 1519, 34, 1, 131 }, + { 0x1, 0x1, 13, 290, 1528, 34, 1, 131 }, + { 0x1, 0x1, 13, 292, 1537, 34, 1, 131 }, + { 0x1, 0x1, 13, 294, 1547, 34, 1, 131 }, + { 0x1, 0x1, 13, 296, 1557, 34, 1, 131 }, + { 0x0, 0x0, 19, -1, 795, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 796, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 797, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 798, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 799, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 800, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 801, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 802, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 803, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 804, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 805, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 806, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 807, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 808, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 809, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 810, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 811, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 812, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 813, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 814, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 815, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 816, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 817, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 818, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 819, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 820, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 821, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 822, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 823, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 824, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2827, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2828, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2843, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2844, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2849, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2850, 0, 0, -1 }, + { 0x0, 0x0, 21, 831, 2839, 0, 0, -1 }, + { 0x0, 0x0, 21, 832, 2841, 0, 0, -1 }, + { 0x0, 0x0, 23, -1, 2837, 0, 0, -1 }, + { 0x0, 0x0, 23, -1, 2838, 0, 0, -1 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, 1272, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, 1293, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, 1326, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 33, 1, 82 }, + { 0x1, 0x1, 24, -1, -1, 33, 1, 82 }, + { 0x1, 0x1, 24, 1342, 1455, 35, 1, 137 }, + { 0x1, 0x1, 24, 1343, 1464, 35, 1, 137 }, + { 0x1, 0x1, 24, 1344, 1473, 35, 1, 137 }, + { 0x1, 0x1, 24, 1345, 1486, 35, 1, 137 }, + { 0x1, 0x1, 24, 1346, 1495, 35, 1, 137 }, + { 0x1, 0x1, 24, 1347, 1504, 35, 1, 137 }, + { 0x1, 0x1, 24, 1348, 1513, 35, 1, 137 }, + { 0x1, 0x1, 24, 1349, 1522, 35, 1, 137 }, + { 0x1, 0x1, 24, 1350, 1531, 35, 1, 137 }, + { 0x1, 0x1, 24, 1351, 1541, 35, 1, 137 }, + { 0x1, 0x1, 24, 1352, 1551, 35, 1, 137 }, + { 0x1, 0x1, 24, 1353, 1561, 35, 1, 137 }, + { 0x1, 0x1, 24, 1354, 1570, 35, 1, 151 }, + { 0x1, 0x1, 24, 1355, 1576, 35, 1, 156 }, + { 0x1, 0x1, 24, 1356, 1582, 35, 1, 156 }, + { 0x1, 0x1, 24, 1357, 1588, 35, 1, 151 }, + { 0x1, 0x1, 24, 1358, 1594, 35, 1, 156 }, + { 0x1, 0x1, 24, 1359, 1600, 35, 1, 156 }, + { 0x1, 0x1, 24, 1360, 1606, 35, 1, 151 }, + { 0x1, 0x1, 24, 1361, 1612, 35, 1, 156 }, + { 0x1, 0x1, 24, 1362, 1618, 35, 1, 156 }, + { 0x1, 0x1, 24, 1363, 1624, 35, 1, 151 }, + { 0x1, 0x1, 24, 1364, 1630, 35, 1, 156 }, + { 0x1, 0x1, 24, 1365, 1636, 35, 1, 151 }, + { 0x1, 0x1, 24, 1366, 1642, 35, 1, 156 }, + { 0x1, 0x1, 24, 1367, 1648, 35, 1, 151 }, + { 0x1, 0x1, 24, 1368, 1654, 35, 1, 156 }, + { 0x1, 0x1, 24, 1369, 1660, 35, 1, 151 }, + { 0x1, 0x1, 24, 1370, 1666, 35, 1, 156 }, + { 0x1, 0x1, 24, 1371, 1672, 35, 1, 156 }, + { 0x0, 0x0, 33, 2821, 2819, 0, 0, -1 }, + { 0x0, 0x0, 33, 2824, 2822, 0, 0, -1 }, + { 0x0, 0x0, 33, 2830, 2829, 0, 0, -1 }, + { 0x0, 0x0, 33, 2832, 2831, 0, 0, -1 }, + { 0x0, 0x0, 33, 2846, 2845, 0, 0, -1 }, + { 0x0, 0x0, 33, 2848, 2847, 0, 0, -1 }, + { 0x0, 0x0, 35, -1, 2840, 0, 0, -1 }, + { 0x0, 0x0, 35, -1, 2842, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, 2290, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2349, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2352, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2357, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2360, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2363, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2366, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2369, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2402, 37, 1, 30 }, + { 0x3, 0x3, 38, -1, -1, 30, 1, 144 }, + { 0x0, 0x0, 38, 1142, -1, 0, 1, 102 }, + { 0x0, 0x0, 38, -1, -1, 0, 1, 111 }, + { 0x0, 0x0, 38, 1148, -1, 0, 1, 123 }, + { 0x3, 0x3, 38, -1, -1, 30, 1, 160 }, + { 0x0, 0x0, 38, 1149, -1, 0, 1, 41 }, + { 0x0, 0x0, 40, -1, 973, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 981, 0, 0, -1 }, + { 0x0, 0x0, 40, 1151, 977, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 622, 33, 1, 6 }, + { 0x18000001, 0x18000001, 40, -1, 630, 6, 1, 7 }, + { 0x3, 0x3, 40, 1152, 626, 33, 1, 6 }, + { 0x0, 0x0, 40, -1, 985, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 642, 33, 1, 8 }, + { 0x0, 0x0, 40, -1, 989, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 654, 33, 1, 16 }, + { 0x0, 0x0, 40, -1, 994, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 998, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 677, 33, 1, 18 }, + { 0x3, 0x3, 40, -1, 681, 33, 1, 18 }, + { 0x0, 0x0, 40, -1, 1002, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1006, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 701, 33, 1, 19 }, + { 0x18000001, 0x18000001, 40, -1, 705, 6, 1, 19 }, + { 0x0, 0x0, 40, -1, 1010, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 717, 33, 1, 20 }, + { 0x0, 0x0, 40, -1, 1014, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1018, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 737, 33, 1, 21 }, + { 0x18000001, 0x18000001, 40, -1, 741, 6, 1, 21 }, + { 0x0, 0x0, 40, -1, 1022, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 753, 33, 1, 22 }, + { 0x0, 0x0, 40, -1, 1027, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1031, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 776, 33, 1, 18 }, + { 0x3, 0x3, 40, -1, 780, 33, 1, 18 }, + { 0x0, 0x0, 40, -1, 1035, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 792, 33, 1, 22 }, + { 0x0, 0x0, 41, 851, 972, 0, 0, -1 }, + { 0x0, 0x0, 41, 852, 980, 0, 0, -1 }, + { 0x0, 0x0, 41, 853, 976, 0, 0, -1 }, + { 0x1, 0x1, 41, 854, 621, 34, 1, 6 }, + { 0x10000001, 0x10000001, 41, 855, 629, 6, 1, 7 }, + { 0x1, 0x1, 41, 856, 625, 34, 1, 6 }, + { 0x0, 0x0, 41, 857, 984, 0, 0, -1 }, + { 0x1, 0x1, 41, 858, 641, 34, 1, 8 }, + { 0x0, 0x0, 41, 859, 988, 0, 0, -1 }, + { 0x1, 0x1, 41, 860, 653, 34, 1, 16 }, + { 0x0, 0x0, 41, 861, 993, 0, 0, -1 }, + { 0x0, 0x0, 41, 862, 997, 0, 0, -1 }, + { 0x1, 0x1, 41, 863, 676, 34, 1, 18 }, + { 0x1, 0x1, 41, 864, 680, 34, 1, 18 }, + { 0x0, 0x0, 41, 865, 1001, 0, 0, -1 }, + { 0x0, 0x0, 41, 866, 1005, 0, 0, -1 }, + { 0x1, 0x1, 41, 867, 700, 34, 1, 19 }, + { 0x10000001, 0x10000001, 41, 868, 704, 6, 1, 19 }, + { 0x0, 0x0, 41, 869, 1009, 0, 0, -1 }, + { 0x1, 0x1, 41, 870, 716, 34, 1, 20 }, + { 0x0, 0x0, 41, 871, 1013, 0, 0, -1 }, + { 0x0, 0x0, 41, 872, 1017, 0, 0, -1 }, + { 0x1, 0x1, 41, 873, 736, 34, 1, 21 }, + { 0x10000001, 0x10000001, 41, 874, 740, 6, 1, 21 }, + { 0x0, 0x0, 41, 875, 1021, 0, 0, -1 }, + { 0x1, 0x1, 41, 876, 752, 34, 1, 22 }, + { 0x0, 0x0, 41, 877, 1026, 0, 0, -1 }, + { 0x0, 0x0, 41, 878, 1030, 0, 0, -1 }, + { 0x1, 0x1, 41, 879, 775, 34, 1, 18 }, + { 0x1, 0x1, 41, 880, 779, 34, 1, 18 }, + { 0x0, 0x0, 41, 881, 1034, 0, 0, -1 }, + { 0x1, 0x1, 41, 882, 791, 34, 1, 22 }, + { 0x800001, 0x800001, 41, -1, 1156, 4, 1, 17 }, + { 0x1, 0x1, 41, 2236, 1154, 4, 1, 17 }, + { 0x1, 0x1, 41, 957, 1159, 4, 1, 23 }, + { 0x2, 0x3, 41, -1, 1164, 20, 1, 68 }, + { 0x1, 0x1, 41, 2237, 1162, 21, 1, 68 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 86 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 86 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 130 }, + { 0x1, 0x1, 44, 1372, 297, 38, 1, 1 }, + { 0x1, 0x1, 44, 1373, 299, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 302, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 424, 0, 0, -1 }, + { 0x1, 0x1, 44, 1377, 319, 38, 1, 1 }, + { 0x1, 0x1, 44, 1378, 321, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 324, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 464, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 326, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 344, 0, 0, -1 }, + { 0x1, 0x1, 44, 1384, 345, 38, 1, 1 }, + { 0x1, 0x1, 44, 1385, 347, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 350, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 472, 0, 0, -1 }, + { 0x1, 0x1, 44, 1389, 367, 38, 1, 1 }, + { 0x1, 0x1, 44, 1390, 369, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 372, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 512, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 374, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 392, 0, 0, -1 }, + { 0x0, 0x0, 44, 1248, 2297, 0, 0, -1 }, + { 0x0, 0x0, 44, 1249, 2305, 0, 1, 55 }, + { 0x0, 0x0, 44, 1250, 2972, 0, 1, 55 }, + { 0x0, 0x0, 44, 1251, 2373, 0, 0, -1 }, + { 0x0, 0x0, 44, 1252, -1, 0, 1, 50 }, + { 0x0, 0x0, 44, 1120, -1, 0, 1, 0 }, + { 0x0, 0x0, 44, 1121, -1, 0, 1, 0 }, + { 0x0, 0x0, 44, 1122, -1, 0, 1, 0 }, + { 0x1, 0x1, 45, -1, 1676, 30, 1, 158 }, + { 0x1, 0x1, 45, 963, 1675, 30, 1, 158 }, + { 0x1, 0x1, 45, -1, 1680, 30, 1, 159 }, + { 0x1, 0x1, 45, 964, 1679, 30, 1, 159 }, + { 0x1, 0x1, 45, -1, 1684, 30, 1, 159 }, + { 0x1, 0x1, 45, 965, 1683, 30, 1, 159 }, + { 0x3, 0x3, 46, -1, 1160, 3, 1, 23 }, + { 0x1, 0x1, 47, 2257, -1, 30, 1, 144 }, + { 0x1, 0x1, 47, 2288, -1, 30, 1, 160 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 56, -1, 1677, 31, 1, 158 }, + { 0x1, 0x1, 56, -1, 1681, 31, 1, 159 }, + { 0x1, 0x1, 56, -1, 1685, 31, 1, 159 }, + { 0x0, 0x0, 56, -1, -1, 0, 1, 101 }, + { 0x2, 0x3, 56, -1, -1, 27, 1, 101 }, + { 0x1, 0x1, 56, -1, -1, 28, 1, 101 }, + { 0x0, 0x0, 65, 14, 592, 0, 1, 6 }, + { 0x0, 0x0, 65, 1273, 595, 0, 1, 6 }, + { 0x1, 0x1, 65, 1274, 597, 33, 1, 6 }, + { 0x1, 0x1, 65, 1275, 599, 34, 1, 6 }, + { 0x3, 0x3, 65, 1276, 601, 33, 1, 6 }, + { 0x0, 0x0, 65, 1277, 603, 0, 1, 6 }, + { 0x1, 0x1, 65, 1278, 605, 33, 1, 6 }, + { 0x1, 0x1, 65, 1279, 607, 34, 1, 6 }, + { 0x3, 0x3, 65, 1280, 609, 33, 1, 6 }, + { 0x1, 0x1, 65, 1281, 611, 6, 1, 7 }, + { 0x8000001, 0x8000001, 65, 1282, 613, 6, 1, 7 }, + { 0x10000001, 0x10000001, 65, 1283, 615, 6, 1, 7 }, + { 0x18000001, 0x18000001, 65, 1284, 617, 6, 1, 7 }, + { 0x0, 0x0, 65, 1285, 631, 0, 1, 8 }, + { 0x1, 0x1, 65, 1286, 633, 33, 1, 8 }, + { 0x1, 0x1, 65, 1287, 635, 34, 1, 8 }, + { 0x3, 0x3, 65, 1288, 637, 33, 1, 8 }, + { 0x0, 0x0, 65, 1289, 643, 0, 1, 16 }, + { 0x1, 0x1, 65, 1290, 645, 33, 1, 16 }, + { 0x1, 0x1, 65, 1291, 647, 34, 1, 16 }, + { 0x3, 0x3, 65, 1292, 649, 33, 1, 16 }, + { 0x0, 0x0, 65, 15, 655, 0, 1, 18 }, + { 0x0, 0x0, 65, 1294, 658, 0, 1, 18 }, + { 0x1, 0x1, 65, 1295, 660, 33, 1, 18 }, + { 0x1, 0x1, 65, 1296, 662, 34, 1, 18 }, + { 0x3, 0x3, 65, 1297, 664, 33, 1, 18 }, + { 0x0, 0x0, 65, 1298, 666, 0, 1, 18 }, + { 0x1, 0x1, 65, 1299, 668, 33, 1, 18 }, + { 0x1, 0x1, 65, 1300, 670, 34, 1, 18 }, + { 0x3, 0x3, 65, 1301, 672, 33, 1, 18 }, + { 0x0, 0x0, 65, 1302, 682, 0, 1, 19 }, + { 0x1, 0x1, 65, 1303, 684, 33, 1, 19 }, + { 0x1, 0x1, 65, 1304, 686, 34, 1, 19 }, + { 0x3, 0x3, 65, 1305, 688, 33, 1, 19 }, + { 0x1, 0x1, 65, 1306, 690, 6, 1, 19 }, + { 0x8000001, 0x8000001, 65, 1307, 692, 6, 1, 19 }, + { 0x10000001, 0x10000001, 65, 1308, 694, 6, 1, 19 }, + { 0x18000001, 0x18000001, 65, 1309, 696, 6, 1, 19 }, + { 0x0, 0x0, 65, 1310, 706, 0, 1, 20 }, + { 0x1, 0x1, 65, 1311, 708, 33, 1, 20 }, + { 0x1, 0x1, 65, 1312, 710, 34, 1, 20 }, + { 0x3, 0x3, 65, 1313, 712, 33, 1, 20 }, + { 0x0, 0x0, 65, 1314, 718, 0, 1, 21 }, + { 0x1, 0x1, 65, 1315, 720, 33, 1, 21 }, + { 0x1, 0x1, 65, 1316, 722, 34, 1, 21 }, + { 0x3, 0x3, 65, 1317, 724, 33, 1, 21 }, + { 0x1, 0x1, 65, 1318, 726, 6, 1, 21 }, + { 0x8000001, 0x8000001, 65, 1319, 728, 6, 1, 21 }, + { 0x10000001, 0x10000001, 65, 1320, 730, 6, 1, 21 }, + { 0x18000001, 0x18000001, 65, 1321, 732, 6, 1, 21 }, + { 0x0, 0x0, 65, 1322, 742, 0, 1, 22 }, + { 0x1, 0x1, 65, 1323, 744, 33, 1, 22 }, + { 0x1, 0x1, 65, 1324, 746, 34, 1, 22 }, + { 0x3, 0x3, 65, 1325, 748, 33, 1, 22 }, + { 0x0, 0x0, 65, 17, 754, 0, 1, 18 }, + { 0x0, 0x0, 65, 1327, 757, 0, 1, 18 }, + { 0x1, 0x1, 65, 1328, 759, 33, 1, 18 }, + { 0x1, 0x1, 65, 1329, 761, 34, 1, 18 }, + { 0x3, 0x3, 65, 1330, 763, 33, 1, 18 }, + { 0x0, 0x0, 65, 1331, 765, 0, 1, 18 }, + { 0x1, 0x1, 65, 1332, 767, 33, 1, 18 }, + { 0x1, 0x1, 65, 1333, 769, 34, 1, 18 }, + { 0x3, 0x3, 65, 1334, 771, 33, 1, 18 }, + { 0x0, 0x0, 65, 1335, 781, 0, 1, 22 }, + { 0x1, 0x1, 65, 1336, 783, 33, 1, 22 }, + { 0x1, 0x1, 65, 1337, 785, 34, 1, 22 }, + { 0x3, 0x3, 65, 1338, 787, 33, 1, 22 }, + { 0x3, 0x3, 66, 561, 1539, 33, 1, 136 }, + { 0x3, 0x3, 66, 562, 1549, 33, 1, 136 }, + { 0x3, 0x3, 66, 563, 1559, 33, 1, 136 }, + { 0x0, 0x0, 66, -1, 1564, 0, 1, 147 }, + { 0x0, 0x0, 66, -1, 1565, 0, 1, 152 }, + { 0x0, 0x0, 66, -1, 1566, 0, 1, 152 }, + { 0x0, 0x0, 107, 1046, 2345, 0, 0, -1 }, + { 0x0, 0x0, 107, 1047, 2864, 0, 1, 30 }, + { 0x0, 0x0, 107, 1048, 2386, 0, 0, -1 }, + { 0x0, 0x0, 107, 1049, 2868, 0, 1, 30 }, + { 0x0, 0x0, 109, -1, 2347, 0, 0, -1 }, + { 0x1, 0x1, 109, -1, 2865, 27, 1, 30 }, + { 0x0, 0x0, 109, -1, 2388, 0, 0, -1 }, + { 0x1, 0x1, 109, -1, 2869, 27, 1, 30 }, + { 0x0, 0x0, 110, 1051, -1, 0, 1, 122 }, + { 0x1, 0x1, 111, -1, -1, 27, 1, 122 }, + { 0x0, 0x0, 112, 1082, 2894, 0, 1, 1 }, + { 0x0, 0x0, 112, 1083, 2897, 0, 1, 1 }, + { 0x0, 0x0, 112, 1224, 305, 0, 0, -1 }, + { 0x0, 0x0, 112, 1225, 309, 0, 0, -1 }, + { 0x0, 0x0, 112, 1185, 440, 0, 0, -1 }, + { 0x0, 0x0, 112, 1186, 448, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 456, 0, 0, -1 }, + { 0x0, 0x0, 112, 1084, 2910, 0, 1, 1 }, + { 0x0, 0x0, 112, 1085, 2913, 0, 1, 1 }, + { 0x0, 0x0, 112, -1, 330, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 334, 0, 0, -1 }, + { 0x0, 0x0, 112, 1233, 335, 0, 0, -1 }, + { 0x0, 0x0, 112, 1234, 339, 0, 0, -1 }, + { 0x0, 0x0, 112, 1086, 2934, 0, 1, 1 }, + { 0x0, 0x0, 112, 1087, 2937, 0, 1, 1 }, + { 0x0, 0x0, 112, 1237, 353, 0, 0, -1 }, + { 0x0, 0x0, 112, 1238, 357, 0, 0, -1 }, + { 0x0, 0x0, 112, 1198, 488, 0, 0, -1 }, + { 0x0, 0x0, 112, 1199, 496, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 504, 0, 0, -1 }, + { 0x0, 0x0, 112, 1391, 2948, 0, 1, 1 }, + { 0x0, 0x0, 112, 1392, 2950, 0, 1, 1 }, + { 0x0, 0x0, 112, -1, 378, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 382, 0, 0, -1 }, + { 0x0, 0x0, 112, 1246, 383, 0, 0, -1 }, + { 0x0, 0x0, 112, 1247, 387, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 2315, 0, 0, -1 }, + { 0x1, 0x9, 112, -1, 2319, 33, 1, 55 }, + { 0x1, 0x9, 112, -1, 2981, 33, 1, 55 }, + { 0x2, 0x3, 112, 1408, 2382, 27, 1, 50 }, + { 0x1, 0x1, 114, 1374, 2895, 37, 1, 1 }, + { 0x1, 0x1, 114, 1375, 2898, 37, 1, 1 }, + { 0x1, 0x1, 114, 1379, 2911, 37, 1, 1 }, + { 0x1, 0x1, 114, 1380, 2914, 37, 1, 1 }, + { 0x1, 0x1, 114, 1386, 2935, 37, 1, 1 }, + { 0x1, 0x1, 114, 1387, 2938, 37, 1, 1 }, + { 0x0, 0x0, 114, -1, 2958, 0, 1, 1 }, + { 0x0, 0x0, 114, -1, 2959, 0, 1, 1 }, + { 0x0, 0x0, 115, 1123, 2890, 0, 1, 1 }, + { 0x0, 0x0, 115, 1124, 2892, 0, 1, 1 }, + { 0x0, 0x0, 115, 1183, 303, 0, 0, -1 }, + { 0x0, 0x0, 115, 1184, 307, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 444, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 452, 0, 0, -1 }, + { 0x0, 0x0, 115, 1228, 454, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 2908, 0, 1, 1 }, + { 0x0, 0x0, 115, -1, 2909, 0, 1, 1 }, + { 0x0, 0x0, 115, 1231, 328, 0, 0, -1 }, + { 0x0, 0x0, 115, 1232, 332, 0, 0, -1 }, + { 0x0, 0x0, 115, 1192, 337, 0, 0, -1 }, + { 0x0, 0x0, 115, 1193, 341, 0, 0, -1 }, + { 0x0, 0x0, 115, 1127, 2930, 0, 1, 1 }, + { 0x0, 0x0, 115, 1128, 2932, 0, 1, 1 }, + { 0x0, 0x0, 115, 1196, 351, 0, 0, -1 }, + { 0x0, 0x0, 115, 1197, 355, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 492, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 500, 0, 0, -1 }, + { 0x0, 0x0, 115, 1241, 502, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 2946, 0, 1, 1 }, + { 0x0, 0x0, 115, -1, 2947, 0, 1, 1 }, + { 0x0, 0x0, 115, 1244, 376, 0, 0, -1 }, + { 0x0, 0x0, 115, 1245, 380, 0, 0, -1 }, + { 0x0, 0x0, 115, 1205, 385, 0, 0, -1 }, + { 0x0, 0x0, 115, 1206, 389, 0, 0, -1 }, + { 0x0, 0x0, 115, 1078, 2313, 0, 0, -1 }, + { 0x0, 0x0, 115, 1079, 2317, 0, 1, 55 }, + { 0x0, 0x0, 115, 1080, 2980, 0, 1, 55 }, + { 0x0, 0x0, 115, 1081, 2381, 0, 1, 50 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 116, -1, 2891, 37, 1, 1 }, + { 0x1, 0x1, 116, -1, 2893, 37, 1, 1 }, + { 0x0, 0x0, 116, -1, 2918, 0, 1, 1 }, + { 0x0, 0x0, 116, -1, 2919, 0, 1, 1 }, + { 0x1, 0x1, 116, -1, 2931, 37, 1, 1 }, + { 0x1, 0x1, 116, -1, 2933, 37, 1, 1 }, + { 0x0, 0x0, 116, -1, 2956, 0, 1, 1 }, + { 0x0, 0x0, 116, -1, 2957, 0, 1, 1 }, + { 0x0, 0x0, 117, 1176, -1, 0, 1, 0 }, + { 0x0, 0x0, 117, 1177, -1, 0, 1, 0 }, + { 0x0, 0x0, 117, 1178, -1, 0, 1, 0 }, + { 0x3, 0x3, 117, 1136, -1, 34, 1, 34 }, + { 0x3, 0x3, 117, 1137, -1, 34, 1, 41 }, + { 0x1, 0x1, 119, -1, -1, 35, 1, 34 }, + { 0x1, 0x1, 119, -1, -1, 35, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 67 }, + { 0x1, 0x1, 120, -1, -1, 36, 1, 129 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 103 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 112 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 75 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 124 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 121, -1, 2820, 0, 0, -1 }, + { 0x0, 0x0, 121, -1, 2823, 0, 0, -1 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, 918, -1, 23, 1, 68 }, + { 0x9, 0x9, 122, 919, -1, 20, 1, 68 }, + { 0x0, 0x0, 126, 2199, -1, 0, 1, 0 }, + { 0x0, 0x0, 126, 2200, -1, 0, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 126, -1, -1, 27, 1, 34 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 126, -1, -1, 0, 1, 121 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 126, 1134, -1, 0, 1, 34 }, + { 0x0, 0x0, 126, 1262, -1, 0, 1, 41 }, + { 0x0, 0x0, 140, 1212, 2886, 0, 1, 1 }, + { 0x0, 0x0, 140, 1213, 2888, 0, 1, 1 }, + { 0x0, 0x0, 140, 1054, 304, 0, 0, -1 }, + { 0x0, 0x0, 140, 1055, 432, 0, 0, -1 }, + { 0x0, 0x0, 140, 1094, 313, 0, 0, -1 }, + { 0x0, 0x0, 140, 1095, 317, 0, 0, -1 }, + { 0x0, 0x0, 140, 1096, 453, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 2906, 0, 1, 1 }, + { 0x0, 0x0, 140, -1, 2907, 0, 1, 1 }, + { 0x0, 0x0, 140, 1099, 327, 0, 0, -1 }, + { 0x0, 0x0, 140, 1100, 331, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 338, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 342, 0, 0, -1 }, + { 0x0, 0x0, 140, 1216, 2926, 0, 1, 1 }, + { 0x0, 0x0, 140, 1217, 2928, 0, 1, 1 }, + { 0x0, 0x0, 140, 1067, 352, 0, 0, -1 }, + { 0x0, 0x0, 140, 1068, 480, 0, 0, -1 }, + { 0x0, 0x0, 140, 1107, 361, 0, 0, -1 }, + { 0x0, 0x0, 140, 1108, 365, 0, 0, -1 }, + { 0x0, 0x0, 140, 1109, 501, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 2944, 0, 1, 1 }, + { 0x0, 0x0, 140, -1, 2945, 0, 1, 1 }, + { 0x0, 0x0, 140, 1112, 375, 0, 0, -1 }, + { 0x0, 0x0, 140, 1113, 379, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 386, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 390, 0, 0, -1 }, + { 0x0, 0x0, 140, 3012, 2301, 0, 0, -1 }, + { 0x1, 0x1, 140, 3013, 2309, 33, 1, 55 }, + { 0x1, 0x1, 140, 3014, 2974, 33, 1, 55 }, + { 0x0, 0x0, 140, 3015, 2375, 0, 0, -1 }, + { 0x1, 0x1, 140, 3016, -1, 28, 1, 50 }, + { 0x1, 0x1, 141, -1, 2887, 37, 1, 1 }, + { 0x1, 0x1, 141, -1, 2889, 37, 1, 1 }, + { 0x0, 0x0, 141, -1, 2916, 0, 1, 1 }, + { 0x0, 0x0, 141, -1, 2917, 0, 1, 1 }, + { 0x1, 0x1, 141, -1, 2927, 37, 1, 1 }, + { 0x1, 0x1, 141, -1, 2929, 37, 1, 1 }, + { 0x0, 0x0, 141, -1, 2954, 0, 1, 1 }, + { 0x0, 0x0, 141, -1, 2955, 0, 1, 1 }, + { 0x1, 0x1, 144, 917, 1158, 3, 1, 23 }, + { 0x0, 0x0, 145, 2201, -1, 0, 1, 34 }, + { 0x0, 0x0, 146, 923, 2880, 0, 1, 1 }, + { 0x0, 0x0, 146, 924, 2883, 0, 1, 1 }, + { 0x0, 0x0, 146, -1, 306, 0, 0, -1 }, + { 0x0, 0x0, 146, -1, 436, 0, 0, -1 }, + { 0x0, 0x0, 146, 1056, 311, 0, 0, -1 }, + { 0x0, 0x0, 146, 1057, 315, 0, 0, -1 }, + { 0x0, 0x0, 146, 1058, 455, 0, 0, -1 }, + { 0x0, 0x0, 146, 927, 2900, 0, 1, 1 }, + { 0x0, 0x0, 146, 928, 2903, 0, 1, 1 }, + { 0x0, 0x0, 146, 1061, 329, 0, 0, -1 }, + { 0x0, 0x0, 146, 1062, 333, 0, 0, -1 }, + { 0x0, 0x0, 146, 1101, 336, 0, 0, -1 }, + { 0x0, 0x0, 146, 1102, 340, 0, 0, -1 }, + { 0x0, 0x0, 146, 933, 2920, 0, 1, 1 }, + { 0x0, 0x0, 146, 934, 2923, 0, 1, 1 }, + { 0x0, 0x0, 146, -1, 354, 0, 0, -1 }, + { 0x0, 0x0, 146, -1, 484, 0, 0, -1 }, + { 0x0, 0x0, 146, 1069, 359, 0, 0, -1 }, + { 0x0, 0x0, 146, 1070, 363, 0, 0, -1 }, + { 0x0, 0x0, 146, 1071, 503, 0, 0, -1 }, + { 0x0, 0x0, 146, 937, 2940, 0, 1, 1 }, + { 0x0, 0x0, 146, 938, 2942, 0, 1, 1 }, + { 0x0, 0x0, 146, 1074, 377, 0, 0, -1 }, + { 0x0, 0x0, 146, 1075, 381, 0, 0, -1 }, + { 0x0, 0x0, 146, 1114, 384, 0, 0, -1 }, + { 0x0, 0x0, 146, 1115, 388, 0, 0, -1 }, + { 0x0, 0x0, 146, 1207, 2299, 0, 0, -1 }, + { 0x1, 0x1, 146, 1208, 2307, 36, 1, 55 }, + { 0x1, 0x1, 146, 1209, 2973, 36, 1, 55 }, + { 0x0, 0x0, 146, 1210, 2374, 0, 0, -1 }, + { 0x1, 0x1, 146, 1211, -1, 27, 1, 50 }, + { 0x1, 0x1, 147, -1, 2882, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2885, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2902, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2905, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2922, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2925, 37, 1, 1 }, + { 0x0, 0x0, 147, -1, 2952, 0, 1, 1 }, + { 0x0, 0x0, 147, -1, 2953, 0, 1, 1 }, + { 0x0, 0x0, 148, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 148, 1135, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 67 }, + { 0x0, 0x0, 149, -1, 2960, 0, 1, 64 }, + { 0x0, 0x0, 149, -1, 2961, 0, 1, 64 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 87 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 87 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 92 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 150, -1, 593, 12, 1, 6 }, + { 0x1, 0x1, 150, -1, 596, 12, 1, 6 }, + { 0x200001, 0x200001, 150, -1, 598, 12, 1, 6 }, + { 0x400001, 0x400001, 150, -1, 600, 12, 1, 6 }, + { 0x600001, 0x600001, 150, -1, 602, 12, 1, 6 }, + { 0x1, 0x1, 150, -1, 604, 12, 1, 6 }, + { 0x200001, 0x200001, 150, -1, 606, 12, 1, 6 }, + { 0x400001, 0x400001, 150, -1, 608, 12, 1, 6 }, + { 0x600001, 0x600001, 150, -1, 610, 12, 1, 6 }, + { 0x41, 0x41, 150, -1, 612, 6, 1, 7 }, + { 0x8000041, 0x8000041, 150, -1, 614, 6, 1, 7 }, + { 0x10000041, 0x10000041, 150, -1, 616, 6, 1, 7 }, + { 0x18000041, 0x18000041, 150, -1, 618, 6, 1, 7 }, + { 0x1, 0x1, 150, -1, 632, 12, 1, 8 }, + { 0x200001, 0x200001, 150, -1, 634, 12, 1, 8 }, + { 0x400001, 0x400001, 150, -1, 636, 12, 1, 8 }, + { 0x600001, 0x600001, 150, -1, 638, 12, 1, 8 }, + { 0x1, 0x1, 150, -1, 644, 12, 1, 16 }, + { 0x200001, 0x200001, 150, -1, 646, 12, 1, 16 }, + { 0x400001, 0x400001, 150, -1, 648, 12, 1, 16 }, + { 0x600001, 0x600001, 150, -1, 650, 12, 1, 16 }, + { 0x1, 0x1, 150, -1, 656, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 659, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 661, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 663, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 665, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 667, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 669, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 671, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 673, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 683, 12, 1, 19 }, + { 0x200001, 0x200001, 150, -1, 685, 12, 1, 19 }, + { 0x400001, 0x400001, 150, -1, 687, 12, 1, 19 }, + { 0x600001, 0x600001, 150, -1, 689, 12, 1, 19 }, + { 0x41, 0x41, 150, -1, 691, 6, 1, 19 }, + { 0x8000041, 0x8000041, 150, -1, 693, 6, 1, 19 }, + { 0x10000041, 0x10000041, 150, -1, 695, 6, 1, 19 }, + { 0x18000041, 0x18000041, 150, -1, 697, 6, 1, 19 }, + { 0x1, 0x1, 150, -1, 707, 12, 1, 20 }, + { 0x200001, 0x200001, 150, -1, 709, 12, 1, 20 }, + { 0x400001, 0x400001, 150, -1, 711, 12, 1, 20 }, + { 0x600001, 0x600001, 150, -1, 713, 12, 1, 20 }, + { 0x1, 0x1, 150, -1, 719, 12, 1, 21 }, + { 0x200001, 0x200001, 150, -1, 721, 12, 1, 21 }, + { 0x400001, 0x400001, 150, -1, 723, 12, 1, 21 }, + { 0x600001, 0x600001, 150, -1, 725, 12, 1, 21 }, + { 0x41, 0x41, 150, -1, 727, 6, 1, 21 }, + { 0x8000041, 0x8000041, 150, -1, 729, 6, 1, 21 }, + { 0x10000041, 0x10000041, 150, -1, 731, 6, 1, 21 }, + { 0x18000041, 0x18000041, 150, -1, 733, 6, 1, 21 }, + { 0x1, 0x1, 150, -1, 743, 12, 1, 22 }, + { 0x200001, 0x200001, 150, -1, 745, 12, 1, 22 }, + { 0x400001, 0x400001, 150, -1, 747, 12, 1, 22 }, + { 0x600001, 0x600001, 150, -1, 749, 12, 1, 22 }, + { 0x1, 0x1, 150, -1, 755, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 758, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 760, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 762, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 764, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 766, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 768, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 770, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 772, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 782, 12, 1, 22 }, + { 0x200001, 0x200001, 150, -1, 784, 12, 1, 22 }, + { 0x400001, 0x400001, 150, -1, 786, 12, 1, 22 }, + { 0x600001, 0x600001, 150, -1, 788, 12, 1, 22 }, + { 0x0, 0x0, 155, -1, -1, 0, 1, 131 }, + { 0x0, 0x0, 159, 793, -1, 0, 1, 81 }, + { 0x0, 0x0, 159, 794, -1, 0, 1, 81 }, + { 0x9, 0x9, 159, -1, 1456, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1465, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1474, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1487, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1496, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1505, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1514, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1523, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1532, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1542, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1552, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1562, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1571, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1577, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1583, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1589, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1595, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1601, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1607, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1613, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1619, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1625, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1631, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1637, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1643, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1649, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1655, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1661, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1667, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1673, 32, 1, 156 }, + { 0x0, 0x0, 160, 1253, 298, 0, 0, -1 }, + { 0x0, 0x0, 160, 1254, 422, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2896, 38, 1, 1 }, + { 0x1, 0x1, 160, 925, 2899, 38, 1, 1 }, + { 0x0, 0x0, 160, 926, 423, 0, 0, -1 }, + { 0x0, 0x0, 160, 1255, 320, 0, 0, -1 }, + { 0x0, 0x0, 160, 1256, 462, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2912, 38, 1, 1 }, + { 0x1, 0x1, 160, 929, 2915, 38, 1, 1 }, + { 0x0, 0x0, 160, 930, 463, 0, 0, -1 }, + { 0x0, 0x0, 160, 931, 325, 0, 0, -1 }, + { 0x0, 0x0, 160, 932, 343, 0, 0, -1 }, + { 0x0, 0x0, 160, 1257, 346, 0, 0, -1 }, + { 0x0, 0x0, 160, 1258, 470, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2936, 38, 1, 1 }, + { 0x1, 0x1, 160, 935, 2939, 38, 1, 1 }, + { 0x0, 0x0, 160, 936, 471, 0, 0, -1 }, + { 0x0, 0x0, 160, -1, 368, 0, 0, -1 }, + { 0x0, 0x0, 160, -1, 510, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2949, 38, 1, 1 }, + { 0x1, 0x1, 160, 939, 2951, 38, 1, 1 }, + { 0x0, 0x0, 160, 940, 511, 0, 0, -1 }, + { 0x0, 0x0, 160, 941, 373, 0, 0, -1 }, + { 0x0, 0x0, 160, 942, 391, 0, 0, -1 }, + { 0x0, 0x0, 161, 1415, 2321, 0, 0, -1 }, + { 0x0, 0x0, 161, 1416, 2329, 0, 1, 55 }, + { 0x0, 0x0, 161, 1417, 2990, 0, 1, 55 }, + { 0x0, 0x0, 161, 1418, 2377, 0, 0, -1 }, + { 0x1, 0x1, 161, 1419, -1, 29, 1, 50 }, + { 0x0, 0x0, 162, -1, 2339, 0, 0, -1 }, + { 0x1, 0x9, 162, -1, 2343, 33, 1, 55 }, + { 0x1, 0x9, 162, -1, 2999, 33, 1, 55 }, + { 0x6, 0x7, 162, -1, 2384, 27, 1, 50 }, + { 0x0, 0x0, 163, 1401, 2337, 0, 0, -1 }, + { 0x0, 0x0, 163, 1402, 2341, 0, 1, 55 }, + { 0x0, 0x0, 163, 1403, 2998, 0, 1, 55 }, + { 0x1, 0x1, 163, 1404, 2383, 29, 1, 50 }, + { 0x1, 0x1, 164, 1422, -1, 27, 1, 34 }, + { 0x0, 0x0, 165, 2193, 2325, 0, 0, -1 }, + { 0x1, 0x1, 165, 2194, 2333, 33, 1, 55 }, + { 0x1, 0x1, 165, 2195, 2992, 33, 1, 55 }, + { 0x0, 0x0, 165, 2196, 2379, 0, 0, -1 }, + { 0x3, 0x3, 165, 2197, -1, 28, 1, 50 }, + { 0x0, 0x0, 166, 1410, 2323, 0, 0, -1 }, + { 0x1, 0x1, 166, 1411, 2331, 36, 1, 55 }, + { 0x1, 0x1, 166, 1412, 2991, 36, 1, 55 }, + { 0x0, 0x0, 166, 1413, 2378, 0, 0, -1 }, + { 0x5, 0x5, 166, 1414, -1, 27, 1, 50 }, + { 0x0, 0x0, 167, -1, 2962, 0, 1, 64 }, + { 0x0, 0x0, 167, -1, 2963, 0, 1, 64 }, + { 0x1, 0x1, 169, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 170, 2779, -1, 27, 1, 34 }, + { 0x1, 0x1, 170, 2780, -1, 27, 1, 34 }, + { 0x1, 0x1, 171, 1703, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1704, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1705, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1706, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1707, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1708, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1709, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1710, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1711, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1712, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1713, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1714, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1715, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1716, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1717, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1718, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1719, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1720, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1721, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1722, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1723, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1724, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1725, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1726, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1727, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1728, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1729, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1730, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1731, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1732, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1733, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1734, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1735, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1736, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1737, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1738, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1739, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1740, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1741, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1742, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1743, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1744, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1745, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1746, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1747, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1748, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1749, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1750, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1751, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1752, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1753, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1754, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1755, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1756, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1757, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1758, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1759, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1760, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1761, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1762, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1763, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1764, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1765, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1766, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1767, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1768, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1769, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1770, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1771, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1772, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1773, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1774, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1775, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1776, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1777, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1778, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1779, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1780, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1781, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1782, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1783, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1784, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1785, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1786, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1787, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1788, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1789, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1790, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1791, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1792, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1793, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1794, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1795, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1796, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1797, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1798, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1799, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1800, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1801, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1802, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1803, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1804, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1805, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1806, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1807, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1808, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1809, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1810, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1811, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1812, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1813, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1814, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1815, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1816, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1817, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1818, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1819, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1820, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1821, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1822, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1823, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1824, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1825, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1826, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1827, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1828, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1829, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1830, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1831, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1832, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1833, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1834, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1835, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1836, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1837, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1838, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1839, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1840, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1841, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1842, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1843, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1844, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1845, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1846, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1847, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1848, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1849, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1850, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1851, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1852, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1853, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1854, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1855, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1856, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1857, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1858, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1859, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1860, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1861, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1862, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1863, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1864, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1865, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1866, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1867, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1868, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1869, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1870, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1871, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1872, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1873, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1874, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1875, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1876, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1877, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1878, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1879, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1880, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1881, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1882, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1883, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1884, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1885, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1886, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1887, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1888, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1889, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1890, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1891, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1892, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1893, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1894, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1895, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1896, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1897, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1898, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1899, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1900, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1901, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1902, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1903, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1904, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1905, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1906, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1907, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1908, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1909, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1910, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1911, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1912, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1913, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1914, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1915, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1916, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1917, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1918, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1919, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1920, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1921, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1922, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1923, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1924, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1925, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1926, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1927, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1928, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1929, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1930, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1931, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1932, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1933, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1934, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1935, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1936, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1937, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1938, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1939, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1940, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1941, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1942, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1943, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1944, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1945, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1946, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1947, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1948, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1949, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1950, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1951, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1952, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1691, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1692, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1693, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1694, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1695, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1696, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1697, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1698, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1699, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1700, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1701, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1702, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1997, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1998, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1999, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 2000, -1, 28, 1, 143 }, + { 0x1, 0x1, 172, 1953, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1954, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1955, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1956, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1957, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1958, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1959, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1960, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1961, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1962, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1963, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1964, -1, 29, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 271, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2258, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 273, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2259, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 275, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2260, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, 277, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, 278, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 279, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2261, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 281, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2262, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 283, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2263, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 285, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2264, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 287, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2265, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 289, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2266, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 291, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2267, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 293, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2268, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 295, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2269, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2270, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2271, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2272, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2273, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2274, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2275, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2276, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2277, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2278, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2279, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2280, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2281, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2282, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2283, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2284, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2285, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2286, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2287, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, 951, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, 952, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 953, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 954, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 955, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 956, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2224, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2225, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, 2226, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, 2227, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2228, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2229, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2230, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2231, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 140 }, + { 0x3, 0x3, 173, 2232, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, + { 0x3, 0x3, 173, 2233, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x0, 0x0, 174, -1, 394, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 396, 0, 0, -1 }, + { 0x0, 0x0, 174, 3042, 3002, 0, 1, 1 }, + { 0x0, 0x0, 174, 3043, 3003, 0, 1, 1 }, + { 0x0, 0x0, 174, -1, 402, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 404, 0, 0, -1 }, + { 0x0, 0x0, 174, 3046, 3006, 0, 1, 76 }, + { 0x0, 0x0, 174, 3047, 3007, 0, 1, 76 }, + { 0x0, 0x0, 174, -1, 410, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 412, 0, 0, -1 }, + { 0x0, 0x0, 174, 3050, 3010, 0, 1, 1 }, + { 0x0, 0x0, 174, 3051, 3011, 0, 1, 1 }, + { 0x11, 0x31, 175, 2881, 417, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 418, 12, 1, 4 }, + { 0x11, 0x31, 175, 2073, 419, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 421, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 425, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 426, 12, 1, 4 }, + { 0x11, 0x11, 175, -1, 427, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 428, 12, 1, 4 }, + { 0x1, 0x1, 175, 2079, 429, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 431, 12, 1, 4 }, + { 0x11, 0x11, 175, 2081, 433, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 435, 12, 1, 4 }, + { 0x1, 0x1, 175, 2083, 437, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 439, 12, 1, 4 }, + { 0x11, 0x11, 175, 2085, 441, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 443, 12, 1, 4 }, + { 0x1, 0x1, 175, 2087, 445, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 447, 12, 1, 4 }, + { 0x11, 0x11, 175, 2089, 449, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 451, 12, 1, 4 }, + { 0x11, 0x31, 175, 2901, 457, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 458, 12, 1, 4 }, + { 0x11, 0x31, 175, 2095, 459, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 461, 12, 1, 4 }, + { 0x11, 0x31, 175, 2921, 465, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 466, 12, 1, 4 }, + { 0x11, 0x31, 175, 2121, 467, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 469, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 473, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 474, 12, 1, 4 }, + { 0x11, 0x11, 175, -1, 475, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 476, 12, 1, 4 }, + { 0x1, 0x1, 175, 2127, 477, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 479, 12, 1, 4 }, + { 0x11, 0x11, 175, 2129, 481, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 483, 12, 1, 4 }, + { 0x1, 0x1, 175, 2131, 485, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 487, 12, 1, 4 }, + { 0x11, 0x11, 175, 2133, 489, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 491, 12, 1, 4 }, + { 0x1, 0x1, 175, 2135, 493, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 495, 12, 1, 4 }, + { 0x11, 0x11, 175, 2137, 497, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 499, 12, 1, 4 }, + { 0x11, 0x31, 175, 2941, 505, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 506, 12, 1, 4 }, + { 0x11, 0x31, 175, 2143, 507, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 509, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 513, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 514, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 515, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 516, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 521, 33, 1, 79 }, + { 0x200001, 0x200001, 175, -1, 522, 12, 1, 79 }, + { 0x1, 0x1, 175, -1, 523, 33, 1, 79 }, + { 0x200001, 0x200001, 175, -1, 524, 12, 1, 79 }, + { 0x1, 0x1, 175, -1, 529, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 530, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 531, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 532, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2884, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2016, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2022, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2024, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2026, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2028, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2030, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2032, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2904, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2036, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x2200001, 0x6200001, 176, 2924, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2040, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2046, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2048, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2050, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2052, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2054, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2056, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2943, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2060, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 397, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 398, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 399, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 400, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 80 }, + { 0x1, 0x1, 176, 405, -1, 33, 1, 79 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 }, + { 0x200001, 0x200001, 176, 406, -1, 12, 1, 79 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 80 }, + { 0x1, 0x1, 176, 407, -1, 33, 1, 79 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 }, + { 0x200001, 0x200001, 176, 408, -1, 12, 1, 79 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 413, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 414, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 415, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 416, -1, 12, 1, 4 }, + { 0x0, 0x0, 177, -1, 2327, 0, 0, -1 }, + { 0x9, 0x9, 177, -1, 2335, 33, 1, 50 }, + { 0x9, 0x9, 177, -1, 2993, 33, 1, 50 }, + { 0x0, 0x0, 177, -1, 2380, 0, 0, -1 }, + { 0x7, 0x7, 177, -1, -1, 27, 1, 50 }, + { 0x1, 0x1, 197, -1, -1, 27, 1, 10 }, + { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, + { 0x2, 0x3, 211, 1169, -1, 27, 1, 34 }, + { 0x0, 0x0, 211, 1170, -1, 0, 1, 34 }, + { 0x0, 0x0, 211, 1171, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1172, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1173, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1174, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 3026, -1, 0, 1, 100 }, + { 0x0, 0x0, 211, 3027, -1, 0, 1, 100 }, + { 0x0, 0x0, 211, 3028, 967, 0, 0, -1 }, + { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 213, -1, 1426, 32, 1, 142 }, + { 0x1, 0x1, 213, -1, 1428, 32, 1, 142 }, + { 0x1, 0x1, 213, -1, 1430, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1432, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1434, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1436, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1438, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1440, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1442, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1444, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1446, 32, 1, 143 }, + { 0x1, 0x1, 213, -1, 1448, 32, 1, 143 }, + { 0x1, 0x1, 213, -1, 1965, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1967, 32, 1, 145 }, + { 0x1, 0x1, 213, -1, 1969, 32, 1, 139 }, + { 0x1, 0x1, 213, -1, 1971, 32, 1, 139 }, + { 0x1, 0x1, 213, -1, 1973, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1975, 32, 1, 145 }, + { 0x1, 0x1, 213, -1, 1977, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1979, 32, 1, 145 }, + { 0x1, 0x1, 213, 2783, 1981, 32, 1, 138 }, + { 0x1, 0x1, 213, 2784, 1984, 32, 1, 145 }, + { 0x0, 0x0, 214, -1, 2825, 0, 0, -1 }, + { 0x0, 0x0, 214, -1, 2826, 0, 0, -1 }, + { 0x0, 0x0, 214, -1, 2851, 0, 0, -1 }, + { 0x5, 0x5, 214, -1, 2854, 20, 1, 68 }, + { 0x0, 0x0, 218, 2209, 966, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, 1139, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, 1264, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, -1, 0, 1, 128 }, + { 0x0, 0x0, 219, -1, -1, 0, 1, 67 }, + { 0x1, 0x1, 219, 833, 2289, 36, 1, 66 }, + { 0x1, 0x1, 219, 834, 2348, 36, 1, 66 }, + { 0x0, 0x0, 219, 835, 2351, 0, 0, -1 }, + { 0x1, 0x1, 219, 836, -1, 36, 1, 66 }, + { 0x0, 0x0, 219, 1423, -1, 0, 1, 34 }, + { 0x1, 0x1, 219, 837, 2356, 36, 1, 66 }, + { 0x0, 0x0, 219, 838, 2359, 0, 0, -1 }, + { 0x1, 0x1, 219, 839, -1, 36, 1, 66 }, + { 0x0, 0x0, 219, 840, 2362, 0, 0, -1 }, + { 0x1, 0x1, 219, 841, -1, 36, 1, 66 }, + { 0x1, 0x1, 219, 842, 2365, 36, 1, 66 }, + { 0x1, 0x1, 219, 843, 2368, 36, 1, 66 }, + { 0x0, 0x0, 219, 1424, -1, 0, 1, 34 }, + { 0x1, 0x1, 219, 844, 2401, 36, 1, 66 }, + { 0x1, 0x1, 219, 845, -1, 31, 1, 144 }, + { 0x1, 0x1, 219, 228, 1449, 32, 1, 133 }, + { 0x1, 0x1, 219, 229, 1458, 32, 1, 133 }, + { 0x1, 0x1, 219, 230, 1467, 32, 1, 133 }, + { 0x1, 0x1, 219, 231, 1480, 32, 1, 133 }, + { 0x1, 0x1, 219, 232, 1489, 32, 1, 133 }, + { 0x1, 0x1, 219, 233, 1498, 32, 1, 133 }, + { 0x1, 0x1, 219, 234, 1507, 32, 1, 133 }, + { 0x1, 0x1, 219, 235, 1516, 32, 1, 133 }, + { 0x1, 0x1, 219, 236, 1525, 32, 1, 133 }, + { 0x1, 0x1, 219, 237, 1534, 32, 1, 133 }, + { 0x1, 0x1, 219, 238, 1544, 32, 1, 133 }, + { 0x1, 0x1, 219, 239, 1554, 32, 1, 133 }, + { 0x1, 0x1, 219, 240, 1567, 32, 1, 148 }, + { 0x1, 0x1, 219, 241, 1573, 32, 1, 153 }, + { 0x1, 0x1, 219, 242, 1579, 32, 1, 153 }, + { 0x1, 0x1, 219, 243, 1585, 32, 1, 148 }, + { 0x1, 0x1, 219, 244, 1591, 32, 1, 153 }, + { 0x1, 0x1, 219, 245, 1597, 32, 1, 153 }, + { 0x1, 0x1, 219, 246, 1603, 32, 1, 148 }, + { 0x1, 0x1, 219, 247, 1609, 32, 1, 153 }, + { 0x1, 0x1, 219, 248, 1615, 32, 1, 153 }, + { 0x1, 0x1, 219, 249, 1621, 32, 1, 148 }, + { 0x1, 0x1, 219, 250, 1627, 32, 1, 153 }, + { 0x1, 0x1, 219, 251, 1633, 32, 1, 148 }, + { 0x1, 0x1, 219, 252, 1639, 32, 1, 153 }, + { 0x1, 0x1, 219, 253, 1645, 32, 1, 148 }, + { 0x1, 0x1, 219, 254, 1651, 32, 1, 153 }, + { 0x1, 0x1, 219, 255, 1657, 32, 1, 148 }, + { 0x1, 0x1, 219, 256, 1663, 32, 1, 153 }, + { 0x1, 0x1, 219, 257, 1669, 32, 1, 153 }, + { 0x1, 0x1, 219, 849, -1, 31, 1, 160 }, + { 0x0, 0x0, 220, 2404, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2405, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 25, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2407, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2408, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2409, -1, 0, 1, 45 }, + { 0x0, 0x0, 220, 2410, -1, 0, 1, 40 }, + { 0x1, 0x1, 220, 2411, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2412, -1, 0, 1, 54 }, + { 0x1000001, 0x1000001, 220, 2413, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2414, -1, 36, 1, 54 }, + { 0x200001, 0x200001, 220, 2415, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2416, -1, 33, 1, 54 }, + { 0x1200001, 0x1200001, 220, 2417, -1, 12, 1, 49 }, + { 0x9, 0x9, 220, 2418, -1, 33, 1, 49 }, + { 0x0, 0x0, 220, 2419, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2420, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2421, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2422, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2423, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2424, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2425, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2426, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2427, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2428, -1, 0, 1, 54 }, + { 0x200001, 0x1200001, 220, 2429, -1, 12, 1, 59 }, + { 0x1, 0x9, 220, 2430, -1, 33, 1, 54 }, + { 0x0, 0x0, 220, 2431, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2432, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2433, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2434, -1, 0, 1, 54 }, + { 0x1, 0x1, 220, 2435, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2436, -1, 0, 1, 54 }, + { 0x1000001, 0x1000001, 220, 2437, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2438, -1, 36, 1, 54 }, + { 0x200001, 0x200001, 220, 2439, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2440, -1, 33, 1, 54 }, + { 0x1200001, 0x1200001, 220, 2441, -1, 12, 1, 49 }, + { 0x9, 0x9, 220, 2442, -1, 33, 1, 49 }, + { 0x0, 0x0, 220, 2443, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2444, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2445, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2446, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2447, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2448, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2449, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2450, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2451, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2452, -1, 0, 1, 54 }, + { 0x200001, 0x1200001, 220, 2453, -1, 12, 1, 59 }, + { 0x1, 0x9, 220, 2454, -1, 33, 1, 54 }, + { 0x0, 0x0, 220, 2455, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2456, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2457, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2458, -1, 0, 1, 54 }, + { 0x1, 0x1, 220, 2459, -1, 28, 1, 29 }, + { 0x0, 0x0, 220, 2460, -1, 0, 1, 29 }, + { 0x3, 0x3, 220, 2461, -1, 27, 1, 29 }, + { 0x1, 0x1, 220, 2462, -1, 27, 1, 29 }, + { 0x0, 0x0, 220, 2463, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2464, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2465, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2466, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2467, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2468, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2469, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2470, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2471, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2472, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 37, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2474, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2475, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2476, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2477, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2478, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2479, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2480, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2481, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 42, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2483, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2484, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 43, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2486, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2487, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2488, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2489, -1, 27, 1, 49 }, + { 0x1, 0x1, 220, 2490, -1, 28, 1, 49 }, + { 0x3, 0x3, 220, 2491, -1, 27, 1, 49 }, + { 0x1, 0x1, 220, 2492, -1, 29, 1, 49 }, + { 0x5, 0x5, 220, 2493, -1, 27, 1, 49 }, + { 0x3, 0x3, 220, 2494, -1, 28, 1, 49 }, + { 0x7, 0x7, 220, 2495, -1, 27, 1, 49 }, + { 0x0, 0x0, 220, 2496, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2497, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2498, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2499, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2500, -1, 28, 1, 29 }, + { 0x0, 0x0, 220, 2501, -1, 0, 1, 29 }, + { 0x3, 0x3, 220, 2502, -1, 27, 1, 29 }, + { 0x1, 0x1, 220, 2503, -1, 27, 1, 29 }, + { 0x0, 0x0, 220, 2504, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2505, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2506, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 52, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2508, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2509, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 57, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2511, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2512, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2513, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2514, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2515, -1, 0, 1, 35 }, + { 0x0, 0x0, 220, 2516, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2517, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 64, -1, 0, 1, 29 }, + { 0x1, 0x1, 221, 2519, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2520, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2521, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2522, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2523, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2524, -1, 34, 1, 46 }, + { 0x1, 0x1, 221, 2525, -1, 34, 1, 42 }, + { 0x400001, 0x400001, 221, 2526, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2527, -1, 34, 1, 56 }, + { 0x1400001, 0x1400001, 221, 2528, -1, 12, 1, 61 }, + { 0x5, 0x5, 221, 2529, -1, 34, 1, 56 }, + { 0x600001, 0x600001, 221, 2530, -1, 12, 1, 61 }, + { 0x3, 0x3, 221, 2531, -1, 33, 1, 56 }, + { 0x1600001, 0x1600001, 221, 2532, -1, 12, 1, 51 }, + { 0xb, 0xb, 221, 2533, -1, 33, 1, 51 }, + { 0x1, 0x1, 221, 2534, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2535, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2536, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2537, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2538, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2539, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2540, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2541, -1, 34, 1, 51 }, + { 0x400001, 0x400001, 221, 2542, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2543, -1, 34, 1, 56 }, + { 0x600001, 0x1600001, 221, 2544, -1, 12, 1, 61 }, + { 0x3, 0xb, 221, 2545, -1, 33, 1, 56 }, + { 0x1, 0x1, 221, 2546, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2547, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2548, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2549, -1, 34, 1, 56 }, + { 0x400001, 0x400001, 221, 2550, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2551, -1, 34, 1, 56 }, + { 0x1400001, 0x1400001, 221, 2552, -1, 12, 1, 61 }, + { 0x5, 0x5, 221, 2553, -1, 34, 1, 56 }, + { 0x600001, 0x600001, 221, 2554, -1, 12, 1, 61 }, + { 0x3, 0x3, 221, 2555, -1, 33, 1, 56 }, + { 0x1600001, 0x1600001, 221, 2556, -1, 12, 1, 51 }, + { 0xb, 0xb, 221, 2557, -1, 33, 1, 51 }, + { 0x1, 0x1, 221, 2558, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2559, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2560, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2561, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2562, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2563, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2564, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2565, -1, 34, 1, 51 }, + { 0x400001, 0x400001, 221, 2566, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2567, -1, 34, 1, 56 }, + { 0x600001, 0x1600001, 221, 2568, -1, 12, 1, 61 }, + { 0x3, 0xb, 221, 2569, -1, 33, 1, 56 }, + { 0x1, 0x1, 221, 2570, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2571, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2572, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2573, -1, 34, 1, 56 }, + { 0x41, 0x41, 221, 2574, -1, 28, 1, 31 }, + { 0x1, 0x1, 221, 2575, -1, 34, 1, 31 }, + { 0x83, 0x83, 221, 2576, -1, 27, 1, 31 }, + { 0x81, 0x81, 221, 2577, -1, 27, 1, 31 }, + { 0x1, 0x1, 221, 2578, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2579, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2580, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2581, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2582, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2583, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2584, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2585, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2586, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2587, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2588, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2589, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2590, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2591, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2592, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2593, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2594, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2595, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2596, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2597, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2598, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2599, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2600, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2601, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2602, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2603, -1, 34, 1, 51 }, + { 0x81, 0x81, 221, 2604, -1, 27, 1, 51 }, + { 0x41, 0x41, 221, 2605, -1, 28, 1, 51 }, + { 0x83, 0x83, 221, 2606, -1, 27, 1, 51 }, + { 0x21, 0x21, 221, 2607, -1, 29, 1, 51 }, + { 0x85, 0x85, 221, 2608, -1, 27, 1, 51 }, + { 0x43, 0x43, 221, 2609, -1, 28, 1, 51 }, + { 0x87, 0x87, 221, 2610, -1, 27, 1, 51 }, + { 0x1, 0x1, 221, 2611, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2612, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2613, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2614, -1, 34, 1, 51 }, + { 0x41, 0x41, 221, 2615, -1, 28, 1, 31 }, + { 0x1, 0x1, 221, 2616, -1, 34, 1, 31 }, + { 0x83, 0x83, 221, 2617, -1, 27, 1, 31 }, + { 0x81, 0x81, 221, 2618, -1, 27, 1, 31 }, + { 0x1, 0x1, 221, 2619, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2620, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2621, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2622, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2623, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2624, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2625, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2626, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2627, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2628, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2629, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2630, -1, 34, 1, 37 }, + { 0x1, 0x1, 221, 2631, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2632, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2633, -1, 34, 1, 31 }, + { 0x1, 0x1, 222, 2634, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2635, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2636, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2637, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2638, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2639, -1, 35, 1, 47 }, + { 0x1, 0x1, 222, 2640, -1, 35, 1, 43 }, + { 0x800001, 0x800001, 222, 2641, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2642, -1, 35, 1, 57 }, + { 0x1800001, 0x1800001, 222, 2643, -1, 12, 1, 62 }, + { 0x3, 0x3, 222, 2644, -1, 35, 1, 57 }, + { 0xa00001, 0xa00001, 222, 2645, -1, 12, 1, 62 }, + { 0x5, 0x5, 222, 2646, -1, 33, 1, 57 }, + { 0x1a00001, 0x1a00001, 222, 2647, -1, 12, 1, 52 }, + { 0xd, 0xd, 222, 2648, -1, 33, 1, 52 }, + { 0x1, 0x1, 222, 2649, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2650, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2651, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2652, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2653, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2654, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2655, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2656, -1, 35, 1, 52 }, + { 0x800001, 0x800001, 222, 2657, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2658, -1, 35, 1, 57 }, + { 0xa00001, 0x1a00001, 222, 2659, -1, 12, 1, 62 }, + { 0x5, 0xd, 222, 2660, -1, 33, 1, 57 }, + { 0x1, 0x1, 222, 2661, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2662, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2663, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2664, -1, 35, 1, 57 }, + { 0x800001, 0x800001, 222, 2665, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2666, -1, 35, 1, 57 }, + { 0x1800001, 0x1800001, 222, 2667, -1, 12, 1, 62 }, + { 0x3, 0x3, 222, 2668, -1, 35, 1, 57 }, + { 0xa00001, 0xa00001, 222, 2669, -1, 12, 1, 62 }, + { 0x5, 0x5, 222, 2670, -1, 33, 1, 57 }, + { 0x1a00001, 0x1a00001, 222, 2671, -1, 12, 1, 52 }, + { 0xd, 0xd, 222, 2672, -1, 33, 1, 52 }, + { 0x1, 0x1, 222, 2673, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2674, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2675, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2676, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2677, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2678, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2679, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2680, -1, 35, 1, 52 }, + { 0x800001, 0x800001, 222, 2681, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2682, -1, 35, 1, 57 }, + { 0xa00001, 0x1a00001, 222, 2683, -1, 12, 1, 62 }, + { 0x5, 0xd, 222, 2684, -1, 33, 1, 57 }, + { 0x1, 0x1, 222, 2685, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2686, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2687, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2688, -1, 35, 1, 57 }, + { 0x81, 0x81, 222, 2689, -1, 28, 1, 32 }, + { 0x1, 0x1, 222, 2690, -1, 35, 1, 32 }, + { 0x103, 0x103, 222, 2691, -1, 27, 1, 32 }, + { 0x101, 0x101, 222, 2692, -1, 27, 1, 32 }, + { 0x1, 0x1, 222, 2693, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2694, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2695, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2696, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2697, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2698, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2699, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2700, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2701, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2702, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2703, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2704, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2705, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2706, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2707, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2708, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2709, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2710, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2711, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2712, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2713, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2714, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2715, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2716, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2717, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2718, -1, 35, 1, 52 }, + { 0x101, 0x101, 222, 2719, -1, 27, 1, 52 }, + { 0x81, 0x81, 222, 2720, -1, 28, 1, 52 }, + { 0x103, 0x103, 222, 2721, -1, 27, 1, 52 }, + { 0x41, 0x41, 222, 2722, -1, 29, 1, 52 }, + { 0x105, 0x105, 222, 2723, -1, 27, 1, 52 }, + { 0x83, 0x83, 222, 2724, -1, 28, 1, 52 }, + { 0x107, 0x107, 222, 2725, -1, 27, 1, 52 }, + { 0x1, 0x1, 222, 2726, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2727, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2728, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2729, -1, 35, 1, 52 }, + { 0x81, 0x81, 222, 2730, -1, 28, 1, 32 }, + { 0x1, 0x1, 222, 2731, -1, 35, 1, 32 }, + { 0x103, 0x103, 222, 2732, -1, 27, 1, 32 }, + { 0x101, 0x101, 222, 2733, -1, 27, 1, 32 }, + { 0x1, 0x1, 222, 2734, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2735, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2736, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2737, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2738, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2739, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2740, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2741, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2742, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2743, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2744, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2745, -1, 35, 1, 38 }, + { 0x1, 0x1, 222, 2746, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2747, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2748, -1, 35, 1, 32 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2243, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 48 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 44 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2964, -1, 34, 1, 58 }, + { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2965, -1, 34, 1, 58 }, + { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2966, -1, 33, 1, 58 }, + { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 }, + { 0xf, 0xf, 223, 2967, -1, 33, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2968, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2969, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2970, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, 2971, -1, 34, 1, 53 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2976, -1, 34, 1, 58 }, + { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0xf, 223, 2977, -1, 33, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2978, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2979, -1, 34, 1, 58 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2982, -1, 34, 1, 58 }, + { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2983, -1, 34, 1, 58 }, + { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2984, -1, 33, 1, 58 }, + { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 }, + { 0xf, 0xf, 223, 2985, -1, 33, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2986, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2987, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2988, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, 2989, -1, 34, 1, 53 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2994, -1, 34, 1, 58 }, + { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0xf, 223, 2995, -1, 33, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2996, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2997, -1, 34, 1, 58 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 }, + { 0x3, 0x3, 223, 2862, -1, 34, 1, 33 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 33 }, + { 0x181, 0x181, 223, 2863, -1, 27, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2244, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2245, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2248, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2249, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2251, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2253, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2254, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x181, 0x181, 223, -1, -1, 27, 1, 53 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 53 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 53 }, + { 0x61, 0x61, 223, -1, -1, 29, 1, 53 }, + { 0x185, 0x185, 223, -1, -1, 27, 1, 53 }, + { 0xc3, 0xc3, 223, -1, -1, 28, 1, 53 }, + { 0x187, 0x187, 223, -1, -1, 27, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 }, + { 0x3, 0x3, 223, 2866, -1, 34, 1, 33 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 33 }, + { 0x181, 0x181, 223, 2867, -1, 27, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 39 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2256, -1, 34, 1, 33 }, + { 0x3, 0x3, 224, 540, 1451, 32, 1, 135 }, + { 0x3, 0x3, 224, 541, 1460, 32, 1, 135 }, + { 0x3, 0x3, 224, 542, 1469, 32, 1, 135 }, + { 0x3, 0x3, 224, 543, 1482, 32, 1, 135 }, + { 0x3, 0x3, 224, 544, 1491, 32, 1, 135 }, + { 0x3, 0x3, 224, 545, 1500, 32, 1, 135 }, + { 0x3, 0x3, 224, 546, 1509, 32, 1, 135 }, + { 0x3, 0x3, 224, 547, 1518, 32, 1, 135 }, + { 0x3, 0x3, 224, 548, 1527, 32, 1, 135 }, + { 0x3, 0x3, 224, 549, 1536, 32, 1, 135 }, + { 0x3, 0x3, 224, 550, 1546, 32, 1, 135 }, + { 0x3, 0x3, 224, 551, 1556, 32, 1, 135 }, + { 0x3, 0x3, 224, 564, 1569, 32, 1, 150 }, + { 0x3, 0x3, 224, 565, 1575, 32, 1, 155 }, + { 0x3, 0x3, 224, 566, 1581, 32, 1, 155 }, + { 0x3, 0x3, 224, 567, 1587, 32, 1, 150 }, + { 0x3, 0x3, 224, 568, 1593, 32, 1, 155 }, + { 0x3, 0x3, 224, 569, 1599, 32, 1, 155 }, + { 0x3, 0x3, 224, 570, 1605, 32, 1, 150 }, + { 0x3, 0x3, 224, 571, 1611, 32, 1, 155 }, + { 0x3, 0x3, 224, 572, 1617, 32, 1, 155 }, + { 0x3, 0x3, 224, 573, 1623, 32, 1, 150 }, + { 0x3, 0x3, 224, 574, 1629, 32, 1, 155 }, + { 0x3, 0x3, 224, 575, 1635, 32, 1, 150 }, + { 0x3, 0x3, 224, 576, 1641, 32, 1, 155 }, + { 0x3, 0x3, 224, 577, 1647, 32, 1, 150 }, + { 0x3, 0x3, 224, 578, 1653, 32, 1, 155 }, + { 0x3, 0x3, 224, 579, 1659, 32, 1, 150 }, + { 0x3, 0x3, 224, 580, 1665, 32, 1, 155 }, + { 0x3, 0x3, 224, 581, 1671, 32, 1, 155 }, + { 0x1, 0x1, 225, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 225, -1, -1, 28, 1, 34 }, + { 0x0, 0x0, 232, 958, -1, 0, 1, 144 }, + { 0x0, 0x0, 232, 959, -1, 0, 1, 160 }, + { 0x1, 0x1, 233, -1, 1982, 33, 1, 140 }, + { 0x1, 0x1, 233, -1, 1985, 33, 1, 146 }, + { 0x0, 0x0, 233, -1, 1987, 0, 1, 157 }, + { 0x0, 0x0, 233, -1, 1988, 0, 1, 161 }, + { 0x0, 0x0, 234, 883, 971, 0, 0, -1 }, + { 0x0, 0x0, 234, 884, 979, 0, 0, -1 }, + { 0x0, 0x0, 234, 885, 975, 0, 0, -1 }, + { 0x1, 0x1, 234, 886, 620, 33, 1, 6 }, + { 0x8000001, 0x8000001, 234, 887, 628, 6, 1, 7 }, + { 0x1, 0x1, 234, 888, 624, 33, 1, 6 }, + { 0x0, 0x0, 234, 889, 983, 0, 0, -1 }, + { 0x1, 0x1, 234, 890, 640, 33, 1, 8 }, + { 0x0, 0x0, 234, 891, 987, 0, 0, -1 }, + { 0x1, 0x1, 234, 892, 652, 33, 1, 16 }, + { 0x0, 0x0, 234, 893, 992, 0, 0, -1 }, + { 0x0, 0x0, 234, 894, 996, 0, 0, -1 }, + { 0x1, 0x1, 234, 895, 675, 33, 1, 18 }, + { 0x1, 0x1, 234, 896, 679, 33, 1, 18 }, + { 0x0, 0x0, 234, 897, 1000, 0, 0, -1 }, + { 0x0, 0x0, 234, 898, 1004, 0, 0, -1 }, + { 0x1, 0x1, 234, 899, 699, 33, 1, 19 }, + { 0x8000001, 0x8000001, 234, 900, 703, 6, 1, 19 }, + { 0x0, 0x0, 234, 901, 1008, 0, 0, -1 }, + { 0x1, 0x1, 234, 902, 715, 33, 1, 20 }, + { 0x0, 0x0, 234, 903, 1012, 0, 0, -1 }, + { 0x0, 0x0, 234, 904, 1016, 0, 0, -1 }, + { 0x1, 0x1, 234, 905, 735, 33, 1, 21 }, + { 0x8000001, 0x8000001, 234, 906, 739, 6, 1, 21 }, + { 0x0, 0x0, 234, 907, 1020, 0, 0, -1 }, + { 0x1, 0x1, 234, 908, 751, 33, 1, 22 }, + { 0x0, 0x0, 234, 909, 1025, 0, 0, -1 }, + { 0x0, 0x0, 234, 910, 1029, 0, 0, -1 }, + { 0x1, 0x1, 234, 911, 774, 33, 1, 18 }, + { 0x1, 0x1, 234, 912, 778, 33, 1, 18 }, + { 0x0, 0x0, 234, 913, 1033, 0, 0, -1 }, + { 0x1, 0x1, 234, 914, 790, 33, 1, 22 }, + { 0x0, 0x0, 235, 2787, 970, 0, 0, -1 }, + { 0x0, 0x0, 235, 2788, 978, 0, 0, -1 }, + { 0x0, 0x0, 235, 2789, 974, 0, 0, -1 }, + { 0x0, 0x0, 235, 2790, 619, 0, 1, 6 }, + { 0x1, 0x1, 235, 2791, 627, 6, 1, 7 }, + { 0x0, 0x0, 235, 2792, 623, 0, 1, 6 }, + { 0x0, 0x0, 235, 2793, 982, 0, 0, -1 }, + { 0x0, 0x0, 235, 2794, 639, 0, 1, 8 }, + { 0x0, 0x0, 235, 2795, 986, 0, 0, -1 }, + { 0x0, 0x0, 235, 2796, 651, 0, 1, 16 }, + { 0x0, 0x0, 235, 2797, 991, 0, 0, -1 }, + { 0x0, 0x0, 235, 2798, 995, 0, 0, -1 }, + { 0x0, 0x0, 235, 2799, 674, 0, 1, 18 }, + { 0x0, 0x0, 235, 2800, 678, 0, 1, 18 }, + { 0x0, 0x0, 235, 2801, 999, 0, 0, -1 }, + { 0x0, 0x0, 235, 2802, 1003, 0, 0, -1 }, + { 0x0, 0x0, 235, 2803, 698, 0, 1, 19 }, + { 0x1, 0x1, 235, 2804, 702, 6, 1, 19 }, + { 0x0, 0x0, 235, 2805, 1007, 0, 0, -1 }, + { 0x0, 0x0, 235, 2806, 714, 0, 1, 20 }, + { 0x0, 0x0, 235, 2807, 1011, 0, 0, -1 }, + { 0x0, 0x0, 235, 2808, 1015, 0, 0, -1 }, + { 0x0, 0x0, 235, 2809, 734, 0, 1, 21 }, + { 0x1, 0x1, 235, 2810, 738, 6, 1, 21 }, + { 0x0, 0x0, 235, 2811, 1019, 0, 0, -1 }, + { 0x0, 0x0, 235, 2812, 750, 0, 1, 22 }, + { 0x0, 0x0, 235, 2813, 1024, 0, 0, -1 }, + { 0x0, 0x0, 235, 2814, 1028, 0, 0, -1 }, + { 0x0, 0x0, 235, 2815, 773, 0, 1, 18 }, + { 0x0, 0x0, 235, 2816, 777, 0, 1, 18 }, + { 0x0, 0x0, 235, 2817, 1032, 0, 0, -1 }, + { 0x0, 0x0, 235, 2818, 789, 0, 1, 22 }, + { 0x1, 0x1, 235, 915, 1155, 27, 1, 17 }, + { 0x0, 0x0, 235, 916, 1153, 0, 1, 17 }, + { 0x0, 0x0, 235, 1220, 1157, 0, 1, 23 }, + { 0x0, 0x1, 235, 1165, 1163, 20, 1, 68 }, + { 0x0, 0x0, 235, 111, 1161, 0, 1, 68 }, + { 0x1, 0x1, 238, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 238, -1, -1, 0, 1, 0 }, + { 0x1, 0x1, 238, 3022, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3023, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3024, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3025, -1, 27, 1, 0 }, + { 0x0, 0x0, 261, -1, 2344, 0, 0, -1 }, + { 0x0, 0x0, 261, -1, 2346, 0, 0, -1 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x0, 0x0, 261, -1, 2385, 0, 0, -1 }, + { 0x0, 0x0, 261, -1, 2387, 0, 0, -1 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x0, 0x0, 263, 23, -1, 0, 1, 0 }, + { 0x0, 0x0, 263, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 263, -1, -1, 0, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 263, 180, -1, 0, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 301, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 323, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 349, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 371, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x0, 0x0, 264, -1, 2296, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2298, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2300, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2302, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2304, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2306, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2308, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2310, 12, 1, 50 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 50 }, + { 0x0, 0x0, 264, -1, 2312, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2314, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2316, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2318, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x0, 0x0, 264, -1, 2320, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2322, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2324, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2326, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2328, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2330, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2332, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2334, 12, 1, 50 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 50 }, + { 0x0, 0x0, 264, -1, 2336, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2338, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2340, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2342, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, 393, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 395, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 517, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 519, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 401, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 403, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 525, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 527, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 409, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 411, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 533, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 535, -1, 12, 1, 2 }, + { 0x0, 0x0, 265, -1, 2303, 0, 0, -1 }, + { 0x9, 0x9, 265, -1, 2311, 33, 1, 50 }, + { 0x9, 0x9, 265, -1, 2975, 33, 1, 50 }, + { 0x0, 0x0, 265, 1399, 2376, 0, 0, -1 }, + { 0x3, 0x3, 265, 1400, -1, 27, 1, 50 }, + { 0x0, 0x0, 269, 2856, -1, 0, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 271, 3018, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3019, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3020, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3021, -1, 28, 1, 0 }, + { 0x1, 0x1, 273, -1, -1, 27, 1, 100 }, + { 0x1, 0x1, 273, -1, -1, 27, 1, 100 }, + { 0x0, 0x0, 273, -1, 968, 0, 0, -1 }, + { 0x0, 0x0, 274, 3031, 2833, 0, 0, -1 }, + { 0x0, 0x0, 274, 3032, 2835, 0, 0, -1 }, + { 0x0, 0x0, 275, -1, 2834, 0, 0, -1 }, + { 0x0, 0x0, 275, -1, 2836, 0, 0, -1 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 281, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 285, -1, 2350, 0, 1, 30 }, + { 0x0, 0x0, 286, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 286, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 286, 2001, 3000, 0, 1, 1 }, + { 0x0, 0x0, 286, 2002, 3001, 0, 1, 1 }, + { 0x0, 0x0, 286, -1, 518, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 520, 0, 0, -1 }, + { 0x0, 0x0, 286, 2005, 3004, 0, 1, 76 }, + { 0x0, 0x0, 286, 2006, 3005, 0, 1, 76 }, + { 0x0, 0x0, 286, -1, 526, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 528, 0, 0, -1 }, + { 0x0, 0x0, 286, 2009, 3008, 0, 1, 1 }, + { 0x0, 0x0, 286, 2010, 3009, 0, 1, 1 }, + { 0x0, 0x0, 286, -1, 534, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 536, 0, 0, -1 }, +}; + +static const struct ia64_main_table +main_table[] = { + { 5, 1, 1, 0x0000010000000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 0, }, + { 5, 1, 1, 0x0000010008000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 1, }, + { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 2, }, + { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 64, 26, 0, 0 }, 0x0, 3, }, + { 6, 1, 1, 0x0000012000000000ull, 0x000001e000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 4, }, + { 7, 1, 1, 0x0000010040000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 5, }, + { 7, 1, 1, 0x0000010c00000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 6, }, + { 8, 1, 1, 0x0000010800000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 7, }, + { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 3, 53, 54, 55 }, 0x221, 8, }, + { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 53, 54, 55, 0 }, 0x261, 9, }, + { 10, 1, 1, 0x0000010060000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 10, }, + { 10, 1, 1, 0x0000010160000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 11, }, + { 11, 1, 1, 0x0000010068000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 12, }, + { 11, 1, 1, 0x0000010168000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 13, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011ffull, { 16, 0, 0, 0, 0 }, 0x40, 969, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x0, 825, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x40, 826, }, + { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x200, 2234, }, + { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x240, 2235, }, + { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x0, 582, }, + { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x40, 583, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011ffull, { 82, 0, 0, 0, 0 }, 0x40, 990, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x0, 827, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x40, 828, }, + { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x210, 3029, }, + { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x250, 3030, }, + { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x30, 590, }, + { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x70, 591, }, + { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x230, 588, }, + { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x270, 589, }, + { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x0, 584, }, + { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x40, 585, }, + { 15, 4, 0, 0x0000000000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 537, }, + { 15, 5, 0, 0x0000000000000000ull, 0x000001e3f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 960, }, + { 15, 2, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1138, }, + { 15, 3, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1263, }, + { 15, 6, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3033, }, + { 15, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 16, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011ffull, { 83, 0, 0, 0, 0 }, 0x40, 1023, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x0, 829, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x40, 830, }, + { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x0, 586, }, + { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x40, 587, }, + { 17, 4, 0, 0x0000004080000000ull, 0x000001e9f8000018ull, { 16, 78, 0, 0, 0 }, 0x20, 2852, }, + { 17, 4, 0, 0x000000e000000000ull, 0x000001e800000018ull, { 82, 78, 0, 0, 0 }, 0x20, 2853, }, + { 18, 4, 0, 0x0000000060000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x2c, 222, }, + { 22, 2, 0, 0x0000000200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2239, }, + { 22, 3, 0, 0x0000000800000000ull, 0x000001ee00000000ull, { 24, 82, 0, 0, 0 }, 0x0, 226, }, + { 22, 3, 0, 0x0000000c00000000ull, 0x000001ee00000000ull, { 18, 82, 0, 0, 0 }, 0x0, 227, }, + { 22, 3, 0, 0x0000002200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2240, }, + { 22, 3, 0, 0x0000002600000000ull, 0x000001ee00000000ull, { 19, 81, 0, 0, 0 }, 0x0, 2241, }, + { 22, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2242, }, + { 25, 4, 0, 0x0000000020000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 18, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1222, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1223, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1181, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1182, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1090, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1091, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1052, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1053, }, + { 26, 1, 2, 0x0000018200000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1376, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1092, }, + { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1093, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1226, }, + { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1227, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1187, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1229, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1230, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1188, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1189, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1097, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1098, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1059, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1060, }, + { 26, 1, 2, 0x0000018a00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1381, }, + { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 23, 60, 26, 0 }, 0x0, 1214, }, + { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 60, 26, 0, 0 }, 0x40, 1215, }, + { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 22, 60, 26, 0 }, 0x0, 1125, }, + { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 60, 26, 0, 0 }, 0x40, 1126, }, + { 26, 1, 2, 0x000001c200000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1382, }, + { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1190, }, + { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1191, }, + { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1063, }, + { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1064, }, + { 26, 1, 2, 0x000001ca00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1383, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1235, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1236, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1194, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1195, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1103, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1104, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1065, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1066, }, + { 27, 1, 2, 0x0000018600000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1388, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1105, }, + { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1106, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1239, }, + { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1240, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1200, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1242, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1243, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1201, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1202, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1110, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1111, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1072, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1073, }, + { 27, 1, 2, 0x0000018e00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1393, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 57, 26, 0 }, 0x0, 1259, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 57, 26, 0, 0 }, 0x40, 1260, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 59, 26, 0 }, 0x0, 1218, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 59, 26, 0, 0 }, 0x40, 1219, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 59, 26, 0 }, 0x0, 1129, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 59, 26, 0, 0 }, 0x40, 1130, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 57, 26, 0 }, 0x0, 1088, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 57, 26, 0, 0 }, 0x40, 1089, }, + { 27, 1, 2, 0x000001c600000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1394, }, + { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1203, }, + { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1204, }, + { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1076, }, + { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1077, }, + { 27, 1, 2, 0x000001ce00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1395, }, + { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 1, 2 }, 0x0, 259, }, + { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 260, }, + { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 261, }, + { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 262, }, + { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 263, }, + { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 264, }, + { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 265, }, + { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 266, }, + { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 267, }, + { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 268, }, + { 34, 4, 0, 0x0000000010000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 19, }, + { 36, 2, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1167, }, + { 37, 2, 1, 0x00000000c8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1168, }, + { 39, 2, 1, 0x0000008000000000ull, 0x000001e000000000ull, { 24, 25, 26, 47, 73 }, 0x0, 20, }, + { 39, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 74, 0 }, 0x0, 3038, }, + { 39, 2, 1, 0x000000a604000000ull, 0x000001ee04000000ull, { 24, 56, 45, 74, 0 }, 0x0, 3039, }, + { 39, 2, 1, 0x000000ae00000000ull, 0x000001ee00000000ull, { 24, 48, 26, 46, 74 }, 0x0, 21, }, + { 43, 4, 0, 0x0000000080000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x20, 22, }, + { 48, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 74, 0 }, 0x0, 2870, }, + { 50, 5, 1, 0x0000000080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 24, }, + { 51, 5, 1, 0x0000010008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2291, }, + { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2292, }, + { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 26, }, + { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2293, }, + { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 27, }, + { 54, 5, 1, 0x0000000160000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 28, }, + { 55, 5, 1, 0x0000000168000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 29, }, + { 57, 3, 0, 0x0000002180000000ull, 0x000001fff8000000ull, { 26, 0, 0, 0, 0 }, 0x0, 30, }, + { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x0, 2294, }, + { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x40, 31, }, + { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 23, 19, 61, 0 }, 0x0, 1265, }, + { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 19, 61, 0, 0 }, 0x40, 1266, }, + { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 22, 19, 61, 0 }, 0x40, 1420, }, + { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 19, 61, 0, 0 }, 0x40, 1421, }, + { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 2295, }, + { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x40, 32, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x0, 943, }, + { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 19, 20, 0, 0 }, 0x40, 944, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x40, 945, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x0, 1116, }, + { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 20, 19, 0, 0 }, 0x40, 1117, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x40, 1118, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x0, 1396, }, + { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 19, 20, 0, 0 }, 0x40, 1397, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x40, 1398, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x0, 1405, }, + { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 20, 19, 0, 0 }, 0x40, 1406, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x40, 1407, }, + { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1042, }, + { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1043, }, + { 62, 5, 1, 0x00000000e0000000ull, 0x000001e3f8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 3036, }, + { 62, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 3037, }, + { 63, 3, 1, 0x0000008488000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 269, }, + { 64, 3, 1, 0x00000084c8000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 270, }, + { 67, 3, 0, 0x0000000060000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 33, }, + { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2353, }, + { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 34, }, + { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2354, }, + { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 35, }, + { 70, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2247, }, + { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2355, }, + { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 36, }, + { 72, 5, 1, 0x00000001c8000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 1221, }, + { 73, 5, 1, 0x0000010000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2358, }, + { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2361, }, + { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 38, }, + { 75, 5, 1, 0x0000000088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 39, }, + { 76, 5, 1, 0x0000000088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 40, }, + { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2364, }, + { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 41, }, + { 78, 5, 1, 0x0000018000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2367, }, + { 79, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 2370, }, + { 80, 5, 1, 0x0000000170000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 44, }, + { 81, 5, 1, 0x0000002080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 45, }, + { 82, 5, 1, 0x0000000140000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 46, }, + { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2371, }, + { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 47, }, + { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2372, }, + { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 48, }, + { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 946, }, + { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 947, }, + { 85, 5, 1, 0x0000002188000000ull, 0x000001eff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 1119, }, + { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1044, }, + { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1045, }, + { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2389, }, + { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 49, }, + { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2390, }, + { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 50, }, + { 89, 5, 1, 0x0000002080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2255, }, + { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2391, }, + { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 51, }, + { 91, 5, 1, 0x0000013000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2392, }, + { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2393, }, + { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 53, }, + { 93, 5, 1, 0x0000002088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 54, }, + { 94, 5, 1, 0x0000002088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 55, }, + { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2394, }, + { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 56, }, + { 96, 5, 1, 0x000001b000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2395, }, + { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2396, }, + { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 58, }, + { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2397, }, + { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 59, }, + { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2398, }, + { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 60, }, + { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2399, }, + { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 61, }, + { 101, 5, 1, 0x000001c000000000ull, 0x000001f000000000ull, { 18, 20, 21, 19, 0 }, 0x0, 62, }, + { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x0, 2400, }, + { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x40, 63, }, + { 103, 5, 1, 0x0000014008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2403, }, + { 104, 5, 1, 0x00000001a0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 65, }, + { 105, 5, 1, 0x00000001e0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2202, }, + { 106, 3, 0, 0x0000000100000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 66, }, + { 108, 5, 1, 0x0000000178000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 67, }, + { 113, 3, 1, 0x0000008708000000ull, 0x000001ffc8000000ull, { 24, 19, 0, 0, 0 }, 0x0, 2781, }, + { 118, 4, 0, 0x0000004008000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 538, }, + { 118, 5, 0, 0x000000000c000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 961, }, + { 118, 2, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1141, }, + { 118, 3, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1267, }, + { 118, 6, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3034, }, + { 118, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 68, }, + { 123, 3, 0, 0x0000000080000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 69, }, + { 123, 3, 0, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 0, 0, 0, 0 }, 0x0, 920, }, + { 123, 3, 0, 0x0000000098000000ull, 0x000001eff8000000ull, { 18, 0, 0, 0, 0 }, 0x0, 921, }, + { 124, 3, 0, 0x0000002170000000ull, 0x000001eff8000000ull, { 25, 0, 0, 0, 0 }, 0xc, 846, }, + { 125, 3, 1, 0x0000002070000000ull, 0x000001eff8000000ull, { 31, 25, 0, 0, 0 }, 0x8, 847, }, + { 125, 3, 1, 0x0000002078000000ull, 0x000001eff8000000ull, { 32, 25, 0, 0, 0 }, 0x8, 1143, }, + { 127, 3, 1, 0x0000008000000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 70, }, + { 127, 3, 1, 0x0000009000000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 71, }, + { 127, 3, 1, 0x000000a000000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 72, }, + { 128, 3, 2, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 1, 28, 0, 0 }, 0x0, 73, }, + { 128, 3, 1, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x40, 74, }, + { 129, 3, 1, 0x0000008040000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 75, }, + { 129, 3, 1, 0x0000009040000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 76, }, + { 129, 3, 1, 0x000000a040000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 77, }, + { 130, 3, 1, 0x0000008080000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 78, }, + { 130, 3, 1, 0x0000009080000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 79, }, + { 130, 3, 1, 0x000000a080000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 80, }, + { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 81, }, + { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 84, 0, 0 }, 0x0, 1339, }, + { 131, 3, 1, 0x00000090c0000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 82, }, + { 131, 3, 1, 0x000000a0c0000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 83, }, + { 132, 3, 1, 0x000000c6c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 1039, }, + { 132, 3, 1, 0x000000d6c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 1040, }, + { 132, 3, 1, 0x000000e6c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 1041, }, + { 133, 3, 1, 0x000000c040000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 84, }, + { 133, 3, 1, 0x000000d040000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 85, }, + { 133, 3, 1, 0x000000e040000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 86, }, + { 134, 3, 1, 0x000000c0c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 87, }, + { 134, 3, 1, 0x000000d0c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 88, }, + { 134, 3, 1, 0x000000e0c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 89, }, + { 135, 3, 1, 0x000000c000000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 90, }, + { 135, 3, 1, 0x000000d000000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 91, }, + { 135, 3, 1, 0x000000e000000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 92, }, + { 136, 3, 2, 0x000000c048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 93, }, + { 136, 3, 2, 0x000000d048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 94, }, + { 137, 3, 2, 0x000000c0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 95, }, + { 137, 3, 2, 0x000000d0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 96, }, + { 138, 3, 2, 0x000000c088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 97, }, + { 138, 3, 2, 0x000000d088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 5, 0 }, 0x400, 98, }, + { 139, 3, 1, 0x000000c080000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 99, }, + { 139, 3, 1, 0x000000d080000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 100, }, + { 139, 3, 1, 0x000000e080000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 101, }, + { 142, 3, 0, 0x000000cb00000000ull, 0x000001fff8000000ull, { 28, 0, 0, 0, 0 }, 0x0, 102, }, + { 142, 3, 0, 0x000000db00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x400, 103, }, + { 142, 3, 0, 0x000000eb00000000ull, 0x000001eff0000000ull, { 28, 63, 0, 0, 0 }, 0x400, 104, }, + { 143, 3, 0, 0x0000000050000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 105, }, + { 151, 3, 0, 0x0000000110000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 106, }, + { 152, 2, 1, 0x000000e880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2203, }, + { 153, 2, 1, 0x000000ea80000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2204, }, + { 154, 2, 1, 0x000000f880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2205, }, + { 155, 1, 1, 0x0000010800000000ull, 0x000001fff80fe000ull, { 24, 26, 0, 0, 0 }, 0x0, 107, }, + { 155, 1, 1, 0x0000012000000000ull, 0x000001e000300000ull, { 24, 67, 0, 0, 0 }, 0x40, 108, }, + { 155, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 109, }, + { 155, 2, 1, 0x0000000e00100000ull, 0x000001ee00f00000ull, { 15, 25, 0, 0, 0 }, 0x40, 110, }, + { 155, 2, 1, 0x0000000e00000000ull, 0x000001ee00f00000ull, { 15, 25, 79, 0, 0 }, 0x0, 2855, }, + { 155, 2, 1, 0x0000000188000000ull, 0x000001eff8000000ull, { 24, 16, 0, 0, 0 }, 0x0, 112, }, + { 155, 2, 1, 0x0000000600000000ull, 0x000001ee00000000ull, { 9, 25, 65, 0, 0 }, 0x0, 113, }, + { 155, 2, 1, 0x00000016ff001fc0ull, 0x000001feff001fc0ull, { 9, 25, 0, 0, 0 }, 0x40, 114, }, + { 155, 2, 1, 0x0000000400000000ull, 0x000001ee00000000ull, { 10, 69, 0, 0, 0 }, 0x0, 115, }, + { 155, 2, 1, 0x0000000180000000ull, 0x000001eff8000000ull, { 24, 8, 0, 0, 0 }, 0x0, 116, }, + { 155, 2, 1, 0x0000000198000000ull, 0x000001eff8000000ull, { 24, 9, 0, 0, 0 }, 0x0, 117, }, + { 155, 2, 1, 0x0000000150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1144, }, + { 155, 2, 1, 0x0000000050000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1145, }, + { 155, 2, 1, 0x0000000190000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1146, }, + { 155, 3, 1, 0x0000000140000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1268, }, + { 155, 3, 1, 0x0000002150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1269, }, + { 155, 3, 1, 0x0000002110000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1270, }, + { 155, 3, 1, 0x0000002160000000ull, 0x000001eff8000000ull, { 17, 25, 0, 0, 0 }, 0x8, 118, }, + { 155, 3, 1, 0x0000002120000000ull, 0x000001eff8000000ull, { 24, 17, 0, 0, 0 }, 0x8, 119, }, + { 155, 3, 1, 0x0000002168000000ull, 0x000001eff8000000ull, { 12, 25, 0, 0, 0 }, 0x8, 120, }, + { 155, 3, 1, 0x0000002148000000ull, 0x000001eff8000000ull, { 13, 25, 0, 0, 0 }, 0x0, 121, }, + { 155, 3, 1, 0x0000002128000000ull, 0x000001eff8000000ull, { 24, 11, 0, 0, 0 }, 0x8, 122, }, + { 155, 3, 1, 0x0000002108000000ull, 0x000001eff8000000ull, { 24, 13, 0, 0, 0 }, 0x0, 123, }, + { 155, 3, 1, 0x0000002000000000ull, 0x000001eff8000000ull, { 38, 25, 0, 0, 0 }, 0x8, 124, }, + { 155, 3, 1, 0x0000002008000000ull, 0x000001eff8000000ull, { 30, 25, 0, 0, 0 }, 0x8, 125, }, + { 155, 3, 1, 0x0000002010000000ull, 0x000001eff8000000ull, { 33, 25, 0, 0, 0 }, 0x8, 126, }, + { 155, 3, 1, 0x0000002018000000ull, 0x000001eff8000000ull, { 35, 25, 0, 0, 0 }, 0x8, 127, }, + { 155, 3, 1, 0x0000002020000000ull, 0x000001eff8000000ull, { 36, 25, 0, 0, 0 }, 0x8, 128, }, + { 155, 3, 1, 0x0000002028000000ull, 0x000001eff8000000ull, { 37, 25, 0, 0, 0 }, 0x8, 129, }, + { 155, 3, 1, 0x0000002030000000ull, 0x000001eff8000000ull, { 34, 25, 0, 0, 0 }, 0x8, 130, }, + { 155, 3, 1, 0x0000002080000000ull, 0x000001eff8000000ull, { 24, 38, 0, 0, 0 }, 0x8, 131, }, + { 155, 3, 1, 0x0000002088000000ull, 0x000001eff8000000ull, { 24, 30, 0, 0, 0 }, 0x8, 132, }, + { 155, 3, 1, 0x0000002090000000ull, 0x000001eff8000000ull, { 24, 33, 0, 0, 0 }, 0x8, 133, }, + { 155, 3, 1, 0x0000002098000000ull, 0x000001eff8000000ull, { 24, 35, 0, 0, 0 }, 0x8, 134, }, + { 155, 3, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 24, 36, 0, 0, 0 }, 0x8, 135, }, + { 155, 3, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 24, 37, 0, 0, 0 }, 0x0, 136, }, + { 155, 3, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 24, 34, 0, 0, 0 }, 0x8, 137, }, + { 155, 3, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 24, 29, 0, 0, 0 }, 0x0, 138, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 14, 0, 0, 0 }, 0x0, 139, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 56, 0, 0, 0 }, 0x0, 140, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 25, 0, 0, 0 }, 0x0, 141, }, + { 156, 6, 1, 0x000000c000000000ull, 0x000001e000100000ull, { 24, 71, 0, 0, 0 }, 0x0, 142, }, + { 157, 2, 1, 0x000000eca0000000ull, 0x000001fff0000000ull, { 24, 25, 75, 0, 0 }, 0x0, 143, }, + { 158, 2, 1, 0x000000eea0000000ull, 0x000001fff0000000ull, { 24, 25, 76, 0, 0 }, 0x0, 144, }, + { 168, 4, 0, 0x0000004000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 539, }, + { 168, 5, 0, 0x0000000008000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 962, }, + { 168, 2, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1147, }, + { 168, 3, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1271, }, + { 168, 6, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3035, }, + { 168, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 145, }, + { 175, 1, 1, 0x0000010070000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 146, }, + { 175, 1, 1, 0x0000010170000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 147, }, + { 178, 2, 1, 0x000000ea00000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 3017, }, + { 179, 2, 1, 0x000000f820000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2857, }, + { 180, 1, 1, 0x0000010400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 148, }, + { 181, 1, 1, 0x0000010600000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 149, }, + { 182, 1, 1, 0x0000011400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 150, }, + { 183, 1, 1, 0x0000010450000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 151, }, + { 184, 1, 1, 0x0000010650000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 152, }, + { 185, 1, 1, 0x0000010470000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 153, }, + { 186, 1, 1, 0x0000010670000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 154, }, + { 187, 1, 1, 0x0000010520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 948, }, + { 188, 1, 1, 0x0000010720000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 949, }, + { 189, 1, 1, 0x0000011520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 950, }, + { 190, 2, 1, 0x000000e850000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2871, }, + { 191, 2, 1, 0x000000ea70000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 155, }, + { 192, 2, 1, 0x000000e810000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2872, }, + { 193, 2, 1, 0x000000ea30000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 156, }, + { 194, 2, 1, 0x000000ead0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2206, }, + { 195, 2, 1, 0x000000e230000000ull, 0x000001ff30000000ull, { 24, 25, 26, 42, 0 }, 0x0, 157, }, + { 196, 2, 1, 0x000000e690000000ull, 0x000001fff0000000ull, { 24, 26, 0, 0, 0 }, 0x0, 158, }, + { 198, 3, 1, 0x00000021c0000000ull, 0x000001eff8000000ull, { 24, 26, 25, 0, 0 }, 0x0, 2207, }, + { 198, 3, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 24, 26, 49, 0, 0 }, 0x0, 2208, }, + { 198, 3, 0, 0x0000002188000000ull, 0x000001eff8000000ull, { 26, 49, 0, 0, 0 }, 0x0, 2238, }, + { 199, 2, 1, 0x000000e8b0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 159, }, + { 200, 2, 1, 0x000000e240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 160, }, + { 200, 2, 1, 0x000000ee50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 161, }, + { 201, 2, 1, 0x000000f040000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 162, }, + { 201, 2, 1, 0x000000fc50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 163, }, + { 202, 1, 1, 0x0000010680000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 164, }, + { 203, 2, 1, 0x000000e220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 165, }, + { 203, 2, 1, 0x000000e630000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 166, }, + { 204, 2, 1, 0x000000f020000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 167, }, + { 204, 2, 1, 0x000000f430000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 168, }, + { 205, 1, 1, 0x00000106c0000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 169, }, + { 206, 1, 1, 0x0000010420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 170, }, + { 207, 1, 1, 0x0000010620000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 171, }, + { 208, 1, 1, 0x0000011420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 172, }, + { 209, 3, 0, 0x0000002048000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 1175, }, + { 209, 3, 0, 0x0000002050000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0xc, 1050, }, + { 209, 3, 0, 0x00000021a0000000ull, 0x000001eff8000000ull, { 26, 0, 0, 0, 0 }, 0x8, 922, }, + { 210, 3, 0, 0x0000002060000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 848, }, + { 215, 4, 0, 0x0000000040000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x22c, 173, }, + { 216, 3, 0, 0x0000000038000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 174, }, + { 217, 3, 0, 0x0000000028000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 175, }, + { 226, 3, 1, 0x000000c708000000ull, 0x000001ffc8000000ull, { 18, 25, 0, 0, 0 }, 0x0, 2782, }, + { 227, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 0, 0 }, 0x140, 176, }, + { 227, 2, 1, 0x000000f240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 177, }, + { 228, 1, 1, 0x0000010080000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 178, }, + { 229, 1, 1, 0x00000100c0000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 179, }, + { 230, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 0, 0 }, 0x140, 2878, }, + { 230, 2, 1, 0x000000f220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 181, }, + { 231, 2, 1, 0x000000ac00000000ull, 0x000001ee00000000ull, { 24, 25, 26, 44, 0 }, 0x0, 182, }, + { 236, 3, 0, 0x0000000180000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 850, }, + { 237, 3, 0, 0x0000000030000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 183, }, + { 239, 3, 1, 0x0000008c00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 184, }, + { 239, 3, 1, 0x000000ac00000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 185, }, + { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 1, 0, 0 }, 0x0, 186, }, + { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x40, 187, }, + { 241, 3, 1, 0x0000008c40000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 188, }, + { 241, 3, 1, 0x000000ac40000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 189, }, + { 242, 3, 1, 0x0000008c80000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 190, }, + { 242, 3, 1, 0x000000ac80000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 191, }, + { 243, 3, 1, 0x0000008cc0000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 192, }, + { 243, 3, 1, 0x000000acc0000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 193, }, + { 244, 3, 1, 0x000000cec0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 2785, }, + { 244, 3, 1, 0x000000eec0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 2786, }, + { 245, 3, 1, 0x000000cc40000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 194, }, + { 245, 3, 1, 0x000000ec40000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 195, }, + { 246, 3, 1, 0x000000ccc0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 196, }, + { 246, 3, 1, 0x000000ecc0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 197, }, + { 247, 3, 1, 0x000000cc00000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 198, }, + { 247, 3, 1, 0x000000ec00000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 199, }, + { 248, 3, 1, 0x000000cc80000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 200, }, + { 248, 3, 1, 0x000000ec80000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 201, }, + { 249, 1, 1, 0x0000010028000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 202, }, + { 249, 1, 1, 0x0000010020000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 203, }, + { 249, 1, 1, 0x0000010128000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 204, }, + { 250, 3, 0, 0x0000000020000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 205, }, + { 251, 2, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 206, }, + { 252, 2, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 207, }, + { 253, 2, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 208, }, + { 254, 3, 0, 0x0000000198000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 1150, }, + { 255, 3, 1, 0x00000020f8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 209, }, + { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 23, 26, 77, 0 }, 0x0, 3040, }, + { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 26, 77, 0, 0 }, 0x40, 3041, }, + { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 22, 26, 77, 0 }, 0x40, 2003, }, + { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 26, 77, 0, 0 }, 0x40, 2004, }, + { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 23, 50, 0, 0 }, 0x0, 3044, }, + { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 50, 0, 0, 0 }, 0x40, 3045, }, + { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 22, 50, 0, 0 }, 0x40, 2007, }, + { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 50, 0, 0, 0 }, 0x40, 2008, }, + { 258, 3, 1, 0x00000020d0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 210, }, + { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 23, 26, 0, 0 }, 0x0, 3048, }, + { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 26, 0, 0, 0 }, 0x40, 3049, }, + { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 22, 26, 0, 0 }, 0x40, 2011, }, + { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 26, 0, 0, 0 }, 0x40, 2012, }, + { 260, 3, 1, 0x00000020f0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 211, }, + { 262, 3, 1, 0x00000020d8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 212, }, + { 266, 2, 1, 0x000000e840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1131, }, + { 267, 2, 1, 0x000000ea40000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1132, }, + { 268, 2, 1, 0x000000f840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1133, }, + { 272, 4, 0, 0x00000000c0000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x28, 223, }, + { 277, 3, 1, 0x0000008208000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 213, }, + { 278, 3, 1, 0x0000008248000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 214, }, + { 279, 3, 1, 0x0000008288000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 215, }, + { 280, 3, 1, 0x00000082c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 216, }, + { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 1179, }, + { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 1261, }, + { 283, 5, 1, 0x000001d000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 1180, }, + { 284, 1, 1, 0x0000010078000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 217, }, + { 284, 1, 1, 0x0000010178000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 218, }, + { 287, 2, 1, 0x0000000080000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 219, }, + { 288, 2, 1, 0x0000000088000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 220, }, + { 289, 2, 1, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 221, }, +}; + +static const char dis_table[] = { +0xa0, 0xc7, 0xc8, 0xa0, 0x2e, 0xd8, 0xa0, 0x2c, 0xc0, 0xa0, 0x1c, 0x00, +0x98, 0xb0, 0x02, 0x50, 0x90, 0x50, 0x90, 0x28, 0x24, 0x39, 0x28, 0x24, +0x39, 0x20, 0x90, 0x28, 0x24, 0x39, 0x18, 0x24, 0x39, 0x10, 0x91, 0x60, +0x90, 0x28, 0x24, 0x39, 0x00, 0x10, 0x10, 0x58, 0x41, 0x61, 0xc7, 0xc0, +0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +0x10, 0x10, 0x52, 0xc0, 0xc0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +0x10, 0x10, 0x10, 0x24, 0x24, 0x70, 0x90, 0x28, 0x24, 0x38, 0xf0, 0x24, +0x38, 0xe8, 0xa8, 0x0b, 0x48, 0x15, 0x20, 0x97, 0x20, 0x95, 0xc8, 0x9a, +0xb8, 0x05, 0x38, 0x91, 0x18, 0x90, 0xa0, 0x90, 0x60, 0x80, 0x90, 0x20, +0x34, 0xa6, 0xa4, 0x25, 0x00, 0x34, 0xa3, 0x80, 0xa4, 0x36, 0xa0, 0x36, +0xd9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x36, 0xcf, 0x80, 0x34, 0x86, 0x81, +0x33, 0xe2, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x24, 0x10, 0x34, +0x83, 0xa4, 0x1f, 0x08, 0x34, 0x80, 0x90, 0x38, 0xa4, 0x38, 0xa0, 0x37, +0x1a, 0xa4, 0x38, 0x48, 0x37, 0x0e, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x37, +0x20, 0x36, 0xef, 0xa4, 0x36, 0xf8, 0x36, 0xea, 0x80, 0xa4, 0x23, 0xf0, +0x34, 0x7f, 0x92, 0x18, 0x91, 0xc0, 0x80, 0x91, 0x80, 0x90, 0xf8, 0xdb, +0x84, 0x60, 0xf9, 0x40, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x68, 0x8c, 0x43, +0xc8, 0x84, 0x38, 0x83, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x58, 0x8c, 0x43, +0xa8, 0x84, 0x38, 0x81, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, +0x35, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x33, 0xa4, 0x1f, 0x18, 0x33, 0xe4, +0x80, 0x90, 0x28, 0x80, 0x33, 0xe0, 0x80, 0x34, 0x88, 0x81, 0x90, 0x38, +0xa4, 0x24, 0x80, 0x34, 0x8b, 0xa4, 0x24, 0x48, 0x34, 0x85, 0xc0, 0x40, +0x10, 0x10, 0x90, 0x38, 0xa4, 0x1e, 0xf0, 0x33, 0xdf, 0xa4, 0x1e, 0xe0, +0x33, 0xdd, 0x18, 0x24, 0x24, 0xf8, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, +0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x38, 0x38, 0x6d, 0xc0, 0xc0, 0x80, 0xa4, +0x42, 0x28, 0x38, 0x69, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, +0x2f, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2d, 0x92, 0xb8, 0x99, 0x84, 0x24, +0x68, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x98, 0x36, +0xd8, 0x82, 0x36, 0xce, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, +0x98, 0x37, 0x19, 0xa4, 0x38, 0x40, 0x37, 0x0d, 0x80, 0x90, 0x38, 0xa4, +0x37, 0x18, 0x36, 0xee, 0xa4, 0x36, 0xf0, 0x36, 0xe9, 0x83, 0x90, 0xa8, +0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x08, 0x38, 0x61, 0xc0, +0xc0, 0x80, 0xa4, 0x41, 0xf8, 0x38, 0x5d, 0xd3, 0x82, 0x40, 0x50, 0xc0, +0xc0, 0x81, 0x38, 0x29, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x27, 0x18, 0x24, +0x24, 0x78, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, +0x41, 0xd8, 0x38, 0x55, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xc8, 0x38, 0x51, +0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x23, 0x50, 0xc0, 0xc0, +0x81, 0x38, 0x21, 0x94, 0x50, 0x92, 0xf8, 0x99, 0x84, 0x1f, 0x48, 0x90, +0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x90, 0x36, 0xd7, 0x82, +0x36, 0xcd, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, 0x90, 0x37, +0x18, 0xa4, 0x38, 0x38, 0x37, 0x0c, 0x80, 0x90, 0x38, 0xa4, 0x37, 0x10, +0x36, 0xed, 0xa4, 0x36, 0xe8, 0x36, 0xe8, 0x83, 0x90, 0xe8, 0xd3, 0x83, +0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x78, 0x8c, 0x43, 0xe8, 0x84, 0x38, +0x85, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x60, 0x8c, 0x43, 0xb8, 0x84, 0x38, +0x82, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x37, 0x50, 0xc0, +0xc0, 0x81, 0x38, 0x34, 0x18, 0x24, 0x1f, 0x40, 0x83, 0x90, 0xa8, 0xd3, +0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x48, 0x38, 0x71, 0xc0, 0xc0, +0x80, 0xa4, 0x42, 0x30, 0x38, 0x6b, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, +0x81, 0x38, 0x31, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2e, 0x92, 0xb8, 0x99, +0x84, 0x1f, 0x38, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, +0x88, 0x36, 0xd6, 0x82, 0x36, 0xcc, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, +0xa4, 0x38, 0x88, 0x37, 0x17, 0xa4, 0x38, 0x30, 0x37, 0x0b, 0x80, 0x90, +0x38, 0xa4, 0x37, 0x08, 0x36, 0xec, 0xa4, 0x36, 0xe0, 0x36, 0xe7, 0x83, +0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x18, 0x38, +0x65, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x00, 0x38, 0x5f, 0xd3, 0x82, 0x40, +0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2b, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x28, +0x18, 0x20, 0x01, 0x48, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, +0x80, 0xa4, 0x41, 0xe8, 0x38, 0x59, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xd0, +0x38, 0x53, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x25, 0x50, +0xc0, 0xc0, 0x81, 0x38, 0x22, 0xda, 0x06, 0xe0, 0xf9, 0x80, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x24, 0xe8, 0x34, 0x9b, 0x80, 0x34, 0x98, 0x90, 0x38, +0xa4, 0x24, 0x90, 0x34, 0x96, 0x80, 0x34, 0x93, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x24, 0xd0, 0x34, 0x9c, 0x80, 0x34, 0x99, 0x90, 0x38, 0xa4, 0x24, +0xa8, 0x34, 0x97, 0x80, 0x34, 0x94, 0xc8, 0x40, 0x19, 0x00, 0x91, 0x58, +0x90, 0x60, 0x82, 0x90, 0x20, 0x36, 0xcb, 0xa4, 0x36, 0x48, 0x36, 0xca, +0x90, 0xc0, 0x80, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0xc1, 0x00, 0x85, +0x37, 0x03, 0xc9, 0xe1, 0xc0, 0x40, 0x85, 0x37, 0x00, 0x80, 0x36, 0xff, +0x10, 0x10, 0x81, 0x36, 0xdb, 0x90, 0xa8, 0x10, 0x10, 0x90, 0x28, 0x81, +0x36, 0xf9, 0x90, 0x38, 0xa4, 0x37, 0xa0, 0x36, 0xf5, 0xa4, 0x37, 0x90, +0x36, 0xf3, 0x90, 0x70, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x37, 0xb8, 0x36, +0xf8, 0x80, 0x36, 0xf6, 0x90, 0x60, 0x90, 0x28, 0x24, 0x37, 0xf0, 0xa4, +0x37, 0xe0, 0x36, 0xfd, 0x80, 0xa4, 0x37, 0xd0, 0x36, 0xfb, 0x80, 0x90, +0xf8, 0x90, 0x90, 0x90, 0x50, 0x90, 0x28, 0x80, 0x38, 0x17, 0x80, 0x38, +0x20, 0x80, 0xa4, 0x40, 0xf0, 0x38, 0x1f, 0x90, 0x28, 0x81, 0x38, 0x1d, +0x80, 0xa4, 0x40, 0xd8, 0x38, 0x1c, 0x90, 0x28, 0x82, 0x38, 0x1a, 0x81, +0xa4, 0x40, 0xc0, 0x38, 0x19, 0x98, 0xe8, 0x01, 0xb0, 0x90, 0x88, 0x90, +0x60, 0xa4, 0x36, 0x38, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb7, 0x24, +0x36, 0x30, 0x90, 0x28, 0x24, 0x36, 0x28, 0x24, 0x36, 0x20, 0x90, 0x88, +0x90, 0x60, 0xa4, 0x36, 0x10, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb6, +0x24, 0x36, 0x08, 0x90, 0x28, 0x24, 0x36, 0x00, 0x24, 0x35, 0xf8, 0xa8, +0x09, 0x00, 0x0e, 0x20, 0x96, 0x48, 0x95, 0xe8, 0x93, 0x38, 0x91, 0xa0, +0x90, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x60, 0x33, 0xcd, 0xa4, +0x1e, 0x50, 0x33, 0xcb, 0x90, 0x38, 0xa4, 0x1e, 0x40, 0x33, 0xc9, 0x80, +0x33, 0xc7, 0x90, 0x60, 0x90, 0x28, 0x24, 0x1e, 0x00, 0xa4, 0x1d, 0xf0, +0x33, 0xbf, 0x90, 0x38, 0xa4, 0x1d, 0xe0, 0x33, 0xbd, 0xa4, 0x1e, 0x28, +0x33, 0xc6, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x18, 0x33, +0xc4, 0xa4, 0x1e, 0x08, 0x33, 0xc2, 0x90, 0x38, 0xa4, 0x35, 0xb0, 0x36, +0xbc, 0xa4, 0x35, 0x50, 0x36, 0xb0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x32, +0x90, 0x36, 0x5e, 0xa4, 0x32, 0x60, 0x36, 0x58, 0x10, 0x10, 0xa4, 0x1d, +0xd0, 0x33, 0xbb, 0x99, 0x60, 0x02, 0x70, 0x90, 0x90, 0x90, 0x50, 0x90, +0x28, 0x24, 0x1e, 0x90, 0x80, 0x33, 0xda, 0x80, 0xa4, 0x1e, 0x98, 0x33, +0xd8, 0x90, 0x50, 0x90, 0x28, 0x24, 0x1e, 0xa0, 0x80, 0x33, 0xdb, 0x90, +0x38, 0xa4, 0x1e, 0xa8, 0x33, 0xd9, 0xa4, 0x1e, 0x70, 0x33, 0xcf, 0x90, +0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xe8, 0x36, 0xa5, 0xa4, 0x34, +0x48, 0x36, 0x92, 0x90, 0x38, 0xa4, 0x33, 0xe0, 0x36, 0x83, 0xa4, 0x33, +0x50, 0x36, 0x72, 0x81, 0xa4, 0x1e, 0x80, 0x33, 0xd1, 0xe4, 0xa2, 0x04, +0x40, 0x38, 0x13, 0x18, 0x24, 0x1d, 0xc8, 0xe4, 0xe2, 0x02, 0xc0, 0x38, +0x0d, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, +0x38, 0xa4, 0x35, 0xa8, 0x36, 0xbb, 0xa4, 0x35, 0x48, 0x36, 0xaf, 0x80, +0x90, 0x38, 0xa4, 0x32, 0x88, 0x36, 0x5d, 0xa4, 0x32, 0x58, 0x36, 0x57, +0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xd8, +0x36, 0xa4, 0xa4, 0x34, 0x40, 0x36, 0x90, 0x90, 0x38, 0xa4, 0x33, 0xd0, +0x36, 0x82, 0xa4, 0x33, 0x48, 0x36, 0x70, 0xe4, 0xa2, 0x01, 0x40, 0x38, +0x07, 0x18, 0x24, 0x1d, 0xc0, 0xe4, 0xe1, 0xff, 0xc0, 0x38, 0x01, 0x92, +0x90, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, +0x38, 0xa4, 0x35, 0xa0, 0x36, 0xba, 0xa4, 0x35, 0x40, 0x36, 0xae, 0x80, +0x90, 0x38, 0xa4, 0x32, 0x80, 0x36, 0x5c, 0xa4, 0x32, 0x50, 0x36, 0x56, +0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xc8, +0x36, 0xa3, 0xa4, 0x34, 0x38, 0x36, 0x8e, 0x90, 0x38, 0xa4, 0x33, 0xc0, +0x36, 0x81, 0xa4, 0x33, 0x40, 0x36, 0x6e, 0xe4, 0xa2, 0x04, 0x80, 0x38, +0x15, 0x10, 0x10, 0xe4, 0xe2, 0x03, 0x00, 0x38, 0x0f, 0x92, 0x50, 0x99, +0x1c, 0x1e, 0xb0, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, +0x35, 0x98, 0x36, 0xb9, 0xa4, 0x35, 0x38, 0x36, 0xad, 0x80, 0x90, 0x38, +0xa4, 0x32, 0x78, 0x36, 0x5b, 0xa4, 0x32, 0x48, 0x36, 0x55, 0x18, 0x20, +0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xb8, 0x36, 0xa2, +0xa4, 0x34, 0x30, 0x36, 0x8c, 0x90, 0x38, 0xa4, 0x33, 0xb0, 0x36, 0x80, +0xa4, 0x33, 0x38, 0x36, 0x6c, 0xe4, 0xa2, 0x01, 0x80, 0x38, 0x09, 0x10, +0x10, 0xe4, 0xe2, 0x00, 0x00, 0x38, 0x03, 0xc0, 0x40, 0x80, 0x10, 0x10, +0x81, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0x98, 0x80, 0x85, 0x36, 0x66, +0xc9, 0xe1, 0x99, 0x00, 0x85, 0x36, 0x63, 0x80, 0x36, 0x61, 0x80, 0xd8, +0x47, 0x80, 0x0d, 0xc0, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, 0xd5, +0x81, 0x80, 0x80, 0x37, 0xfd, 0x80, 0x37, 0xfb, 0xd5, 0x81, 0x80, 0x80, +0x37, 0xf9, 0x80, 0x37, 0xf7, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, +0xd5, 0x81, 0x80, 0x80, 0x37, 0xfe, 0x80, 0x37, 0xfc, 0xd5, 0x81, 0x80, +0x80, 0x37, 0xfa, 0x80, 0x37, 0xf8, 0xc0, 0x80, 0x83, 0xa4, 0x3f, 0xa8, +0x37, 0xf6, 0xa0, 0x59, 0x60, 0xa0, 0x41, 0xe0, 0xa8, 0x1e, 0xb0, 0x34, +0x88, 0xa0, 0x12, 0x38, 0xa0, 0x0b, 0x48, 0x96, 0x00, 0x9a, 0xf0, 0x05, +0xc0, 0x91, 0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x15, 0x58, +0x33, 0xb5, 0xa4, 0x15, 0x78, 0x33, 0xb4, 0x10, 0x10, 0xa4, 0x15, 0x68, +0x33, 0xb3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0xf8, 0x33, 0x9a, 0xa4, +0x15, 0x18, 0x33, 0x99, 0x10, 0x10, 0xa4, 0x15, 0x08, 0x33, 0x98, 0x90, +0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x98, 0x33, 0x7f, 0xa4, 0x14, +0xb8, 0x33, 0x7e, 0x10, 0x10, 0xa4, 0x14, 0xa8, 0x33, 0x7d, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x14, 0x38, 0x33, 0x63, 0xa4, 0x14, 0x58, 0x33, 0x62, +0x10, 0x10, 0xa4, 0x14, 0x48, 0x33, 0x61, 0x91, 0x70, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x15, 0x28, 0x33, 0xb0, 0xa4, 0x15, 0x48, 0x33, +0xb2, 0x10, 0x10, 0xa4, 0x15, 0x38, 0x33, 0xb1, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x14, 0xc8, 0x33, 0x95, 0xa4, 0x14, 0xe8, 0x33, 0x97, 0x10, 0x10, +0xa4, 0x14, 0xd8, 0x33, 0x96, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x14, 0x68, 0x33, 0x7a, 0xa4, 0x14, 0x88, 0x33, 0x7c, 0x10, 0x10, 0xa4, +0x14, 0x78, 0x33, 0x7b, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x08, 0x33, +0x5e, 0xa4, 0x14, 0x28, 0x33, 0x60, 0x10, 0x10, 0xa4, 0x14, 0x18, 0x33, +0x5f, 0xe4, 0xe1, 0x8b, 0x40, 0x36, 0x41, 0x9a, 0xf0, 0x05, 0x00, 0x91, +0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0xa0, 0x33, 0xad, +0xa4, 0x13, 0x98, 0x33, 0xaf, 0x10, 0x10, 0xa4, 0x13, 0x90, 0x33, 0xae, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x88, 0x33, 0x92, 0xa4, 0x13, 0x80, +0x33, 0x94, 0x10, 0x10, 0xa4, 0x13, 0x78, 0x33, 0x93, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x13, 0x70, 0x33, 0x77, 0xa4, 0x13, 0x68, 0x33, +0x79, 0x10, 0x10, 0xa4, 0x13, 0x60, 0x33, 0x78, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x13, 0x58, 0x33, 0x5b, 0xa4, 0x13, 0x50, 0x33, 0x5d, 0x10, 0x10, +0xa4, 0x13, 0x48, 0x33, 0x5c, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0xaa, 0x80, 0x33, 0xac, 0x10, 0x10, 0x80, 0x33, 0xab, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x8f, 0x80, 0x33, 0x91, 0x10, 0x10, +0x80, 0x33, 0x90, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x74, +0x80, 0x33, 0x76, 0x10, 0x10, 0x80, 0x33, 0x75, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x58, 0x80, 0x33, 0x5a, 0x10, 0x10, 0x80, 0x33, 0x59, 0xe4, +0xe1, 0x66, 0x40, 0x35, 0xc1, 0x95, 0x40, 0x9a, 0x90, 0x05, 0x00, 0x91, +0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xa7, 0x80, 0x33, +0xa9, 0x10, 0x10, 0x80, 0x33, 0xa8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x8c, 0x80, 0x33, 0x8e, 0x10, 0x10, 0x80, 0x33, 0x8d, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x13, 0x30, 0x33, 0x71, 0xa4, 0x13, 0x40, 0x33, +0x73, 0x10, 0x10, 0xa4, 0x13, 0x38, 0x33, 0x72, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x13, 0x00, 0x33, 0x55, 0xa4, 0x13, 0x10, 0x33, 0x57, 0x10, 0x10, +0xa4, 0x13, 0x08, 0x33, 0x56, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0xa4, 0x80, 0x33, 0xa6, 0x10, 0x10, 0x80, 0x33, 0xa5, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x89, 0x80, 0x33, 0x8b, 0x10, 0x10, +0x80, 0x33, 0x8a, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x18, +0x33, 0x6e, 0xa4, 0x13, 0x28, 0x33, 0x70, 0x10, 0x10, 0xa4, 0x13, 0x20, +0x33, 0x6f, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x12, 0xe8, 0x33, 0x52, 0xa4, +0x12, 0xf8, 0x33, 0x54, 0x10, 0x10, 0xa4, 0x12, 0xf0, 0x33, 0x53, 0xe4, +0xe1, 0x8a, 0x40, 0x36, 0x3d, 0x98, 0xb8, 0x01, 0x68, 0x10, 0x10, 0x10, +0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4f, 0x80, 0x33, 0x51, 0x10, +0x10, 0x80, 0x33, 0x50, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x97, 0x00, +0x60, 0xa0, 0x96, 0xc0, 0x90, 0x30, 0x60, 0xa0, 0x96, 0x80, 0x60, 0xa0, +0x96, 0x40, 0xe4, 0xe1, 0x64, 0x40, 0x35, 0xb9, 0xa0, 0x08, 0x08, 0x94, +0xe0, 0x9a, 0x60, 0x04, 0xa0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x13, 0xd8, 0x33, 0x9e, 0xa4, 0x13, 0xf8, 0x33, 0xa3, 0x10, +0x10, 0xa4, 0x13, 0xe8, 0x33, 0xa2, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x83, 0x80, 0x33, 0x88, 0x10, 0x10, 0x80, 0x33, 0x87, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x68, 0x80, 0x33, 0x6d, 0x10, 0x10, 0x80, +0x33, 0x6c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x49, 0x80, 0x33, 0x4e, +0x10, 0x10, 0x80, 0x33, 0x4d, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x13, 0xa8, 0x33, 0x9b, 0xa4, 0x13, 0xc8, 0x33, 0x9d, 0x10, +0x10, 0xa4, 0x13, 0xb8, 0x33, 0x9c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x80, 0x80, 0x33, 0x82, 0x10, 0x10, 0x80, 0x33, 0x81, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x65, 0x80, 0x33, 0x67, 0x10, 0x10, 0x80, +0x33, 0x66, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x46, 0x80, 0x33, 0x48, +0x10, 0x10, 0x80, 0x33, 0x47, 0xe4, 0xe1, 0x89, 0x40, 0x36, 0x39, 0x9a, +0x60, 0x02, 0xe0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x1a, 0x20, 0x33, 0x9f, 0xa4, 0x1a, 0x10, 0x33, 0xa1, 0x10, 0x10, 0xa4, +0x1a, 0x00, 0x33, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x84, 0x80, +0x33, 0x86, 0x10, 0x10, 0x80, 0x33, 0x85, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0x69, 0x80, 0x33, 0x6b, 0x10, 0x10, 0x80, 0x33, 0x6a, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4a, 0x80, 0x33, 0x4c, 0x10, 0x10, +0x80, 0x33, 0x4b, 0x81, 0x90, 0x50, 0x90, 0x28, 0x24, 0x19, 0xd0, 0x24, +0x19, 0xf0, 0x10, 0x10, 0x24, 0x19, 0xe0, 0xe4, 0xe1, 0x62, 0x40, 0x35, +0xb1, 0x93, 0x90, 0x99, 0xb8, 0x03, 0x50, 0x90, 0xe8, 0x90, 0x88, 0x90, +0x40, 0x80, 0xa4, 0x15, 0xb8, 0x32, 0xca, 0x10, 0x10, 0xa4, 0x15, 0xa8, +0x32, 0xc9, 0x90, 0x28, 0x81, 0x32, 0xc6, 0x10, 0x10, 0x80, 0x32, 0xc5, +0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xc2, 0x10, 0x10, 0x80, 0x32, 0xc1, +0x90, 0x28, 0x81, 0x32, 0xbe, 0x10, 0x10, 0x80, 0x32, 0xbd, 0x90, 0xe8, +0x90, 0x88, 0x90, 0x40, 0x80, 0xa4, 0x15, 0x88, 0x32, 0xc7, 0x10, 0x10, +0xa4, 0x15, 0x98, 0x32, 0xc8, 0x90, 0x28, 0x81, 0x32, 0xc3, 0x10, 0x10, +0x80, 0x32, 0xc4, 0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xbf, 0x10, 0x10, +0x80, 0x32, 0xc0, 0x90, 0x28, 0x81, 0x32, 0xbb, 0x10, 0x10, 0x80, 0x32, +0xbc, 0xe4, 0xe1, 0x88, 0x40, 0x36, 0x35, 0x88, 0x00, 0x88, 0x10, 0x10, +0x10, 0x10, 0x90, 0x28, 0x81, 0x32, 0xb9, 0x10, 0x10, 0x80, 0x32, 0xba, +0xe4, 0xe1, 0x60, 0x40, 0x35, 0xa9, 0xa0, 0x0e, 0x80, 0xa0, 0x09, 0x08, +0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x33, 0x39, 0x80, 0x33, 0x38, 0x10, 0x10, 0x80, 0x33, +0x37, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x1e, 0x80, 0x33, 0x1d, 0x10, +0x10, 0x80, 0x33, 0x1c, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x03, 0x80, 0x33, 0x02, 0x10, 0x10, 0x80, 0x33, 0x01, 0x90, 0x50, 0x90, +0x28, 0x80, 0x32, 0xe8, 0x80, 0x32, 0xe7, 0x10, 0x10, 0x80, 0x32, 0xe6, +0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x34, 0x80, +0x33, 0x36, 0x10, 0x10, 0x80, 0x33, 0x35, 0x90, 0x50, 0x90, 0x28, 0x80, +0x33, 0x19, 0x80, 0x33, 0x1b, 0x10, 0x10, 0x80, 0x33, 0x1a, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfe, 0x80, 0x33, 0x00, 0x10, 0x10, +0x80, 0x32, 0xff, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe3, 0x80, 0x32, +0xe5, 0x10, 0x10, 0x80, 0x32, 0xe4, 0xe4, 0xe1, 0x7a, 0x40, 0x36, 0x11, +0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x31, 0x80, 0x33, 0x33, 0x10, 0x10, 0x80, 0x33, 0x32, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x16, 0x80, 0x33, 0x18, 0x10, 0x10, 0x80, +0x33, 0x17, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfb, 0x80, +0x32, 0xfd, 0x10, 0x10, 0x80, 0x32, 0xfc, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xe0, 0x80, 0x32, 0xe2, 0x10, 0x10, 0x80, 0x32, 0xe1, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x2e, 0x80, 0x33, 0x30, +0x10, 0x10, 0x80, 0x33, 0x2f, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x13, +0x80, 0x33, 0x15, 0x10, 0x10, 0x80, 0x33, 0x14, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xf8, 0x80, 0x32, 0xfa, 0x10, 0x10, 0x80, 0x32, +0xf9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xdd, 0x80, 0x32, 0xdf, 0x10, +0x10, 0x80, 0x32, 0xde, 0xe4, 0xe1, 0x59, 0x40, 0x35, 0x79, 0x94, 0x80, +0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x2b, 0x80, 0x33, 0x2d, 0x10, 0x10, 0x80, 0x33, 0x2c, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x10, 0x80, 0x33, 0x12, 0x10, 0x10, 0x80, +0x33, 0x11, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xf5, 0x80, +0x32, 0xf7, 0x10, 0x10, 0x80, 0x32, 0xf6, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xda, 0x80, 0x32, 0xdc, 0x10, 0x10, 0x80, 0x32, 0xdb, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x28, 0x80, 0x33, 0x2a, +0x10, 0x10, 0x80, 0x33, 0x29, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x0d, +0x80, 0x33, 0x0f, 0x10, 0x10, 0x80, 0x33, 0x0e, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xf2, 0x80, 0x32, 0xf4, 0x10, 0x10, 0x80, 0x32, +0xf3, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd7, 0x80, 0x32, 0xd9, 0x10, +0x10, 0x80, 0x32, 0xd8, 0xe4, 0xe1, 0x78, 0x40, 0x36, 0x09, 0x88, 0x00, +0xb0, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd4, +0x80, 0x32, 0xd6, 0x10, 0x10, 0x80, 0x32, 0xd5, 0xe4, 0xe1, 0x58, 0x40, +0x35, 0x75, 0x96, 0xe8, 0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x22, 0x80, 0x33, 0x27, +0x10, 0x10, 0x80, 0x33, 0x26, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x07, +0x80, 0x33, 0x0c, 0x10, 0x10, 0x80, 0x33, 0x0b, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xec, 0x80, 0x32, 0xf1, 0x10, 0x10, 0x80, 0x32, +0xf0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xce, 0x80, 0x32, 0xd3, 0x10, +0x10, 0x80, 0x32, 0xd2, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x1f, 0x80, 0x33, 0x21, 0x10, 0x10, 0x80, 0x33, 0x20, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x04, 0x80, 0x33, 0x06, 0x10, 0x10, 0x80, +0x33, 0x05, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe9, 0x80, +0x32, 0xeb, 0x10, 0x10, 0x80, 0x32, 0xea, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xcb, 0x80, 0x32, 0xcd, 0x10, 0x10, 0x80, 0x32, 0xcc, 0xe4, 0xe1, +0x76, 0x40, 0x36, 0x01, 0x88, 0x02, 0x28, 0x91, 0x10, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x23, 0x80, 0x33, 0x25, 0x10, 0x10, 0x80, +0x33, 0x24, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x08, 0x80, 0x33, 0x0a, +0x10, 0x10, 0x80, 0x33, 0x09, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xed, 0x80, 0x32, 0xef, 0x10, 0x10, 0x80, 0x32, 0xee, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xcf, 0x80, 0x32, 0xd1, 0x10, 0x10, 0x80, 0x32, +0xd0, 0xe4, 0xe1, 0x57, 0x40, 0x35, 0x71, 0x90, 0x40, 0xe5, 0x21, 0x74, +0x40, 0x35, 0xf9, 0xe5, 0x21, 0x56, 0x40, 0x35, 0x6d, 0x9e, 0xb4, 0x23, +0xe8, 0x93, 0x70, 0x91, 0xd8, 0xd5, 0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, +0x48, 0x80, 0x8c, 0x3f, 0x38, 0x84, 0x37, 0xf1, 0xa4, 0x3d, 0x18, 0x37, +0xbb, 0x90, 0x28, 0x24, 0x3c, 0x58, 0xa4, 0x3a, 0xd8, 0x37, 0x73, 0xd0, +0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x18, 0x84, 0x37, 0xef, 0xa4, +0x3d, 0x08, 0x37, 0xb9, 0x90, 0x28, 0x24, 0x3c, 0x48, 0xa4, 0x3a, 0xc8, +0x37, 0x71, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, +0xdb, 0xa4, 0x3c, 0xe8, 0x37, 0xb5, 0x90, 0x28, 0x24, 0x3c, 0x28, 0xa4, +0x3a, 0xa8, 0x37, 0x6d, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd7, +0xa4, 0x3c, 0xd8, 0x37, 0xb3, 0x90, 0x28, 0x24, 0x3c, 0x18, 0xa4, 0x3a, +0x98, 0x37, 0x6b, 0x91, 0x98, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, +0x28, 0x80, 0x37, 0xcf, 0xa4, 0x3c, 0xb8, 0x37, 0xaf, 0x90, 0x28, 0x24, +0x3b, 0xf8, 0xa4, 0x3a, 0x78, 0x37, 0x67, 0xd0, 0xc3, 0x40, 0x90, 0x28, +0x80, 0x37, 0xcb, 0xa4, 0x3c, 0xa8, 0x37, 0xad, 0x90, 0x28, 0x24, 0x3b, +0xe8, 0xa4, 0x3a, 0x68, 0x37, 0x65, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, +0x90, 0x28, 0x80, 0x37, 0xc3, 0xa4, 0x3c, 0x88, 0x37, 0xa9, 0x90, 0x28, +0x24, 0x3b, 0xc8, 0xa4, 0x3a, 0x48, 0x37, 0x61, 0xd0, 0xc3, 0x40, 0x90, +0x28, 0x80, 0x37, 0xbf, 0xa4, 0x3c, 0x78, 0x37, 0xa7, 0x90, 0x28, 0x24, +0x3b, 0xb8, 0xa4, 0x3a, 0x38, 0x37, 0x5f, 0x93, 0x70, 0x91, 0xd8, 0xd5, +0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x58, 0x84, +0x37, 0xf3, 0xa4, 0x3d, 0x28, 0x37, 0xbd, 0x90, 0x28, 0x24, 0x3c, 0x68, +0xa4, 0x3a, 0xe8, 0x37, 0x75, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, +0x3f, 0x28, 0x84, 0x37, 0xf0, 0xa4, 0x3d, 0x10, 0x37, 0xba, 0x90, 0x28, +0x24, 0x3c, 0x50, 0xa4, 0x3a, 0xd0, 0x37, 0x72, 0xd5, 0x06, 0x80, 0xd0, +0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xdf, 0xa4, 0x3c, 0xf8, 0x37, 0xb7, +0x90, 0x28, 0x24, 0x3c, 0x38, 0xa4, 0x3a, 0xb8, 0x37, 0x6f, 0xd0, 0xc3, +0x40, 0x90, 0x28, 0x80, 0x37, 0xd9, 0xa4, 0x3c, 0xe0, 0x37, 0xb4, 0x90, +0x28, 0x24, 0x3c, 0x20, 0xa4, 0x3a, 0xa0, 0x37, 0x6c, 0x91, 0x98, 0xd5, +0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd3, 0xa4, 0x3c, +0xc8, 0x37, 0xb1, 0x90, 0x28, 0x24, 0x3c, 0x08, 0xa4, 0x3a, 0x88, 0x37, +0x69, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xcd, 0xa4, 0x3c, 0xb0, +0x37, 0xae, 0x90, 0x28, 0x24, 0x3b, 0xf0, 0xa4, 0x3a, 0x70, 0x37, 0x66, +0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc7, 0xa4, +0x3c, 0x98, 0x37, 0xab, 0x90, 0x28, 0x24, 0x3b, 0xd8, 0xa4, 0x3a, 0x58, +0x37, 0x63, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc1, 0xa4, 0x3c, +0x80, 0x37, 0xa8, 0x90, 0x28, 0x24, 0x3b, 0xc0, 0xa4, 0x3a, 0x40, 0x37, +0x60, 0x99, 0xd8, 0x03, 0x90, 0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, +0x40, 0x51, 0x40, 0xc0, 0xa4, 0x23, 0x80, 0x34, 0x60, 0xd1, 0x42, 0x00, +0xa4, 0x22, 0x80, 0x34, 0x40, 0xa4, 0x21, 0x80, 0x34, 0x20, 0x5b, 0x41, +0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, 0x22, 0xa0, 0x34, 0x64, 0xd1, +0x42, 0x00, 0xa4, 0x21, 0xa0, 0x34, 0x44, 0xa4, 0x20, 0xa0, 0x34, 0x24, +0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, +0x22, 0xe0, 0x34, 0x6c, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xe0, 0x34, 0x4c, +0xa4, 0x20, 0xe0, 0x34, 0x2c, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, +0xc0, 0xa4, 0x22, 0xc0, 0x34, 0x68, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xc0, +0x34, 0x48, 0xa4, 0x20, 0xc0, 0x34, 0x28, 0xa8, 0x0b, 0x18, 0x13, 0xa8, +0x96, 0x80, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0xb8, 0x32, 0x58, 0x24, 0x12, 0xb0, 0x90, 0x38, +0xa4, 0x11, 0xe0, 0x32, 0x3d, 0x24, 0x11, 0xd8, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x11, 0x08, 0x32, 0x22, 0x24, 0x11, 0x00, 0x90, 0x38, 0xa4, 0x10, +0x30, 0x32, 0x07, 0x24, 0x10, 0x28, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x12, 0xa8, 0x32, 0x53, 0x24, 0x12, 0xa0, 0x90, 0x38, 0xa4, 0x11, +0xd0, 0x32, 0x38, 0x24, 0x11, 0xc8, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, +0xf8, 0x32, 0x1d, 0x24, 0x10, 0xf0, 0x90, 0x38, 0xa4, 0x10, 0x20, 0x32, +0x02, 0x24, 0x10, 0x18, 0xe4, 0xe1, 0xd0, 0x40, 0x37, 0x43, 0x99, 0x90, +0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x90, 0x32, +0x50, 0x24, 0x12, 0x88, 0x90, 0x38, 0xa4, 0x11, 0xb8, 0x32, 0x35, 0x24, +0x11, 0xb0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xe0, 0x32, 0x1a, 0x24, +0x10, 0xd8, 0x90, 0x38, 0xa4, 0x10, 0x08, 0x31, 0xff, 0x24, 0x10, 0x00, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x78, 0x32, 0x4d, 0x24, +0x12, 0x70, 0x90, 0x38, 0xa4, 0x11, 0xa0, 0x32, 0x32, 0x24, 0x11, 0x98, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xc8, 0x32, 0x17, 0x24, 0x10, 0xc0, +0x90, 0x38, 0xa4, 0x0f, 0xf0, 0x31, 0xfc, 0x24, 0x0f, 0xe8, 0xe4, 0xe1, +0xce, 0xc0, 0x37, 0x3d, 0x93, 0x78, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x60, 0x32, 0x4a, 0x24, 0x12, 0x58, +0x90, 0x38, 0xa4, 0x11, 0x88, 0x32, 0x2f, 0x24, 0x11, 0x80, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x10, 0xb0, 0x32, 0x14, 0x24, 0x10, 0xa8, 0x90, 0x38, +0xa4, 0x0f, 0xd8, 0x31, 0xf9, 0x24, 0x0f, 0xd0, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0x48, 0x32, 0x47, 0x24, 0x12, 0x40, 0x90, 0x38, +0xa4, 0x11, 0x70, 0x32, 0x2c, 0x24, 0x11, 0x68, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x10, 0x98, 0x32, 0x11, 0x24, 0x10, 0x90, 0x90, 0x38, 0xa4, 0x0f, +0xc0, 0x31, 0xf6, 0x24, 0x0f, 0xb8, 0xec, 0xa1, 0x1e, 0x00, 0x02, 0x00, +0x34, 0x7a, 0xa4, 0x39, 0xa8, 0x37, 0x37, 0x88, 0x00, 0x88, 0x10, 0x10, +0x10, 0x10, 0x90, 0x38, 0xa4, 0x0f, 0xa8, 0x31, 0xf3, 0x24, 0x0f, 0xa0, +0xe9, 0x61, 0x1d, 0x40, 0x02, 0x00, 0x34, 0x76, 0xe3, 0x61, 0xcb, 0xc0, +0x37, 0x31, 0x95, 0x08, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x30, 0x32, 0x41, 0x24, 0x12, 0x28, +0x90, 0x38, 0xa4, 0x11, 0x58, 0x32, 0x26, 0x24, 0x11, 0x50, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x10, 0x80, 0x32, 0x0b, 0x24, 0x10, 0x78, 0x90, 0x38, +0xa4, 0x0f, 0x90, 0x31, 0xed, 0x24, 0x0f, 0x88, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0x00, 0x32, 0x3e, 0x24, 0x11, 0xf8, 0x90, 0x38, +0xa4, 0x11, 0x28, 0x32, 0x23, 0x24, 0x11, 0x20, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x10, 0x50, 0x32, 0x08, 0x24, 0x10, 0x48, 0x90, 0x38, 0xa4, 0x0f, +0x60, 0x31, 0xea, 0x24, 0x0f, 0x58, 0xe4, 0xe1, 0xd0, 0x80, 0x37, 0x45, +0x88, 0x01, 0x88, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x20, +0x32, 0x42, 0x24, 0x12, 0x18, 0x90, 0x38, 0xa4, 0x11, 0x48, 0x32, 0x27, +0x24, 0x11, 0x40, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0x70, 0x32, 0x0c, +0x24, 0x10, 0x68, 0x90, 0x38, 0xa4, 0x0f, 0x80, 0x31, 0xee, 0x24, 0x0f, +0x78, 0xe4, 0xe1, 0xcf, 0x00, 0x37, 0x3f, 0x92, 0xd0, 0x99, 0x50, 0x02, +0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe9, 0x24, 0x0f, +0x40, 0x90, 0x28, 0x80, 0x31, 0xe5, 0x24, 0x0f, 0x20, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0xe1, 0x24, 0x0f, 0x00, 0x90, 0x28, 0x80, 0x31, 0xdd, +0x24, 0x0e, 0xe0, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe6, +0x24, 0x0f, 0x38, 0x90, 0x28, 0x80, 0x31, 0xe2, 0x24, 0x0f, 0x18, 0x90, +0x50, 0x90, 0x28, 0x80, 0x31, 0xde, 0x24, 0x0e, 0xf8, 0x90, 0x28, 0x80, +0x31, 0xda, 0x24, 0x0e, 0xd8, 0xec, 0xe1, 0xcd, 0xa1, 0x1f, 0x00, 0x37, +0x39, 0x88, 0x00, 0x78, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x80, 0x31, +0xd8, 0x24, 0x0e, 0xc8, 0xec, 0xe1, 0xcc, 0x21, 0x1d, 0x00, 0x37, 0x33, +0xe5, 0xa1, 0x55, 0x40, 0x35, 0x51, 0xa0, 0x2a, 0x10, 0xa8, 0x16, 0x60, +0x29, 0xd8, 0xa0, 0x0c, 0x48, 0xa0, 0x0a, 0xc8, 0x95, 0x60, 0x92, 0xb0, +0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xa1, 0x80, +0x31, 0xa0, 0x10, 0x10, 0x80, 0x31, 0x9f, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x08, 0x98, 0x31, 0xb3, 0xa4, 0x08, 0x90, 0x31, 0xb2, 0x10, 0x10, 0xa4, +0x08, 0x88, 0x31, 0xb1, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, +0xb8, 0x31, 0xd7, 0xa4, 0x09, 0xb0, 0x31, 0xd6, 0x10, 0x10, 0xa4, 0x09, +0xa8, 0x31, 0xd5, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x28, 0x31, 0xc5, +0xa4, 0x09, 0x20, 0x31, 0xc4, 0x10, 0x10, 0xa4, 0x09, 0x18, 0x31, 0xc3, +0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x9c, 0x80, +0x31, 0x9e, 0x10, 0x10, 0x80, 0x31, 0x9d, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x08, 0x70, 0x31, 0xae, 0xa4, 0x08, 0x80, 0x31, 0xb0, 0x10, 0x10, 0xa4, +0x08, 0x78, 0x31, 0xaf, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, +0x90, 0x31, 0xd2, 0xa4, 0x09, 0xa0, 0x31, 0xd4, 0x10, 0x10, 0xa4, 0x09, +0x98, 0x31, 0xd3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x00, 0x31, 0xc0, +0xa4, 0x09, 0x10, 0x31, 0xc2, 0x10, 0x10, 0xa4, 0x09, 0x08, 0x31, 0xc1, +0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, +0x99, 0x80, 0x31, 0x9b, 0x10, 0x10, 0x80, 0x31, 0x9a, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x58, 0x31, 0xab, 0xa4, 0x08, 0x68, 0x31, 0xad, 0x10, +0x10, 0xa4, 0x08, 0x60, 0x31, 0xac, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x09, 0x78, 0x31, 0xcf, 0xa4, 0x09, 0x88, 0x31, 0xd1, 0x10, 0x10, +0xa4, 0x09, 0x80, 0x31, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xe8, +0x31, 0xbd, 0xa4, 0x08, 0xf8, 0x31, 0xbf, 0x10, 0x10, 0xa4, 0x08, 0xf0, +0x31, 0xbe, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, +0x96, 0x80, 0x31, 0x98, 0x10, 0x10, 0x80, 0x31, 0x97, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x40, 0x31, 0xa8, 0xa4, 0x08, 0x50, 0x31, 0xaa, 0x10, +0x10, 0xa4, 0x08, 0x48, 0x31, 0xa9, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x09, 0x60, 0x31, 0xcc, 0xa4, 0x09, 0x70, 0x31, 0xce, 0x10, 0x10, +0xa4, 0x09, 0x68, 0x31, 0xcd, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xd0, +0x31, 0xba, 0xa4, 0x08, 0xe0, 0x31, 0xbc, 0x10, 0x10, 0xa4, 0x08, 0xd8, +0x31, 0xbb, 0x10, 0x10, 0x90, 0xa8, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, +0x90, 0x28, 0x80, 0x31, 0x8d, 0x80, 0x31, 0x8f, 0x10, 0x10, 0x80, 0x31, +0x8e, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0xc0, 0x60, 0xa0, 0x2a, +0x80, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0x40, 0x60, 0xa0, 0x2a, 0x00, 0x97, +0xf0, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x93, 0x80, 0x31, 0x95, 0x10, 0x10, 0x80, 0x31, 0x94, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x28, 0x31, 0xa5, 0xa4, 0x08, 0x38, +0x31, 0xa7, 0x10, 0x10, 0xa4, 0x08, 0x30, 0x31, 0xa6, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x09, 0x48, 0x31, 0xc9, 0xa4, 0x09, 0x58, 0x31, +0xcb, 0x10, 0x10, 0xa4, 0x09, 0x50, 0x31, 0xca, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x08, 0xb8, 0x31, 0xb7, 0xa4, 0x08, 0xc8, 0x31, 0xb9, 0x10, 0x10, +0xa4, 0x08, 0xc0, 0x31, 0xb8, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x90, 0x80, 0x31, 0x92, 0x10, 0x10, 0x80, 0x31, 0x91, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x10, 0x31, 0xa2, 0xa4, 0x08, 0x20, +0x31, 0xa4, 0x10, 0x10, 0xa4, 0x08, 0x18, 0x31, 0xa3, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x09, 0x30, 0x31, 0xc6, 0xa4, 0x09, 0x40, 0x31, +0xc8, 0x10, 0x10, 0xa4, 0x09, 0x38, 0x31, 0xc7, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x08, 0xa0, 0x31, 0xb4, 0xa4, 0x08, 0xb0, 0x31, 0xb6, 0x10, 0x10, +0xa4, 0x08, 0xa8, 0x31, 0xb5, 0x10, 0x10, 0x91, 0x40, 0x90, 0xa0, 0x90, +0x50, 0x90, 0x28, 0x80, 0x30, 0xcb, 0x80, 0x30, 0xca, 0x90, 0x28, 0x80, +0x30, 0xc9, 0x80, 0x30, 0xc8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xc4, +0x80, 0x30, 0xc7, 0x90, 0x28, 0x80, 0x30, 0xc6, 0x80, 0x30, 0xc5, 0x90, +0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbc, 0x80, 0x30, 0xc3, 0x90, +0x28, 0x80, 0x30, 0xc2, 0x80, 0x30, 0xc1, 0x90, 0x50, 0x90, 0x28, 0x80, +0x30, 0xbd, 0x80, 0x30, 0xc0, 0x90, 0x28, 0x80, 0x30, 0xbf, 0x80, 0x30, +0xbe, 0x91, 0x88, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, +0x3b, 0x10, 0x10, 0x80, 0x31, 0x3a, 0x90, 0x28, 0x81, 0x31, 0x3d, 0x10, +0x10, 0x80, 0x31, 0x3c, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, 0x41, 0x10, +0x10, 0x80, 0x31, 0x40, 0x90, 0x28, 0x81, 0x31, 0x3f, 0x10, 0x10, 0x80, +0x31, 0x3e, 0x80, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x81, 0x31, 0x38, +0x10, 0x10, 0x80, 0x31, 0x39, 0xa0, 0x0b, 0x90, 0xa0, 0x0a, 0xc8, 0x95, +0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x31, 0x56, 0x80, 0x31, 0x55, 0x10, 0x10, 0x80, 0x31, 0x54, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x06, 0xe8, 0x31, 0x68, 0xa4, 0x06, 0xe0, 0x31, 0x67, +0x10, 0x10, 0xa4, 0x06, 0xd8, 0x31, 0x66, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x08, 0x31, 0x8c, 0xa4, 0x08, 0x00, 0x31, 0x8b, 0x10, +0x10, 0xa4, 0x07, 0xf8, 0x31, 0x8a, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, +0x78, 0x31, 0x7a, 0xa4, 0x07, 0x70, 0x31, 0x79, 0x10, 0x10, 0xa4, 0x07, +0x68, 0x31, 0x78, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x31, 0x51, 0x80, 0x31, 0x53, 0x10, 0x10, 0x80, 0x31, 0x52, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x06, 0xc0, 0x31, 0x63, 0xa4, 0x06, 0xd0, 0x31, 0x65, +0x10, 0x10, 0xa4, 0x06, 0xc8, 0x31, 0x64, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x07, 0xe0, 0x31, 0x87, 0xa4, 0x07, 0xf0, 0x31, 0x89, 0x10, +0x10, 0xa4, 0x07, 0xe8, 0x31, 0x88, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, +0x50, 0x31, 0x75, 0xa4, 0x07, 0x60, 0x31, 0x77, 0x10, 0x10, 0xa4, 0x07, +0x58, 0x31, 0x76, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x4e, 0x80, 0x31, 0x50, 0x10, 0x10, 0x80, 0x31, 0x4f, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0xa8, 0x31, 0x60, 0xa4, 0x06, 0xb8, +0x31, 0x62, 0x10, 0x10, 0xa4, 0x06, 0xb0, 0x31, 0x61, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0xc8, 0x31, 0x84, 0xa4, 0x07, 0xd8, 0x31, +0x86, 0x10, 0x10, 0xa4, 0x07, 0xd0, 0x31, 0x85, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x07, 0x38, 0x31, 0x72, 0xa4, 0x07, 0x48, 0x31, 0x74, 0x10, 0x10, +0xa4, 0x07, 0x40, 0x31, 0x73, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x4b, 0x80, 0x31, 0x4d, 0x10, 0x10, 0x80, 0x31, 0x4c, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x90, 0x31, 0x5d, 0xa4, 0x06, 0xa0, +0x31, 0x5f, 0x10, 0x10, 0xa4, 0x06, 0x98, 0x31, 0x5e, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0xb0, 0x31, 0x81, 0xa4, 0x07, 0xc0, 0x31, +0x83, 0x10, 0x10, 0xa4, 0x07, 0xb8, 0x31, 0x82, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x07, 0x20, 0x31, 0x6f, 0xa4, 0x07, 0x30, 0x31, 0x71, 0x10, 0x10, +0xa4, 0x07, 0x28, 0x31, 0x70, 0x10, 0x10, 0x80, 0x10, 0x10, 0x10, 0x10, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x42, 0x80, 0x31, 0x44, 0x10, 0x10, +0x80, 0x31, 0x43, 0x80, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x48, 0x80, 0x31, 0x4a, 0x10, 0x10, +0x80, 0x31, 0x49, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x78, 0x31, 0x5a, +0xa4, 0x06, 0x88, 0x31, 0x5c, 0x10, 0x10, 0xa4, 0x06, 0x80, 0x31, 0x5b, +0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x98, 0x31, 0x7e, 0xa4, +0x07, 0xa8, 0x31, 0x80, 0x10, 0x10, 0xa4, 0x07, 0xa0, 0x31, 0x7f, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0x08, 0x31, 0x6c, 0xa4, 0x07, 0x18, 0x31, +0x6e, 0x10, 0x10, 0xa4, 0x07, 0x10, 0x31, 0x6d, 0x91, 0x40, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x45, 0x80, 0x31, 0x47, 0x10, 0x10, +0x80, 0x31, 0x46, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x60, 0x31, 0x57, +0xa4, 0x06, 0x70, 0x31, 0x59, 0x10, 0x10, 0xa4, 0x06, 0x68, 0x31, 0x58, +0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x80, 0x31, 0x7b, 0xa4, +0x07, 0x90, 0x31, 0x7d, 0x10, 0x10, 0xa4, 0x07, 0x88, 0x31, 0x7c, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x06, 0xf0, 0x31, 0x69, 0xa4, 0x07, 0x00, 0x31, +0x6b, 0x10, 0x10, 0xa4, 0x06, 0xf8, 0x31, 0x6a, 0x10, 0x10, 0x91, 0x40, +0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbb, 0x80, 0x30, 0xba, +0x90, 0x28, 0x80, 0x30, 0xb9, 0x80, 0x30, 0xb8, 0x90, 0x50, 0x90, 0x28, +0x80, 0x30, 0xb4, 0x80, 0x30, 0xb7, 0x90, 0x28, 0x80, 0x30, 0xb6, 0x80, +0x30, 0xb5, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xac, 0x80, +0x30, 0xb3, 0x90, 0x28, 0x80, 0x30, 0xb2, 0x80, 0x30, 0xb1, 0x90, 0x50, +0x90, 0x28, 0x80, 0x30, 0xad, 0x80, 0x30, 0xb0, 0x90, 0x28, 0x80, 0x30, +0xaf, 0x80, 0x30, 0xae, 0xc3, 0xc0, 0x30, 0x42, 0x9c, 0xe8, 0x07, 0x60, +0x91, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x80, 0x88, 0x00, 0x80, 0x90, 0x50, +0x90, 0x28, 0x80, 0x33, 0xf8, 0x80, 0x33, 0xf9, 0x81, 0x33, 0xef, 0xd0, +0x41, 0x80, 0x24, 0x20, 0x90, 0x24, 0x20, 0x98, 0x10, 0x10, 0x80, 0x90, +0x58, 0x80, 0x90, 0x28, 0x24, 0x1f, 0x90, 0x24, 0x1f, 0x98, 0x81, 0x24, +0x1f, 0x50, 0x92, 0x68, 0x91, 0x00, 0x80, 0x90, 0x90, 0x90, 0x30, 0x80, +0x24, 0x20, 0x00, 0x90, 0x38, 0xa4, 0x1f, 0xf8, 0x34, 0x06, 0x80, 0x34, +0x05, 0x80, 0x90, 0x28, 0x80, 0x34, 0x0f, 0xa4, 0x1f, 0xe0, 0x34, 0x0e, +0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x80, 0x34, 0x09, 0xa4, 0x1f, +0xf0, 0x34, 0x08, 0x90, 0x28, 0x80, 0x34, 0x04, 0xa4, 0x1f, 0xe8, 0x34, +0x03, 0x90, 0x50, 0x90, 0x28, 0x80, 0x34, 0x0d, 0x80, 0x34, 0x0c, 0x90, +0x28, 0x24, 0x20, 0x88, 0x24, 0x20, 0x80, 0x90, 0x58, 0x80, 0x10, 0x10, +0x80, 0x10, 0x10, 0x80, 0x33, 0xfb, 0x80, 0x90, 0x40, 0x10, 0x10, 0x80, +0x24, 0x1f, 0x60, 0x80, 0x10, 0x10, 0x80, 0x33, 0xfa, 0x91, 0x58, 0x91, +0x00, 0x90, 0x80, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xf6, 0x80, +0x33, 0xf7, 0x81, 0x33, 0xee, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0xf4, 0x80, 0x33, 0xf5, 0x81, 0x33, 0xed, 0x83, 0x90, 0x28, 0x24, 0x1f, +0x80, 0x24, 0x1f, 0x88, 0x90, 0xe8, 0x81, 0x90, 0x88, 0x90, 0x38, 0x10, +0x10, 0x80, 0x34, 0x07, 0x90, 0x28, 0x80, 0x34, 0x02, 0x80, 0x34, 0x01, +0x80, 0x90, 0x28, 0x80, 0x34, 0x0b, 0x80, 0x34, 0x0a, 0x82, 0x10, 0x10, +0x80, 0x24, 0x1f, 0x58, 0x97, 0x10, 0x9e, 0x10, 0x06, 0x98, 0x93, 0x00, +0x91, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x80, 0x30, +0x71, 0x24, 0x03, 0x78, 0x90, 0x38, 0xa4, 0x04, 0x10, 0x30, 0x83, 0x24, +0x04, 0x08, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x30, 0x30, 0xa7, 0x24, +0x05, 0x28, 0x90, 0x38, 0xa4, 0x04, 0xa0, 0x30, 0x95, 0x24, 0x04, 0x98, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x70, 0x30, 0x6c, 0x24, +0x03, 0x68, 0x90, 0x38, 0xa4, 0x04, 0x00, 0x30, 0x7e, 0x24, 0x03, 0xf8, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x20, 0x30, 0xa2, 0x24, 0x05, 0x18, +0x90, 0x38, 0xa4, 0x04, 0x90, 0x30, 0x90, 0x24, 0x04, 0x88, 0x91, 0x80, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x58, 0x30, 0x69, 0x24, +0x03, 0x50, 0x90, 0x38, 0xa4, 0x03, 0xe8, 0x30, 0x7b, 0x24, 0x03, 0xe0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x08, 0x30, 0x9f, 0x24, 0x05, 0x00, +0x90, 0x38, 0xa4, 0x04, 0x78, 0x30, 0x8d, 0x24, 0x04, 0x70, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x40, 0x30, 0x66, 0x24, 0x03, 0x38, +0x90, 0x38, 0xa4, 0x03, 0xd0, 0x30, 0x78, 0x24, 0x03, 0xc8, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x04, 0xf0, 0x30, 0x9c, 0x24, 0x04, 0xe8, 0x90, 0x38, +0xa4, 0x04, 0x60, 0x30, 0x8a, 0x24, 0x04, 0x58, 0x10, 0x10, 0x80, 0x10, +0x10, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x02, 0xf8, 0x30, 0x5d, 0x24, 0x02, +0xf0, 0xd7, 0x42, 0x00, 0xa4, 0x39, 0x58, 0x37, 0x2d, 0xa4, 0x39, 0x38, +0x37, 0x29, 0x9c, 0xe0, 0x06, 0x90, 0x93, 0x00, 0x91, 0x80, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x28, 0x30, 0x63, 0x24, 0x03, 0x20, +0x90, 0x38, 0xa4, 0x03, 0xb8, 0x30, 0x75, 0x24, 0x03, 0xb0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x04, 0xd8, 0x30, 0x99, 0x24, 0x04, 0xd0, 0x90, 0x38, +0xa4, 0x04, 0x48, 0x30, 0x87, 0x24, 0x04, 0x40, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x03, 0x10, 0x30, 0x60, 0x24, 0x03, 0x08, 0x90, 0x38, +0xa4, 0x03, 0xa0, 0x30, 0x72, 0x24, 0x03, 0x98, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x04, 0xc0, 0x30, 0x96, 0x24, 0x04, 0xb8, 0x90, 0x38, 0xa4, 0x04, +0x30, 0x30, 0x84, 0x24, 0x04, 0x28, 0x10, 0x10, 0x90, 0xe0, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x02, 0x88, 0x30, 0x52, 0xa4, 0x02, 0x78, 0x30, 0x50, +0x90, 0x38, 0xa4, 0x02, 0x70, 0x30, 0x4b, 0xa4, 0x02, 0x60, 0x30, 0x4d, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x02, 0x50, 0x30, 0x43, 0xa4, 0x02, 0x40, +0x30, 0x49, 0x90, 0x38, 0xa4, 0x02, 0x38, 0x30, 0x44, 0xa4, 0x02, 0x28, +0x30, 0x46, 0x91, 0x48, 0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, +0x30, 0x56, 0x24, 0x02, 0xa8, 0x90, 0x28, 0x80, 0x30, 0x58, 0x24, 0x02, +0xb8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0x5c, 0x24, 0x02, 0xd8, 0x90, +0x28, 0x80, 0x30, 0x5a, 0x24, 0x02, 0xc8, 0x80, 0x10, 0x10, 0x10, 0x10, +0x90, 0x28, 0x80, 0x30, 0x53, 0x24, 0x02, 0xa0, 0xd7, 0x42, 0x00, 0xa4, +0x39, 0x60, 0x37, 0x2e, 0xa4, 0x39, 0x40, 0x37, 0x2a, 0xa0, 0x14, 0x68, +0xa0, 0x10, 0x90, 0xa0, 0x0c, 0x60, 0x9e, 0x88, 0x09, 0xd0, 0x94, 0xf0, +0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4c, 0x40, +0x85, 0x35, 0x4d, 0xcb, 0x61, 0x45, 0x00, 0x85, 0x35, 0x23, 0x9a, 0x00, +0x03, 0xf8, 0x91, 0x98, 0x80, 0x91, 0x10, 0x90, 0xa0, 0x90, 0x68, 0x90, +0x20, 0x3a, 0x75, 0xc9, 0xe2, 0x9c, 0xc0, 0x85, 0x35, 0x4b, 0xa4, 0x53, +0x88, 0x3a, 0x72, 0x90, 0x38, 0xa4, 0x53, 0x50, 0x3a, 0x6b, 0xa4, 0x53, +0x40, 0x3a, 0x69, 0x90, 0x48, 0x10, 0x10, 0xa4, 0x53, 0x08, 0x3a, 0x62, +0x10, 0x10, 0x80, 0x3a, 0x5e, 0x81, 0x10, 0x10, 0x80, 0xa4, 0x52, 0xd8, +0x3a, 0x5c, 0x91, 0xb0, 0x91, 0x60, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x53, 0x78, 0x3a, 0x70, 0xa4, 0x53, 0x68, 0x3a, 0x6e, 0x90, 0x38, +0xa4, 0x53, 0x30, 0x3a, 0x67, 0xa4, 0x53, 0x20, 0x3a, 0x65, 0x90, 0x48, +0x10, 0x10, 0xa4, 0x52, 0xf8, 0x3a, 0x60, 0x10, 0x10, 0x80, 0x3a, 0x5d, +0x90, 0x28, 0x80, 0x3a, 0x56, 0x80, 0x3a, 0x55, 0x81, 0x10, 0x10, 0x80, +0xa4, 0x52, 0xc8, 0x3a, 0x5a, 0xcb, 0x61, 0x44, 0xc0, 0x85, 0x35, 0x22, +0x90, 0xd8, 0x88, 0x00, 0x90, 0x84, 0x90, 0x38, 0xc1, 0xc0, 0x85, 0x3a, +0x78, 0xc9, 0xe1, 0x4c, 0x00, 0x85, 0x35, 0x49, 0xcb, 0x61, 0x44, 0x80, +0x85, 0x35, 0x21, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b, +0xc0, 0x85, 0x35, 0x47, 0xcb, 0x61, 0x44, 0x40, 0x85, 0x35, 0x20, 0x91, +0xf8, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b, +0x40, 0x85, 0x35, 0x43, 0xcb, 0x61, 0x43, 0xc0, 0x85, 0x35, 0x1e, 0x88, +0x01, 0x00, 0x90, 0xa0, 0x81, 0x90, 0x70, 0x80, 0x90, 0x20, 0x3a, 0x6c, +0xc9, 0xe1, 0x4b, 0x00, 0x85, 0x35, 0x41, 0x81, 0x3a, 0x63, 0x81, 0x10, +0x10, 0x80, 0xa4, 0x52, 0xb8, 0x3a, 0x58, 0xcb, 0x61, 0x43, 0x80, 0x85, +0x35, 0x1d, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, +0x4a, 0xc0, 0x85, 0x35, 0x3f, 0xcb, 0x61, 0x43, 0x40, 0x85, 0x35, 0x1c, +0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x80, 0x85, 0x35, +0x3d, 0xcb, 0x61, 0x43, 0x00, 0x85, 0x35, 0x1b, 0x92, 0x38, 0x81, 0x91, +0x68, 0x91, 0x18, 0x90, 0x80, 0x90, 0x40, 0x80, 0xa4, 0x54, 0x38, 0x3a, +0x88, 0x80, 0xa4, 0x54, 0x30, 0x3a, 0x85, 0x90, 0x28, 0x81, 0x3a, 0x84, +0x90, 0x38, 0xa4, 0x54, 0x10, 0x3a, 0x83, 0xa4, 0x54, 0x00, 0x3a, 0x81, +0x90, 0x28, 0x80, 0x3a, 0x7f, 0x80, 0x3a, 0x7e, 0x80, 0x90, 0x40, 0x10, +0x10, 0x80, 0x24, 0x53, 0xe8, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x53, 0xd8, +0x3a, 0x7c, 0xa4, 0x53, 0xc8, 0x3a, 0x7a, 0x90, 0x28, 0x80, 0x3a, 0x77, +0x80, 0x3a, 0x76, 0x9a, 0xd0, 0x03, 0xe0, 0x91, 0x60, 0x90, 0xb0, 0x88, +0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x00, 0x85, 0x35, 0x39, +0xcb, 0x61, 0x42, 0x80, 0x85, 0x35, 0x19, 0x88, 0x00, 0x68, 0x84, 0x10, +0x10, 0xc9, 0xe1, 0x49, 0xc0, 0x85, 0x35, 0x37, 0xcb, 0x61, 0x42, 0x40, +0x85, 0x35, 0x18, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, +0xe1, 0x49, 0x80, 0x85, 0x35, 0x35, 0xcb, 0x61, 0x42, 0x00, 0x85, 0x35, +0x17, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x49, 0x40, 0x85, +0x35, 0x33, 0xcb, 0x61, 0x41, 0xc0, 0x85, 0x35, 0x16, 0x90, 0x90, 0x90, +0x48, 0xcb, 0xa1, 0x40, 0x00, 0x85, 0x35, 0x05, 0xcb, 0xa1, 0x3f, 0xc0, +0x85, 0x35, 0x04, 0x90, 0x48, 0xcb, 0xa1, 0x3f, 0x80, 0x85, 0x35, 0x03, +0xcb, 0xa1, 0x3f, 0x40, 0x85, 0x35, 0x02, 0xcb, 0xa2, 0x94, 0xc0, 0x80, +0x3a, 0x54, 0x92, 0x40, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, +0x60, 0x84, 0x24, 0x27, 0xd8, 0x8c, 0x27, 0x58, 0x84, 0x24, 0x27, 0xd0, +0x90, 0x48, 0x8c, 0x27, 0x50, 0x84, 0x24, 0x27, 0xc8, 0x8c, 0x27, 0x48, +0x84, 0x24, 0x27, 0xc0, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x38, 0x84, +0x24, 0x27, 0xb0, 0x8c, 0x27, 0x30, 0x84, 0x24, 0x27, 0xa8, 0x90, 0x48, +0x8c, 0x27, 0x28, 0x84, 0x24, 0x27, 0xa0, 0x8c, 0x27, 0x20, 0x84, 0x24, +0x27, 0x98, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x10, 0x84, +0x24, 0x27, 0x88, 0x8c, 0x27, 0x08, 0x84, 0x24, 0x27, 0x80, 0x90, 0x48, +0x8c, 0x27, 0x00, 0x84, 0x24, 0x27, 0x78, 0x8c, 0x26, 0xf8, 0x84, 0x24, +0x27, 0x70, 0x90, 0x38, 0xa4, 0x26, 0xe0, 0x34, 0xdd, 0xa4, 0x26, 0xd0, +0x34, 0xdb, 0xa0, 0x0f, 0x50, 0xa0, 0x09, 0x08, 0x9a, 0x30, 0x04, 0x40, +0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x92, 0xc0, +0x3a, 0x43, 0xe5, 0x22, 0x8a, 0xc0, 0x3a, 0x3f, 0xcb, 0x61, 0x32, 0x40, +0x85, 0x34, 0xd8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x82, 0xc0, 0x3a, +0x03, 0xe5, 0x22, 0x7a, 0xc0, 0x39, 0xff, 0xcb, 0x61, 0x32, 0x00, 0x85, +0x34, 0xd7, 0x90, 0x48, 0xcb, 0xa1, 0x31, 0xc0, 0x85, 0x34, 0xd6, 0xcb, +0xa1, 0x31, 0x80, 0x85, 0x34, 0xd5, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, +0x00, 0x80, 0xe5, 0x22, 0x6c, 0xc0, 0x39, 0xcb, 0xe5, 0x22, 0x60, 0xc0, +0x39, 0x9b, 0xcb, 0x61, 0x31, 0x00, 0x85, 0x34, 0xd3, 0x98, 0x50, 0x00, +0x80, 0xe5, 0x22, 0x54, 0xc0, 0x39, 0x6b, 0xe5, 0x22, 0x48, 0xc0, 0x39, +0x3b, 0xcb, 0x61, 0x30, 0xc0, 0x85, 0x34, 0xd2, 0x90, 0x48, 0xcb, 0xa1, +0x30, 0x80, 0x85, 0x34, 0xd1, 0xcb, 0xa1, 0x30, 0x40, 0x85, 0x34, 0xd0, +0x92, 0x20, 0x91, 0x30, 0x90, 0xb8, 0xd5, 0x03, 0x00, 0xc0, 0xc0, 0x81, +0x8c, 0x01, 0xa0, 0x84, 0x30, 0x3e, 0xc0, 0xc0, 0x81, 0x8c, 0x01, 0x80, +0x84, 0x30, 0x3c, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, 0x30, 0x28, 0xc0, +0xc0, 0x81, 0x30, 0x24, 0x90, 0x78, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, +0x30, 0x1c, 0xc0, 0xc0, 0x81, 0x30, 0x18, 0xd5, 0x02, 0x00, 0xc0, 0xc0, +0x81, 0x30, 0x10, 0xc0, 0xc0, 0x81, 0x30, 0x0c, 0x91, 0x70, 0x90, 0xd8, +0xd5, 0x03, 0x80, 0xc8, 0xe2, 0x40, 0xc0, 0x81, 0x8c, 0x01, 0xc0, 0x84, +0x30, 0x40, 0xc8, 0xe2, 0x42, 0xc0, 0x81, 0x8c, 0x01, 0x90, 0x84, 0x30, +0x3d, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x3f, 0xc0, 0x81, 0x30, 0x2c, 0xc8, +0xe2, 0x3a, 0x40, 0x81, 0x30, 0x26, 0x90, 0x98, 0xd5, 0x02, 0x80, 0xc8, +0xe2, 0x2f, 0x40, 0x81, 0x30, 0x20, 0xc8, 0xe2, 0x31, 0x40, 0x81, 0x30, +0x1a, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x2e, 0x40, 0x81, 0x30, 0x14, 0xc8, +0xe2, 0x28, 0xc0, 0x81, 0x30, 0x0e, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x90, +0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x86, 0xc0, 0x3a, 0x13, +0xe5, 0x22, 0x88, 0xc0, 0x3a, 0x37, 0xcb, 0x61, 0x2f, 0xc0, 0x85, 0x34, +0xce, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x76, 0xc0, 0x39, 0xd3, 0xe5, +0x22, 0x78, 0xc0, 0x39, 0xf7, 0xcb, 0x61, 0x2f, 0x80, 0x85, 0x34, 0xcd, +0x90, 0x48, 0xcb, 0xa1, 0x2f, 0x40, 0x85, 0x34, 0xcc, 0xcb, 0xa1, 0x2f, +0x00, 0x85, 0x34, 0xcb, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, +0xe5, 0x22, 0x68, 0xc0, 0x39, 0xbb, 0xe5, 0x22, 0x5c, 0xc0, 0x39, 0x8b, +0xcb, 0x61, 0x2d, 0x40, 0x85, 0x34, 0xba, 0x98, 0x50, 0x00, 0x80, 0xe5, +0x22, 0x50, 0xc0, 0x39, 0x5b, 0xe5, 0x22, 0x44, 0xc0, 0x39, 0x2b, 0xcb, +0x61, 0x2d, 0x00, 0x85, 0x34, 0xb9, 0x90, 0x48, 0xcb, 0xa1, 0x2c, 0xc0, +0x85, 0x34, 0xb8, 0xcb, 0xa1, 0x2c, 0x80, 0x85, 0x34, 0xb7, 0x91, 0x00, +0x90, 0x80, 0x90, 0x40, 0xe5, 0x20, 0x02, 0x40, 0x30, 0x0a, 0xe5, 0x20, +0x01, 0x80, 0x30, 0x07, 0x90, 0x40, 0xe5, 0x20, 0x00, 0xc0, 0x30, 0x04, +0xe5, 0x20, 0x00, 0x00, 0x30, 0x01, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, +0x35, 0xc0, 0x38, 0xcd, 0xe5, 0x22, 0x38, 0x00, 0x38, 0xf5, 0x90, 0x40, +0xe5, 0x22, 0x24, 0x40, 0x38, 0x87, 0xe5, 0x22, 0x26, 0x80, 0x38, 0xaf, +0x80, 0x99, 0x28, 0x02, 0xf0, 0x8c, 0x25, 0x48, 0x90, 0x80, 0x90, 0x40, +0xe5, 0x22, 0x8c, 0xc0, 0x3a, 0x2f, 0xe5, 0x22, 0x89, 0xc0, 0x3a, 0x3b, +0x90, 0x40, 0xe5, 0x22, 0x7c, 0xc0, 0x39, 0xef, 0xe5, 0x22, 0x79, 0xc0, +0x39, 0xfb, 0x91, 0x48, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, +0x6a, 0xc0, 0x39, 0xc3, 0xe5, 0x22, 0x5e, 0xc0, 0x39, 0x93, 0xcb, 0x61, +0x2b, 0x00, 0x85, 0x34, 0xb0, 0x90, 0x40, 0xe5, 0x22, 0x52, 0xc0, 0x39, +0x63, 0xe5, 0x22, 0x46, 0xc0, 0x39, 0x33, 0x90, 0x48, 0xcb, 0xa1, 0x2a, +0x80, 0x85, 0x34, 0xae, 0xcb, 0xa1, 0x2a, 0xc0, 0x85, 0x34, 0xaf, 0x10, +0x10, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, 0x3c, 0x40, 0x38, 0xed, 0xe5, +0x22, 0x39, 0x40, 0x38, 0xfb, 0x90, 0x40, 0xe5, 0x22, 0x2a, 0xc0, 0x38, +0xa7, 0xe5, 0x22, 0x27, 0xc0, 0x38, 0xb5, +}; + +static const struct ia64_dis_names ia64_dis_names[] = { +{ 0x51, 41, 0, 10 }, +{ 0x31, 41, 1, 20 }, +{ 0x11, 42, 0, 19 }, +{ 0x29, 41, 0, 12 }, +{ 0x19, 41, 1, 24 }, +{ 0x9, 42, 0, 23 }, +{ 0x15, 41, 0, 14 }, +{ 0xd, 41, 1, 28 }, +{ 0x5, 42, 0, 27 }, +{ 0xb, 41, 0, 16 }, +{ 0x7, 41, 1, 32 }, +{ 0x3, 42, 0, 31 }, +{ 0x51, 39, 1, 58 }, +{ 0x50, 39, 0, 34 }, +{ 0xd1, 39, 1, 57 }, +{ 0xd0, 39, 0, 33 }, +{ 0x31, 39, 1, 68 }, +{ 0x30, 39, 1, 44 }, +{ 0x11, 40, 1, 67 }, +{ 0x10, 40, 0, 43 }, +{ 0x71, 39, 1, 66 }, +{ 0x70, 39, 1, 42 }, +{ 0x31, 40, 1, 65 }, +{ 0x30, 40, 0, 41 }, +{ 0x29, 39, 1, 60 }, +{ 0x28, 39, 0, 36 }, +{ 0x69, 39, 1, 59 }, +{ 0x68, 39, 0, 35 }, +{ 0x19, 39, 1, 72 }, +{ 0x18, 39, 1, 48 }, +{ 0x9, 40, 1, 71 }, +{ 0x8, 40, 0, 47 }, +{ 0x39, 39, 1, 70 }, +{ 0x38, 39, 1, 46 }, +{ 0x19, 40, 1, 69 }, +{ 0x18, 40, 0, 45 }, +{ 0x15, 39, 1, 62 }, +{ 0x14, 39, 0, 38 }, +{ 0x35, 39, 1, 61 }, +{ 0x34, 39, 0, 37 }, +{ 0xd, 39, 1, 76 }, +{ 0xc, 39, 1, 52 }, +{ 0x5, 40, 1, 75 }, +{ 0x4, 40, 0, 51 }, +{ 0x1d, 39, 1, 74 }, +{ 0x1c, 39, 1, 50 }, +{ 0xd, 40, 1, 73 }, +{ 0xc, 40, 0, 49 }, +{ 0xb, 39, 1, 64 }, +{ 0xa, 39, 0, 40 }, +{ 0x1b, 39, 1, 63 }, +{ 0x1a, 39, 0, 39 }, +{ 0x7, 39, 1, 80 }, +{ 0x6, 39, 1, 56 }, +{ 0x3, 40, 1, 79 }, +{ 0x2, 40, 0, 55 }, +{ 0xf, 39, 1, 78 }, +{ 0xe, 39, 1, 54 }, +{ 0x7, 40, 1, 77 }, +{ 0x6, 40, 0, 53 }, +{ 0x8, 38, 0, 82 }, +{ 0x18, 38, 0, 81 }, +{ 0x1, 38, 1, 86 }, +{ 0x2, 38, 0, 85 }, +{ 0x3, 38, 1, 84 }, +{ 0x4, 38, 0, 83 }, +{ 0x1, 336, 0, 87 }, +{ 0x20, 289, 0, 98 }, +{ 0x220, 289, 0, 94 }, +{ 0x1220, 289, 0, 91 }, +{ 0xa20, 289, 0, 92 }, +{ 0x620, 289, 0, 93 }, +{ 0x120, 289, 0, 95 }, +{ 0xa0, 289, 0, 96 }, +{ 0x60, 289, 0, 97 }, +{ 0x10, 289, 0, 102 }, +{ 0x90, 289, 0, 99 }, +{ 0x50, 289, 0, 100 }, +{ 0x30, 289, 0, 101 }, +{ 0x8, 289, 0, 103 }, +{ 0x4, 289, 0, 104 }, +{ 0x2, 289, 0, 105 }, +{ 0x1, 289, 0, 106 }, +{ 0x1, 411, 0, 108 }, +{ 0x3, 411, 0, 107 }, +{ 0x2, 417, 0, 109 }, +{ 0x1, 417, 0, 110 }, +{ 0x2, 413, 0, 111 }, +{ 0x1, 413, 0, 112 }, +{ 0x2, 415, 0, 113 }, +{ 0x1, 415, 0, 114 }, +{ 0x2, 419, 0, 115 }, +{ 0x1, 419, 0, 116 }, +{ 0x1, 268, 0, 143 }, +{ 0x5, 268, 0, 141 }, +{ 0x3, 268, 0, 142 }, +{ 0x140, 277, 0, 119 }, +{ 0x540, 277, 0, 117 }, +{ 0x340, 277, 0, 118 }, +{ 0xc0, 277, 0, 131 }, +{ 0x2c0, 277, 0, 129 }, +{ 0x1c0, 277, 0, 130 }, +{ 0x20, 277, 0, 146 }, +{ 0xa0, 277, 0, 144 }, +{ 0x60, 277, 0, 145 }, +{ 0x10, 277, 0, 158 }, +{ 0x50, 277, 0, 156 }, +{ 0x30, 277, 0, 157 }, +{ 0x8, 277, 0, 170 }, +{ 0x28, 277, 0, 168 }, +{ 0x18, 277, 0, 169 }, +{ 0x4, 277, 0, 180 }, +{ 0x2, 277, 0, 181 }, +{ 0x1, 277, 0, 182 }, +{ 0x140, 271, 0, 122 }, +{ 0x540, 271, 0, 120 }, +{ 0x340, 271, 0, 121 }, +{ 0xc0, 271, 0, 134 }, +{ 0x2c0, 271, 0, 132 }, +{ 0x1c0, 271, 0, 133 }, +{ 0x20, 271, 0, 149 }, +{ 0xa0, 271, 0, 147 }, +{ 0x60, 271, 0, 148 }, +{ 0x10, 271, 0, 161 }, +{ 0x50, 271, 0, 159 }, +{ 0x30, 271, 0, 160 }, +{ 0x8, 271, 0, 173 }, +{ 0x28, 271, 0, 171 }, +{ 0x18, 271, 0, 172 }, +{ 0x4, 271, 0, 183 }, +{ 0x2, 271, 0, 184 }, +{ 0x1, 271, 0, 185 }, +{ 0x140, 274, 0, 125 }, +{ 0x540, 274, 0, 123 }, +{ 0x340, 274, 0, 124 }, +{ 0xc0, 274, 0, 137 }, +{ 0x2c0, 274, 0, 135 }, +{ 0x1c0, 274, 0, 136 }, +{ 0x20, 274, 0, 152 }, +{ 0xa0, 274, 0, 150 }, +{ 0x60, 274, 0, 151 }, +{ 0x10, 274, 0, 164 }, +{ 0x50, 274, 0, 162 }, +{ 0x30, 274, 0, 163 }, +{ 0x8, 274, 0, 176 }, +{ 0x28, 274, 0, 174 }, +{ 0x18, 274, 0, 175 }, +{ 0x4, 274, 0, 186 }, +{ 0x2, 274, 0, 187 }, +{ 0x1, 274, 0, 188 }, +{ 0x140, 286, 0, 128 }, +{ 0x540, 286, 0, 126 }, +{ 0x340, 286, 0, 127 }, +{ 0xc0, 286, 0, 140 }, +{ 0x2c0, 286, 0, 138 }, +{ 0x1c0, 286, 0, 139 }, +{ 0x20, 286, 0, 155 }, +{ 0xa0, 286, 0, 153 }, +{ 0x60, 286, 0, 154 }, +{ 0x10, 286, 0, 167 }, +{ 0x50, 286, 0, 165 }, +{ 0x30, 286, 0, 166 }, +{ 0x8, 286, 0, 179 }, +{ 0x28, 286, 0, 177 }, +{ 0x18, 286, 0, 178 }, +{ 0x4, 286, 0, 189 }, +{ 0x2, 286, 0, 190 }, +{ 0x1, 286, 0, 191 }, +{ 0x8, 390, 0, 192 }, +{ 0x4, 390, 0, 193 }, +{ 0x2, 390, 0, 194 }, +{ 0x1, 390, 0, 195 }, +{ 0x20, 288, 0, 203 }, +{ 0x220, 288, 0, 199 }, +{ 0x1220, 288, 0, 196 }, +{ 0xa20, 288, 0, 197 }, +{ 0x620, 288, 0, 198 }, +{ 0x120, 288, 0, 200 }, +{ 0xa0, 288, 0, 201 }, +{ 0x60, 288, 0, 202 }, +{ 0x10, 288, 0, 207 }, +{ 0x90, 288, 0, 204 }, +{ 0x50, 288, 0, 205 }, +{ 0x30, 288, 0, 206 }, +{ 0x8, 288, 0, 208 }, +{ 0x4, 288, 0, 209 }, +{ 0x2, 288, 0, 210 }, +{ 0x1, 288, 0, 211 }, +{ 0x20, 287, 0, 219 }, +{ 0x220, 287, 0, 215 }, +{ 0x1220, 287, 0, 212 }, +{ 0xa20, 287, 0, 213 }, +{ 0x620, 287, 0, 214 }, +{ 0x120, 287, 0, 216 }, +{ 0xa0, 287, 0, 217 }, +{ 0x60, 287, 0, 218 }, +{ 0x10, 287, 0, 223 }, +{ 0x90, 287, 0, 220 }, +{ 0x50, 287, 0, 221 }, +{ 0x30, 287, 0, 222 }, +{ 0x8, 287, 0, 224 }, +{ 0x4, 287, 0, 225 }, +{ 0x2, 287, 0, 226 }, +{ 0x1, 287, 0, 227 }, +{ 0x140, 279, 0, 230 }, +{ 0x540, 279, 0, 228 }, +{ 0x340, 279, 0, 229 }, +{ 0xc0, 279, 0, 239 }, +{ 0x2c0, 279, 0, 237 }, +{ 0x1c0, 279, 0, 238 }, +{ 0x20, 279, 0, 248 }, +{ 0xa0, 279, 0, 246 }, +{ 0x60, 279, 0, 247 }, +{ 0x10, 279, 0, 257 }, +{ 0x50, 279, 0, 255 }, +{ 0x30, 279, 0, 256 }, +{ 0x8, 279, 0, 266 }, +{ 0x28, 279, 0, 264 }, +{ 0x18, 279, 0, 265 }, +{ 0x4, 279, 0, 273 }, +{ 0x2, 279, 0, 274 }, +{ 0x1, 279, 0, 275 }, +{ 0x140, 281, 0, 233 }, +{ 0x540, 281, 0, 231 }, +{ 0x340, 281, 0, 232 }, +{ 0xc0, 281, 0, 242 }, +{ 0x2c0, 281, 0, 240 }, +{ 0x1c0, 281, 0, 241 }, +{ 0x20, 281, 0, 251 }, +{ 0xa0, 281, 0, 249 }, +{ 0x60, 281, 0, 250 }, +{ 0x10, 281, 0, 260 }, +{ 0x50, 281, 0, 258 }, +{ 0x30, 281, 0, 259 }, +{ 0x8, 281, 0, 269 }, +{ 0x28, 281, 0, 267 }, +{ 0x18, 281, 0, 268 }, +{ 0x4, 281, 0, 276 }, +{ 0x2, 281, 0, 277 }, +{ 0x1, 281, 0, 278 }, +{ 0x140, 283, 0, 236 }, +{ 0x540, 283, 0, 234 }, +{ 0x340, 283, 0, 235 }, +{ 0xc0, 283, 0, 245 }, +{ 0x2c0, 283, 0, 243 }, +{ 0x1c0, 283, 0, 244 }, +{ 0x20, 283, 0, 254 }, +{ 0xa0, 283, 0, 252 }, +{ 0x60, 283, 0, 253 }, +{ 0x10, 283, 0, 263 }, +{ 0x50, 283, 0, 261 }, +{ 0x30, 283, 0, 262 }, +{ 0x8, 283, 0, 272 }, +{ 0x28, 283, 0, 270 }, +{ 0x18, 283, 0, 271 }, +{ 0x4, 283, 0, 279 }, +{ 0x2, 283, 0, 280 }, +{ 0x1, 283, 0, 281 }, +{ 0x140, 278, 0, 284 }, +{ 0x540, 278, 0, 282 }, +{ 0x340, 278, 0, 283 }, +{ 0xc0, 278, 0, 293 }, +{ 0x2c0, 278, 0, 291 }, +{ 0x1c0, 278, 0, 292 }, +{ 0x20, 278, 0, 302 }, +{ 0xa0, 278, 0, 300 }, +{ 0x60, 278, 0, 301 }, +{ 0x10, 278, 0, 311 }, +{ 0x50, 278, 0, 309 }, +{ 0x30, 278, 0, 310 }, +{ 0x8, 278, 0, 320 }, +{ 0x28, 278, 0, 318 }, +{ 0x18, 278, 0, 319 }, +{ 0x4, 278, 0, 327 }, +{ 0x2, 278, 0, 328 }, +{ 0x1, 278, 0, 329 }, +{ 0x140, 280, 0, 287 }, +{ 0x540, 280, 0, 285 }, +{ 0x340, 280, 0, 286 }, +{ 0xc0, 280, 0, 296 }, +{ 0x2c0, 280, 0, 294 }, +{ 0x1c0, 280, 0, 295 }, +{ 0x20, 280, 0, 305 }, +{ 0xa0, 280, 0, 303 }, +{ 0x60, 280, 0, 304 }, +{ 0x10, 280, 0, 314 }, +{ 0x50, 280, 0, 312 }, +{ 0x30, 280, 0, 313 }, +{ 0x8, 280, 0, 323 }, +{ 0x28, 280, 0, 321 }, +{ 0x18, 280, 0, 322 }, +{ 0x4, 280, 0, 330 }, +{ 0x2, 280, 0, 331 }, +{ 0x1, 280, 0, 332 }, +{ 0x140, 282, 0, 290 }, +{ 0x540, 282, 0, 288 }, +{ 0x340, 282, 0, 289 }, +{ 0xc0, 282, 0, 299 }, +{ 0x2c0, 282, 0, 297 }, +{ 0x1c0, 282, 0, 298 }, +{ 0x20, 282, 0, 308 }, +{ 0xa0, 282, 0, 306 }, +{ 0x60, 282, 0, 307 }, +{ 0x10, 282, 0, 317 }, +{ 0x50, 282, 0, 315 }, +{ 0x30, 282, 0, 316 }, +{ 0x8, 282, 0, 326 }, +{ 0x28, 282, 0, 324 }, +{ 0x18, 282, 0, 325 }, +{ 0x4, 282, 0, 333 }, +{ 0x2, 282, 0, 334 }, +{ 0x1, 282, 0, 335 }, +{ 0x1, 410, 0, 337 }, +{ 0x3, 410, 0, 336 }, +{ 0x2, 416, 0, 338 }, +{ 0x1, 416, 0, 339 }, +{ 0x2, 412, 0, 340 }, +{ 0x1, 412, 0, 341 }, +{ 0x2, 414, 0, 342 }, +{ 0x1, 414, 0, 343 }, +{ 0x2, 418, 0, 344 }, +{ 0x1, 418, 0, 345 }, +{ 0x1, 267, 0, 372 }, +{ 0x5, 267, 0, 370 }, +{ 0x3, 267, 0, 371 }, +{ 0x140, 276, 0, 348 }, +{ 0x540, 276, 0, 346 }, +{ 0x340, 276, 0, 347 }, +{ 0xc0, 276, 0, 360 }, +{ 0x2c0, 276, 0, 358 }, +{ 0x1c0, 276, 0, 359 }, +{ 0x20, 276, 0, 375 }, +{ 0xa0, 276, 0, 373 }, +{ 0x60, 276, 0, 374 }, +{ 0x10, 276, 0, 387 }, +{ 0x50, 276, 0, 385 }, +{ 0x30, 276, 0, 386 }, +{ 0x8, 276, 0, 399 }, +{ 0x28, 276, 0, 397 }, +{ 0x18, 276, 0, 398 }, +{ 0x4, 276, 0, 409 }, +{ 0x2, 276, 0, 410 }, +{ 0x1, 276, 0, 411 }, +{ 0x140, 270, 0, 351 }, +{ 0x540, 270, 0, 349 }, +{ 0x340, 270, 0, 350 }, +{ 0xc0, 270, 0, 363 }, +{ 0x2c0, 270, 0, 361 }, +{ 0x1c0, 270, 0, 362 }, +{ 0x20, 270, 0, 378 }, +{ 0xa0, 270, 0, 376 }, +{ 0x60, 270, 0, 377 }, +{ 0x10, 270, 0, 390 }, +{ 0x50, 270, 0, 388 }, +{ 0x30, 270, 0, 389 }, +{ 0x8, 270, 0, 402 }, +{ 0x28, 270, 0, 400 }, +{ 0x18, 270, 0, 401 }, +{ 0x4, 270, 0, 412 }, +{ 0x2, 270, 0, 413 }, +{ 0x1, 270, 0, 414 }, +{ 0x140, 273, 0, 354 }, +{ 0x540, 273, 0, 352 }, +{ 0x340, 273, 0, 353 }, +{ 0xc0, 273, 0, 366 }, +{ 0x2c0, 273, 0, 364 }, +{ 0x1c0, 273, 0, 365 }, +{ 0x20, 273, 0, 381 }, +{ 0xa0, 273, 0, 379 }, +{ 0x60, 273, 0, 380 }, +{ 0x10, 273, 0, 393 }, +{ 0x50, 273, 0, 391 }, +{ 0x30, 273, 0, 392 }, +{ 0x8, 273, 0, 405 }, +{ 0x28, 273, 0, 403 }, +{ 0x18, 273, 0, 404 }, +{ 0x4, 273, 0, 415 }, +{ 0x2, 273, 0, 416 }, +{ 0x1, 273, 0, 417 }, +{ 0x140, 285, 0, 357 }, +{ 0x540, 285, 0, 355 }, +{ 0x340, 285, 0, 356 }, +{ 0xc0, 285, 0, 369 }, +{ 0x2c0, 285, 0, 367 }, +{ 0x1c0, 285, 0, 368 }, +{ 0x20, 285, 0, 384 }, +{ 0xa0, 285, 0, 382 }, +{ 0x60, 285, 0, 383 }, +{ 0x10, 285, 0, 396 }, +{ 0x50, 285, 0, 394 }, +{ 0x30, 285, 0, 395 }, +{ 0x8, 285, 0, 408 }, +{ 0x28, 285, 0, 406 }, +{ 0x18, 285, 0, 407 }, +{ 0x4, 285, 0, 418 }, +{ 0x2, 285, 0, 419 }, +{ 0x1, 285, 0, 420 }, +{ 0x1, 266, 0, 447 }, +{ 0x5, 266, 0, 445 }, +{ 0x3, 266, 0, 446 }, +{ 0x140, 275, 0, 423 }, +{ 0x540, 275, 0, 421 }, +{ 0x340, 275, 0, 422 }, +{ 0xc0, 275, 0, 435 }, +{ 0x2c0, 275, 0, 433 }, +{ 0x1c0, 275, 0, 434 }, +{ 0x20, 275, 0, 450 }, +{ 0xa0, 275, 0, 448 }, +{ 0x60, 275, 0, 449 }, +{ 0x10, 275, 0, 462 }, +{ 0x50, 275, 0, 460 }, +{ 0x30, 275, 0, 461 }, +{ 0x8, 275, 0, 474 }, +{ 0x28, 275, 0, 472 }, +{ 0x18, 275, 0, 473 }, +{ 0x4, 275, 0, 484 }, +{ 0x2, 275, 0, 485 }, +{ 0x1, 275, 0, 486 }, +{ 0x140, 269, 0, 426 }, +{ 0x540, 269, 0, 424 }, +{ 0x340, 269, 0, 425 }, +{ 0xc0, 269, 0, 438 }, +{ 0x2c0, 269, 0, 436 }, +{ 0x1c0, 269, 0, 437 }, +{ 0x20, 269, 0, 453 }, +{ 0xa0, 269, 0, 451 }, +{ 0x60, 269, 0, 452 }, +{ 0x10, 269, 0, 465 }, +{ 0x50, 269, 0, 463 }, +{ 0x30, 269, 0, 464 }, +{ 0x8, 269, 0, 477 }, +{ 0x28, 269, 0, 475 }, +{ 0x18, 269, 0, 476 }, +{ 0x4, 269, 0, 487 }, +{ 0x2, 269, 0, 488 }, +{ 0x1, 269, 0, 489 }, +{ 0x140, 272, 0, 429 }, +{ 0x540, 272, 0, 427 }, +{ 0x340, 272, 0, 428 }, +{ 0xc0, 272, 0, 441 }, +{ 0x2c0, 272, 0, 439 }, +{ 0x1c0, 272, 0, 440 }, +{ 0x20, 272, 0, 456 }, +{ 0xa0, 272, 0, 454 }, +{ 0x60, 272, 0, 455 }, +{ 0x10, 272, 0, 468 }, +{ 0x50, 272, 0, 466 }, +{ 0x30, 272, 0, 467 }, +{ 0x8, 272, 0, 480 }, +{ 0x28, 272, 0, 478 }, +{ 0x18, 272, 0, 479 }, +{ 0x4, 272, 0, 490 }, +{ 0x2, 272, 0, 491 }, +{ 0x1, 272, 0, 492 }, +{ 0x140, 284, 0, 432 }, +{ 0x540, 284, 0, 430 }, +{ 0x340, 284, 0, 431 }, +{ 0xc0, 284, 0, 444 }, +{ 0x2c0, 284, 0, 442 }, +{ 0x1c0, 284, 0, 443 }, +{ 0x20, 284, 0, 459 }, +{ 0xa0, 284, 0, 457 }, +{ 0x60, 284, 0, 458 }, +{ 0x10, 284, 0, 471 }, +{ 0x50, 284, 0, 469 }, +{ 0x30, 284, 0, 470 }, +{ 0x8, 284, 0, 483 }, +{ 0x28, 284, 0, 481 }, +{ 0x18, 284, 0, 482 }, +{ 0x4, 284, 0, 493 }, +{ 0x2, 284, 0, 494 }, +{ 0x1, 284, 0, 495 }, +{ 0x8, 409, 0, 497 }, +{ 0x18, 409, 0, 496 }, +{ 0x4, 409, 0, 499 }, +{ 0xc, 409, 0, 498 }, +{ 0x2, 409, 0, 506 }, +{ 0x1, 409, 0, 507 }, +{ 0x4, 407, 0, 501 }, +{ 0xc, 407, 0, 500 }, +{ 0x2, 407, 0, 508 }, +{ 0x1, 407, 0, 509 }, +{ 0x4, 405, 0, 503 }, +{ 0xc, 405, 0, 502 }, +{ 0x2, 405, 0, 510 }, +{ 0x1, 405, 0, 511 }, +{ 0x4, 401, 0, 505 }, +{ 0xc, 401, 0, 504 }, +{ 0x2, 401, 0, 512 }, +{ 0x1, 401, 0, 513 }, +{ 0xa00, 265, 0, 528 }, +{ 0x2a00, 265, 0, 526 }, +{ 0x1a00, 265, 0, 527 }, +{ 0x600, 265, 0, 540 }, +{ 0x2600, 265, 0, 516 }, +{ 0xa600, 265, 0, 514 }, +{ 0x6600, 265, 0, 515 }, +{ 0x1600, 265, 0, 538 }, +{ 0xe00, 265, 0, 539 }, +{ 0x100, 265, 0, 552 }, +{ 0x500, 265, 0, 550 }, +{ 0x300, 265, 0, 551 }, +{ 0x80, 265, 0, 555 }, +{ 0x280, 265, 0, 553 }, +{ 0x180, 265, 0, 554 }, +{ 0x40, 265, 0, 567 }, +{ 0x140, 265, 0, 565 }, +{ 0xc0, 265, 0, 566 }, +{ 0x20, 265, 0, 579 }, +{ 0xa0, 265, 0, 577 }, +{ 0x60, 265, 0, 578 }, +{ 0x10, 265, 0, 591 }, +{ 0x50, 265, 0, 589 }, +{ 0x30, 265, 0, 590 }, +{ 0x8, 265, 0, 603 }, +{ 0x28, 265, 0, 601 }, +{ 0x18, 265, 0, 602 }, +{ 0x4, 265, 0, 613 }, +{ 0x2, 265, 0, 614 }, +{ 0x1, 265, 0, 615 }, +{ 0x500, 261, 0, 531 }, +{ 0x1500, 261, 0, 529 }, +{ 0xd00, 261, 0, 530 }, +{ 0x300, 261, 0, 543 }, +{ 0x1300, 261, 0, 519 }, +{ 0x5300, 261, 0, 517 }, +{ 0x3300, 261, 0, 518 }, +{ 0xb00, 261, 0, 541 }, +{ 0x700, 261, 0, 542 }, +{ 0x80, 261, 0, 558 }, +{ 0x280, 261, 0, 556 }, +{ 0x180, 261, 0, 557 }, +{ 0x40, 261, 0, 570 }, +{ 0x140, 261, 0, 568 }, +{ 0xc0, 261, 0, 569 }, +{ 0x20, 261, 0, 582 }, +{ 0xa0, 261, 0, 580 }, +{ 0x60, 261, 0, 581 }, +{ 0x10, 261, 0, 594 }, +{ 0x50, 261, 0, 592 }, +{ 0x30, 261, 0, 593 }, +{ 0x8, 261, 0, 606 }, +{ 0x28, 261, 0, 604 }, +{ 0x18, 261, 0, 605 }, +{ 0x4, 261, 0, 616 }, +{ 0x2, 261, 0, 617 }, +{ 0x1, 261, 0, 618 }, +{ 0x500, 258, 0, 534 }, +{ 0x1500, 258, 0, 532 }, +{ 0xd00, 258, 0, 533 }, +{ 0x300, 258, 0, 546 }, +{ 0x1300, 258, 0, 522 }, +{ 0x5300, 258, 0, 520 }, +{ 0x3300, 258, 0, 521 }, +{ 0xb00, 258, 0, 544 }, +{ 0x700, 258, 0, 545 }, +{ 0x80, 258, 0, 561 }, +{ 0x280, 258, 0, 559 }, +{ 0x180, 258, 0, 560 }, +{ 0x40, 258, 0, 573 }, +{ 0x140, 258, 0, 571 }, +{ 0xc0, 258, 0, 572 }, +{ 0x20, 258, 0, 585 }, +{ 0xa0, 258, 0, 583 }, +{ 0x60, 258, 0, 584 }, +{ 0x10, 258, 0, 597 }, +{ 0x50, 258, 0, 595 }, +{ 0x30, 258, 0, 596 }, +{ 0x8, 258, 0, 609 }, +{ 0x28, 258, 0, 607 }, +{ 0x18, 258, 0, 608 }, +{ 0x4, 258, 0, 619 }, +{ 0x2, 258, 0, 620 }, +{ 0x1, 258, 0, 621 }, +{ 0x500, 253, 0, 537 }, +{ 0x1500, 253, 0, 535 }, +{ 0xd00, 253, 0, 536 }, +{ 0x300, 253, 0, 549 }, +{ 0x1300, 253, 0, 525 }, +{ 0x5300, 253, 0, 523 }, +{ 0x3300, 253, 0, 524 }, +{ 0xb00, 253, 0, 547 }, +{ 0x700, 253, 0, 548 }, +{ 0x80, 253, 0, 564 }, +{ 0x280, 253, 0, 562 }, +{ 0x180, 253, 0, 563 }, +{ 0x40, 253, 0, 576 }, +{ 0x140, 253, 0, 574 }, +{ 0xc0, 253, 0, 575 }, +{ 0x20, 253, 0, 588 }, +{ 0xa0, 253, 0, 586 }, +{ 0x60, 253, 0, 587 }, +{ 0x10, 253, 0, 600 }, +{ 0x50, 253, 0, 598 }, +{ 0x30, 253, 0, 599 }, +{ 0x8, 253, 0, 612 }, +{ 0x28, 253, 0, 610 }, +{ 0x18, 253, 0, 611 }, +{ 0x4, 253, 0, 622 }, +{ 0x2, 253, 0, 623 }, +{ 0x1, 253, 0, 624 }, +{ 0x8, 238, 0, 625 }, +{ 0x4, 238, 0, 626 }, +{ 0x2, 238, 0, 627 }, +{ 0x1, 238, 0, 628 }, +{ 0x2, 176, 0, 631 }, +{ 0xa, 176, 0, 629 }, +{ 0x6, 176, 0, 630 }, +{ 0x1, 176, 0, 637 }, +{ 0x5, 176, 0, 635 }, +{ 0x3, 176, 0, 636 }, +{ 0x2, 175, 0, 634 }, +{ 0xa, 175, 0, 632 }, +{ 0x6, 175, 0, 633 }, +{ 0x1, 175, 0, 640 }, +{ 0x5, 175, 0, 638 }, +{ 0x3, 175, 0, 639 }, +{ 0x4, 451, 0, 641 }, +{ 0x2, 451, 0, 642 }, +{ 0x1, 451, 0, 643 }, +{ 0x4, 450, 0, 644 }, +{ 0x2, 450, 0, 645 }, +{ 0x1, 450, 0, 646 }, +{ 0x4, 449, 0, 647 }, +{ 0x2, 449, 0, 648 }, +{ 0x1, 449, 0, 649 }, +{ 0x4, 448, 0, 650 }, +{ 0x2, 448, 0, 651 }, +{ 0x1, 448, 0, 652 }, +{ 0x2, 123, 1, 658 }, +{ 0x2, 124, 0, 657 }, +{ 0xa, 123, 1, 654 }, +{ 0xa, 124, 0, 653 }, +{ 0x6, 123, 1, 656 }, +{ 0x6, 124, 0, 655 }, +{ 0x1, 123, 1, 688 }, +{ 0x1, 124, 0, 687 }, +{ 0x5, 123, 1, 684 }, +{ 0x5, 124, 0, 683 }, +{ 0x3, 123, 1, 686 }, +{ 0x3, 124, 0, 685 }, +{ 0x2, 131, 1, 664 }, +{ 0x2, 132, 0, 663 }, +{ 0xa, 131, 1, 660 }, +{ 0xa, 132, 0, 659 }, +{ 0x6, 131, 1, 662 }, +{ 0x6, 132, 0, 661 }, +{ 0x1, 131, 1, 694 }, +{ 0x1, 132, 0, 693 }, +{ 0x5, 131, 1, 690 }, +{ 0x5, 132, 0, 689 }, +{ 0x3, 131, 1, 692 }, +{ 0x3, 132, 0, 691 }, +{ 0x2, 129, 1, 670 }, +{ 0x2, 130, 0, 669 }, +{ 0xa, 129, 1, 666 }, +{ 0xa, 130, 0, 665 }, +{ 0x6, 129, 1, 668 }, +{ 0x6, 130, 0, 667 }, +{ 0x1, 129, 1, 700 }, +{ 0x1, 130, 0, 699 }, +{ 0x5, 129, 1, 696 }, +{ 0x5, 130, 0, 695 }, +{ 0x3, 129, 1, 698 }, +{ 0x3, 130, 0, 697 }, +{ 0x2, 127, 1, 676 }, +{ 0x2, 128, 0, 675 }, +{ 0xa, 127, 1, 672 }, +{ 0xa, 128, 0, 671 }, +{ 0x6, 127, 1, 674 }, +{ 0x6, 128, 0, 673 }, +{ 0x1, 127, 1, 706 }, +{ 0x1, 128, 0, 705 }, +{ 0x5, 127, 1, 702 }, +{ 0x5, 128, 0, 701 }, +{ 0x3, 127, 1, 704 }, +{ 0x3, 128, 0, 703 }, +{ 0x2, 125, 1, 682 }, +{ 0x2, 126, 0, 681 }, +{ 0xa, 125, 1, 678 }, +{ 0xa, 126, 0, 677 }, +{ 0x6, 125, 1, 680 }, +{ 0x6, 126, 0, 679 }, +{ 0x1, 125, 1, 712 }, +{ 0x1, 126, 0, 711 }, +{ 0x5, 125, 1, 708 }, +{ 0x5, 126, 0, 707 }, +{ 0x3, 125, 1, 710 }, +{ 0x3, 126, 0, 709 }, +{ 0x4, 402, 1, 718 }, +{ 0x4, 403, 0, 717 }, +{ 0xc, 402, 1, 716 }, +{ 0xc, 403, 0, 715 }, +{ 0x2, 402, 1, 728 }, +{ 0x2, 403, 0, 727 }, +{ 0x1, 402, 1, 730 }, +{ 0x1, 403, 0, 729 }, +{ 0x8, 408, 0, 714 }, +{ 0x18, 408, 0, 713 }, +{ 0x4, 408, 0, 720 }, +{ 0xc, 408, 0, 719 }, +{ 0x2, 408, 0, 731 }, +{ 0x1, 408, 0, 732 }, +{ 0x4, 406, 0, 722 }, +{ 0xc, 406, 0, 721 }, +{ 0x2, 406, 0, 733 }, +{ 0x1, 406, 0, 734 }, +{ 0x4, 404, 0, 724 }, +{ 0xc, 404, 0, 723 }, +{ 0x2, 404, 0, 735 }, +{ 0x1, 404, 0, 736 }, +{ 0x4, 400, 0, 726 }, +{ 0xc, 400, 0, 725 }, +{ 0x2, 400, 0, 737 }, +{ 0x1, 400, 0, 738 }, +{ 0xa00, 264, 0, 753 }, +{ 0x2a00, 264, 0, 751 }, +{ 0x1a00, 264, 0, 752 }, +{ 0x600, 264, 0, 765 }, +{ 0x2600, 264, 0, 741 }, +{ 0xa600, 264, 0, 739 }, +{ 0x6600, 264, 0, 740 }, +{ 0x1600, 264, 0, 763 }, +{ 0xe00, 264, 0, 764 }, +{ 0x100, 264, 0, 777 }, +{ 0x500, 264, 0, 775 }, +{ 0x300, 264, 0, 776 }, +{ 0x80, 264, 0, 780 }, +{ 0x280, 264, 0, 778 }, +{ 0x180, 264, 0, 779 }, +{ 0x40, 264, 0, 792 }, +{ 0x140, 264, 0, 790 }, +{ 0xc0, 264, 0, 791 }, +{ 0x20, 264, 0, 804 }, +{ 0xa0, 264, 0, 802 }, +{ 0x60, 264, 0, 803 }, +{ 0x10, 264, 0, 816 }, +{ 0x50, 264, 0, 814 }, +{ 0x30, 264, 0, 815 }, +{ 0x8, 264, 0, 828 }, +{ 0x28, 264, 0, 826 }, +{ 0x18, 264, 0, 827 }, +{ 0x4, 264, 0, 838 }, +{ 0x2, 264, 0, 839 }, +{ 0x1, 264, 0, 840 }, +{ 0x500, 260, 0, 756 }, +{ 0x1500, 260, 0, 754 }, +{ 0xd00, 260, 0, 755 }, +{ 0x300, 260, 0, 768 }, +{ 0x1300, 260, 0, 744 }, +{ 0x5300, 260, 0, 742 }, +{ 0x3300, 260, 0, 743 }, +{ 0xb00, 260, 0, 766 }, +{ 0x700, 260, 0, 767 }, +{ 0x80, 260, 0, 783 }, +{ 0x280, 260, 0, 781 }, +{ 0x180, 260, 0, 782 }, +{ 0x40, 260, 0, 795 }, +{ 0x140, 260, 0, 793 }, +{ 0xc0, 260, 0, 794 }, +{ 0x20, 260, 0, 807 }, +{ 0xa0, 260, 0, 805 }, +{ 0x60, 260, 0, 806 }, +{ 0x10, 260, 0, 819 }, +{ 0x50, 260, 0, 817 }, +{ 0x30, 260, 0, 818 }, +{ 0x8, 260, 0, 831 }, +{ 0x28, 260, 0, 829 }, +{ 0x18, 260, 0, 830 }, +{ 0x4, 260, 0, 841 }, +{ 0x2, 260, 0, 842 }, +{ 0x1, 260, 0, 843 }, +{ 0x500, 257, 0, 759 }, +{ 0x1500, 257, 0, 757 }, +{ 0xd00, 257, 0, 758 }, +{ 0x300, 257, 0, 771 }, +{ 0x1300, 257, 0, 747 }, +{ 0x5300, 257, 0, 745 }, +{ 0x3300, 257, 0, 746 }, +{ 0xb00, 257, 0, 769 }, +{ 0x700, 257, 0, 770 }, +{ 0x80, 257, 0, 786 }, +{ 0x280, 257, 0, 784 }, +{ 0x180, 257, 0, 785 }, +{ 0x40, 257, 0, 798 }, +{ 0x140, 257, 0, 796 }, +{ 0xc0, 257, 0, 797 }, +{ 0x20, 257, 0, 810 }, +{ 0xa0, 257, 0, 808 }, +{ 0x60, 257, 0, 809 }, +{ 0x10, 257, 0, 822 }, +{ 0x50, 257, 0, 820 }, +{ 0x30, 257, 0, 821 }, +{ 0x8, 257, 0, 834 }, +{ 0x28, 257, 0, 832 }, +{ 0x18, 257, 0, 833 }, +{ 0x4, 257, 0, 844 }, +{ 0x2, 257, 0, 845 }, +{ 0x1, 257, 0, 846 }, +{ 0x500, 252, 0, 762 }, +{ 0x1500, 252, 0, 760 }, +{ 0xd00, 252, 0, 761 }, +{ 0x300, 252, 0, 774 }, +{ 0x1300, 252, 0, 750 }, +{ 0x5300, 252, 0, 748 }, +{ 0x3300, 252, 0, 749 }, +{ 0xb00, 252, 0, 772 }, +{ 0x700, 252, 0, 773 }, +{ 0x80, 252, 0, 789 }, +{ 0x280, 252, 0, 787 }, +{ 0x180, 252, 0, 788 }, +{ 0x40, 252, 0, 801 }, +{ 0x140, 252, 0, 799 }, +{ 0xc0, 252, 0, 800 }, +{ 0x20, 252, 0, 813 }, +{ 0xa0, 252, 0, 811 }, +{ 0x60, 252, 0, 812 }, +{ 0x10, 252, 0, 825 }, +{ 0x50, 252, 0, 823 }, +{ 0x30, 252, 0, 824 }, +{ 0x8, 252, 0, 837 }, +{ 0x28, 252, 0, 835 }, +{ 0x18, 252, 0, 836 }, +{ 0x4, 252, 0, 847 }, +{ 0x2, 252, 0, 848 }, +{ 0x1, 252, 0, 849 }, +{ 0x8, 254, 1, 895 }, +{ 0x8, 255, 0, 894 }, +{ 0x28, 254, 1, 891 }, +{ 0x28, 255, 0, 890 }, +{ 0x18, 254, 1, 893 }, +{ 0x18, 255, 0, 892 }, +{ 0x4, 254, 1, 957 }, +{ 0x4, 255, 0, 956 }, +{ 0x2, 254, 1, 959 }, +{ 0x2, 255, 0, 958 }, +{ 0x1, 254, 1, 961 }, +{ 0x1, 255, 0, 960 }, +{ 0xa00, 262, 0, 865 }, +{ 0x2a00, 262, 0, 863 }, +{ 0x1a00, 262, 0, 864 }, +{ 0x600, 262, 0, 877 }, +{ 0x2600, 262, 0, 853 }, +{ 0xa600, 262, 0, 851 }, +{ 0x6600, 262, 0, 852 }, +{ 0x1600, 262, 0, 875 }, +{ 0xe00, 262, 0, 876 }, +{ 0x100, 262, 0, 889 }, +{ 0x500, 262, 0, 887 }, +{ 0x300, 262, 0, 888 }, +{ 0x80, 262, 0, 898 }, +{ 0x280, 262, 0, 896 }, +{ 0x180, 262, 0, 897 }, +{ 0x40, 262, 0, 910 }, +{ 0x140, 262, 0, 908 }, +{ 0xc0, 262, 0, 909 }, +{ 0x20, 262, 0, 922 }, +{ 0xa0, 262, 0, 920 }, +{ 0x60, 262, 0, 921 }, +{ 0x10, 262, 0, 934 }, +{ 0x50, 262, 0, 932 }, +{ 0x30, 262, 0, 933 }, +{ 0x8, 262, 0, 946 }, +{ 0x28, 262, 0, 944 }, +{ 0x18, 262, 0, 945 }, +{ 0x4, 262, 0, 962 }, +{ 0x2, 262, 0, 963 }, +{ 0x1, 262, 1, 964 }, +{ 0x1, 263, 0, 850 }, +{ 0x500, 259, 0, 868 }, +{ 0x1500, 259, 0, 866 }, +{ 0xd00, 259, 0, 867 }, +{ 0x300, 259, 0, 880 }, +{ 0x1300, 259, 0, 856 }, +{ 0x5300, 259, 0, 854 }, +{ 0x3300, 259, 0, 855 }, +{ 0xb00, 259, 0, 878 }, +{ 0x700, 259, 0, 879 }, +{ 0x80, 259, 0, 901 }, +{ 0x280, 259, 0, 899 }, +{ 0x180, 259, 0, 900 }, +{ 0x40, 259, 0, 913 }, +{ 0x140, 259, 0, 911 }, +{ 0xc0, 259, 0, 912 }, +{ 0x20, 259, 0, 925 }, +{ 0xa0, 259, 0, 923 }, +{ 0x60, 259, 0, 924 }, +{ 0x10, 259, 0, 937 }, +{ 0x50, 259, 0, 935 }, +{ 0x30, 259, 0, 936 }, +{ 0x8, 259, 0, 949 }, +{ 0x28, 259, 0, 947 }, +{ 0x18, 259, 0, 948 }, +{ 0x4, 259, 0, 965 }, +{ 0x2, 259, 0, 966 }, +{ 0x1, 259, 0, 967 }, +{ 0x500, 256, 0, 871 }, +{ 0x1500, 256, 0, 869 }, +{ 0xd00, 256, 0, 870 }, +{ 0x300, 256, 0, 883 }, +{ 0x1300, 256, 0, 859 }, +{ 0x5300, 256, 0, 857 }, +{ 0x3300, 256, 0, 858 }, +{ 0xb00, 256, 0, 881 }, +{ 0x700, 256, 0, 882 }, +{ 0x80, 256, 0, 904 }, +{ 0x280, 256, 0, 902 }, +{ 0x180, 256, 0, 903 }, +{ 0x40, 256, 0, 916 }, +{ 0x140, 256, 0, 914 }, +{ 0xc0, 256, 0, 915 }, +{ 0x20, 256, 0, 928 }, +{ 0xa0, 256, 0, 926 }, +{ 0x60, 256, 0, 927 }, +{ 0x10, 256, 0, 940 }, +{ 0x50, 256, 0, 938 }, +{ 0x30, 256, 0, 939 }, +{ 0x8, 256, 0, 952 }, +{ 0x28, 256, 0, 950 }, +{ 0x18, 256, 0, 951 }, +{ 0x4, 256, 0, 968 }, +{ 0x2, 256, 0, 969 }, +{ 0x1, 256, 0, 970 }, +{ 0x500, 251, 0, 874 }, +{ 0x1500, 251, 0, 872 }, +{ 0xd00, 251, 0, 873 }, +{ 0x300, 251, 0, 886 }, +{ 0x1300, 251, 0, 862 }, +{ 0x5300, 251, 0, 860 }, +{ 0x3300, 251, 0, 861 }, +{ 0xb00, 251, 0, 884 }, +{ 0x700, 251, 0, 885 }, +{ 0x80, 251, 0, 907 }, +{ 0x280, 251, 0, 905 }, +{ 0x180, 251, 0, 906 }, +{ 0x40, 251, 0, 919 }, +{ 0x140, 251, 0, 917 }, +{ 0xc0, 251, 0, 918 }, +{ 0x20, 251, 0, 931 }, +{ 0xa0, 251, 0, 929 }, +{ 0x60, 251, 0, 930 }, +{ 0x10, 251, 0, 943 }, +{ 0x50, 251, 0, 941 }, +{ 0x30, 251, 0, 942 }, +{ 0x8, 251, 0, 955 }, +{ 0x28, 251, 0, 953 }, +{ 0x18, 251, 0, 954 }, +{ 0x4, 251, 0, 971 }, +{ 0x2, 251, 0, 972 }, +{ 0x1, 251, 0, 973 }, +{ 0x2, 150, 0, 975 }, +{ 0x1, 150, 0, 976 }, +{ 0x1, 50, 0, 977 }, +{ 0x3, 49, 0, 978 }, +{ 0x1, 428, 0, 979 }, +{ 0x1, 442, 0, 980 }, +{ 0x2, 386, 0, 983 }, +{ 0x1, 386, 0, 984 }, +{ 0x2, 384, 0, 985 }, +{ 0x1, 384, 0, 986 }, +{ 0x1, 383, 0, 987 }, +{ 0x1, 328, 0, 992 }, +{ 0x1, 327, 0, 993 }, +{ 0x1, 326, 0, 994 }, +{ 0x1, 325, 0, 995 }, +{ 0x1, 250, 0, 996 }, +{ 0x1, 249, 0, 997 }, +{ 0x1, 324, 0, 998 }, +{ 0x1, 323, 0, 999 }, +{ 0x1, 322, 0, 1000 }, +{ 0x1, 321, 0, 1001 }, +{ 0x1, 320, 0, 1002 }, +{ 0x1, 319, 0, 1003 }, +{ 0x1, 318, 0, 1004 }, +{ 0x2, 248, 0, 1005 }, +{ 0x1, 248, 0, 1006 }, +{ 0x2, 366, 0, 1012 }, +{ 0x1, 366, 0, 1013 }, +{ 0x1, 317, 0, 1014 }, +{ 0x1, 316, 0, 1015 }, +{ 0x1, 315, 0, 1016 }, +{ 0x1, 314, 0, 1017 }, +{ 0x1, 8, 1, 1019 }, +{ 0x1, 9, 0, 1018 }, +{ 0x1, 313, 0, 1020 }, +{ 0x1, 312, 0, 1021 }, +{ 0x1, 311, 0, 1022 }, +{ 0x1, 310, 0, 1023 }, +{ 0x1, 388, 0, 1024 }, +{ 0x1, 399, 0, 1025 }, +{ 0x1, 389, 0, 1026 }, +{ 0x1, 423, 0, 1027 }, +{ 0x1, 309, 0, 1031 }, +{ 0x1, 247, 0, 1032 }, +{ 0x1, 177, 0, 1035 }, +{ 0x2, 291, 0, 1039 }, +{ 0x1, 291, 0, 1040 }, +{ 0x1, 236, 0, 1041 }, +{ 0x5, 48, 0, 1043 }, +{ 0x3, 48, 0, 1044 }, +{ 0x5, 47, 0, 1045 }, +{ 0x3, 47, 0, 1046 }, +{ 0x1, 365, 0, 1047 }, +{ 0x1, 373, 0, 1048 }, +{ 0x1, 371, 0, 1049 }, +{ 0x1, 392, 0, 1050 }, +{ 0x1, 372, 0, 1051 }, +{ 0x1, 370, 0, 1052 }, +{ 0x2, 378, 0, 1053 }, +{ 0x1, 378, 0, 1055 }, +{ 0x2, 376, 0, 1054 }, +{ 0x1, 376, 0, 1056 }, +{ 0x2, 396, 0, 1057 }, +{ 0x1, 396, 0, 1060 }, +{ 0x2, 377, 0, 1058 }, +{ 0x1, 377, 0, 1061 }, +{ 0x2, 375, 0, 1059 }, +{ 0x1, 375, 0, 1062 }, +{ 0x1, 338, 0, 1063 }, +{ 0x1, 337, 0, 1064 }, +{ 0x1, 369, 0, 1065 }, +{ 0x1, 360, 0, 1066 }, +{ 0x1, 362, 0, 1067 }, +{ 0x1, 359, 0, 1068 }, +{ 0x1, 361, 0, 1069 }, +{ 0x2, 446, 0, 1070 }, +{ 0x1, 446, 0, 1073 }, +{ 0x2, 445, 0, 1071 }, +{ 0x1, 445, 0, 1074 }, +{ 0x2, 444, 0, 1072 }, +{ 0x1, 444, 0, 1075 }, +{ 0x1, 348, 0, 1076 }, +{ 0x2, 347, 0, 1077 }, +{ 0x1, 347, 0, 1078 }, +{ 0x2, 294, 0, 1079 }, +{ 0x1, 294, 0, 1082 }, +{ 0x2, 293, 0, 1080 }, +{ 0x1, 293, 0, 1083 }, +{ 0x2, 292, 0, 1081 }, +{ 0x1, 292, 0, 1084 }, +{ 0x2, 363, 0, 1085 }, +{ 0x1, 363, 0, 1086 }, +{ 0x2, 364, 0, 1087 }, +{ 0x1, 364, 0, 1088 }, +{ 0xa, 438, 1, 1100 }, +{ 0xa, 439, 1, 1099 }, +{ 0xa, 440, 1, 1098 }, +{ 0xa, 441, 0, 1097 }, +{ 0x1a, 438, 1, 1092 }, +{ 0x1a, 439, 1, 1091 }, +{ 0x32, 440, 1, 1090 }, +{ 0x32, 441, 0, 1089 }, +{ 0x6, 438, 1, 1108 }, +{ 0x6, 439, 1, 1107 }, +{ 0x6, 440, 1, 1106 }, +{ 0x6, 441, 0, 1105 }, +{ 0x1, 438, 1, 1120 }, +{ 0x1, 439, 1, 1119 }, +{ 0x1, 440, 1, 1118 }, +{ 0x1, 441, 0, 1117 }, +{ 0x9, 438, 1, 1104 }, +{ 0x9, 439, 1, 1103 }, +{ 0x9, 440, 1, 1102 }, +{ 0x9, 441, 0, 1101 }, +{ 0x19, 438, 1, 1096 }, +{ 0x19, 439, 1, 1095 }, +{ 0x31, 440, 1, 1094 }, +{ 0x31, 441, 0, 1093 }, +{ 0x5, 438, 1, 1112 }, +{ 0x5, 439, 1, 1111 }, +{ 0x5, 440, 1, 1110 }, +{ 0x5, 441, 0, 1109 }, +{ 0x3, 438, 1, 1116 }, +{ 0x3, 439, 1, 1115 }, +{ 0x3, 440, 1, 1114 }, +{ 0x3, 441, 0, 1113 }, +{ 0xa, 429, 1, 1132 }, +{ 0xa, 430, 1, 1131 }, +{ 0xa, 431, 1, 1130 }, +{ 0xa, 432, 0, 1129 }, +{ 0x1a, 429, 1, 1124 }, +{ 0x1a, 430, 1, 1123 }, +{ 0x32, 431, 1, 1122 }, +{ 0x32, 432, 0, 1121 }, +{ 0x6, 429, 1, 1140 }, +{ 0x6, 430, 1, 1139 }, +{ 0x6, 431, 1, 1138 }, +{ 0x6, 432, 0, 1137 }, +{ 0x1, 429, 1, 1152 }, +{ 0x1, 430, 1, 1151 }, +{ 0x1, 431, 1, 1150 }, +{ 0x1, 432, 0, 1149 }, +{ 0x9, 429, 1, 1136 }, +{ 0x9, 430, 1, 1135 }, +{ 0x9, 431, 1, 1134 }, +{ 0x9, 432, 0, 1133 }, +{ 0x19, 429, 1, 1128 }, +{ 0x19, 430, 1, 1127 }, +{ 0x31, 431, 1, 1126 }, +{ 0x31, 432, 0, 1125 }, +{ 0x5, 429, 1, 1144 }, +{ 0x5, 430, 1, 1143 }, +{ 0x5, 431, 1, 1142 }, +{ 0x5, 432, 0, 1141 }, +{ 0x3, 429, 1, 1148 }, +{ 0x3, 430, 1, 1147 }, +{ 0x3, 431, 1, 1146 }, +{ 0x3, 432, 0, 1145 }, +{ 0xa, 433, 1, 1164 }, +{ 0xa, 434, 1, 1163 }, +{ 0xa, 435, 1, 1162 }, +{ 0xa, 436, 0, 1161 }, +{ 0x1a, 433, 1, 1156 }, +{ 0x1a, 434, 1, 1155 }, +{ 0x32, 435, 1, 1154 }, +{ 0x32, 436, 0, 1153 }, +{ 0x6, 433, 1, 1172 }, +{ 0x6, 434, 1, 1171 }, +{ 0x6, 435, 1, 1170 }, +{ 0x6, 436, 0, 1169 }, +{ 0x1, 433, 1, 1184 }, +{ 0x1, 434, 1, 1183 }, +{ 0x1, 435, 1, 1182 }, +{ 0x1, 436, 0, 1181 }, +{ 0x9, 433, 1, 1168 }, +{ 0x9, 434, 1, 1167 }, +{ 0x9, 435, 1, 1166 }, +{ 0x9, 436, 0, 1165 }, +{ 0x19, 433, 1, 1160 }, +{ 0x19, 434, 1, 1159 }, +{ 0x31, 435, 1, 1158 }, +{ 0x31, 436, 0, 1157 }, +{ 0x5, 433, 1, 1176 }, +{ 0x5, 434, 1, 1175 }, +{ 0x5, 435, 1, 1174 }, +{ 0x5, 436, 0, 1173 }, +{ 0x3, 433, 1, 1180 }, +{ 0x3, 434, 1, 1179 }, +{ 0x3, 435, 1, 1178 }, +{ 0x3, 436, 0, 1177 }, +{ 0x1, 139, 0, 1185 }, +{ 0x1, 138, 0, 1186 }, +{ 0x1, 391, 1, 1188 }, +{ 0x1, 137, 0, 1187 }, +{ 0x2, 395, 1, 1190 }, +{ 0x2, 141, 0, 1189 }, +{ 0x1, 395, 1, 1192 }, +{ 0x1, 141, 0, 1191 }, +{ 0x1, 397, 0, 1193 }, +{ 0x1, 136, 0, 1194 }, +{ 0x2, 135, 0, 1195 }, +{ 0x2, 134, 0, 1196 }, +{ 0x1, 459, 1, 1202 }, +{ 0x1, 246, 0, 1033 }, +{ 0x1, 458, 0, 1203 }, +{ 0x1, 457, 1, 1204 }, +{ 0x1, 245, 0, 1042 }, +{ 0x1, 308, 0, 1205 }, +{ 0x1, 307, 1, 1206 }, +{ 0x1, 290, 0, 1034 }, +{ 0x1, 306, 0, 1207 }, +{ 0x1, 305, 1, 1208 }, +{ 0x1, 427, 0, 1036 }, +{ 0x1, 304, 1, 1209 }, +{ 0x1, 398, 0, 1038 }, +{ 0x1, 303, 0, 1210 }, +{ 0x1, 302, 0, 1211 }, +{ 0x1, 301, 0, 1212 }, +{ 0x1, 300, 1, 1213 }, +{ 0x2, 398, 0, 1037 }, +{ 0x10, 299, 0, 1217 }, +{ 0x90, 299, 0, 1215 }, +{ 0x190, 299, 0, 1214 }, +{ 0x50, 299, 0, 1216 }, +{ 0x30, 299, 0, 1219 }, +{ 0x70, 299, 0, 1218 }, +{ 0x8, 299, 0, 1221 }, +{ 0x18, 299, 0, 1220 }, +{ 0x4, 299, 0, 1222 }, +{ 0x1, 299, 0, 1225 }, +{ 0x3, 299, 0, 1224 }, +{ 0x1, 298, 1, 1226 }, +{ 0x2, 299, 0, 1223 }, +{ 0x3, 46, 0, 1227 }, +{ 0x1, 241, 1, 1228 }, +{ 0x1, 242, 1, 1028 }, +{ 0x1, 243, 0, 88 }, +{ 0x1, 341, 1, 1229 }, +{ 0x1, 342, 1, 1029 }, +{ 0x1, 343, 0, 89 }, +{ 0x1, 34, 1, 1230 }, +{ 0x1, 35, 1, 1030 }, +{ 0x1, 36, 0, 90 }, +{ 0x1, 230, 0, 1231 }, +{ 0x4, 452, 0, 1232 }, +{ 0x2, 452, 0, 1233 }, +{ 0x1, 452, 1, 1235 }, +{ 0x1, 453, 0, 1234 }, +{ 0x8, 454, 0, 1236 }, +{ 0x4, 454, 0, 1237 }, +{ 0x1, 454, 1, 1239 }, +{ 0x2, 454, 0, 1238 }, +{ 0x8, 219, 0, 1240 }, +{ 0x4, 219, 0, 1241 }, +{ 0x2, 219, 0, 1242 }, +{ 0x1, 219, 1, 1244 }, +{ 0x1, 220, 0, 1243 }, +{ 0x10, 221, 0, 1245 }, +{ 0x8, 221, 0, 1246 }, +{ 0x4, 221, 0, 1247 }, +{ 0x1, 221, 1, 1249 }, +{ 0x2, 221, 0, 1248 }, +{ 0x220, 191, 0, 1250 }, +{ 0x120, 191, 0, 1251 }, +{ 0xa0, 191, 0, 1252 }, +{ 0x60, 191, 1, 1254 }, +{ 0x4, 192, 0, 1253 }, +{ 0x110, 191, 0, 1260 }, +{ 0x90, 191, 0, 1261 }, +{ 0x50, 191, 0, 1262 }, +{ 0x30, 191, 1, 1264 }, +{ 0x2, 192, 0, 1263 }, +{ 0x8, 191, 0, 1265 }, +{ 0x4, 191, 0, 1266 }, +{ 0x2, 191, 0, 1267 }, +{ 0x1, 191, 1, 1269 }, +{ 0x1, 192, 0, 1268 }, +{ 0x440, 193, 0, 1255 }, +{ 0x240, 193, 0, 1256 }, +{ 0x140, 193, 0, 1257 }, +{ 0xc0, 193, 1, 1259 }, +{ 0x40, 193, 0, 1258 }, +{ 0x220, 193, 0, 1270 }, +{ 0x120, 193, 0, 1271 }, +{ 0xa0, 193, 0, 1272 }, +{ 0x60, 193, 1, 1274 }, +{ 0x20, 193, 0, 1273 }, +{ 0x10, 193, 0, 1275 }, +{ 0x8, 193, 0, 1276 }, +{ 0x4, 193, 0, 1277 }, +{ 0x1, 193, 1, 1279 }, +{ 0x2, 193, 0, 1278 }, +{ 0x8, 215, 0, 1280 }, +{ 0x4, 215, 0, 1281 }, +{ 0x2, 215, 0, 1282 }, +{ 0x1, 215, 1, 1284 }, +{ 0x1, 216, 0, 1283 }, +{ 0x220, 187, 0, 1285 }, +{ 0x120, 187, 0, 1286 }, +{ 0xa0, 187, 0, 1287 }, +{ 0x60, 187, 1, 1289 }, +{ 0x4, 188, 0, 1288 }, +{ 0x110, 187, 0, 1295 }, +{ 0x90, 187, 0, 1296 }, +{ 0x50, 187, 0, 1297 }, +{ 0x30, 187, 1, 1299 }, +{ 0x2, 188, 0, 1298 }, +{ 0x8, 187, 0, 1300 }, +{ 0x4, 187, 0, 1301 }, +{ 0x2, 187, 0, 1302 }, +{ 0x1, 187, 1, 1304 }, +{ 0x1, 188, 0, 1303 }, +{ 0x440, 233, 0, 1290 }, +{ 0x240, 233, 0, 1291 }, +{ 0x140, 233, 0, 1292 }, +{ 0xc0, 233, 1, 1294 }, +{ 0x40, 233, 0, 1293 }, +{ 0x220, 233, 0, 1305 }, +{ 0x120, 233, 0, 1306 }, +{ 0xa0, 233, 0, 1307 }, +{ 0x60, 233, 1, 1309 }, +{ 0x20, 233, 0, 1308 }, +{ 0x10, 233, 0, 1310 }, +{ 0x8, 233, 0, 1311 }, +{ 0x4, 233, 0, 1312 }, +{ 0x1, 233, 1, 1314 }, +{ 0x2, 233, 0, 1313 }, +{ 0x8, 207, 0, 1315 }, +{ 0x4, 207, 0, 1316 }, +{ 0x2, 207, 0, 1317 }, +{ 0x1, 207, 1, 1319 }, +{ 0x1, 208, 0, 1318 }, +{ 0x10, 214, 0, 1320 }, +{ 0x8, 214, 0, 1321 }, +{ 0x4, 214, 0, 1322 }, +{ 0x1, 214, 1, 1324 }, +{ 0x2, 214, 0, 1323 }, +{ 0x220, 178, 0, 1325 }, +{ 0x120, 178, 0, 1326 }, +{ 0xa0, 178, 0, 1327 }, +{ 0x60, 178, 1, 1329 }, +{ 0x4, 179, 0, 1328 }, +{ 0x110, 178, 0, 1350 }, +{ 0x90, 178, 0, 1351 }, +{ 0x50, 178, 0, 1352 }, +{ 0x30, 178, 1, 1354 }, +{ 0x2, 179, 0, 1353 }, +{ 0x8, 178, 0, 1355 }, +{ 0x4, 178, 0, 1356 }, +{ 0x2, 178, 0, 1357 }, +{ 0x1, 178, 1, 1359 }, +{ 0x1, 179, 0, 1358 }, +{ 0x440, 186, 0, 1330 }, +{ 0x240, 186, 0, 1331 }, +{ 0x140, 186, 0, 1332 }, +{ 0xc0, 186, 1, 1334 }, +{ 0x40, 186, 0, 1333 }, +{ 0x220, 186, 0, 1360 }, +{ 0x120, 186, 0, 1361 }, +{ 0xa0, 186, 0, 1362 }, +{ 0x60, 186, 1, 1364 }, +{ 0x20, 186, 0, 1363 }, +{ 0x10, 186, 0, 1365 }, +{ 0x8, 186, 0, 1366 }, +{ 0x4, 186, 0, 1367 }, +{ 0x1, 186, 1, 1369 }, +{ 0x2, 186, 0, 1368 }, +{ 0x440, 143, 0, 1335 }, +{ 0x240, 143, 0, 1336 }, +{ 0x140, 143, 0, 1337 }, +{ 0xc0, 143, 1, 1339 }, +{ 0x40, 143, 0, 1338 }, +{ 0x220, 143, 0, 1370 }, +{ 0x120, 143, 0, 1371 }, +{ 0xa0, 143, 0, 1372 }, +{ 0x60, 143, 1, 1374 }, +{ 0x20, 143, 0, 1373 }, +{ 0x10, 143, 0, 1375 }, +{ 0x8, 143, 0, 1376 }, +{ 0x1, 143, 1, 1379 }, +{ 0x2, 143, 0, 1378 }, +{ 0x440, 194, 1, 1345 }, +{ 0x441, 174, 0, 1340 }, +{ 0x240, 194, 1, 1346 }, +{ 0x241, 174, 0, 1341 }, +{ 0x140, 194, 1, 1347 }, +{ 0x141, 174, 0, 1342 }, +{ 0xc0, 194, 1, 1349 }, +{ 0x40, 194, 1, 1348 }, +{ 0xc1, 174, 1, 1344 }, +{ 0x41, 174, 0, 1343 }, +{ 0x220, 194, 1, 1390 }, +{ 0x221, 174, 0, 1380 }, +{ 0x120, 194, 1, 1391 }, +{ 0x121, 174, 0, 1381 }, +{ 0xa0, 194, 1, 1392 }, +{ 0xa1, 174, 0, 1382 }, +{ 0x60, 194, 1, 1394 }, +{ 0x20, 194, 1, 1393 }, +{ 0x61, 174, 1, 1384 }, +{ 0x21, 174, 0, 1383 }, +{ 0x10, 194, 1, 1395 }, +{ 0x11, 174, 0, 1385 }, +{ 0x8, 194, 1, 1396 }, +{ 0x9, 174, 0, 1386 }, +{ 0x4, 194, 1, 1397 }, +{ 0x5, 174, 0, 1387 }, +{ 0x1, 194, 1, 1399 }, +{ 0x2, 194, 1, 1398 }, +{ 0x3, 174, 1, 1389 }, +{ 0x1, 174, 0, 1388 }, +{ 0x1, 153, 1, 1407 }, +{ 0x1, 154, 1, 1406 }, +{ 0x1, 155, 1, 1405 }, +{ 0x1, 156, 0, 1404 }, +{ 0x3, 153, 1, 1403 }, +{ 0x3, 154, 1, 1402 }, +{ 0x3, 155, 1, 1401 }, +{ 0x3, 156, 0, 1400 }, +{ 0x1108, 159, 1, 1569 }, +{ 0x1108, 160, 1, 1568 }, +{ 0x1108, 165, 1, 1409 }, +{ 0x1108, 166, 0, 1408 }, +{ 0x908, 159, 1, 1571 }, +{ 0x908, 160, 1, 1570 }, +{ 0x908, 165, 1, 1411 }, +{ 0x908, 166, 0, 1410 }, +{ 0x508, 159, 1, 1573 }, +{ 0x508, 160, 1, 1572 }, +{ 0x508, 165, 1, 1413 }, +{ 0x508, 166, 0, 1412 }, +{ 0x308, 159, 1, 1577 }, +{ 0x308, 160, 1, 1576 }, +{ 0x108, 160, 1, 1574 }, +{ 0x18, 161, 1, 1575 }, +{ 0x308, 165, 1, 1417 }, +{ 0x308, 166, 1, 1416 }, +{ 0x108, 166, 1, 1414 }, +{ 0x18, 167, 0, 1415 }, +{ 0x88, 159, 1, 1609 }, +{ 0x88, 160, 1, 1608 }, +{ 0x88, 165, 1, 1489 }, +{ 0x88, 166, 0, 1488 }, +{ 0x48, 159, 1, 1611 }, +{ 0x48, 160, 1, 1610 }, +{ 0x48, 165, 1, 1491 }, +{ 0x48, 166, 0, 1490 }, +{ 0x28, 159, 1, 1613 }, +{ 0x28, 160, 1, 1612 }, +{ 0x28, 165, 1, 1493 }, +{ 0x28, 166, 0, 1492 }, +{ 0x18, 159, 1, 1617 }, +{ 0x18, 160, 1, 1616 }, +{ 0x8, 160, 1, 1614 }, +{ 0x8, 161, 1, 1615 }, +{ 0x18, 165, 1, 1497 }, +{ 0x18, 166, 1, 1496 }, +{ 0x8, 166, 1, 1494 }, +{ 0x8, 167, 0, 1495 }, +{ 0x884, 159, 1, 1579 }, +{ 0x884, 160, 1, 1578 }, +{ 0x442, 162, 1, 1469 }, +{ 0x442, 163, 1, 1468 }, +{ 0x884, 165, 1, 1439 }, +{ 0x884, 166, 1, 1438 }, +{ 0x442, 168, 1, 1419 }, +{ 0x442, 169, 0, 1418 }, +{ 0x484, 159, 1, 1581 }, +{ 0x484, 160, 1, 1580 }, +{ 0x242, 162, 1, 1471 }, +{ 0x242, 163, 1, 1470 }, +{ 0x484, 165, 1, 1441 }, +{ 0x484, 166, 1, 1440 }, +{ 0x242, 168, 1, 1421 }, +{ 0x242, 169, 0, 1420 }, +{ 0x284, 159, 1, 1583 }, +{ 0x284, 160, 1, 1582 }, +{ 0x142, 162, 1, 1473 }, +{ 0x142, 163, 1, 1472 }, +{ 0x284, 165, 1, 1443 }, +{ 0x284, 166, 1, 1442 }, +{ 0x142, 168, 1, 1423 }, +{ 0x142, 169, 0, 1422 }, +{ 0x184, 159, 1, 1587 }, +{ 0x184, 160, 1, 1586 }, +{ 0x84, 160, 1, 1584 }, +{ 0xc, 161, 1, 1585 }, +{ 0xc2, 162, 1, 1477 }, +{ 0xc2, 163, 1, 1476 }, +{ 0x42, 163, 1, 1474 }, +{ 0x6, 164, 1, 1475 }, +{ 0x184, 165, 1, 1447 }, +{ 0x184, 166, 1, 1446 }, +{ 0x84, 166, 1, 1444 }, +{ 0xc, 167, 1, 1445 }, +{ 0xc2, 168, 1, 1427 }, +{ 0xc2, 169, 1, 1426 }, +{ 0x42, 169, 1, 1424 }, +{ 0x6, 170, 0, 1425 }, +{ 0x44, 159, 1, 1619 }, +{ 0x44, 160, 1, 1618 }, +{ 0x22, 162, 1, 1549 }, +{ 0x22, 163, 1, 1548 }, +{ 0x44, 165, 1, 1519 }, +{ 0x44, 166, 1, 1518 }, +{ 0x22, 168, 1, 1499 }, +{ 0x22, 169, 0, 1498 }, +{ 0x24, 159, 1, 1621 }, +{ 0x24, 160, 1, 1620 }, +{ 0x12, 162, 1, 1551 }, +{ 0x12, 163, 1, 1550 }, +{ 0x24, 165, 1, 1521 }, +{ 0x24, 166, 1, 1520 }, +{ 0x12, 168, 1, 1501 }, +{ 0x12, 169, 0, 1500 }, +{ 0x14, 159, 1, 1623 }, +{ 0x14, 160, 1, 1622 }, +{ 0xa, 162, 1, 1553 }, +{ 0xa, 163, 1, 1552 }, +{ 0x14, 165, 1, 1523 }, +{ 0x14, 166, 1, 1522 }, +{ 0xa, 168, 1, 1503 }, +{ 0xa, 169, 0, 1502 }, +{ 0xc, 159, 1, 1627 }, +{ 0xc, 160, 1, 1626 }, +{ 0x4, 160, 1, 1624 }, +{ 0x4, 161, 1, 1625 }, +{ 0x6, 162, 1, 1557 }, +{ 0x6, 163, 1, 1556 }, +{ 0x2, 163, 1, 1554 }, +{ 0x2, 164, 1, 1555 }, +{ 0xc, 165, 1, 1527 }, +{ 0xc, 166, 1, 1526 }, +{ 0x4, 166, 1, 1524 }, +{ 0x4, 167, 1, 1525 }, +{ 0x6, 168, 1, 1507 }, +{ 0x6, 169, 1, 1506 }, +{ 0x2, 169, 1, 1504 }, +{ 0x2, 170, 0, 1505 }, +{ 0x442, 159, 1, 1589 }, +{ 0x442, 160, 1, 1588 }, +{ 0x221, 162, 1, 1479 }, +{ 0x221, 163, 1, 1478 }, +{ 0x442, 165, 1, 1449 }, +{ 0x442, 166, 1, 1448 }, +{ 0x221, 168, 1, 1429 }, +{ 0x221, 169, 0, 1428 }, +{ 0x242, 159, 1, 1591 }, +{ 0x242, 160, 1, 1590 }, +{ 0x121, 162, 1, 1481 }, +{ 0x121, 163, 1, 1480 }, +{ 0x242, 165, 1, 1451 }, +{ 0x242, 166, 1, 1450 }, +{ 0x121, 168, 1, 1431 }, +{ 0x121, 169, 0, 1430 }, +{ 0x142, 159, 1, 1593 }, +{ 0x142, 160, 1, 1592 }, +{ 0xa1, 162, 1, 1483 }, +{ 0xa1, 163, 1, 1482 }, +{ 0x142, 165, 1, 1453 }, +{ 0x142, 166, 1, 1452 }, +{ 0xa1, 168, 1, 1433 }, +{ 0xa1, 169, 0, 1432 }, +{ 0xc2, 159, 1, 1597 }, +{ 0xc2, 160, 1, 1596 }, +{ 0x42, 160, 1, 1594 }, +{ 0x6, 161, 1, 1595 }, +{ 0x61, 162, 1, 1487 }, +{ 0x61, 163, 1, 1486 }, +{ 0x21, 163, 1, 1484 }, +{ 0x3, 164, 1, 1485 }, +{ 0xc2, 165, 1, 1457 }, +{ 0xc2, 166, 1, 1456 }, +{ 0x42, 166, 1, 1454 }, +{ 0x6, 167, 1, 1455 }, +{ 0x61, 168, 1, 1437 }, +{ 0x61, 169, 1, 1436 }, +{ 0x21, 169, 1, 1434 }, +{ 0x3, 170, 0, 1435 }, +{ 0x22, 159, 1, 1629 }, +{ 0x22, 160, 1, 1628 }, +{ 0x11, 162, 1, 1559 }, +{ 0x11, 163, 1, 1558 }, +{ 0x22, 165, 1, 1529 }, +{ 0x22, 166, 1, 1528 }, +{ 0x11, 168, 1, 1509 }, +{ 0x11, 169, 0, 1508 }, +{ 0x12, 159, 1, 1631 }, +{ 0x12, 160, 1, 1630 }, +{ 0x9, 162, 1, 1561 }, +{ 0x9, 163, 1, 1560 }, +{ 0x12, 165, 1, 1531 }, +{ 0x12, 166, 1, 1530 }, +{ 0x9, 168, 1, 1511 }, +{ 0x9, 169, 0, 1510 }, +{ 0xa, 159, 1, 1633 }, +{ 0xa, 160, 1, 1632 }, +{ 0x5, 162, 1, 1563 }, +{ 0x5, 163, 1, 1562 }, +{ 0xa, 165, 1, 1533 }, +{ 0xa, 166, 1, 1532 }, +{ 0x5, 168, 1, 1513 }, +{ 0x5, 169, 0, 1512 }, +{ 0x6, 159, 1, 1637 }, +{ 0x6, 160, 1, 1636 }, +{ 0x2, 160, 1, 1634 }, +{ 0x2, 161, 1, 1635 }, +{ 0x3, 162, 1, 1567 }, +{ 0x3, 163, 1, 1566 }, +{ 0x1, 163, 1, 1564 }, +{ 0x1, 164, 1, 1565 }, +{ 0x6, 165, 1, 1537 }, +{ 0x6, 166, 1, 1536 }, +{ 0x2, 166, 1, 1534 }, +{ 0x2, 167, 1, 1535 }, +{ 0x3, 168, 1, 1517 }, +{ 0x3, 169, 1, 1516 }, +{ 0x1, 169, 1, 1514 }, +{ 0x1, 170, 0, 1515 }, +{ 0x221, 159, 1, 1599 }, +{ 0x221, 160, 1, 1598 }, +{ 0x221, 165, 1, 1459 }, +{ 0x221, 166, 0, 1458 }, +{ 0x121, 159, 1, 1601 }, +{ 0x121, 160, 1, 1600 }, +{ 0x121, 165, 1, 1461 }, +{ 0x121, 166, 0, 1460 }, +{ 0xa1, 159, 1, 1603 }, +{ 0xa1, 160, 1, 1602 }, +{ 0xa1, 165, 1, 1463 }, +{ 0xa1, 166, 0, 1462 }, +{ 0x61, 159, 1, 1607 }, +{ 0x61, 160, 1, 1606 }, +{ 0x21, 160, 1, 1604 }, +{ 0x3, 161, 1, 1605 }, +{ 0x61, 165, 1, 1467 }, +{ 0x61, 166, 1, 1466 }, +{ 0x21, 166, 1, 1464 }, +{ 0x3, 167, 0, 1465 }, +{ 0x11, 159, 1, 1639 }, +{ 0x11, 160, 1, 1638 }, +{ 0x11, 165, 1, 1539 }, +{ 0x11, 166, 0, 1538 }, +{ 0x9, 159, 1, 1641 }, +{ 0x9, 160, 1, 1640 }, +{ 0x9, 165, 1, 1541 }, +{ 0x9, 166, 0, 1540 }, +{ 0x5, 159, 1, 1643 }, +{ 0x5, 160, 1, 1642 }, +{ 0x5, 165, 1, 1543 }, +{ 0x5, 166, 0, 1542 }, +{ 0x3, 159, 1, 1647 }, +{ 0x3, 160, 1, 1646 }, +{ 0x1, 160, 1, 1644 }, +{ 0x1, 161, 1, 1645 }, +{ 0x3, 165, 1, 1547 }, +{ 0x3, 166, 1, 1546 }, +{ 0x1, 166, 1, 1544 }, +{ 0x1, 167, 0, 1545 }, +{ 0x442, 205, 0, 1648 }, +{ 0x242, 205, 0, 1649 }, +{ 0x142, 205, 0, 1650 }, +{ 0xc2, 205, 1, 1652 }, +{ 0x6, 206, 1, 1651 }, +{ 0x1, 443, 0, 981 }, +{ 0x22, 205, 0, 1658 }, +{ 0x12, 205, 0, 1659 }, +{ 0xa, 205, 0, 1660 }, +{ 0x6, 205, 1, 1662 }, +{ 0x2, 206, 1, 1661 }, +{ 0x2, 367, 0, 1010 }, +{ 0x221, 205, 0, 1653 }, +{ 0x121, 205, 0, 1654 }, +{ 0xa1, 205, 0, 1655 }, +{ 0x61, 205, 1, 1657 }, +{ 0x3, 206, 1, 1656 }, +{ 0x1, 437, 0, 982 }, +{ 0x11, 205, 0, 1663 }, +{ 0x9, 205, 0, 1664 }, +{ 0x5, 205, 0, 1665 }, +{ 0x3, 205, 1, 1667 }, +{ 0x1, 206, 1, 1666 }, +{ 0x1, 367, 0, 1011 }, +{ 0x4, 211, 0, 1668 }, +{ 0x1, 211, 0, 1670 }, +{ 0x1, 218, 0, 1671 }, +{ 0x1, 217, 1, 1672 }, +{ 0x2, 211, 0, 1669 }, +{ 0x1, 196, 0, 1673 }, +{ 0x880, 202, 0, 1674 }, +{ 0x480, 202, 0, 1675 }, +{ 0x280, 202, 0, 1676 }, +{ 0x180, 202, 1, 1678 }, +{ 0x80, 203, 0, 1677 }, +{ 0x440, 202, 1, 1689 }, +{ 0x88, 204, 0, 1679 }, +{ 0x240, 202, 1, 1690 }, +{ 0x48, 204, 0, 1680 }, +{ 0x140, 202, 1, 1691 }, +{ 0x28, 204, 0, 1681 }, +{ 0xc0, 202, 1, 1693 }, +{ 0x40, 203, 1, 1692 }, +{ 0x18, 204, 1, 1683 }, +{ 0x8, 204, 0, 1682 }, +{ 0x220, 202, 1, 1694 }, +{ 0x44, 204, 0, 1684 }, +{ 0x120, 202, 1, 1695 }, +{ 0x24, 204, 0, 1685 }, +{ 0xa0, 202, 1, 1696 }, +{ 0x14, 204, 0, 1686 }, +{ 0x60, 202, 1, 1698 }, +{ 0x20, 203, 1, 1697 }, +{ 0xc, 204, 1, 1688 }, +{ 0x4, 204, 0, 1687 }, +{ 0x110, 202, 0, 1699 }, +{ 0x90, 202, 0, 1700 }, +{ 0x50, 202, 0, 1701 }, +{ 0x30, 202, 1, 1703 }, +{ 0x10, 203, 1, 1702 }, +{ 0x1, 385, 0, 974 }, +{ 0x88, 202, 0, 1704 }, +{ 0x48, 202, 0, 1705 }, +{ 0x28, 202, 0, 1706 }, +{ 0x18, 202, 1, 1708 }, +{ 0x8, 203, 1, 1707 }, +{ 0xc, 368, 0, 1007 }, +{ 0x44, 202, 1, 1719 }, +{ 0x22, 204, 0, 1709 }, +{ 0x24, 202, 1, 1720 }, +{ 0x12, 204, 0, 1710 }, +{ 0x14, 202, 1, 1721 }, +{ 0xa, 204, 0, 1711 }, +{ 0xc, 202, 1, 1723 }, +{ 0x4, 203, 1, 1722 }, +{ 0x6, 204, 1, 1713 }, +{ 0x2, 204, 1, 1712 }, +{ 0x6, 368, 0, 1008 }, +{ 0x22, 202, 1, 1724 }, +{ 0x11, 204, 0, 1714 }, +{ 0x12, 202, 1, 1725 }, +{ 0x9, 204, 0, 1715 }, +{ 0xa, 202, 1, 1726 }, +{ 0x5, 204, 0, 1716 }, +{ 0x6, 202, 1, 1728 }, +{ 0x2, 203, 1, 1727 }, +{ 0x3, 204, 1, 1718 }, +{ 0x1, 204, 1, 1717 }, +{ 0x3, 368, 0, 1009 }, +{ 0x11, 202, 0, 1729 }, +{ 0x9, 202, 0, 1730 }, +{ 0x5, 202, 0, 1731 }, +{ 0x3, 202, 1, 1733 }, +{ 0x1, 203, 0, 1732 }, +{ 0x8, 198, 0, 1734 }, +{ 0x4, 198, 0, 1735 }, +{ 0x2, 198, 0, 1736 }, +{ 0x1, 198, 1, 1738 }, +{ 0x1, 199, 1, 1737 }, +{ 0x1, 332, 0, 988 }, +{ 0x8, 200, 0, 1739 }, +{ 0x4, 200, 0, 1740 }, +{ 0x2, 200, 0, 1741 }, +{ 0x1, 200, 1, 1743 }, +{ 0x1, 201, 1, 1742 }, +{ 0x1, 331, 0, 989 }, +{ 0x8, 209, 0, 1744 }, +{ 0x4, 209, 0, 1745 }, +{ 0x2, 209, 0, 1746 }, +{ 0x1, 209, 1, 1748 }, +{ 0x1, 210, 1, 1747 }, +{ 0x1, 330, 0, 990 }, +{ 0x8, 212, 0, 1749 }, +{ 0x4, 212, 0, 1750 }, +{ 0x2, 212, 0, 1751 }, +{ 0x1, 212, 1, 1753 }, +{ 0x1, 213, 1, 1752 }, +{ 0x1, 329, 0, 991 }, +{ 0x8, 224, 0, 1754 }, +{ 0x4, 224, 0, 1755 }, +{ 0x2, 224, 0, 1756 }, +{ 0x1, 224, 1, 1758 }, +{ 0x1, 225, 0, 1757 }, +{ 0x8, 222, 0, 1759 }, +{ 0x4, 222, 0, 1760 }, +{ 0x2, 222, 0, 1761 }, +{ 0x1, 222, 1, 1763 }, +{ 0x1, 223, 0, 1762 }, +{ 0x1, 240, 0, 1764 }, +{ 0x1, 340, 0, 1765 }, +{ 0x1, 33, 0, 1766 }, +{ 0x8, 151, 0, 1767 }, +{ 0x4, 151, 0, 1768 }, +{ 0x2, 151, 0, 1769 }, +{ 0x1, 151, 1, 1771 }, +{ 0x1, 152, 0, 1770 }, +{ 0x8, 157, 0, 1772 }, +{ 0x4, 157, 0, 1773 }, +{ 0x2, 157, 0, 1774 }, +{ 0x1, 157, 1, 1776 }, +{ 0x1, 158, 0, 1775 }, +{ 0x8, 231, 0, 1777 }, +{ 0x4, 231, 0, 1778 }, +{ 0x2, 231, 0, 1779 }, +{ 0x1, 231, 1, 1781 }, +{ 0x1, 232, 0, 1780 }, +{ 0x1, 173, 0, 1782 }, +{ 0x442, 171, 0, 1783 }, +{ 0x242, 171, 0, 1784 }, +{ 0x142, 171, 0, 1785 }, +{ 0xc2, 171, 1, 1787 }, +{ 0x6, 172, 0, 1786 }, +{ 0x22, 171, 0, 1793 }, +{ 0x12, 171, 0, 1794 }, +{ 0xa, 171, 0, 1795 }, +{ 0x6, 171, 1, 1797 }, +{ 0x2, 172, 1, 1796 }, +{ 0x1, 135, 0, 1197 }, +{ 0x221, 171, 0, 1788 }, +{ 0x121, 171, 0, 1789 }, +{ 0xa1, 171, 0, 1790 }, +{ 0x61, 171, 1, 1792 }, +{ 0x3, 172, 0, 1791 }, +{ 0x11, 171, 0, 1798 }, +{ 0x9, 171, 0, 1799 }, +{ 0x5, 171, 0, 1800 }, +{ 0x3, 171, 1, 1802 }, +{ 0x1, 172, 1, 1801 }, +{ 0x1, 134, 0, 1198 }, +{ 0x1, 237, 0, 1803 }, +{ 0x1, 195, 0, 1804 }, +{ 0x1, 149, 0, 1805 }, +{ 0x1, 148, 0, 1806 }, +{ 0x4, 234, 0, 1807 }, +{ 0x2, 234, 0, 1808 }, +{ 0x1, 234, 0, 1809 }, +{ 0x1, 197, 0, 1810 }, +{ 0x2, 235, 0, 1811 }, +{ 0x1, 235, 0, 1812 }, +{ 0x4, 185, 0, 1813 }, +{ 0x2, 185, 0, 1814 }, +{ 0x1, 185, 0, 1815 }, +{ 0x4, 182, 0, 1816 }, +{ 0x1, 190, 0, 1819 }, +{ 0x1, 189, 1, 1820 }, +{ 0x2, 182, 0, 1817 }, +{ 0x1, 142, 0, 1821 }, +{ 0x1, 297, 1, 1822 }, +{ 0x1, 182, 0, 1818 }, +{ 0x8, 144, 0, 1823 }, +{ 0x4, 144, 0, 1824 }, +{ 0x2, 144, 0, 1825 }, +{ 0x1, 144, 1, 1827 }, +{ 0x1, 145, 0, 1826 }, +{ 0x8, 146, 0, 1828 }, +{ 0x4, 146, 0, 1829 }, +{ 0x2, 146, 0, 1830 }, +{ 0x1, 146, 1, 1832 }, +{ 0x1, 147, 1, 1831 }, +{ 0x1, 426, 0, 1199 }, +{ 0x8, 180, 0, 1833 }, +{ 0x4, 180, 0, 1834 }, +{ 0x2, 180, 0, 1835 }, +{ 0x1, 180, 1, 1837 }, +{ 0x1, 181, 1, 1836 }, +{ 0x1, 425, 0, 1200 }, +{ 0x8, 183, 0, 1838 }, +{ 0x4, 183, 0, 1839 }, +{ 0x2, 183, 0, 1840 }, +{ 0x1, 183, 1, 1842 }, +{ 0x1, 184, 1, 1841 }, +{ 0x1, 424, 0, 1201 }, +{ 0x8, 228, 0, 1843 }, +{ 0x4, 228, 0, 1844 }, +{ 0x2, 228, 0, 1845 }, +{ 0x1, 228, 1, 1847 }, +{ 0x1, 229, 0, 1846 }, +{ 0x8, 226, 0, 1848 }, +{ 0x4, 226, 0, 1849 }, +{ 0x2, 226, 0, 1850 }, +{ 0x1, 226, 1, 1852 }, +{ 0x1, 227, 0, 1851 }, +{ 0x8, 44, 0, 1857 }, +{ 0x18, 44, 0, 1853 }, +{ 0x4, 44, 0, 1858 }, +{ 0xc, 44, 0, 1854 }, +{ 0x2, 44, 0, 1859 }, +{ 0x6, 44, 0, 1855 }, +{ 0x1, 44, 0, 1860 }, +{ 0x3, 44, 0, 1856 }, +{ 0x51, 30, 0, 1862 }, +{ 0xd1, 30, 0, 1861 }, +{ 0x31, 30, 1, 1872 }, +{ 0x11, 31, 0, 1871 }, +{ 0x71, 30, 1, 1870 }, +{ 0x31, 31, 0, 1869 }, +{ 0x29, 30, 0, 1864 }, +{ 0x69, 30, 0, 1863 }, +{ 0x19, 30, 1, 1876 }, +{ 0x9, 31, 0, 1875 }, +{ 0x39, 30, 1, 1874 }, +{ 0x19, 31, 0, 1873 }, +{ 0x15, 30, 0, 1866 }, +{ 0x35, 30, 0, 1865 }, +{ 0xd, 30, 1, 1880 }, +{ 0x5, 31, 0, 1879 }, +{ 0x1d, 30, 1, 1878 }, +{ 0xd, 31, 0, 1877 }, +{ 0xb, 30, 0, 1868 }, +{ 0x1b, 30, 0, 1867 }, +{ 0x7, 30, 1, 1884 }, +{ 0x3, 31, 0, 1883 }, +{ 0xf, 30, 1, 1882 }, +{ 0x7, 31, 0, 1881 }, +{ 0xa2, 28, 0, 1886 }, +{ 0x1a2, 28, 0, 1885 }, +{ 0x62, 28, 1, 1896 }, +{ 0x22, 29, 0, 1895 }, +{ 0xe2, 28, 1, 1894 }, +{ 0x62, 29, 0, 1893 }, +{ 0x52, 28, 0, 1888 }, +{ 0xd2, 28, 0, 1887 }, +{ 0x32, 28, 1, 1900 }, +{ 0x12, 29, 0, 1899 }, +{ 0x72, 28, 1, 1898 }, +{ 0x32, 29, 0, 1897 }, +{ 0x2a, 28, 0, 1890 }, +{ 0x6a, 28, 0, 1889 }, +{ 0x1a, 28, 1, 1904 }, +{ 0xa, 29, 0, 1903 }, +{ 0x3a, 28, 1, 1902 }, +{ 0x1a, 29, 0, 1901 }, +{ 0x16, 28, 0, 1892 }, +{ 0x36, 28, 0, 1891 }, +{ 0xe, 28, 1, 1908 }, +{ 0x6, 29, 0, 1907 }, +{ 0x1e, 28, 1, 1906 }, +{ 0xe, 29, 0, 1905 }, +{ 0x51, 28, 0, 1910 }, +{ 0xd1, 28, 0, 1909 }, +{ 0x31, 28, 1, 1920 }, +{ 0x11, 29, 0, 1919 }, +{ 0x71, 28, 1, 1918 }, +{ 0x31, 29, 0, 1917 }, +{ 0x29, 28, 0, 1912 }, +{ 0x69, 28, 0, 1911 }, +{ 0x19, 28, 1, 1924 }, +{ 0x9, 29, 0, 1923 }, +{ 0x39, 28, 1, 1922 }, +{ 0x19, 29, 0, 1921 }, +{ 0x15, 28, 0, 1914 }, +{ 0x35, 28, 0, 1913 }, +{ 0xd, 28, 1, 1928 }, +{ 0x5, 29, 0, 1927 }, +{ 0x1d, 28, 1, 1926 }, +{ 0xd, 29, 0, 1925 }, +{ 0xb, 28, 0, 1916 }, +{ 0x1b, 28, 0, 1915 }, +{ 0x7, 28, 1, 1932 }, +{ 0x3, 29, 0, 1931 }, +{ 0xf, 28, 1, 1930 }, +{ 0x7, 29, 0, 1929 }, +{ 0x51, 26, 0, 1934 }, +{ 0xd1, 26, 0, 1933 }, +{ 0x31, 26, 1, 1944 }, +{ 0x11, 27, 0, 1943 }, +{ 0x71, 26, 1, 1942 }, +{ 0x31, 27, 0, 1941 }, +{ 0x29, 26, 0, 1936 }, +{ 0x69, 26, 0, 1935 }, +{ 0x19, 26, 1, 1948 }, +{ 0x9, 27, 0, 1947 }, +{ 0x39, 26, 1, 1946 }, +{ 0x19, 27, 0, 1945 }, +{ 0x15, 26, 0, 1938 }, +{ 0x35, 26, 0, 1937 }, +{ 0xd, 26, 1, 1952 }, +{ 0x5, 27, 0, 1951 }, +{ 0x1d, 26, 1, 1950 }, +{ 0xd, 27, 0, 1949 }, +{ 0xb, 26, 0, 1940 }, +{ 0x1b, 26, 0, 1939 }, +{ 0x7, 26, 1, 1956 }, +{ 0x3, 27, 0, 1955 }, +{ 0xf, 26, 1, 1954 }, +{ 0x7, 27, 0, 1953 }, +{ 0xa2, 24, 0, 1958 }, +{ 0x1a2, 24, 0, 1957 }, +{ 0x62, 24, 1, 1968 }, +{ 0x22, 25, 0, 1967 }, +{ 0xe2, 24, 1, 1966 }, +{ 0x62, 25, 0, 1965 }, +{ 0x52, 24, 0, 1960 }, +{ 0xd2, 24, 0, 1959 }, +{ 0x32, 24, 1, 1972 }, +{ 0x12, 25, 0, 1971 }, +{ 0x72, 24, 1, 1970 }, +{ 0x32, 25, 0, 1969 }, +{ 0x2a, 24, 0, 1962 }, +{ 0x6a, 24, 0, 1961 }, +{ 0x1a, 24, 1, 1976 }, +{ 0xa, 25, 0, 1975 }, +{ 0x3a, 24, 1, 1974 }, +{ 0x1a, 25, 0, 1973 }, +{ 0x16, 24, 0, 1964 }, +{ 0x36, 24, 0, 1963 }, +{ 0xe, 24, 1, 1980 }, +{ 0x6, 25, 0, 1979 }, +{ 0x1e, 24, 1, 1978 }, +{ 0xe, 25, 0, 1977 }, +{ 0x51, 24, 0, 1982 }, +{ 0xd1, 24, 0, 1981 }, +{ 0x31, 24, 1, 1992 }, +{ 0x11, 25, 0, 1991 }, +{ 0x71, 24, 1, 1990 }, +{ 0x31, 25, 0, 1989 }, +{ 0x29, 24, 0, 1984 }, +{ 0x69, 24, 0, 1983 }, +{ 0x19, 24, 1, 1996 }, +{ 0x9, 25, 0, 1995 }, +{ 0x39, 24, 1, 1994 }, +{ 0x19, 25, 0, 1993 }, +{ 0x15, 24, 0, 1986 }, +{ 0x35, 24, 0, 1985 }, +{ 0xd, 24, 1, 2000 }, +{ 0x5, 25, 0, 1999 }, +{ 0x1d, 24, 1, 1998 }, +{ 0xd, 25, 0, 1997 }, +{ 0xb, 24, 0, 1988 }, +{ 0x1b, 24, 0, 1987 }, +{ 0x7, 24, 1, 2004 }, +{ 0x3, 25, 0, 2003 }, +{ 0xf, 24, 1, 2002 }, +{ 0x7, 25, 0, 2001 }, +{ 0x51, 22, 1, 2030 }, +{ 0x50, 22, 0, 2006 }, +{ 0xd1, 22, 1, 2029 }, +{ 0xd0, 22, 0, 2005 }, +{ 0x31, 22, 1, 2040 }, +{ 0x30, 22, 1, 2016 }, +{ 0x11, 23, 1, 2039 }, +{ 0x10, 23, 0, 2015 }, +{ 0x71, 22, 1, 2038 }, +{ 0x70, 22, 1, 2014 }, +{ 0x31, 23, 1, 2037 }, +{ 0x30, 23, 0, 2013 }, +{ 0x29, 22, 1, 2032 }, +{ 0x28, 22, 0, 2008 }, +{ 0x69, 22, 1, 2031 }, +{ 0x68, 22, 0, 2007 }, +{ 0x19, 22, 1, 2044 }, +{ 0x18, 22, 1, 2020 }, +{ 0x9, 23, 1, 2043 }, +{ 0x8, 23, 0, 2019 }, +{ 0x39, 22, 1, 2042 }, +{ 0x38, 22, 1, 2018 }, +{ 0x19, 23, 1, 2041 }, +{ 0x18, 23, 0, 2017 }, +{ 0x15, 22, 1, 2034 }, +{ 0x14, 22, 0, 2010 }, +{ 0x35, 22, 1, 2033 }, +{ 0x34, 22, 0, 2009 }, +{ 0xd, 22, 1, 2048 }, +{ 0xc, 22, 1, 2024 }, +{ 0x5, 23, 1, 2047 }, +{ 0x4, 23, 0, 2023 }, +{ 0x1d, 22, 1, 2046 }, +{ 0x1c, 22, 1, 2022 }, +{ 0xd, 23, 1, 2045 }, +{ 0xc, 23, 0, 2021 }, +{ 0xb, 22, 1, 2036 }, +{ 0xa, 22, 0, 2012 }, +{ 0x1b, 22, 1, 2035 }, +{ 0x1a, 22, 0, 2011 }, +{ 0x7, 22, 1, 2052 }, +{ 0x6, 22, 1, 2028 }, +{ 0x3, 23, 1, 2051 }, +{ 0x2, 23, 0, 2027 }, +{ 0xf, 22, 1, 2050 }, +{ 0xe, 22, 1, 2026 }, +{ 0x7, 23, 1, 2049 }, +{ 0x6, 23, 0, 2025 }, +{ 0x8, 21, 0, 2054 }, +{ 0x18, 21, 0, 2053 }, +{ 0x1, 21, 1, 2058 }, +{ 0x2, 21, 0, 2057 }, +{ 0x3, 21, 1, 2056 }, +{ 0x4, 21, 0, 2055 }, +{ 0x1, 239, 0, 2059 }, +{ 0x1, 339, 0, 2060 }, +{ 0x14, 43, 0, 2063 }, +{ 0x34, 43, 0, 2061 }, +{ 0xc, 43, 0, 2064 }, +{ 0x1c, 43, 0, 2062 }, +{ 0x2, 43, 0, 2067 }, +{ 0x6, 43, 0, 2065 }, +{ 0x1, 43, 0, 2068 }, +{ 0x3, 43, 0, 2066 }, +{ 0x51, 19, 0, 2070 }, +{ 0xd1, 19, 0, 2069 }, +{ 0x31, 19, 1, 2080 }, +{ 0x11, 20, 0, 2079 }, +{ 0x71, 19, 1, 2078 }, +{ 0x31, 20, 0, 2077 }, +{ 0x29, 19, 0, 2072 }, +{ 0x69, 19, 0, 2071 }, +{ 0x19, 19, 1, 2084 }, +{ 0x9, 20, 0, 2083 }, +{ 0x39, 19, 1, 2082 }, +{ 0x19, 20, 0, 2081 }, +{ 0x15, 19, 0, 2074 }, +{ 0x35, 19, 0, 2073 }, +{ 0xd, 19, 1, 2088 }, +{ 0x5, 20, 0, 2087 }, +{ 0x1d, 19, 1, 2086 }, +{ 0xd, 20, 0, 2085 }, +{ 0xb, 19, 0, 2076 }, +{ 0x1b, 19, 0, 2075 }, +{ 0x7, 19, 1, 2092 }, +{ 0x3, 20, 0, 2091 }, +{ 0xf, 19, 1, 2090 }, +{ 0x7, 20, 0, 2089 }, +{ 0x1, 32, 0, 2093 }, +{ 0x2, 447, 0, 2094 }, +{ 0x1, 447, 0, 2095 }, +{ 0x1, 140, 0, 2096 }, +{ 0x2, 45, 0, 2097 }, +{ 0x1, 45, 0, 2098 }, +{ 0x1, 387, 0, 2099 }, +{ 0x2, 52, 0, 2100 }, +{ 0x1, 52, 0, 2101 }, +{ 0x1, 133, 0, 2102 }, +{ 0x51, 17, 0, 2104 }, +{ 0xd1, 17, 0, 2103 }, +{ 0x31, 17, 1, 2114 }, +{ 0x11, 18, 0, 2113 }, +{ 0x71, 17, 1, 2112 }, +{ 0x31, 18, 0, 2111 }, +{ 0x29, 17, 0, 2106 }, +{ 0x69, 17, 0, 2105 }, +{ 0x19, 17, 1, 2118 }, +{ 0x9, 18, 0, 2117 }, +{ 0x39, 17, 1, 2116 }, +{ 0x19, 18, 0, 2115 }, +{ 0x15, 17, 0, 2108 }, +{ 0x35, 17, 0, 2107 }, +{ 0xd, 17, 1, 2122 }, +{ 0x5, 18, 0, 2121 }, +{ 0x1d, 17, 1, 2120 }, +{ 0xd, 18, 0, 2119 }, +{ 0xb, 17, 0, 2110 }, +{ 0x1b, 17, 0, 2109 }, +{ 0x7, 17, 1, 2126 }, +{ 0x3, 18, 0, 2125 }, +{ 0xf, 17, 1, 2124 }, +{ 0x7, 18, 0, 2123 }, +{ 0xa20, 15, 0, 2128 }, +{ 0x1a20, 15, 0, 2127 }, +{ 0x620, 15, 1, 2138 }, +{ 0x220, 16, 0, 2137 }, +{ 0xe20, 15, 1, 2136 }, +{ 0x620, 16, 0, 2135 }, +{ 0x520, 15, 0, 2130 }, +{ 0xd20, 15, 0, 2129 }, +{ 0x320, 15, 1, 2142 }, +{ 0x120, 16, 0, 2141 }, +{ 0x720, 15, 1, 2140 }, +{ 0x320, 16, 0, 2139 }, +{ 0x2a0, 15, 0, 2132 }, +{ 0x6a0, 15, 0, 2131 }, +{ 0x1a0, 15, 1, 2146 }, +{ 0xa0, 16, 0, 2145 }, +{ 0x3a0, 15, 1, 2144 }, +{ 0x1a0, 16, 0, 2143 }, +{ 0x160, 15, 0, 2134 }, +{ 0x360, 15, 0, 2133 }, +{ 0xe0, 15, 1, 2150 }, +{ 0x60, 16, 0, 2149 }, +{ 0x1e0, 15, 1, 2148 }, +{ 0xe0, 16, 0, 2147 }, +{ 0x51, 15, 1, 2176 }, +{ 0x50, 15, 0, 2152 }, +{ 0xd1, 15, 1, 2175 }, +{ 0xd0, 15, 0, 2151 }, +{ 0x31, 15, 1, 2186 }, +{ 0x30, 15, 1, 2162 }, +{ 0x11, 16, 1, 2185 }, +{ 0x10, 16, 0, 2161 }, +{ 0x71, 15, 1, 2184 }, +{ 0x70, 15, 1, 2160 }, +{ 0x31, 16, 1, 2183 }, +{ 0x30, 16, 0, 2159 }, +{ 0x29, 15, 1, 2178 }, +{ 0x28, 15, 0, 2154 }, +{ 0x69, 15, 1, 2177 }, +{ 0x68, 15, 0, 2153 }, +{ 0x19, 15, 1, 2190 }, +{ 0x18, 15, 1, 2166 }, +{ 0x9, 16, 1, 2189 }, +{ 0x8, 16, 0, 2165 }, +{ 0x39, 15, 1, 2188 }, +{ 0x38, 15, 1, 2164 }, +{ 0x19, 16, 1, 2187 }, +{ 0x18, 16, 0, 2163 }, +{ 0x15, 15, 1, 2180 }, +{ 0x14, 15, 0, 2156 }, +{ 0x35, 15, 1, 2179 }, +{ 0x34, 15, 0, 2155 }, +{ 0xd, 15, 1, 2194 }, +{ 0xc, 15, 1, 2170 }, +{ 0x5, 16, 1, 2193 }, +{ 0x4, 16, 0, 2169 }, +{ 0x1d, 15, 1, 2192 }, +{ 0x1c, 15, 1, 2168 }, +{ 0xd, 16, 1, 2191 }, +{ 0xc, 16, 0, 2167 }, +{ 0xb, 15, 1, 2182 }, +{ 0xa, 15, 0, 2158 }, +{ 0x1b, 15, 1, 2181 }, +{ 0x1a, 15, 0, 2157 }, +{ 0x7, 15, 1, 2198 }, +{ 0x6, 15, 1, 2174 }, +{ 0x3, 16, 1, 2197 }, +{ 0x2, 16, 0, 2173 }, +{ 0xf, 15, 1, 2196 }, +{ 0xe, 15, 1, 2172 }, +{ 0x7, 16, 1, 2195 }, +{ 0x6, 16, 0, 2171 }, +{ 0x8, 14, 0, 2200 }, +{ 0x18, 14, 0, 2199 }, +{ 0x1, 14, 1, 2204 }, +{ 0x2, 14, 0, 2203 }, +{ 0x3, 14, 1, 2202 }, +{ 0x4, 14, 0, 2201 }, +{ 0x1, 109, 1, 2356 }, +{ 0x1, 110, 1, 2355 }, +{ 0x1, 111, 1, 2354 }, +{ 0x1, 112, 1, 2353 }, +{ 0x1, 113, 1, 2352 }, +{ 0x1, 114, 1, 2351 }, +{ 0x1, 115, 1, 2350 }, +{ 0x1, 116, 1, 2349 }, +{ 0x39, 41, 1, 22 }, +{ 0x19, 42, 0, 21 }, +{ 0x3, 109, 1, 2348 }, +{ 0x3, 110, 1, 2347 }, +{ 0x3, 111, 1, 2346 }, +{ 0x3, 112, 1, 2345 }, +{ 0x3, 113, 1, 2344 }, +{ 0x3, 114, 1, 2343 }, +{ 0x3, 115, 1, 2342 }, +{ 0x3, 116, 1, 2341 }, +{ 0x69, 41, 0, 11 }, +{ 0x14, 100, 1, 2336 }, +{ 0x22, 101, 1, 2333 }, +{ 0x44, 101, 1, 2335 }, +{ 0xa, 108, 1, 2334 }, +{ 0xd1, 41, 0, 9 }, +{ 0x34, 100, 1, 2208 }, +{ 0xc4, 101, 1, 2207 }, +{ 0x1c, 107, 1, 2205 }, +{ 0xe, 122, 0, 2206 }, +{ 0xc, 100, 1, 2496 }, +{ 0xa, 101, 1, 2493 }, +{ 0x14, 101, 1, 2495 }, +{ 0x6, 108, 0, 2494 }, +{ 0x2, 100, 1, 2220 }, +{ 0x2, 101, 1, 2219 }, +{ 0x2, 106, 1, 2218 }, +{ 0x2, 107, 0, 2217 }, +{ 0x12, 100, 1, 2216 }, +{ 0x42, 101, 1, 2215 }, +{ 0x6, 106, 1, 2214 }, +{ 0x6, 107, 0, 2213 }, +{ 0xa, 100, 1, 2340 }, +{ 0x12, 101, 1, 2339 }, +{ 0x24, 101, 1, 2337 }, +{ 0x5, 108, 1, 2338 }, +{ 0x71, 41, 1, 18 }, +{ 0x31, 42, 0, 17 }, +{ 0x1a, 100, 1, 2212 }, +{ 0x32, 101, 1, 2211 }, +{ 0x1a, 107, 1, 2209 }, +{ 0x7, 122, 0, 2210 }, +{ 0x6, 100, 1, 2500 }, +{ 0x6, 101, 1, 2499 }, +{ 0xc, 101, 1, 2497 }, +{ 0x3, 108, 0, 2498 }, +{ 0x1, 100, 1, 2516 }, +{ 0x1, 101, 1, 2515 }, +{ 0x1, 102, 1, 2514 }, +{ 0x1, 103, 1, 2513 }, +{ 0x1, 104, 1, 2512 }, +{ 0x1, 105, 1, 2511 }, +{ 0x1, 106, 1, 2510 }, +{ 0x1, 107, 0, 2509 }, +{ 0x3, 100, 1, 2508 }, +{ 0x3, 101, 1, 2507 }, +{ 0x3, 102, 1, 2506 }, +{ 0x3, 103, 1, 2505 }, +{ 0x3, 104, 1, 2504 }, +{ 0x3, 105, 1, 2503 }, +{ 0x3, 106, 1, 2502 }, +{ 0x3, 107, 0, 2501 }, +{ 0x8, 67, 1, 2380 }, +{ 0x8, 68, 1, 2379 }, +{ 0x2, 73, 1, 2374 }, +{ 0x2, 74, 1, 2373 }, +{ 0x1, 76, 1, 2378 }, +{ 0x1, 77, 1, 2377 }, +{ 0x1, 78, 1, 2376 }, +{ 0x1, 79, 1, 2375 }, +{ 0xf, 41, 1, 30 }, +{ 0x7, 42, 0, 29 }, +{ 0x18, 67, 1, 2372 }, +{ 0x18, 68, 1, 2371 }, +{ 0x6, 73, 1, 2366 }, +{ 0x6, 74, 1, 2365 }, +{ 0x3, 76, 1, 2370 }, +{ 0x3, 77, 1, 2369 }, +{ 0x3, 78, 1, 2368 }, +{ 0x3, 79, 1, 2367 }, +{ 0x1b, 41, 0, 15 }, +{ 0x14, 67, 1, 2360 }, +{ 0x22, 68, 1, 2357 }, +{ 0x44, 68, 1, 2359 }, +{ 0xa, 75, 1, 2358 }, +{ 0x35, 41, 0, 13 }, +{ 0x34, 67, 1, 2224 }, +{ 0xc4, 68, 1, 2223 }, +{ 0x38, 74, 1, 2221 }, +{ 0xe, 85, 0, 2222 }, +{ 0xc, 67, 1, 2520 }, +{ 0xa, 68, 1, 2517 }, +{ 0x14, 68, 1, 2519 }, +{ 0x6, 75, 0, 2518 }, +{ 0x2, 67, 1, 2236 }, +{ 0x2, 68, 1, 2235 }, +{ 0x4, 73, 1, 2234 }, +{ 0x4, 74, 0, 2233 }, +{ 0x12, 67, 1, 2232 }, +{ 0x42, 68, 1, 2231 }, +{ 0xc, 73, 1, 2230 }, +{ 0xc, 74, 0, 2229 }, +{ 0xa, 67, 1, 2364 }, +{ 0x12, 68, 1, 2363 }, +{ 0x24, 68, 1, 2361 }, +{ 0x5, 75, 1, 2362 }, +{ 0x1d, 41, 1, 26 }, +{ 0xd, 42, 0, 25 }, +{ 0x1a, 67, 1, 2228 }, +{ 0x32, 68, 1, 2227 }, +{ 0x34, 74, 1, 2225 }, +{ 0x7, 85, 0, 2226 }, +{ 0x6, 67, 1, 2524 }, +{ 0x6, 68, 1, 2523 }, +{ 0xc, 68, 1, 2521 }, +{ 0x3, 75, 0, 2522 }, +{ 0x1, 67, 1, 2540 }, +{ 0x1, 68, 1, 2539 }, +{ 0x1, 69, 1, 2538 }, +{ 0x1, 70, 1, 2537 }, +{ 0x1, 71, 1, 2536 }, +{ 0x1, 72, 1, 2535 }, +{ 0x1, 73, 1, 2534 }, +{ 0x1, 74, 0, 2533 }, +{ 0x3, 67, 1, 2532 }, +{ 0x3, 68, 1, 2531 }, +{ 0x3, 69, 1, 2530 }, +{ 0x3, 70, 1, 2529 }, +{ 0x3, 71, 1, 2528 }, +{ 0x3, 72, 1, 2527 }, +{ 0x3, 73, 1, 2526 }, +{ 0x3, 74, 0, 2525 }, +{ 0x28, 95, 1, 2388 }, +{ 0x44, 96, 1, 2383 }, +{ 0x88, 96, 1, 2387 }, +{ 0x44, 97, 1, 2382 }, +{ 0x88, 97, 1, 2386 }, +{ 0x44, 98, 1, 2381 }, +{ 0x88, 98, 1, 2385 }, +{ 0x28, 99, 0, 2384 }, +{ 0x68, 95, 1, 2244 }, +{ 0x188, 96, 1, 2243 }, +{ 0x188, 97, 1, 2242 }, +{ 0x188, 98, 1, 2241 }, +{ 0x38, 118, 1, 2240 }, +{ 0x38, 119, 1, 2239 }, +{ 0x38, 120, 1, 2238 }, +{ 0x38, 121, 0, 2237 }, +{ 0x18, 95, 1, 2548 }, +{ 0x14, 96, 1, 2543 }, +{ 0x28, 96, 1, 2547 }, +{ 0x14, 97, 1, 2542 }, +{ 0x28, 97, 1, 2546 }, +{ 0x14, 98, 1, 2541 }, +{ 0x28, 98, 1, 2545 }, +{ 0x18, 99, 0, 2544 }, +{ 0x14, 95, 1, 2396 }, +{ 0x24, 96, 1, 2395 }, +{ 0x48, 96, 1, 2391 }, +{ 0x24, 97, 1, 2394 }, +{ 0x48, 97, 1, 2390 }, +{ 0x24, 98, 1, 2393 }, +{ 0x48, 98, 1, 2389 }, +{ 0x14, 99, 0, 2392 }, +{ 0x34, 95, 1, 2252 }, +{ 0x64, 96, 1, 2251 }, +{ 0x64, 97, 1, 2250 }, +{ 0x64, 98, 1, 2249 }, +{ 0x1c, 118, 1, 2248 }, +{ 0x1c, 119, 1, 2247 }, +{ 0x1c, 120, 1, 2246 }, +{ 0x1c, 121, 0, 2245 }, +{ 0xc, 95, 1, 2556 }, +{ 0xc, 96, 1, 2555 }, +{ 0x18, 96, 1, 2551 }, +{ 0xc, 97, 1, 2554 }, +{ 0x18, 97, 1, 2550 }, +{ 0xc, 98, 1, 2553 }, +{ 0x18, 98, 1, 2549 }, +{ 0xc, 99, 0, 2552 }, +{ 0xa, 95, 1, 2404 }, +{ 0x11, 96, 1, 2399 }, +{ 0x22, 96, 1, 2403 }, +{ 0x11, 97, 1, 2398 }, +{ 0x22, 97, 1, 2402 }, +{ 0x11, 98, 1, 2397 }, +{ 0x22, 98, 1, 2401 }, +{ 0xa, 99, 0, 2400 }, +{ 0x1a, 95, 1, 2260 }, +{ 0x62, 96, 1, 2259 }, +{ 0x62, 97, 1, 2258 }, +{ 0x62, 98, 1, 2257 }, +{ 0xe, 118, 1, 2256 }, +{ 0xe, 119, 1, 2255 }, +{ 0xe, 120, 1, 2254 }, +{ 0xe, 121, 0, 2253 }, +{ 0x6, 95, 1, 2564 }, +{ 0x5, 96, 1, 2559 }, +{ 0xa, 96, 1, 2563 }, +{ 0x5, 97, 1, 2558 }, +{ 0xa, 97, 1, 2562 }, +{ 0x5, 98, 1, 2557 }, +{ 0xa, 98, 1, 2561 }, +{ 0x6, 99, 0, 2560 }, +{ 0x5, 95, 1, 2412 }, +{ 0x9, 96, 1, 2411 }, +{ 0x12, 96, 1, 2407 }, +{ 0x9, 97, 1, 2410 }, +{ 0x12, 97, 1, 2406 }, +{ 0x9, 98, 1, 2409 }, +{ 0x12, 98, 1, 2405 }, +{ 0x5, 99, 0, 2408 }, +{ 0xd, 95, 1, 2268 }, +{ 0x19, 96, 1, 2267 }, +{ 0x19, 97, 1, 2266 }, +{ 0x19, 98, 1, 2265 }, +{ 0x7, 118, 1, 2264 }, +{ 0x7, 119, 1, 2263 }, +{ 0x7, 120, 1, 2262 }, +{ 0x7, 121, 0, 2261 }, +{ 0x3, 95, 1, 2572 }, +{ 0x3, 96, 1, 2571 }, +{ 0x6, 96, 1, 2567 }, +{ 0x3, 97, 1, 2570 }, +{ 0x6, 97, 1, 2566 }, +{ 0x3, 98, 1, 2569 }, +{ 0x6, 98, 1, 2565 }, +{ 0x3, 99, 0, 2568 }, +{ 0x28, 62, 1, 2420 }, +{ 0x44, 63, 1, 2415 }, +{ 0x88, 63, 1, 2419 }, +{ 0x44, 64, 1, 2414 }, +{ 0x88, 64, 1, 2418 }, +{ 0x44, 65, 1, 2413 }, +{ 0x88, 65, 1, 2417 }, +{ 0x28, 66, 0, 2416 }, +{ 0x68, 62, 1, 2276 }, +{ 0x188, 63, 1, 2275 }, +{ 0x188, 64, 1, 2274 }, +{ 0x188, 65, 1, 2273 }, +{ 0x38, 81, 1, 2272 }, +{ 0x38, 82, 1, 2271 }, +{ 0x38, 83, 1, 2270 }, +{ 0x38, 84, 0, 2269 }, +{ 0x18, 62, 1, 2580 }, +{ 0x14, 63, 1, 2575 }, +{ 0x28, 63, 1, 2579 }, +{ 0x14, 64, 1, 2574 }, +{ 0x28, 64, 1, 2578 }, +{ 0x14, 65, 1, 2573 }, +{ 0x28, 65, 1, 2577 }, +{ 0x18, 66, 0, 2576 }, +{ 0x14, 62, 1, 2428 }, +{ 0x24, 63, 1, 2427 }, +{ 0x48, 63, 1, 2423 }, +{ 0x24, 64, 1, 2426 }, +{ 0x48, 64, 1, 2422 }, +{ 0x24, 65, 1, 2425 }, +{ 0x48, 65, 1, 2421 }, +{ 0x14, 66, 0, 2424 }, +{ 0x34, 62, 1, 2284 }, +{ 0x64, 63, 1, 2283 }, +{ 0x64, 64, 1, 2282 }, +{ 0x64, 65, 1, 2281 }, +{ 0x1c, 81, 1, 2280 }, +{ 0x1c, 82, 1, 2279 }, +{ 0x1c, 83, 1, 2278 }, +{ 0x1c, 84, 0, 2277 }, +{ 0xc, 62, 1, 2588 }, +{ 0xc, 63, 1, 2587 }, +{ 0x18, 63, 1, 2583 }, +{ 0xc, 64, 1, 2586 }, +{ 0x18, 64, 1, 2582 }, +{ 0xc, 65, 1, 2585 }, +{ 0x18, 65, 1, 2581 }, +{ 0xc, 66, 0, 2584 }, +{ 0xa, 62, 1, 2436 }, +{ 0x11, 63, 1, 2431 }, +{ 0x22, 63, 1, 2435 }, +{ 0x11, 64, 1, 2430 }, +{ 0x22, 64, 1, 2434 }, +{ 0x11, 65, 1, 2429 }, +{ 0x22, 65, 1, 2433 }, +{ 0xa, 66, 0, 2432 }, +{ 0x1a, 62, 1, 2292 }, +{ 0x62, 63, 1, 2291 }, +{ 0x62, 64, 1, 2290 }, +{ 0x62, 65, 1, 2289 }, +{ 0xe, 81, 1, 2288 }, +{ 0xe, 82, 1, 2287 }, +{ 0xe, 83, 1, 2286 }, +{ 0xe, 84, 0, 2285 }, +{ 0x6, 62, 1, 2596 }, +{ 0x5, 63, 1, 2591 }, +{ 0xa, 63, 1, 2595 }, +{ 0x5, 64, 1, 2590 }, +{ 0xa, 64, 1, 2594 }, +{ 0x5, 65, 1, 2589 }, +{ 0xa, 65, 1, 2593 }, +{ 0x6, 66, 0, 2592 }, +{ 0x5, 62, 1, 2444 }, +{ 0x9, 63, 1, 2443 }, +{ 0x12, 63, 1, 2439 }, +{ 0x9, 64, 1, 2442 }, +{ 0x12, 64, 1, 2438 }, +{ 0x9, 65, 1, 2441 }, +{ 0x12, 65, 1, 2437 }, +{ 0x5, 66, 0, 2440 }, +{ 0xd, 62, 1, 2300 }, +{ 0x19, 63, 1, 2299 }, +{ 0x19, 64, 1, 2298 }, +{ 0x19, 65, 1, 2297 }, +{ 0x7, 81, 1, 2296 }, +{ 0x7, 82, 1, 2295 }, +{ 0x7, 83, 1, 2294 }, +{ 0x7, 84, 0, 2293 }, +{ 0x3, 62, 1, 2604 }, +{ 0x3, 63, 1, 2603 }, +{ 0x6, 63, 1, 2599 }, +{ 0x3, 64, 1, 2602 }, +{ 0x6, 64, 1, 2598 }, +{ 0x3, 65, 1, 2601 }, +{ 0x6, 65, 1, 2597 }, +{ 0x3, 66, 0, 2600 }, +{ 0x8, 86, 1, 2468 }, +{ 0x8, 87, 1, 2467 }, +{ 0x2, 88, 1, 2466 }, +{ 0x2, 89, 1, 2465 }, +{ 0x2, 90, 1, 2464 }, +{ 0x2, 91, 1, 2463 }, +{ 0x2, 92, 1, 2462 }, +{ 0x2, 93, 0, 2461 }, +{ 0x18, 86, 1, 2460 }, +{ 0x18, 87, 1, 2459 }, +{ 0x6, 88, 1, 2458 }, +{ 0x6, 89, 1, 2457 }, +{ 0x6, 90, 1, 2456 }, +{ 0x6, 91, 1, 2455 }, +{ 0x6, 92, 1, 2454 }, +{ 0x6, 93, 0, 2453 }, +{ 0x14, 86, 1, 2448 }, +{ 0x22, 87, 1, 2445 }, +{ 0x44, 87, 1, 2447 }, +{ 0xa, 94, 0, 2446 }, +{ 0x34, 86, 1, 2304 }, +{ 0xc4, 87, 1, 2303 }, +{ 0x38, 93, 1, 2301 }, +{ 0xe, 117, 0, 2302 }, +{ 0xc, 86, 1, 2608 }, +{ 0xa, 87, 1, 2605 }, +{ 0x14, 87, 1, 2607 }, +{ 0x6, 94, 0, 2606 }, +{ 0x2, 86, 1, 2316 }, +{ 0x2, 87, 1, 2315 }, +{ 0x4, 92, 1, 2314 }, +{ 0x4, 93, 0, 2313 }, +{ 0x12, 86, 1, 2312 }, +{ 0x42, 87, 1, 2311 }, +{ 0xc, 92, 1, 2310 }, +{ 0xc, 93, 0, 2309 }, +{ 0xa, 86, 1, 2452 }, +{ 0x12, 87, 1, 2451 }, +{ 0x24, 87, 1, 2449 }, +{ 0x5, 94, 0, 2450 }, +{ 0x1a, 86, 1, 2308 }, +{ 0x32, 87, 1, 2307 }, +{ 0x34, 93, 1, 2305 }, +{ 0x7, 117, 0, 2306 }, +{ 0x6, 86, 1, 2612 }, +{ 0x6, 87, 1, 2611 }, +{ 0xc, 87, 1, 2609 }, +{ 0x3, 94, 0, 2610 }, +{ 0x1, 86, 1, 2628 }, +{ 0x1, 87, 1, 2627 }, +{ 0x1, 88, 1, 2626 }, +{ 0x1, 89, 1, 2625 }, +{ 0x1, 90, 1, 2624 }, +{ 0x1, 91, 1, 2623 }, +{ 0x1, 92, 1, 2622 }, +{ 0x1, 93, 0, 2621 }, +{ 0x3, 86, 1, 2620 }, +{ 0x3, 87, 1, 2619 }, +{ 0x3, 88, 1, 2618 }, +{ 0x3, 89, 1, 2617 }, +{ 0x3, 90, 1, 2616 }, +{ 0x3, 91, 1, 2615 }, +{ 0x3, 92, 1, 2614 }, +{ 0x3, 93, 0, 2613 }, +{ 0x8, 53, 1, 2492 }, +{ 0x8, 54, 1, 2491 }, +{ 0x2, 55, 1, 2490 }, +{ 0x2, 56, 1, 2489 }, +{ 0x2, 57, 1, 2488 }, +{ 0x2, 58, 1, 2487 }, +{ 0x2, 59, 1, 2486 }, +{ 0x2, 60, 0, 2485 }, +{ 0x18, 53, 1, 2484 }, +{ 0x18, 54, 1, 2483 }, +{ 0x6, 55, 1, 2482 }, +{ 0x6, 56, 1, 2481 }, +{ 0x6, 57, 1, 2480 }, +{ 0x6, 58, 1, 2479 }, +{ 0x6, 59, 1, 2478 }, +{ 0x6, 60, 0, 2477 }, +{ 0x14, 53, 1, 2472 }, +{ 0x22, 54, 1, 2469 }, +{ 0x44, 54, 1, 2471 }, +{ 0xa, 61, 0, 2470 }, +{ 0x34, 53, 1, 2320 }, +{ 0xc4, 54, 1, 2319 }, +{ 0x38, 60, 1, 2317 }, +{ 0xe, 80, 0, 2318 }, +{ 0xc, 53, 1, 2632 }, +{ 0xa, 54, 1, 2629 }, +{ 0x14, 54, 1, 2631 }, +{ 0x6, 61, 0, 2630 }, +{ 0x2, 53, 1, 2332 }, +{ 0x2, 54, 1, 2331 }, +{ 0x4, 59, 1, 2330 }, +{ 0x4, 60, 0, 2329 }, +{ 0x12, 53, 1, 2328 }, +{ 0x42, 54, 1, 2327 }, +{ 0xc, 59, 1, 2326 }, +{ 0xc, 60, 0, 2325 }, +{ 0xa, 53, 1, 2476 }, +{ 0x12, 54, 1, 2475 }, +{ 0x24, 54, 1, 2473 }, +{ 0x5, 61, 0, 2474 }, +{ 0x1a, 53, 1, 2324 }, +{ 0x32, 54, 1, 2323 }, +{ 0x34, 60, 1, 2321 }, +{ 0x7, 80, 0, 2322 }, +{ 0x6, 53, 1, 2636 }, +{ 0x6, 54, 1, 2635 }, +{ 0xc, 54, 1, 2633 }, +{ 0x3, 61, 0, 2634 }, +{ 0x1, 53, 1, 2652 }, +{ 0x1, 54, 1, 2651 }, +{ 0x1, 55, 1, 2650 }, +{ 0x1, 56, 1, 2649 }, +{ 0x1, 57, 1, 2648 }, +{ 0x1, 58, 1, 2647 }, +{ 0x1, 59, 1, 2646 }, +{ 0x1, 60, 0, 2645 }, +{ 0x3, 53, 1, 2644 }, +{ 0x3, 54, 1, 2643 }, +{ 0x3, 55, 1, 2642 }, +{ 0x3, 56, 1, 2641 }, +{ 0x3, 57, 1, 2640 }, +{ 0x3, 58, 1, 2639 }, +{ 0x3, 59, 1, 2638 }, +{ 0x3, 60, 0, 2637 }, +{ 0x1, 4, 0, 2653 }, +{ 0x1, 296, 0, 2654 }, +{ 0x1, 379, 0, 2655 }, +{ 0x1, 374, 0, 2656 }, +{ 0x2, 358, 0, 2657 }, +{ 0x1, 358, 0, 2660 }, +{ 0x2, 357, 0, 2658 }, +{ 0x1, 357, 0, 2661 }, +{ 0x2, 356, 0, 2659 }, +{ 0x1, 356, 0, 2662 }, +{ 0x1, 355, 0, 2663 }, +{ 0x1, 354, 0, 2664 }, +{ 0x2, 353, 0, 2665 }, +{ 0x1, 353, 0, 2667 }, +{ 0x2, 352, 0, 2666 }, +{ 0x1, 352, 0, 2668 }, +{ 0x1, 382, 0, 2675 }, +{ 0x8, 381, 0, 2669 }, +{ 0x4, 381, 0, 2671 }, +{ 0x2, 381, 0, 2673 }, +{ 0x1, 381, 0, 2676 }, +{ 0x8, 380, 0, 2670 }, +{ 0x4, 380, 0, 2672 }, +{ 0x2, 380, 0, 2674 }, +{ 0x1, 380, 0, 2677 }, +{ 0x1, 351, 0, 2684 }, +{ 0x8, 350, 0, 2678 }, +{ 0x4, 350, 0, 2680 }, +{ 0x2, 350, 0, 2682 }, +{ 0x1, 350, 0, 2685 }, +{ 0x8, 349, 0, 2679 }, +{ 0x4, 349, 0, 2681 }, +{ 0x2, 349, 1, 2683 }, +{ 0x4, 143, 0, 1377 }, +{ 0x1, 349, 0, 2686 }, +{ 0x1, 6, 0, 2687 }, +{ 0x1, 7, 0, 2688 }, +{ 0x1, 295, 0, 2689 }, +{ 0x1, 456, 0, 2690 }, +{ 0x1, 346, 0, 2691 }, +{ 0x1, 13, 0, 2692 }, +{ 0x1, 11, 0, 2693 }, +{ 0x1, 422, 0, 2694 }, +{ 0x1, 394, 0, 2695 }, +{ 0x1, 393, 0, 2696 }, +{ 0x1, 455, 0, 2697 }, +{ 0x1, 345, 0, 2698 }, +{ 0x1, 12, 0, 2699 }, +{ 0x1, 10, 0, 2700 }, +{ 0x1, 5, 0, 2701 }, +{ 0x1, 421, 0, 2702 }, +{ 0x1, 420, 0, 2703 }, +{ 0x1, 1, 0, 2704 }, +{ 0x1, 0, 0, 2705 }, +}; + diff --git a/tools/debugger/xenitp/ia64-asmtab.h b/tools/debugger/xenitp/ia64-asmtab.h new file mode 100644 index 0000000..08a2cd9 --- /dev/null +++ b/tools/debugger/xenitp/ia64-asmtab.h @@ -0,0 +1,148 @@ +/* ia64-asmtab.h -- Header for compacted IA-64 opcode tables. + Copyright 1999, 2000 Free Software Foundation, Inc. + Contributed by Bob Manson of Cygnus Support + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef IA64_ASMTAB_H +#define IA64_ASMTAB_H + +#include "ia64.h" + +/* The primary opcode table is made up of the following: */ +struct ia64_main_table +{ + /* The entry in the string table that corresponds to the name of this + opcode. */ + unsigned short name_index; + + /* The type of opcode; corresponds to the TYPE field in + struct ia64_opcode. */ + unsigned char opcode_type; + + /* The number of outputs for this opcode. */ + unsigned char num_outputs; + + /* The base insn value for this opcode. It may be modified by completers. */ + ia64_insn opcode; + + /* The mask of valid bits in OPCODE. Zeros indicate operand fields. */ + ia64_insn mask; + + /* The operands of this instruction. Corresponds to the OPERANDS field + in struct ia64_opcode. */ + unsigned char operands[5]; + + /* The flags for this instruction. Corresponds to the FLAGS field in + struct ia64_opcode. */ + short flags; + + /* The tree of completers for this instruction; this is an offset into + completer_table. */ + short completers; +}; + +/* Each instruction has a set of possible "completers", or additional + suffixes that can alter the instruction's behavior, and which has + potentially different dependencies. + + The completer entries modify certain bits in the instruction opcode. + Which bits are to be modified are marked by the BITS, MASK and + OFFSET fields. The completer entry may also note dependencies for the + opcode. + + These completers are arranged in a DAG; the pointers are indexes + into the completer_table array. The completer DAG is searched by + find_completer () and ia64_find_matching_opcode (). + + Note that each completer needs to be applied in turn, so that if we + have the instruction + cmp.lt.unc + the completer entries for both "lt" and "unc" would need to be applied + to the opcode's value. + + Some instructions do not require any completers; these contain an + empty completer entry. Instructions that require a completer do + not contain an empty entry. + + Terminal completers (those completers that validly complete an + instruction) are marked by having the TERMINAL_COMPLETER flag set. + + Only dependencies listed in the terminal completer for an opcode are + considered to apply to that opcode instance. */ + +struct ia64_completer_table +{ + /* The bit value that this completer sets. */ + unsigned int bits; + + /* And its mask. 1s are bits that are to be modified in the + instruction. */ + unsigned int mask; + + /* The entry in the string table that corresponds to the name of this + completer. */ + unsigned short name_index; + + /* An alternative completer, or -1 if this is the end of the chain. */ + short alternative; + + /* A pointer to the DAG of completers that can potentially follow + this one, or -1. */ + short subentries; + + /* The bit offset in the instruction where BITS and MASK should be + applied. */ + unsigned char offset : 7; + + unsigned char terminal_completer : 1; + + /* Index into the dependency list table */ + short dependencies; +}; + +/* This contains sufficient information for the disassembler to resolve + the complete name of the original instruction. */ +struct ia64_dis_names +{ + /* COMPLETER_INDEX represents the tree of completers that make up + the instruction. The LSB represents the top of the tree for the + specified instruction. + + A 0 bit indicates to go to the next alternate completer via the + alternative field; a 1 bit indicates that the current completer + is part of the instruction, and to go down the subentries index. + We know we've reached the final completer when we run out of 1 + bits. + + There is always at least one 1 bit. */ + unsigned int completer_index : 20; + + /* The index in the main_table[] array for the instruction. */ + unsigned short insn_index : 11; + + /* If set, the next entry in this table is an alternate possibility + for this instruction encoding. Which one to use is determined by + the instruction type and other factors (see opcode_verify ()). */ + unsigned int next_flag : 1; + + /* The disassembly priority of this entry among instructions. */ + unsigned short priority; +}; + +#endif diff --git a/tools/debugger/xenitp/ia64-dis.c b/tools/debugger/xenitp/ia64-dis.c new file mode 100644 index 0000000..5068768 --- /dev/null +++ b/tools/debugger/xenitp/ia64-dis.c @@ -0,0 +1,309 @@ +/* ia64-dis.c -- Disassemble ia64 instructions + Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include +#include + +#include "dis-asm.h" +#include "ia64.h" + +#define NELEMS(a) ((int) (sizeof (a) / sizeof (a[0]))) + +/* Disassemble ia64 instruction. */ + +/* Return the instruction type for OPCODE found in unit UNIT. */ + +static enum ia64_insn_type +unit_to_type (ia64_insn opcode, enum ia64_unit unit) +{ + enum ia64_insn_type type; + int op; + + op = IA64_OP (opcode); + + if (op >= 8 && (unit == IA64_UNIT_I || unit == IA64_UNIT_M)) + { + type = IA64_TYPE_A; + } + else + { + switch (unit) + { + case IA64_UNIT_I: + type = IA64_TYPE_I; break; + case IA64_UNIT_M: + type = IA64_TYPE_M; break; + case IA64_UNIT_B: + type = IA64_TYPE_B; break; + case IA64_UNIT_F: + type = IA64_TYPE_F; break; + case IA64_UNIT_L: + case IA64_UNIT_X: + type = IA64_TYPE_X; break; + default: + type = -1; + } + } + return type; +} + +int +print_insn_ia64 (bfd_vma memaddr, struct disassemble_info *info) +{ + ia64_insn t0, t1, slot[3], template, s_bit, insn; + int slotnum, j, status, need_comma, retval, slot_multiplier; + const struct ia64_operand *odesc; + const struct ia64_opcode *idesc; + const char *err, *str, *tname; + BFD_HOST_U_64_BIT value; + bfd_byte bundle[16]; + enum ia64_unit unit; + char regname[16]; + + if (info->bytes_per_line == 0) + info->bytes_per_line = 6; + info->display_endian = info->endian; + + slot_multiplier = info->bytes_per_line; + retval = slot_multiplier; + + slotnum = (((long) memaddr) & 0xf) / slot_multiplier; + if (slotnum > 2) + return -1; + + memaddr -= (memaddr & 0xf); + status = (*info->read_memory_func) (memaddr, bundle, sizeof (bundle), info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + /* bundles are always in little-endian byte order */ + t0 = bfd_getl64 (bundle); + t1 = bfd_getl64 (bundle + 8); + s_bit = t0 & 1; + template = (t0 >> 1) & 0xf; + slot[0] = (t0 >> 5) & 0x1ffffffffffLL; + slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18); + slot[2] = (t1 >> 23) & 0x1ffffffffffLL; + + tname = ia64_templ_desc[template].name; + if (slotnum == 0) + (*info->fprintf_func) (info->stream, "[%s] ", tname); + else + (*info->fprintf_func) (info->stream, " "); + + unit = ia64_templ_desc[template].exec_unit[slotnum]; + + if (template == 2 && slotnum == 1) + { + /* skip L slot in MLI template: */ + slotnum = 2; + retval += slot_multiplier; + } + + insn = slot[slotnum]; + + if (unit == IA64_UNIT_NIL) + goto decoding_failed; + + idesc = ia64_dis_opcode (insn, unit_to_type (insn, unit)); + if (idesc == NULL) + goto decoding_failed; + + /* print predicate, if any: */ + + if ((idesc->flags & IA64_OPCODE_NO_PRED) + || (insn & 0x3f) == 0) + (*info->fprintf_func) (info->stream, " "); + else + (*info->fprintf_func) (info->stream, "(p%02d) ", (int)(insn & 0x3f)); + + /* now the actual instruction: */ + + (*info->fprintf_func) (info->stream, "%s", idesc->name); + if (idesc->operands[0]) + (*info->fprintf_func) (info->stream, " "); + + need_comma = 0; + for (j = 0; j < NELEMS (idesc->operands) && idesc->operands[j]; ++j) + { + odesc = elf64_ia64_operands + idesc->operands[j]; + + if (need_comma) + (*info->fprintf_func) (info->stream, ","); + + if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64) + { + /* special case of 64 bit immediate load: */ + value = ((insn >> 13) & 0x7f) | (((insn >> 27) & 0x1ff) << 7) + | (((insn >> 22) & 0x1f) << 16) | (((insn >> 21) & 0x1) << 21) + | (slot[1] << 22) | (((insn >> 36) & 0x1) << 63); + } + else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62) + { + /* 62-bit immediate for nop.x/break.x */ + value = ((slot[1] & 0x1ffffffffffLL) << 21) + | (((insn >> 36) & 0x1) << 20) + | ((insn >> 6) & 0xfffff); + } + else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64) + { + /* 60-bit immediate for long branches. */ + value = (((insn >> 13) & 0xfffff) + | (((insn >> 36) & 1) << 59) + | (((slot[1] >> 2) & 0x7fffffffffLL) << 20)) << 4; + } + else + { + err = (*odesc->extract) (odesc, insn, &value); + if (err) + { + (*info->fprintf_func) (info->stream, "%s", err); + goto done; + } + } + + switch (odesc->class) + { + case IA64_OPND_CLASS_CST: + (*info->fprintf_func) (info->stream, "%s", odesc->str); + break; + + case IA64_OPND_CLASS_REG: + if (odesc->str[0] == 'a' && odesc->str[1] == 'r') + { + switch (value) + { + case 0: case 1: case 2: case 3: + case 4: case 5: case 6: case 7: + sprintf (regname, "ar.k%u", (unsigned int) value); + break; + case 16: strcpy (regname, "ar.rsc"); break; + case 17: strcpy (regname, "ar.bsp"); break; + case 18: strcpy (regname, "ar.bspstore"); break; + case 19: strcpy (regname, "ar.rnat"); break; + case 32: strcpy (regname, "ar.ccv"); break; + case 36: strcpy (regname, "ar.unat"); break; + case 40: strcpy (regname, "ar.fpsr"); break; + case 44: strcpy (regname, "ar.itc"); break; + case 64: strcpy (regname, "ar.pfs"); break; + case 65: strcpy (regname, "ar.lc"); break; + case 66: strcpy (regname, "ar.ec"); break; + default: + sprintf (regname, "ar%u", (unsigned int) value); + break; + } + (*info->fprintf_func) (info->stream, "%s", regname); + } + else if (odesc->str[0] == 'c' && odesc->str[1] == 'r') + { + switch (value) + { + case 0: strcpy (regname, "cr.dcr"); break; + case 1: strcpy (regname, "cr.itm"); break; + case 2: strcpy (regname, "cr.iva"); break; + case 8: strcpy (regname, "cr.pta"); break; + case 16: strcpy (regname, "cr.ipsr"); break; + case 17: strcpy (regname, "cr.isr"); break; + case 19: strcpy (regname, "cr.iip"); break; + case 20: strcpy (regname, "cr.ifa"); break; + case 21: strcpy (regname, "cr.itir"); break; + case 22: strcpy (regname, "cr.iipa"); break; + case 23: strcpy (regname, "cr.ifs"); break; + case 24: strcpy (regname, "cr.iim"); break; + case 25: strcpy (regname, "cr.iha"); break; + case 64: strcpy (regname, "cr.lid"); break; + case 65: strcpy (regname, "cr.ivr"); break; + case 66: strcpy (regname, "cr.tpr"); break; + case 67: strcpy (regname, "cr.eoi"); break; + case 68: strcpy (regname, "cr.irr0"); break; + case 69: strcpy (regname, "cr.irr1"); break; + case 70: strcpy (regname, "cr.irr2"); break; + case 71: strcpy (regname, "cr.irr3"); break; + case 72: strcpy (regname, "cr.itv"); break; + case 73: strcpy (regname, "cr.pmv"); break; + case 74: strcpy (regname, "cr.cmcv"); break; + case 80: strcpy (regname, "cr.lrr0"); break; + case 81: strcpy (regname, "cr.lrr1"); break; + default: + sprintf (regname, "cr%u", (unsigned int) value); + break; + } + (*info->fprintf_func) (info->stream, "%s", regname); + } + else + (*info->fprintf_func) (info->stream, "%s%d", odesc->str, (int)value); + break; + + case IA64_OPND_CLASS_IND: + (*info->fprintf_func) (info->stream, "%s[r%d]", odesc->str, (int)value); + break; + + case IA64_OPND_CLASS_ABS: + str = 0; + if (odesc - elf64_ia64_operands == IA64_OPND_MBTYPE4) + switch (value) + { + case 0x0: str = "@brcst"; break; + case 0x8: str = "@mix"; break; + case 0x9: str = "@shuf"; break; + case 0xa: str = "@alt"; break; + case 0xb: str = "@rev"; break; + } + + if (str) + (*info->fprintf_func) (info->stream, "%s", str); + else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_SIGNED) + (*info->fprintf_func) (info->stream, "%lld", (long long) value); + else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_UNSIGNED) + (*info->fprintf_func) (info->stream, "%llu", (long long) value); + else + (*info->fprintf_func) (info->stream, "0x%llx", (long long) value); + break; + + case IA64_OPND_CLASS_REL: + (*info->print_address_func) (memaddr + value, info); + break; + } + + need_comma = 1; + if (j + 1 == idesc->num_outputs) + { + (*info->fprintf_func) (info->stream, "="); + need_comma = 0; + } + } + if (slotnum + 1 == ia64_templ_desc[template].group_boundary + || ((slotnum == 2) && s_bit)) + (*info->fprintf_func) (info->stream, ";;"); + + done: + ia64_free_opcode ((struct ia64_opcode *)idesc); + failed: + if (slotnum == 2) + retval += 16 - 3*slot_multiplier; + return retval; + + decoding_failed: + (*info->fprintf_func) (info->stream, " data8 %#011llx", (long long) insn); + goto failed; +} diff --git a/tools/debugger/xenitp/ia64-gen.c b/tools/debugger/xenitp/ia64-gen.c new file mode 100644 index 0000000..5562283 --- /dev/null +++ b/tools/debugger/xenitp/ia64-gen.c @@ -0,0 +1,2865 @@ +/* ia64-gen.c -- Generate a shrunk set of opcode tables + Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + Written by Bob Manson, Cygnus Solutions, + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* While the ia64-opc-* set of opcode tables are easy to maintain, + they waste a tremendous amount of space. ia64-gen rearranges the + instructions into a directed acyclic graph (DAG) of instruction opcodes and + their possible completers, as well as compacting the set of strings used. + + The disassembler table consists of a state machine that does + branching based on the bits of the opcode being disassembled. The + state encodings have been chosen to minimize the amount of space + required. + + The resource table is constructed based on some text dependency tables, + which are also easier to maintain than the final representation. */ + +#include +#include +#include + +#include "ansidecl.h" +#include "libiberty.h" +#include "safe-ctype.h" +#include "sysdep.h" +#include "getopt.h" +#include "ia64-opc.h" +#include "ia64-opc-a.c" +#include "ia64-opc-i.c" +#include "ia64-opc-m.c" +#include "ia64-opc-b.c" +#include "ia64-opc-f.c" +#include "ia64-opc-x.c" +#include "ia64-opc-d.c" + +#include +#define _(String) gettext (String) + +/* This is a copy of fprintf_vma from bfd/bfd-in2.h. We have to use this + always, because we might be compiled without BFD64 defined, if configured + for a 32-bit target and --enable-targets=all is used. This will work for + both 32-bit and 64-bit hosts. */ +#define _opcode_int64_low(x) ((unsigned long) (((x) & 0xffffffff))) +#define _opcode_int64_high(x) ((unsigned long) (((x) >> 32) & 0xffffffff)) +#define opcode_fprintf_vma(s,x) \ + fprintf ((s), "%08lx%08lx", _opcode_int64_high (x), _opcode_int64_low (x)) + +const char * program_name = NULL; +int debug = 0; + +#define NELEMS(a) (sizeof (a) / sizeof ((a)[0])) +#define tmalloc(X) (X *) xmalloc (sizeof (X)) + +/* The main opcode table entry. Each entry is a unique combination of + name and flags (no two entries in the table compare as being equal + via opcodes_eq). */ +struct main_entry +{ + /* The base name of this opcode. The names of its completers are + appended to it to generate the full instruction name. */ + struct string_entry *name; + /* The base opcode entry. Which one to use is a fairly arbitrary choice; + it uses the first one passed to add_opcode_entry. */ + struct ia64_opcode *opcode; + /* The list of completers that can be applied to this opcode. */ + struct completer_entry *completers; + /* Next entry in the chain. */ + struct main_entry *next; + /* Index in the main table. */ + int main_index; +} *maintable, **ordered_table; + +int otlen = 0; +int ottotlen = 0; +int opcode_count = 0; + +/* The set of possible completers for an opcode. */ +struct completer_entry +{ + /* This entry's index in the ia64_completer_table[] array. */ + int num; + + /* The name of the completer. */ + struct string_entry *name; + + /* This entry's parent. */ + struct completer_entry *parent; + + /* Set if this is a terminal completer (occurs at the end of an + opcode). */ + int is_terminal; + + /* An alternative completer. */ + struct completer_entry *alternative; + + /* Additional completers that can be appended to this one. */ + struct completer_entry *addl_entries; + + /* Before compute_completer_bits () is invoked, this contains the actual + instruction opcode for this combination of opcode and completers. + Afterwards, it contains those bits that are different from its + parent opcode. */ + ia64_insn bits; + + /* Bits set to 1 correspond to those bits in this completer's opcode + that are different from its parent completer's opcode (or from + the base opcode if the entry is the root of the opcode's completer + list). This field is filled in by compute_completer_bits (). */ + ia64_insn mask; + + /* Index into the opcode dependency list, or -1 if none. */ + int dependencies; + + /* Remember the order encountered in the opcode tables. */ + int order; +}; + +/* One entry in the disassembler name table. */ +struct disent +{ + /* The index into the ia64_name_dis array for this entry. */ + int ournum; + + /* The index into the main_table[] array. */ + int insn; + + /* The disassmbly priority of this entry. */ + int priority; + + /* The completer_index value for this entry. */ + int completer_index; + + /* How many other entries share this decode. */ + int nextcnt; + + /* The next entry sharing the same decode. */ + struct disent *nexte; + + /* The next entry in the name list. */ + struct disent *next_ent; +} *disinsntable = NULL; + +/* A state machine that will eventually be used to generate the + disassembler table. */ +struct bittree +{ + struct disent *disent; + struct bittree *bits[3]; /* 0, 1, and X (don't care). */ + int bits_to_skip; + int skip_flag; +} *bittree; + +/* The string table contains all opcodes and completers sorted in + alphabetical order. */ + +/* One entry in the string table. */ +struct string_entry +{ + /* The index in the ia64_strings[] array for this entry. */ + int num; + /* And the string. */ + char *s; +} **string_table = NULL; + +int strtablen = 0; +int strtabtotlen = 0; + + +/* Resource dependency entries. */ +struct rdep +{ + char *name; /* Resource name. */ + unsigned + mode:2, /* RAW, WAW, or WAR. */ + semantics:3; /* Dependency semantics. */ + char *extra; /* Additional semantics info. */ + int nchks; + int total_chks; /* Total #of terminal insns. */ + int *chks; /* Insn classes which read (RAW), write + (WAW), or write (WAR) this rsrc. */ + int *chknotes; /* Dependency notes for each class. */ + int nregs; + int total_regs; /* Total #of terminal insns. */ + int *regs; /* Insn class which write (RAW), write2 + (WAW), or read (WAR) this rsrc. */ + int *regnotes; /* Dependency notes for each class. */ + + int waw_special; /* Special WAW dependency note. */ +} **rdeps = NULL; + +static int rdepslen = 0; +static int rdepstotlen = 0; + +/* Array of all instruction classes. */ +struct iclass +{ + char *name; /* Instruction class name. */ + int is_class; /* Is a class, not a terminal. */ + int nsubs; + int *subs; /* Other classes within this class. */ + int nxsubs; + int xsubs[4]; /* Exclusions. */ + char *comment; /* Optional comment. */ + int note; /* Optional note. */ + int terminal_resolved; /* Did we match this with anything? */ + int orphan; /* Detect class orphans. */ +} **ics = NULL; + +static int iclen = 0; +static int ictotlen = 0; + +/* An opcode dependency (chk/reg pair of dependency lists). */ +struct opdep +{ + int chk; /* index into dlists */ + int reg; /* index into dlists */ +} **opdeps; + +static int opdeplen = 0; +static int opdeptotlen = 0; + +/* A generic list of dependencies w/notes encoded. These may be shared. */ +struct deplist +{ + int len; + unsigned short *deps; +} **dlists; + +static int dlistlen = 0; +static int dlisttotlen = 0; + + +static void fail (const char *, ...) ATTRIBUTE_PRINTF_1; +static void warn (const char *, ...) ATTRIBUTE_PRINTF_1; +static struct rdep * insert_resource (const char *, enum ia64_dependency_mode); +static int deplist_equals (struct deplist *, struct deplist *); +static short insert_deplist (int, unsigned short *); +static short insert_dependencies (int, unsigned short *, int, unsigned short *); +static void mark_used (struct iclass *, int); +static int fetch_insn_class (const char *, int); +static int sub_compare (const void *, const void *); +static void load_insn_classes (void); +static void parse_resource_users (const char *, int **, int *, int **); +static int parse_semantics (char *); +static void add_dep (const char *, const char *, const char *, int, int, char *, int); +static void load_depfile (const char *, enum ia64_dependency_mode); +static void load_dependencies (void); +static int irf_operand (int, const char *); +static int in_iclass_mov_x (struct ia64_opcode *, struct iclass *, const char *, const char *); +static int in_iclass (struct ia64_opcode *, struct iclass *, const char *, const char *, int *); +static int lookup_regindex (const char *, int); +static int lookup_specifier (const char *); +static void print_dependency_table (void); +static struct string_entry * insert_string (char *); +static void gen_dis_table (struct bittree *); +static void print_dis_table (void); +static void generate_disassembler (void); +static void print_string_table (void); +static int completer_entries_eq (struct completer_entry *, struct completer_entry *); +static struct completer_entry * insert_gclist (struct completer_entry *); +static int get_prefix_len (const char *); +static void compute_completer_bits (struct main_entry *, struct completer_entry *); +static void collapse_redundant_completers (void); +static int insert_opcode_dependencies (struct ia64_opcode *, struct completer_entry *); +static void insert_completer_entry (struct ia64_opcode *, struct main_entry *, int); +static void print_completer_entry (struct completer_entry *); +static void print_completer_table (void); +static int opcodes_eq (struct ia64_opcode *, struct ia64_opcode *); +static void add_opcode_entry (struct ia64_opcode *); +static void print_main_table (void); +static void shrink (struct ia64_opcode *); +static void print_version (void); +static void usage (FILE *, int); +static void finish_distable (void); +static void insert_bit_table_ent (struct bittree *, int, ia64_insn, ia64_insn, int, int, int); +static void add_dis_entry (struct bittree *, ia64_insn, ia64_insn, int, struct completer_entry *, int); +static void compact_distree (struct bittree *); +static struct bittree * make_bittree_entry (void); +static struct disent * add_dis_table_ent (struct disent *, int, int, int); + + +static void +fail (const char *message, ...) +{ + va_list args; + + va_start (args, message); + fprintf (stderr, _("%s: Error: "), program_name); + vfprintf (stderr, message, args); + va_end (args); + xexit (1); +} + +static void +warn (const char *message, ...) +{ + va_list args; + + va_start (args, message); + + fprintf (stderr, _("%s: Warning: "), program_name); + vfprintf (stderr, message, args); + va_end (args); +} + +/* Add NAME to the resource table, where TYPE is RAW or WAW. */ +static struct rdep * +insert_resource (const char *name, enum ia64_dependency_mode type) +{ + if (rdepslen == rdepstotlen) + { + rdepstotlen += 20; + rdeps = (struct rdep **) + xrealloc (rdeps, sizeof(struct rdep **) * rdepstotlen); + } + rdeps[rdepslen] = tmalloc(struct rdep); + memset((void *)rdeps[rdepslen], 0, sizeof(struct rdep)); + rdeps[rdepslen]->name = xstrdup (name); + rdeps[rdepslen]->mode = type; + rdeps[rdepslen]->waw_special = 0; + + return rdeps[rdepslen++]; +} + +/* Are the lists of dependency indexes equivalent? */ +static int +deplist_equals (struct deplist *d1, struct deplist *d2) +{ + int i; + + if (d1->len != d2->len) + return 0; + + for (i = 0; i < d1->len; i++) + if (d1->deps[i] != d2->deps[i]) + return 0; + + return 1; +} + +/* Add the list of dependencies to the list of dependency lists. */ +static short +insert_deplist (int count, unsigned short *deps) +{ + /* Sort the list, then see if an equivalent list exists already. + this results in a much smaller set of dependency lists. */ + struct deplist *list; + char set[0x10000]; + int i; + + memset ((void *)set, 0, sizeof (set)); + for (i = 0; i < count; i++) + set[deps[i]] = 1; + + count = 0; + for (i = 0; i < (int) sizeof (set); i++) + if (set[i]) + ++count; + + list = tmalloc (struct deplist); + list->len = count; + list->deps = (unsigned short *) malloc (sizeof (unsigned short) * count); + + for (i = 0, count = 0; i < (int) sizeof (set); i++) + if (set[i]) + list->deps[count++] = i; + + /* Does this list exist already? */ + for (i = 0; i < dlistlen; i++) + if (deplist_equals (list, dlists[i])) + { + free (list->deps); + free (list); + return i; + } + + if (dlistlen == dlisttotlen) + { + dlisttotlen += 20; + dlists = (struct deplist **) + xrealloc (dlists, sizeof(struct deplist **) * dlisttotlen); + } + dlists[dlistlen] = list; + + return dlistlen++; +} + +/* Add the given pair of dependency lists to the opcode dependency list. */ +static short +insert_dependencies (int nchks, unsigned short *chks, + int nregs, unsigned short *regs) +{ + struct opdep *pair; + int i; + int regind = -1; + int chkind = -1; + + if (nregs > 0) + regind = insert_deplist (nregs, regs); + if (nchks > 0) + chkind = insert_deplist (nchks, chks); + + for (i = 0; i < opdeplen; i++) + if (opdeps[i]->chk == chkind + && opdeps[i]->reg == regind) + return i; + + pair = tmalloc (struct opdep); + pair->chk = chkind; + pair->reg = regind; + + if (opdeplen == opdeptotlen) + { + opdeptotlen += 20; + opdeps = (struct opdep **) + xrealloc (opdeps, sizeof(struct opdep **) * opdeptotlen); + } + opdeps[opdeplen] = pair; + + return opdeplen++; +} + +static void +mark_used (struct iclass *ic, int clear_terminals) +{ + int i; + + ic->orphan = 0; + if (clear_terminals) + ic->terminal_resolved = 1; + + for (i = 0; i < ic->nsubs; i++) + mark_used (ics[ic->subs[i]], clear_terminals); + + for (i = 0; i < ic->nxsubs; i++) + mark_used (ics[ic->xsubs[i]], clear_terminals); +} + +/* Look up an instruction class; if CREATE make a new one if none found; + returns the index into the insn class array. */ +static int +fetch_insn_class (const char *full_name, int create) +{ + char *name; + char *notestr; + char *xsect; + char *comment; + int i, note = 0; + int ind; + int is_class = 0; + + if (strncmp (full_name, "IC:", 3) == 0) + { + name = xstrdup (full_name + 3); + is_class = 1; + } + else + name = xstrdup (full_name); + + if ((xsect = strchr(name, '\\')) != NULL) + is_class = 1; + if ((comment = strchr(name, '[')) != NULL) + is_class = 1; + if ((notestr = strchr(name, '+')) != NULL) + is_class = 1; + + /* If it is a composite class, then ignore comments and notes that come after + the '\\', since they don't apply to the part we are decoding now. */ + if (xsect) + { + if (comment > xsect) + comment = 0; + if (notestr > xsect) + notestr = 0; + } + + if (notestr) + { + char *nextnotestr; + + note = atoi (notestr + 1); + if ((nextnotestr = strchr (notestr + 1, '+')) != NULL) + { + if (strcmp (notestr, "+1+13") == 0) + note = 13; + else if (!xsect || nextnotestr < xsect) + warn (_("multiple note %s not handled\n"), notestr); + } + } + + /* If it's a composite class, leave the notes and comments in place so that + we have a unique name for the composite class. Otherwise, we remove + them. */ + if (!xsect) + { + if (notestr) + *notestr = 0; + if (comment) + *comment = 0; + } + + for (i = 0; i < iclen; i++) + if (strcmp (name, ics[i]->name) == 0 + && ((comment == NULL && ics[i]->comment == NULL) + || (comment != NULL && ics[i]->comment != NULL + && strncmp (ics[i]->comment, comment, + strlen (ics[i]->comment)) == 0)) + && note == ics[i]->note) + return i; + + if (!create) + return -1; + + /* Doesn't exist, so make a new one. */ + if (iclen == ictotlen) + { + ictotlen += 20; + ics = (struct iclass **) + xrealloc (ics, (ictotlen) * sizeof (struct iclass *)); + } + + ind = iclen++; + ics[ind] = tmalloc (struct iclass); + memset ((void *)ics[ind], 0, sizeof (struct iclass)); + ics[ind]->name = xstrdup (name); + ics[ind]->is_class = is_class; + ics[ind]->orphan = 1; + + if (comment) + { + ics[ind]->comment = xstrdup (comment + 1); + ics[ind]->comment[strlen (ics[ind]->comment)-1] = 0; + } + + if (notestr) + ics[ind]->note = note; + + /* If it's a composite class, there's a comment or note, look for an + existing class or terminal with the same name. */ + if ((xsect || comment || notestr) && is_class) + { + /* First, populate with the class we're based on. */ + char *subname = name; + + if (xsect) + *xsect = 0; + else if (comment) + *comment = 0; + else if (notestr) + *notestr = 0; + + ics[ind]->nsubs = 1; + ics[ind]->subs = tmalloc(int); + ics[ind]->subs[0] = fetch_insn_class (subname, 1);; + } + + while (xsect) + { + char *subname = xsect + 1; + + xsect = strchr (subname, '\\'); + if (xsect) + *xsect = 0; + ics[ind]->xsubs[ics[ind]->nxsubs] = fetch_insn_class (subname,1); + ics[ind]->nxsubs++; + } + free (name); + + return ind; +} + +/* For sorting a class's sub-class list only; make sure classes appear before + terminals. */ +static int +sub_compare (const void *e1, const void *e2) +{ + struct iclass *ic1 = ics[*(int *)e1]; + struct iclass *ic2 = ics[*(int *)e2]; + + if (ic1->is_class) + { + if (!ic2->is_class) + return -1; + } + else if (ic2->is_class) + return 1; + + return strcmp (ic1->name, ic2->name); +} + +static void +load_insn_classes (void) +{ + FILE *fp = fopen ("ia64-ic.tbl", "r"); + char buf[2048]; + + if (fp == NULL) + fail (_("can't find ia64-ic.tbl for reading\n")); + + /* Discard first line. */ + fgets (buf, sizeof(buf), fp); + + while (!feof (fp)) + { + int iclass; + char *name; + char *tmp; + + if (fgets (buf, sizeof (buf), fp) == NULL) + break; + + while (ISSPACE (buf[strlen (buf) - 1])) + buf[strlen (buf) - 1] = '\0'; + + name = tmp = buf; + while (*tmp != ';') + { + ++tmp; + if (tmp == buf + sizeof (buf)) + abort (); + } + *tmp++ = '\0'; + + iclass = fetch_insn_class (name, 1); + ics[iclass]->is_class = 1; + + if (strcmp (name, "none") == 0) + { + ics[iclass]->is_class = 0; + ics[iclass]->terminal_resolved = 1; + continue; + } + + /* For this class, record all sub-classes. */ + while (*tmp) + { + char *subname; + int sub; + + while (*tmp && ISSPACE (*tmp)) + { + ++tmp; + if (tmp == buf + sizeof (buf)) + abort (); + } + subname = tmp; + while (*tmp && *tmp != ',') + { + ++tmp; + if (tmp == buf + sizeof (buf)) + abort (); + } + if (*tmp == ',') + *tmp++ = '\0'; + + ics[iclass]->subs = (int *) + xrealloc ((void *)ics[iclass]->subs, + (ics[iclass]->nsubs + 1) * sizeof (int)); + + sub = fetch_insn_class (subname, 1); + ics[iclass]->subs = (int *) + xrealloc (ics[iclass]->subs, (ics[iclass]->nsubs + 1) * sizeof (int)); + ics[iclass]->subs[ics[iclass]->nsubs++] = sub; + } + + /* Make sure classes come before terminals. */ + qsort ((void *)ics[iclass]->subs, + ics[iclass]->nsubs, sizeof(int), sub_compare); + } + fclose (fp); + + if (debug) + printf ("%d classes\n", iclen); +} + +/* Extract the insn classes from the given line. */ +static void +parse_resource_users (ref, usersp, nusersp, notesp) + const char *ref; + int **usersp; + int *nusersp; + int **notesp; +{ + int c; + char *line = xstrdup (ref); + char *tmp = line; + int *users = *usersp; + int count = *nusersp; + int *notes = *notesp; + + c = *tmp; + while (c != 0) + { + char *notestr; + int note; + char *xsect; + int iclass; + int create = 0; + char *name; + + while (ISSPACE (*tmp)) + ++tmp; + name = tmp; + while (*tmp && *tmp != ',') + ++tmp; + c = *tmp; + *tmp++ = '\0'; + + xsect = strchr (name, '\\'); + if ((notestr = strstr (name, "+")) != NULL) + { + char *nextnotestr; + + note = atoi (notestr + 1); + if ((nextnotestr = strchr (notestr + 1, '+')) != NULL) + { + /* Note 13 always implies note 1. */ + if (strcmp (notestr, "+1+13") == 0) + note = 13; + else if (!xsect || nextnotestr < xsect) + warn (_("multiple note %s not handled\n"), notestr); + } + if (!xsect) + *notestr = '\0'; + } + else + note = 0; + + /* All classes are created when the insn class table is parsed; + Individual instructions might not appear until the dependency tables + are read. Only create new classes if it's *not* an insn class, + or if it's a composite class (which wouldn't necessarily be in the IC + table). */ + if (strncmp (name, "IC:", 3) != 0 || xsect != NULL) + create = 1; + + iclass = fetch_insn_class (name, create); + if (iclass != -1) + { + users = (int *) + xrealloc ((void *) users,(count + 1) * sizeof (int)); + notes = (int *) + xrealloc ((void *) notes,(count + 1) * sizeof (int)); + notes[count] = note; + users[count++] = iclass; + mark_used (ics[iclass], 0); + } + else if (debug) + printf("Class %s not found\n", name); + } + /* Update the return values. */ + *usersp = users; + *nusersp = count; + *notesp = notes; + + free (line); +} + +static int +parse_semantics (char *sem) +{ + if (strcmp (sem, "none") == 0) + return IA64_DVS_NONE; + else if (strcmp (sem, "implied") == 0) + return IA64_DVS_IMPLIED; + else if (strcmp (sem, "impliedF") == 0) + return IA64_DVS_IMPLIEDF; + else if (strcmp (sem, "data") == 0) + return IA64_DVS_DATA; + else if (strcmp (sem, "instr") == 0) + return IA64_DVS_INSTR; + else if (strcmp (sem, "specific") == 0) + return IA64_DVS_SPECIFIC; + else if (strcmp (sem, "stop") == 0) + return IA64_DVS_STOP; + else + return IA64_DVS_OTHER; +} + +static void +add_dep (const char *name, const char *chk, const char *reg, + int semantics, int mode, char *extra, int flag) +{ + struct rdep *rs; + + rs = insert_resource (name, mode); + + parse_resource_users (chk, &rs->chks, &rs->nchks, &rs->chknotes); + parse_resource_users (reg, &rs->regs, &rs->nregs, &rs->regnotes); + + rs->semantics = semantics; + rs->extra = extra; + rs->waw_special = flag; +} + +static void +load_depfile (const char *filename, enum ia64_dependency_mode mode) +{ + FILE *fp = fopen (filename, "r"); + char buf[1024]; + + if (fp == NULL) + fail (_("can't find %s for reading\n"), filename); + + fgets (buf, sizeof(buf), fp); + while (!feof (fp)) + { + char *name, *tmp; + int semantics; + char *extra; + char *regp, *chkp; + + if (fgets (buf, sizeof(buf), fp) == NULL) + break; + + while (ISSPACE (buf[strlen (buf) - 1])) + buf[strlen (buf) - 1] = '\0'; + + name = tmp = buf; + while (*tmp != ';') + ++tmp; + *tmp++ = '\0'; + + while (ISSPACE (*tmp)) + ++tmp; + regp = tmp; + tmp = strchr (tmp, ';'); + if (!tmp) + abort (); + *tmp++ = 0; + while (ISSPACE (*tmp)) + ++tmp; + chkp = tmp; + tmp = strchr (tmp, ';'); + if (!tmp) + abort (); + *tmp++ = 0; + while (ISSPACE (*tmp)) + ++tmp; + semantics = parse_semantics (tmp); + extra = semantics == IA64_DVS_OTHER ? xstrdup (tmp) : NULL; + + /* For WAW entries, if the chks and regs differ, we need to enter the + entries in both positions so that the tables will be parsed properly, + without a lot of extra work. */ + if (mode == IA64_DV_WAW && strcmp (regp, chkp) != 0) + { + add_dep (name, chkp, regp, semantics, mode, extra, 0); + add_dep (name, regp, chkp, semantics, mode, extra, 1); + } + else + { + add_dep (name, chkp, regp, semantics, mode, extra, 0); + } + } + fclose (fp); +} + +static void +load_dependencies (void) +{ + load_depfile ("ia64-raw.tbl", IA64_DV_RAW); + load_depfile ("ia64-waw.tbl", IA64_DV_WAW); + load_depfile ("ia64-war.tbl", IA64_DV_WAR); + + if (debug) + printf ("%d RAW/WAW/WAR dependencies\n", rdepslen); +} + +/* Is the given operand an indirect register file operand? */ +static int +irf_operand (int op, const char *field) +{ + if (!field) + { + return op == IA64_OPND_RR_R3 || op == IA64_OPND_DBR_R3 + || op == IA64_OPND_IBR_R3 || op == IA64_OPND_PKR_R3 + || op == IA64_OPND_PMC_R3 || op == IA64_OPND_PMD_R3 + || op == IA64_OPND_MSR_R3 || op == IA64_OPND_CPUID_R3; + } + else + { + return ((op == IA64_OPND_RR_R3 && strstr (field, "rr")) + || (op == IA64_OPND_DBR_R3 && strstr (field, "dbr")) + || (op == IA64_OPND_IBR_R3 && strstr (field, "ibr")) + || (op == IA64_OPND_PKR_R3 && strstr (field, "pkr")) + || (op == IA64_OPND_PMC_R3 && strstr (field, "pmc")) + || (op == IA64_OPND_PMD_R3 && strstr (field, "pmd")) + || (op == IA64_OPND_MSR_R3 && strstr (field, "msr")) + || (op == IA64_OPND_CPUID_R3 && strstr (field, "cpuid"))); + } +} + +/* Handle mov_ar, mov_br, mov_cr, mov_indirect, mov_ip, mov_pr, mov_psr, and + mov_um insn classes. */ +static int +in_iclass_mov_x (struct ia64_opcode *idesc, struct iclass *ic, + const char *format, const char *field) +{ + int plain_mov = strcmp (idesc->name, "mov") == 0; + + if (!format) + return 0; + + switch (ic->name[4]) + { + default: + abort (); + case 'a': + { + int i = strcmp (idesc->name, "mov.i") == 0; + int m = strcmp (idesc->name, "mov.m") == 0; + int i2627 = i && idesc->operands[0] == IA64_OPND_AR3; + int i28 = i && idesc->operands[1] == IA64_OPND_AR3; + int m2930 = m && idesc->operands[0] == IA64_OPND_AR3; + int m31 = m && idesc->operands[1] == IA64_OPND_AR3; + int pseudo0 = plain_mov && idesc->operands[1] == IA64_OPND_AR3; + int pseudo1 = plain_mov && idesc->operands[0] == IA64_OPND_AR3; + + /* IC:mov ar */ + if (i2627) + return strstr (format, "I26") || strstr (format, "I27"); + if (i28) + return strstr (format, "I28") != NULL; + if (m2930) + return strstr (format, "M29") || strstr (format, "M30"); + if (m31) + return strstr (format, "M31") != NULL; + if (pseudo0 || pseudo1) + return 1; + } + break; + case 'b': + { + int i21 = idesc->operands[0] == IA64_OPND_B1; + int i22 = plain_mov && idesc->operands[1] == IA64_OPND_B2; + if (i22) + return strstr (format, "I22") != NULL; + if (i21) + return strstr (format, "I21") != NULL; + } + break; + case 'c': + { + int m32 = plain_mov && idesc->operands[0] == IA64_OPND_CR3; + int m33 = plain_mov && idesc->operands[1] == IA64_OPND_CR3; + if (m32) + return strstr (format, "M32") != NULL; + if (m33) + return strstr (format, "M33") != NULL; + } + break; + case 'i': + if (ic->name[5] == 'n') + { + int m42 = plain_mov && irf_operand (idesc->operands[0], field); + int m43 = plain_mov && irf_operand (idesc->operands[1], field); + if (m42) + return strstr (format, "M42") != NULL; + if (m43) + return strstr (format, "M43") != NULL; + } + else if (ic->name[5] == 'p') + { + return idesc->operands[1] == IA64_OPND_IP; + } + else + abort (); + break; + case 'p': + if (ic->name[5] == 'r') + { + int i25 = plain_mov && idesc->operands[1] == IA64_OPND_PR; + int i23 = plain_mov && idesc->operands[0] == IA64_OPND_PR; + int i24 = plain_mov && idesc->operands[0] == IA64_OPND_PR_ROT; + if (i23) + return strstr (format, "I23") != NULL; + if (i24) + return strstr (format, "I24") != NULL; + if (i25) + return strstr (format, "I25") != NULL; + } + else if (ic->name[5] == 's') + { + int m35 = plain_mov && idesc->operands[0] == IA64_OPND_PSR_L; + int m36 = plain_mov && idesc->operands[1] == IA64_OPND_PSR; + if (m35) + return strstr (format, "M35") != NULL; + if (m36) + return strstr (format, "M36") != NULL; + } + else + abort (); + break; + case 'u': + { + int m35 = plain_mov && idesc->operands[0] == IA64_OPND_PSR_UM; + int m36 = plain_mov && idesc->operands[1] == IA64_OPND_PSR_UM; + if (m35) + return strstr (format, "M35") != NULL; + if (m36) + return strstr (format, "M36") != NULL; + } + break; + } + return 0; +} + +/* Is the given opcode in the given insn class? */ +static int +in_iclass (struct ia64_opcode *idesc, struct iclass *ic, + const char *format, const char *field, int *notep) +{ + int i; + int resolved = 0; + + if (ic->comment) + { + if (!strncmp (ic->comment, "Format", 6)) + { + /* Assume that the first format seen is the most restrictive, and + only keep a later one if it looks like it's more restrictive. */ + if (format) + { + if (strlen (ic->comment) < strlen (format)) + { + warn (_("most recent format '%s'\nappears more restrictive than '%s'\n"), + ic->comment, format); + format = ic->comment; + } + } + else + format = ic->comment; + } + else if (!strncmp (ic->comment, "Field", 5)) + { + if (field) + warn (_("overlapping field %s->%s\n"), + ic->comment, field); + field = ic->comment; + } + } + + /* An insn class matches anything that is the same followed by completers, + except when the absence and presence of completers constitutes different + instructions. */ + if (ic->nsubs == 0 && ic->nxsubs == 0) + { + int is_mov = strncmp (idesc->name, "mov", 3) == 0; + int plain_mov = strcmp (idesc->name, "mov") == 0; + int len = strlen(ic->name); + + resolved = ((strncmp (ic->name, idesc->name, len) == 0) + && (idesc->name[len] == '\0' + || idesc->name[len] == '.')); + + /* All break, nop, and hint variations must match exactly. */ + if (resolved && + (strcmp (ic->name, "break") == 0 + || strcmp (ic->name, "nop") == 0 + || strcmp (ic->name, "hint") == 0)) + resolved = strcmp (ic->name, idesc->name) == 0; + + /* Assume restrictions in the FORMAT/FIELD negate resolution, + unless specifically allowed by clauses in this block. */ + if (resolved && field) + { + /* Check Field(sf)==sN against opcode sN. */ + if (strstr(field, "(sf)==") != NULL) + { + char *sf; + + if ((sf = strstr (idesc->name, ".s")) != 0) + resolved = strcmp (sf + 1, strstr (field, "==") + 2) == 0; + } + /* Check Field(lftype)==XXX. */ + else if (strstr (field, "(lftype)") != NULL) + { + if (strstr (idesc->name, "fault") != NULL) + resolved = strstr (field, "fault") != NULL; + else + resolved = strstr (field, "fault") == NULL; + } + /* Handle Field(ctype)==XXX. */ + else if (strstr (field, "(ctype)") != NULL) + { + if (strstr (idesc->name, "or.andcm")) + resolved = strstr (field, "or.andcm") != NULL; + else if (strstr (idesc->name, "and.orcm")) + resolved = strstr (field, "and.orcm") != NULL; + else if (strstr (idesc->name, "orcm")) + resolved = strstr (field, "or orcm") != NULL; + else if (strstr (idesc->name, "or")) + resolved = strstr (field, "or orcm") != NULL; + else if (strstr (idesc->name, "andcm")) + resolved = strstr (field, "and andcm") != NULL; + else if (strstr (idesc->name, "and")) + resolved = strstr (field, "and andcm") != NULL; + else if (strstr (idesc->name, "unc")) + resolved = strstr (field, "unc") != NULL; + else + resolved = strcmp (field, "Field(ctype)==") == 0; + } + } + + if (resolved && format) + { + if (strncmp (idesc->name, "dep", 3) == 0 + && strstr (format, "I13") != NULL) + resolved = idesc->operands[1] == IA64_OPND_IMM8; + else if (strncmp (idesc->name, "chk", 3) == 0 + && strstr (format, "M21") != NULL) + resolved = idesc->operands[0] == IA64_OPND_F2; + else if (strncmp (idesc->name, "lfetch", 6) == 0) + resolved = (strstr (format, "M14 M15") != NULL + && (idesc->operands[1] == IA64_OPND_R2 + || idesc->operands[1] == IA64_OPND_IMM9b)); + else if (strncmp (idesc->name, "br.call", 7) == 0 + && strstr (format, "B5") != NULL) + resolved = idesc->operands[1] == IA64_OPND_B2; + else if (strncmp (idesc->name, "br.call", 7) == 0 + && strstr (format, "B3") != NULL) + resolved = idesc->operands[1] == IA64_OPND_TGT25c; + else if (strncmp (idesc->name, "brp", 3) == 0 + && strstr (format, "B7") != NULL) + resolved = idesc->operands[0] == IA64_OPND_B2; + else if (strcmp (ic->name, "invala") == 0) + resolved = strcmp (idesc->name, ic->name) == 0; + else if (strncmp (idesc->name, "st", 2) == 0 + && (strstr (format, "M5") != NULL + || strstr (format, "M10") != NULL)) + resolved = idesc->flags & IA64_OPCODE_POSTINC; + else if (strncmp (idesc->name, "ld", 2) == 0 + && (strstr (format, "M2 M3") != NULL + || strstr (format, "M12") != NULL + || strstr (format, "M7 M8") != NULL)) + resolved = idesc->flags & IA64_OPCODE_POSTINC; + else + resolved = 0; + } + + /* Misc brl variations ('.cond' is optional); + plain brl matches brl.cond. */ + if (!resolved + && (strcmp (idesc->name, "brl") == 0 + || strncmp (idesc->name, "brl.", 4) == 0) + && strcmp (ic->name, "brl.cond") == 0) + { + resolved = 1; + } + + /* Misc br variations ('.cond' is optional). */ + if (!resolved + && (strcmp (idesc->name, "br") == 0 + || strncmp (idesc->name, "br.", 3) == 0) + && strcmp (ic->name, "br.cond") == 0) + { + if (format) + resolved = (strstr (format, "B4") != NULL + && idesc->operands[0] == IA64_OPND_B2) + || (strstr (format, "B1") != NULL + && idesc->operands[0] == IA64_OPND_TGT25c); + else + resolved = 1; + } + + /* probe variations. */ + if (!resolved && strncmp (idesc->name, "probe", 5) == 0) + { + resolved = strcmp (ic->name, "probe") == 0 + && !((strstr (idesc->name, "fault") != NULL) + ^ (format && strstr (format, "M40") != NULL)); + } + + /* mov variations. */ + if (!resolved && is_mov) + { + if (plain_mov) + { + /* mov alias for fmerge. */ + if (strcmp (ic->name, "fmerge") == 0) + { + resolved = idesc->operands[0] == IA64_OPND_F1 + && idesc->operands[1] == IA64_OPND_F3; + } + /* mov alias for adds (r3 or imm14). */ + else if (strcmp (ic->name, "adds") == 0) + { + resolved = (idesc->operands[0] == IA64_OPND_R1 + && (idesc->operands[1] == IA64_OPND_R3 + || (idesc->operands[1] == IA64_OPND_IMM14))); + } + /* mov alias for addl. */ + else if (strcmp (ic->name, "addl") == 0) + { + resolved = idesc->operands[0] == IA64_OPND_R1 + && idesc->operands[1] == IA64_OPND_IMM22; + } + } + + /* Some variants of mov and mov.[im]. */ + if (!resolved && strncmp (ic->name, "mov_", 4) == 0) + resolved = in_iclass_mov_x (idesc, ic, format, field); + } + + /* Keep track of this so we can flag any insn classes which aren't + mapped onto at least one real insn. */ + if (resolved) + ic->terminal_resolved = 1; + } + else for (i = 0; i < ic->nsubs; i++) + { + if (in_iclass (idesc, ics[ic->subs[i]], format, field, notep)) + { + int j; + + for (j = 0; j < ic->nxsubs; j++) + if (in_iclass (idesc, ics[ic->xsubs[j]], NULL, NULL, NULL)) + return 0; + + if (debug > 1) + printf ("%s is in IC %s\n", idesc->name, ic->name); + + resolved = 1; + break; + } + } + + /* If it's in this IC, add the IC note (if any) to the insn. */ + if (resolved) + { + if (ic->note && notep) + { + if (*notep && *notep != ic->note) + warn (_("overwriting note %d with note %d (IC:%s)\n"), + *notep, ic->note, ic->name); + + *notep = ic->note; + } + } + + return resolved; +} + + +static int +lookup_regindex (const char *name, int specifier) +{ + switch (specifier) + { + case IA64_RS_ARX: + if (strstr (name, "[RSC]")) + return 16; + if (strstr (name, "[BSP]")) + return 17; + else if (strstr (name, "[BSPSTORE]")) + return 18; + else if (strstr (name, "[RNAT]")) + return 19; + else if (strstr (name, "[FCR]")) + return 21; + else if (strstr (name, "[EFLAG]")) + return 24; + else if (strstr (name, "[CSD]")) + return 25; + else if (strstr (name, "[SSD]")) + return 26; + else if (strstr (name, "[CFLG]")) + return 27; + else if (strstr (name, "[FSR]")) + return 28; + else if (strstr (name, "[FIR]")) + return 29; + else if (strstr (name, "[FDR]")) + return 30; + else if (strstr (name, "[CCV]")) + return 32; + else if (strstr (name, "[ITC]")) + return 44; + else if (strstr (name, "[PFS]")) + return 64; + else if (strstr (name, "[LC]")) + return 65; + else if (strstr (name, "[EC]")) + return 66; + abort (); + case IA64_RS_CRX: + if (strstr (name, "[DCR]")) + return 0; + else if (strstr (name, "[ITM]")) + return 1; + else if (strstr (name, "[IVA]")) + return 2; + else if (strstr (name, "[PTA]")) + return 8; + else if (strstr (name, "[GPTA]")) + return 9; + else if (strstr (name, "[IPSR]")) + return 16; + else if (strstr (name, "[ISR]")) + return 17; + else if (strstr (name, "[IIP]")) + return 19; + else if (strstr (name, "[IFA]")) + return 20; + else if (strstr (name, "[ITIR]")) + return 21; + else if (strstr (name, "[IIPA]")) + return 22; + else if (strstr (name, "[IFS]")) + return 23; + else if (strstr (name, "[IIM]")) + return 24; + else if (strstr (name, "[IHA]")) + return 25; + else if (strstr (name, "[LID]")) + return 64; + else if (strstr (name, "[IVR]")) + return 65; + else if (strstr (name, "[TPR]")) + return 66; + else if (strstr (name, "[EOI]")) + return 67; + else if (strstr (name, "[ITV]")) + return 72; + else if (strstr (name, "[PMV]")) + return 73; + else if (strstr (name, "[CMCV]")) + return 74; + abort (); + case IA64_RS_PSR: + if (strstr (name, ".be")) + return 1; + else if (strstr (name, ".up")) + return 2; + else if (strstr (name, ".ac")) + return 3; + else if (strstr (name, ".mfl")) + return 4; + else if (strstr (name, ".mfh")) + return 5; + else if (strstr (name, ".ic")) + return 13; + else if (strstr (name, ".i")) + return 14; + else if (strstr (name, ".pk")) + return 15; + else if (strstr (name, ".dt")) + return 17; + else if (strstr (name, ".dfl")) + return 18; + else if (strstr (name, ".dfh")) + return 19; + else if (strstr (name, ".sp")) + return 20; + else if (strstr (name, ".pp")) + return 21; + else if (strstr (name, ".di")) + return 22; + else if (strstr (name, ".si")) + return 23; + else if (strstr (name, ".db")) + return 24; + else if (strstr (name, ".lp")) + return 25; + else if (strstr (name, ".tb")) + return 26; + else if (strstr (name, ".rt")) + return 27; + else if (strstr (name, ".cpl")) + return 32; + else if (strstr (name, ".rs")) + return 34; + else if (strstr (name, ".mc")) + return 35; + else if (strstr (name, ".it")) + return 36; + else if (strstr (name, ".id")) + return 37; + else if (strstr (name, ".da")) + return 38; + else if (strstr (name, ".dd")) + return 39; + else if (strstr (name, ".ss")) + return 40; + else if (strstr (name, ".ri")) + return 41; + else if (strstr (name, ".ed")) + return 43; + else if (strstr (name, ".bn")) + return 44; + else if (strstr (name, ".ia")) + return 45; + else if (strstr (name, ".vm")) + return 46; + else + abort (); + default: + break; + } + return REG_NONE; +} + +static int +lookup_specifier (const char *name) +{ + if (strchr (name, '%')) + { + if (strstr (name, "AR[K%]") != NULL) + return IA64_RS_AR_K; + if (strstr (name, "AR[UNAT]") != NULL) + return IA64_RS_AR_UNAT; + if (strstr (name, "AR%, % in 8") != NULL) + return IA64_RS_AR; + if (strstr (name, "AR%, % in 48") != NULL) + return IA64_RS_ARb; + if (strstr (name, "BR%") != NULL) + return IA64_RS_BR; + if (strstr (name, "CR[IRR%]") != NULL) + return IA64_RS_CR_IRR; + if (strstr (name, "CR[LRR%]") != NULL) + return IA64_RS_CR_LRR; + if (strstr (name, "CR%") != NULL) + return IA64_RS_CR; + if (strstr (name, "FR%, % in 0") != NULL) + return IA64_RS_FR; + if (strstr (name, "FR%, % in 2") != NULL) + return IA64_RS_FRb; + if (strstr (name, "GR%") != NULL) + return IA64_RS_GR; + if (strstr (name, "PR%, % in 1 ") != NULL) + return IA64_RS_PR; + if (strstr (name, "PR%, % in 16 ") != NULL) + return IA64_RS_PRr; + + warn (_("don't know how to specify %% dependency %s\n"), + name); + } + else if (strchr (name, '#')) + { + if (strstr (name, "CPUID#") != NULL) + return IA64_RS_CPUID; + if (strstr (name, "DBR#") != NULL) + return IA64_RS_DBR; + if (strstr (name, "IBR#") != NULL) + return IA64_RS_IBR; + if (strstr (name, "MSR#") != NULL) + return IA64_RS_MSR; + if (strstr (name, "PKR#") != NULL) + return IA64_RS_PKR; + if (strstr (name, "PMC#") != NULL) + return IA64_RS_PMC; + if (strstr (name, "PMD#") != NULL) + return IA64_RS_PMD; + if (strstr (name, "RR#") != NULL) + return IA64_RS_RR; + + warn (_("Don't know how to specify # dependency %s\n"), + name); + } + else if (strncmp (name, "AR[FPSR]", 8) == 0) + return IA64_RS_AR_FPSR; + else if (strncmp (name, "AR[", 3) == 0) + return IA64_RS_ARX; + else if (strncmp (name, "CR[", 3) == 0) + return IA64_RS_CRX; + else if (strncmp (name, "PSR.", 4) == 0) + return IA64_RS_PSR; + else if (strcmp (name, "InService*") == 0) + return IA64_RS_INSERVICE; + else if (strcmp (name, "GR0") == 0) + return IA64_RS_GR0; + else if (strcmp (name, "CFM") == 0) + return IA64_RS_CFM; + else if (strcmp (name, "PR63") == 0) + return IA64_RS_PR63; + else if (strcmp (name, "RSE") == 0) + return IA64_RS_RSE; + + return IA64_RS_ANY; +} + +static void +print_dependency_table () +{ + int i, j; + + if (debug) + { + for (i=0;i < iclen;i++) + { + if (ics[i]->is_class) + { + if (!ics[i]->nsubs) + { + if (ics[i]->comment) + warn (_("IC:%s [%s] has no terminals or sub-classes\n"), + ics[i]->name, ics[i]->comment); + else + warn (_("IC:%s has no terminals or sub-classes\n"), + ics[i]->name); + } + } + else + { + if (!ics[i]->terminal_resolved && !ics[i]->orphan) + { + if (ics[i]->comment) + warn (_("no insns mapped directly to terminal IC %s [%s]"), + ics[i]->name, ics[i]->comment); + else + warn (_("no insns mapped directly to terminal IC %s\n"), + ics[i]->name); + } + } + } + + for (i = 0; i < iclen; i++) + { + if (ics[i]->orphan) + { + mark_used (ics[i], 1); + warn (_("class %s is defined but not used\n"), + ics[i]->name); + } + } + + if (debug > 1) + for (i = 0; i < rdepslen; i++) + { + static const char *mode_str[] = { "RAW", "WAW", "WAR" }; + + if (rdeps[i]->total_chks == 0) + warn (_("Warning: rsrc %s (%s) has no chks%s\n"), + rdeps[i]->name, mode_str[rdeps[i]->mode], + rdeps[i]->total_regs ? "" : " or regs"); + else if (rdeps[i]->total_regs == 0) + warn (_("rsrc %s (%s) has no regs\n"), + rdeps[i]->name, mode_str[rdeps[i]->mode]); + } + } + + /* The dependencies themselves. */ + printf ("static const struct ia64_dependency\ndependencies[] = {\n"); + for (i = 0; i < rdepslen; i++) + { + /* '%', '#', AR[], CR[], or PSR. indicates we need to specify the actual + resource used. */ + int specifier = lookup_specifier (rdeps[i]->name); + int regindex = lookup_regindex (rdeps[i]->name, specifier); + + printf (" { \"%s\", %d, %d, %d, %d, ", + rdeps[i]->name, specifier, + (int)rdeps[i]->mode, (int)rdeps[i]->semantics, regindex); + if (rdeps[i]->semantics == IA64_DVS_OTHER) + { + const char *quote, *rest; + + putchar ('\"'); + rest = rdeps[i]->extra; + quote = strchr (rest, '\"'); + while (quote != NULL) + { + printf ("%.*s\\\"", (int) (quote - rest), rest); + rest = quote + 1; + quote = strchr (rest, '\"'); + } + printf ("%s\", ", rest); + } + else + printf ("NULL, "); + printf("},\n"); + } + printf ("};\n\n"); + + /* And dependency lists. */ + for (i=0;i < dlistlen;i++) + { + int len = 2; + printf ("static const unsigned short dep%d[] = {\n ", i); + for (j=0;j < dlists[i]->len; j++) + { + len += printf ("%d, ", dlists[i]->deps[j]); + if (len > 75) + { + printf("\n "); + len = 2; + } + } + printf ("\n};\n\n"); + } + + /* And opcode dependency list. */ + printf ("#define NELS(X) (sizeof(X)/sizeof(X[0]))\n"); + printf ("static const struct ia64_opcode_dependency\n"); + printf ("op_dependencies[] = {\n"); + for (i = 0; i < opdeplen; i++) + { + printf (" { "); + if (opdeps[i]->chk == -1) + printf ("0, NULL, "); + else + printf ("NELS(dep%d), dep%d, ", opdeps[i]->chk, opdeps[i]->chk); + if (opdeps[i]->reg == -1) + printf ("0, NULL, "); + else + printf ("NELS(dep%d), dep%d, ", opdeps[i]->reg, opdeps[i]->reg); + printf ("},\n"); + } + printf ("};\n\n"); +} + + +/* Add STR to the string table. */ +static struct string_entry * +insert_string (char *str) +{ + int start = 0, end = strtablen; + int i, x; + + if (strtablen == strtabtotlen) + { + strtabtotlen += 20; + string_table = (struct string_entry **) + xrealloc (string_table, + sizeof (struct string_entry **) * strtabtotlen); + } + + if (strtablen == 0) + { + strtablen = 1; + string_table[0] = tmalloc (struct string_entry); + string_table[0]->s = xstrdup (str); + string_table[0]->num = 0; + return string_table[0]; + } + + if (strcmp (str, string_table[strtablen - 1]->s) > 0) + i = end; + else if (strcmp (str, string_table[0]->s) < 0) + i = 0; + else + { + while (1) + { + int c; + + i = (start + end) / 2; + c = strcmp (str, string_table[i]->s); + + if (c < 0) + end = i - 1; + else if (c == 0) + return string_table[i]; + else + start = i + 1; + + if (start > end) + break; + } + } + + for (; i > 0 && i < strtablen; i--) + if (strcmp (str, string_table[i - 1]->s) > 0) + break; + + for (; i < strtablen; i++) + if (strcmp (str, string_table[i]->s) < 0) + break; + + for (x = strtablen - 1; x >= i; x--) + { + string_table[x + 1] = string_table[x]; + string_table[x + 1]->num = x + 1; + } + + string_table[i] = tmalloc (struct string_entry); + string_table[i]->s = xstrdup (str); + string_table[i]->num = i; + strtablen++; + + return string_table[i]; +} + +static struct bittree * +make_bittree_entry (void) +{ + struct bittree *res = tmalloc (struct bittree); + + res->disent = NULL; + res->bits[0] = NULL; + res->bits[1] = NULL; + res->bits[2] = NULL; + res->skip_flag = 0; + res->bits_to_skip = 0; + return res; +} + + +static struct disent * +add_dis_table_ent (which, insn, order, completer_index) + struct disent *which; + int insn; + int order; + int completer_index; +{ + int ci = 0; + struct disent *ent; + + if (which != NULL) + { + ent = which; + + ent->nextcnt++; + while (ent->nexte != NULL) + ent = ent->nexte; + + ent = (ent->nexte = tmalloc (struct disent)); + } + else + { + ent = tmalloc (struct disent); + ent->next_ent = disinsntable; + disinsntable = ent; + which = ent; + } + ent->nextcnt = 0; + ent->nexte = NULL; + ent->insn = insn; + ent->priority = order; + + while (completer_index != 1) + { + ci = (ci << 1) | (completer_index & 1); + completer_index >>= 1; + } + ent->completer_index = ci; + return which; +} + +static void +finish_distable () +{ + struct disent *ent = disinsntable; + struct disent *prev = ent; + + ent->ournum = 32768; + while ((ent = ent->next_ent) != NULL) + { + ent->ournum = prev->ournum + prev->nextcnt + 1; + prev = ent; + } +} + +static void +insert_bit_table_ent (curr_ent, bit, opcode, mask, + opcodenum, order, completer_index) + struct bittree *curr_ent; + int bit; + ia64_insn opcode; + ia64_insn mask; + int opcodenum; + int order; + int completer_index; +{ + ia64_insn m; + int b; + struct bittree *next; + + if (bit == -1) + { + struct disent *nent = add_dis_table_ent (curr_ent->disent, + opcodenum, order, + completer_index); + curr_ent->disent = nent; + return; + } + + m = ((ia64_insn) 1) << bit; + + if (mask & m) + b = (opcode & m) ? 1 : 0; + else + b = 2; + + next = curr_ent->bits[b]; + if (next == NULL) + { + next = make_bittree_entry (); + curr_ent->bits[b] = next; + } + insert_bit_table_ent (next, bit - 1, opcode, mask, opcodenum, order, + completer_index); +} + +static void +add_dis_entry (first, opcode, mask, opcodenum, ent, completer_index) + struct bittree *first; + ia64_insn opcode; + ia64_insn mask; + int opcodenum; + struct completer_entry *ent; + int completer_index; +{ + if (completer_index & (1 << 20)) + abort (); + + while (ent != NULL) + { + ia64_insn newopcode = (opcode & (~ ent->mask)) | ent->bits; + add_dis_entry (first, newopcode, mask, opcodenum, ent->addl_entries, + (completer_index << 1) | 1); + + if (ent->is_terminal) + { + insert_bit_table_ent (bittree, 40, newopcode, mask, + opcodenum, opcode_count - ent->order - 1, + (completer_index << 1) | 1); + } + completer_index <<= 1; + ent = ent->alternative; + } +} + +/* This optimization pass combines multiple "don't care" nodes. */ +static void +compact_distree (ent) + struct bittree *ent; +{ +#define IS_SKIP(ent) \ + ((ent->bits[2] !=NULL) \ + && (ent->bits[0] == NULL && ent->bits[1] == NULL && ent->skip_flag == 0)) + + int bitcnt = 0; + struct bittree *nent = ent; + int x; + + while (IS_SKIP (nent)) + { + bitcnt++; + nent = nent->bits[2]; + } + + if (bitcnt) + { + struct bittree *next = ent->bits[2]; + + ent->bits[0] = nent->bits[0]; + ent->bits[1] = nent->bits[1]; + ent->bits[2] = nent->bits[2]; + ent->disent = nent->disent; + ent->skip_flag = 1; + ent->bits_to_skip = bitcnt; + while (next != nent) + { + struct bittree *b = next; + next = next->bits[2]; + free (b); + } + free (nent); + } + + for (x = 0; x < 3; x++) + { + struct bittree *i = ent->bits[x]; + + if (i != NULL) + compact_distree (i); + } +} + +static unsigned char *insn_list; +static int insn_list_len = 0; +static int tot_insn_list_len = 0; + +/* Generate the disassembler state machine corresponding to the tree + in ENT. */ +static void +gen_dis_table (ent) + struct bittree *ent; +{ + int x; + int our_offset = insn_list_len; + int bitsused = 5; + int totbits = bitsused; + int needed_bytes; + int zero_count = 0; + int zero_dest = 0; /* Initialize this with 0 to keep gcc quiet... */ + + /* If this is a terminal entry, there's no point in skipping any + bits. */ + if (ent->skip_flag && ent->bits[0] == NULL && ent->bits[1] == NULL && + ent->bits[2] == NULL) + { + if (ent->disent == NULL) + abort (); + else + ent->skip_flag = 0; + } + + /* Calculate the amount of space needed for this entry, or at least + a conservatively large approximation. */ + if (ent->skip_flag) + totbits += 5; + + for (x = 1; x < 3; x++) + if (ent->bits[x] != NULL) + totbits += 16; + + if (ent->disent != NULL) + { + if (ent->bits[2] != NULL) + abort (); + + totbits += 16; + } + + /* Now allocate the space. */ + needed_bytes = (totbits + 7) / 8; + if ((needed_bytes + insn_list_len) > tot_insn_list_len) + { + tot_insn_list_len += 256; + insn_list = (unsigned char *) xrealloc (insn_list, tot_insn_list_len); + } + our_offset = insn_list_len; + insn_list_len += needed_bytes; + memset (insn_list + our_offset, 0, needed_bytes); + + /* Encode the skip entry by setting bit 6 set in the state op field, + and store the # of bits to skip immediately after. */ + if (ent->skip_flag) + { + bitsused += 5; + insn_list[our_offset + 0] |= 0x40 | ((ent->bits_to_skip >> 2) & 0xf); + insn_list[our_offset + 1] |= ((ent->bits_to_skip & 3) << 6); + } + +#define IS_ONLY_IFZERO(ENT) \ + ((ENT)->bits[0] != NULL && (ENT)->bits[1] == NULL && (ENT)->bits[2] == NULL \ + && (ENT)->disent == NULL && (ENT)->skip_flag == 0) + + /* Store an "if (bit is zero)" instruction by setting bit 7 in the + state op field. */ + if (ent->bits[0] != NULL) + { + struct bittree *nent = ent->bits[0]; + zero_count = 0; + + insn_list[our_offset] |= 0x80; + + /* We can encode sequences of multiple "if (bit is zero)" tests + by storing the # of zero bits to check in the lower 3 bits of + the instruction. However, this only applies if the state + solely tests for a zero bit. */ + + if (IS_ONLY_IFZERO (ent)) + { + while (IS_ONLY_IFZERO (nent) && zero_count < 7) + { + nent = nent->bits[0]; + zero_count++; + } + + insn_list[our_offset + 0] |= zero_count; + } + zero_dest = insn_list_len; + gen_dis_table (nent); + } + + /* Now store the remaining tests. We also handle a sole "termination + entry" by storing it as an "any bit" test. */ + + for (x = 1; x < 3; x++) + { + if (ent->bits[x] != NULL || (x == 2 && ent->disent != NULL)) + { + struct bittree *i = ent->bits[x]; + int idest; + int currbits = 15; + + if (i != NULL) + { + /* If the instruction being branched to only consists of + a termination entry, use the termination entry as the + place to branch to instead. */ + if (i->bits[0] == NULL && i->bits[1] == NULL + && i->bits[2] == NULL && i->disent != NULL) + { + idest = i->disent->ournum; + i = NULL; + } + else + idest = insn_list_len - our_offset; + } + else + idest = ent->disent->ournum; + + /* If the destination offset for the if (bit is 1) test is less + than 256 bytes away, we can store it as 8-bits instead of 16; + the instruction has bit 5 set for the 16-bit address, and bit + 4 for the 8-bit address. Since we've already allocated 16 + bits for the address we need to deallocate the space. + + Note that branchings within the table are relative, and + there are no branches that branch past our instruction yet + so we do not need to adjust any other offsets. */ + if (x == 1) + { + if (idest <= 256) + { + int start = our_offset + bitsused / 8 + 1; + + memmove (insn_list + start, + insn_list + start + 1, + insn_list_len - (start + 1)); + currbits = 7; + totbits -= 8; + needed_bytes--; + insn_list_len--; + insn_list[our_offset] |= 0x10; + idest--; + } + else + insn_list[our_offset] |= 0x20; + } + else + { + /* An instruction which solely consists of a termination + marker and whose disassembly name index is < 4096 + can be stored in 16 bits. The encoding is slightly + odd; the upper 4 bits of the instruction are 0x3, and + bit 3 loses its normal meaning. */ + + if (ent->bits[0] == NULL && ent->bits[1] == NULL + && ent->bits[2] == NULL && ent->skip_flag == 0 + && ent->disent != NULL + && ent->disent->ournum < (32768 + 4096)) + { + int start = our_offset + bitsused / 8 + 1; + + memmove (insn_list + start, + insn_list + start + 1, + insn_list_len - (start + 1)); + currbits = 11; + totbits -= 5; + bitsused--; + needed_bytes--; + insn_list_len--; + insn_list[our_offset] |= 0x30; + idest &= ~32768; + } + else + insn_list[our_offset] |= 0x08; + } + + if (debug) + { + int id = idest; + + if (i == NULL) + id |= 32768; + else if (! (id & 32768)) + id += our_offset; + + if (x == 1) + printf ("%d: if (1) goto %d\n", our_offset, id); + else + printf ("%d: try %d\n", our_offset, id); + } + + /* Store the address of the entry being branched to. */ + while (currbits >= 0) + { + unsigned char *byte = insn_list + our_offset + bitsused / 8; + + if (idest & (1 << currbits)) + *byte |= (1 << (7 - (bitsused % 8))); + + bitsused++; + currbits--; + } + + /* Now generate the states for the entry being branched to. */ + if (i != NULL) + gen_dis_table (i); + } + } + + if (debug) + { + if (ent->skip_flag) + printf ("%d: skipping %d\n", our_offset, ent->bits_to_skip); + + if (ent->bits[0] != NULL) + printf ("%d: if (0:%d) goto %d\n", our_offset, zero_count + 1, + zero_dest); + } + + if (bitsused != totbits) + abort (); +} + +static void +print_dis_table (void) +{ + int x; + struct disent *cent = disinsntable; + + printf ("static const char dis_table[] = {\n"); + for (x = 0; x < insn_list_len; x++) + { + if ((x > 0) && ((x % 12) == 0)) + printf ("\n"); + + printf ("0x%02x, ", insn_list[x]); + } + printf ("\n};\n\n"); + + printf ("static const struct ia64_dis_names ia64_dis_names[] = {\n"); + while (cent != NULL) + { + struct disent *ent = cent; + + while (ent != NULL) + { + printf ("{ 0x%x, %d, %d, %d },\n", ent->completer_index, + ent->insn, (ent->nexte != NULL ? 1 : 0), + ent->priority); + ent = ent->nexte; + } + cent = cent->next_ent; + } + printf ("};\n\n"); +} + +static void +generate_disassembler (void) +{ + int i; + + bittree = make_bittree_entry (); + + for (i = 0; i < otlen; i++) + { + struct main_entry *ptr = ordered_table[i]; + + if (ptr->opcode->type != IA64_TYPE_DYN) + add_dis_entry (bittree, + ptr->opcode->opcode, ptr->opcode->mask, + ptr->main_index, + ptr->completers, 1); + } + + compact_distree (bittree); + finish_distable (); + gen_dis_table (bittree); + + print_dis_table (); +} + +static void +print_string_table (void) +{ + int x; + char lbuf[80], buf[80]; + int blen = 0; + + printf ("static const char * const ia64_strings[] = {\n"); + lbuf[0] = '\0'; + + for (x = 0; x < strtablen; x++) + { + int len; + + if (strlen (string_table[x]->s) > 75) + abort (); + + sprintf (buf, " \"%s\",", string_table[x]->s); + len = strlen (buf); + + if ((blen + len) > 75) + { + printf (" %s\n", lbuf); + lbuf[0] = '\0'; + blen = 0; + } + strcat (lbuf, buf); + blen += len; + } + + if (blen > 0) + printf (" %s\n", lbuf); + + printf ("};\n\n"); +} + +static struct completer_entry **glist; +static int glistlen = 0; +static int glisttotlen = 0; + +/* If the completer trees ENT1 and ENT2 are equal, return 1. */ + +static int +completer_entries_eq (ent1, ent2) + struct completer_entry *ent1, *ent2; +{ + while (ent1 != NULL && ent2 != NULL) + { + if (ent1->name->num != ent2->name->num + || ent1->bits != ent2->bits + || ent1->mask != ent2->mask + || ent1->is_terminal != ent2->is_terminal + || ent1->dependencies != ent2->dependencies + || ent1->order != ent2->order) + return 0; + + if (! completer_entries_eq (ent1->addl_entries, ent2->addl_entries)) + return 0; + + ent1 = ent1->alternative; + ent2 = ent2->alternative; + } + + return ent1 == ent2; +} + +/* Insert ENT into the global list of completers and return it. If an + equivalent entry (according to completer_entries_eq) already exists, + it is returned instead. */ +static struct completer_entry * +insert_gclist (struct completer_entry *ent) +{ + if (ent != NULL) + { + int i; + int x; + int start = 0, end; + + ent->addl_entries = insert_gclist (ent->addl_entries); + ent->alternative = insert_gclist (ent->alternative); + + i = glistlen / 2; + end = glistlen; + + if (glisttotlen == glistlen) + { + glisttotlen += 20; + glist = (struct completer_entry **) + xrealloc (glist, sizeof (struct completer_entry *) * glisttotlen); + } + + if (glistlen == 0) + { + glist[0] = ent; + glistlen = 1; + return ent; + } + + if (ent->name->num < glist[0]->name->num) + i = 0; + else if (ent->name->num > glist[end - 1]->name->num) + i = end; + else + { + int c; + + while (1) + { + i = (start + end) / 2; + c = ent->name->num - glist[i]->name->num; + + if (c < 0) + end = i - 1; + else if (c == 0) + { + while (i > 0 + && ent->name->num == glist[i - 1]->name->num) + i--; + + break; + } + else + start = i + 1; + + if (start > end) + break; + } + + if (c == 0) + { + while (i < glistlen) + { + if (ent->name->num != glist[i]->name->num) + break; + + if (completer_entries_eq (ent, glist[i])) + return glist[i]; + + i++; + } + } + } + + for (; i > 0 && i < glistlen; i--) + if (ent->name->num >= glist[i - 1]->name->num) + break; + + for (; i < glistlen; i++) + if (ent->name->num < glist[i]->name->num) + break; + + for (x = glistlen - 1; x >= i; x--) + glist[x + 1] = glist[x]; + + glist[i] = ent; + glistlen++; + } + return ent; +} + +static int +get_prefix_len (name) + const char *name; +{ + char *c; + + if (name[0] == '\0') + return 0; + + c = strchr (name, '.'); + if (c != NULL) + return c - name; + else + return strlen (name); +} + +static void +compute_completer_bits (ment, ent) + struct main_entry *ment; + struct completer_entry *ent; +{ + while (ent != NULL) + { + compute_completer_bits (ment, ent->addl_entries); + + if (ent->is_terminal) + { + ia64_insn mask = 0; + ia64_insn our_bits = ent->bits; + struct completer_entry *p = ent->parent; + ia64_insn p_bits; + int x; + + while (p != NULL && ! p->is_terminal) + p = p->parent; + + if (p != NULL) + p_bits = p->bits; + else + p_bits = ment->opcode->opcode; + + for (x = 0; x < 64; x++) + { + ia64_insn m = ((ia64_insn) 1) << x; + + if ((p_bits & m) != (our_bits & m)) + mask |= m; + else + our_bits &= ~m; + } + ent->bits = our_bits; + ent->mask = mask; + } + else + { + ent->bits = 0; + ent->mask = 0; + } + + ent = ent->alternative; + } +} + +/* Find identical completer trees that are used in different + instructions and collapse their entries. */ +static void +collapse_redundant_completers (void) +{ + struct main_entry *ptr; + int x; + + for (ptr = maintable; ptr != NULL; ptr = ptr->next) + { + if (ptr->completers == NULL) + abort (); + + compute_completer_bits (ptr, ptr->completers); + ptr->completers = insert_gclist (ptr->completers); + } + + /* The table has been finalized, now number the indexes. */ + for (x = 0; x < glistlen; x++) + glist[x]->num = x; +} + + +/* Attach two lists of dependencies to each opcode. + 1) all resources which, when already marked in use, conflict with this + opcode (chks) + 2) all resources which must be marked in use when this opcode is used + (regs). */ +static int +insert_opcode_dependencies (opc, cmp) + struct ia64_opcode *opc; + struct completer_entry *cmp ATTRIBUTE_UNUSED; +{ + /* Note all resources which point to this opcode. rfi has the most chks + (79) and cmpxchng has the most regs (54) so 100 here should be enough. */ + int i; + int nregs = 0; + unsigned short regs[256]; + int nchks = 0; + unsigned short chks[256]; + /* Flag insns for which no class matched; there should be none. */ + int no_class_found = 1; + + for (i = 0; i < rdepslen; i++) + { + struct rdep *rs = rdeps[i]; + int j; + + if (strcmp (opc->name, "cmp.eq.and") == 0 + && strncmp (rs->name, "PR%", 3) == 0 + && rs->mode == 1) + no_class_found = 99; + + for (j=0; j < rs->nregs;j++) + { + int ic_note = 0; + + if (in_iclass (opc, ics[rs->regs[j]], NULL, NULL, &ic_note)) + { + /* We can ignore ic_note 11 for non PR resources. */ + if (ic_note == 11 && strncmp (rs->name, "PR", 2) != 0) + ic_note = 0; + + if (ic_note != 0 && rs->regnotes[j] != 0 + && ic_note != rs->regnotes[j] + && !(ic_note == 11 && rs->regnotes[j] == 1)) + warn (_("IC note %d in opcode %s (IC:%s) conflicts with resource %s note %d\n"), + ic_note, opc->name, ics[rs->regs[j]]->name, + rs->name, rs->regnotes[j]); + /* Instruction class notes override resource notes. + So far, only note 11 applies to an IC instead of a resource, + and note 11 implies note 1. */ + if (ic_note) + regs[nregs++] = RDEP(ic_note, i); + else + regs[nregs++] = RDEP(rs->regnotes[j], i); + no_class_found = 0; + ++rs->total_regs; + } + } + + for (j = 0; j < rs->nchks; j++) + { + int ic_note = 0; + + if (in_iclass (opc, ics[rs->chks[j]], NULL, NULL, &ic_note)) + { + /* We can ignore ic_note 11 for non PR resources. */ + if (ic_note == 11 && strncmp (rs->name, "PR", 2) != 0) + ic_note = 0; + + if (ic_note != 0 && rs->chknotes[j] != 0 + && ic_note != rs->chknotes[j] + && !(ic_note == 11 && rs->chknotes[j] == 1)) + warn (_("IC note %d for opcode %s (IC:%s) conflicts with resource %s note %d\n"), + ic_note, opc->name, ics[rs->chks[j]]->name, + rs->name, rs->chknotes[j]); + if (ic_note) + chks[nchks++] = RDEP(ic_note, i); + else + chks[nchks++] = RDEP(rs->chknotes[j], i); + no_class_found = 0; + ++rs->total_chks; + } + } + } + + if (no_class_found) + warn (_("opcode %s has no class (ops %d %d %d)\n"), + opc->name, + opc->operands[0], opc->operands[1], opc->operands[2]); + + return insert_dependencies (nchks, chks, nregs, regs); +} + +static void +insert_completer_entry (opc, tabent, order) + struct ia64_opcode *opc; + struct main_entry *tabent; + int order; +{ + struct completer_entry **ptr = &tabent->completers; + struct completer_entry *parent = NULL; + char pcopy[129], *prefix; + int at_end = 0; + + if (strlen (opc->name) > 128) + abort (); + + strcpy (pcopy, opc->name); + prefix = pcopy + get_prefix_len (pcopy); + + if (prefix[0] != '\0') + prefix++; + + while (! at_end) + { + int need_new_ent = 1; + int plen = get_prefix_len (prefix); + struct string_entry *sent; + + at_end = (prefix[plen] == '\0'); + prefix[plen] = '\0'; + sent = insert_string (prefix); + + while (*ptr != NULL) + { + int cmpres = sent->num - (*ptr)->name->num; + + if (cmpres == 0) + { + need_new_ent = 0; + break; + } + else + ptr = &((*ptr)->alternative); + } + + if (need_new_ent) + { + struct completer_entry *nent = tmalloc (struct completer_entry); + + nent->name = sent; + nent->parent = parent; + nent->addl_entries = NULL; + nent->alternative = *ptr; + *ptr = nent; + nent->is_terminal = 0; + nent->dependencies = -1; + } + + if (! at_end) + { + parent = *ptr; + ptr = &((*ptr)->addl_entries); + prefix += plen + 1; + } + } + + if ((*ptr)->is_terminal) + abort (); + + (*ptr)->is_terminal = 1; + (*ptr)->mask = (ia64_insn)-1; + (*ptr)->bits = opc->opcode; + (*ptr)->dependencies = insert_opcode_dependencies (opc, *ptr); + (*ptr)->order = order; +} + +static void +print_completer_entry (ent) + struct completer_entry *ent; +{ + int moffset = 0; + ia64_insn mask = ent->mask, bits = ent->bits; + + if (mask != 0) + { + while (! (mask & 1)) + { + moffset++; + mask = mask >> 1; + bits = bits >> 1; + } + + if (bits & 0xffffffff00000000LL) + abort (); + } + + printf (" { 0x%x, 0x%x, %d, %d, %d, %d, %d, %d },\n", + (int)bits, + (int)mask, + ent->name->num, + ent->alternative != NULL ? ent->alternative->num : -1, + ent->addl_entries != NULL ? ent->addl_entries->num : -1, + moffset, + ent->is_terminal ? 1 : 0, + ent->dependencies); +} + +static void +print_completer_table () +{ + int x; + + printf ("static const struct ia64_completer_table\ncompleter_table[] = {\n"); + for (x = 0; x < glistlen; x++) + print_completer_entry (glist[x]); + printf ("};\n\n"); +} + +static int +opcodes_eq (opc1, opc2) + struct ia64_opcode *opc1; + struct ia64_opcode *opc2; +{ + int x; + int plen1, plen2; + + if ((opc1->mask != opc2->mask) || (opc1->type != opc2->type) + || (opc1->num_outputs != opc2->num_outputs) + || (opc1->flags != opc2->flags)) + return 0; + + for (x = 0; x < 5; x++) + if (opc1->operands[x] != opc2->operands[x]) + return 0; + + plen1 = get_prefix_len (opc1->name); + plen2 = get_prefix_len (opc2->name); + + if (plen1 == plen2 && (memcmp (opc1->name, opc2->name, plen1) == 0)) + return 1; + + return 0; +} + +static void +add_opcode_entry (opc) + struct ia64_opcode *opc; +{ + struct main_entry **place; + struct string_entry *name; + char prefix[129]; + int found_it = 0; + + if (strlen (opc->name) > 128) + abort (); + + place = &maintable; + strcpy (prefix, opc->name); + prefix[get_prefix_len (prefix)] = '\0'; + name = insert_string (prefix); + + /* Walk the list of opcode table entries. If it's a new + instruction, allocate and fill in a new entry. Note + the main table is alphabetical by opcode name. */ + + while (*place != NULL) + { + if ((*place)->name->num == name->num + && opcodes_eq ((*place)->opcode, opc)) + { + found_it = 1; + break; + } + if ((*place)->name->num > name->num) + break; + + place = &((*place)->next); + } + if (! found_it) + { + struct main_entry *nent = tmalloc (struct main_entry); + + nent->name = name; + nent->opcode = opc; + nent->next = *place; + nent->completers = 0; + *place = nent; + + if (otlen == ottotlen) + { + ottotlen += 20; + ordered_table = (struct main_entry **) + xrealloc (ordered_table, sizeof (struct main_entry *) * ottotlen); + } + ordered_table[otlen++] = nent; + } + + insert_completer_entry (opc, *place, opcode_count++); +} + +static void +print_main_table (void) +{ + struct main_entry *ptr = maintable; + int index = 0; + + printf ("static const struct ia64_main_table\nmain_table[] = {\n"); + while (ptr != NULL) + { + printf (" { %d, %d, %d, 0x", + ptr->name->num, + ptr->opcode->type, + ptr->opcode->num_outputs); + opcode_fprintf_vma (stdout, ptr->opcode->opcode); + printf ("ull, 0x"); + opcode_fprintf_vma (stdout, ptr->opcode->mask); + printf ("ull, { %d, %d, %d, %d, %d }, 0x%x, %d, },\n", + ptr->opcode->operands[0], + ptr->opcode->operands[1], + ptr->opcode->operands[2], + ptr->opcode->operands[3], + ptr->opcode->operands[4], + ptr->opcode->flags, + ptr->completers->num); + + ptr->main_index = index++; + + ptr = ptr->next; + } + printf ("};\n\n"); +} + +static void +shrink (table) + struct ia64_opcode *table; +{ + int curr_opcode; + + for (curr_opcode = 0; table[curr_opcode].name != NULL; curr_opcode++) + { + add_opcode_entry (table + curr_opcode); + if (table[curr_opcode].num_outputs == 2 + && ((table[curr_opcode].operands[0] == IA64_OPND_P1 + && table[curr_opcode].operands[1] == IA64_OPND_P2) + || (table[curr_opcode].operands[0] == IA64_OPND_P2 + && table[curr_opcode].operands[1] == IA64_OPND_P1))) + { + struct ia64_opcode *alias = tmalloc(struct ia64_opcode); + unsigned i; + + *alias = table[curr_opcode]; + for (i = 2; i < NELEMS (alias->operands); ++i) + alias->operands[i - 1] = alias->operands[i]; + alias->operands[NELEMS (alias->operands) - 1] = IA64_OPND_NIL; + --alias->num_outputs; + alias->flags |= PSEUDO; + add_opcode_entry (alias); + } + } +} + + +/* Program options. */ +#define OPTION_SRCDIR 200 + +struct option long_options[] = +{ + {"srcdir", required_argument, NULL, OPTION_SRCDIR}, + {"debug", no_argument, NULL, 'd'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {0, no_argument, NULL, 0} +}; + +static void +print_version (void) +{ + printf ("%s: version 1.0\n", program_name); + xexit (0); +} + +static void +usage (FILE * stream, int status) +{ + fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n", + program_name); + xexit (status); +} + +int +main (int argc, char **argv) +{ + extern int chdir (char *); + char *srcdir = NULL; + int c; + + program_name = *argv; + xmalloc_set_program_name (program_name); + + while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF) + switch (c) + { + case OPTION_SRCDIR: + srcdir = optarg; + break; + case 'V': + case 'v': + print_version (); + break; + case 'd': + debug = 1; + break; + case 'h': + case '?': + usage (stderr, 0); + default: + case 0: + break; + } + + if (optind != argc) + usage (stdout, 1); + + if (srcdir != NULL) + if (chdir (srcdir) != 0) + fail (_("unable to change directory to \"%s\", errno = %s\n"), + srcdir, strerror (errno)); + + load_insn_classes (); + load_dependencies (); + + shrink (ia64_opcodes_a); + shrink (ia64_opcodes_b); + shrink (ia64_opcodes_f); + shrink (ia64_opcodes_i); + shrink (ia64_opcodes_m); + shrink (ia64_opcodes_x); + shrink (ia64_opcodes_d); + + collapse_redundant_completers (); + + printf ("/* This file is automatically generated by ia64-gen. Do not edit! */\n"); + print_string_table (); + print_dependency_table (); + print_completer_table (); + print_main_table (); + + generate_disassembler (); + + exit (0); +} diff --git a/tools/debugger/xenitp/ia64-opc-a.c b/tools/debugger/xenitp/ia64-opc-a.c new file mode 100644 index 0000000..e865094 --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-a.c @@ -0,0 +1,419 @@ +/* ia64-opc-a.c -- IA-64 `A' opcode table. + Copyright 1998, 1999, 2000, 2001, 2002, 2004 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-opc.h" + +#define A IA64_TYPE_A, 1 +#define A2 IA64_TYPE_A, 2 + +/* instruction bit fields: */ +#define bC(x) (((ia64_insn) ((x) & 0x1)) << 12) +#define bImm14(x) ((((ia64_insn) (((x) >> 0) & 0x7f)) << 13) | \ + (((ia64_insn) (((x) >> 7) & 0x3f)) << 27) | \ + (((ia64_insn) (((x) >> 13) & 0x01)) << 36)) +#define bR3a(x) (((ia64_insn) ((x) & 0x7f)) << 20) +#define bR3b(x) (((ia64_insn) ((x) & 0x3)) << 20) +#define bTa(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bTb(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bVe(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bX(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bX2(x) (((ia64_insn) ((x) & 0x3)) << 34) +#define bX2a(x) (((ia64_insn) ((x) & 0x3)) << 34) +#define bX2b(x) (((ia64_insn) ((x) & 0x3)) << 27) +#define bX4(x) (((ia64_insn) ((x) & 0xf)) << 29) +#define bZa(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bZb(x) (((ia64_insn) ((x) & 0x1)) << 33) + +/* instruction bit masks: */ +#define mC bC (-1) +#define mImm14 bImm14 (-1) +#define mR3a bR3a (-1) +#define mR3b bR3b (-1) +#define mTa bTa (-1) +#define mTb bTb (-1) +#define mVe bVe (-1) +#define mX bX (-1) +#define mX2 bX2 (-1) +#define mX2a bX2a (-1) +#define mX2b bX2b (-1) +#define mX4 bX4 (-1) +#define mZa bZa (-1) +#define mZb bZb (-1) + +#define OpR3b(a,b) (bOp (a) | bR3b (b)), (mOp | mR3b) +#define OpX2aVe(a,b,c) (bOp (a) | bX2a (b) | bVe (c)), \ + (mOp | mX2a | mVe) +#define OpX2aVeR3a(a,b,c,d) (bOp (a) | bX2a (b) | bVe (c) | bR3a (d)), \ + (mOp | mX2a | mVe | mR3a) +#define OpX2aVeImm14(a,b,c,d) (bOp (a) | bX2a (b) | bVe (c) | bImm14 (d)), \ + (mOp | mX2a | mVe | mImm14) +#define OpX2aVeX4(a,b,c,d) (bOp (a) | bX2a (b) | bVe (c) | bX4 (d)), \ + (mOp | mX2a | mVe | mX4) +#define OpX2aVeX4X2b(a,b,c,d,e) \ + (bOp (a) | bX2a (b) | bVe (c) | bX4 (d) | bX2b (e)), \ + (mOp | mX2a | mVe | mX4 | mX2b) +#define OpX2TbTaC(a,b,c,d,e) \ + (bOp (a) | bX2 (b) | bTb (c) | bTa (d) | bC (e)), \ + (mOp | mX2 | mTb | mTa | mC) +#define OpX2TaC(a,b,c,d) (bOp (a) | bX2 (b) | bTa (c) | bC (d)), \ + (mOp | mX2 | mTa | mC) +#define OpX2aZaZbX4(a,b,c,d,e) \ + (bOp (a) | bX2a (b) | bZa (c) | bZb (d) | bX4 (e)), \ + (mOp | mX2a | mZa | mZb | mX4) +#define OpX2aZaZbX4X2b(a,b,c,d,e,f) \ + (bOp (a) | bX2a (b) | bZa (c) | bZb (d) | bX4 (e) | bX2b (f)), \ + (mOp | mX2a | mZa | mZb | mX4 | mX2b) + +/* Used to initialise unused fields in ia64_opcode struct, + in order to stop gcc from complaining. */ +#define EMPTY 0,0,NULL + +struct ia64_opcode ia64_opcodes_a[] = + { + /* A-type instruction encodings (sorted according to major opcode). */ + + {"add", A, OpX2aVeX4X2b (8, 0, 0, 0, 0), {R1, R2, R3}, EMPTY}, + {"add", A, OpX2aVeX4X2b (8, 0, 0, 0, 1), {R1, R2, R3, C1}, EMPTY}, + {"sub", A, OpX2aVeX4X2b (8, 0, 0, 1, 1), {R1, R2, R3}, EMPTY}, + {"sub", A, OpX2aVeX4X2b (8, 0, 0, 1, 0), {R1, R2, R3, C1}, EMPTY}, + {"addp4", A, OpX2aVeX4X2b (8, 0, 0, 2, 0), {R1, R2, R3}, EMPTY}, + {"and", A, OpX2aVeX4X2b (8, 0, 0, 3, 0), {R1, R2, R3}, EMPTY}, + {"andcm", A, OpX2aVeX4X2b (8, 0, 0, 3, 1), {R1, R2, R3}, EMPTY}, + {"or", A, OpX2aVeX4X2b (8, 0, 0, 3, 2), {R1, R2, R3}, EMPTY}, + {"xor", A, OpX2aVeX4X2b (8, 0, 0, 3, 3), {R1, R2, R3}, EMPTY}, + {"shladd", A, OpX2aVeX4 (8, 0, 0, 4), {R1, R2, CNT2a, R3}, EMPTY}, + {"shladdp4", A, OpX2aVeX4 (8, 0, 0, 6), {R1, R2, CNT2a, R3}, EMPTY}, + {"sub", A, OpX2aVeX4X2b (8, 0, 0, 9, 1), {R1, IMM8, R3}, EMPTY}, + {"and", A, OpX2aVeX4X2b (8, 0, 0, 0xb, 0), {R1, IMM8, R3}, EMPTY}, + {"andcm", A, OpX2aVeX4X2b (8, 0, 0, 0xb, 1), {R1, IMM8, R3}, EMPTY}, + {"or", A, OpX2aVeX4X2b (8, 0, 0, 0xb, 2), {R1, IMM8, R3}, EMPTY}, + {"xor", A, OpX2aVeX4X2b (8, 0, 0, 0xb, 3), {R1, IMM8, R3}, EMPTY}, + {"mov", A, OpX2aVeImm14 (8, 2, 0, 0), {R1, R3}, EMPTY}, + /* A mov immediate pseudo for adds was deleted. It failed for immediate + operands requiring relocs, e.g. @pltoff(a). */ + {"adds", A, OpX2aVe (8, 2, 0), {R1, IMM14, R3}, EMPTY}, + {"addp4", A, OpX2aVe (8, 3, 0), {R1, IMM14, R3}, EMPTY}, + {"padd1", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 0, 0), {R1, R2, R3}, EMPTY}, + {"padd2", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 0, 0), {R1, R2, R3}, EMPTY}, + {"padd4", A, OpX2aZaZbX4X2b (8, 1, 1, 0, 0, 0), {R1, R2, R3}, EMPTY}, + {"padd1.sss", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 0, 1), {R1, R2, R3}, EMPTY}, + {"padd2.sss", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 0, 1), {R1, R2, R3}, EMPTY}, + {"padd1.uuu", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 0, 2), {R1, R2, R3}, EMPTY}, + {"padd2.uuu", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 0, 2), {R1, R2, R3}, EMPTY}, + {"padd1.uus", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 0, 3), {R1, R2, R3}, EMPTY}, + {"padd2.uus", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 0, 3), {R1, R2, R3}, EMPTY}, + {"psub1", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 1, 0), {R1, R2, R3}, EMPTY}, + {"psub2", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 1, 0), {R1, R2, R3}, EMPTY}, + {"psub4", A, OpX2aZaZbX4X2b (8, 1, 1, 0, 1, 0), {R1, R2, R3}, EMPTY}, + {"psub1.sss", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 1, 1), {R1, R2, R3}, EMPTY}, + {"psub2.sss", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 1, 1), {R1, R2, R3}, EMPTY}, + {"psub1.uuu", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 1, 2), {R1, R2, R3}, EMPTY}, + {"psub2.uuu", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 1, 2), {R1, R2, R3}, EMPTY}, + {"psub1.uus", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 1, 3), {R1, R2, R3}, EMPTY}, + {"psub2.uus", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 1, 3), {R1, R2, R3}, EMPTY}, + {"pavg1", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 2, 2), {R1, R2, R3}, EMPTY}, + {"pavg2", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 2, 2), {R1, R2, R3}, EMPTY}, + {"pavg1.raz", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 2, 3), {R1, R2, R3}, EMPTY}, + {"pavg2.raz", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 2, 3), {R1, R2, R3}, EMPTY}, + {"pavgsub1", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 3, 2), {R1, R2, R3}, EMPTY}, + {"pavgsub2", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 3, 2), {R1, R2, R3}, EMPTY}, + {"pcmp1.eq", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 9, 0), {R1, R2, R3}, EMPTY}, + {"pcmp2.eq", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 9, 0), {R1, R2, R3}, EMPTY}, + {"pcmp4.eq", A, OpX2aZaZbX4X2b (8, 1, 1, 0, 9, 0), {R1, R2, R3}, EMPTY}, + {"pcmp1.gt", A, OpX2aZaZbX4X2b (8, 1, 0, 0, 9, 1), {R1, R2, R3}, EMPTY}, + {"pcmp2.gt", A, OpX2aZaZbX4X2b (8, 1, 0, 1, 9, 1), {R1, R2, R3}, EMPTY}, + {"pcmp4.gt", A, OpX2aZaZbX4X2b (8, 1, 1, 0, 9, 1), {R1, R2, R3}, EMPTY}, + {"pshladd2", A, OpX2aZaZbX4 (8, 1, 0, 1, 4), {R1, R2, CNT2b, R3}, EMPTY}, + {"pshradd2", A, OpX2aZaZbX4 (8, 1, 0, 1, 6), {R1, R2, CNT2b, R3}, EMPTY}, + + {"mov", A, OpR3b (9, 0), {R1, IMM22}, PSEUDO, 0, NULL}, + {"addl", A, Op (9), {R1, IMM22, R3_2}, EMPTY}, + + {"cmp.lt", A2, OpX2TbTaC (0xc, 0, 0, 0, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp.le", A2, OpX2TbTaC (0xc, 0, 0, 0, 0), {P2, P1, R3, R2}, EMPTY}, + {"cmp.gt", A2, OpX2TbTaC (0xc, 0, 0, 0, 0), {P1, P2, R3, R2}, EMPTY}, + {"cmp.ge", A2, OpX2TbTaC (0xc, 0, 0, 0, 0), {P2, P1, R2, R3}, EMPTY}, + {"cmp.lt.unc", A2, OpX2TbTaC (0xc, 0, 0, 0, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp.le.unc", A2, OpX2TbTaC (0xc, 0, 0, 0, 1), {P2, P1, R3, R2}, EMPTY}, + {"cmp.gt.unc", A2, OpX2TbTaC (0xc, 0, 0, 0, 1), {P1, P2, R3, R2}, EMPTY}, + {"cmp.ge.unc", A2, OpX2TbTaC (0xc, 0, 0, 0, 1), {P2, P1, R2, R3}, EMPTY}, + {"cmp.eq.and", A2, OpX2TbTaC (0xc, 0, 0, 1, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp.ne.andcm", A2, OpX2TbTaC (0xc, 0, 0, 1, 0), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp.ne.and", A2, OpX2TbTaC (0xc, 0, 0, 1, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp.eq.andcm", A2, OpX2TbTaC (0xc, 0, 0, 1, 1), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp4.lt", A2, OpX2TbTaC (0xc, 1, 0, 0, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.le", A2, OpX2TbTaC (0xc, 1, 0, 0, 0), {P2, P1, R3, R2}, EMPTY}, + {"cmp4.gt", A2, OpX2TbTaC (0xc, 1, 0, 0, 0), {P1, P2, R3, R2}, EMPTY}, + {"cmp4.ge", A2, OpX2TbTaC (0xc, 1, 0, 0, 0), {P2, P1, R2, R3}, EMPTY}, + {"cmp4.lt.unc", A2, OpX2TbTaC (0xc, 1, 0, 0, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.le.unc", A2, OpX2TbTaC (0xc, 1, 0, 0, 1), {P2, P1, R3, R2}, EMPTY}, + {"cmp4.gt.unc", A2, OpX2TbTaC (0xc, 1, 0, 0, 1), {P1, P2, R3, R2}, EMPTY}, + {"cmp4.ge.unc", A2, OpX2TbTaC (0xc, 1, 0, 0, 1), {P2, P1, R2, R3}, EMPTY}, + {"cmp4.eq.and", A2, OpX2TbTaC (0xc, 1, 0, 1, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.ne.andcm", A2, OpX2TbTaC (0xc, 1, 0, 1, 0), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp4.ne.and", A2, OpX2TbTaC (0xc, 1, 0, 1, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.eq.andcm", A2, OpX2TbTaC (0xc, 1, 0, 1, 1), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp.gt.and", A2, OpX2TbTaC (0xc, 0, 1, 0, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.lt.and", A2, OpX2TbTaC (0xc, 0, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.le.andcm", A2, OpX2TbTaC (0xc, 0, 1, 0, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.ge.andcm", A2, OpX2TbTaC (0xc, 0, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.le.and", A2, OpX2TbTaC (0xc, 0, 1, 0, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.ge.and", A2, OpX2TbTaC (0xc, 0, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.gt.andcm", A2, OpX2TbTaC (0xc, 0, 1, 0, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.lt.andcm", A2, OpX2TbTaC (0xc, 0, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ge.and", A2, OpX2TbTaC (0xc, 0, 1, 1, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.le.and", A2, OpX2TbTaC (0xc, 0, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt.andcm", A2, OpX2TbTaC (0xc, 0, 1, 1, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.gt.andcm", A2, OpX2TbTaC (0xc, 0, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt.and", A2, OpX2TbTaC (0xc, 0, 1, 1, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.gt.and", A2, OpX2TbTaC (0xc, 0, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ge.andcm", A2, OpX2TbTaC (0xc, 0, 1, 1, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.le.andcm", A2, OpX2TbTaC (0xc, 0, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.gt.and", A2, OpX2TbTaC (0xc, 1, 1, 0, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.lt.and", A2, OpX2TbTaC (0xc, 1, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.le.andcm", A2, OpX2TbTaC (0xc, 1, 1, 0, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.ge.andcm", A2, OpX2TbTaC (0xc, 1, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.le.and", A2, OpX2TbTaC (0xc, 1, 1, 0, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.ge.and", A2, OpX2TbTaC (0xc, 1, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.gt.andcm", A2, OpX2TbTaC (0xc, 1, 1, 0, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.lt.andcm", A2, OpX2TbTaC (0xc, 1, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.ge.and", A2, OpX2TbTaC (0xc, 1, 1, 1, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.le.and", A2, OpX2TbTaC (0xc, 1, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.lt.andcm", A2, OpX2TbTaC (0xc, 1, 1, 1, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.gt.andcm", A2, OpX2TbTaC (0xc, 1, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.lt.and", A2, OpX2TbTaC (0xc, 1, 1, 1, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.gt.and", A2, OpX2TbTaC (0xc, 1, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.ge.andcm", A2, OpX2TbTaC (0xc, 1, 1, 1, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.le.andcm", A2, OpX2TbTaC (0xc, 1, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt", A2, OpX2TaC (0xc, 2, 0, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.le", A2, OpX2TaC (0xc, 2, 0, 0), {P1, P2, IMM8M1, R3}, EMPTY}, + {"cmp.gt", A2, OpX2TaC (0xc, 2, 0, 0), {P2, P1, IMM8M1, R3}, EMPTY}, + {"cmp.ge", A2, OpX2TaC (0xc, 2, 0, 0), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp.lt.unc", A2, OpX2TaC (0xc, 2, 0, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.le.unc", A2, OpX2TaC (0xc, 2, 0, 1), {P1, P2, IMM8M1, R3}, EMPTY}, + {"cmp.gt.unc", A2, OpX2TaC (0xc, 2, 0, 1), {P2, P1, IMM8M1, R3}, EMPTY}, + {"cmp.ge.unc", A2, OpX2TaC (0xc, 2, 0, 1), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp.eq.and", A2, OpX2TaC (0xc, 2, 1, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.ne.andcm", A2, OpX2TaC (0xc, 2, 1, 0), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp.ne.and", A2, OpX2TaC (0xc, 2, 1, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.eq.andcm", A2, OpX2TaC (0xc, 2, 1, 1), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp4.lt", A2, OpX2TaC (0xc, 3, 0, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.le", A2, OpX2TaC (0xc, 3, 0, 0), {P1, P2, IMM8M1, R3}, EMPTY}, + {"cmp4.gt", A2, OpX2TaC (0xc, 3, 0, 0), {P2, P1, IMM8M1, R3}, EMPTY}, + {"cmp4.ge", A2, OpX2TaC (0xc, 3, 0, 0), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp4.lt.unc", A2, OpX2TaC (0xc, 3, 0, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.le.unc", A2, OpX2TaC (0xc, 3, 0, 1), {P1, P2, IMM8M1, R3}, EMPTY}, + {"cmp4.gt.unc", A2, OpX2TaC (0xc, 3, 0, 1), {P2, P1, IMM8M1, R3}, EMPTY}, + {"cmp4.ge.unc", A2, OpX2TaC (0xc, 3, 0, 1), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp4.eq.and", A2, OpX2TaC (0xc, 3, 1, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.ne.andcm", A2, OpX2TaC (0xc, 3, 1, 0), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp4.ne.and", A2, OpX2TaC (0xc, 3, 1, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.eq.andcm", A2, OpX2TaC (0xc, 3, 1, 1), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp.ltu", A2, OpX2TbTaC (0xd, 0, 0, 0, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp.leu", A2, OpX2TbTaC (0xd, 0, 0, 0, 0), {P2, P1, R3, R2}, EMPTY}, + {"cmp.gtu", A2, OpX2TbTaC (0xd, 0, 0, 0, 0), {P1, P2, R3, R2}, EMPTY}, + {"cmp.geu", A2, OpX2TbTaC (0xd, 0, 0, 0, 0), {P2, P1, R2, R3}, EMPTY}, + {"cmp.ltu.unc", A2, OpX2TbTaC (0xd, 0, 0, 0, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp.leu.unc", A2, OpX2TbTaC (0xd, 0, 0, 0, 1), {P2, P1, R3, R2}, EMPTY}, + {"cmp.gtu.unc", A2, OpX2TbTaC (0xd, 0, 0, 0, 1), {P1, P2, R3, R2}, EMPTY}, + {"cmp.geu.unc", A2, OpX2TbTaC (0xd, 0, 0, 0, 1), {P2, P1, R2, R3}, EMPTY}, + {"cmp.eq.or", A2, OpX2TbTaC (0xd, 0, 0, 1, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp.ne.orcm", A2, OpX2TbTaC (0xd, 0, 0, 1, 0), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp.ne.or", A2, OpX2TbTaC (0xd, 0, 0, 1, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp.eq.orcm", A2, OpX2TbTaC (0xd, 0, 0, 1, 1), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp4.ltu", A2, OpX2TbTaC (0xd, 1, 0, 0, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.leu", A2, OpX2TbTaC (0xd, 1, 0, 0, 0), {P2, P1, R3, R2}, EMPTY}, + {"cmp4.gtu", A2, OpX2TbTaC (0xd, 1, 0, 0, 0), {P1, P2, R3, R2}, EMPTY}, + {"cmp4.geu", A2, OpX2TbTaC (0xd, 1, 0, 0, 0), {P2, P1, R2, R3}, EMPTY}, + {"cmp4.ltu.unc", A2, OpX2TbTaC (0xd, 1, 0, 0, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.leu.unc", A2, OpX2TbTaC (0xd, 1, 0, 0, 1), {P2, P1, R3, R2}, EMPTY}, + {"cmp4.gtu.unc", A2, OpX2TbTaC (0xd, 1, 0, 0, 1), {P1, P2, R3, R2}, EMPTY}, + {"cmp4.geu.unc", A2, OpX2TbTaC (0xd, 1, 0, 0, 1), {P2, P1, R2, R3}, EMPTY}, + {"cmp4.eq.or", A2, OpX2TbTaC (0xd, 1, 0, 1, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.ne.orcm", A2, OpX2TbTaC (0xd, 1, 0, 1, 0), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp4.ne.or", A2, OpX2TbTaC (0xd, 1, 0, 1, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.eq.orcm", A2, OpX2TbTaC (0xd, 1, 0, 1, 1), {P1, P2, R2, R3}, PSEUDO, 0, NULL}, + {"cmp.gt.or", A2, OpX2TbTaC (0xd, 0, 1, 0, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.lt.or", A2, OpX2TbTaC (0xd, 0, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.le.orcm", A2, OpX2TbTaC (0xd, 0, 1, 0, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.ge.orcm", A2, OpX2TbTaC (0xd, 0, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.le.or", A2, OpX2TbTaC (0xd, 0, 1, 0, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.ge.or", A2, OpX2TbTaC (0xd, 0, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.gt.orcm", A2, OpX2TbTaC (0xd, 0, 1, 0, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.lt.orcm", A2, OpX2TbTaC (0xd, 0, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ge.or", A2, OpX2TbTaC (0xd, 0, 1, 1, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.le.or", A2, OpX2TbTaC (0xd, 0, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt.orcm", A2, OpX2TbTaC (0xd, 0, 1, 1, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.gt.orcm", A2, OpX2TbTaC (0xd, 0, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt.or", A2, OpX2TbTaC (0xd, 0, 1, 1, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.gt.or", A2, OpX2TbTaC (0xd, 0, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ge.orcm", A2, OpX2TbTaC (0xd, 0, 1, 1, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.le.orcm", A2, OpX2TbTaC (0xd, 0, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.gt.or", A2, OpX2TbTaC (0xd, 1, 1, 0, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.lt.or", A2, OpX2TbTaC (0xd, 1, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.le.orcm", A2, OpX2TbTaC (0xd, 1, 1, 0, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.ge.orcm", A2, OpX2TbTaC (0xd, 1, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.le.or", A2, OpX2TbTaC (0xd, 1, 1, 0, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.ge.or", A2, OpX2TbTaC (0xd, 1, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.gt.orcm", A2, OpX2TbTaC (0xd, 1, 1, 0, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.lt.orcm", A2, OpX2TbTaC (0xd, 1, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.ge.or", A2, OpX2TbTaC (0xd, 1, 1, 1, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.le.or", A2, OpX2TbTaC (0xd, 1, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.lt.orcm", A2, OpX2TbTaC (0xd, 1, 1, 1, 0), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.gt.orcm", A2, OpX2TbTaC (0xd, 1, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.lt.or", A2, OpX2TbTaC (0xd, 1, 1, 1, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.gt.or", A2, OpX2TbTaC (0xd, 1, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.ge.orcm", A2, OpX2TbTaC (0xd, 1, 1, 1, 1), {P1, P2, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.le.orcm", A2, OpX2TbTaC (0xd, 1, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ltu", A2, OpX2TaC (0xd, 2, 0, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.leu", A2, OpX2TaC (0xd, 2, 0, 0), {P1, P2, IMM8M1U8, R3}, EMPTY}, + {"cmp.gtu", A2, OpX2TaC (0xd, 2, 0, 0), {P2, P1, IMM8M1U8, R3}, EMPTY}, + {"cmp.geu", A2, OpX2TaC (0xd, 2, 0, 0), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp.ltu.unc", A2, OpX2TaC (0xd, 2, 0, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.leu.unc", A2, OpX2TaC (0xd, 2, 0, 1), {P1, P2, IMM8M1U8, R3}, EMPTY}, + {"cmp.gtu.unc", A2, OpX2TaC (0xd, 2, 0, 1), {P2, P1, IMM8M1U8, R3}, EMPTY}, + {"cmp.geu.unc", A2, OpX2TaC (0xd, 2, 0, 1), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp.eq.or", A2, OpX2TaC (0xd, 2, 1, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.ne.orcm", A2, OpX2TaC (0xd, 2, 1, 0), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp.ne.or", A2, OpX2TaC (0xd, 2, 1, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.eq.orcm", A2, OpX2TaC (0xd, 2, 1, 1), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp4.ltu", A2, OpX2TaC (0xd, 3, 0, 0), {P1, P2, IMM8U4, R3}, EMPTY}, + {"cmp4.leu", A2, OpX2TaC (0xd, 3, 0, 0), {P1, P2, IMM8M1U4, R3}, EMPTY}, + {"cmp4.gtu", A2, OpX2TaC (0xd, 3, 0, 0), {P2, P1, IMM8M1U4, R3}, EMPTY}, + {"cmp4.geu", A2, OpX2TaC (0xd, 3, 0, 0), {P2, P1, IMM8U4, R3}, EMPTY}, + {"cmp4.ltu.unc", A2, OpX2TaC (0xd, 3, 0, 1), {P1, P2, IMM8U4, R3}, EMPTY}, + {"cmp4.leu.unc", A2, OpX2TaC (0xd, 3, 0, 1), {P1, P2, IMM8M1U4, R3}, EMPTY}, + {"cmp4.gtu.unc", A2, OpX2TaC (0xd, 3, 0, 1), {P2, P1, IMM8M1U4, R3}, EMPTY}, + {"cmp4.geu.unc", A2, OpX2TaC (0xd, 3, 0, 1), {P2, P1, IMM8U4, R3}, EMPTY}, + {"cmp4.eq.or", A2, OpX2TaC (0xd, 3, 1, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.ne.orcm", A2, OpX2TaC (0xd, 3, 1, 0), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp4.ne.or", A2, OpX2TaC (0xd, 3, 1, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.eq.orcm", A2, OpX2TaC (0xd, 3, 1, 1), {P1, P2, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp.eq", A2, OpX2TbTaC (0xe, 0, 0, 0, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp.ne", A2, OpX2TbTaC (0xe, 0, 0, 0, 0), {P2, P1, R2, R3}, EMPTY}, + {"cmp.eq.unc", A2, OpX2TbTaC (0xe, 0, 0, 0, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp.ne.unc", A2, OpX2TbTaC (0xe, 0, 0, 0, 1), {P2, P1, R2, R3}, EMPTY}, + {"cmp.eq.or.andcm", A2, OpX2TbTaC (0xe, 0, 0, 1, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp.ne.and.orcm", A2, OpX2TbTaC (0xe, 0, 0, 1, 0), {P2, P1, R2, R3}, PSEUDO, 0, NULL}, + {"cmp.ne.or.andcm", A2, OpX2TbTaC (0xe, 0, 0, 1, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp.eq.and.orcm", A2, OpX2TbTaC (0xe, 0, 0, 1, 1), {P2, P1, R2, R3}, PSEUDO, 0, NULL}, + {"cmp4.eq", A2, OpX2TbTaC (0xe, 1, 0, 0, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.ne", A2, OpX2TbTaC (0xe, 1, 0, 0, 0), {P2, P1, R2, R3}, EMPTY}, + {"cmp4.eq.unc", A2, OpX2TbTaC (0xe, 1, 0, 0, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.ne.unc", A2, OpX2TbTaC (0xe, 1, 0, 0, 1), {P2, P1, R2, R3}, EMPTY}, + {"cmp4.eq.or.andcm", A2, OpX2TbTaC (0xe, 1, 0, 1, 0), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.ne.and.orcm", A2, OpX2TbTaC (0xe, 1, 0, 1, 0), {P2, P1, R2, R3}, PSEUDO, 0, NULL}, + {"cmp4.ne.or.andcm", A2, OpX2TbTaC (0xe, 1, 0, 1, 1), {P1, P2, R2, R3}, EMPTY}, + {"cmp4.eq.and.orcm", A2, OpX2TbTaC (0xe, 1, 0, 1, 1), {P2, P1, R2, R3}, PSEUDO, 0, NULL}, + {"cmp.gt.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.lt.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.le.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 0), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.ge.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 0), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.le.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.ge.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.gt.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 1), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.lt.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 0, 1), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ge.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.le.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 0), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.gt.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 0), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.lt.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp.gt.or.andcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.ge.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 1), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp.le.and.orcm", A2, OpX2TbTaC (0xe, 0, 1, 1, 1), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.gt.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.lt.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.le.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 0), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.ge.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 0), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.le.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.ge.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.gt.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 1), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.lt.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 0, 1), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.ge.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 0), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.le.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 0), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.lt.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 0), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.gt.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 0), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.lt.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 1), {P1, P2, GR0, R3}, EMPTY}, + {"cmp4.gt.or.andcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 1), {P1, P2, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp4.ge.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 1), {P2, P1, GR0, R3}, PSEUDO, 0, NULL}, + {"cmp4.le.and.orcm", A2, OpX2TbTaC (0xe, 1, 1, 1, 1), {P2, P1, R3, GR0}, PSEUDO, 0, NULL}, + {"cmp.eq", A2, OpX2TaC (0xe, 2, 0, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.ne", A2, OpX2TaC (0xe, 2, 0, 0), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp.eq.unc", A2, OpX2TaC (0xe, 2, 0, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.ne.unc", A2, OpX2TaC (0xe, 2, 0, 1), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp.eq.or.andcm", A2, OpX2TaC (0xe, 2, 1, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.ne.and.orcm", A2, OpX2TaC (0xe, 2, 1, 0), {P2, P1, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp.ne.or.andcm", A2, OpX2TaC (0xe, 2, 1, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp.eq.and.orcm", A2, OpX2TaC (0xe, 2, 1, 1), {P2, P1, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp4.eq", A2, OpX2TaC (0xe, 3, 0, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.ne", A2, OpX2TaC (0xe, 3, 0, 0), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp4.eq.unc", A2, OpX2TaC (0xe, 3, 0, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.ne.unc", A2, OpX2TaC (0xe, 3, 0, 1), {P2, P1, IMM8, R3}, EMPTY}, + {"cmp4.eq.or.andcm", A2, OpX2TaC (0xe, 3, 1, 0), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.ne.and.orcm", A2, OpX2TaC (0xe, 3, 1, 0), {P2, P1, IMM8, R3}, PSEUDO, 0, NULL}, + {"cmp4.ne.or.andcm", A2, OpX2TaC (0xe, 3, 1, 1), {P1, P2, IMM8, R3}, EMPTY}, + {"cmp4.eq.and.orcm", A2, OpX2TaC (0xe, 3, 1, 1), {P2, P1, IMM8, R3}, PSEUDO, 0, NULL}, + + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; + +#undef A +#undef A2 +#undef bC +#undef bImm14 +#undef bR3a +#undef bR3b +#undef bTa +#undef bTb +#undef bVe +#undef bX +#undef bX2 +#undef bX2a +#undef bX2b +#undef bX4 +#undef bZa +#undef bZb +#undef mC +#undef mImm14 +#undef mR3a +#undef mR3b +#undef mTa +#undef mTb +#undef mVe +#undef mX +#undef mX2 +#undef mX2a +#undef mX2b +#undef mX4 +#undef mZa +#undef mZb +#undef OpR3a +#undef OpR3b +#undef OpX2aVe +#undef OpX2aVeImm14 +#undef OpX2aVeX4 +#undef OpX2aVeX4X2b +#undef OpX2TbTaC +#undef OpX2TaC +#undef OpX2aZaZbX4 +#undef OpX2aZaZbX4X2b +#undef EMPTY diff --git a/tools/debugger/xenitp/ia64-opc-b.c b/tools/debugger/xenitp/ia64-opc-b.c new file mode 100644 index 0000000..dbc74e1 --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-b.c @@ -0,0 +1,512 @@ +/* ia64-opc-b.c -- IA-64 `B' opcode table. + Copyright 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-opc.h" + +#define B0 IA64_TYPE_B, 0 +#define B IA64_TYPE_B, 1 + +/* instruction bit fields: */ +#define bBtype(x) (((ia64_insn) ((x) & 0x7)) << 6) +#define bD(x) (((ia64_insn) ((x) & 0x1)) << 35) +#define bIh(x) (((ia64_insn) ((x) & 0x1)) << 35) +#define bPa(x) (((ia64_insn) ((x) & 0x1)) << 12) +#define bPr(x) (((ia64_insn) ((x) & 0x3f)) << 0) +#define bWha(x) (((ia64_insn) ((x) & 0x3)) << 33) +#define bWhb(x) (((ia64_insn) ((x) & 0x3)) << 3) +#define bWhc(x) (((ia64_insn) ((x) & 0x7)) << 32) +#define bX6(x) (((ia64_insn) ((x) & 0x3f)) << 27) + +#define mBtype bBtype (-1) +#define mD bD (-1) +#define mIh bIh (-1) +#define mPa bPa (-1) +#define mPr bPr (-1) +#define mWha bWha (-1) +#define mWhb bWhb (-1) +#define mWhc bWhc (-1) +#define mX6 bX6 (-1) + +#define OpX6(a,b) (bOp (a) | bX6 (b)), (mOp | mX6) +#define OpPaWhaD(a,b,c,d) \ + (bOp (a) | bPa (b) | bWha (c) | bD (d)), (mOp | mPa | mWha | mD) +#define OpPaWhcD(a,b,c,d) \ + (bOp (a) | bPa (b) | bWhc (c) | bD (d)), (mOp | mPa | mWhc | mD) +#define OpBtypePaWhaD(a,b,c,d,e) \ + (bOp (a) | bBtype (b) | bPa (c) | bWha (d) | bD (e)), \ + (mOp | mBtype | mPa | mWha | mD) +#define OpBtypePaWhaDPr(a,b,c,d,e,f) \ + (bOp (a) | bBtype (b) | bPa (c) | bWha (d) | bD (e) | bPr (f)), \ + (mOp | mBtype | mPa | mWha | mD | mPr) +#define OpX6BtypePaWhaD(a,b,c,d,e,f) \ + (bOp (a) | bX6 (b) | bBtype (c) | bPa (d) | bWha (e) | bD (f)), \ + (mOp | mX6 | mBtype | mPa | mWha | mD) +#define OpX6BtypePaWhaDPr(a,b,c,d,e,f,g) \ + (bOp (a) | bX6 (b) | bBtype (c) | bPa (d) | bWha (e) | bD (f) | bPr (g)), \ + (mOp | mX6 | mBtype | mPa | mWha | mD | mPr) +#define OpIhWhb(a,b,c) \ + (bOp (a) | bIh (b) | bWhb (c)), \ + (mOp | mIh | mWhb) +#define OpX6IhWhb(a,b,c,d) \ + (bOp (a) | bX6 (b) | bIh (c) | bWhb (d)), \ + (mOp | mX6 | mIh | mWhb) + +/* Used to initialise unused fields in ia64_opcode struct, + in order to stop gcc from complaining. */ +#define EMPTY 0,0,NULL + +struct ia64_opcode ia64_opcodes_b[] = + { + /* B-type instruction encodings (sorted according to major opcode) */ + +#define BR(a,b) \ + B0, OpX6BtypePaWhaDPr (0, 0x20, 0, a, 0, b, 0), {B2}, PSEUDO, 0, NULL + {"br.few", BR (0, 0)}, + {"br", BR (0, 0)}, + {"br.few.clr", BR (0, 1)}, + {"br.clr", BR (0, 1)}, + {"br.many", BR (1, 0)}, + {"br.many.clr", BR (1, 1)}, +#undef BR + +#define BR(a,b,c,d,e) B0, OpX6BtypePaWhaD (0, a, b, c, d, e), {B2}, EMPTY +#define BRP(a,b,c,d,e) B0, OpX6BtypePaWhaD (0, a, b, c, d, e), {B2}, PSEUDO, 0, NULL +#define BRT(a,b,c,d,e,f) B0, OpX6BtypePaWhaD (0, a, b, c, d, e), {B2}, f, 0, NULL + {"br.cond.sptk.few", BR (0x20, 0, 0, 0, 0)}, + {"br.cond.sptk", BRP (0x20, 0, 0, 0, 0)}, + {"br.cond.sptk.few.clr", BR (0x20, 0, 0, 0, 1)}, + {"br.cond.sptk.clr", BRP (0x20, 0, 0, 0, 1)}, + {"br.cond.spnt.few", BR (0x20, 0, 0, 1, 0)}, + {"br.cond.spnt", BRP (0x20, 0, 0, 1, 0)}, + {"br.cond.spnt.few.clr", BR (0x20, 0, 0, 1, 1)}, + {"br.cond.spnt.clr", BRP (0x20, 0, 0, 1, 1)}, + {"br.cond.dptk.few", BR (0x20, 0, 0, 2, 0)}, + {"br.cond.dptk", BRP (0x20, 0, 0, 2, 0)}, + {"br.cond.dptk.few.clr", BR (0x20, 0, 0, 2, 1)}, + {"br.cond.dptk.clr", BRP (0x20, 0, 0, 2, 1)}, + {"br.cond.dpnt.few", BR (0x20, 0, 0, 3, 0)}, + {"br.cond.dpnt", BRP (0x20, 0, 0, 3, 0)}, + {"br.cond.dpnt.few.clr", BR (0x20, 0, 0, 3, 1)}, + {"br.cond.dpnt.clr", BRP (0x20, 0, 0, 3, 1)}, + {"br.cond.sptk.many", BR (0x20, 0, 1, 0, 0)}, + {"br.cond.sptk.many.clr", BR (0x20, 0, 1, 0, 1)}, + {"br.cond.spnt.many", BR (0x20, 0, 1, 1, 0)}, + {"br.cond.spnt.many.clr", BR (0x20, 0, 1, 1, 1)}, + {"br.cond.dptk.many", BR (0x20, 0, 1, 2, 0)}, + {"br.cond.dptk.many.clr", BR (0x20, 0, 1, 2, 1)}, + {"br.cond.dpnt.many", BR (0x20, 0, 1, 3, 0)}, + {"br.cond.dpnt.many.clr", BR (0x20, 0, 1, 3, 1)}, + {"br.sptk.few", BR (0x20, 0, 0, 0, 0)}, + {"br.sptk", BRP (0x20, 0, 0, 0, 0)}, + {"br.sptk.few.clr", BR (0x20, 0, 0, 0, 1)}, + {"br.sptk.clr", BRP (0x20, 0, 0, 0, 1)}, + {"br.spnt.few", BR (0x20, 0, 0, 1, 0)}, + {"br.spnt", BRP (0x20, 0, 0, 1, 0)}, + {"br.spnt.few.clr", BR (0x20, 0, 0, 1, 1)}, + {"br.spnt.clr", BRP (0x20, 0, 0, 1, 1)}, + {"br.dptk.few", BR (0x20, 0, 0, 2, 0)}, + {"br.dptk", BRP (0x20, 0, 0, 2, 0)}, + {"br.dptk.few.clr", BR (0x20, 0, 0, 2, 1)}, + {"br.dptk.clr", BRP (0x20, 0, 0, 2, 1)}, + {"br.dpnt.few", BR (0x20, 0, 0, 3, 0)}, + {"br.dpnt", BRP (0x20, 0, 0, 3, 0)}, + {"br.dpnt.few.clr", BR (0x20, 0, 0, 3, 1)}, + {"br.dpnt.clr", BRP (0x20, 0, 0, 3, 1)}, + {"br.sptk.many", BR (0x20, 0, 1, 0, 0)}, + {"br.sptk.many.clr", BR (0x20, 0, 1, 0, 1)}, + {"br.spnt.many", BR (0x20, 0, 1, 1, 0)}, + {"br.spnt.many.clr", BR (0x20, 0, 1, 1, 1)}, + {"br.dptk.many", BR (0x20, 0, 1, 2, 0)}, + {"br.dptk.many.clr", BR (0x20, 0, 1, 2, 1)}, + {"br.dpnt.many", BR (0x20, 0, 1, 3, 0)}, + {"br.dpnt.many.clr", BR (0x20, 0, 1, 3, 1)}, + {"br.ia.sptk.few", BR (0x20, 1, 0, 0, 0)}, + {"br.ia.sptk", BRP (0x20, 1, 0, 0, 0)}, + {"br.ia.sptk.few.clr", BR (0x20, 1, 0, 0, 1)}, + {"br.ia.sptk.clr", BRP (0x20, 1, 0, 0, 1)}, + {"br.ia.spnt.few", BR (0x20, 1, 0, 1, 0)}, + {"br.ia.spnt", BRP (0x20, 1, 0, 1, 0)}, + {"br.ia.spnt.few.clr", BR (0x20, 1, 0, 1, 1)}, + {"br.ia.spnt.clr", BRP (0x20, 1, 0, 1, 1)}, + {"br.ia.dptk.few", BR (0x20, 1, 0, 2, 0)}, + {"br.ia.dptk", BRP (0x20, 1, 0, 2, 0)}, + {"br.ia.dptk.few.clr", BR (0x20, 1, 0, 2, 1)}, + {"br.ia.dptk.clr", BRP (0x20, 1, 0, 2, 1)}, + {"br.ia.dpnt.few", BR (0x20, 1, 0, 3, 0)}, + {"br.ia.dpnt", BRP (0x20, 1, 0, 3, 0)}, + {"br.ia.dpnt.few.clr", BR (0x20, 1, 0, 3, 1)}, + {"br.ia.dpnt.clr", BRP (0x20, 1, 0, 3, 1)}, + {"br.ia.sptk.many", BR (0x20, 1, 1, 0, 0)}, + {"br.ia.sptk.many.clr", BR (0x20, 1, 1, 0, 1)}, + {"br.ia.spnt.many", BR (0x20, 1, 1, 1, 0)}, + {"br.ia.spnt.many.clr", BR (0x20, 1, 1, 1, 1)}, + {"br.ia.dptk.many", BR (0x20, 1, 1, 2, 0)}, + {"br.ia.dptk.many.clr", BR (0x20, 1, 1, 2, 1)}, + {"br.ia.dpnt.many", BR (0x20, 1, 1, 3, 0)}, + {"br.ia.dpnt.many.clr", BR (0x20, 1, 1, 3, 1)}, + {"br.ret.sptk.few", BRT (0x21, 4, 0, 0, 0, MOD_RRBS)}, + {"br.ret.sptk", BRT (0x21, 4, 0, 0, 0, PSEUDO | MOD_RRBS)}, + {"br.ret.sptk.few.clr", BRT (0x21, 4, 0, 0, 1, MOD_RRBS)}, + {"br.ret.sptk.clr", BRT (0x21, 4, 0, 0, 1, PSEUDO | MOD_RRBS)}, + {"br.ret.spnt.few", BRT (0x21, 4, 0, 1, 0, MOD_RRBS)}, + {"br.ret.spnt", BRT (0x21, 4, 0, 1, 0, PSEUDO | MOD_RRBS)}, + {"br.ret.spnt.few.clr", BRT (0x21, 4, 0, 1, 1, MOD_RRBS)}, + {"br.ret.spnt.clr", BRT (0x21, 4, 0, 1, 1, PSEUDO | MOD_RRBS)}, + {"br.ret.dptk.few", BRT (0x21, 4, 0, 2, 0, MOD_RRBS)}, + {"br.ret.dptk", BRT (0x21, 4, 0, 2, 0, PSEUDO | MOD_RRBS)}, + {"br.ret.dptk.few.clr", BRT (0x21, 4, 0, 2, 1, MOD_RRBS)}, + {"br.ret.dptk.clr", BRT (0x21, 4, 0, 2, 1, PSEUDO | MOD_RRBS)}, + {"br.ret.dpnt.few", BRT (0x21, 4, 0, 3, 0, MOD_RRBS)}, + {"br.ret.dpnt", BRT (0x21, 4, 0, 3, 0, PSEUDO | MOD_RRBS)}, + {"br.ret.dpnt.few.clr", BRT (0x21, 4, 0, 3, 1, MOD_RRBS)}, + {"br.ret.dpnt.clr", BRT (0x21, 4, 0, 3, 1, PSEUDO | MOD_RRBS)}, + {"br.ret.sptk.many", BRT (0x21, 4, 1, 0, 0, MOD_RRBS)}, + {"br.ret.sptk.many.clr", BRT (0x21, 4, 1, 0, 1, MOD_RRBS)}, + {"br.ret.spnt.many", BRT (0x21, 4, 1, 1, 0, MOD_RRBS)}, + {"br.ret.spnt.many.clr", BRT (0x21, 4, 1, 1, 1, MOD_RRBS)}, + {"br.ret.dptk.many", BRT (0x21, 4, 1, 2, 0, MOD_RRBS)}, + {"br.ret.dptk.many.clr", BRT (0x21, 4, 1, 2, 1, MOD_RRBS)}, + {"br.ret.dpnt.many", BRT (0x21, 4, 1, 3, 0, MOD_RRBS)}, + {"br.ret.dpnt.many.clr", BRT (0x21, 4, 1, 3, 1, MOD_RRBS)}, +#undef BR +#undef BRP +#undef BRT + + {"cover", B0, OpX6 (0, 0x02), {0, }, NO_PRED | LAST | MOD_RRBS, 0, NULL}, + {"clrrrb", B0, OpX6 (0, 0x04), {0, }, NO_PRED | LAST | MOD_RRBS, 0, NULL}, + {"clrrrb.pr", B0, OpX6 (0, 0x05), {0, }, NO_PRED | LAST | MOD_RRBS, 0, NULL}, + {"rfi", B0, OpX6 (0, 0x08), {0, }, NO_PRED | LAST | PRIV | MOD_RRBS, 0, NULL}, + {"bsw.0", B0, OpX6 (0, 0x0c), {0, }, NO_PRED | LAST | PRIV, 0, NULL}, + {"bsw.1", B0, OpX6 (0, 0x0d), {0, }, NO_PRED | LAST | PRIV, 0, NULL}, + {"epc", B0, OpX6 (0, 0x10), {0, }, NO_PRED, 0, NULL}, + {"vmsw.0", B0, OpX6 (0, 0x18), {0, }, NO_PRED | PRIV, 0, NULL}, + {"vmsw.1", B0, OpX6 (0, 0x19), {0, }, NO_PRED | PRIV, 0, NULL}, + + {"break.b", B0, OpX6 (0, 0x00), {IMMU21}, EMPTY}, + + {"br.call.sptk.few", B, OpPaWhcD (1, 0, 1, 0), {B1, B2}, EMPTY}, + {"br.call.sptk", B, OpPaWhcD (1, 0, 1, 0), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.sptk.few.clr", B, OpPaWhcD (1, 0, 1, 1), {B1, B2}, EMPTY}, + {"br.call.sptk.clr", B, OpPaWhcD (1, 0, 1, 1), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.spnt.few", B, OpPaWhcD (1, 0, 3, 0), {B1, B2}, EMPTY}, + {"br.call.spnt", B, OpPaWhcD (1, 0, 3, 0), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.spnt.few.clr", B, OpPaWhcD (1, 0, 3, 1), {B1, B2}, EMPTY}, + {"br.call.spnt.clr", B, OpPaWhcD (1, 0, 3, 1), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.dptk.few", B, OpPaWhcD (1, 0, 5, 0), {B1, B2}, EMPTY}, + {"br.call.dptk", B, OpPaWhcD (1, 0, 5, 0), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.dptk.few.clr", B, OpPaWhcD (1, 0, 5, 1), {B1, B2}, EMPTY}, + {"br.call.dptk.clr", B, OpPaWhcD (1, 0, 5, 1), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.dpnt.few", B, OpPaWhcD (1, 0, 7, 0), {B1, B2}, EMPTY}, + {"br.call.dpnt", B, OpPaWhcD (1, 0, 7, 0), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.dpnt.few.clr", B, OpPaWhcD (1, 0, 7, 1), {B1, B2}, EMPTY}, + {"br.call.dpnt.clr", B, OpPaWhcD (1, 0, 7, 1), {B1, B2}, PSEUDO, 0, NULL}, + {"br.call.sptk.many", B, OpPaWhcD (1, 1, 1, 0), {B1, B2}, EMPTY}, + {"br.call.sptk.many.clr", B, OpPaWhcD (1, 1, 1, 1), {B1, B2}, EMPTY}, + {"br.call.spnt.many", B, OpPaWhcD (1, 1, 3, 0), {B1, B2}, EMPTY}, + {"br.call.spnt.many.clr", B, OpPaWhcD (1, 1, 3, 1), {B1, B2}, EMPTY}, + {"br.call.dptk.many", B, OpPaWhcD (1, 1, 5, 0), {B1, B2}, EMPTY}, + {"br.call.dptk.many.clr", B, OpPaWhcD (1, 1, 5, 1), {B1, B2}, EMPTY}, + {"br.call.dpnt.many", B, OpPaWhcD (1, 1, 7, 0), {B1, B2}, EMPTY}, + {"br.call.dpnt.many.clr", B, OpPaWhcD (1, 1, 7, 1), {B1, B2}, EMPTY}, + +#define BRP(a,b,c) \ + B0, OpX6IhWhb (2, a, b, c), {B2, TAG13}, NO_PRED, 0, NULL + {"brp.sptk", BRP (0x10, 0, 0)}, + {"brp.dptk", BRP (0x10, 0, 2)}, + {"brp.sptk.imp", BRP (0x10, 1, 0)}, + {"brp.dptk.imp", BRP (0x10, 1, 2)}, + {"brp.ret.sptk", BRP (0x11, 0, 0)}, + {"brp.ret.dptk", BRP (0x11, 0, 2)}, + {"brp.ret.sptk.imp", BRP (0x11, 1, 0)}, + {"brp.ret.dptk.imp", BRP (0x11, 1, 2)}, +#undef BRP + + {"nop.b", B0, OpX6 (2, 0x00), {IMMU21}, EMPTY}, + {"hint.b", B0, OpX6 (2, 0x01), {IMMU21}, EMPTY}, + +#define BR(a,b) \ + B0, OpBtypePaWhaDPr (4, 0, a, 0, b, 0), {TGT25c}, PSEUDO, 0, NULL + {"br.few", BR (0, 0)}, + {"br", BR (0, 0)}, + {"br.few.clr", BR (0, 1)}, + {"br.clr", BR (0, 1)}, + {"br.many", BR (1, 0)}, + {"br.many.clr", BR (1, 1)}, +#undef BR + +#define BR(a,b,c) \ + B0, OpBtypePaWhaD (4, 0, a, b, c), {TGT25c}, EMPTY +#define BRP(a,b,c) \ + B0, OpBtypePaWhaD (4, 0, a, b, c), {TGT25c}, PSEUDO, 0, NULL + {"br.cond.sptk.few", BR (0, 0, 0)}, + {"br.cond.sptk", BRP (0, 0, 0)}, + {"br.cond.sptk.few.clr", BR (0, 0, 1)}, + {"br.cond.sptk.clr", BRP (0, 0, 1)}, + {"br.cond.spnt.few", BR (0, 1, 0)}, + {"br.cond.spnt", BRP (0, 1, 0)}, + {"br.cond.spnt.few.clr", BR (0, 1, 1)}, + {"br.cond.spnt.clr", BRP (0, 1, 1)}, + {"br.cond.dptk.few", BR (0, 2, 0)}, + {"br.cond.dptk", BRP (0, 2, 0)}, + {"br.cond.dptk.few.clr", BR (0, 2, 1)}, + {"br.cond.dptk.clr", BRP (0, 2, 1)}, + {"br.cond.dpnt.few", BR (0, 3, 0)}, + {"br.cond.dpnt", BRP (0, 3, 0)}, + {"br.cond.dpnt.few.clr", BR (0, 3, 1)}, + {"br.cond.dpnt.clr", BRP (0, 3, 1)}, + {"br.cond.sptk.many", BR (1, 0, 0)}, + {"br.cond.sptk.many.clr", BR (1, 0, 1)}, + {"br.cond.spnt.many", BR (1, 1, 0)}, + {"br.cond.spnt.many.clr", BR (1, 1, 1)}, + {"br.cond.dptk.many", BR (1, 2, 0)}, + {"br.cond.dptk.many.clr", BR (1, 2, 1)}, + {"br.cond.dpnt.many", BR (1, 3, 0)}, + {"br.cond.dpnt.many.clr", BR (1, 3, 1)}, + {"br.sptk.few", BR (0, 0, 0)}, + {"br.sptk", BRP (0, 0, 0)}, + {"br.sptk.few.clr", BR (0, 0, 1)}, + {"br.sptk.clr", BRP (0, 0, 1)}, + {"br.spnt.few", BR (0, 1, 0)}, + {"br.spnt", BRP (0, 1, 0)}, + {"br.spnt.few.clr", BR (0, 1, 1)}, + {"br.spnt.clr", BRP (0, 1, 1)}, + {"br.dptk.few", BR (0, 2, 0)}, + {"br.dptk", BRP (0, 2, 0)}, + {"br.dptk.few.clr", BR (0, 2, 1)}, + {"br.dptk.clr", BRP (0, 2, 1)}, + {"br.dpnt.few", BR (0, 3, 0)}, + {"br.dpnt", BRP (0, 3, 0)}, + {"br.dpnt.few.clr", BR (0, 3, 1)}, + {"br.dpnt.clr", BRP (0, 3, 1)}, + {"br.sptk.many", BR (1, 0, 0)}, + {"br.sptk.many.clr", BR (1, 0, 1)}, + {"br.spnt.many", BR (1, 1, 0)}, + {"br.spnt.many.clr", BR (1, 1, 1)}, + {"br.dptk.many", BR (1, 2, 0)}, + {"br.dptk.many.clr", BR (1, 2, 1)}, + {"br.dpnt.many", BR (1, 3, 0)}, + {"br.dpnt.many.clr", BR (1, 3, 1)}, +#undef BR +#undef BRP + +#define BR(a,b,c,d, e) \ + B0, OpBtypePaWhaD (4, a, b, c, d), {TGT25c}, SLOT2 | e, 0, NULL + {"br.wexit.sptk.few", BR (2, 0, 0, 0, MOD_RRBS)}, + {"br.wexit.sptk", BR (2, 0, 0, 0, PSEUDO | MOD_RRBS)}, + {"br.wexit.sptk.few.clr", BR (2, 0, 0, 1, MOD_RRBS)}, + {"br.wexit.sptk.clr", BR (2, 0, 0, 1, PSEUDO | MOD_RRBS)}, + {"br.wexit.spnt.few", BR (2, 0, 1, 0, MOD_RRBS)}, + {"br.wexit.spnt", BR (2, 0, 1, 0, PSEUDO | MOD_RRBS)}, + {"br.wexit.spnt.few.clr", BR (2, 0, 1, 1, MOD_RRBS)}, + {"br.wexit.spnt.clr", BR (2, 0, 1, 1, PSEUDO | MOD_RRBS)}, + {"br.wexit.dptk.few", BR (2, 0, 2, 0, MOD_RRBS)}, + {"br.wexit.dptk", BR (2, 0, 2, 0, PSEUDO | MOD_RRBS)}, + {"br.wexit.dptk.few.clr", BR (2, 0, 2, 1, MOD_RRBS)}, + {"br.wexit.dptk.clr", BR (2, 0, 2, 1, PSEUDO | MOD_RRBS)}, + {"br.wexit.dpnt.few", BR (2, 0, 3, 0, MOD_RRBS)}, + {"br.wexit.dpnt", BR (2, 0, 3, 0, PSEUDO | MOD_RRBS)}, + {"br.wexit.dpnt.few.clr", BR (2, 0, 3, 1, MOD_RRBS)}, + {"br.wexit.dpnt.clr", BR (2, 0, 3, 1, PSEUDO | MOD_RRBS)}, + {"br.wexit.sptk.many", BR (2, 1, 0, 0, MOD_RRBS)}, + {"br.wexit.sptk.many.clr", BR (2, 1, 0, 1, MOD_RRBS)}, + {"br.wexit.spnt.many", BR (2, 1, 1, 0, MOD_RRBS)}, + {"br.wexit.spnt.many.clr", BR (2, 1, 1, 1, MOD_RRBS)}, + {"br.wexit.dptk.many", BR (2, 1, 2, 0, MOD_RRBS)}, + {"br.wexit.dptk.many.clr", BR (2, 1, 2, 1, MOD_RRBS)}, + {"br.wexit.dpnt.many", BR (2, 1, 3, 0, MOD_RRBS)}, + {"br.wexit.dpnt.many.clr", BR (2, 1, 3, 1, MOD_RRBS)}, + {"br.wtop.sptk.few", BR (3, 0, 0, 0, MOD_RRBS)}, + {"br.wtop.sptk", BR (3, 0, 0, 0, PSEUDO | MOD_RRBS)}, + {"br.wtop.sptk.few.clr", BR (3, 0, 0, 1, MOD_RRBS)}, + {"br.wtop.sptk.clr", BR (3, 0, 0, 1, PSEUDO | MOD_RRBS)}, + {"br.wtop.spnt.few", BR (3, 0, 1, 0, MOD_RRBS)}, + {"br.wtop.spnt", BR (3, 0, 1, 0, PSEUDO | MOD_RRBS)}, + {"br.wtop.spnt.few.clr", BR (3, 0, 1, 1, MOD_RRBS)}, + {"br.wtop.spnt.clr", BR (3, 0, 1, 1, PSEUDO | MOD_RRBS)}, + {"br.wtop.dptk.few", BR (3, 0, 2, 0, MOD_RRBS)}, + {"br.wtop.dptk", BR (3, 0, 2, 0, PSEUDO | MOD_RRBS)}, + {"br.wtop.dptk.few.clr", BR (3, 0, 2, 1, MOD_RRBS)}, + {"br.wtop.dptk.clr", BR (3, 0, 2, 1, PSEUDO | MOD_RRBS)}, + {"br.wtop.dpnt.few", BR (3, 0, 3, 0, MOD_RRBS)}, + {"br.wtop.dpnt", BR (3, 0, 3, 0, PSEUDO | MOD_RRBS)}, + {"br.wtop.dpnt.few.clr", BR (3, 0, 3, 1, MOD_RRBS)}, + {"br.wtop.dpnt.clr", BR (3, 0, 3, 1, PSEUDO | MOD_RRBS)}, + {"br.wtop.sptk.many", BR (3, 1, 0, 0, MOD_RRBS)}, + {"br.wtop.sptk.many.clr", BR (3, 1, 0, 1, MOD_RRBS)}, + {"br.wtop.spnt.many", BR (3, 1, 1, 0, MOD_RRBS)}, + {"br.wtop.spnt.many.clr", BR (3, 1, 1, 1, MOD_RRBS)}, + {"br.wtop.dptk.many", BR (3, 1, 2, 0, MOD_RRBS)}, + {"br.wtop.dptk.many.clr", BR (3, 1, 2, 1, MOD_RRBS)}, + {"br.wtop.dpnt.many", BR (3, 1, 3, 0, MOD_RRBS)}, + {"br.wtop.dpnt.many.clr", BR (3, 1, 3, 1, MOD_RRBS)}, + +#undef BR +#define BR(a,b,c,d) \ + B0, OpBtypePaWhaD (4, a, b, c, d), {TGT25c}, SLOT2 | NO_PRED, 0, NULL +#define BRT(a,b,c,d,e) \ + B0, OpBtypePaWhaD (4, a, b, c, d), {TGT25c}, SLOT2 | NO_PRED | e, 0, NULL + {"br.cloop.sptk.few", BR (5, 0, 0, 0)}, + {"br.cloop.sptk", BRT (5, 0, 0, 0, PSEUDO)}, + {"br.cloop.sptk.few.clr", BR (5, 0, 0, 1)}, + {"br.cloop.sptk.clr", BRT (5, 0, 0, 1, PSEUDO)}, + {"br.cloop.spnt.few", BR (5, 0, 1, 0)}, + {"br.cloop.spnt", BRT (5, 0, 1, 0, PSEUDO)}, + {"br.cloop.spnt.few.clr", BR (5, 0, 1, 1)}, + {"br.cloop.spnt.clr", BRT (5, 0, 1, 1, PSEUDO)}, + {"br.cloop.dptk.few", BR (5, 0, 2, 0)}, + {"br.cloop.dptk", BRT (5, 0, 2, 0, PSEUDO)}, + {"br.cloop.dptk.few.clr", BR (5, 0, 2, 1)}, + {"br.cloop.dptk.clr", BRT (5, 0, 2, 1, PSEUDO)}, + {"br.cloop.dpnt.few", BR (5, 0, 3, 0)}, + {"br.cloop.dpnt", BRT (5, 0, 3, 0, PSEUDO)}, + {"br.cloop.dpnt.few.clr", BR (5, 0, 3, 1)}, + {"br.cloop.dpnt.clr", BRT (5, 0, 3, 1, PSEUDO)}, + {"br.cloop.sptk.many", BR (5, 1, 0, 0)}, + {"br.cloop.sptk.many.clr", BR (5, 1, 0, 1)}, + {"br.cloop.spnt.many", BR (5, 1, 1, 0)}, + {"br.cloop.spnt.many.clr", BR (5, 1, 1, 1)}, + {"br.cloop.dptk.many", BR (5, 1, 2, 0)}, + {"br.cloop.dptk.many.clr", BR (5, 1, 2, 1)}, + {"br.cloop.dpnt.many", BR (5, 1, 3, 0)}, + {"br.cloop.dpnt.many.clr", BR (5, 1, 3, 1)}, + {"br.cexit.sptk.few", BRT (6, 0, 0, 0, MOD_RRBS)}, + {"br.cexit.sptk", BRT (6, 0, 0, 0, PSEUDO | MOD_RRBS)}, + {"br.cexit.sptk.few.clr", BRT (6, 0, 0, 1, MOD_RRBS)}, + {"br.cexit.sptk.clr", BRT (6, 0, 0, 1, PSEUDO | MOD_RRBS)}, + {"br.cexit.spnt.few", BRT (6, 0, 1, 0, MOD_RRBS)}, + {"br.cexit.spnt", BRT (6, 0, 1, 0, PSEUDO | MOD_RRBS)}, + {"br.cexit.spnt.few.clr", BRT (6, 0, 1, 1, MOD_RRBS)}, + {"br.cexit.spnt.clr", BRT (6, 0, 1, 1, PSEUDO | MOD_RRBS)}, + {"br.cexit.dptk.few", BRT (6, 0, 2, 0, MOD_RRBS)}, + {"br.cexit.dptk", BRT (6, 0, 2, 0, PSEUDO | MOD_RRBS)}, + {"br.cexit.dptk.few.clr", BRT (6, 0, 2, 1, MOD_RRBS)}, + {"br.cexit.dptk.clr", BRT (6, 0, 2, 1, PSEUDO | MOD_RRBS)}, + {"br.cexit.dpnt.few", BRT (6, 0, 3, 0, MOD_RRBS)}, + {"br.cexit.dpnt", BRT (6, 0, 3, 0, PSEUDO | MOD_RRBS)}, + {"br.cexit.dpnt.few.clr", BRT (6, 0, 3, 1, MOD_RRBS)}, + {"br.cexit.dpnt.clr", BRT (6, 0, 3, 1, PSEUDO | MOD_RRBS)}, + {"br.cexit.sptk.many", BRT (6, 1, 0, 0, MOD_RRBS)}, + {"br.cexit.sptk.many.clr", BRT (6, 1, 0, 1, MOD_RRBS)}, + {"br.cexit.spnt.many", BRT (6, 1, 1, 0, MOD_RRBS)}, + {"br.cexit.spnt.many.clr", BRT (6, 1, 1, 1, MOD_RRBS)}, + {"br.cexit.dptk.many", BRT (6, 1, 2, 0, MOD_RRBS)}, + {"br.cexit.dptk.many.clr", BRT (6, 1, 2, 1, MOD_RRBS)}, + {"br.cexit.dpnt.many", BRT (6, 1, 3, 0, MOD_RRBS)}, + {"br.cexit.dpnt.many.clr", BRT (6, 1, 3, 1, MOD_RRBS)}, + {"br.ctop.sptk.few", BRT (7, 0, 0, 0, MOD_RRBS)}, + {"br.ctop.sptk", BRT (7, 0, 0, 0, PSEUDO | MOD_RRBS)}, + {"br.ctop.sptk.few.clr", BRT (7, 0, 0, 1, MOD_RRBS)}, + {"br.ctop.sptk.clr", BRT (7, 0, 0, 1, PSEUDO | MOD_RRBS)}, + {"br.ctop.spnt.few", BRT (7, 0, 1, 0, MOD_RRBS)}, + {"br.ctop.spnt", BRT (7, 0, 1, 0, PSEUDO | MOD_RRBS)}, + {"br.ctop.spnt.few.clr", BRT (7, 0, 1, 1, MOD_RRBS)}, + {"br.ctop.spnt.clr", BRT (7, 0, 1, 1, PSEUDO | MOD_RRBS)}, + {"br.ctop.dptk.few", BRT (7, 0, 2, 0, MOD_RRBS)}, + {"br.ctop.dptk", BRT (7, 0, 2, 0, PSEUDO | MOD_RRBS)}, + {"br.ctop.dptk.few.clr", BRT (7, 0, 2, 1, MOD_RRBS)}, + {"br.ctop.dptk.clr", BRT (7, 0, 2, 1, PSEUDO | MOD_RRBS)}, + {"br.ctop.dpnt.few", BRT (7, 0, 3, 0, MOD_RRBS)}, + {"br.ctop.dpnt", BRT (7, 0, 3, 0, PSEUDO | MOD_RRBS)}, + {"br.ctop.dpnt.few.clr", BRT (7, 0, 3, 1, MOD_RRBS)}, + {"br.ctop.dpnt.clr", BRT (7, 0, 3, 1, PSEUDO | MOD_RRBS)}, + {"br.ctop.sptk.many", BRT (7, 1, 0, 0, MOD_RRBS)}, + {"br.ctop.sptk.many.clr", BRT (7, 1, 0, 1, MOD_RRBS)}, + {"br.ctop.spnt.many", BRT (7, 1, 1, 0, MOD_RRBS)}, + {"br.ctop.spnt.many.clr", BRT (7, 1, 1, 1, MOD_RRBS)}, + {"br.ctop.dptk.many", BRT (7, 1, 2, 0, MOD_RRBS)}, + {"br.ctop.dptk.many.clr", BRT (7, 1, 2, 1, MOD_RRBS)}, + {"br.ctop.dpnt.many", BRT (7, 1, 3, 0, MOD_RRBS)}, + {"br.ctop.dpnt.many.clr", BRT (7, 1, 3, 1, MOD_RRBS)}, +#undef BR +#undef BRT + + {"br.call.sptk.few", B, OpPaWhaD (5, 0, 0, 0), {B1, TGT25c}, EMPTY}, + {"br.call.sptk", B, OpPaWhaD (5, 0, 0, 0), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.sptk.few.clr", B, OpPaWhaD (5, 0, 0, 1), {B1, TGT25c}, EMPTY}, + {"br.call.sptk.clr", B, OpPaWhaD (5, 0, 0, 1), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.spnt.few", B, OpPaWhaD (5, 0, 1, 0), {B1, TGT25c}, EMPTY}, + {"br.call.spnt", B, OpPaWhaD (5, 0, 1, 0), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.spnt.few.clr", B, OpPaWhaD (5, 0, 1, 1), {B1, TGT25c}, EMPTY}, + {"br.call.spnt.clr", B, OpPaWhaD (5, 0, 1, 1), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.dptk.few", B, OpPaWhaD (5, 0, 2, 0), {B1, TGT25c}, EMPTY}, + {"br.call.dptk", B, OpPaWhaD (5, 0, 2, 0), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.dptk.few.clr", B, OpPaWhaD (5, 0, 2, 1), {B1, TGT25c}, EMPTY}, + {"br.call.dptk.clr", B, OpPaWhaD (5, 0, 2, 1), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.dpnt.few", B, OpPaWhaD (5, 0, 3, 0), {B1, TGT25c}, EMPTY}, + {"br.call.dpnt", B, OpPaWhaD (5, 0, 3, 0), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.dpnt.few.clr", B, OpPaWhaD (5, 0, 3, 1), {B1, TGT25c}, EMPTY}, + {"br.call.dpnt.clr", B, OpPaWhaD (5, 0, 3, 1), {B1, TGT25c}, PSEUDO, 0, NULL}, + {"br.call.sptk.many", B, OpPaWhaD (5, 1, 0, 0), {B1, TGT25c}, EMPTY}, + {"br.call.sptk.many.clr", B, OpPaWhaD (5, 1, 0, 1), {B1, TGT25c}, EMPTY}, + {"br.call.spnt.many", B, OpPaWhaD (5, 1, 1, 0), {B1, TGT25c}, EMPTY}, + {"br.call.spnt.many.clr", B, OpPaWhaD (5, 1, 1, 1), {B1, TGT25c}, EMPTY}, + {"br.call.dptk.many", B, OpPaWhaD (5, 1, 2, 0), {B1, TGT25c}, EMPTY}, + {"br.call.dptk.many.clr", B, OpPaWhaD (5, 1, 2, 1), {B1, TGT25c}, EMPTY}, + {"br.call.dpnt.many", B, OpPaWhaD (5, 1, 3, 0), {B1, TGT25c}, EMPTY}, + {"br.call.dpnt.many.clr", B, OpPaWhaD (5, 1, 3, 1), {B1, TGT25c}, EMPTY}, + + /* Branch predict. */ +#define BRP(a,b) \ + B0, OpIhWhb (7, a, b), {TGT25c, TAG13}, NO_PRED, 0, NULL + {"brp.sptk", BRP (0, 0)}, + {"brp.loop", BRP (0, 1)}, + {"brp.dptk", BRP (0, 2)}, + {"brp.exit", BRP (0, 3)}, + {"brp.sptk.imp", BRP (1, 0)}, + {"brp.loop.imp", BRP (1, 1)}, + {"brp.dptk.imp", BRP (1, 2)}, + {"brp.exit.imp", BRP (1, 3)}, +#undef BRP + + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; + +#undef B0 +#undef B +#undef bBtype +#undef bD +#undef bIh +#undef bPa +#undef bPr +#undef bWha +#undef bWhb +#undef bWhc +#undef bX6 +#undef mBtype +#undef mD +#undef mIh +#undef mPa +#undef mPr +#undef mWha +#undef mWhb +#undef mWhc +#undef mX6 +#undef OpX6 +#undef OpPaWhaD +#undef OpPaWhcD +#undef OpBtypePaWhaD +#undef OpBtypePaWhaDPr +#undef OpX6BtypePaWhaD +#undef OpX6BtypePaWhaDPr +#undef OpIhWhb +#undef OpX6IhWhb +#undef EMPTY diff --git a/tools/debugger/xenitp/ia64-opc-d.c b/tools/debugger/xenitp/ia64-opc-d.c new file mode 100644 index 0000000..f0bd064 --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-d.c @@ -0,0 +1,34 @@ +/* ia64-opc-d.c -- IA-64 `D' opcode table. + Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +struct ia64_opcode ia64_opcodes_d[] = + { + {"add", IA64_TYPE_DYN, 1, 0, 0, {IA64_OPND_R1, IA64_OPND_IMM22, IA64_OPND_R3_2}, 0, 0, NULL}, + {"add", IA64_TYPE_DYN, 1, 0, 0, {IA64_OPND_R1, IA64_OPND_IMM14, IA64_OPND_R3}, 0, 0, NULL}, + {"break", IA64_TYPE_DYN, 0, 0, 0, {IA64_OPND_IMMU21}, 0, 0, NULL}, + {"chk.s", IA64_TYPE_DYN, 0, 0, 0, {IA64_OPND_R2, IA64_OPND_TGT25b}, 0, 0, NULL}, + {"hint", IA64_TYPE_DYN, 0, 0, 0, {IA64_OPND_IMMU21}, 0, 0, NULL}, + {"mov", IA64_TYPE_DYN, 1, 0, 0, {IA64_OPND_R1, IA64_OPND_AR3}, 0, 0, NULL}, + {"mov", IA64_TYPE_DYN, 1, 0, 0, {IA64_OPND_AR3, IA64_OPND_IMM8}, 0, 0, NULL}, + {"mov", IA64_TYPE_DYN, 1, 0, 0, {IA64_OPND_AR3, IA64_OPND_R2}, 0, 0, NULL}, + {"nop", IA64_TYPE_DYN, 0, 0, 0, {IA64_OPND_IMMU21}, 0, 0, NULL}, + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; diff --git a/tools/debugger/xenitp/ia64-opc-f.c b/tools/debugger/xenitp/ia64-opc-f.c new file mode 100644 index 0000000..0b13c9c --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-f.c @@ -0,0 +1,656 @@ +/* ia64-opc-f.c -- IA-64 `F' opcode table. + Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-opc.h" + +#define f0 IA64_TYPE_F, 0 +#define f IA64_TYPE_F, 1 +#define f2 IA64_TYPE_F, 2 + +#define bF2(x) (((ia64_insn) ((x) & 0x7f)) << 13) +#define bF4(x) (((ia64_insn) ((x) & 0x7f)) << 27) +#define bQ(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bRa(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bRb(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bSf(x) (((ia64_insn) ((x) & 0x3)) << 34) +#define bTa(x) (((ia64_insn) ((x) & 0x1)) << 12) +#define bXa(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bXb(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bX2(x) (((ia64_insn) ((x) & 0x3)) << 34) +#define bX6(x) (((ia64_insn) ((x) & 0x3f)) << 27) +#define bY(x) (((ia64_insn) ((x) & 0x1)) << 26) + +#define mF2 bF2 (-1) +#define mF4 bF4 (-1) +#define mQ bQ (-1) +#define mRa bRa (-1) +#define mRb bRb (-1) +#define mSf bSf (-1) +#define mTa bTa (-1) +#define mXa bXa (-1) +#define mXb bXb (-1) +#define mX2 bX2 (-1) +#define mX6 bX6 (-1) +#define mY bY (-1) + +#define OpXa(a,b) (bOp (a) | bXa (b)), (mOp | mXa) +#define OpXaSf(a,b,c) (bOp (a) | bXa (b) | bSf (c)), (mOp | mXa | mSf) +#define OpXaSfF2(a,b,c,d) \ + (bOp (a) | bXa (b) | bSf (c) | bF2 (d)), (mOp | mXa | mSf | mF2) +#define OpXaSfF4(a,b,c,d) \ + (bOp (a) | bXa (b) | bSf (c) | bF4 (d)), (mOp | mXa | mSf | mF4) +#define OpXaSfF2F4(a,b,c,d,e) \ + (bOp (a) | bXa (b) | bSf (c) | bF2 (d) | bF4 (e)), \ + (mOp | mXa | mSf | mF2 | mF4) +#define OpXaX2(a,b,c) (bOp (a) | bXa (b) | bX2 (c)), (mOp | mXa | mX2) +#define OpXaX2F2(a,b,c,d) \ + (bOp (a) | bXa (b) | bX2 (c) | bF2 (d)), (mOp | mXa | mX2 | mF2) +#define OpRaRbTaSf(a,b,c,d,e) \ + (bOp (a) | bRa (b) | bRb (c) | bTa (d) | bSf (e)), \ + (mOp | mRa | mRb | mTa | mSf) +#define OpTa(a,b) (bOp (a) | bTa (b)), (mOp | mTa) +#define OpXbQSf(a,b,c,d) \ + (bOp (a) | bXb (b) | bQ (c) | bSf (d)), (mOp | mXb | mQ | mSf) +#define OpXbX6(a,b,c) \ + (bOp (a) | bXb (b) | bX6 (c)), (mOp | mXb | mX6) +#define OpXbX6Y(a,b,c,d) \ + (bOp (a) | bXb (b) | bX6 (c) | bY (d)), (mOp | mXb | mX6 | mY) +#define OpXbX6F2(a,b,c,d) \ + (bOp (a) | bXb (b) | bX6 (c) | bF2 (d)), (mOp | mXb | mX6 | mF2) +#define OpXbX6Sf(a,b,c,d) \ + (bOp (a) | bXb (b) | bX6 (c) | bSf (d)), (mOp | mXb | mX6 | mSf) + +/* Used to initialise unused fields in ia64_opcode struct, + in order to stop gcc from complaining. */ +#define EMPTY 0,0,NULL + +struct ia64_opcode ia64_opcodes_f[] = + { + /* F-type instruction encodings (sorted according to major opcode). */ + + {"frcpa.s0", f2, OpXbQSf (0, 1, 0, 0), {F1, P2, F2, F3}, EMPTY}, + {"frcpa", f2, OpXbQSf (0, 1, 0, 0), {F1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"frcpa.s1", f2, OpXbQSf (0, 1, 0, 1), {F1, P2, F2, F3}, EMPTY}, + {"frcpa.s2", f2, OpXbQSf (0, 1, 0, 2), {F1, P2, F2, F3}, EMPTY}, + {"frcpa.s3", f2, OpXbQSf (0, 1, 0, 3), {F1, P2, F2, F3}, EMPTY}, + + {"frsqrta.s0", f2, OpXbQSf (0, 1, 1, 0), {F1, P2, F3}, EMPTY}, + {"frsqrta", f2, OpXbQSf (0, 1, 1, 0), {F1, P2, F3}, PSEUDO, 0, NULL}, + {"frsqrta.s1", f2, OpXbQSf (0, 1, 1, 1), {F1, P2, F3}, EMPTY}, + {"frsqrta.s2", f2, OpXbQSf (0, 1, 1, 2), {F1, P2, F3}, EMPTY}, + {"frsqrta.s3", f2, OpXbQSf (0, 1, 1, 3), {F1, P2, F3}, EMPTY}, + + {"fmin.s0", f, OpXbX6Sf (0, 0, 0x14, 0), {F1, F2, F3}, EMPTY}, + {"fmin", f, OpXbX6Sf (0, 0, 0x14, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fmin.s1", f, OpXbX6Sf (0, 0, 0x14, 1), {F1, F2, F3}, EMPTY}, + {"fmin.s2", f, OpXbX6Sf (0, 0, 0x14, 2), {F1, F2, F3}, EMPTY}, + {"fmin.s3", f, OpXbX6Sf (0, 0, 0x14, 3), {F1, F2, F3}, EMPTY}, + {"fmax.s0", f, OpXbX6Sf (0, 0, 0x15, 0), {F1, F2, F3}, EMPTY}, + {"fmax", f, OpXbX6Sf (0, 0, 0x15, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fmax.s1", f, OpXbX6Sf (0, 0, 0x15, 1), {F1, F2, F3}, EMPTY}, + {"fmax.s2", f, OpXbX6Sf (0, 0, 0x15, 2), {F1, F2, F3}, EMPTY}, + {"fmax.s3", f, OpXbX6Sf (0, 0, 0x15, 3), {F1, F2, F3}, EMPTY}, + {"famin.s0", f, OpXbX6Sf (0, 0, 0x16, 0), {F1, F2, F3}, EMPTY}, + {"famin", f, OpXbX6Sf (0, 0, 0x16, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"famin.s1", f, OpXbX6Sf (0, 0, 0x16, 1), {F1, F2, F3}, EMPTY}, + {"famin.s2", f, OpXbX6Sf (0, 0, 0x16, 2), {F1, F2, F3}, EMPTY}, + {"famin.s3", f, OpXbX6Sf (0, 0, 0x16, 3), {F1, F2, F3}, EMPTY}, + {"famax.s0", f, OpXbX6Sf (0, 0, 0x17, 0), {F1, F2, F3}, EMPTY}, + {"famax", f, OpXbX6Sf (0, 0, 0x17, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"famax.s1", f, OpXbX6Sf (0, 0, 0x17, 1), {F1, F2, F3}, EMPTY}, + {"famax.s2", f, OpXbX6Sf (0, 0, 0x17, 2), {F1, F2, F3}, EMPTY}, + {"famax.s3", f, OpXbX6Sf (0, 0, 0x17, 3), {F1, F2, F3}, EMPTY}, + + {"mov", f, OpXbX6 (0, 0, 0x10), {F1, F3}, PSEUDO | F2_EQ_F3, 0, NULL}, + {"fabs", f, OpXbX6F2 (0, 0, 0x10, 0), {F1, F3}, PSEUDO, 0, NULL}, + {"fneg", f, OpXbX6 (0, 0, 0x11), {F1, F3}, PSEUDO | F2_EQ_F3, 0, NULL}, + {"fnegabs", f, OpXbX6F2 (0, 0, 0x11, 0), {F1, F3}, PSEUDO, 0, NULL}, + {"fmerge.s", f, OpXbX6 (0, 0, 0x10), {F1, F2, F3}, EMPTY}, + {"fmerge.ns", f, OpXbX6 (0, 0, 0x11), {F1, F2, F3}, EMPTY}, + + {"fmerge.se", f, OpXbX6 (0, 0, 0x12), {F1, F2, F3}, EMPTY}, + {"fmix.lr", f, OpXbX6 (0, 0, 0x39), {F1, F2, F3}, EMPTY}, + {"fmix.r", f, OpXbX6 (0, 0, 0x3a), {F1, F2, F3}, EMPTY}, + {"fmix.l", f, OpXbX6 (0, 0, 0x3b), {F1, F2, F3}, EMPTY}, + {"fsxt.r", f, OpXbX6 (0, 0, 0x3c), {F1, F2, F3}, EMPTY}, + {"fsxt.l", f, OpXbX6 (0, 0, 0x3d), {F1, F2, F3}, EMPTY}, + {"fpack", f, OpXbX6 (0, 0, 0x28), {F1, F2, F3}, EMPTY}, + {"fswap", f, OpXbX6 (0, 0, 0x34), {F1, F2, F3}, EMPTY}, + {"fswap.nl", f, OpXbX6 (0, 0, 0x35), {F1, F2, F3}, EMPTY}, + {"fswap.nr", f, OpXbX6 (0, 0, 0x36), {F1, F2, F3}, EMPTY}, + {"fand", f, OpXbX6 (0, 0, 0x2c), {F1, F2, F3}, EMPTY}, + {"fandcm", f, OpXbX6 (0, 0, 0x2d), {F1, F2, F3}, EMPTY}, + {"for", f, OpXbX6 (0, 0, 0x2e), {F1, F2, F3}, EMPTY}, + {"fxor", f, OpXbX6 (0, 0, 0x2f), {F1, F2, F3}, EMPTY}, + + {"fcvt.fx.s0", f, OpXbX6Sf (0, 0, 0x18, 0), {F1, F2}, EMPTY}, + {"fcvt.fx", f, OpXbX6Sf (0, 0, 0x18, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fcvt.fx.s1", f, OpXbX6Sf (0, 0, 0x18, 1), {F1, F2}, EMPTY}, + {"fcvt.fx.s2", f, OpXbX6Sf (0, 0, 0x18, 2), {F1, F2}, EMPTY}, + {"fcvt.fx.s3", f, OpXbX6Sf (0, 0, 0x18, 3), {F1, F2}, EMPTY}, + {"fcvt.fxu.s0", f, OpXbX6Sf (0, 0, 0x19, 0), {F1, F2}, EMPTY}, + {"fcvt.fxu", f, OpXbX6Sf (0, 0, 0x19, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fcvt.fxu.s1", f, OpXbX6Sf (0, 0, 0x19, 1), {F1, F2}, EMPTY}, + {"fcvt.fxu.s2", f, OpXbX6Sf (0, 0, 0x19, 2), {F1, F2}, EMPTY}, + {"fcvt.fxu.s3", f, OpXbX6Sf (0, 0, 0x19, 3), {F1, F2}, EMPTY}, + {"fcvt.fx.trunc.s0", f, OpXbX6Sf (0, 0, 0x1a, 0), {F1, F2}, EMPTY}, + {"fcvt.fx.trunc", f, OpXbX6Sf (0, 0, 0x1a, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fcvt.fx.trunc.s1", f, OpXbX6Sf (0, 0, 0x1a, 1), {F1, F2}, EMPTY}, + {"fcvt.fx.trunc.s2", f, OpXbX6Sf (0, 0, 0x1a, 2), {F1, F2}, EMPTY}, + {"fcvt.fx.trunc.s3", f, OpXbX6Sf (0, 0, 0x1a, 3), {F1, F2}, EMPTY}, + {"fcvt.fxu.trunc.s0", f, OpXbX6Sf (0, 0, 0x1b, 0), {F1, F2}, EMPTY}, + {"fcvt.fxu.trunc", f, OpXbX6Sf (0, 0, 0x1b, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fcvt.fxu.trunc.s1", f, OpXbX6Sf (0, 0, 0x1b, 1), {F1, F2}, EMPTY}, + {"fcvt.fxu.trunc.s2", f, OpXbX6Sf (0, 0, 0x1b, 2), {F1, F2}, EMPTY}, + {"fcvt.fxu.trunc.s3", f, OpXbX6Sf (0, 0, 0x1b, 3), {F1, F2}, EMPTY}, + + {"fcvt.xf", f, OpXbX6 (0, 0, 0x1c), {F1, F2}, EMPTY}, + + {"fsetc.s0", f0, OpXbX6Sf (0, 0, 0x04, 0), {IMMU7a, IMMU7b}, EMPTY}, + {"fsetc", f0, OpXbX6Sf (0, 0, 0x04, 0), {IMMU7a, IMMU7b}, PSEUDO, 0, NULL}, + {"fsetc.s1", f0, OpXbX6Sf (0, 0, 0x04, 1), {IMMU7a, IMMU7b}, EMPTY}, + {"fsetc.s2", f0, OpXbX6Sf (0, 0, 0x04, 2), {IMMU7a, IMMU7b}, EMPTY}, + {"fsetc.s3", f0, OpXbX6Sf (0, 0, 0x04, 3), {IMMU7a, IMMU7b}, EMPTY}, + {"fclrf.s0", f0, OpXbX6Sf (0, 0, 0x05, 0), {}, EMPTY}, + {"fclrf", f0, OpXbX6Sf (0, 0, 0x05, 0), {0}, PSEUDO, 0, NULL}, + {"fclrf.s1", f0, OpXbX6Sf (0, 0, 0x05, 1), {}, EMPTY}, + {"fclrf.s2", f0, OpXbX6Sf (0, 0, 0x05, 2), {}, EMPTY}, + {"fclrf.s3", f0, OpXbX6Sf (0, 0, 0x05, 3), {}, EMPTY}, + {"fchkf.s0", f0, OpXbX6Sf (0, 0, 0x08, 0), {TGT25}, EMPTY}, + {"fchkf", f0, OpXbX6Sf (0, 0, 0x08, 0), {TGT25}, PSEUDO, 0, NULL}, + {"fchkf.s1", f0, OpXbX6Sf (0, 0, 0x08, 1), {TGT25}, EMPTY}, + {"fchkf.s2", f0, OpXbX6Sf (0, 0, 0x08, 2), {TGT25}, EMPTY}, + {"fchkf.s3", f0, OpXbX6Sf (0, 0, 0x08, 3), {TGT25}, EMPTY}, + + {"break.f", f0, OpXbX6 (0, 0, 0x00), {IMMU21}, EMPTY}, + {"nop.f", f0, OpXbX6Y (0, 0, 0x01, 0), {IMMU21}, EMPTY}, + {"hint.f", f0, OpXbX6Y (0, 0, 0x01, 1), {IMMU21}, EMPTY}, + + {"fprcpa.s0", f2, OpXbQSf (1, 1, 0, 0), {F1, P2, F2, F3}, EMPTY}, + {"fprcpa", f2, OpXbQSf (1, 1, 0, 0), {F1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fprcpa.s1", f2, OpXbQSf (1, 1, 0, 1), {F1, P2, F2, F3}, EMPTY}, + {"fprcpa.s2", f2, OpXbQSf (1, 1, 0, 2), {F1, P2, F2, F3}, EMPTY}, + {"fprcpa.s3", f2, OpXbQSf (1, 1, 0, 3), {F1, P2, F2, F3}, EMPTY}, + + {"fprsqrta.s0", f2, OpXbQSf (1, 1, 1, 0), {F1, P2, F3}, EMPTY}, + {"fprsqrta", f2, OpXbQSf (1, 1, 1, 0), {F1, P2, F3}, PSEUDO, 0, NULL}, + {"fprsqrta.s1", f2, OpXbQSf (1, 1, 1, 1), {F1, P2, F3}, EMPTY}, + {"fprsqrta.s2", f2, OpXbQSf (1, 1, 1, 2), {F1, P2, F3}, EMPTY}, + {"fprsqrta.s3", f2, OpXbQSf (1, 1, 1, 3), {F1, P2, F3}, EMPTY}, + + {"fpmin.s0", f, OpXbX6Sf (1, 0, 0x14, 0), {F1, F2, F3}, EMPTY}, + {"fpmin", f, OpXbX6Sf (1, 0, 0x14, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpmin.s1", f, OpXbX6Sf (1, 0, 0x14, 1), {F1, F2, F3}, EMPTY}, + {"fpmin.s2", f, OpXbX6Sf (1, 0, 0x14, 2), {F1, F2, F3}, EMPTY}, + {"fpmin.s3", f, OpXbX6Sf (1, 0, 0x14, 3), {F1, F2, F3}, EMPTY}, + {"fpmax.s0", f, OpXbX6Sf (1, 0, 0x15, 0), {F1, F2, F3}, EMPTY}, + {"fpmax", f, OpXbX6Sf (1, 0, 0x15, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpmax.s1", f, OpXbX6Sf (1, 0, 0x15, 1), {F1, F2, F3}, EMPTY}, + {"fpmax.s2", f, OpXbX6Sf (1, 0, 0x15, 2), {F1, F2, F3}, EMPTY}, + {"fpmax.s3", f, OpXbX6Sf (1, 0, 0x15, 3), {F1, F2, F3}, EMPTY}, + {"fpamin.s0", f, OpXbX6Sf (1, 0, 0x16, 0), {F1, F2, F3}, EMPTY}, + {"fpamin", f, OpXbX6Sf (1, 0, 0x16, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpamin.s1", f, OpXbX6Sf (1, 0, 0x16, 1), {F1, F2, F3}, EMPTY}, + {"fpamin.s2", f, OpXbX6Sf (1, 0, 0x16, 2), {F1, F2, F3}, EMPTY}, + {"fpamin.s3", f, OpXbX6Sf (1, 0, 0x16, 3), {F1, F2, F3}, EMPTY}, + {"fpamax.s0", f, OpXbX6Sf (1, 0, 0x17, 0), {F1, F2, F3}, EMPTY}, + {"fpamax", f, OpXbX6Sf (1, 0, 0x17, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpamax.s1", f, OpXbX6Sf (1, 0, 0x17, 1), {F1, F2, F3}, EMPTY}, + {"fpamax.s2", f, OpXbX6Sf (1, 0, 0x17, 2), {F1, F2, F3}, EMPTY}, + {"fpamax.s3", f, OpXbX6Sf (1, 0, 0x17, 3), {F1, F2, F3}, EMPTY}, + + {"fpcmp.eq.s0", f, OpXbX6Sf (1, 0, 0x30, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.eq", f, OpXbX6Sf (1, 0, 0x30, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.eq.s1", f, OpXbX6Sf (1, 0, 0x30, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.eq.s2", f, OpXbX6Sf (1, 0, 0x30, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.eq.s3", f, OpXbX6Sf (1, 0, 0x30, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.lt.s0", f, OpXbX6Sf (1, 0, 0x31, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.lt", f, OpXbX6Sf (1, 0, 0x31, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.lt.s1", f, OpXbX6Sf (1, 0, 0x31, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.lt.s2", f, OpXbX6Sf (1, 0, 0x31, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.lt.s3", f, OpXbX6Sf (1, 0, 0x31, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.le.s0", f, OpXbX6Sf (1, 0, 0x32, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.le", f, OpXbX6Sf (1, 0, 0x32, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.le.s1", f, OpXbX6Sf (1, 0, 0x32, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.le.s2", f, OpXbX6Sf (1, 0, 0x32, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.le.s3", f, OpXbX6Sf (1, 0, 0x32, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.gt.s0", f, OpXbX6Sf (1, 0, 0x31, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.gt", f, OpXbX6Sf (1, 0, 0x31, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.gt.s1", f, OpXbX6Sf (1, 0, 0x31, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.gt.s2", f, OpXbX6Sf (1, 0, 0x31, 2), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.gt.s3", f, OpXbX6Sf (1, 0, 0x31, 3), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ge.s0", f, OpXbX6Sf (1, 0, 0x32, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ge", f, OpXbX6Sf (1, 0, 0x32, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ge.s1", f, OpXbX6Sf (1, 0, 0x32, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ge.s2", f, OpXbX6Sf (1, 0, 0x32, 2), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ge.s3", f, OpXbX6Sf (1, 0, 0x32, 3), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.unord.s0", f, OpXbX6Sf (1, 0, 0x33, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.unord", f, OpXbX6Sf (1, 0, 0x33, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.unord.s1", f, OpXbX6Sf (1, 0, 0x33, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.unord.s2", f, OpXbX6Sf (1, 0, 0x33, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.unord.s3", f, OpXbX6Sf (1, 0, 0x33, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.neq.s0", f, OpXbX6Sf (1, 0, 0x34, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.neq", f, OpXbX6Sf (1, 0, 0x34, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.neq.s1", f, OpXbX6Sf (1, 0, 0x34, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.neq.s2", f, OpXbX6Sf (1, 0, 0x34, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.neq.s3", f, OpXbX6Sf (1, 0, 0x34, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.nlt.s0", f, OpXbX6Sf (1, 0, 0x35, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.nlt", f, OpXbX6Sf (1, 0, 0x35, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.nlt.s1", f, OpXbX6Sf (1, 0, 0x35, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.nlt.s2", f, OpXbX6Sf (1, 0, 0x35, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.nlt.s3", f, OpXbX6Sf (1, 0, 0x35, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.nle.s0", f, OpXbX6Sf (1, 0, 0x36, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.nle", f, OpXbX6Sf (1, 0, 0x36, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.nle.s1", f, OpXbX6Sf (1, 0, 0x36, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.nle.s2", f, OpXbX6Sf (1, 0, 0x36, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.nle.s3", f, OpXbX6Sf (1, 0, 0x36, 3), {F1, F2, F3}, EMPTY}, + {"fpcmp.ngt.s0", f, OpXbX6Sf (1, 0, 0x35, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ngt", f, OpXbX6Sf (1, 0, 0x35, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ngt.s1", f, OpXbX6Sf (1, 0, 0x35, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ngt.s2", f, OpXbX6Sf (1, 0, 0x35, 2), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ngt.s3", f, OpXbX6Sf (1, 0, 0x35, 3), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.nge.s0", f, OpXbX6Sf (1, 0, 0x36, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.nge", f, OpXbX6Sf (1, 0, 0x36, 0), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.nge.s1", f, OpXbX6Sf (1, 0, 0x36, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.nge.s2", f, OpXbX6Sf (1, 0, 0x36, 2), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.nge.s3", f, OpXbX6Sf (1, 0, 0x36, 3), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fpcmp.ord.s0", f, OpXbX6Sf (1, 0, 0x37, 0), {F1, F2, F3}, EMPTY}, + {"fpcmp.ord", f, OpXbX6Sf (1, 0, 0x37, 0), {F1, F2, F3}, PSEUDO, 0, NULL}, + {"fpcmp.ord.s1", f, OpXbX6Sf (1, 0, 0x37, 1), {F1, F2, F3}, EMPTY}, + {"fpcmp.ord.s2", f, OpXbX6Sf (1, 0, 0x37, 2), {F1, F2, F3}, EMPTY}, + {"fpcmp.ord.s3", f, OpXbX6Sf (1, 0, 0x37, 3), {F1, F2, F3}, EMPTY}, + + {"fpabs", f, OpXbX6F2 (1, 0, 0x10, 0), {F1, F3}, PSEUDO, 0, NULL}, + {"fpneg", f, OpXbX6 (1, 0, 0x11), {F1, F3}, PSEUDO | F2_EQ_F3, 0, NULL}, + {"fpnegabs", f, OpXbX6F2 (1, 0, 0x11, 0), {F1, F3}, PSEUDO, 0, NULL}, + {"fpmerge.s", f, OpXbX6 (1, 0, 0x10), {F1, F2, F3}, EMPTY}, + {"fpmerge.ns", f, OpXbX6 (1, 0, 0x11), {F1, F2, F3}, EMPTY}, + {"fpmerge.se", f, OpXbX6 (1, 0, 0x12), {F1, F2, F3}, EMPTY}, + + {"fpcvt.fx.s0", f, OpXbX6Sf (1, 0, 0x18, 0), {F1, F2}, EMPTY}, + {"fpcvt.fx", f, OpXbX6Sf (1, 0, 0x18, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fpcvt.fx.s1", f, OpXbX6Sf (1, 0, 0x18, 1), {F1, F2}, EMPTY}, + {"fpcvt.fx.s2", f, OpXbX6Sf (1, 0, 0x18, 2), {F1, F2}, EMPTY}, + {"fpcvt.fx.s3", f, OpXbX6Sf (1, 0, 0x18, 3), {F1, F2}, EMPTY}, + {"fpcvt.fxu.s0", f, OpXbX6Sf (1, 0, 0x19, 0), {F1, F2}, EMPTY}, + {"fpcvt.fxu", f, OpXbX6Sf (1, 0, 0x19, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fpcvt.fxu.s1", f, OpXbX6Sf (1, 0, 0x19, 1), {F1, F2}, EMPTY}, + {"fpcvt.fxu.s2", f, OpXbX6Sf (1, 0, 0x19, 2), {F1, F2}, EMPTY}, + {"fpcvt.fxu.s3", f, OpXbX6Sf (1, 0, 0x19, 3), {F1, F2}, EMPTY}, + {"fpcvt.fx.trunc.s0", f, OpXbX6Sf (1, 0, 0x1a, 0), {F1, F2}, EMPTY}, + {"fpcvt.fx.trunc", f, OpXbX6Sf (1, 0, 0x1a, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fpcvt.fx.trunc.s1", f, OpXbX6Sf (1, 0, 0x1a, 1), {F1, F2}, EMPTY}, + {"fpcvt.fx.trunc.s2", f, OpXbX6Sf (1, 0, 0x1a, 2), {F1, F2}, EMPTY}, + {"fpcvt.fx.trunc.s3", f, OpXbX6Sf (1, 0, 0x1a, 3), {F1, F2}, EMPTY}, + {"fpcvt.fxu.trunc.s0", f, OpXbX6Sf (1, 0, 0x1b, 0), {F1, F2}, EMPTY}, + {"fpcvt.fxu.trunc", f, OpXbX6Sf (1, 0, 0x1b, 0), {F1, F2}, PSEUDO, 0, NULL}, + {"fpcvt.fxu.trunc.s1", f, OpXbX6Sf (1, 0, 0x1b, 1), {F1, F2}, EMPTY}, + {"fpcvt.fxu.trunc.s2", f, OpXbX6Sf (1, 0, 0x1b, 2), {F1, F2}, EMPTY}, + {"fpcvt.fxu.trunc.s3", f, OpXbX6Sf (1, 0, 0x1b, 3), {F1, F2}, EMPTY}, + + {"fcmp.eq.s0", f2, OpRaRbTaSf (4, 0, 0, 0, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq", f2, OpRaRbTaSf (4, 0, 0, 0, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.eq.s1", f2, OpRaRbTaSf (4, 0, 0, 0, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq.s2", f2, OpRaRbTaSf (4, 0, 0, 0, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq.s3", f2, OpRaRbTaSf (4, 0, 0, 0, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.s0", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.lt.s1", f2, OpRaRbTaSf (4, 0, 1, 0, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.s2", f2, OpRaRbTaSf (4, 0, 1, 0, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.s3", f2, OpRaRbTaSf (4, 0, 1, 0, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.s0", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.le.s1", f2, OpRaRbTaSf (4, 1, 0, 0, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.s2", f2, OpRaRbTaSf (4, 1, 0, 0, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.s3", f2, OpRaRbTaSf (4, 1, 0, 0, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.s0", f2, OpRaRbTaSf (4, 1, 1, 0, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord", f2, OpRaRbTaSf (4, 1, 1, 0, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.unord.s1", f2, OpRaRbTaSf (4, 1, 1, 0, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.s2", f2, OpRaRbTaSf (4, 1, 1, 0, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.s3", f2, OpRaRbTaSf (4, 1, 1, 0, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq.unc.s0", f2, OpRaRbTaSf (4, 0, 0, 1, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq.unc", f2, OpRaRbTaSf (4, 0, 0, 1, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.eq.unc.s1", f2, OpRaRbTaSf (4, 0, 0, 1, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq.unc.s2", f2, OpRaRbTaSf (4, 0, 0, 1, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.eq.unc.s3", f2, OpRaRbTaSf (4, 0, 0, 1, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.unc.s0", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.unc", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.lt.unc.s1", f2, OpRaRbTaSf (4, 0, 1, 1, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.unc.s2", f2, OpRaRbTaSf (4, 0, 1, 1, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.lt.unc.s3", f2, OpRaRbTaSf (4, 0, 1, 1, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.unc.s0", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.unc", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.le.unc.s1", f2, OpRaRbTaSf (4, 1, 0, 1, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.unc.s2", f2, OpRaRbTaSf (4, 1, 0, 1, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.le.unc.s3", f2, OpRaRbTaSf (4, 1, 0, 1, 3), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.unc.s0", f2, OpRaRbTaSf (4, 1, 1, 1, 0), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.unc", f2, OpRaRbTaSf (4, 1, 1, 1, 0), {P1, P2, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.unord.unc.s1", f2, OpRaRbTaSf (4, 1, 1, 1, 1), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.unc.s2", f2, OpRaRbTaSf (4, 1, 1, 1, 2), {P1, P2, F2, F3}, EMPTY}, + {"fcmp.unord.unc.s3", f2, OpRaRbTaSf (4, 1, 1, 1, 3), {P1, P2, F2, F3}, EMPTY}, + + /* pseudo-ops of the above */ + {"fcmp.gt.s0", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.gt", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P1, P2, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.gt.s1", f2, OpRaRbTaSf (4, 0, 1, 0, 1), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.gt.s2", f2, OpRaRbTaSf (4, 0, 1, 0, 2), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.gt.s3", f2, OpRaRbTaSf (4, 0, 1, 0, 3), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.s0", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P1, P2, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.ge.s1", f2, OpRaRbTaSf (4, 1, 0, 0, 1), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.s2", f2, OpRaRbTaSf (4, 1, 0, 0, 2), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.s3", f2, OpRaRbTaSf (4, 1, 0, 0, 3), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.neq.s0", f2, OpRaRbTaSf (4, 0, 0, 0, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.neq", f2, OpRaRbTaSf (4, 0, 0, 0, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.neq.s1", f2, OpRaRbTaSf (4, 0, 0, 0, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.neq.s2", f2, OpRaRbTaSf (4, 0, 0, 0, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.neq.s3", f2, OpRaRbTaSf (4, 0, 0, 0, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.s0", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.nlt.s1", f2, OpRaRbTaSf (4, 0, 1, 0, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.s2", f2, OpRaRbTaSf (4, 0, 1, 0, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.s3", f2, OpRaRbTaSf (4, 0, 1, 0, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.s0", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.nle.s1", f2, OpRaRbTaSf (4, 1, 0, 0, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.s2", f2, OpRaRbTaSf (4, 1, 0, 0, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.s3", f2, OpRaRbTaSf (4, 1, 0, 0, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ngt.s0", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ngt", f2, OpRaRbTaSf (4, 0, 1, 0, 0), {P2, P1, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.ngt.s1", f2, OpRaRbTaSf (4, 0, 1, 0, 1), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ngt.s2", f2, OpRaRbTaSf (4, 0, 1, 0, 2), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ngt.s3", f2, OpRaRbTaSf (4, 0, 1, 0, 3), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.s0", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge", f2, OpRaRbTaSf (4, 1, 0, 0, 0), {P2, P1, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.nge.s1", f2, OpRaRbTaSf (4, 1, 0, 0, 1), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.s2", f2, OpRaRbTaSf (4, 1, 0, 0, 2), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.s3", f2, OpRaRbTaSf (4, 1, 0, 0, 3), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ord.s0", f2, OpRaRbTaSf (4, 1, 1, 0, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ord", f2, OpRaRbTaSf (4, 1, 1, 0, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.ord.s1", f2, OpRaRbTaSf (4, 1, 1, 0, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ord.s2", f2, OpRaRbTaSf (4, 1, 1, 0, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ord.s3", f2, OpRaRbTaSf (4, 1, 1, 0, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.gt.unc.s0", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.gt.unc", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P1, P2, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.gt.unc.s1", f2, OpRaRbTaSf (4, 0, 1, 1, 1), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.gt.unc.s2", f2, OpRaRbTaSf (4, 0, 1, 1, 2), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.gt.unc.s3", f2, OpRaRbTaSf (4, 0, 1, 1, 3), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.unc.s0", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.unc", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P1, P2, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.ge.unc.s1", f2, OpRaRbTaSf (4, 1, 0, 1, 1), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.unc.s2", f2, OpRaRbTaSf (4, 1, 0, 1, 2), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.ge.unc.s3", f2, OpRaRbTaSf (4, 1, 0, 1, 3), {P1, P2, F3, F2}, EMPTY}, + {"fcmp.neq.unc.s0", f2, OpRaRbTaSf (4, 0, 0, 1, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.neq.unc", f2, OpRaRbTaSf (4, 0, 0, 1, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.neq.unc.s1", f2, OpRaRbTaSf (4, 0, 0, 1, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.neq.unc.s2", f2, OpRaRbTaSf (4, 0, 0, 1, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.neq.unc.s3", f2, OpRaRbTaSf (4, 0, 0, 1, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.unc.s0", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.unc", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.nlt.unc.s1", f2, OpRaRbTaSf (4, 0, 1, 1, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.unc.s2", f2, OpRaRbTaSf (4, 0, 1, 1, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nlt.unc.s3", f2, OpRaRbTaSf (4, 0, 1, 1, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.unc.s0", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.unc", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.nle.unc.s1", f2, OpRaRbTaSf (4, 1, 0, 1, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.unc.s2", f2, OpRaRbTaSf (4, 1, 0, 1, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.nle.unc.s3", f2, OpRaRbTaSf (4, 1, 0, 1, 3), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ngt.unc.s0", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ngt.unc", f2, OpRaRbTaSf (4, 0, 1, 1, 0), {P2, P1, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.ngt.unc.s1", f2, OpRaRbTaSf (4, 0, 1, 1, 1), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ngt.unc.s2", f2, OpRaRbTaSf (4, 0, 1, 1, 2), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ngt.unc.s3", f2, OpRaRbTaSf (4, 0, 1, 1, 3), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.unc.s0", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.unc", f2, OpRaRbTaSf (4, 1, 0, 1, 0), {P2, P1, F3, F2}, PSEUDO, 0, NULL}, + {"fcmp.nge.unc.s1", f2, OpRaRbTaSf (4, 1, 0, 1, 1), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.unc.s2", f2, OpRaRbTaSf (4, 1, 0, 1, 2), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.nge.unc.s3", f2, OpRaRbTaSf (4, 1, 0, 1, 3), {P2, P1, F3, F2}, EMPTY}, + {"fcmp.ord.unc.s0", f2, OpRaRbTaSf (4, 1, 1, 1, 0), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ord.unc", f2, OpRaRbTaSf (4, 1, 1, 1, 0), {P2, P1, F2, F3}, PSEUDO, 0, NULL}, + {"fcmp.ord.unc.s1", f2, OpRaRbTaSf (4, 1, 1, 1, 1), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ord.unc.s2", f2, OpRaRbTaSf (4, 1, 1, 1, 2), {P2, P1, F2, F3}, EMPTY}, + {"fcmp.ord.unc.s3", f2, OpRaRbTaSf (4, 1, 1, 1, 3), {P2, P1, F2, F3}, EMPTY}, + + {"fclass.m", f2, OpTa (5, 0), {P1, P2, F2, IMMU9}, EMPTY}, + {"fclass.nm", f2, OpTa (5, 0), {P2, P1, F2, IMMU9}, PSEUDO, 0, NULL}, + {"fclass.m.unc", f2, OpTa (5, 1), {P1, P2, F2, IMMU9}, EMPTY}, + {"fclass.nm.unc", f2, OpTa (5, 1), {P2, P1, F2, IMMU9}, PSEUDO, 0, NULL}, + + /* note: fnorm and fcvt.xuf have identical encodings! */ + {"fnorm.s0", f, OpXaSfF2F4 (0x8, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm", f, OpXaSfF2F4 (0x8, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s1", f, OpXaSfF2F4 (0x8, 0, 1, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s2", f, OpXaSfF2F4 (0x8, 0, 2, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s3", f, OpXaSfF2F4 (0x8, 0, 3, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s.s0", f, OpXaSfF2F4 (0x8, 1, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s", f, OpXaSfF2F4 (0x8, 1, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s.s1", f, OpXaSfF2F4 (0x8, 1, 1, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s.s2", f, OpXaSfF2F4 (0x8, 1, 2, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.s.s3", f, OpXaSfF2F4 (0x8, 1, 3, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s0", f, OpXaSfF2F4 (0x8, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf", f, OpXaSfF2F4 (0x8, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s1", f, OpXaSfF2F4 (0x8, 0, 1, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s2", f, OpXaSfF2F4 (0x8, 0, 2, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s3", f, OpXaSfF2F4 (0x8, 0, 3, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s.s0", f, OpXaSfF2F4 (0x8, 1, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s", f, OpXaSfF2F4 (0x8, 1, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s.s1", f, OpXaSfF2F4 (0x8, 1, 1, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s.s2", f, OpXaSfF2F4 (0x8, 1, 2, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.s.s3", f, OpXaSfF2F4 (0x8, 1, 3, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fadd.s0", f, OpXaSfF4 (0x8, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd", f, OpXaSfF4 (0x8, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s1", f, OpXaSfF4 (0x8, 0, 1, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s2", f, OpXaSfF4 (0x8, 0, 2, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s3", f, OpXaSfF4 (0x8, 0, 3, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s.s0", f, OpXaSfF4 (0x8, 1, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s", f, OpXaSfF4 (0x8, 1, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s.s1", f, OpXaSfF4 (0x8, 1, 1, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s.s2", f, OpXaSfF4 (0x8, 1, 2, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.s.s3", f, OpXaSfF4 (0x8, 1, 3, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fmpy.s0", f, OpXaSfF2 (0x8, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy", f, OpXaSfF2 (0x8, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s1", f, OpXaSfF2 (0x8, 0, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s2", f, OpXaSfF2 (0x8, 0, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s3", f, OpXaSfF2 (0x8, 0, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s.s0", f, OpXaSfF2 (0x8, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s", f, OpXaSfF2 (0x8, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s.s1", f, OpXaSfF2 (0x8, 1, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s.s2", f, OpXaSfF2 (0x8, 1, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.s.s3", f, OpXaSfF2 (0x8, 1, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fma.s0", f, OpXaSf (0x8, 0, 0), {F1, F3, F4, F2}, EMPTY}, + {"fma", f, OpXaSf (0x8, 0, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fma.s1", f, OpXaSf (0x8, 0, 1), {F1, F3, F4, F2}, EMPTY}, + {"fma.s2", f, OpXaSf (0x8, 0, 2), {F1, F3, F4, F2}, EMPTY}, + {"fma.s3", f, OpXaSf (0x8, 0, 3), {F1, F3, F4, F2}, EMPTY}, + {"fma.s.s0", f, OpXaSf (0x8, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"fma.s", f, OpXaSf (0x8, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fma.s.s1", f, OpXaSf (0x8, 1, 1), {F1, F3, F4, F2}, EMPTY}, + {"fma.s.s2", f, OpXaSf (0x8, 1, 2), {F1, F3, F4, F2}, EMPTY}, + {"fma.s.s3", f, OpXaSf (0x8, 1, 3), {F1, F3, F4, F2}, EMPTY}, + + {"fnorm.d.s0", f, OpXaSfF2F4 (0x9, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.d", f, OpXaSfF2F4 (0x9, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.d.s1", f, OpXaSfF2F4 (0x9, 0, 1, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.d.s2", f, OpXaSfF2F4 (0x9, 0, 2, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fnorm.d.s3", f, OpXaSfF2F4 (0x9, 0, 3, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.d.s0", f, OpXaSfF2F4 (0x9, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.d", f, OpXaSfF2F4 (0x9, 0, 0, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.d.s1", f, OpXaSfF2F4 (0x9, 0, 1, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.d.s2", f, OpXaSfF2F4 (0x9, 0, 2, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fcvt.xuf.d.s3", f, OpXaSfF2F4 (0x9, 0, 3, 0, 1), {F1, F3}, PSEUDO, 0, NULL}, + {"fadd.d.s0", f, OpXaSfF4 (0x9, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.d", f, OpXaSfF4 (0x9, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.d.s1", f, OpXaSfF4 (0x9, 0, 1, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.d.s2", f, OpXaSfF4 (0x9, 0, 2, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fadd.d.s3", f, OpXaSfF4 (0x9, 0, 3, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fmpy.d.s0", f, OpXaSfF2 (0x9, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.d", f, OpXaSfF2 (0x9, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.d.s1", f, OpXaSfF2 (0x9, 0, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.d.s2", f, OpXaSfF2 (0x9, 0, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fmpy.d.s3", f, OpXaSfF2 (0x9, 0, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fma.d.s0", f, OpXaSf (0x9, 0, 0), {F1, F3, F4, F2}, EMPTY}, + {"fma.d", f, OpXaSf (0x9, 0, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fma.d.s1", f, OpXaSf (0x9, 0, 1), {F1, F3, F4, F2}, EMPTY}, + {"fma.d.s2", f, OpXaSf (0x9, 0, 2), {F1, F3, F4, F2}, EMPTY}, + {"fma.d.s3", f, OpXaSf (0x9, 0, 3), {F1, F3, F4, F2}, EMPTY}, + + {"fpmpy.s0", f, OpXaSfF2 (0x9, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpmpy", f, OpXaSfF2 (0x9, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpmpy.s1", f, OpXaSfF2 (0x9, 1, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpmpy.s2", f, OpXaSfF2 (0x9, 1, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpmpy.s3", f, OpXaSfF2 (0x9, 1, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpma.s0", f, OpXaSf (0x9, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"fpma", f, OpXaSf (0x9, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fpma.s1", f, OpXaSf (0x9, 1, 1), {F1, F3, F4, F2}, EMPTY}, + {"fpma.s2", f, OpXaSf (0x9, 1, 2), {F1, F3, F4, F2}, EMPTY}, + {"fpma.s3", f, OpXaSf (0x9, 1, 3), {F1, F3, F4, F2}, EMPTY}, + + {"fsub.s0", f, OpXaSfF4 (0xa, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub", f, OpXaSfF4 (0xa, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s1", f, OpXaSfF4 (0xa, 0, 1, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s2", f, OpXaSfF4 (0xa, 0, 2, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s3", f, OpXaSfF4 (0xa, 0, 3, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s.s0", f, OpXaSfF4 (0xa, 1, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s", f, OpXaSfF4 (0xa, 1, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s.s1", f, OpXaSfF4 (0xa, 1, 1, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s.s2", f, OpXaSfF4 (0xa, 1, 2, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.s.s3", f, OpXaSfF4 (0xa, 1, 3, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fms.s0", f, OpXaSf (0xa, 0, 0), {F1, F3, F4, F2}, EMPTY}, + {"fms", f, OpXaSf (0xa, 0, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fms.s1", f, OpXaSf (0xa, 0, 1), {F1, F3, F4, F2}, EMPTY}, + {"fms.s2", f, OpXaSf (0xa, 0, 2), {F1, F3, F4, F2}, EMPTY}, + {"fms.s3", f, OpXaSf (0xa, 0, 3), {F1, F3, F4, F2}, EMPTY}, + {"fms.s.s0", f, OpXaSf (0xa, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"fms.s", f, OpXaSf (0xa, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fms.s.s1", f, OpXaSf (0xa, 1, 1), {F1, F3, F4, F2}, EMPTY}, + {"fms.s.s2", f, OpXaSf (0xa, 1, 2), {F1, F3, F4, F2}, EMPTY}, + {"fms.s.s3", f, OpXaSf (0xa, 1, 3), {F1, F3, F4, F2}, EMPTY}, + {"fsub.d.s0", f, OpXaSfF4 (0xb, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.d", f, OpXaSfF4 (0xb, 0, 0, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.d.s1", f, OpXaSfF4 (0xb, 0, 1, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.d.s2", f, OpXaSfF4 (0xb, 0, 2, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fsub.d.s3", f, OpXaSfF4 (0xb, 0, 3, 1), {F1, F3, F2}, PSEUDO, 0, NULL}, + {"fms.d.s0", f, OpXaSf (0xb, 0, 0), {F1, F3, F4, F2}, EMPTY}, + {"fms.d", f, OpXaSf (0xb, 0, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fms.d.s1", f, OpXaSf (0xb, 0, 1), {F1, F3, F4, F2}, EMPTY}, + {"fms.d.s2", f, OpXaSf (0xb, 0, 2), {F1, F3, F4, F2}, EMPTY}, + {"fms.d.s3", f, OpXaSf (0xb, 0, 3), {F1, F3, F4, F2}, EMPTY}, + + {"fpms.s0", f, OpXaSf (0xb, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"fpms", f, OpXaSf (0xb, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fpms.s1", f, OpXaSf (0xb, 1, 1), {F1, F3, F4, F2}, EMPTY}, + {"fpms.s2", f, OpXaSf (0xb, 1, 2), {F1, F3, F4, F2}, EMPTY}, + {"fpms.s3", f, OpXaSf (0xb, 1, 3), {F1, F3, F4, F2}, EMPTY}, + + {"fnmpy.s0", f, OpXaSfF2 (0xc, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy", f, OpXaSfF2 (0xc, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s1", f, OpXaSfF2 (0xc, 0, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s2", f, OpXaSfF2 (0xc, 0, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s3", f, OpXaSfF2 (0xc, 0, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s.s0", f, OpXaSfF2 (0xc, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s", f, OpXaSfF2 (0xc, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s.s1", f, OpXaSfF2 (0xc, 1, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s.s2", f, OpXaSfF2 (0xc, 1, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.s.s3", f, OpXaSfF2 (0xc, 1, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnma.s0", f, OpXaSf (0xc, 0, 0), {F1, F3, F4, F2}, EMPTY}, + {"fnma", f, OpXaSf (0xc, 0, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fnma.s1", f, OpXaSf (0xc, 0, 1), {F1, F3, F4, F2}, EMPTY}, + {"fnma.s2", f, OpXaSf (0xc, 0, 2), {F1, F3, F4, F2}, EMPTY}, + {"fnma.s3", f, OpXaSf (0xc, 0, 3), {F1, F3, F4, F2}, EMPTY}, + {"fnma.s.s0", f, OpXaSf (0xc, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"fnma.s", f, OpXaSf (0xc, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fnma.s.s1", f, OpXaSf (0xc, 1, 1), {F1, F3, F4, F2}, EMPTY}, + {"fnma.s.s2", f, OpXaSf (0xc, 1, 2), {F1, F3, F4, F2}, EMPTY}, + {"fnma.s.s3", f, OpXaSf (0xc, 1, 3), {F1, F3, F4, F2}, EMPTY}, + {"fnmpy.d.s0", f, OpXaSfF2 (0xd, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.d", f, OpXaSfF2 (0xd, 0, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.d.s1", f, OpXaSfF2 (0xd, 0, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.d.s2", f, OpXaSfF2 (0xd, 0, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnmpy.d.s3", f, OpXaSfF2 (0xd, 0, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fnma.d.s0", f, OpXaSf (0xd, 0, 0), {F1, F3, F4, F2}, EMPTY}, + {"fnma.d", f, OpXaSf (0xd, 0, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fnma.d.s1", f, OpXaSf (0xd, 0, 1), {F1, F3, F4, F2}, EMPTY}, + {"fnma.d.s2", f, OpXaSf (0xd, 0, 2), {F1, F3, F4, F2}, EMPTY}, + {"fnma.d.s3", f, OpXaSf (0xd, 0, 3), {F1, F3, F4, F2}, EMPTY}, + + {"fpnmpy.s0", f, OpXaSfF2 (0xd, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpnmpy", f, OpXaSfF2 (0xd, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpnmpy.s1", f, OpXaSfF2 (0xd, 1, 1, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpnmpy.s2", f, OpXaSfF2 (0xd, 1, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpnmpy.s3", f, OpXaSfF2 (0xd, 1, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"fpnma.s0", f, OpXaSf (0xd, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"fpnma", f, OpXaSf (0xd, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"fpnma.s1", f, OpXaSf (0xd, 1, 1), {F1, F3, F4, F2}, EMPTY}, + {"fpnma.s2", f, OpXaSf (0xd, 1, 2), {F1, F3, F4, F2}, EMPTY}, + {"fpnma.s3", f, OpXaSf (0xd, 1, 3), {F1, F3, F4, F2}, EMPTY}, + + {"xmpy.l", f, OpXaX2F2 (0xe, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"xmpy.lu", f, OpXaX2F2 (0xe, 1, 0, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"xmpy.h", f, OpXaX2F2 (0xe, 1, 3, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"xmpy.hu", f, OpXaX2F2 (0xe, 1, 2, 0), {F1, F3, F4}, PSEUDO, 0, NULL}, + {"xma.l", f, OpXaX2 (0xe, 1, 0), {F1, F3, F4, F2}, EMPTY}, + {"xma.lu", f, OpXaX2 (0xe, 1, 0), {F1, F3, F4, F2}, PSEUDO, 0, NULL}, + {"xma.h", f, OpXaX2 (0xe, 1, 3), {F1, F3, F4, F2}, EMPTY}, + {"xma.hu", f, OpXaX2 (0xe, 1, 2), {F1, F3, F4, F2}, EMPTY}, + + {"fselect", f, OpXa (0xe, 0), {F1, F3, F4, F2}, EMPTY}, + + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; + +#undef f0 +#undef f +#undef f2 +#undef bF2 +#undef bF4 +#undef bQ +#undef bRa +#undef bRb +#undef bSf +#undef bTa +#undef bXa +#undef bXb +#undef bX2 +#undef bX6 +#undef mF2 +#undef mF4 +#undef mQ +#undef mRa +#undef mRb +#undef mSf +#undef mTa +#undef mXa +#undef mXb +#undef mX2 +#undef mX6 +#undef OpXa +#undef OpXaSf +#undef OpXaSfF2 +#undef OpXaSfF4 +#undef OpXaSfF2F4 +#undef OpXaX2 +#undef OpRaRbTaSf +#undef OpTa +#undef OpXbQSf +#undef OpXbX6 +#undef OpXbX6F2 +#undef OpXbX6Sf +#undef EMPTY diff --git a/tools/debugger/xenitp/ia64-opc-i.c b/tools/debugger/xenitp/ia64-opc-i.c new file mode 100644 index 0000000..69cd969 --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-i.c @@ -0,0 +1,338 @@ +/* ia64-opc-i.c -- IA-64 `I' opcode table. + Copyright 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-opc.h" + +#define I0 IA64_TYPE_I, 0 +#define I IA64_TYPE_I, 1 +#define I2 IA64_TYPE_I, 2 + +/* instruction bit fields: */ +#define bC(x) (((ia64_insn) ((x) & 0x1)) << 12) +#define bIh(x) (((ia64_insn) ((x) & 0x1)) << 23) +#define bTa(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bTag13(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bTb(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bVc(x) (((ia64_insn) ((x) & 0x1)) << 20) +#define bVe(x) (((ia64_insn) ((x) & 0x1)) << 32) +#define bWh(x) (((ia64_insn) ((x) & 0x3)) << 20) +#define bX(x) (((ia64_insn) ((x) & 0x1)) << 33) +#define bXb(x) (((ia64_insn) ((x) & 0x1)) << 22) +#define bXc(x) (((ia64_insn) ((x) & 0x1)) << 19) +#define bX2(x) (((ia64_insn) ((x) & 0x3)) << 34) +#define bX2a(x) (((ia64_insn) ((x) & 0x3)) << 34) +#define bX2b(x) (((ia64_insn) ((x) & 0x3)) << 28) +#define bX2c(x) (((ia64_insn) ((x) & 0x3)) << 30) +#define bX3(x) (((ia64_insn) ((x) & 0x7)) << 33) +#define bX6(x) (((ia64_insn) ((x) & 0x3f)) << 27) +#define bYa(x) (((ia64_insn) ((x) & 0x1)) << 13) +#define bYb(x) (((ia64_insn) ((x) & 0x1)) << 26) +#define bZa(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bZb(x) (((ia64_insn) ((x) & 0x1)) << 33) + +/* instruction bit masks: */ +#define mC bC (-1) +#define mIh bIh (-1) +#define mTa bTa (-1) +#define mTag13 bTag13 (-1) +#define mTb bTb (-1) +#define mVc bVc (-1) +#define mVe bVe (-1) +#define mWh bWh (-1) +#define mX bX (-1) +#define mXb bXb (-1) +#define mXc bXc (-1) +#define mX2 bX2 (-1) +#define mX2a bX2a (-1) +#define mX2b bX2b (-1) +#define mX2c bX2c (-1) +#define mX3 bX3 (-1) +#define mX6 bX6 (-1) +#define mYa bYa (-1) +#define mYb bYb (-1) +#define mZa bZa (-1) +#define mZb bZb (-1) + +#define OpZaZbVeX2aX2b(a,b,c,d,e,f) \ + (bOp (a) | bZa (b) | bZb (c) | bVe (d) | bX2a (e) | bX2b (f)), \ + (mOp | mZa | mZb | mVe | mX2a | mX2b) +#define OpZaZbVeX2aX2bX2c(a,b,c,d,e,f,g) \ + (bOp (a) | bZa (b) | bZb (c) | bVe (d) | bX2a (e) | bX2b (f) | bX2c (g)), \ + (mOp | mZa | mZb | mVe | mX2a | mX2b | mX2c) +#define OpX2X(a,b,c) (bOp (a) | bX2 (b) | bX (c)), (mOp | mX2 | mX) +#define OpX2XYa(a,b,c,d) (bOp (a) | bX2 (b) | bX (c) | bYa (d)), \ + (mOp | mX2 | mX | mYa) +#define OpX2XYb(a,b,c,d) (bOp (a) | bX2 (b) | bX (c) | bYb (d)), \ + (mOp | mX2 | mX | mYb) +#define OpX2TaTbYaC(a,b,c,d,e,f) \ + (bOp (a) | bX2 (b) | bTa (c) | bTb (d) | bYa (e) | bC (f)), \ + (mOp | mX2 | mTa | mTb | mYa | mC) +#define OpX2TaTbYaXcC(a,b,c,d,e,f,g) \ + (bOp (a) | bX2 (b) | bTa (c) | bTb (d) | bYa (e) | bXc (f) | bC (g)), \ + (mOp | mX2 | mTa | mTb | mYa | mXc | mC) +#define OpX3(a,b) (bOp (a) | bX3 (b)), (mOp | mX3) +#define OpX3X6(a,b,c) (bOp (a) | bX3 (b) | bX6(c)), \ + (mOp | mX3 | mX6) +#define OpX3X6Yb(a,b,c,d) (bOp (a) | bX3 (b) | bX6(c) | bYb(d)), \ + (mOp | mX3 | mX6 | mYb) +#define OpX3XbIhWh(a,b,c,d,e) \ + (bOp (a) | bX3 (b) | bXb (c) | bIh (d) | bWh (e)), \ + (mOp | mX3 | mXb | mIh | mWh) +#define OpX3XbIhWhTag13(a,b,c,d,e,f) \ + (bOp (a) | bX3 (b) | bXb (c) | bIh (d) | bWh (e) | bTag13 (f)), \ + (mOp | mX3 | mXb | mIh | mWh | mTag13) + +#define FULL17 ((ia64_insn)0x10ff001fc0LL) + +/* Used to initialise unused fields in ia64_opcode struct, + in order to stop gcc from complaining. */ +#define EMPTY 0,0,NULL + +struct ia64_opcode ia64_opcodes_i[] = + { + /* I-type instruction encodings (sorted according to major opcode). */ + + {"break.i", I0, OpX3X6 (0, 0, 0x00), {IMMU21}, X_IN_MLX, 0, NULL}, + {"nop.i", I0, OpX3X6Yb (0, 0, 0x01, 0), {IMMU21}, X_IN_MLX, 0, NULL}, + {"hint.i", I0, OpX3X6Yb (0, 0, 0x01, 1), {IMMU21}, X_IN_MLX, 0, NULL}, + {"chk.s.i", I0, OpX3 (0, 1), {R2, TGT25b}, EMPTY}, + + {"mov", I, OpX3XbIhWhTag13 (0, 7, 0, 0, 1, 0), {B1, R2}, PSEUDO, 0, NULL}, +#define MOV(a,b,c,d) \ + I, OpX3XbIhWh (0, a, b, c, d), {B1, R2, TAG13b}, EMPTY + {"mov.sptk", MOV (7, 0, 0, 0)}, + {"mov.sptk.imp", MOV (7, 0, 1, 0)}, + {"mov", MOV (7, 0, 0, 1)}, + {"mov.imp", MOV (7, 0, 1, 1)}, + {"mov.dptk", MOV (7, 0, 0, 2)}, + {"mov.dptk.imp", MOV (7, 0, 1, 2)}, + {"mov.ret.sptk", MOV (7, 1, 0, 0)}, + {"mov.ret.sptk.imp", MOV (7, 1, 1, 0)}, + {"mov.ret", MOV (7, 1, 0, 1)}, + {"mov.ret.imp", MOV (7, 1, 1, 1)}, + {"mov.ret.dptk", MOV (7, 1, 0, 2)}, + {"mov.ret.dptk.imp", MOV (7, 1, 1, 2)}, +#undef MOV + {"mov", I, OpX3X6 (0, 0, 0x31), {R1, B2}, EMPTY}, + {"mov", I, OpX3 (0, 3), {PR, R2, IMM17}, EMPTY}, + /* Don't remove one of the seemingly redundant FULL17-s. */ + {"mov", I, FULL17 | OpX3 (0, 3) | FULL17, {PR, R2}, PSEUDO, 0, NULL}, + {"mov", I, OpX3 (0, 2), {PR_ROT, IMM44}, EMPTY}, + {"mov", I, OpX3X6 (0, 0, 0x30), {R1, IP}, EMPTY}, + {"mov", I, OpX3X6 (0, 0, 0x33), {R1, PR}, EMPTY}, + {"mov.i", I, OpX3X6 (0, 0, 0x2a), {AR3, R2}, EMPTY}, + {"mov.i", I, OpX3X6 (0, 0, 0x0a), {AR3, IMM8}, EMPTY}, + {"mov.i", I, OpX3X6 (0, 0, 0x32), {R1, AR3}, EMPTY}, + {"zxt1", I, OpX3X6 (0, 0, 0x10), {R1, R3}, EMPTY}, + {"zxt2", I, OpX3X6 (0, 0, 0x11), {R1, R3}, EMPTY}, + {"zxt4", I, OpX3X6 (0, 0, 0x12), {R1, R3}, EMPTY}, + {"sxt1", I, OpX3X6 (0, 0, 0x14), {R1, R3}, EMPTY}, + {"sxt2", I, OpX3X6 (0, 0, 0x15), {R1, R3}, EMPTY}, + {"sxt4", I, OpX3X6 (0, 0, 0x16), {R1, R3}, EMPTY}, + {"czx1.l", I, OpX3X6 (0, 0, 0x18), {R1, R3}, EMPTY}, + {"czx2.l", I, OpX3X6 (0, 0, 0x19), {R1, R3}, EMPTY}, + {"czx1.r", I, OpX3X6 (0, 0, 0x1c), {R1, R3}, EMPTY}, + {"czx2.r", I, OpX3X6 (0, 0, 0x1d), {R1, R3}, EMPTY}, + + {"dep", I, Op (4), {R1, R2, R3, CPOS6c, LEN4}, EMPTY}, + + {"shrp", I, OpX2X (5, 3, 0), {R1, R2, R3, CNT6}, EMPTY}, + + {"shr.u", I, OpX2XYa (5, 1, 0, 0), {R1, R3, POS6}, + PSEUDO | LEN_EQ_64MCNT, 0, NULL}, + {"extr.u", I, OpX2XYa (5, 1, 0, 0), {R1, R3, POS6, LEN6}, EMPTY}, + + {"shr", I, OpX2XYa (5, 1, 0, 1), {R1, R3, POS6}, + PSEUDO | LEN_EQ_64MCNT, 0, NULL}, + {"extr", I, OpX2XYa (5, 1, 0, 1), {R1, R3, POS6, LEN6}, EMPTY}, + + {"shl", I, OpX2XYb (5, 1, 1, 0), {R1, R2, CPOS6a}, + PSEUDO | LEN_EQ_64MCNT, 0, NULL}, + {"dep.z", I, OpX2XYb (5, 1, 1, 0), {R1, R2, CPOS6a, LEN6}, EMPTY}, + {"dep.z", I, OpX2XYb (5, 1, 1, 1), {R1, IMM8, CPOS6a, LEN6}, EMPTY}, + {"dep", I, OpX2X (5, 3, 1), {R1, IMM1, R3, CPOS6b, LEN6}, EMPTY}, +#define TF(a,b,c) \ + I2, OpX2TaTbYaXcC (5, 0, a, b, 1, 1, c), {P1, P2, IMMU5b}, EMPTY +#define TFCM(a,b,c) \ + I2, OpX2TaTbYaXcC (5, 0, a, b, 1, 1, c), {P2, P1, IMMU5b}, PSEUDO, 0, NULL + {"tf.z", TF (0, 0, 0)}, + {"tf.nz", TFCM (0, 0, 0)}, + {"tf.z.unc", TF (0, 0, 1)}, + {"tf.nz.unc", TFCM (0, 0, 1)}, + {"tf.z.and", TF (0, 1, 0)}, + {"tf.nz.andcm", TFCM (0, 1, 0)}, + {"tf.nz.and", TF (0, 1, 1)}, + {"tf.z.andcm", TFCM (0, 1, 1)}, + {"tf.z.or", TF (1, 0, 0)}, + {"tf.nz.orcm", TFCM (1, 0, 0)}, + {"tf.nz.or", TF (1, 0, 1)}, + {"tf.z.orcm", TFCM (1, 0, 1)}, + {"tf.z.or.andcm", TF (1, 1, 0)}, + {"tf.nz.and.orcm", TFCM (1, 1, 0)}, + {"tf.nz.or.andcm", TF (1, 1, 1)}, + {"tf.z.and.orcm", TFCM (1, 1, 1)}, +#undef TF +#undef TFCM +#define TBIT(a,b,c,d) \ + I2, OpX2TaTbYaC (5, 0, a, b, c, d), {P1, P2, R3, POS6}, EMPTY +#define TBITCM(a,b,c,d) \ + I2, OpX2TaTbYaC (5, 0, a, b, c, d), {P2, P1, R3, POS6}, PSEUDO, 0, NULL + {"tbit.z", TBIT (0, 0, 0, 0)}, + {"tbit.nz", TBITCM (0, 0, 0, 0)}, + {"tbit.z.unc", TBIT (0, 0, 0, 1)}, + {"tbit.nz.unc", TBITCM (0, 0, 0, 1)}, + {"tbit.z.and", TBIT (0, 1, 0, 0)}, + {"tbit.nz.andcm", TBITCM (0, 1, 0, 0)}, + {"tbit.nz.and", TBIT (0, 1, 0, 1)}, + {"tbit.z.andcm", TBITCM (0, 1, 0, 1)}, + {"tbit.z.or", TBIT (1, 0, 0, 0)}, + {"tbit.nz.orcm", TBITCM (1, 0, 0, 0)}, + {"tbit.nz.or", TBIT (1, 0, 0, 1)}, + {"tbit.z.orcm", TBITCM (1, 0, 0, 1)}, + {"tbit.z.or.andcm", TBIT (1, 1, 0, 0)}, + {"tbit.nz.and.orcm", TBITCM (1, 1, 0, 0)}, + {"tbit.nz.or.andcm", TBIT (1, 1, 0, 1)}, + {"tbit.z.and.orcm", TBITCM (1, 1, 0, 1)}, +#undef TBIT +#undef TBITCM +#define TNAT(a,b,c,d) \ + I2, OpX2TaTbYaC (5, 0, a, b, c, d), {P1, P2, R3}, EMPTY +#define TNATCM(a,b,c,d) \ + I2, OpX2TaTbYaC (5, 0, a, b, c, d), {P2, P1, R3}, PSEUDO, 0, NULL + {"tnat.z", TNAT (0, 0, 1, 0)}, + {"tnat.nz", TNATCM (0, 0, 1, 0)}, + {"tnat.z.unc", TNAT (0, 0, 1, 1)}, + {"tnat.nz.unc", TNATCM (0, 0, 1, 1)}, + {"tnat.z.and", TNAT (0, 1, 1, 0)}, + {"tnat.nz.andcm", TNATCM (0, 1, 1, 0)}, + {"tnat.nz.and", TNAT (0, 1, 1, 1)}, + {"tnat.z.andcm", TNATCM (0, 1, 1, 1)}, + {"tnat.z.or", TNAT (1, 0, 1, 0)}, + {"tnat.nz.orcm", TNATCM (1, 0, 1, 0)}, + {"tnat.nz.or", TNAT (1, 0, 1, 1)}, + {"tnat.z.orcm", TNATCM (1, 0, 1, 1)}, + {"tnat.z.or.andcm", TNAT (1, 1, 1, 0)}, + {"tnat.nz.and.orcm", TNATCM (1, 1, 1, 0)}, + {"tnat.nz.or.andcm", TNAT (1, 1, 1, 1)}, + {"tnat.z.and.orcm", TNATCM (1, 1, 1, 1)}, +#undef TNAT +#undef TNATCM + + {"pmpyshr2", I, OpZaZbVeX2aX2b (7, 0, 1, 0, 0, 3), {R1, R2, R3, CNT2c}, EMPTY}, + {"pmpyshr2.u", I, OpZaZbVeX2aX2b (7, 0, 1, 0, 0, 1), {R1, R2, R3, CNT2c}, EMPTY}, + {"pmpy2.r", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 1, 3), {R1, R2, R3}, EMPTY}, + {"pmpy2.l", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 3, 3), {R1, R2, R3}, EMPTY}, + {"mix1.r", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 0, 2), {R1, R2, R3}, EMPTY}, + {"mix2.r", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 0, 2), {R1, R2, R3}, EMPTY}, + {"mix4.r", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 2, 0, 2), {R1, R2, R3}, EMPTY}, + {"mix1.l", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 2, 2), {R1, R2, R3}, EMPTY}, + {"mix2.l", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 2, 2), {R1, R2, R3}, EMPTY}, + {"mix4.l", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 2, 2, 2), {R1, R2, R3}, EMPTY}, + {"pack2.uss", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 0, 0), {R1, R2, R3}, EMPTY}, + {"pack2.sss", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 2, 0), {R1, R2, R3}, EMPTY}, + {"pack4.sss", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 2, 2, 0), {R1, R2, R3}, EMPTY}, + {"unpack1.h", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 0, 1), {R1, R2, R3}, EMPTY}, + {"unpack2.h", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 0, 1), {R1, R2, R3}, EMPTY}, + {"unpack4.h", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 2, 0, 1), {R1, R2, R3}, EMPTY}, + {"unpack1.l", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 2, 1), {R1, R2, R3}, EMPTY}, + {"unpack2.l", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 2, 1), {R1, R2, R3}, EMPTY}, + {"unpack4.l", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 2, 2, 1), {R1, R2, R3}, EMPTY}, + {"pmin1.u", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 1, 0), {R1, R2, R3}, EMPTY}, + {"pmax1.u", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 1, 1), {R1, R2, R3}, EMPTY}, + {"pmin2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 3, 0), {R1, R2, R3}, EMPTY}, + {"pmax2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 2, 3, 1), {R1, R2, R3}, EMPTY}, + {"psad1", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 2, 3, 2), {R1, R2, R3}, EMPTY}, + {"mux1", I, OpZaZbVeX2aX2bX2c (7, 0, 0, 0, 3, 2, 2), {R1, R2, MBTYPE4}, EMPTY}, + {"mux2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 3, 2, 2), {R1, R2, MHTYPE8}, EMPTY}, + {"pshr2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 0, 2, 0), {R1, R3, R2}, EMPTY}, + {"pshr4", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 0, 2, 0), {R1, R3, R2}, EMPTY}, + {"shr", I, OpZaZbVeX2aX2bX2c (7, 1, 1, 0, 0, 2, 0), {R1, R3, R2}, EMPTY}, + {"pshr2.u", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 0, 0, 0), {R1, R3, R2}, EMPTY}, + {"pshr4.u", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 0, 0, 0), {R1, R3, R2}, EMPTY}, + {"shr.u", I, OpZaZbVeX2aX2bX2c (7, 1, 1, 0, 0, 0, 0), {R1, R3, R2}, EMPTY}, + {"pshr2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 1, 3, 0), {R1, R3, CNT5}, EMPTY}, + {"pshr4", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 1, 3, 0), {R1, R3, CNT5}, EMPTY}, + {"pshr2.u", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 1, 1, 0), {R1, R3, CNT5}, EMPTY}, + {"pshr4.u", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 1, 1, 0), {R1, R3, CNT5}, EMPTY}, + {"pshl2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 0, 0, 1), {R1, R2, R3}, EMPTY}, + {"pshl4", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 0, 0, 1), {R1, R2, R3}, EMPTY}, + {"shl", I, OpZaZbVeX2aX2bX2c (7, 1, 1, 0, 0, 0, 1), {R1, R2, R3}, EMPTY}, + {"pshl2", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 3, 1, 1), {R1, R2, CCNT5}, EMPTY}, + {"pshl4", I, OpZaZbVeX2aX2bX2c (7, 1, 0, 0, 3, 1, 1), {R1, R2, CCNT5}, EMPTY}, + {"popcnt", I, OpZaZbVeX2aX2bX2c (7, 0, 1, 0, 1, 1, 2), {R1, R3}, EMPTY}, + + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; + +#undef I0 +#undef I +#undef I2 +#undef L +#undef bC +#undef bIh +#undef bTa +#undef bTag13 +#undef bTb +#undef bVc +#undef bVe +#undef bWh +#undef bX +#undef bXb +#undef bX2 +#undef bX2a +#undef bX2b +#undef bX2c +#undef bX3 +#undef bX6 +#undef bY +#undef bZa +#undef bZb +#undef mC +#undef mIh +#undef mTa +#undef mTag13 +#undef mTb +#undef mVc +#undef mVe +#undef mWh +#undef mX +#undef mXb +#undef mX2 +#undef mX2a +#undef mX2b +#undef mX2c +#undef mX3 +#undef mX6 +#undef mY +#undef mZa +#undef mZb +#undef OpZaZbVeX2aX2b +#undef OpZaZbVeX2aX2bX2c +#undef OpX2X +#undef OpX2XYa +#undef OpX2XYb +#undef OpX2TaTbYaC +#undef OpX3 +#undef OpX3X6 +#undef OpX3XbIhWh +#undef OpX3XbIhWhTag13 +#undef EMPTY diff --git a/tools/debugger/xenitp/ia64-opc-m.c b/tools/debugger/xenitp/ia64-opc-m.c new file mode 100644 index 0000000..d790386 --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-m.c @@ -0,0 +1,1118 @@ +/* ia64-opc-m.c -- IA-64 `M' opcode table. + Copyright 1998, 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-opc.h" + +#define M0 IA64_TYPE_M, 0 +#define M IA64_TYPE_M, 1 +#define M2 IA64_TYPE_M, 2 + +/* instruction bit fields: */ +#define bM(x) (((ia64_insn) ((x) & 0x1)) << 36) +#define bX(x) (((ia64_insn) ((x) & 0x1)) << 27) +#define bX2(x) (((ia64_insn) ((x) & 0x3)) << 31) +#define bX3(x) (((ia64_insn) ((x) & 0x7)) << 33) +#define bX4(x) (((ia64_insn) ((x) & 0xf)) << 27) +#define bX6a(x) (((ia64_insn) ((x) & 0x3f)) << 30) +#define bX6b(x) (((ia64_insn) ((x) & 0x3f)) << 27) +#define bX7(x) (((ia64_insn) ((x) & 0x1)) << 36) /* note: alias for bM() */ +#define bY(x) (((ia64_insn) ((x) & 0x1)) << 26) +#define bHint(x) (((ia64_insn) ((x) & 0x3)) << 28) + +#define mM bM (-1) +#define mX bX (-1) +#define mX2 bX2 (-1) +#define mX3 bX3 (-1) +#define mX4 bX4 (-1) +#define mX6a bX6a (-1) +#define mX6b bX6b (-1) +#define mX7 bX7 (-1) +#define mY bY (-1) +#define mHint bHint (-1) + +#define OpX3(a,b) (bOp (a) | bX3 (b)), (mOp | mX3) +#define OpX3X6b(a,b,c) (bOp (a) | bX3 (b) | bX6b (c)), \ + (mOp | mX3 | mX6b) +#define OpX3X6bX7(a,b,c,d) (bOp (a) | bX3 (b) | bX6b (c) | bX7 (d)), \ + (mOp | mX3 | mX6b | mX7) +#define OpX3X4(a,b,c) (bOp (a) | bX3 (b) | bX4 (c)), \ + (mOp | mX3 | mX4) +#define OpX3X4X2(a,b,c,d) (bOp (a) | bX3 (b) | bX4 (c) | bX2 (d)), \ + (mOp | mX3 | mX4 | mX2) +#define OpX3X4X2Y(a,b,c,d,e) (bOp (a) | bX3 (b) | bX4 (c) | bX2 (d) | bY (e)), \ + (mOp | mX3 | mX4 | mX2 | mY) +#define OpX6aHint(a,b,c) (bOp (a) | bX6a (b) | bHint (c)), \ + (mOp | mX6a | mHint) +#define OpXX6aHint(a,b,c,d) (bOp (a) | bX (b) | bX6a (c) | bHint (d)), \ + (mOp | mX | mX6a | mHint) +#define OpMXX6a(a,b,c,d) \ + (bOp (a) | bM (b) | bX (c) | bX6a (d)), (mOp | mM | mX | mX6a) +#define OpMXX6aHint(a,b,c,d,e) \ + (bOp (a) | bM (b) | bX (c) | bX6a (d) | bHint (e)), \ + (mOp | mM | mX | mX6a | mHint) + +/* Used to initialise unused fields in ia64_opcode struct, + in order to stop gcc from complaining. */ +#define EMPTY 0,0,NULL + +struct ia64_opcode ia64_opcodes_m[] = + { + /* M-type instruction encodings (sorted according to major opcode). */ + + {"chk.a.nc", M0, OpX3 (0, 4), {R1, TGT25c}, EMPTY}, + {"chk.a.clr", M0, OpX3 (0, 5), {R1, TGT25c}, EMPTY}, + {"chk.a.nc", M0, OpX3 (0, 6), {F1, TGT25c}, EMPTY}, + {"chk.a.clr", M0, OpX3 (0, 7), {F1, TGT25c}, EMPTY}, + + {"invala", M0, OpX3X4X2 (0, 0, 0, 1), {}, EMPTY}, + {"fwb", M0, OpX3X4X2 (0, 0, 0, 2), {}, EMPTY}, + {"mf", M0, OpX3X4X2 (0, 0, 2, 2), {}, EMPTY}, + {"mf.a", M0, OpX3X4X2 (0, 0, 3, 2), {}, EMPTY}, + {"srlz.d", M0, OpX3X4X2 (0, 0, 0, 3), {}, EMPTY}, + {"srlz.i", M0, OpX3X4X2 (0, 0, 1, 3), {}, EMPTY}, + {"sync.i", M0, OpX3X4X2 (0, 0, 3, 3), {}, EMPTY}, + {"flushrs", M0, OpX3X4X2 (0, 0, 0xc, 0), {}, FIRST | NO_PRED, 0, NULL}, + {"loadrs", M0, OpX3X4X2 (0, 0, 0xa, 0), {}, FIRST | NO_PRED, 0, NULL}, + {"invala.e", M0, OpX3X4X2 (0, 0, 2, 1), {R1}, EMPTY}, + {"invala.e", M0, OpX3X4X2 (0, 0, 3, 1), {F1}, EMPTY}, + {"mov.m", M, OpX3X4X2 (0, 0, 8, 2), {AR3, IMM8}, EMPTY}, + + {"break.m", M0, OpX3X4X2 (0, 0, 0, 0), {IMMU21}, EMPTY}, + {"nop.m", M0, OpX3X4X2Y (0, 0, 1, 0, 0), {IMMU21}, EMPTY}, + {"hint.m", M0, OpX3X4X2Y (0, 0, 1, 0, 1), {IMMU21}, EMPTY}, + + {"sum", M0, OpX3X4 (0, 0, 4), {IMMU24}, EMPTY}, + {"rum", M0, OpX3X4 (0, 0, 5), {IMMU24}, EMPTY}, + {"ssm", M0, OpX3X4 (0, 0, 6), {IMMU24}, PRIV, 0, NULL}, + {"rsm", M0, OpX3X4 (0, 0, 7), {IMMU24}, PRIV, 0, NULL}, + + {"mov.m", M, OpX3X6b (1, 0, 0x2a), {AR3, R2}, EMPTY}, + {"mov.m", M, OpX3X6b (1, 0, 0x22), {R1, AR3}, EMPTY}, + {"mov", M, OpX3X6b (1, 0, 0x2c), {CR3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x24), {R1, CR3}, PRIV, 0, NULL}, + + {"alloc", M, OpX3 (1, 6), {R1, AR_PFS, SOF, SOL, SOR}, FIRST|NO_PRED|MOD_RRBS, 0, NULL}, + {"alloc", M, OpX3 (1, 6), {R1, SOF, SOL, SOR}, PSEUDO|FIRST|NO_PRED|MOD_RRBS, 0, NULL}, + + {"mov", M, OpX3X6b (1, 0, 0x2d), {PSR_L, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x29), {PSR_UM, R2}, EMPTY}, + {"mov", M, OpX3X6b (1, 0, 0x25), {R1, PSR}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x21), {R1, PSR_UM}, EMPTY}, + {"probe.r", M, OpX3X6b (1, 0, 0x38), {R1, R3, R2}, EMPTY}, + {"probe.w", M, OpX3X6b (1, 0, 0x39), {R1, R3, R2}, EMPTY}, + {"probe.r", M, OpX3X6b (1, 0, 0x18), {R1, R3, IMMU2}, EMPTY}, + {"probe.w", M, OpX3X6b (1, 0, 0x19), {R1, R3, IMMU2}, EMPTY}, + {"probe.rw.fault", M0, OpX3X6b (1, 0, 0x31), {R3, IMMU2}, EMPTY}, + {"probe.r.fault", M0, OpX3X6b (1, 0, 0x32), {R3, IMMU2}, EMPTY}, + {"probe.w.fault", M0, OpX3X6b (1, 0, 0x33), {R3, IMMU2}, EMPTY}, + {"itc.d", M0, OpX3X6b (1, 0, 0x2e), {R2}, LAST | PRIV, 0, NULL}, + {"itc.i", M0, OpX3X6b (1, 0, 0x2f), {R2}, LAST | PRIV, 0, NULL}, + + {"mov", M, OpX3X6b (1, 0, 0x00), {RR_R3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x01), {DBR_R3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x02), {IBR_R3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x03), {PKR_R3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x04), {PMC_R3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x05), {PMD_R3, R2}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x06), {MSR_R3, R2}, PRIV, 0, NULL}, + {"itr.d", M, OpX3X6b (1, 0, 0x0e), {DTR_R3, R2}, PRIV, 0, NULL}, + {"itr.i", M, OpX3X6b (1, 0, 0x0f), {ITR_R3, R2}, PRIV, 0, NULL}, + + {"mov", M, OpX3X6b (1, 0, 0x10), {R1, RR_R3}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x11), {R1, DBR_R3}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x12), {R1, IBR_R3}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x13), {R1, PKR_R3}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x14), {R1, PMC_R3}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x15), {R1, PMD_R3}, EMPTY}, + {"mov", M, OpX3X6b (1, 0, 0x16), {R1, MSR_R3}, PRIV, 0, NULL}, + {"mov", M, OpX3X6b (1, 0, 0x17), {R1, CPUID_R3}, EMPTY}, + + {"ptc.l", M0, OpX3X6b (1, 0, 0x09), {R3, R2}, PRIV, 0, NULL}, + {"ptc.g", M0, OpX3X6b (1, 0, 0x0a), {R3, R2}, LAST | PRIV, 0, NULL}, + {"ptc.ga", M0, OpX3X6b (1, 0, 0x0b), {R3, R2}, LAST | PRIV, 0, NULL}, + {"ptr.d", M0, OpX3X6b (1, 0, 0x0c), {R3, R2}, PRIV, 0, NULL}, + {"ptr.i", M0, OpX3X6b (1, 0, 0x0d), {R3, R2}, PRIV, 0, NULL}, + + {"thash", M, OpX3X6b (1, 0, 0x1a), {R1, R3}, EMPTY}, + {"ttag", M, OpX3X6b (1, 0, 0x1b), {R1, R3}, EMPTY}, + {"tpa", M, OpX3X6b (1, 0, 0x1e), {R1, R3}, PRIV, 0, NULL}, + {"tak", M, OpX3X6b (1, 0, 0x1f), {R1, R3}, PRIV, 0, NULL}, + + {"chk.s.m", M0, OpX3 (1, 1), {R2, TGT25b}, EMPTY}, + {"chk.s", M0, OpX3 (1, 3), {F2, TGT25b}, EMPTY}, + + {"fc", M0, OpX3X6bX7 (1, 0, 0x30, 0), {R3}, EMPTY}, + {"fc.i", M0, OpX3X6bX7 (1, 0, 0x30, 1), {R3}, EMPTY}, + {"ptc.e", M0, OpX3X6b (1, 0, 0x34), {R3}, PRIV, 0, NULL}, + + /* integer load */ + {"ld1", M, OpMXX6aHint (4, 0, 0, 0x00, 0), {R1, MR3}, EMPTY}, + {"ld1.nt1", M, OpMXX6aHint (4, 0, 0, 0x00, 1), {R1, MR3}, EMPTY}, + {"ld1.nta", M, OpMXX6aHint (4, 0, 0, 0x00, 3), {R1, MR3}, EMPTY}, + {"ld2", M, OpMXX6aHint (4, 0, 0, 0x01, 0), {R1, MR3}, EMPTY}, + {"ld2.nt1", M, OpMXX6aHint (4, 0, 0, 0x01, 1), {R1, MR3}, EMPTY}, + {"ld2.nta", M, OpMXX6aHint (4, 0, 0, 0x01, 3), {R1, MR3}, EMPTY}, + {"ld4", M, OpMXX6aHint (4, 0, 0, 0x02, 0), {R1, MR3}, EMPTY}, + {"ld4.nt1", M, OpMXX6aHint (4, 0, 0, 0x02, 1), {R1, MR3}, EMPTY}, + {"ld4.nta", M, OpMXX6aHint (4, 0, 0, 0x02, 3), {R1, MR3}, EMPTY}, + {"ld8", M, OpMXX6aHint (4, 0, 0, 0x03, 0), {R1, MR3}, EMPTY}, + {"ld8.nt1", M, OpMXX6aHint (4, 0, 0, 0x03, 1), {R1, MR3}, EMPTY}, + {"ld8.nta", M, OpMXX6aHint (4, 0, 0, 0x03, 3), {R1, MR3}, EMPTY}, + {"ld16", M2, OpMXX6aHint (4, 0, 1, 0x28, 0), {R1, AR_CSD, MR3}, EMPTY}, + {"ld16", M, OpMXX6aHint (4, 0, 1, 0x28, 0), {R1, MR3}, PSEUDO, 0, NULL}, + {"ld16.nt1", M2, OpMXX6aHint (4, 0, 1, 0x28, 1), {R1, AR_CSD, MR3}, EMPTY}, + {"ld16.nt1", M, OpMXX6aHint (4, 0, 1, 0x28, 1), {R1, MR3}, PSEUDO, 0, NULL}, + {"ld16.nta", M2, OpMXX6aHint (4, 0, 1, 0x28, 3), {R1, AR_CSD, MR3}, EMPTY}, + {"ld16.nta", M, OpMXX6aHint (4, 0, 1, 0x28, 3), {R1, MR3}, PSEUDO, 0, NULL}, + {"ld1.s", M, OpMXX6aHint (4, 0, 0, 0x04, 0), {R1, MR3}, EMPTY}, + {"ld1.s.nt1", M, OpMXX6aHint (4, 0, 0, 0x04, 1), {R1, MR3}, EMPTY}, + {"ld1.s.nta", M, OpMXX6aHint (4, 0, 0, 0x04, 3), {R1, MR3}, EMPTY}, + {"ld2.s", M, OpMXX6aHint (4, 0, 0, 0x05, 0), {R1, MR3}, EMPTY}, + {"ld2.s.nt1", M, OpMXX6aHint (4, 0, 0, 0x05, 1), {R1, MR3}, EMPTY}, + {"ld2.s.nta", M, OpMXX6aHint (4, 0, 0, 0x05, 3), {R1, MR3}, EMPTY}, + {"ld4.s", M, OpMXX6aHint (4, 0, 0, 0x06, 0), {R1, MR3}, EMPTY}, + {"ld4.s.nt1", M, OpMXX6aHint (4, 0, 0, 0x06, 1), {R1, MR3}, EMPTY}, + {"ld4.s.nta", M, OpMXX6aHint (4, 0, 0, 0x06, 3), {R1, MR3}, EMPTY}, + {"ld8.s", M, OpMXX6aHint (4, 0, 0, 0x07, 0), {R1, MR3}, EMPTY}, + {"ld8.s.nt1", M, OpMXX6aHint (4, 0, 0, 0x07, 1), {R1, MR3}, EMPTY}, + {"ld8.s.nta", M, OpMXX6aHint (4, 0, 0, 0x07, 3), {R1, MR3}, EMPTY}, + {"ld1.a", M, OpMXX6aHint (4, 0, 0, 0x08, 0), {R1, MR3}, EMPTY}, + {"ld1.a.nt1", M, OpMXX6aHint (4, 0, 0, 0x08, 1), {R1, MR3}, EMPTY}, + {"ld1.a.nta", M, OpMXX6aHint (4, 0, 0, 0x08, 3), {R1, MR3}, EMPTY}, + {"ld2.a", M, OpMXX6aHint (4, 0, 0, 0x09, 0), {R1, MR3}, EMPTY}, + {"ld2.a.nt1", M, OpMXX6aHint (4, 0, 0, 0x09, 1), {R1, MR3}, EMPTY}, + {"ld2.a.nta", M, OpMXX6aHint (4, 0, 0, 0x09, 3), {R1, MR3}, EMPTY}, + {"ld4.a", M, OpMXX6aHint (4, 0, 0, 0x0a, 0), {R1, MR3}, EMPTY}, + {"ld4.a.nt1", M, OpMXX6aHint (4, 0, 0, 0x0a, 1), {R1, MR3}, EMPTY}, + {"ld4.a.nta", M, OpMXX6aHint (4, 0, 0, 0x0a, 3), {R1, MR3}, EMPTY}, + {"ld8.a", M, OpMXX6aHint (4, 0, 0, 0x0b, 0), {R1, MR3}, EMPTY}, + {"ld8.a.nt1", M, OpMXX6aHint (4, 0, 0, 0x0b, 1), {R1, MR3}, EMPTY}, + {"ld8.a.nta", M, OpMXX6aHint (4, 0, 0, 0x0b, 3), {R1, MR3}, EMPTY}, + {"ld1.sa", M, OpMXX6aHint (4, 0, 0, 0x0c, 0), {R1, MR3}, EMPTY}, + {"ld1.sa.nt1", M, OpMXX6aHint (4, 0, 0, 0x0c, 1), {R1, MR3}, EMPTY}, + {"ld1.sa.nta", M, OpMXX6aHint (4, 0, 0, 0x0c, 3), {R1, MR3}, EMPTY}, + {"ld2.sa", M, OpMXX6aHint (4, 0, 0, 0x0d, 0), {R1, MR3}, EMPTY}, + {"ld2.sa.nt1", M, OpMXX6aHint (4, 0, 0, 0x0d, 1), {R1, MR3}, EMPTY}, + {"ld2.sa.nta", M, OpMXX6aHint (4, 0, 0, 0x0d, 3), {R1, MR3}, EMPTY}, + {"ld4.sa", M, OpMXX6aHint (4, 0, 0, 0x0e, 0), {R1, MR3}, EMPTY}, + {"ld4.sa.nt1", M, OpMXX6aHint (4, 0, 0, 0x0e, 1), {R1, MR3}, EMPTY}, + {"ld4.sa.nta", M, OpMXX6aHint (4, 0, 0, 0x0e, 3), {R1, MR3}, EMPTY}, + {"ld8.sa", M, OpMXX6aHint (4, 0, 0, 0x0f, 0), {R1, MR3}, EMPTY}, + {"ld8.sa.nt1", M, OpMXX6aHint (4, 0, 0, 0x0f, 1), {R1, MR3}, EMPTY}, + {"ld8.sa.nta", M, OpMXX6aHint (4, 0, 0, 0x0f, 3), {R1, MR3}, EMPTY}, + {"ld1.bias", M, OpMXX6aHint (4, 0, 0, 0x10, 0), {R1, MR3}, EMPTY}, + {"ld1.bias.nt1", M, OpMXX6aHint (4, 0, 0, 0x10, 1), {R1, MR3}, EMPTY}, + {"ld1.bias.nta", M, OpMXX6aHint (4, 0, 0, 0x10, 3), {R1, MR3}, EMPTY}, + {"ld2.bias", M, OpMXX6aHint (4, 0, 0, 0x11, 0), {R1, MR3}, EMPTY}, + {"ld2.bias.nt1", M, OpMXX6aHint (4, 0, 0, 0x11, 1), {R1, MR3}, EMPTY}, + {"ld2.bias.nta", M, OpMXX6aHint (4, 0, 0, 0x11, 3), {R1, MR3}, EMPTY}, + {"ld4.bias", M, OpMXX6aHint (4, 0, 0, 0x12, 0), {R1, MR3}, EMPTY}, + {"ld4.bias.nt1", M, OpMXX6aHint (4, 0, 0, 0x12, 1), {R1, MR3}, EMPTY}, + {"ld4.bias.nta", M, OpMXX6aHint (4, 0, 0, 0x12, 3), {R1, MR3}, EMPTY}, + {"ld8.bias", M, OpMXX6aHint (4, 0, 0, 0x13, 0), {R1, MR3}, EMPTY}, + {"ld8.bias.nt1", M, OpMXX6aHint (4, 0, 0, 0x13, 1), {R1, MR3}, EMPTY}, + {"ld8.bias.nta", M, OpMXX6aHint (4, 0, 0, 0x13, 3), {R1, MR3}, EMPTY}, + {"ld1.acq", M, OpMXX6aHint (4, 0, 0, 0x14, 0), {R1, MR3}, EMPTY}, + {"ld1.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x14, 1), {R1, MR3}, EMPTY}, + {"ld1.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x14, 3), {R1, MR3}, EMPTY}, + {"ld2.acq", M, OpMXX6aHint (4, 0, 0, 0x15, 0), {R1, MR3}, EMPTY}, + {"ld2.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x15, 1), {R1, MR3}, EMPTY}, + {"ld2.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x15, 3), {R1, MR3}, EMPTY}, + {"ld4.acq", M, OpMXX6aHint (4, 0, 0, 0x16, 0), {R1, MR3}, EMPTY}, + {"ld4.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x16, 1), {R1, MR3}, EMPTY}, + {"ld4.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x16, 3), {R1, MR3}, EMPTY}, + {"ld8.acq", M, OpMXX6aHint (4, 0, 0, 0x17, 0), {R1, MR3}, EMPTY}, + {"ld8.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x17, 1), {R1, MR3}, EMPTY}, + {"ld8.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x17, 3), {R1, MR3}, EMPTY}, + {"ld16.acq", M2, OpMXX6aHint (4, 0, 1, 0x2c, 0), {R1, AR_CSD, MR3}, EMPTY}, + {"ld16.acq", M, OpMXX6aHint (4, 0, 1, 0x2c, 0), {R1, MR3}, PSEUDO, 0, NULL}, + {"ld16.acq.nt1", M2, OpMXX6aHint (4, 0, 1, 0x2c, 1), {R1, AR_CSD, MR3}, EMPTY}, + {"ld16.acq.nt1", M, OpMXX6aHint (4, 0, 1, 0x2c, 1), {R1, MR3}, PSEUDO, 0, NULL}, + {"ld16.acq.nta", M2, OpMXX6aHint (4, 0, 1, 0x2c, 3), {R1, AR_CSD, MR3}, EMPTY}, + {"ld16.acq.nta", M, OpMXX6aHint (4, 0, 1, 0x2c, 3), {R1, MR3}, PSEUDO, 0, NULL}, + {"ld8.fill", M, OpMXX6aHint (4, 0, 0, 0x1b, 0), {R1, MR3}, EMPTY}, + {"ld8.fill.nt1", M, OpMXX6aHint (4, 0, 0, 0x1b, 1), {R1, MR3}, EMPTY}, + {"ld8.fill.nta", M, OpMXX6aHint (4, 0, 0, 0x1b, 3), {R1, MR3}, EMPTY}, + {"ld1.c.clr", M, OpMXX6aHint (4, 0, 0, 0x20, 0), {R1, MR3}, EMPTY}, + {"ld1.c.clr.nt1", M, OpMXX6aHint (4, 0, 0, 0x20, 1), {R1, MR3}, EMPTY}, + {"ld1.c.clr.nta", M, OpMXX6aHint (4, 0, 0, 0x20, 3), {R1, MR3}, EMPTY}, + {"ld2.c.clr", M, OpMXX6aHint (4, 0, 0, 0x21, 0), {R1, MR3}, EMPTY}, + {"ld2.c.clr.nt1", M, OpMXX6aHint (4, 0, 0, 0x21, 1), {R1, MR3}, EMPTY}, + {"ld2.c.clr.nta", M, OpMXX6aHint (4, 0, 0, 0x21, 3), {R1, MR3}, EMPTY}, + {"ld4.c.clr", M, OpMXX6aHint (4, 0, 0, 0x22, 0), {R1, MR3}, EMPTY}, + {"ld4.c.clr.nt1", M, OpMXX6aHint (4, 0, 0, 0x22, 1), {R1, MR3}, EMPTY}, + {"ld4.c.clr.nta", M, OpMXX6aHint (4, 0, 0, 0x22, 3), {R1, MR3}, EMPTY}, + {"ld8.c.clr", M, OpMXX6aHint (4, 0, 0, 0x23, 0), {R1, MR3}, EMPTY}, + {"ld8.c.clr.nt1", M, OpMXX6aHint (4, 0, 0, 0x23, 1), {R1, MR3}, EMPTY}, + {"ld8.c.clr.nta", M, OpMXX6aHint (4, 0, 0, 0x23, 3), {R1, MR3}, EMPTY}, + {"ld1.c.nc", M, OpMXX6aHint (4, 0, 0, 0x24, 0), {R1, MR3}, EMPTY}, + {"ld1.c.nc.nt1", M, OpMXX6aHint (4, 0, 0, 0x24, 1), {R1, MR3}, EMPTY}, + {"ld1.c.nc.nta", M, OpMXX6aHint (4, 0, 0, 0x24, 3), {R1, MR3}, EMPTY}, + {"ld2.c.nc", M, OpMXX6aHint (4, 0, 0, 0x25, 0), {R1, MR3}, EMPTY}, + {"ld2.c.nc.nt1", M, OpMXX6aHint (4, 0, 0, 0x25, 1), {R1, MR3}, EMPTY}, + {"ld2.c.nc.nta", M, OpMXX6aHint (4, 0, 0, 0x25, 3), {R1, MR3}, EMPTY}, + {"ld4.c.nc", M, OpMXX6aHint (4, 0, 0, 0x26, 0), {R1, MR3}, EMPTY}, + {"ld4.c.nc.nt1", M, OpMXX6aHint (4, 0, 0, 0x26, 1), {R1, MR3}, EMPTY}, + {"ld4.c.nc.nta", M, OpMXX6aHint (4, 0, 0, 0x26, 3), {R1, MR3}, EMPTY}, + {"ld8.c.nc", M, OpMXX6aHint (4, 0, 0, 0x27, 0), {R1, MR3}, EMPTY}, + {"ld8.c.nc.nt1", M, OpMXX6aHint (4, 0, 0, 0x27, 1), {R1, MR3}, EMPTY}, + {"ld8.c.nc.nta", M, OpMXX6aHint (4, 0, 0, 0x27, 3), {R1, MR3}, EMPTY}, + {"ld1.c.clr.acq", M, OpMXX6aHint (4, 0, 0, 0x28, 0), {R1, MR3}, EMPTY}, + {"ld1.c.clr.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x28, 1), {R1, MR3}, EMPTY}, + {"ld1.c.clr.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x28, 3), {R1, MR3}, EMPTY}, + {"ld2.c.clr.acq", M, OpMXX6aHint (4, 0, 0, 0x29, 0), {R1, MR3}, EMPTY}, + {"ld2.c.clr.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x29, 1), {R1, MR3}, EMPTY}, + {"ld2.c.clr.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x29, 3), {R1, MR3}, EMPTY}, + {"ld4.c.clr.acq", M, OpMXX6aHint (4, 0, 0, 0x2a, 0), {R1, MR3}, EMPTY}, + {"ld4.c.clr.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x2a, 1), {R1, MR3}, EMPTY}, + {"ld4.c.clr.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x2a, 3), {R1, MR3}, EMPTY}, + {"ld8.c.clr.acq", M, OpMXX6aHint (4, 0, 0, 0x2b, 0), {R1, MR3}, EMPTY}, + {"ld8.c.clr.acq.nt1", M, OpMXX6aHint (4, 0, 0, 0x2b, 1), {R1, MR3}, EMPTY}, + {"ld8.c.clr.acq.nta", M, OpMXX6aHint (4, 0, 0, 0x2b, 3), {R1, MR3}, EMPTY}, + + /* Pseudo-op that generates ldxmov relocation. */ + {"ld8.mov", M, OpMXX6aHint (4, 0, 0, 0x03, 0), + {R1, MR3, IA64_OPND_LDXMOV}, EMPTY}, + + /* Integer load w/increment by register. */ +#define LDINCREG(c,h) M, OpMXX6aHint (4, 1, 0, c, h), {R1, MR3, R2}, POSTINC, 0, NULL + {"ld1", LDINCREG (0x00, 0)}, + {"ld1.nt1", LDINCREG (0x00, 1)}, + {"ld1.nta", LDINCREG (0x00, 3)}, + {"ld2", LDINCREG (0x01, 0)}, + {"ld2.nt1", LDINCREG (0x01, 1)}, + {"ld2.nta", LDINCREG (0x01, 3)}, + {"ld4", LDINCREG (0x02, 0)}, + {"ld4.nt1", LDINCREG (0x02, 1)}, + {"ld4.nta", LDINCREG (0x02, 3)}, + {"ld8", LDINCREG (0x03, 0)}, + {"ld8.nt1", LDINCREG (0x03, 1)}, + {"ld8.nta", LDINCREG (0x03, 3)}, + {"ld1.s", LDINCREG (0x04, 0)}, + {"ld1.s.nt1", LDINCREG (0x04, 1)}, + {"ld1.s.nta", LDINCREG (0x04, 3)}, + {"ld2.s", LDINCREG (0x05, 0)}, + {"ld2.s.nt1", LDINCREG (0x05, 1)}, + {"ld2.s.nta", LDINCREG (0x05, 3)}, + {"ld4.s", LDINCREG (0x06, 0)}, + {"ld4.s.nt1", LDINCREG (0x06, 1)}, + {"ld4.s.nta", LDINCREG (0x06, 3)}, + {"ld8.s", LDINCREG (0x07, 0)}, + {"ld8.s.nt1", LDINCREG (0x07, 1)}, + {"ld8.s.nta", LDINCREG (0x07, 3)}, + {"ld1.a", LDINCREG (0x08, 0)}, + {"ld1.a.nt1", LDINCREG (0x08, 1)}, + {"ld1.a.nta", LDINCREG (0x08, 3)}, + {"ld2.a", LDINCREG (0x09, 0)}, + {"ld2.a.nt1", LDINCREG (0x09, 1)}, + {"ld2.a.nta", LDINCREG (0x09, 3)}, + {"ld4.a", LDINCREG (0x0a, 0)}, + {"ld4.a.nt1", LDINCREG (0x0a, 1)}, + {"ld4.a.nta", LDINCREG (0x0a, 3)}, + {"ld8.a", LDINCREG (0x0b, 0)}, + {"ld8.a.nt1", LDINCREG (0x0b, 1)}, + {"ld8.a.nta", LDINCREG (0x0b, 3)}, + {"ld1.sa", LDINCREG (0x0c, 0)}, + {"ld1.sa.nt1", LDINCREG (0x0c, 1)}, + {"ld1.sa.nta", LDINCREG (0x0c, 3)}, + {"ld2.sa", LDINCREG (0x0d, 0)}, + {"ld2.sa.nt1", LDINCREG (0x0d, 1)}, + {"ld2.sa.nta", LDINCREG (0x0d, 3)}, + {"ld4.sa", LDINCREG (0x0e, 0)}, + {"ld4.sa.nt1", LDINCREG (0x0e, 1)}, + {"ld4.sa.nta", LDINCREG (0x0e, 3)}, + {"ld8.sa", LDINCREG (0x0f, 0)}, + {"ld8.sa.nt1", LDINCREG (0x0f, 1)}, + {"ld8.sa.nta", LDINCREG (0x0f, 3)}, + {"ld1.bias", LDINCREG (0x10, 0)}, + {"ld1.bias.nt1", LDINCREG (0x10, 1)}, + {"ld1.bias.nta", LDINCREG (0x10, 3)}, + {"ld2.bias", LDINCREG (0x11, 0)}, + {"ld2.bias.nt1", LDINCREG (0x11, 1)}, + {"ld2.bias.nta", LDINCREG (0x11, 3)}, + {"ld4.bias", LDINCREG (0x12, 0)}, + {"ld4.bias.nt1", LDINCREG (0x12, 1)}, + {"ld4.bias.nta", LDINCREG (0x12, 3)}, + {"ld8.bias", LDINCREG (0x13, 0)}, + {"ld8.bias.nt1", LDINCREG (0x13, 1)}, + {"ld8.bias.nta", LDINCREG (0x13, 3)}, + {"ld1.acq", LDINCREG (0x14, 0)}, + {"ld1.acq.nt1", LDINCREG (0x14, 1)}, + {"ld1.acq.nta", LDINCREG (0x14, 3)}, + {"ld2.acq", LDINCREG (0x15, 0)}, + {"ld2.acq.nt1", LDINCREG (0x15, 1)}, + {"ld2.acq.nta", LDINCREG (0x15, 3)}, + {"ld4.acq", LDINCREG (0x16, 0)}, + {"ld4.acq.nt1", LDINCREG (0x16, 1)}, + {"ld4.acq.nta", LDINCREG (0x16, 3)}, + {"ld8.acq", LDINCREG (0x17, 0)}, + {"ld8.acq.nt1", LDINCREG (0x17, 1)}, + {"ld8.acq.nta", LDINCREG (0x17, 3)}, + {"ld8.fill", LDINCREG (0x1b, 0)}, + {"ld8.fill.nt1", LDINCREG (0x1b, 1)}, + {"ld8.fill.nta", LDINCREG (0x1b, 3)}, + {"ld1.c.clr", LDINCREG (0x20, 0)}, + {"ld1.c.clr.nt1", LDINCREG (0x20, 1)}, + {"ld1.c.clr.nta", LDINCREG (0x20, 3)}, + {"ld2.c.clr", LDINCREG (0x21, 0)}, + {"ld2.c.clr.nt1", LDINCREG (0x21, 1)}, + {"ld2.c.clr.nta", LDINCREG (0x21, 3)}, + {"ld4.c.clr", LDINCREG (0x22, 0)}, + {"ld4.c.clr.nt1", LDINCREG (0x22, 1)}, + {"ld4.c.clr.nta", LDINCREG (0x22, 3)}, + {"ld8.c.clr", LDINCREG (0x23, 0)}, + {"ld8.c.clr.nt1", LDINCREG (0x23, 1)}, + {"ld8.c.clr.nta", LDINCREG (0x23, 3)}, + {"ld1.c.nc", LDINCREG (0x24, 0)}, + {"ld1.c.nc.nt1", LDINCREG (0x24, 1)}, + {"ld1.c.nc.nta", LDINCREG (0x24, 3)}, + {"ld2.c.nc", LDINCREG (0x25, 0)}, + {"ld2.c.nc.nt1", LDINCREG (0x25, 1)}, + {"ld2.c.nc.nta", LDINCREG (0x25, 3)}, + {"ld4.c.nc", LDINCREG (0x26, 0)}, + {"ld4.c.nc.nt1", LDINCREG (0x26, 1)}, + {"ld4.c.nc.nta", LDINCREG (0x26, 3)}, + {"ld8.c.nc", LDINCREG (0x27, 0)}, + {"ld8.c.nc.nt1", LDINCREG (0x27, 1)}, + {"ld8.c.nc.nta", LDINCREG (0x27, 3)}, + {"ld1.c.clr.acq", LDINCREG (0x28, 0)}, + {"ld1.c.clr.acq.nt1", LDINCREG (0x28, 1)}, + {"ld1.c.clr.acq.nta", LDINCREG (0x28, 3)}, + {"ld2.c.clr.acq", LDINCREG (0x29, 0)}, + {"ld2.c.clr.acq.nt1", LDINCREG (0x29, 1)}, + {"ld2.c.clr.acq.nta", LDINCREG (0x29, 3)}, + {"ld4.c.clr.acq", LDINCREG (0x2a, 0)}, + {"ld4.c.clr.acq.nt1", LDINCREG (0x2a, 1)}, + {"ld4.c.clr.acq.nta", LDINCREG (0x2a, 3)}, + {"ld8.c.clr.acq", LDINCREG (0x2b, 0)}, + {"ld8.c.clr.acq.nt1", LDINCREG (0x2b, 1)}, + {"ld8.c.clr.acq.nta", LDINCREG (0x2b, 3)}, +#undef LDINCREG + + {"st1", M, OpMXX6aHint (4, 0, 0, 0x30, 0), {MR3, R2}, EMPTY}, + {"st1.nta", M, OpMXX6aHint (4, 0, 0, 0x30, 3), {MR3, R2}, EMPTY}, + {"st2", M, OpMXX6aHint (4, 0, 0, 0x31, 0), {MR3, R2}, EMPTY}, + {"st2.nta", M, OpMXX6aHint (4, 0, 0, 0x31, 3), {MR3, R2}, EMPTY}, + {"st4", M, OpMXX6aHint (4, 0, 0, 0x32, 0), {MR3, R2}, EMPTY}, + {"st4.nta", M, OpMXX6aHint (4, 0, 0, 0x32, 3), {MR3, R2}, EMPTY}, + {"st8", M, OpMXX6aHint (4, 0, 0, 0x33, 0), {MR3, R2}, EMPTY}, + {"st8.nta", M, OpMXX6aHint (4, 0, 0, 0x33, 3), {MR3, R2}, EMPTY}, + {"st16", M, OpMXX6aHint (4, 0, 1, 0x30, 0), {MR3, R2, AR_CSD}, EMPTY}, + {"st16", M, OpMXX6aHint (4, 0, 1, 0x30, 0), {MR3, R2}, PSEUDO, 0, NULL}, + {"st16.nta", M, OpMXX6aHint (4, 0, 1, 0x30, 3), {MR3, R2, AR_CSD}, EMPTY}, + {"st16.nta", M, OpMXX6aHint (4, 0, 1, 0x30, 3), {MR3, R2}, PSEUDO, 0, NULL}, + {"st1.rel", M, OpMXX6aHint (4, 0, 0, 0x34, 0), {MR3, R2}, EMPTY}, + {"st1.rel.nta", M, OpMXX6aHint (4, 0, 0, 0x34, 3), {MR3, R2}, EMPTY}, + {"st2.rel", M, OpMXX6aHint (4, 0, 0, 0x35, 0), {MR3, R2}, EMPTY}, + {"st2.rel.nta", M, OpMXX6aHint (4, 0, 0, 0x35, 3), {MR3, R2}, EMPTY}, + {"st4.rel", M, OpMXX6aHint (4, 0, 0, 0x36, 0), {MR3, R2}, EMPTY}, + {"st4.rel.nta", M, OpMXX6aHint (4, 0, 0, 0x36, 3), {MR3, R2}, EMPTY}, + {"st8.rel", M, OpMXX6aHint (4, 0, 0, 0x37, 0), {MR3, R2}, EMPTY}, + {"st8.rel.nta", M, OpMXX6aHint (4, 0, 0, 0x37, 3), {MR3, R2}, EMPTY}, + {"st16.rel", M, OpMXX6aHint (4, 0, 1, 0x34, 0), {MR3, R2, AR_CSD}, EMPTY}, + {"st16.rel", M, OpMXX6aHint (4, 0, 1, 0x34, 0), {MR3, R2}, PSEUDO, 0, NULL}, + {"st16.rel.nta", M, OpMXX6aHint (4, 0, 1, 0x34, 3), {MR3, R2, AR_CSD}, EMPTY}, + {"st16.rel.nta", M, OpMXX6aHint (4, 0, 1, 0x34, 3), {MR3, R2}, PSEUDO, 0, NULL}, + {"st8.spill", M, OpMXX6aHint (4, 0, 0, 0x3b, 0), {MR3, R2}, EMPTY}, + {"st8.spill.nta", M, OpMXX6aHint (4, 0, 0, 0x3b, 3), {MR3, R2}, EMPTY}, + +#define CMPXCHG(c,h) M, OpMXX6aHint (4, 0, 1, c, h), {R1, MR3, R2, AR_CCV}, EMPTY +#define CMPXCHG_P(c,h) M, OpMXX6aHint (4, 0, 1, c, h), {R1, MR3, R2}, PSEUDO, 0, NULL +#define CMPXCHG16(c,h) M, OpMXX6aHint (4, 0, 1, c, h), {R1, MR3, R2, AR_CSD, AR_CCV}, EMPTY +#define CMPXCHG16_P(c,h) M, OpMXX6aHint (4, 0, 1, c, h), {R1, MR3, R2}, PSEUDO, 0, NULL +#define CMPXCHG_acq 0 +#define CMPXCHG_rel 4 +#define CMPXCHG_1 0 +#define CMPXCHG_2 1 +#define CMPXCHG_4 2 +#define CMPXCHG_8 3 +#define CMPXCHGn(n, s) \ + {"cmpxchg"#n"."#s, CMPXCHG (CMPXCHG_##n|CMPXCHG_##s, 0)}, \ + {"cmpxchg"#n"."#s, CMPXCHG_P (CMPXCHG_##n|CMPXCHG_##s, 0)}, \ + {"cmpxchg"#n"."#s".nt1", CMPXCHG (CMPXCHG_##n|CMPXCHG_##s, 1)}, \ + {"cmpxchg"#n"."#s".nt1", CMPXCHG_P (CMPXCHG_##n|CMPXCHG_##s, 1)}, \ + {"cmpxchg"#n"."#s".nta", CMPXCHG (CMPXCHG_##n|CMPXCHG_##s, 3)}, \ + {"cmpxchg"#n"."#s".nta", CMPXCHG_P (CMPXCHG_##n|CMPXCHG_##s, 3)} +#define CMP8XCHG16(s) \ + {"cmp8xchg16."#s, CMPXCHG16 (0x20|CMPXCHG_##s, 0)}, \ + {"cmp8xchg16."#s, CMPXCHG16_P (0x20|CMPXCHG_##s, 0)}, \ + {"cmp8xchg16."#s".nt1", CMPXCHG16 (0x20|CMPXCHG_##s, 1)}, \ + {"cmp8xchg16."#s".nt1", CMPXCHG16_P (0x20|CMPXCHG_##s, 1)}, \ + {"cmp8xchg16."#s".nta", CMPXCHG16 (0x20|CMPXCHG_##s, 3)}, \ + {"cmp8xchg16."#s".nta", CMPXCHG16_P (0x20|CMPXCHG_##s, 3)} +#define CMPXCHG_ALL(s) CMPXCHGn(1, s), \ + CMPXCHGn(2, s), \ + CMPXCHGn(4, s), \ + CMPXCHGn(8, s), \ + CMP8XCHG16(s) + CMPXCHG_ALL(acq), + CMPXCHG_ALL(rel), +#undef CMPXCHG +#undef CMPXCHG_P +#undef CMPXCHG16 +#undef CMPXCHG16_P +#undef CMPXCHG_acq +#undef CMPXCHG_rel +#undef CMPXCHG_1 +#undef CMPXCHG_2 +#undef CMPXCHG_4 +#undef CMPXCHG_8 +#undef CMPXCHGn +#undef CMPXCHG16 +#undef CMPXCHG_ALL + {"xchg1", M, OpMXX6aHint (4, 0, 1, 0x08, 0), {R1, MR3, R2}, EMPTY}, + {"xchg1.nt1", M, OpMXX6aHint (4, 0, 1, 0x08, 1), {R1, MR3, R2}, EMPTY}, + {"xchg1.nta", M, OpMXX6aHint (4, 0, 1, 0x08, 3), {R1, MR3, R2}, EMPTY}, + {"xchg2", M, OpMXX6aHint (4, 0, 1, 0x09, 0), {R1, MR3, R2}, EMPTY}, + {"xchg2.nt1", M, OpMXX6aHint (4, 0, 1, 0x09, 1), {R1, MR3, R2}, EMPTY}, + {"xchg2.nta", M, OpMXX6aHint (4, 0, 1, 0x09, 3), {R1, MR3, R2}, EMPTY}, + {"xchg4", M, OpMXX6aHint (4, 0, 1, 0x0a, 0), {R1, MR3, R2}, EMPTY}, + {"xchg4.nt1", M, OpMXX6aHint (4, 0, 1, 0x0a, 1), {R1, MR3, R2}, EMPTY}, + {"xchg4.nta", M, OpMXX6aHint (4, 0, 1, 0x0a, 3), {R1, MR3, R2}, EMPTY}, + {"xchg8", M, OpMXX6aHint (4, 0, 1, 0x0b, 0), {R1, MR3, R2}, EMPTY}, + {"xchg8.nt1", M, OpMXX6aHint (4, 0, 1, 0x0b, 1), {R1, MR3, R2}, EMPTY}, + {"xchg8.nta", M, OpMXX6aHint (4, 0, 1, 0x0b, 3), {R1, MR3, R2}, EMPTY}, + + {"fetchadd4.acq", M, OpMXX6aHint (4, 0, 1, 0x12, 0), {R1, MR3, INC3}, EMPTY}, + {"fetchadd4.acq.nt1", M, OpMXX6aHint (4, 0, 1, 0x12, 1), {R1, MR3, INC3}, EMPTY}, + {"fetchadd4.acq.nta", M, OpMXX6aHint (4, 0, 1, 0x12, 3), {R1, MR3, INC3}, EMPTY}, + {"fetchadd8.acq", M, OpMXX6aHint (4, 0, 1, 0x13, 0), {R1, MR3, INC3}, EMPTY}, + {"fetchadd8.acq.nt1", M, OpMXX6aHint (4, 0, 1, 0x13, 1), {R1, MR3, INC3}, EMPTY}, + {"fetchadd8.acq.nta", M, OpMXX6aHint (4, 0, 1, 0x13, 3), {R1, MR3, INC3}, EMPTY}, + {"fetchadd4.rel", M, OpMXX6aHint (4, 0, 1, 0x16, 0), {R1, MR3, INC3}, EMPTY}, + {"fetchadd4.rel.nt1", M, OpMXX6aHint (4, 0, 1, 0x16, 1), {R1, MR3, INC3}, EMPTY}, + {"fetchadd4.rel.nta", M, OpMXX6aHint (4, 0, 1, 0x16, 3), {R1, MR3, INC3}, EMPTY}, + {"fetchadd8.rel", M, OpMXX6aHint (4, 0, 1, 0x17, 0), {R1, MR3, INC3}, EMPTY}, + {"fetchadd8.rel.nt1", M, OpMXX6aHint (4, 0, 1, 0x17, 1), {R1, MR3, INC3}, EMPTY}, + {"fetchadd8.rel.nta", M, OpMXX6aHint (4, 0, 1, 0x17, 3), {R1, MR3, INC3}, EMPTY}, + + {"getf.sig", M, OpMXX6a (4, 0, 1, 0x1c), {R1, F2}, EMPTY}, + {"getf.exp", M, OpMXX6a (4, 0, 1, 0x1d), {R1, F2}, EMPTY}, + {"getf.s", M, OpMXX6a (4, 0, 1, 0x1e), {R1, F2}, EMPTY}, + {"getf.d", M, OpMXX6a (4, 0, 1, 0x1f), {R1, F2}, EMPTY}, + + /* Integer load w/increment by immediate. */ +#define LDINCIMMED(c,h) M, OpX6aHint (5, c, h), {R1, MR3, IMM9b}, POSTINC, 0, NULL + {"ld1", LDINCIMMED (0x00, 0)}, + {"ld1.nt1", LDINCIMMED (0x00, 1)}, + {"ld1.nta", LDINCIMMED (0x00, 3)}, + {"ld2", LDINCIMMED (0x01, 0)}, + {"ld2.nt1", LDINCIMMED (0x01, 1)}, + {"ld2.nta", LDINCIMMED (0x01, 3)}, + {"ld4", LDINCIMMED (0x02, 0)}, + {"ld4.nt1", LDINCIMMED (0x02, 1)}, + {"ld4.nta", LDINCIMMED (0x02, 3)}, + {"ld8", LDINCIMMED (0x03, 0)}, + {"ld8.nt1", LDINCIMMED (0x03, 1)}, + {"ld8.nta", LDINCIMMED (0x03, 3)}, + {"ld1.s", LDINCIMMED (0x04, 0)}, + {"ld1.s.nt1", LDINCIMMED (0x04, 1)}, + {"ld1.s.nta", LDINCIMMED (0x04, 3)}, + {"ld2.s", LDINCIMMED (0x05, 0)}, + {"ld2.s.nt1", LDINCIMMED (0x05, 1)}, + {"ld2.s.nta", LDINCIMMED (0x05, 3)}, + {"ld4.s", LDINCIMMED (0x06, 0)}, + {"ld4.s.nt1", LDINCIMMED (0x06, 1)}, + {"ld4.s.nta", LDINCIMMED (0x06, 3)}, + {"ld8.s", LDINCIMMED (0x07, 0)}, + {"ld8.s.nt1", LDINCIMMED (0x07, 1)}, + {"ld8.s.nta", LDINCIMMED (0x07, 3)}, + {"ld1.a", LDINCIMMED (0x08, 0)}, + {"ld1.a.nt1", LDINCIMMED (0x08, 1)}, + {"ld1.a.nta", LDINCIMMED (0x08, 3)}, + {"ld2.a", LDINCIMMED (0x09, 0)}, + {"ld2.a.nt1", LDINCIMMED (0x09, 1)}, + {"ld2.a.nta", LDINCIMMED (0x09, 3)}, + {"ld4.a", LDINCIMMED (0x0a, 0)}, + {"ld4.a.nt1", LDINCIMMED (0x0a, 1)}, + {"ld4.a.nta", LDINCIMMED (0x0a, 3)}, + {"ld8.a", LDINCIMMED (0x0b, 0)}, + {"ld8.a.nt1", LDINCIMMED (0x0b, 1)}, + {"ld8.a.nta", LDINCIMMED (0x0b, 3)}, + {"ld1.sa", LDINCIMMED (0x0c, 0)}, + {"ld1.sa.nt1", LDINCIMMED (0x0c, 1)}, + {"ld1.sa.nta", LDINCIMMED (0x0c, 3)}, + {"ld2.sa", LDINCIMMED (0x0d, 0)}, + {"ld2.sa.nt1", LDINCIMMED (0x0d, 1)}, + {"ld2.sa.nta", LDINCIMMED (0x0d, 3)}, + {"ld4.sa", LDINCIMMED (0x0e, 0)}, + {"ld4.sa.nt1", LDINCIMMED (0x0e, 1)}, + {"ld4.sa.nta", LDINCIMMED (0x0e, 3)}, + {"ld8.sa", LDINCIMMED (0x0f, 0)}, + {"ld8.sa.nt1", LDINCIMMED (0x0f, 1)}, + {"ld8.sa.nta", LDINCIMMED (0x0f, 3)}, + {"ld1.bias", LDINCIMMED (0x10, 0)}, + {"ld1.bias.nt1", LDINCIMMED (0x10, 1)}, + {"ld1.bias.nta", LDINCIMMED (0x10, 3)}, + {"ld2.bias", LDINCIMMED (0x11, 0)}, + {"ld2.bias.nt1", LDINCIMMED (0x11, 1)}, + {"ld2.bias.nta", LDINCIMMED (0x11, 3)}, + {"ld4.bias", LDINCIMMED (0x12, 0)}, + {"ld4.bias.nt1", LDINCIMMED (0x12, 1)}, + {"ld4.bias.nta", LDINCIMMED (0x12, 3)}, + {"ld8.bias", LDINCIMMED (0x13, 0)}, + {"ld8.bias.nt1", LDINCIMMED (0x13, 1)}, + {"ld8.bias.nta", LDINCIMMED (0x13, 3)}, + {"ld1.acq", LDINCIMMED (0x14, 0)}, + {"ld1.acq.nt1", LDINCIMMED (0x14, 1)}, + {"ld1.acq.nta", LDINCIMMED (0x14, 3)}, + {"ld2.acq", LDINCIMMED (0x15, 0)}, + {"ld2.acq.nt1", LDINCIMMED (0x15, 1)}, + {"ld2.acq.nta", LDINCIMMED (0x15, 3)}, + {"ld4.acq", LDINCIMMED (0x16, 0)}, + {"ld4.acq.nt1", LDINCIMMED (0x16, 1)}, + {"ld4.acq.nta", LDINCIMMED (0x16, 3)}, + {"ld8.acq", LDINCIMMED (0x17, 0)}, + {"ld8.acq.nt1", LDINCIMMED (0x17, 1)}, + {"ld8.acq.nta", LDINCIMMED (0x17, 3)}, + {"ld8.fill", LDINCIMMED (0x1b, 0)}, + {"ld8.fill.nt1", LDINCIMMED (0x1b, 1)}, + {"ld8.fill.nta", LDINCIMMED (0x1b, 3)}, + {"ld1.c.clr", LDINCIMMED (0x20, 0)}, + {"ld1.c.clr.nt1", LDINCIMMED (0x20, 1)}, + {"ld1.c.clr.nta", LDINCIMMED (0x20, 3)}, + {"ld2.c.clr", LDINCIMMED (0x21, 0)}, + {"ld2.c.clr.nt1", LDINCIMMED (0x21, 1)}, + {"ld2.c.clr.nta", LDINCIMMED (0x21, 3)}, + {"ld4.c.clr", LDINCIMMED (0x22, 0)}, + {"ld4.c.clr.nt1", LDINCIMMED (0x22, 1)}, + {"ld4.c.clr.nta", LDINCIMMED (0x22, 3)}, + {"ld8.c.clr", LDINCIMMED (0x23, 0)}, + {"ld8.c.clr.nt1", LDINCIMMED (0x23, 1)}, + {"ld8.c.clr.nta", LDINCIMMED (0x23, 3)}, + {"ld1.c.nc", LDINCIMMED (0x24, 0)}, + {"ld1.c.nc.nt1", LDINCIMMED (0x24, 1)}, + {"ld1.c.nc.nta", LDINCIMMED (0x24, 3)}, + {"ld2.c.nc", LDINCIMMED (0x25, 0)}, + {"ld2.c.nc.nt1", LDINCIMMED (0x25, 1)}, + {"ld2.c.nc.nta", LDINCIMMED (0x25, 3)}, + {"ld4.c.nc", LDINCIMMED (0x26, 0)}, + {"ld4.c.nc.nt1", LDINCIMMED (0x26, 1)}, + {"ld4.c.nc.nta", LDINCIMMED (0x26, 3)}, + {"ld8.c.nc", LDINCIMMED (0x27, 0)}, + {"ld8.c.nc.nt1", LDINCIMMED (0x27, 1)}, + {"ld8.c.nc.nta", LDINCIMMED (0x27, 3)}, + {"ld1.c.clr.acq", LDINCIMMED (0x28, 0)}, + {"ld1.c.clr.acq.nt1", LDINCIMMED (0x28, 1)}, + {"ld1.c.clr.acq.nta", LDINCIMMED (0x28, 3)}, + {"ld2.c.clr.acq", LDINCIMMED (0x29, 0)}, + {"ld2.c.clr.acq.nt1", LDINCIMMED (0x29, 1)}, + {"ld2.c.clr.acq.nta", LDINCIMMED (0x29, 3)}, + {"ld4.c.clr.acq", LDINCIMMED (0x2a, 0)}, + {"ld4.c.clr.acq.nt1", LDINCIMMED (0x2a, 1)}, + {"ld4.c.clr.acq.nta", LDINCIMMED (0x2a, 3)}, + {"ld8.c.clr.acq", LDINCIMMED (0x2b, 0)}, + {"ld8.c.clr.acq.nt1", LDINCIMMED (0x2b, 1)}, + {"ld8.c.clr.acq.nta", LDINCIMMED (0x2b, 3)}, +#undef LDINCIMMED + + /* Store w/increment by immediate. */ +#define STINCIMMED(c,h) M, OpX6aHint (5, c, h), {MR3, R2, IMM9a}, POSTINC, 0, NULL + {"st1", STINCIMMED (0x30, 0)}, + {"st1.nta", STINCIMMED (0x30, 3)}, + {"st2", STINCIMMED (0x31, 0)}, + {"st2.nta", STINCIMMED (0x31, 3)}, + {"st4", STINCIMMED (0x32, 0)}, + {"st4.nta", STINCIMMED (0x32, 3)}, + {"st8", STINCIMMED (0x33, 0)}, + {"st8.nta", STINCIMMED (0x33, 3)}, + {"st1.rel", STINCIMMED (0x34, 0)}, + {"st1.rel.nta", STINCIMMED (0x34, 3)}, + {"st2.rel", STINCIMMED (0x35, 0)}, + {"st2.rel.nta", STINCIMMED (0x35, 3)}, + {"st4.rel", STINCIMMED (0x36, 0)}, + {"st4.rel.nta", STINCIMMED (0x36, 3)}, + {"st8.rel", STINCIMMED (0x37, 0)}, + {"st8.rel.nta", STINCIMMED (0x37, 3)}, + {"st8.spill", STINCIMMED (0x3b, 0)}, + {"st8.spill.nta", STINCIMMED (0x3b, 3)}, +#undef STINCIMMED + + /* Floating-point load. */ + {"ldfs", M, OpMXX6aHint (6, 0, 0, 0x02, 0), {F1, MR3}, EMPTY}, + {"ldfs.nt1", M, OpMXX6aHint (6, 0, 0, 0x02, 1), {F1, MR3}, EMPTY}, + {"ldfs.nta", M, OpMXX6aHint (6, 0, 0, 0x02, 3), {F1, MR3}, EMPTY}, + {"ldfd", M, OpMXX6aHint (6, 0, 0, 0x03, 0), {F1, MR3}, EMPTY}, + {"ldfd.nt1", M, OpMXX6aHint (6, 0, 0, 0x03, 1), {F1, MR3}, EMPTY}, + {"ldfd.nta", M, OpMXX6aHint (6, 0, 0, 0x03, 3), {F1, MR3}, EMPTY}, + {"ldf8", M, OpMXX6aHint (6, 0, 0, 0x01, 0), {F1, MR3}, EMPTY}, + {"ldf8.nt1", M, OpMXX6aHint (6, 0, 0, 0x01, 1), {F1, MR3}, EMPTY}, + {"ldf8.nta", M, OpMXX6aHint (6, 0, 0, 0x01, 3), {F1, MR3}, EMPTY}, + {"ldfe", M, OpMXX6aHint (6, 0, 0, 0x00, 0), {F1, MR3}, EMPTY}, + {"ldfe.nt1", M, OpMXX6aHint (6, 0, 0, 0x00, 1), {F1, MR3}, EMPTY}, + {"ldfe.nta", M, OpMXX6aHint (6, 0, 0, 0x00, 3), {F1, MR3}, EMPTY}, + {"ldfs.s", M, OpMXX6aHint (6, 0, 0, 0x06, 0), {F1, MR3}, EMPTY}, + {"ldfs.s.nt1", M, OpMXX6aHint (6, 0, 0, 0x06, 1), {F1, MR3}, EMPTY}, + {"ldfs.s.nta", M, OpMXX6aHint (6, 0, 0, 0x06, 3), {F1, MR3}, EMPTY}, + {"ldfd.s", M, OpMXX6aHint (6, 0, 0, 0x07, 0), {F1, MR3}, EMPTY}, + {"ldfd.s.nt1", M, OpMXX6aHint (6, 0, 0, 0x07, 1), {F1, MR3}, EMPTY}, + {"ldfd.s.nta", M, OpMXX6aHint (6, 0, 0, 0x07, 3), {F1, MR3}, EMPTY}, + {"ldf8.s", M, OpMXX6aHint (6, 0, 0, 0x05, 0), {F1, MR3}, EMPTY}, + {"ldf8.s.nt1", M, OpMXX6aHint (6, 0, 0, 0x05, 1), {F1, MR3}, EMPTY}, + {"ldf8.s.nta", M, OpMXX6aHint (6, 0, 0, 0x05, 3), {F1, MR3}, EMPTY}, + {"ldfe.s", M, OpMXX6aHint (6, 0, 0, 0x04, 0), {F1, MR3}, EMPTY}, + {"ldfe.s.nt1", M, OpMXX6aHint (6, 0, 0, 0x04, 1), {F1, MR3}, EMPTY}, + {"ldfe.s.nta", M, OpMXX6aHint (6, 0, 0, 0x04, 3), {F1, MR3}, EMPTY}, + {"ldfs.a", M, OpMXX6aHint (6, 0, 0, 0x0a, 0), {F1, MR3}, EMPTY}, + {"ldfs.a.nt1", M, OpMXX6aHint (6, 0, 0, 0x0a, 1), {F1, MR3}, EMPTY}, + {"ldfs.a.nta", M, OpMXX6aHint (6, 0, 0, 0x0a, 3), {F1, MR3}, EMPTY}, + {"ldfd.a", M, OpMXX6aHint (6, 0, 0, 0x0b, 0), {F1, MR3}, EMPTY}, + {"ldfd.a.nt1", M, OpMXX6aHint (6, 0, 0, 0x0b, 1), {F1, MR3}, EMPTY}, + {"ldfd.a.nta", M, OpMXX6aHint (6, 0, 0, 0x0b, 3), {F1, MR3}, EMPTY}, + {"ldf8.a", M, OpMXX6aHint (6, 0, 0, 0x09, 0), {F1, MR3}, EMPTY}, + {"ldf8.a.nt1", M, OpMXX6aHint (6, 0, 0, 0x09, 1), {F1, MR3}, EMPTY}, + {"ldf8.a.nta", M, OpMXX6aHint (6, 0, 0, 0x09, 3), {F1, MR3}, EMPTY}, + {"ldfe.a", M, OpMXX6aHint (6, 0, 0, 0x08, 0), {F1, MR3}, EMPTY}, + {"ldfe.a.nt1", M, OpMXX6aHint (6, 0, 0, 0x08, 1), {F1, MR3}, EMPTY}, + {"ldfe.a.nta", M, OpMXX6aHint (6, 0, 0, 0x08, 3), {F1, MR3}, EMPTY}, + {"ldfs.sa", M, OpMXX6aHint (6, 0, 0, 0x0e, 0), {F1, MR3}, EMPTY}, + {"ldfs.sa.nt1", M, OpMXX6aHint (6, 0, 0, 0x0e, 1), {F1, MR3}, EMPTY}, + {"ldfs.sa.nta", M, OpMXX6aHint (6, 0, 0, 0x0e, 3), {F1, MR3}, EMPTY}, + {"ldfd.sa", M, OpMXX6aHint (6, 0, 0, 0x0f, 0), {F1, MR3}, EMPTY}, + {"ldfd.sa.nt1", M, OpMXX6aHint (6, 0, 0, 0x0f, 1), {F1, MR3}, EMPTY}, + {"ldfd.sa.nta", M, OpMXX6aHint (6, 0, 0, 0x0f, 3), {F1, MR3}, EMPTY}, + {"ldf8.sa", M, OpMXX6aHint (6, 0, 0, 0x0d, 0), {F1, MR3}, EMPTY}, + {"ldf8.sa.nt1", M, OpMXX6aHint (6, 0, 0, 0x0d, 1), {F1, MR3}, EMPTY}, + {"ldf8.sa.nta", M, OpMXX6aHint (6, 0, 0, 0x0d, 3), {F1, MR3}, EMPTY}, + {"ldfe.sa", M, OpMXX6aHint (6, 0, 0, 0x0c, 0), {F1, MR3}, EMPTY}, + {"ldfe.sa.nt1", M, OpMXX6aHint (6, 0, 0, 0x0c, 1), {F1, MR3}, EMPTY}, + {"ldfe.sa.nta", M, OpMXX6aHint (6, 0, 0, 0x0c, 3), {F1, MR3}, EMPTY}, + {"ldf.fill", M, OpMXX6aHint (6, 0, 0, 0x1b, 0), {F1, MR3}, EMPTY}, + {"ldf.fill.nt1", M, OpMXX6aHint (6, 0, 0, 0x1b, 1), {F1, MR3}, EMPTY}, + {"ldf.fill.nta", M, OpMXX6aHint (6, 0, 0, 0x1b, 3), {F1, MR3}, EMPTY}, + {"ldfs.c.clr", M, OpMXX6aHint (6, 0, 0, 0x22, 0), {F1, MR3}, EMPTY}, + {"ldfs.c.clr.nt1", M, OpMXX6aHint (6, 0, 0, 0x22, 1), {F1, MR3}, EMPTY}, + {"ldfs.c.clr.nta", M, OpMXX6aHint (6, 0, 0, 0x22, 3), {F1, MR3}, EMPTY}, + {"ldfd.c.clr", M, OpMXX6aHint (6, 0, 0, 0x23, 0), {F1, MR3}, EMPTY}, + {"ldfd.c.clr.nt1", M, OpMXX6aHint (6, 0, 0, 0x23, 1), {F1, MR3}, EMPTY}, + {"ldfd.c.clr.nta", M, OpMXX6aHint (6, 0, 0, 0x23, 3), {F1, MR3}, EMPTY}, + {"ldf8.c.clr", M, OpMXX6aHint (6, 0, 0, 0x21, 0), {F1, MR3}, EMPTY}, + {"ldf8.c.clr.nt1", M, OpMXX6aHint (6, 0, 0, 0x21, 1), {F1, MR3}, EMPTY}, + {"ldf8.c.clr.nta", M, OpMXX6aHint (6, 0, 0, 0x21, 3), {F1, MR3}, EMPTY}, + {"ldfe.c.clr", M, OpMXX6aHint (6, 0, 0, 0x20, 0), {F1, MR3}, EMPTY}, + {"ldfe.c.clr.nt1", M, OpMXX6aHint (6, 0, 0, 0x20, 1), {F1, MR3}, EMPTY}, + {"ldfe.c.clr.nta", M, OpMXX6aHint (6, 0, 0, 0x20, 3), {F1, MR3}, EMPTY}, + {"ldfs.c.nc", M, OpMXX6aHint (6, 0, 0, 0x26, 0), {F1, MR3}, EMPTY}, + {"ldfs.c.nc.nt1", M, OpMXX6aHint (6, 0, 0, 0x26, 1), {F1, MR3}, EMPTY}, + {"ldfs.c.nc.nta", M, OpMXX6aHint (6, 0, 0, 0x26, 3), {F1, MR3}, EMPTY}, + {"ldfd.c.nc", M, OpMXX6aHint (6, 0, 0, 0x27, 0), {F1, MR3}, EMPTY}, + {"ldfd.c.nc.nt1", M, OpMXX6aHint (6, 0, 0, 0x27, 1), {F1, MR3}, EMPTY}, + {"ldfd.c.nc.nta", M, OpMXX6aHint (6, 0, 0, 0x27, 3), {F1, MR3}, EMPTY}, + {"ldf8.c.nc", M, OpMXX6aHint (6, 0, 0, 0x25, 0), {F1, MR3}, EMPTY}, + {"ldf8.c.nc.nt1", M, OpMXX6aHint (6, 0, 0, 0x25, 1), {F1, MR3}, EMPTY}, + {"ldf8.c.nc.nta", M, OpMXX6aHint (6, 0, 0, 0x25, 3), {F1, MR3}, EMPTY}, + {"ldfe.c.nc", M, OpMXX6aHint (6, 0, 0, 0x24, 0), {F1, MR3}, EMPTY}, + {"ldfe.c.nc.nt1", M, OpMXX6aHint (6, 0, 0, 0x24, 1), {F1, MR3}, EMPTY}, + {"ldfe.c.nc.nta", M, OpMXX6aHint (6, 0, 0, 0x24, 3), {F1, MR3}, EMPTY}, + + /* Floating-point load w/increment by register. */ +#define FLDINCREG(c,h) M, OpMXX6aHint (6, 1, 0, c, h), {F1, MR3, R2}, POSTINC, 0, NULL + {"ldfs", FLDINCREG (0x02, 0)}, + {"ldfs.nt1", FLDINCREG (0x02, 1)}, + {"ldfs.nta", FLDINCREG (0x02, 3)}, + {"ldfd", FLDINCREG (0x03, 0)}, + {"ldfd.nt1", FLDINCREG (0x03, 1)}, + {"ldfd.nta", FLDINCREG (0x03, 3)}, + {"ldf8", FLDINCREG (0x01, 0)}, + {"ldf8.nt1", FLDINCREG (0x01, 1)}, + {"ldf8.nta", FLDINCREG (0x01, 3)}, + {"ldfe", FLDINCREG (0x00, 0)}, + {"ldfe.nt1", FLDINCREG (0x00, 1)}, + {"ldfe.nta", FLDINCREG (0x00, 3)}, + {"ldfs.s", FLDINCREG (0x06, 0)}, + {"ldfs.s.nt1", FLDINCREG (0x06, 1)}, + {"ldfs.s.nta", FLDINCREG (0x06, 3)}, + {"ldfd.s", FLDINCREG (0x07, 0)}, + {"ldfd.s.nt1", FLDINCREG (0x07, 1)}, + {"ldfd.s.nta", FLDINCREG (0x07, 3)}, + {"ldf8.s", FLDINCREG (0x05, 0)}, + {"ldf8.s.nt1", FLDINCREG (0x05, 1)}, + {"ldf8.s.nta", FLDINCREG (0x05, 3)}, + {"ldfe.s", FLDINCREG (0x04, 0)}, + {"ldfe.s.nt1", FLDINCREG (0x04, 1)}, + {"ldfe.s.nta", FLDINCREG (0x04, 3)}, + {"ldfs.a", FLDINCREG (0x0a, 0)}, + {"ldfs.a.nt1", FLDINCREG (0x0a, 1)}, + {"ldfs.a.nta", FLDINCREG (0x0a, 3)}, + {"ldfd.a", FLDINCREG (0x0b, 0)}, + {"ldfd.a.nt1", FLDINCREG (0x0b, 1)}, + {"ldfd.a.nta", FLDINCREG (0x0b, 3)}, + {"ldf8.a", FLDINCREG (0x09, 0)}, + {"ldf8.a.nt1", FLDINCREG (0x09, 1)}, + {"ldf8.a.nta", FLDINCREG (0x09, 3)}, + {"ldfe.a", FLDINCREG (0x08, 0)}, + {"ldfe.a.nt1", FLDINCREG (0x08, 1)}, + {"ldfe.a.nta", FLDINCREG (0x08, 3)}, + {"ldfs.sa", FLDINCREG (0x0e, 0)}, + {"ldfs.sa.nt1", FLDINCREG (0x0e, 1)}, + {"ldfs.sa.nta", FLDINCREG (0x0e, 3)}, + {"ldfd.sa", FLDINCREG (0x0f, 0)}, + {"ldfd.sa.nt1", FLDINCREG (0x0f, 1)}, + {"ldfd.sa.nta", FLDINCREG (0x0f, 3)}, + {"ldf8.sa", FLDINCREG (0x0d, 0)}, + {"ldf8.sa.nt1", FLDINCREG (0x0d, 1)}, + {"ldf8.sa.nta", FLDINCREG (0x0d, 3)}, + {"ldfe.sa", FLDINCREG (0x0c, 0)}, + {"ldfe.sa.nt1", FLDINCREG (0x0c, 1)}, + {"ldfe.sa.nta", FLDINCREG (0x0c, 3)}, + {"ldf.fill", FLDINCREG (0x1b, 0)}, + {"ldf.fill.nt1", FLDINCREG (0x1b, 1)}, + {"ldf.fill.nta", FLDINCREG (0x1b, 3)}, + {"ldfs.c.clr", FLDINCREG (0x22, 0)}, + {"ldfs.c.clr.nt1", FLDINCREG (0x22, 1)}, + {"ldfs.c.clr.nta", FLDINCREG (0x22, 3)}, + {"ldfd.c.clr", FLDINCREG (0x23, 0)}, + {"ldfd.c.clr.nt1", FLDINCREG (0x23, 1)}, + {"ldfd.c.clr.nta", FLDINCREG (0x23, 3)}, + {"ldf8.c.clr", FLDINCREG (0x21, 0)}, + {"ldf8.c.clr.nt1", FLDINCREG (0x21, 1)}, + {"ldf8.c.clr.nta", FLDINCREG (0x21, 3)}, + {"ldfe.c.clr", FLDINCREG (0x20, 0)}, + {"ldfe.c.clr.nt1", FLDINCREG (0x20, 1)}, + {"ldfe.c.clr.nta", FLDINCREG (0x20, 3)}, + {"ldfs.c.nc", FLDINCREG (0x26, 0)}, + {"ldfs.c.nc.nt1", FLDINCREG (0x26, 1)}, + {"ldfs.c.nc.nta", FLDINCREG (0x26, 3)}, + {"ldfd.c.nc", FLDINCREG (0x27, 0)}, + {"ldfd.c.nc.nt1", FLDINCREG (0x27, 1)}, + {"ldfd.c.nc.nta", FLDINCREG (0x27, 3)}, + {"ldf8.c.nc", FLDINCREG (0x25, 0)}, + {"ldf8.c.nc.nt1", FLDINCREG (0x25, 1)}, + {"ldf8.c.nc.nta", FLDINCREG (0x25, 3)}, + {"ldfe.c.nc", FLDINCREG (0x24, 0)}, + {"ldfe.c.nc.nt1", FLDINCREG (0x24, 1)}, + {"ldfe.c.nc.nta", FLDINCREG (0x24, 3)}, +#undef FLDINCREG + + /* Floating-point store. */ + {"stfs", M, OpMXX6aHint (6, 0, 0, 0x32, 0), {MR3, F2}, EMPTY}, + {"stfs.nta", M, OpMXX6aHint (6, 0, 0, 0x32, 3), {MR3, F2}, EMPTY}, + {"stfd", M, OpMXX6aHint (6, 0, 0, 0x33, 0), {MR3, F2}, EMPTY}, + {"stfd.nta", M, OpMXX6aHint (6, 0, 0, 0x33, 3), {MR3, F2}, EMPTY}, + {"stf8", M, OpMXX6aHint (6, 0, 0, 0x31, 0), {MR3, F2}, EMPTY}, + {"stf8.nta", M, OpMXX6aHint (6, 0, 0, 0x31, 3), {MR3, F2}, EMPTY}, + {"stfe", M, OpMXX6aHint (6, 0, 0, 0x30, 0), {MR3, F2}, EMPTY}, + {"stfe.nta", M, OpMXX6aHint (6, 0, 0, 0x30, 3), {MR3, F2}, EMPTY}, + {"stf.spill", M, OpMXX6aHint (6, 0, 0, 0x3b, 0), {MR3, F2}, EMPTY}, + {"stf.spill.nta", M, OpMXX6aHint (6, 0, 0, 0x3b, 3), {MR3, F2}, EMPTY}, + + /* Floating-point load pair. */ + {"ldfps", M2, OpMXX6aHint (6, 0, 1, 0x02, 0), {F1, F2, MR3}, EMPTY}, + {"ldfps.nt1", M2, OpMXX6aHint (6, 0, 1, 0x02, 1), {F1, F2, MR3}, EMPTY}, + {"ldfps.nta", M2, OpMXX6aHint (6, 0, 1, 0x02, 3), {F1, F2, MR3}, EMPTY}, + {"ldfpd", M2, OpMXX6aHint (6, 0, 1, 0x03, 0), {F1, F2, MR3}, EMPTY}, + {"ldfpd.nt1", M2, OpMXX6aHint (6, 0, 1, 0x03, 1), {F1, F2, MR3}, EMPTY}, + {"ldfpd.nta", M2, OpMXX6aHint (6, 0, 1, 0x03, 3), {F1, F2, MR3}, EMPTY}, + {"ldfp8", M2, OpMXX6aHint (6, 0, 1, 0x01, 0), {F1, F2, MR3}, EMPTY}, + {"ldfp8.nt1", M2, OpMXX6aHint (6, 0, 1, 0x01, 1), {F1, F2, MR3}, EMPTY}, + {"ldfp8.nta", M2, OpMXX6aHint (6, 0, 1, 0x01, 3), {F1, F2, MR3}, EMPTY}, + {"ldfps.s", M2, OpMXX6aHint (6, 0, 1, 0x06, 0), {F1, F2, MR3}, EMPTY}, + {"ldfps.s.nt1", M2, OpMXX6aHint (6, 0, 1, 0x06, 1), {F1, F2, MR3}, EMPTY}, + {"ldfps.s.nta", M2, OpMXX6aHint (6, 0, 1, 0x06, 3), {F1, F2, MR3}, EMPTY}, + {"ldfpd.s", M2, OpMXX6aHint (6, 0, 1, 0x07, 0), {F1, F2, MR3}, EMPTY}, + {"ldfpd.s.nt1", M2, OpMXX6aHint (6, 0, 1, 0x07, 1), {F1, F2, MR3}, EMPTY}, + {"ldfpd.s.nta", M2, OpMXX6aHint (6, 0, 1, 0x07, 3), {F1, F2, MR3}, EMPTY}, + {"ldfp8.s", M2, OpMXX6aHint (6, 0, 1, 0x05, 0), {F1, F2, MR3}, EMPTY}, + {"ldfp8.s.nt1", M2, OpMXX6aHint (6, 0, 1, 0x05, 1), {F1, F2, MR3}, EMPTY}, + {"ldfp8.s.nta", M2, OpMXX6aHint (6, 0, 1, 0x05, 3), {F1, F2, MR3}, EMPTY}, + {"ldfps.a", M2, OpMXX6aHint (6, 0, 1, 0x0a, 0), {F1, F2, MR3}, EMPTY}, + {"ldfps.a.nt1", M2, OpMXX6aHint (6, 0, 1, 0x0a, 1), {F1, F2, MR3}, EMPTY}, + {"ldfps.a.nta", M2, OpMXX6aHint (6, 0, 1, 0x0a, 3), {F1, F2, MR3}, EMPTY}, + {"ldfpd.a", M2, OpMXX6aHint (6, 0, 1, 0x0b, 0), {F1, F2, MR3}, EMPTY}, + {"ldfpd.a.nt1", M2, OpMXX6aHint (6, 0, 1, 0x0b, 1), {F1, F2, MR3}, EMPTY}, + {"ldfpd.a.nta", M2, OpMXX6aHint (6, 0, 1, 0x0b, 3), {F1, F2, MR3}, EMPTY}, + {"ldfp8.a", M2, OpMXX6aHint (6, 0, 1, 0x09, 0), {F1, F2, MR3}, EMPTY}, + {"ldfp8.a.nt1", M2, OpMXX6aHint (6, 0, 1, 0x09, 1), {F1, F2, MR3}, EMPTY}, + {"ldfp8.a.nta", M2, OpMXX6aHint (6, 0, 1, 0x09, 3), {F1, F2, MR3}, EMPTY}, + {"ldfps.sa", M2, OpMXX6aHint (6, 0, 1, 0x0e, 0), {F1, F2, MR3}, EMPTY}, + {"ldfps.sa.nt1", M2, OpMXX6aHint (6, 0, 1, 0x0e, 1), {F1, F2, MR3}, EMPTY}, + {"ldfps.sa.nta", M2, OpMXX6aHint (6, 0, 1, 0x0e, 3), {F1, F2, MR3}, EMPTY}, + {"ldfpd.sa", M2, OpMXX6aHint (6, 0, 1, 0x0f, 0), {F1, F2, MR3}, EMPTY}, + {"ldfpd.sa.nt1", M2, OpMXX6aHint (6, 0, 1, 0x0f, 1), {F1, F2, MR3}, EMPTY}, + {"ldfpd.sa.nta", M2, OpMXX6aHint (6, 0, 1, 0x0f, 3), {F1, F2, MR3}, EMPTY}, + {"ldfp8.sa", M2, OpMXX6aHint (6, 0, 1, 0x0d, 0), {F1, F2, MR3}, EMPTY}, + {"ldfp8.sa.nt1", M2, OpMXX6aHint (6, 0, 1, 0x0d, 1), {F1, F2, MR3}, EMPTY}, + {"ldfp8.sa.nta", M2, OpMXX6aHint (6, 0, 1, 0x0d, 3), {F1, F2, MR3}, EMPTY}, + {"ldfps.c.clr", M2, OpMXX6aHint (6, 0, 1, 0x22, 0), {F1, F2, MR3}, EMPTY}, + {"ldfps.c.clr.nt1", M2, OpMXX6aHint (6, 0, 1, 0x22, 1), {F1, F2, MR3}, EMPTY}, + {"ldfps.c.clr.nta", M2, OpMXX6aHint (6, 0, 1, 0x22, 3), {F1, F2, MR3}, EMPTY}, + {"ldfpd.c.clr", M2, OpMXX6aHint (6, 0, 1, 0x23, 0), {F1, F2, MR3}, EMPTY}, + {"ldfpd.c.clr.nt1", M2, OpMXX6aHint (6, 0, 1, 0x23, 1), {F1, F2, MR3}, EMPTY}, + {"ldfpd.c.clr.nta", M2, OpMXX6aHint (6, 0, 1, 0x23, 3), {F1, F2, MR3}, EMPTY}, + {"ldfp8.c.clr", M2, OpMXX6aHint (6, 0, 1, 0x21, 0), {F1, F2, MR3}, EMPTY}, + {"ldfp8.c.clr.nt1", M2, OpMXX6aHint (6, 0, 1, 0x21, 1), {F1, F2, MR3}, EMPTY}, + {"ldfp8.c.clr.nta", M2, OpMXX6aHint (6, 0, 1, 0x21, 3), {F1, F2, MR3}, EMPTY}, + {"ldfps.c.nc", M2, OpMXX6aHint (6, 0, 1, 0x26, 0), {F1, F2, MR3}, EMPTY}, + {"ldfps.c.nc.nt1", M2, OpMXX6aHint (6, 0, 1, 0x26, 1), {F1, F2, MR3}, EMPTY}, + {"ldfps.c.nc.nta", M2, OpMXX6aHint (6, 0, 1, 0x26, 3), {F1, F2, MR3}, EMPTY}, + {"ldfpd.c.nc", M2, OpMXX6aHint (6, 0, 1, 0x27, 0), {F1, F2, MR3}, EMPTY}, + {"ldfpd.c.nc.nt1", M2, OpMXX6aHint (6, 0, 1, 0x27, 1), {F1, F2, MR3}, EMPTY}, + {"ldfpd.c.nc.nta", M2, OpMXX6aHint (6, 0, 1, 0x27, 3), {F1, F2, MR3}, EMPTY}, + {"ldfp8.c.nc", M2, OpMXX6aHint (6, 0, 1, 0x25, 0), {F1, F2, MR3}, EMPTY}, + {"ldfp8.c.nc.nt1", M2, OpMXX6aHint (6, 0, 1, 0x25, 1), {F1, F2, MR3}, EMPTY}, + {"ldfp8.c.nc.nta", M2, OpMXX6aHint (6, 0, 1, 0x25, 3), {F1, F2, MR3}, EMPTY}, + + /* Floating-point load pair w/increment by immediate. */ +#define LD(a,b,c) M2, OpMXX6aHint (6, 1, 1, a, b), {F1, F2, MR3, c}, POSTINC, 0, NULL + {"ldfps", LD (0x02, 0, C8)}, + {"ldfps.nt1", LD (0x02, 1, C8)}, + {"ldfps.nta", LD (0x02, 3, C8)}, + {"ldfpd", LD (0x03, 0, C16)}, + {"ldfpd.nt1", LD (0x03, 1, C16)}, + {"ldfpd.nta", LD (0x03, 3, C16)}, + {"ldfp8", LD (0x01, 0, C16)}, + {"ldfp8.nt1", LD (0x01, 1, C16)}, + {"ldfp8.nta", LD (0x01, 3, C16)}, + {"ldfps.s", LD (0x06, 0, C8)}, + {"ldfps.s.nt1", LD (0x06, 1, C8)}, + {"ldfps.s.nta", LD (0x06, 3, C8)}, + {"ldfpd.s", LD (0x07, 0, C16)}, + {"ldfpd.s.nt1", LD (0x07, 1, C16)}, + {"ldfpd.s.nta", LD (0x07, 3, C16)}, + {"ldfp8.s", LD (0x05, 0, C16)}, + {"ldfp8.s.nt1", LD (0x05, 1, C16)}, + {"ldfp8.s.nta", LD (0x05, 3, C16)}, + {"ldfps.a", LD (0x0a, 0, C8)}, + {"ldfps.a.nt1", LD (0x0a, 1, C8)}, + {"ldfps.a.nta", LD (0x0a, 3, C8)}, + {"ldfpd.a", LD (0x0b, 0, C16)}, + {"ldfpd.a.nt1", LD (0x0b, 1, C16)}, + {"ldfpd.a.nta", LD (0x0b, 3, C16)}, + {"ldfp8.a", LD (0x09, 0, C16)}, + {"ldfp8.a.nt1", LD (0x09, 1, C16)}, + {"ldfp8.a.nta", LD (0x09, 3, C16)}, + {"ldfps.sa", LD (0x0e, 0, C8)}, + {"ldfps.sa.nt1", LD (0x0e, 1, C8)}, + {"ldfps.sa.nta", LD (0x0e, 3, C8)}, + {"ldfpd.sa", LD (0x0f, 0, C16)}, + {"ldfpd.sa.nt1", LD (0x0f, 1, C16)}, + {"ldfpd.sa.nta", LD (0x0f, 3, C16)}, + {"ldfp8.sa", LD (0x0d, 0, C16)}, + {"ldfp8.sa.nt1", LD (0x0d, 1, C16)}, + {"ldfp8.sa.nta", LD (0x0d, 3, C16)}, + {"ldfps.c.clr", LD (0x22, 0, C8)}, + {"ldfps.c.clr.nt1", LD (0x22, 1, C8)}, + {"ldfps.c.clr.nta", LD (0x22, 3, C8)}, + {"ldfpd.c.clr", LD (0x23, 0, C16)}, + {"ldfpd.c.clr.nt1", LD (0x23, 1, C16)}, + {"ldfpd.c.clr.nta", LD (0x23, 3, C16)}, + {"ldfp8.c.clr", LD (0x21, 0, C16)}, + {"ldfp8.c.clr.nt1", LD (0x21, 1, C16)}, + {"ldfp8.c.clr.nta", LD (0x21, 3, C16)}, + {"ldfps.c.nc", LD (0x26, 0, C8)}, + {"ldfps.c.nc.nt1", LD (0x26, 1, C8)}, + {"ldfps.c.nc.nta", LD (0x26, 3, C8)}, + {"ldfpd.c.nc", LD (0x27, 0, C16)}, + {"ldfpd.c.nc.nt1", LD (0x27, 1, C16)}, + {"ldfpd.c.nc.nta", LD (0x27, 3, C16)}, + {"ldfp8.c.nc", LD (0x25, 0, C16)}, + {"ldfp8.c.nc.nt1", LD (0x25, 1, C16)}, + {"ldfp8.c.nc.nta", LD (0x25, 3, C16)}, +#undef LD + + /* Line prefetch. */ + {"lfetch", M0, OpMXX6aHint (6, 0, 0, 0x2c, 0), {MR3}, EMPTY}, + {"lfetch.nt1", M0, OpMXX6aHint (6, 0, 0, 0x2c, 1), {MR3}, EMPTY}, + {"lfetch.nt2", M0, OpMXX6aHint (6, 0, 0, 0x2c, 2), {MR3}, EMPTY}, + {"lfetch.nta", M0, OpMXX6aHint (6, 0, 0, 0x2c, 3), {MR3}, EMPTY}, + {"lfetch.excl", M0, OpMXX6aHint (6, 0, 0, 0x2d, 0), {MR3}, EMPTY}, + {"lfetch.excl.nt1", M0, OpMXX6aHint (6, 0, 0, 0x2d, 1), {MR3}, EMPTY}, + {"lfetch.excl.nt2", M0, OpMXX6aHint (6, 0, 0, 0x2d, 2), {MR3}, EMPTY}, + {"lfetch.excl.nta", M0, OpMXX6aHint (6, 0, 0, 0x2d, 3), {MR3}, EMPTY}, + {"lfetch.fault", M0, OpMXX6aHint (6, 0, 0, 0x2e, 0), {MR3}, EMPTY}, + {"lfetch.fault.nt1", M0, OpMXX6aHint (6, 0, 0, 0x2e, 1), {MR3}, EMPTY}, + {"lfetch.fault.nt2", M0, OpMXX6aHint (6, 0, 0, 0x2e, 2), {MR3}, EMPTY}, + {"lfetch.fault.nta", M0, OpMXX6aHint (6, 0, 0, 0x2e, 3), {MR3}, EMPTY}, + {"lfetch.fault.excl", M0, OpMXX6aHint (6, 0, 0, 0x2f, 0), {MR3}, EMPTY}, + {"lfetch.fault.excl.nt1", M0, OpMXX6aHint (6, 0, 0, 0x2f, 1), {MR3}, EMPTY}, + {"lfetch.fault.excl.nt2", M0, OpMXX6aHint (6, 0, 0, 0x2f, 2), {MR3}, EMPTY}, + {"lfetch.fault.excl.nta", M0, OpMXX6aHint (6, 0, 0, 0x2f, 3), {MR3}, EMPTY}, + + /* Line prefetch w/increment by register. */ +#define LFETCHINCREG(c,h) M0, OpMXX6aHint (6, 1, 0, c, h), {MR3, R2}, POSTINC, 0, NULL + {"lfetch", LFETCHINCREG (0x2c, 0)}, + {"lfetch.nt1", LFETCHINCREG (0x2c, 1)}, + {"lfetch.nt2", LFETCHINCREG (0x2c, 2)}, + {"lfetch.nta", LFETCHINCREG (0x2c, 3)}, + {"lfetch.excl", LFETCHINCREG (0x2d, 0)}, + {"lfetch.excl.nt1", LFETCHINCREG (0x2d, 1)}, + {"lfetch.excl.nt2", LFETCHINCREG (0x2d, 2)}, + {"lfetch.excl.nta", LFETCHINCREG (0x2d, 3)}, + {"lfetch.fault", LFETCHINCREG (0x2e, 0)}, + {"lfetch.fault.nt1", LFETCHINCREG (0x2e, 1)}, + {"lfetch.fault.nt2", LFETCHINCREG (0x2e, 2)}, + {"lfetch.fault.nta", LFETCHINCREG (0x2e, 3)}, + {"lfetch.fault.excl", LFETCHINCREG (0x2f, 0)}, + {"lfetch.fault.excl.nt1", LFETCHINCREG (0x2f, 1)}, + {"lfetch.fault.excl.nt2", LFETCHINCREG (0x2f, 2)}, + {"lfetch.fault.excl.nta", LFETCHINCREG (0x2f, 3)}, +#undef LFETCHINCREG + + /* Semaphore operations. */ + {"setf.sig", M, OpMXX6a (6, 0, 1, 0x1c), {F1, R2}, EMPTY}, + {"setf.exp", M, OpMXX6a (6, 0, 1, 0x1d), {F1, R2}, EMPTY}, + {"setf.s", M, OpMXX6a (6, 0, 1, 0x1e), {F1, R2}, EMPTY}, + {"setf.d", M, OpMXX6a (6, 0, 1, 0x1f), {F1, R2}, EMPTY}, + + /* Floating-point load w/increment by immediate. */ +#define FLDINCIMMED(c,h) M, OpX6aHint (7, c, h), {F1, MR3, IMM9b}, POSTINC, 0, NULL + {"ldfs", FLDINCIMMED (0x02, 0)}, + {"ldfs.nt1", FLDINCIMMED (0x02, 1)}, + {"ldfs.nta", FLDINCIMMED (0x02, 3)}, + {"ldfd", FLDINCIMMED (0x03, 0)}, + {"ldfd.nt1", FLDINCIMMED (0x03, 1)}, + {"ldfd.nta", FLDINCIMMED (0x03, 3)}, + {"ldf8", FLDINCIMMED (0x01, 0)}, + {"ldf8.nt1", FLDINCIMMED (0x01, 1)}, + {"ldf8.nta", FLDINCIMMED (0x01, 3)}, + {"ldfe", FLDINCIMMED (0x00, 0)}, + {"ldfe.nt1", FLDINCIMMED (0x00, 1)}, + {"ldfe.nta", FLDINCIMMED (0x00, 3)}, + {"ldfs.s", FLDINCIMMED (0x06, 0)}, + {"ldfs.s.nt1", FLDINCIMMED (0x06, 1)}, + {"ldfs.s.nta", FLDINCIMMED (0x06, 3)}, + {"ldfd.s", FLDINCIMMED (0x07, 0)}, + {"ldfd.s.nt1", FLDINCIMMED (0x07, 1)}, + {"ldfd.s.nta", FLDINCIMMED (0x07, 3)}, + {"ldf8.s", FLDINCIMMED (0x05, 0)}, + {"ldf8.s.nt1", FLDINCIMMED (0x05, 1)}, + {"ldf8.s.nta", FLDINCIMMED (0x05, 3)}, + {"ldfe.s", FLDINCIMMED (0x04, 0)}, + {"ldfe.s.nt1", FLDINCIMMED (0x04, 1)}, + {"ldfe.s.nta", FLDINCIMMED (0x04, 3)}, + {"ldfs.a", FLDINCIMMED (0x0a, 0)}, + {"ldfs.a.nt1", FLDINCIMMED (0x0a, 1)}, + {"ldfs.a.nta", FLDINCIMMED (0x0a, 3)}, + {"ldfd.a", FLDINCIMMED (0x0b, 0)}, + {"ldfd.a.nt1", FLDINCIMMED (0x0b, 1)}, + {"ldfd.a.nta", FLDINCIMMED (0x0b, 3)}, + {"ldf8.a", FLDINCIMMED (0x09, 0)}, + {"ldf8.a.nt1", FLDINCIMMED (0x09, 1)}, + {"ldf8.a.nta", FLDINCIMMED (0x09, 3)}, + {"ldfe.a", FLDINCIMMED (0x08, 0)}, + {"ldfe.a.nt1", FLDINCIMMED (0x08, 1)}, + {"ldfe.a.nta", FLDINCIMMED (0x08, 3)}, + {"ldfs.sa", FLDINCIMMED (0x0e, 0)}, + {"ldfs.sa.nt1", FLDINCIMMED (0x0e, 1)}, + {"ldfs.sa.nta", FLDINCIMMED (0x0e, 3)}, + {"ldfd.sa", FLDINCIMMED (0x0f, 0)}, + {"ldfd.sa.nt1", FLDINCIMMED (0x0f, 1)}, + {"ldfd.sa.nta", FLDINCIMMED (0x0f, 3)}, + {"ldf8.sa", FLDINCIMMED (0x0d, 0)}, + {"ldf8.sa.nt1", FLDINCIMMED (0x0d, 1)}, + {"ldf8.sa.nta", FLDINCIMMED (0x0d, 3)}, + {"ldfe.sa", FLDINCIMMED (0x0c, 0)}, + {"ldfe.sa.nt1", FLDINCIMMED (0x0c, 1)}, + {"ldfe.sa.nta", FLDINCIMMED (0x0c, 3)}, + {"ldf.fill", FLDINCIMMED (0x1b, 0)}, + {"ldf.fill.nt1", FLDINCIMMED (0x1b, 1)}, + {"ldf.fill.nta", FLDINCIMMED (0x1b, 3)}, + {"ldfs.c.clr", FLDINCIMMED (0x22, 0)}, + {"ldfs.c.clr.nt1", FLDINCIMMED (0x22, 1)}, + {"ldfs.c.clr.nta", FLDINCIMMED (0x22, 3)}, + {"ldfd.c.clr", FLDINCIMMED (0x23, 0)}, + {"ldfd.c.clr.nt1", FLDINCIMMED (0x23, 1)}, + {"ldfd.c.clr.nta", FLDINCIMMED (0x23, 3)}, + {"ldf8.c.clr", FLDINCIMMED (0x21, 0)}, + {"ldf8.c.clr.nt1", FLDINCIMMED (0x21, 1)}, + {"ldf8.c.clr.nta", FLDINCIMMED (0x21, 3)}, + {"ldfe.c.clr", FLDINCIMMED (0x20, 0)}, + {"ldfe.c.clr.nt1", FLDINCIMMED (0x20, 1)}, + {"ldfe.c.clr.nta", FLDINCIMMED (0x20, 3)}, + {"ldfs.c.nc", FLDINCIMMED (0x26, 0)}, + {"ldfs.c.nc.nt1", FLDINCIMMED (0x26, 1)}, + {"ldfs.c.nc.nta", FLDINCIMMED (0x26, 3)}, + {"ldfd.c.nc", FLDINCIMMED (0x27, 0)}, + {"ldfd.c.nc.nt1", FLDINCIMMED (0x27, 1)}, + {"ldfd.c.nc.nta", FLDINCIMMED (0x27, 3)}, + {"ldf8.c.nc", FLDINCIMMED (0x25, 0)}, + {"ldf8.c.nc.nt1", FLDINCIMMED (0x25, 1)}, + {"ldf8.c.nc.nta", FLDINCIMMED (0x25, 3)}, + {"ldfe.c.nc", FLDINCIMMED (0x24, 0)}, + {"ldfe.c.nc.nt1", FLDINCIMMED (0x24, 1)}, + {"ldfe.c.nc.nta", FLDINCIMMED (0x24, 3)}, +#undef FLDINCIMMED + + /* Floating-point store w/increment by immediate. */ +#define FSTINCIMMED(c,h) M, OpX6aHint (7, c, h), {MR3, F2, IMM9a}, POSTINC, 0, NULL + {"stfs", FSTINCIMMED (0x32, 0)}, + {"stfs.nta", FSTINCIMMED (0x32, 3)}, + {"stfd", FSTINCIMMED (0x33, 0)}, + {"stfd.nta", FSTINCIMMED (0x33, 3)}, + {"stf8", FSTINCIMMED (0x31, 0)}, + {"stf8.nta", FSTINCIMMED (0x31, 3)}, + {"stfe", FSTINCIMMED (0x30, 0)}, + {"stfe.nta", FSTINCIMMED (0x30, 3)}, + {"stf.spill", FSTINCIMMED (0x3b, 0)}, + {"stf.spill.nta", FSTINCIMMED (0x3b, 3)}, +#undef FSTINCIMMED + + /* Line prefetch w/increment by immediate. */ +#define LFETCHINCIMMED(c,h) M0, OpX6aHint (7, c, h), {MR3, IMM9b}, POSTINC, 0, NULL + {"lfetch", LFETCHINCIMMED (0x2c, 0)}, + {"lfetch.nt1", LFETCHINCIMMED (0x2c, 1)}, + {"lfetch.nt2", LFETCHINCIMMED (0x2c, 2)}, + {"lfetch.nta", LFETCHINCIMMED (0x2c, 3)}, + {"lfetch.excl", LFETCHINCIMMED (0x2d, 0)}, + {"lfetch.excl.nt1", LFETCHINCIMMED (0x2d, 1)}, + {"lfetch.excl.nt2", LFETCHINCIMMED (0x2d, 2)}, + {"lfetch.excl.nta", LFETCHINCIMMED (0x2d, 3)}, + {"lfetch.fault", LFETCHINCIMMED (0x2e, 0)}, + {"lfetch.fault.nt1", LFETCHINCIMMED (0x2e, 1)}, + {"lfetch.fault.nt2", LFETCHINCIMMED (0x2e, 2)}, + {"lfetch.fault.nta", LFETCHINCIMMED (0x2e, 3)}, + {"lfetch.fault.excl", LFETCHINCIMMED (0x2f, 0)}, + {"lfetch.fault.excl.nt1", LFETCHINCIMMED (0x2f, 1)}, + {"lfetch.fault.excl.nt2", LFETCHINCIMMED (0x2f, 2)}, + {"lfetch.fault.excl.nta", LFETCHINCIMMED (0x2f, 3)}, +#undef LFETCHINCIMMED + + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; + +#undef M0 +#undef M +#undef M2 +#undef bM +#undef bX +#undef bX2 +#undef bX3 +#undef bX4 +#undef bX6a +#undef bX6b +#undef bHint +#undef mM +#undef mX +#undef mX2 +#undef mX3 +#undef mX4 +#undef mX6a +#undef mX6b +#undef mHint +#undef OpX3 +#undef OpX3X6b +#undef OpX3X4 +#undef OpX3X4X2 +#undef OpX6aHint +#undef OpXX6aHint +#undef OpMXX6a +#undef OpMXX6aHint +#undef EMPTY diff --git a/tools/debugger/xenitp/ia64-opc-x.c b/tools/debugger/xenitp/ia64-opc-x.c new file mode 100644 index 0000000..fcbe92e --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc-x.c @@ -0,0 +1,188 @@ +/* ia64-opc-x.c -- IA-64 `X' opcode table. + Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + Contributed by Timothy Wall + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-opc.h" + +/* Identify the specific X-unit type. */ +#define X0 IA64_TYPE_X, 0 +#define X IA64_TYPE_X, 1 + +/* Instruction bit fields: */ +#define bBtype(x) (((ia64_insn) ((x) & 0x7)) << 6) +#define bD(x) (((ia64_insn) ((x) & 0x1)) << 35) +#define bPa(x) (((ia64_insn) ((x) & 0x1)) << 12) +#define bPr(x) (((ia64_insn) ((x) & 0x3f)) << 0) +#define bVc(x) (((ia64_insn) ((x) & 0x1)) << 20) +#define bWha(x) (((ia64_insn) ((x) & 0x3)) << 33) +#define bX3(x) (((ia64_insn) ((x) & 0x7)) << 33) +#define bX6(x) (((ia64_insn) ((x) & 0x3f)) << 27) +#define bY(x) (((ia64_insn) ((x) & 0x1)) << 26) + +#define mBtype bBtype (-1) +#define mD bD (-1) +#define mPa bPa (-1) +#define mPr bPr (-1) +#define mVc bVc (-1) +#define mWha bWha (-1) +#define mX3 bX3 (-1) +#define mX6 bX6 (-1) +#define mY bY (-1) + +#define OpX3X6(a,b,c) (bOp (a) | bX3 (b) | bX6(c)), \ + (mOp | mX3 | mX6) +#define OpX3X6Y(a,b,c,d) (bOp (a) | bX3 (b) | bX6(c) | bY(d)), \ + (mOp | mX3 | mX6 | mY) +#define OpVc(a,b) (bOp (a) | bVc (b)), (mOp | mVc) +#define OpPaWhaD(a,b,c,d) \ + (bOp (a) | bPa (b) | bWha (c) | bD (d)), (mOp | mPa | mWha | mD) +#define OpBtypePaWhaD(a,b,c,d,e) \ + (bOp (a) | bBtype (b) | bPa (c) | bWha (d) | bD (e)), \ + (mOp | mBtype | mPa | mWha | mD) +#define OpBtypePaWhaDPr(a,b,c,d,e,f) \ + (bOp (a) | bBtype (b) | bPa (c) | bWha (d) | bD (e) | bPr (f)), \ + (mOp | mBtype | mPa | mWha | mD | mPr) + +struct ia64_opcode ia64_opcodes_x[] = + { + {"break.x", X0, OpX3X6 (0, 0, 0x00), {IMMU62}, 0, 0, NULL}, + {"nop.x", X0, OpX3X6Y (0, 0, 0x01, 0), {IMMU62}, 0, 0, NULL}, + {"hint.x", X0, OpX3X6Y (0, 0, 0x01, 1), {IMMU62}, 0, 0, NULL}, + {"movl", X, OpVc (6, 0), {R1, IMMU64}, 0, 0, NULL}, +#define BRL(a,b) \ + X0, OpBtypePaWhaDPr (0xC, 0, a, 0, b, 0), {TGT64}, PSEUDO, 0, NULL + {"brl.few", BRL (0, 0)}, + {"brl", BRL (0, 0)}, + {"brl.few.clr", BRL (0, 1)}, + {"brl.clr", BRL (0, 1)}, + {"brl.many", BRL (1, 0)}, + {"brl.many.clr", BRL (1, 1)}, +#undef BRL +#define BRL(a,b,c) \ + X0, OpBtypePaWhaD (0xC, 0, a, b, c), {TGT64}, 0, 0, NULL +#define BRLP(a,b,c) \ + X0, OpBtypePaWhaD (0xC, 0, a, b, c), {TGT64}, PSEUDO, 0, NULL + {"brl.cond.sptk.few", BRL (0, 0, 0)}, + {"brl.cond.sptk", BRLP (0, 0, 0)}, + {"brl.cond.sptk.few.clr", BRL (0, 0, 1)}, + {"brl.cond.sptk.clr", BRLP (0, 0, 1)}, + {"brl.cond.spnt.few", BRL (0, 1, 0)}, + {"brl.cond.spnt", BRLP (0, 1, 0)}, + {"brl.cond.spnt.few.clr", BRL (0, 1, 1)}, + {"brl.cond.spnt.clr", BRLP (0, 1, 1)}, + {"brl.cond.dptk.few", BRL (0, 2, 0)}, + {"brl.cond.dptk", BRLP (0, 2, 0)}, + {"brl.cond.dptk.few.clr", BRL (0, 2, 1)}, + {"brl.cond.dptk.clr", BRLP (0, 2, 1)}, + {"brl.cond.dpnt.few", BRL (0, 3, 0)}, + {"brl.cond.dpnt", BRLP (0, 3, 0)}, + {"brl.cond.dpnt.few.clr", BRL (0, 3, 1)}, + {"brl.cond.dpnt.clr", BRLP (0, 3, 1)}, + {"brl.cond.sptk.many", BRL (1, 0, 0)}, + {"brl.cond.sptk.many.clr", BRL (1, 0, 1)}, + {"brl.cond.spnt.many", BRL (1, 1, 0)}, + {"brl.cond.spnt.many.clr", BRL (1, 1, 1)}, + {"brl.cond.dptk.many", BRL (1, 2, 0)}, + {"brl.cond.dptk.many.clr", BRL (1, 2, 1)}, + {"brl.cond.dpnt.many", BRL (1, 3, 0)}, + {"brl.cond.dpnt.many.clr", BRL (1, 3, 1)}, + {"brl.sptk.few", BRL (0, 0, 0)}, + {"brl.sptk", BRLP (0, 0, 0)}, + {"brl.sptk.few.clr", BRL (0, 0, 1)}, + {"brl.sptk.clr", BRLP (0, 0, 1)}, + {"brl.spnt.few", BRL (0, 1, 0)}, + {"brl.spnt", BRLP (0, 1, 0)}, + {"brl.spnt.few.clr", BRL (0, 1, 1)}, + {"brl.spnt.clr", BRLP (0, 1, 1)}, + {"brl.dptk.few", BRL (0, 2, 0)}, + {"brl.dptk", BRLP (0, 2, 0)}, + {"brl.dptk.few.clr", BRL (0, 2, 1)}, + {"brl.dptk.clr", BRLP (0, 2, 1)}, + {"brl.dpnt.few", BRL (0, 3, 0)}, + {"brl.dpnt", BRLP (0, 3, 0)}, + {"brl.dpnt.few.clr", BRL (0, 3, 1)}, + {"brl.dpnt.clr", BRLP (0, 3, 1)}, + {"brl.sptk.many", BRL (1, 0, 0)}, + {"brl.sptk.many.clr", BRL (1, 0, 1)}, + {"brl.spnt.many", BRL (1, 1, 0)}, + {"brl.spnt.many.clr", BRL (1, 1, 1)}, + {"brl.dptk.many", BRL (1, 2, 0)}, + {"brl.dptk.many.clr", BRL (1, 2, 1)}, + {"brl.dpnt.many", BRL (1, 3, 0)}, + {"brl.dpnt.many.clr", BRL (1, 3, 1)}, +#undef BRL +#undef BRLP +#define BRL(a,b,c) X, OpPaWhaD (0xD, a, b, c), {B1, TGT64}, 0, 0, NULL +#define BRLP(a,b,c) X, OpPaWhaD (0xD, a, b, c), {B1, TGT64}, PSEUDO, 0, NULL + {"brl.call.sptk.few", BRL (0, 0, 0)}, + {"brl.call.sptk", BRLP (0, 0, 0)}, + {"brl.call.sptk.few.clr", BRL (0, 0, 1)}, + {"brl.call.sptk.clr", BRLP (0, 0, 1)}, + {"brl.call.spnt.few", BRL (0, 1, 0)}, + {"brl.call.spnt", BRLP (0, 1, 0)}, + {"brl.call.spnt.few.clr", BRL (0, 1, 1)}, + {"brl.call.spnt.clr", BRLP (0, 1, 1)}, + {"brl.call.dptk.few", BRL (0, 2, 0)}, + {"brl.call.dptk", BRLP (0, 2, 0)}, + {"brl.call.dptk.few.clr", BRL (0, 2, 1)}, + {"brl.call.dptk.clr", BRLP (0, 2, 1)}, + {"brl.call.dpnt.few", BRL (0, 3, 0)}, + {"brl.call.dpnt", BRLP (0, 3, 0)}, + {"brl.call.dpnt.few.clr", BRL (0, 3, 1)}, + {"brl.call.dpnt.clr", BRLP (0, 3, 1)}, + {"brl.call.sptk.many", BRL (1, 0, 0)}, + {"brl.call.sptk.many.clr", BRL (1, 0, 1)}, + {"brl.call.spnt.many", BRL (1, 1, 0)}, + {"brl.call.spnt.many.clr", BRL (1, 1, 1)}, + {"brl.call.dptk.many", BRL (1, 2, 0)}, + {"brl.call.dptk.many.clr", BRL (1, 2, 1)}, + {"brl.call.dpnt.many", BRL (1, 3, 0)}, + {"brl.call.dpnt.many.clr", BRL (1, 3, 1)}, +#undef BRL +#undef BRLP + {NULL, 0, 0, 0, 0, {0}, 0, 0, NULL} + }; + +#undef X0 +#undef X + +#undef bBtype +#undef bD +#undef bPa +#undef bPr +#undef bVc +#undef bWha +#undef bX3 +#undef bX6 + +#undef mBtype +#undef mD +#undef mPa +#undef mPr +#undef mVc +#undef mWha +#undef mX3 +#undef mX6 + +#undef OpX3X6 +#undef OpVc +#undef OpPaWhaD +#undef OpBtypePaWhaD +#undef OpBtypePaWhaDPr diff --git a/tools/debugger/xenitp/ia64-opc.c b/tools/debugger/xenitp/ia64-opc.c new file mode 100644 index 0000000..7e67a7f --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc.c @@ -0,0 +1,727 @@ +/* ia64-opc.c -- Functions to access the compacted opcode table + Copyright 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + Written by Bob Manson of Cygnus Solutions, + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ia64-asmtab.h" +#include "ia64-asmtab.c" + +static void get_opc_prefix (const char **, char *); +static short int find_string_ent (const char *); +static short int find_main_ent (short int); +static short int find_completer (short int, short int, const char *); +static ia64_insn apply_completer (ia64_insn, int); +static int extract_op_bits (int, int, int); +static int extract_op (int, int *, unsigned int *); +static int opcode_verify (ia64_insn, int, enum ia64_insn_type); +static int locate_opcode_ent (ia64_insn, enum ia64_insn_type); +static struct ia64_opcode *make_ia64_opcode + (ia64_insn, const char *, int, int); +static struct ia64_opcode *ia64_find_matching_opcode + (const char *, short int); + +const struct ia64_templ_desc ia64_templ_desc[16] = + { + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, /* 0 */ + { 2, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, + { 0, { IA64_UNIT_M, IA64_UNIT_L, IA64_UNIT_X }, "MLX" }, + { 0, { 0, }, "-3-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, /* 4 */ + { 1, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_I }, "MFI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_F }, "MMF" }, + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_B }, "MIB" }, /* 8 */ + { 0, { IA64_UNIT_M, IA64_UNIT_B, IA64_UNIT_B }, "MBB" }, + { 0, { 0, }, "-a-" }, + { 0, { IA64_UNIT_B, IA64_UNIT_B, IA64_UNIT_B }, "BBB" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_B }, "MMB" }, /* c */ + { 0, { 0, }, "-d-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_B }, "MFB" }, + { 0, { 0, }, "-f-" }, + }; + + +/* Copy the prefix contained in *PTR (up to a '.' or a NUL) to DEST. + PTR will be adjusted to point to the start of the next portion + of the opcode, or at the NUL character. */ + +static void +get_opc_prefix (const char **ptr, char *dest) +{ + char *c = strchr (*ptr, '.'); + if (c != NULL) + { + memcpy (dest, *ptr, c - *ptr); + dest[c - *ptr] = '\0'; + *ptr = c + 1; + } + else + { + int l = strlen (*ptr); + memcpy (dest, *ptr, l); + dest[l] = '\0'; + *ptr += l; + } +} + +/* Find the index of the entry in the string table corresponding to + STR; return -1 if one does not exist. */ + +static short +find_string_ent (const char *str) +{ + short start = 0; + short end = sizeof (ia64_strings) / sizeof (const char *); + short i = (start + end) / 2; + + if (strcmp (str, ia64_strings[end - 1]) > 0) + { + return -1; + } + while (start <= end) + { + int c = strcmp (str, ia64_strings[i]); + if (c < 0) + { + end = i - 1; + } + else if (c == 0) + { + return i; + } + else + { + start = i + 1; + } + i = (start + end) / 2; + } + return -1; +} + +/* Find the opcode in the main opcode table whose name is STRINGINDEX, or + return -1 if one does not exist. */ + +static short +find_main_ent (short nameindex) +{ + short start = 0; + short end = sizeof (main_table) / sizeof (struct ia64_main_table); + short i = (start + end) / 2; + + if (nameindex < main_table[0].name_index + || nameindex > main_table[end - 1].name_index) + { + return -1; + } + while (start <= end) + { + if (nameindex < main_table[i].name_index) + { + end = i - 1; + } + else if (nameindex == main_table[i].name_index) + { + while (i > 0 && main_table[i - 1].name_index == nameindex) + { + i--; + } + return i; + } + else + { + start = i + 1; + } + i = (start + end) / 2; + } + return -1; +} + +/* Find the index of the entry in the completer table that is part of + MAIN_ENT (starting from PREV_COMPLETER) that matches NAME, or + return -1 if one does not exist. */ + +static short +find_completer (short main_ent, short prev_completer, const char *name) +{ + short name_index = find_string_ent (name); + + if (name_index < 0) + { + return -1; + } + + if (prev_completer == -1) + { + prev_completer = main_table[main_ent].completers; + } + else + { + prev_completer = completer_table[prev_completer].subentries; + } + + while (prev_completer != -1) + { + if (completer_table[prev_completer].name_index == name_index) + { + return prev_completer; + } + prev_completer = completer_table[prev_completer].alternative; + } + return -1; +} + +/* Apply the completer referred to by COMPLETER_INDEX to OPCODE, and + return the result. */ + +static ia64_insn +apply_completer (ia64_insn opcode, int completer_index) +{ + ia64_insn mask = completer_table[completer_index].mask; + ia64_insn bits = completer_table[completer_index].bits; + int shiftamt = (completer_table[completer_index].offset & 63); + + mask = mask << shiftamt; + bits = bits << shiftamt; + opcode = (opcode & ~mask) | bits; + return opcode; +} + +/* Extract BITS number of bits starting from OP_POINTER + BITOFFSET in + the dis_table array, and return its value. (BITOFFSET is numbered + starting from MSB to LSB, so a BITOFFSET of 0 indicates the MSB of the + first byte in OP_POINTER.) */ + +static int +extract_op_bits (int op_pointer, int bitoffset, int bits) +{ + int res = 0; + + op_pointer += (bitoffset / 8); + + if (bitoffset % 8) + { + unsigned int op = dis_table[op_pointer++]; + int numb = 8 - (bitoffset % 8); + int mask = (1 << numb) - 1; + int bata = (bits < numb) ? bits : numb; + int delta = numb - bata; + + res = (res << bata) | ((op & mask) >> delta); + bitoffset += bata; + bits -= bata; + } + while (bits >= 8) + { + res = (res << 8) | (dis_table[op_pointer++] & 255); + bits -= 8; + } + if (bits > 0) + { + unsigned int op = (dis_table[op_pointer++] & 255); + res = (res << bits) | (op >> (8 - bits)); + } + return res; +} + +/* Examine the state machine entry at OP_POINTER in the dis_table + array, and extract its values into OPVAL and OP. The length of the + state entry in bits is returned. */ + +static int +extract_op (int op_pointer, int *opval, unsigned int *op) +{ + int oplen = 5; + + *op = dis_table[op_pointer]; + + if ((*op) & 0x40) + { + opval[0] = extract_op_bits (op_pointer, oplen, 5); + oplen += 5; + } + switch ((*op) & 0x30) + { + case 0x10: + { + opval[1] = extract_op_bits (op_pointer, oplen, 8); + oplen += 8; + opval[1] += op_pointer; + break; + } + case 0x20: + { + opval[1] = extract_op_bits (op_pointer, oplen, 16); + if (! (opval[1] & 32768)) + { + opval[1] += op_pointer; + } + oplen += 16; + break; + } + case 0x30: + { + oplen--; + opval[2] = extract_op_bits (op_pointer, oplen, 12); + oplen += 12; + opval[2] |= 32768; + break; + } + } + if (((*op) & 0x08) && (((*op) & 0x30) != 0x30)) + { + opval[2] = extract_op_bits (op_pointer, oplen, 16); + oplen += 16; + if (! (opval[2] & 32768)) + { + opval[2] += op_pointer; + } + } + return oplen; +} + +/* Returns a non-zero value if the opcode in the main_table list at + PLACE matches OPCODE and is of type TYPE. */ + +static int +opcode_verify (ia64_insn opcode, int place, enum ia64_insn_type type) +{ + if (main_table[place].opcode_type != type) + { + return 0; + } + if (main_table[place].flags + & (IA64_OPCODE_F2_EQ_F3 | IA64_OPCODE_LEN_EQ_64MCNT)) + { + const struct ia64_operand *o1, *o2; + ia64_insn f2, f3; + + if (main_table[place].flags & IA64_OPCODE_F2_EQ_F3) + { + o1 = elf64_ia64_operands + IA64_OPND_F2; + o2 = elf64_ia64_operands + IA64_OPND_F3; + (*o1->extract) (o1, opcode, &f2); + (*o2->extract) (o2, opcode, &f3); + if (f2 != f3) + return 0; + } + else + { + ia64_insn len, count; + + /* length must equal 64-count: */ + o1 = elf64_ia64_operands + IA64_OPND_LEN6; + o2 = elf64_ia64_operands + main_table[place].operands[2]; + (*o1->extract) (o1, opcode, &len); + (*o2->extract) (o2, opcode, &count); + if (len != 64 - count) + return 0; + } + } + return 1; +} + +/* Find an instruction entry in the ia64_dis_names array that matches + opcode OPCODE and is of type TYPE. Returns either a positive index + into the array, or a negative value if an entry for OPCODE could + not be found. Checks all matches and returns the one with the highest + priority. */ + +static int +locate_opcode_ent (ia64_insn opcode, enum ia64_insn_type type) +{ + int currtest[41]; + int bitpos[41]; + int op_ptr[41]; + int currstatenum = 0; + short found_disent = -1; + short found_priority = -1; + + currtest[currstatenum] = 0; + op_ptr[currstatenum] = 0; + bitpos[currstatenum] = 40; + + while (1) + { + int op_pointer = op_ptr[currstatenum]; + unsigned int op; + int currbitnum = bitpos[currstatenum]; + int oplen; + int opval[3] = {0}; + int next_op; + int currbit; + + oplen = extract_op (op_pointer, opval, &op); + + bitpos[currstatenum] = currbitnum; + + /* Skip opval[0] bits in the instruction. */ + if (op & 0x40) + { + currbitnum -= opval[0]; + } + + /* The value of the current bit being tested. */ + currbit = opcode & (((ia64_insn) 1) << currbitnum) ? 1 : 0; + next_op = -1; + + /* We always perform the tests specified in the current state in + a particular order, falling through to the next test if the + previous one failed. */ + switch (currtest[currstatenum]) + { + case 0: + currtest[currstatenum]++; + if (currbit == 0 && (op & 0x80)) + { + /* Check for a zero bit. If this test solely checks for + a zero bit, we can check for up to 8 consecutive zero + bits (the number to check is specified by the lower 3 + bits in the state code.) + + If the state instruction matches, we go to the very + next state instruction; otherwise, try the next test. */ + + if ((op & 0xf8) == 0x80) + { + int count = op & 0x7; + int x; + + for (x = 0; x <= count; x++) + { + int i = + opcode & (((ia64_insn) 1) << (currbitnum - x)) ? 1 : 0; + if (i) + { + break; + } + } + if (x > count) + { + next_op = op_pointer + ((oplen + 7) / 8); + currbitnum -= count; + break; + } + } + else if (! currbit) + { + next_op = op_pointer + ((oplen + 7) / 8); + break; + } + } + /* FALLTHROUGH */ + case 1: + /* If the bit in the instruction is one, go to the state + instruction specified by opval[1]. */ + currtest[currstatenum]++; + if (currbit && (op & 0x30) != 0 && ((op & 0x30) != 0x30)) + { + next_op = opval[1]; + break; + } + /* FALLTHROUGH */ + case 2: + /* Don't care. Skip the current bit and go to the state + instruction specified by opval[2]. + + An encoding of 0x30 is special; this means that a 12-bit + offset into the ia64_dis_names[] array is specified. */ + currtest[currstatenum]++; + if ((op & 0x08) || ((op & 0x30) == 0x30)) + { + next_op = opval[2]; + break; + } + } + + /* If bit 15 is set in the address of the next state, an offset + in the ia64_dis_names array was specified instead. We then + check to see if an entry in the list of opcodes matches the + opcode we were given; if so, we have succeeded. */ + + if ((next_op >= 0) && (next_op & 32768)) + { + short disent = next_op & 32767; + short priority = -1; + + if (next_op > 65535) + { + abort (); + } + + /* Run through the list of opcodes to check, trying to find + one that matches. */ + while (disent >= 0) + { + int place = ia64_dis_names[disent].insn_index; + + priority = ia64_dis_names[disent].priority; + + if (opcode_verify (opcode, place, type) + && priority > found_priority) + { + break; + } + if (ia64_dis_names[disent].next_flag) + { + disent++; + } + else + { + disent = -1; + } + } + + if (disent >= 0) + { + found_disent = disent; + found_priority = priority; + } + /* Try the next test in this state, regardless of whether a match + was found. */ + next_op = -2; + } + + /* next_op == -1 is "back up to the previous state". + next_op == -2 is "stay in this state and try the next test". + Otherwise, transition to the state indicated by next_op. */ + + if (next_op == -1) + { + currstatenum--; + if (currstatenum < 0) + { + return found_disent; + } + } + else if (next_op >= 0) + { + currstatenum++; + bitpos[currstatenum] = currbitnum - 1; + op_ptr[currstatenum] = next_op; + currtest[currstatenum] = 0; + } + } +} + +/* Construct an ia64_opcode entry based on OPCODE, NAME and PLACE. */ + +static struct ia64_opcode * +make_ia64_opcode (ia64_insn opcode, const char *name, int place, int depind) +{ + struct ia64_opcode *res = + (struct ia64_opcode *) malloc (sizeof (struct ia64_opcode)); + res->name = strdup (name); + res->type = main_table[place].opcode_type; + res->num_outputs = main_table[place].num_outputs; + res->opcode = opcode; + res->mask = main_table[place].mask; + res->operands[0] = main_table[place].operands[0]; + res->operands[1] = main_table[place].operands[1]; + res->operands[2] = main_table[place].operands[2]; + res->operands[3] = main_table[place].operands[3]; + res->operands[4] = main_table[place].operands[4]; + res->flags = main_table[place].flags; + res->ent_index = place; + res->dependencies = &op_dependencies[depind]; + return res; +} + +/* Determine the ia64_opcode entry for the opcode specified by INSN + and TYPE. If a valid entry is not found, return NULL. */ +struct ia64_opcode * +ia64_dis_opcode (ia64_insn insn, enum ia64_insn_type type) +{ + int disent = locate_opcode_ent (insn, type); + + if (disent < 0) + { + return NULL; + } + else + { + unsigned int cb = ia64_dis_names[disent].completer_index; + static char name[128]; + int place = ia64_dis_names[disent].insn_index; + int ci = main_table[place].completers; + ia64_insn tinsn = main_table[place].opcode; + + strcpy (name, ia64_strings [main_table[place].name_index]); + + while (cb) + { + if (cb & 1) + { + int cname = completer_table[ci].name_index; + + tinsn = apply_completer (tinsn, ci); + + if (ia64_strings[cname][0] != '\0') + { + strcat (name, "."); + strcat (name, ia64_strings[cname]); + } + if (cb != 1) + { + ci = completer_table[ci].subentries; + } + } + else + { + ci = completer_table[ci].alternative; + } + if (ci < 0) + { + abort (); + } + cb = cb >> 1; + } + if (tinsn != (insn & main_table[place].mask)) + { + abort (); + } + return make_ia64_opcode (insn, name, place, + completer_table[ci].dependencies); + } +} + +/* Search the main_opcode table starting from PLACE for an opcode that + matches NAME. Return NULL if one is not found. */ + +static struct ia64_opcode * +ia64_find_matching_opcode (const char *name, short place) +{ + char op[129]; + const char *suffix; + short name_index; + + if (strlen (name) > 128) + { + return NULL; + } + suffix = name; + get_opc_prefix (&suffix, op); + name_index = find_string_ent (op); + if (name_index < 0) + { + return NULL; + } + + while (main_table[place].name_index == name_index) + { + const char *curr_suffix = suffix; + ia64_insn curr_insn = main_table[place].opcode; + short completer = -1; + + do { + if (suffix[0] == '\0') + { + completer = find_completer (place, completer, suffix); + } + else + { + get_opc_prefix (&curr_suffix, op); + completer = find_completer (place, completer, op); + } + if (completer != -1) + { + curr_insn = apply_completer (curr_insn, completer); + } + } while (completer != -1 && curr_suffix[0] != '\0'); + + if (completer != -1 && curr_suffix[0] == '\0' + && completer_table[completer].terminal_completer) + { + int depind = completer_table[completer].dependencies; + return make_ia64_opcode (curr_insn, name, place, depind); + } + else + { + place++; + } + } + return NULL; +} + +/* Find the next opcode after PREV_ENT that matches PREV_ENT, or return NULL + if one does not exist. + + It is the caller's responsibility to invoke ia64_free_opcode () to + release any resources used by the returned entry. */ + +struct ia64_opcode * +ia64_find_next_opcode (struct ia64_opcode *prev_ent) +{ + return ia64_find_matching_opcode (prev_ent->name, + prev_ent->ent_index + 1); +} + +/* Find the first opcode that matches NAME, or return NULL if it does + not exist. + + It is the caller's responsibility to invoke ia64_free_opcode () to + release any resources used by the returned entry. */ + +struct ia64_opcode * +ia64_find_opcode (const char *name) +{ + char op[129]; + const char *suffix; + short place; + short name_index; + + if (strlen (name) > 128) + { + return NULL; + } + suffix = name; + get_opc_prefix (&suffix, op); + name_index = find_string_ent (op); + if (name_index < 0) + { + return NULL; + } + + place = find_main_ent (name_index); + + if (place < 0) + { + return NULL; + } + return ia64_find_matching_opcode (name, place); +} + +/* Free any resources used by ENT. */ +void +ia64_free_opcode (struct ia64_opcode *ent) +{ + free ((void *)ent->name); + free (ent); +} + +const struct ia64_dependency * +ia64_find_dependency (int index) +{ + index = DEP(index); + + if (index < 0 + || index >= (int)(sizeof(dependencies) / sizeof(dependencies[0]))) + return NULL; + + return &dependencies[index]; +} diff --git a/tools/debugger/xenitp/ia64-opc.h b/tools/debugger/xenitp/ia64-opc.h new file mode 100644 index 0000000..4aff188 --- /dev/null +++ b/tools/debugger/xenitp/ia64-opc.h @@ -0,0 +1,133 @@ +/* ia64-opc.h -- IA-64 opcode table. + Copyright 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef IA64_OPC_H +#define IA64_OPC_H + +#include "ia64.h" + +/* define a couple of abbreviations: */ + +#define bOp(x) (((ia64_insn) ((x) & 0xf)) << 37) +#define mOp bOp (-1) +#define Op(x) bOp (x), mOp + +#define FIRST IA64_OPCODE_FIRST +#define X_IN_MLX IA64_OPCODE_X_IN_MLX +#define LAST IA64_OPCODE_LAST +#define PRIV IA64_OPCODE_PRIV +#define NO_PRED IA64_OPCODE_NO_PRED +#define SLOT2 IA64_OPCODE_SLOT2 +#define PSEUDO IA64_OPCODE_PSEUDO +#define F2_EQ_F3 IA64_OPCODE_F2_EQ_F3 +#define LEN_EQ_64MCNT IA64_OPCODE_LEN_EQ_64MCNT +#define MOD_RRBS IA64_OPCODE_MOD_RRBS +#define POSTINC IA64_OPCODE_POSTINC + +#define AR_CCV IA64_OPND_AR_CCV +#define AR_PFS IA64_OPND_AR_PFS +#define AR_CSD IA64_OPND_AR_CSD +#define C1 IA64_OPND_C1 +#define C8 IA64_OPND_C8 +#define C16 IA64_OPND_C16 +#define GR0 IA64_OPND_GR0 +#define IP IA64_OPND_IP +#define PR IA64_OPND_PR +#define PR_ROT IA64_OPND_PR_ROT +#define PSR IA64_OPND_PSR +#define PSR_L IA64_OPND_PSR_L +#define PSR_UM IA64_OPND_PSR_UM + +#define AR3 IA64_OPND_AR3 +#define B1 IA64_OPND_B1 +#define B2 IA64_OPND_B2 +#define CR3 IA64_OPND_CR3 +#define F1 IA64_OPND_F1 +#define F2 IA64_OPND_F2 +#define F3 IA64_OPND_F3 +#define F4 IA64_OPND_F4 +#define P1 IA64_OPND_P1 +#define P2 IA64_OPND_P2 +#define R1 IA64_OPND_R1 +#define R2 IA64_OPND_R2 +#define R3 IA64_OPND_R3 +#define R3_2 IA64_OPND_R3_2 + +#define CPUID_R3 IA64_OPND_CPUID_R3 +#define DBR_R3 IA64_OPND_DBR_R3 +#define DTR_R3 IA64_OPND_DTR_R3 +#define ITR_R3 IA64_OPND_ITR_R3 +#define IBR_R3 IA64_OPND_IBR_R3 +#define MR3 IA64_OPND_MR3 +#define MSR_R3 IA64_OPND_MSR_R3 +#define PKR_R3 IA64_OPND_PKR_R3 +#define PMC_R3 IA64_OPND_PMC_R3 +#define PMD_R3 IA64_OPND_PMD_R3 +#define RR_R3 IA64_OPND_RR_R3 + +#define CCNT5 IA64_OPND_CCNT5 +#define CNT2a IA64_OPND_CNT2a +#define CNT2b IA64_OPND_CNT2b +#define CNT2c IA64_OPND_CNT2c +#define CNT5 IA64_OPND_CNT5 +#define CNT6 IA64_OPND_CNT6 +#define CPOS6a IA64_OPND_CPOS6a +#define CPOS6b IA64_OPND_CPOS6b +#define CPOS6c IA64_OPND_CPOS6c +#define IMM1 IA64_OPND_IMM1 +#define IMM14 IA64_OPND_IMM14 +#define IMM17 IA64_OPND_IMM17 +#define IMM22 IA64_OPND_IMM22 +#define IMM44 IA64_OPND_IMM44 +#define SOF IA64_OPND_SOF +#define SOL IA64_OPND_SOL +#define SOR IA64_OPND_SOR +#define IMM8 IA64_OPND_IMM8 +#define IMM8U4 IA64_OPND_IMM8U4 +#define IMM8M1 IA64_OPND_IMM8M1 +#define IMM8M1U4 IA64_OPND_IMM8M1U4 +#define IMM8M1U8 IA64_OPND_IMM8M1U8 +#define IMM9a IA64_OPND_IMM9a +#define IMM9b IA64_OPND_IMM9b +#define IMMU2 IA64_OPND_IMMU2 +#define IMMU21 IA64_OPND_IMMU21 +#define IMMU24 IA64_OPND_IMMU24 +#define IMMU62 IA64_OPND_IMMU62 +#define IMMU64 IA64_OPND_IMMU64 +#define IMMU5b IA64_OPND_IMMU5b +#define IMMU7a IA64_OPND_IMMU7a +#define IMMU7b IA64_OPND_IMMU7b +#define IMMU9 IA64_OPND_IMMU9 +#define INC3 IA64_OPND_INC3 +#define LEN4 IA64_OPND_LEN4 +#define LEN6 IA64_OPND_LEN6 +#define MBTYPE4 IA64_OPND_MBTYPE4 +#define MHTYPE8 IA64_OPND_MHTYPE8 +#define POS6 IA64_OPND_POS6 +#define TAG13 IA64_OPND_TAG13 +#define TAG13b IA64_OPND_TAG13b +#define TGT25 IA64_OPND_TGT25 +#define TGT25b IA64_OPND_TGT25b +#define TGT25c IA64_OPND_TGT25c +#define TGT64 IA64_OPND_TGT64 + +#endif diff --git a/tools/debugger/xenitp/ia64.h b/tools/debugger/xenitp/ia64.h new file mode 100644 index 0000000..8b5036f --- /dev/null +++ b/tools/debugger/xenitp/ia64.h @@ -0,0 +1,396 @@ +/* ia64.h -- Header file for ia64 opcode table + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang */ + +#ifndef opcode_ia64_h +#define opcode_ia64_h + +#include + +#include "dis-asm.h" + + +typedef BFD_HOST_U_64_BIT ia64_insn; + +enum ia64_insn_type + { + IA64_TYPE_NIL = 0, /* illegal type */ + IA64_TYPE_A, /* integer alu (I- or M-unit) */ + IA64_TYPE_I, /* non-alu integer (I-unit) */ + IA64_TYPE_M, /* memory (M-unit) */ + IA64_TYPE_B, /* branch (B-unit) */ + IA64_TYPE_F, /* floating-point (F-unit) */ + IA64_TYPE_X, /* long encoding (X-unit) */ + IA64_TYPE_DYN, /* Dynamic opcode */ + IA64_NUM_TYPES + }; + +enum ia64_unit + { + IA64_UNIT_NIL = 0, /* illegal unit */ + IA64_UNIT_I, /* integer unit */ + IA64_UNIT_M, /* memory unit */ + IA64_UNIT_B, /* branching unit */ + IA64_UNIT_F, /* floating-point unit */ + IA64_UNIT_L, /* long "unit" */ + IA64_UNIT_X, /* may be integer or branch unit */ + IA64_NUM_UNITS + }; + +/* Changes to this enumeration must be propagated to the operand table in + bfd/cpu-ia64-opc.c + */ +enum ia64_opnd + { + IA64_OPND_NIL, /* no operand---MUST BE FIRST!*/ + + /* constants */ + IA64_OPND_AR_CSD, /* application register csd (ar.csd) */ + IA64_OPND_AR_CCV, /* application register ccv (ar.ccv) */ + IA64_OPND_AR_PFS, /* application register pfs (ar.pfs) */ + IA64_OPND_C1, /* the constant 1 */ + IA64_OPND_C8, /* the constant 8 */ + IA64_OPND_C16, /* the constant 16 */ + IA64_OPND_GR0, /* gr0 */ + IA64_OPND_IP, /* instruction pointer (ip) */ + IA64_OPND_PR, /* predicate register (pr) */ + IA64_OPND_PR_ROT, /* rotating predicate register (pr.rot) */ + IA64_OPND_PSR, /* processor status register (psr) */ + IA64_OPND_PSR_L, /* processor status register L (psr.l) */ + IA64_OPND_PSR_UM, /* processor status register UM (psr.um) */ + + /* register operands: */ + IA64_OPND_AR3, /* third application register # (bits 20-26) */ + IA64_OPND_B1, /* branch register # (bits 6-8) */ + IA64_OPND_B2, /* branch register # (bits 13-15) */ + IA64_OPND_CR3, /* third control register # (bits 20-26) */ + IA64_OPND_F1, /* first floating-point register # */ + IA64_OPND_F2, /* second floating-point register # */ + IA64_OPND_F3, /* third floating-point register # */ + IA64_OPND_F4, /* fourth floating-point register # */ + IA64_OPND_P1, /* first predicate # */ + IA64_OPND_P2, /* second predicate # */ + IA64_OPND_R1, /* first register # */ + IA64_OPND_R2, /* second register # */ + IA64_OPND_R3, /* third register # */ + IA64_OPND_R3_2, /* third register # (limited to gr0-gr3) */ + + /* memory operands: */ + IA64_OPND_MR3, /* memory at addr of third register # */ + + /* indirect operands: */ + IA64_OPND_CPUID_R3, /* cpuid[reg] */ + IA64_OPND_DBR_R3, /* dbr[reg] */ + IA64_OPND_DTR_R3, /* dtr[reg] */ + IA64_OPND_ITR_R3, /* itr[reg] */ + IA64_OPND_IBR_R3, /* ibr[reg] */ + IA64_OPND_MSR_R3, /* msr[reg] */ + IA64_OPND_PKR_R3, /* pkr[reg] */ + IA64_OPND_PMC_R3, /* pmc[reg] */ + IA64_OPND_PMD_R3, /* pmd[reg] */ + IA64_OPND_RR_R3, /* rr[reg] */ + + /* immediate operands: */ + IA64_OPND_CCNT5, /* 5-bit count (31 - bits 20-24) */ + IA64_OPND_CNT2a, /* 2-bit count (1 + bits 27-28) */ + IA64_OPND_CNT2b, /* 2-bit count (bits 27-28): 1, 2, 3 */ + IA64_OPND_CNT2c, /* 2-bit count (bits 30-31): 0, 7, 15, or 16 */ + IA64_OPND_CNT5, /* 5-bit count (bits 14-18) */ + IA64_OPND_CNT6, /* 6-bit count (bits 27-32) */ + IA64_OPND_CPOS6a, /* 6-bit count (63 - bits 20-25) */ + IA64_OPND_CPOS6b, /* 6-bit count (63 - bits 14-19) */ + IA64_OPND_CPOS6c, /* 6-bit count (63 - bits 31-36) */ + IA64_OPND_IMM1, /* signed 1-bit immediate (bit 36) */ + IA64_OPND_IMMU2, /* unsigned 2-bit immediate (bits 13-14) */ + IA64_OPND_IMMU5b, /* unsigned 5-bit immediate (32 + bits 14-18) */ + IA64_OPND_IMMU7a, /* unsigned 7-bit immediate (bits 13-19) */ + IA64_OPND_IMMU7b, /* unsigned 7-bit immediate (bits 20-26) */ + IA64_OPND_SOF, /* 8-bit stack frame size */ + IA64_OPND_SOL, /* 8-bit size of locals */ + IA64_OPND_SOR, /* 6-bit number of rotating registers (scaled by 8) */ + IA64_OPND_IMM8, /* signed 8-bit immediate (bits 13-19 & 36) */ + IA64_OPND_IMM8U4, /* cmp4*u signed 8-bit immediate (bits 13-19 & 36) */ + IA64_OPND_IMM8M1, /* signed 8-bit immediate -1 (bits 13-19 & 36) */ + IA64_OPND_IMM8M1U4, /* cmp4*u signed 8-bit immediate -1 (bits 13-19 & 36)*/ + IA64_OPND_IMM8M1U8, /* cmp*u signed 8-bit immediate -1 (bits 13-19 & 36) */ + IA64_OPND_IMMU9, /* unsigned 9-bit immediate (bits 33-34, 20-26) */ + IA64_OPND_IMM9a, /* signed 9-bit immediate (bits 6-12, 27, 36) */ + IA64_OPND_IMM9b, /* signed 9-bit immediate (bits 13-19, 27, 36) */ + IA64_OPND_IMM14, /* signed 14-bit immediate (bits 13-19, 27-32, 36) */ + IA64_OPND_IMM17, /* signed 17-bit immediate (2*bits 6-12, 24-31, 36) */ + IA64_OPND_IMMU21, /* unsigned 21-bit immediate (bits 6-25, 36) */ + IA64_OPND_IMM22, /* signed 22-bit immediate (bits 13-19, 22-36) */ + IA64_OPND_IMMU24, /* unsigned 24-bit immediate (bits 6-26, 31-32, 36) */ + IA64_OPND_IMM44, /* signed 44-bit immediate (2^16*bits 6-32, 36) */ + IA64_OPND_IMMU62, /* unsigned 62-bit immediate */ + IA64_OPND_IMMU64, /* unsigned 64-bit immediate (lotsa bits...) */ + IA64_OPND_INC3, /* signed 3-bit (bits 13-15): +/-1, 4, 8, 16 */ + IA64_OPND_LEN4, /* 4-bit count (bits 27-30 + 1) */ + IA64_OPND_LEN6, /* 6-bit count (bits 27-32 + 1) */ + IA64_OPND_MBTYPE4, /* 4-bit mux type (bits 20-23) */ + IA64_OPND_MHTYPE8, /* 8-bit mux type (bits 20-27) */ + IA64_OPND_POS6, /* 6-bit count (bits 14-19) */ + IA64_OPND_TAG13, /* signed 13-bit tag (ip + 16*bits 6-12, 33-34) */ + IA64_OPND_TAG13b, /* signed 13-bit tag (ip + 16*bits 24-32) */ + IA64_OPND_TGT25, /* signed 25-bit (ip + 16*bits 6-25, 36) */ + IA64_OPND_TGT25b, /* signed 25-bit (ip + 16*bits 6-12, 20-32, 36) */ + IA64_OPND_TGT25c, /* signed 25-bit (ip + 16*bits 13-32, 36) */ + IA64_OPND_TGT64, /* 64-bit (ip + 16*bits 13-32, 36, 2-40(L)) */ + IA64_OPND_LDXMOV, /* any symbol, generates R_IA64_LDXMOV. */ + + IA64_OPND_COUNT /* # of operand types (MUST BE LAST!) */ + }; + +enum ia64_dependency_mode +{ + IA64_DV_RAW, + IA64_DV_WAW, + IA64_DV_WAR, +}; + +enum ia64_dependency_semantics +{ + IA64_DVS_NONE, + IA64_DVS_IMPLIED, + IA64_DVS_IMPLIEDF, + IA64_DVS_DATA, + IA64_DVS_INSTR, + IA64_DVS_SPECIFIC, + IA64_DVS_STOP, + IA64_DVS_OTHER, +}; + +enum ia64_resource_specifier +{ + IA64_RS_ANY, + IA64_RS_AR_K, + IA64_RS_AR_UNAT, + IA64_RS_AR, /* 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111 */ + IA64_RS_ARb, /* 48-63, 112-127 */ + IA64_RS_BR, + IA64_RS_CFM, + IA64_RS_CPUID, + IA64_RS_CR_IRR, + IA64_RS_CR_LRR, + IA64_RS_CR, /* 3-7,10-15,18,26-63,75-79,82-127 */ + IA64_RS_DBR, + IA64_RS_FR, + IA64_RS_FRb, + IA64_RS_GR0, + IA64_RS_GR, + IA64_RS_IBR, + IA64_RS_INSERVICE, /* CR[EOI] or CR[IVR] */ + IA64_RS_MSR, + IA64_RS_PKR, + IA64_RS_PMC, + IA64_RS_PMD, + IA64_RS_PR, /* non-rotating, 1-15 */ + IA64_RS_PRr, /* rotating, 16-62 */ + IA64_RS_PR63, + IA64_RS_RR, + + IA64_RS_ARX, /* ARs not in RS_AR or RS_ARb */ + IA64_RS_CRX, /* CRs not in RS_CR */ + IA64_RS_PSR, /* PSR bits */ + IA64_RS_RSE, /* implementation-specific RSE resources */ + IA64_RS_AR_FPSR, +}; + +enum ia64_rse_resource +{ + IA64_RSE_N_STACKED_PHYS, + IA64_RSE_BOF, + IA64_RSE_STORE_REG, + IA64_RSE_LOAD_REG, + IA64_RSE_BSPLOAD, + IA64_RSE_RNATBITINDEX, + IA64_RSE_CFLE, + IA64_RSE_NDIRTY, +}; + +/* Information about a given resource dependency */ +struct ia64_dependency +{ + /* Name of the resource */ + const char *name; + /* Does this dependency need further specification? */ + enum ia64_resource_specifier specifier; + /* Mode of dependency */ + enum ia64_dependency_mode mode; + /* Dependency semantics */ + enum ia64_dependency_semantics semantics; + /* Register index, if applicable (distinguishes AR, CR, and PSR deps) */ +#define REG_NONE (-1) + int regindex; + /* Special info on semantics */ + const char *info; +}; + +/* Two arrays of indexes into the ia64_dependency table. + chks are dependencies to check for conflicts when an opcode is + encountered; regs are dependencies to register (mark as used) when an + opcode is used. chks correspond to readers (RAW) or writers (WAW or + WAR) of a resource, while regs correspond to writers (RAW or WAW) and + readers (WAR) of a resource. */ +struct ia64_opcode_dependency +{ + int nchks; + const unsigned short *chks; + int nregs; + const unsigned short *regs; +}; + +/* encode/extract the note/index for a dependency */ +#define RDEP(N,X) (((N)<<11)|(X)) +#define NOTE(X) (((X)>>11)&0x1F) +#define DEP(X) ((X)&0x7FF) + +/* A template descriptor describes the execution units that are active + for each of the three slots. It also specifies the location of + instruction group boundaries that may be present between two slots. */ +struct ia64_templ_desc + { + int group_boundary; /* 0=no boundary, 1=between slot 0 & 1, etc. */ + enum ia64_unit exec_unit[3]; + const char *name; + }; + +/* The opcode table is an array of struct ia64_opcode. */ + +struct ia64_opcode + { + /* The opcode name. */ + const char *name; + + /* The type of the instruction: */ + enum ia64_insn_type type; + + /* Number of output operands: */ + int num_outputs; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + ia64_insn opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + ia64_insn mask; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + enum ia64_opnd operands[5]; + + /* One bit flags for the opcode. These are primarily used to + indicate specific processors and environments support the + instructions. The defined values are listed below. */ + unsigned int flags; + + /* Used by ia64_find_next_opcode (). */ + short ent_index; + + /* Opcode dependencies. */ + const struct ia64_opcode_dependency *dependencies; + }; + +/* Values defined for the flags field of a struct ia64_opcode. */ + +#define IA64_OPCODE_FIRST (1<<0) /* must be first in an insn group */ +#define IA64_OPCODE_X_IN_MLX (1<<1) /* insn is allowed in X slot of MLX */ +#define IA64_OPCODE_LAST (1<<2) /* must be last in an insn group */ +#define IA64_OPCODE_PRIV (1<<3) /* privileged instruct */ +#define IA64_OPCODE_SLOT2 (1<<4) /* insn allowed in slot 2 only */ +#define IA64_OPCODE_NO_PRED (1<<5) /* insn cannot be predicated */ +#define IA64_OPCODE_PSEUDO (1<<6) /* insn is a pseudo-op */ +#define IA64_OPCODE_F2_EQ_F3 (1<<7) /* constraint: F2 == F3 */ +#define IA64_OPCODE_LEN_EQ_64MCNT (1<<8) /* constraint: LEN == 64-CNT */ +#define IA64_OPCODE_MOD_RRBS (1<<9) /* modifies all rrbs in CFM */ +#define IA64_OPCODE_POSTINC (1<<10) /* postincrement MR3 operand */ + +/* A macro to extract the major opcode from an instruction. */ +#define IA64_OP(i) (((i) >> 37) & 0xf) + +enum ia64_operand_class + { + IA64_OPND_CLASS_CST, /* constant */ + IA64_OPND_CLASS_REG, /* register */ + IA64_OPND_CLASS_IND, /* indirect register */ + IA64_OPND_CLASS_ABS, /* absolute value */ + IA64_OPND_CLASS_REL, /* IP-relative value */ + }; + +/* The operands table is an array of struct ia64_operand. */ + +struct ia64_operand +{ + enum ia64_operand_class class; + + /* Set VALUE as the operand bits for the operand of type SELF in the + instruction pointed to by CODE. If an error occurs, *CODE is not + modified and the returned string describes the cause of the + error. If no error occurs, NULL is returned. */ + const char *(*insert) (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code); + + /* Extract the operand bits for an operand of type SELF from + instruction CODE store them in *VALUE. If an error occurs, the + cause of the error is described by the string returned. If no + error occurs, NULL is returned. */ + const char *(*extract) (const struct ia64_operand *self, ia64_insn code, + ia64_insn *value); + + /* A string whose meaning depends on the operand class. */ + + const char *str; + + struct bit_field + { + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + } + field[4]; /* no operand has more than this many bit-fields */ + + unsigned int flags; + + const char *desc; /* brief description */ +}; + +/* Values defined for the flags field of a struct ia64_operand. */ + +/* Disassemble as signed decimal (instead of hex): */ +#define IA64_OPND_FLAG_DECIMAL_SIGNED (1<<0) +/* Disassemble as unsigned decimal (instead of hex): */ +#define IA64_OPND_FLAG_DECIMAL_UNSIGNED (1<<1) + +extern const struct ia64_templ_desc ia64_templ_desc[16]; + +/* The tables are sorted by major opcode number and are otherwise in + the order in which the disassembler should consider instructions. */ +extern struct ia64_opcode ia64_opcodes_a[]; +extern struct ia64_opcode ia64_opcodes_i[]; +extern struct ia64_opcode ia64_opcodes_m[]; +extern struct ia64_opcode ia64_opcodes_b[]; +extern struct ia64_opcode ia64_opcodes_f[]; +extern struct ia64_opcode ia64_opcodes_d[]; + + +extern struct ia64_opcode *ia64_find_opcode (const char *name); +extern struct ia64_opcode *ia64_find_next_opcode (struct ia64_opcode *ent); + +extern struct ia64_opcode *ia64_dis_opcode (ia64_insn insn, + enum ia64_insn_type type); + +extern void ia64_free_opcode (struct ia64_opcode *ent); +extern const struct ia64_dependency *ia64_find_dependency (int index); + +/* To avoid circular library dependencies, this array is implemented + in bfd/cpu-ia64-opc.c: */ +extern const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT]; + +#endif /* opcode_ia64_h */ diff --git a/tools/debugger/xenitp/xenitp.c b/tools/debugger/xenitp/xenitp.c new file mode 100644 index 0000000..95d3820 --- /dev/null +++ b/tools/debugger/xenitp/xenitp.c @@ -0,0 +1,1720 @@ +/* tools/debugger/xenitp.c - A low-level debugger. + + Based on xenctxt.c, but heavily modified. + Copyright 2007 Tristan Gingold + + Xenitp 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. + + Xenitp is distributed in the hope that 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 "xenctrl.h" +#include +#include "dis-asm.h" + +#ifdef __HYPERVISOR_ia64_debug_op +#define HAVE_DEBUG_OP +#include +#endif + +static int xc_handle = 0; +static int domid = 0; +static vcpu_guest_context_t *cur_ctx; +static int cur_vcpu; + +#define PSR_BN (1UL << 44) +#define PSR_SS (1UL << 40) +#define PSR_DB (1UL << 24) +#define PSR_TB (1UL << 26) +#define PSR_DD (1UL << 39) +#define PSR_ID (1UL << 37) +#define PSR_IT (1UL << 36) +#define PSR_DT (1UL << 17) +#define PSR_RI_SHIFT 41 +#define CFM_SOF_MASK 0x3f + +int virt_to_phys (int is_inst, unsigned long vaddr, unsigned long *paddr); + +/* wrapper for vcpu_gest_context_any_t */ +static int xc_ia64_vcpu_getcontext(int xc_handle, + uint32_t domid, + uint32_t vcpu, + vcpu_guest_context_t *ctxt) +{ + return xc_vcpu_getcontext(xc_handle, domid, vcpu, + (vcpu_guest_context_any_t *)ctxt); +} + +static inline unsigned int ctx_slot (vcpu_guest_context_t *ctx) +{ + return (ctx->regs.psr >> PSR_RI_SHIFT) & 3; +} + +unsigned char * +target_map_memory (unsigned long paddr) +{ + static unsigned long cur_page = (unsigned long)-1; + static unsigned char *cur_map = NULL; + + if ((paddr >> XC_PAGE_SHIFT) != cur_page) { + if (cur_map) { + munmap (cur_map, XC_PAGE_SIZE); + cur_map = NULL; + } + cur_page = paddr >> XC_PAGE_SHIFT; + cur_map = xc_map_foreign_range (xc_handle, domid, XC_PAGE_SIZE, + PROT_READ, cur_page); + if (cur_map == NULL) { + perror ("cannot map page"); + cur_page = -1; + return NULL; + } + } + return cur_map + (paddr & (XC_PAGE_SIZE - 1)); +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +int +target_read_memory (bfd_vma memaddr, bfd_byte *myaddr, + unsigned int length, struct disassemble_info *info) +{ + int i; + unsigned long paddr; + + if (cur_ctx->regs.psr & PSR_IT) { + if (virt_to_phys (1, memaddr, &paddr) != 0) + return EIO; + } + else { + /* Clear UC. */ + paddr = memaddr & ~(1UL << 63); + } + + for (i = 0; i < length; i++) { + unsigned char *p = target_map_memory (paddr + i); + + if (p == NULL) + return EIO; + myaddr[i] = *p; + } + return 0; +} + +/* Print an error message. We can assume that this is in response to + an error return from buffer_read_memory. */ +void +perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info) +{ + if (status != EIO) + /* Can't happen. */ + (*info->fprintf_func) (info->stream, "Unknown error %d\n", status); + else + /* Actually, address between memaddr and memaddr + len was + out of bounds. */ + (*info->fprintf_func) (info->stream, + "Address 0x%" PRIx64 " is out of bounds.\n", + memaddr); +} + +/* This could be in a separate file, to save miniscule amounts of space + in statically linked executables. */ + +/* Just print the address is hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ + +void +generic_print_address (bfd_vma addr, struct disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr); +} + +/* Just return the given address. */ + +int +generic_symbol_at_address (bfd_vma addr, struct disassemble_info * info) +{ + return 1; +} + +bfd_boolean +generic_symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED, + struct disassemble_info *info ATTRIBUTE_UNUSED) +{ + return 1; +} + +bfd_vma bfd_getl32 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + v |= (unsigned long) addr[2] << 16; + v |= (unsigned long) addr[3] << 24; + + return (bfd_vma) v; +} + +bfd_vma bfd_getl64 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + v |= (unsigned long) addr[2] << 16; + v |= (unsigned long) addr[3] << 24; + v |= (unsigned long) addr[4] << 32; + v |= (unsigned long) addr[5] << 40; + v |= (unsigned long) addr[6] << 48; + v |= (unsigned long) addr[7] << 56; + + return (bfd_vma) v; +} + +bfd_vma bfd_getb32 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + v |= (unsigned long) addr[2] << 8; + v |= (unsigned long) addr[3]; + + return (bfd_vma) v; +} + +bfd_vma bfd_getl16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + + return (bfd_vma) v; +} + +bfd_vma bfd_getb16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + + return (bfd_vma) v; +} + +void +init_disassemble_info (struct disassemble_info *info, void *stream, + fprintf_ftype fprintf_func) +{ + memset (info, 0, sizeof (*info)); + + info->flavour = bfd_target_unknown_flavour; + info->arch = bfd_arch_unknown; + info->endian = BFD_ENDIAN_UNKNOWN; + info->octets_per_byte = 1; + info->fprintf_func = fprintf_func; + info->stream = stream; + info->read_memory_func = target_read_memory; + info->memory_error_func = perror_memory; + info->print_address_func = generic_print_address; + info->symbol_at_address_func = generic_symbol_at_address; + info->symbol_is_valid = generic_symbol_is_valid; + info->display_endian = BFD_ENDIAN_UNKNOWN; +} + + +void target_disas (FILE *out, unsigned long code, unsigned long size) +{ + unsigned long pc; + int count; + struct disassemble_info disasm_info; + + INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf); + + disasm_info.read_memory_func = target_read_memory; +#if 0 + disasm_info.buffer = NULL; + disasm_info.buffer_vma = (unsigned long)code; + disasm_info.buffer_length = size; +#endif + + disasm_info.endian = BFD_ENDIAN_LITTLE; + disasm_info.mach = 0; //bfd_mach_ia64; + + for (pc = code; pc < code + size; pc += count) { + int slot = (pc & 0x0f) / 6; + fprintf (out, "0x%016lx+%d:%c ", pc & ~0x0fUL, slot, + ((pc & ~0x0fUL) == cur_ctx->regs.ip + && slot == ctx_slot (cur_ctx)) ? '*' : ' '); + + count = print_insn_ia64 (pc, &disasm_info); + +#if 0 + { + int i; + uint8_t b; + + fprintf (out, " {"); + for (i = 0; i < count; i++) { + target_read_memory (pc + i, &b, 1, &disasm_info); + fprintf (out, " %02x", b); + } + fprintf (out, " }"); + } +#endif + fprintf (out, "\n"); + if (count < 0) + break; + } +} + + +#define PTE_ED_SHIFT 52 +#define PTE_ED_MASK 1 +#define PTE_PPN_SHIFT 12 +#define PTE_PPN_MASK 0x3fffffffff +#define PTE_AR_SHIFT 9 +#define PTE_AR_MASK 7 +#define PTE_PL_SHIFT 7 +#define PTE_PL_MASK 3 +#define PTE_D_SHIFT 6 +#define PTE_D_MASK 1 +#define PTE_A_SHIFT 5 +#define PTE_A_MASK 1 +#define PTE_MA_SHIFT 2 +#define PTE_MA_MASK 7 +#define PTE_P_SHIFT 0 +#define PTE_P_MASK 1 +#define ITIR_KEY_SHIFT 8 +#define ITIR_KEY_MASK 0xffffff +#define ITIR_PS_SHIFT 2 +#define ITIR_PS_MASK 0x3f +#define ITIR_PS_MIN 12 +#define ITIR_PS_MAX 28 +#define RR_RID_SHIFT 8 +#define RR_RID_MASK 0xffffff +#define RR_PS_SHIFT 2 +#define RR_PS_MASK 0x3f +#define RR_VE_MASK 1 + +static const char *get_ps (int ps_val) +{ + static const char ps[][5] = {" 4K", " 8K", " 16K", " ", + " 64K", " ", "256K", " ", + " 1M", " ", " 4M", " ", + " 16M", " ", " 64M", " ", + "256M"}; + return ((ps_val >= ITIR_PS_MIN && ps_val <= ITIR_PS_MAX) ? + ps[ps_val - ITIR_PS_MIN] : " "); + +} + +static void print_a_tr (int i, const struct ia64_tr_entry *tr) +{ + int ps_val, ma_val; + unsigned long pa; + + static const char ma[][4] = {"WB ", " ", " ", " ", + "UC ", "UCE", "WC ", "Nat"}; + + ps_val = tr->itir >> ITIR_PS_SHIFT & ITIR_PS_MASK; + ma_val = tr->pte >> PTE_MA_SHIFT & PTE_MA_MASK; + pa = (tr->pte >> PTE_PPN_SHIFT & PTE_PPN_MASK) << PTE_PPN_SHIFT; + pa = (pa >> ps_val) << ps_val; + printf (" [%2d] %ld %06lx %016lx %013lx %02d %s %ld %ld %ld %ld " + "%ld %d %s %06lx\n", i, + tr->pte >> PTE_P_SHIFT & PTE_P_MASK, + tr->rid >> RR_RID_SHIFT & RR_RID_MASK, + tr->vadr, pa, ps_val, get_ps (ps_val), + tr->pte >> PTE_ED_SHIFT & PTE_ED_MASK, + tr->pte >> PTE_PL_SHIFT & PTE_PL_MASK, + tr->pte >> PTE_AR_SHIFT & PTE_AR_MASK, + tr->pte >> PTE_A_SHIFT & PTE_A_MASK, + tr->pte >> PTE_D_SHIFT & PTE_D_MASK, + ma_val, ma[ma_val], + tr->itir >> ITIR_KEY_SHIFT & ITIR_KEY_MASK); +} + +void print_ctx (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + int i; + unsigned int rbs_size, cfm_sof; + + rbs_size = (regs->ar.bsp - regs->ar.bspstore) / 8; + cfm_sof = (regs->cfm & CFM_SOF_MASK); + printf ("bspstore: %016lx bsp: %016lx rbs_size=%d, sof=%d\n", + regs->ar.bspstore, regs->ar.bsp, rbs_size, cfm_sof); + + for (i = 0; i < cfm_sof; i++) { + int off = cfm_sof - i; + unsigned int rbs_off = + (((62 - ((rbs_size + regs->rbs_voff) % 64) + off)) / 63) + off; + if (rbs_off > rbs_size) + break; + printf (" r%02d: %016lx%s", 32 + i, + regs->rbs[rbs_size - rbs_off], + (i % 3) != 2 ? " " : "\n"); + } + if ((i % 3) != 0) + printf ("\n"); + + printf ("\n"); + printf (" r1: %016lx ", regs->r[1]); + printf (" r2: %016lx ", regs->r[2]); + printf (" r3: %016lx\n", regs->r[3]); + printf (" r4: %016lx ", regs->r[4]); + printf (" r5: %016lx ", regs->r[5]); + printf (" r6: %016lx\n", regs->r[6]); + printf (" r7: %016lx ", regs->r[7]); + printf (" r8: %016lx ", regs->r[8]); + printf (" r9: %016lx\n", regs->r[9]); + printf (" r10: %016lx ", regs->r[10]); + printf (" r11: %016lx ", regs->r[11]); + printf (" sp: %016lx\n", regs->r[12]); + printf (" tp: %016lx ", regs->r[13]); + printf (" r14: %016lx ", regs->r[14]); + printf (" r15: %016lx\n", regs->r[15]); + printf (" r16: %016lx ", regs->r[16]); + printf (" r17: %016lx ", regs->r[17]); + printf (" r18: %016lx\n", regs->r[18]); + printf (" r19: %016lx ", regs->r[19]); + printf (" r20: %016lx ", regs->r[20]); + printf (" r21: %016lx\n", regs->r[21]); + printf (" r22: %016lx ", regs->r[22]); + printf (" r23: %016lx ", regs->r[23]); + printf (" r24: %016lx\n", regs->r[24]); + printf (" r25: %016lx ", regs->r[25]); + printf (" r26: %016lx ", regs->r[26]); + printf (" r27: %016lx\n", regs->r[27]); + printf (" r28: %016lx ", regs->r[28]); + printf (" r29: %016lx ", regs->r[29]); + printf (" r30: %016lx\n", regs->r[30]); + printf (" r31: %016lx ", regs->r[31]); + printf (" "); + printf (" b0: %016lx\n", regs->b[0]); + + printf ("\n"); + printf (" psr: %016lx ", regs->psr); + printf (" cfm: %016lx ", regs->cfm); + printf (" pr: %016lx\n", regs->pr); + + printf ("\n"); + printf (" ip: %016lx+%d", regs->ip, (int)(regs->psr >> PSR_RI_SHIFT) & 3); + printf ("\n"); + target_disas (stdout, regs->ip, 16); +} + +void print_br (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + + printf (" b0: %016lx ", regs->b[0]); + printf (" b1: %016lx ", regs->b[1]); + printf (" b2: %016lx\n", regs->b[2]); + printf (" b3: %016lx ", regs->b[3]); + printf (" b4: %016lx ", regs->b[4]); + printf (" b5: %016lx\n", regs->b[5]); + printf (" b6: %016lx ", regs->b[6]); + printf (" b7: %016lx\n", regs->b[7]); +} + +void print_regs (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + + printf (" r1: %016lx ", regs->r[1]); + printf (" r2: %016lx ", regs->r[2]); + printf (" r3: %016lx\n", regs->r[3]); + printf (" r4: %016lx ", regs->r[4]); + printf (" r5: %016lx ", regs->r[5]); + printf (" r6: %016lx\n", regs->r[6]); + printf (" r7: %016lx ", regs->r[7]); + printf (" r8: %016lx ", regs->r[8]); + printf (" r9: %016lx\n", regs->r[9]); + printf (" r10: %016lx ", regs->r[10]); + printf (" r11: %016lx ", regs->r[11]); + printf (" sp: %016lx\n", regs->r[12]); + printf (" tp: %016lx ", regs->r[13]); + printf (" r14: %016lx ", regs->r[14]); + printf (" r15: %016lx\n", regs->r[15]); + + printf (" Bank %d (current) Bank %d\n", + (regs->psr & PSR_BN) ? 1 : 0, (regs->psr & PSR_BN) ? 0 : 1); + printf ("16:%016lx ", regs->r[16]); + printf ("17:%016lx ", regs->r[17]); + printf ("16:%016lx ", regs->bank[0]); + printf ("17:%016lx\n", regs->bank[1]); + printf ("18:%016lx ", regs->r[18]); + printf ("19:%016lx ", regs->r[19]); + printf ("18:%016lx ", regs->bank[2]); + printf ("19:%016lx\n", regs->bank[3]); + printf ("20:%016lx ", regs->r[20]); + printf ("21:%016lx ", regs->r[21]); + printf ("20:%016lx ", regs->bank[4]); + printf ("21:%016lx\n", regs->bank[5]); + printf ("22:%016lx ", regs->r[22]); + printf ("23:%016lx ", regs->r[23]); + printf ("22:%016lx ", regs->bank[6]); + printf ("23:%016lx\n", regs->bank[7]); + printf ("24:%016lx ", regs->r[24]); + printf ("25:%016lx ", regs->r[25]); + printf ("24:%016lx ", regs->bank[8]); + printf ("25:%016lx\n", regs->bank[9]); + printf ("26:%016lx ", regs->r[26]); + printf ("27:%016lx ", regs->r[27]); + printf ("26:%016lx ", regs->bank[10]); + printf ("27:%016lx\n", regs->bank[11]); + printf ("28:%016lx ", regs->r[28]); + printf ("29:%016lx ", regs->r[29]); + printf ("28:%016lx ", regs->bank[12]); + printf ("29:%016lx\n", regs->bank[13]); + printf ("30:%016lx ", regs->r[30]); + printf ("31:%016lx ", regs->r[31]); + printf ("30:%016lx ", regs->bank[14]); + printf ("31:%016lx\n", regs->bank[15]); + printf ("\n"); +} + +void print_cr (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + + printf (" dcr: %016lx ", regs->cr.dcr); + printf (" itm: %016lx ", regs->cr.itm); + printf (" iva: %016lx\n", regs->cr.iva); + printf (" pta: %016lx ", regs->cr.pta); + printf (" ipsr: %016lx ", regs->cr.ipsr); + printf (" isr: %016lx\n", regs->cr.isr); + printf (" iip: %016lx ", regs->cr.iip); + printf (" ifa: %016lx ", regs->cr.ifa); + printf (" itir: %016lx\n", regs->cr.itir); + printf (" iipa: %016lx ", regs->cr.iipa); + printf (" ifs: %016lx ", regs->cr.ifs); + printf (" iim: %016lx\n", regs->cr.iim); + printf (" iha: %016lx ", regs->cr.iha); + printf (" lid: %016lx ", regs->cr.lid); + printf (" ivr: %016lx\n", regs->cr.ivr); + printf (" tpr: %016lx ", regs->cr.tpr); + printf (" eoi: %016lx ", regs->cr.eoi); + printf (" irr0: %016lx\n", regs->cr.irr[0]); + printf (" irr1: %016lx ", regs->cr.irr[1]); + printf (" irr2: %016lx ", regs->cr.irr[2]); + printf (" irr3: %016lx\n", regs->cr.irr[3]); + printf (" itv: %016lx ", regs->cr.itv); + printf (" pmv: %016lx ", regs->cr.pmv); + printf (" cmcv: %016lx\n", regs->cr.cmcv); + printf (" lrr0: %016lx ", regs->cr.lrr0); + printf (" lrr1: %016lx ", regs->cr.lrr1); + printf (" ev_cb:%016lx\n", ctx->event_callback_ip); +} + +void print_ar (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + + printf (" kr0: %016lx ", regs->ar.kr[0]); + printf (" kr1: %016lx ", regs->ar.kr[1]); + printf (" kr2: %016lx\n", regs->ar.kr[2]); + printf (" kr3: %016lx ", regs->ar.kr[3]); + printf (" kr4: %016lx ", regs->ar.kr[4]); + printf (" kr5: %016lx\n", regs->ar.kr[5]); + printf (" kr6: %016lx ", regs->ar.kr[6]); + printf (" kr7: %016lx ", regs->ar.kr[7]); + printf (" rsc: %016lx\n", regs->ar.rsc); + printf (" bsp: %016lx ", regs->ar.bsp); + printf (" bsps: %016lx ", regs->ar.bspstore); + printf (" rnat: %016lx\n", regs->ar.rnat); + printf (" csd: %016lx ", regs->ar.csd); + printf (" ccv: %016lx ", regs->ar.ccv); + printf (" unat: %016lx\n", regs->ar.unat); + printf (" fpsr: %016lx ", regs->ar.fpsr); + printf (" itc: %016lx\n", regs->ar.itc); + printf (" pfs: %016lx ", regs->ar.pfs); + printf (" lc: %016lx ", regs->ar.lc); + printf (" ec: %016lx\n", regs->ar.ec); +} + +void print_a_rr (int num, unsigned long rr) +{ + int ps_val = (rr >> RR_PS_SHIFT) & RR_PS_MASK; + + printf (" [%d] %06lx %02x %s %ld\n", + num, (rr >> RR_RID_SHIFT) & RR_RID_MASK, + ps_val, get_ps (ps_val), rr & RR_VE_MASK); +} + +void print_rr (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + int i; + + printf (" rr: rid ps ve\n"); + for (i = 0; i < 8; i++) + print_a_rr (i, regs->rr[i]); +} + +void print_db (vcpu_guest_context_t *ctx) +{ + struct vcpu_guest_context_regs *regs = &ctx->regs; + int i; + + for (i = 0; i < 7; i += 2) + printf (" ibr[%d]: %016lx ibr[%d]: %016lx\n", + i, regs->ibr[i], i + 1, regs->ibr[i + 1]); + printf ("\n"); + for (i = 0; i < 7; i += 2) + printf (" dbr[%d]: %016lx dbr[%d]: %016lx\n", + i, regs->dbr[i], i + 1, regs->dbr[i + 1]); +} + +struct bit_descr { + const char *name; + unsigned char sz; +}; + +const struct bit_descr psr_bits[] = + { + {"", 1 }, {"be", 1 }, {"up", 1 }, {"ac", 1 }, + {"mfl", 1 }, {"mfh", 1 }, {"", 7 }, + {"ic", 1 }, {"i", 1 }, {"pk", 1 }, + {"", 1 }, {"dt", 1 }, {"dfl", 1 }, {"dfh", 1 }, + {"sp", 1 }, {"pp", 1 }, {"di", 1 }, {"si", 1 }, + {"db", 1 }, {"lp", 1 }, {"tb", 1 }, {"rt", 1 }, + {"", 4 }, + {"cpl", 2 }, {"is", 1 }, {"mc", 1 }, + {"it", 1 }, {"id", 1 }, {"da", 1 }, {"dd", 1 }, + {"ss", 1 }, {"ri", 2 }, {"ed", 1 }, + {"bn", 1 }, {"ia", 1 }, {"vm", 1 }, + {NULL, 0 } + }; + +void print_bits (const struct bit_descr *desc, unsigned long val) +{ + const struct bit_descr *d; + unsigned int off; + + /* Reverse order. */ + for (d = desc, off = 0; d->name; d++) + off += d->sz; + + d--; + + while (1) { + off -= d->sz; + if (*d->name) { + if (d->sz != 1 || ((val >> off) & 1)) + printf (" %s", d->name); + if (d->sz != 1) + printf ("=%lx", (val >> off) & ((1 << d->sz) - 1)); + } + if (d == desc) + break; + d--; + } +} + +void print_tr (vcpu_guest_context_t *ctx) +{ + struct vcpu_tr_regs *tr = &ctx->regs.tr; + int i; + + printf ("\n itr: P rid va pa ps ed pl " + "ar a d ma key\n"); + + for (i = 0; i < sizeof (tr->itrs) / sizeof (tr->itrs[0]); i++) + print_a_tr (i, &tr->itrs[i]); + + printf ("\n dtr: P rid va pa ps ed pl " + "ar a d ma key\n"); + + for (i = 0; i < sizeof (tr->dtrs) / sizeof (tr->dtrs[0]); i++) + print_a_tr (i, &tr->dtrs[i]); +} + +int lock_pages (void *addr, size_t len); +void unlock_pages (void *addr, size_t len); +int do_xen_hypercall (int xc_handle, privcmd_hypercall_t *hypercall); + +#ifdef HAVE_DEBUG_OP +static int do_ia64_debug_op (int xc_handle, + unsigned long cmd, unsigned long domain, + xen_ia64_debug_op_t *op) +{ + int ret = -1; + privcmd_hypercall_t hypercall; + + hypercall.op = __HYPERVISOR_ia64_debug_op; + hypercall.arg[0] = cmd; + hypercall.arg[1] = domain; + hypercall.arg[2] = (unsigned long)op; + + if (lock_pages (op, sizeof (*op)) != 0) { + perror ("Could not lock memory for Xen hypercall"); + goto out1; + } + + ret = do_xen_hypercall (xc_handle, &hypercall); + if (ret < 0) { + if (errno == EACCES) + fprintf (stderr,"domctl operation failed -- need to " + "rebuild the user-space tool set?\n"); + } + + unlock_pages (op, sizeof (*op)); + +out1: + return ret; +} +#endif + +static volatile int ctrl_c_hit; + +void ctrl_c_handler (int sig) +{ + ctrl_c_hit = 1; +} + +int wait_domain (int vcpu, vcpu_guest_context_t *ctx) +{ + struct timespec ts; + xc_dominfo_t dominfo; + int ret; + int cnt = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 10*1000*1000; + + ret = xc_domain_unpause (xc_handle, domid); + if (ret < 0) + perror ("xc_domain_unpause"); + + ctrl_c_hit = 0; + + while (1) { + ret = xc_domain_getinfo (xc_handle, domid, 1, &dominfo); + if (ret < 0) + perror ("xc_domain_getinfo"); + + if (dominfo.paused) + break; + + if (ctrl_c_hit) { + fflush (stdout); + /* Force pause. */ + ret = xc_domain_pause (xc_handle, domid); + if (ret < 0) + perror ("xc_domain_pause"); + + break; + } + + printf ("%c\b", "/-\\|"[(cnt++) % 4]); + fflush (stdout); + nanosleep (&ts, NULL); + } + return xc_ia64_vcpu_getcontext (xc_handle, domid, vcpu, ctx); +} + +int virt_to_phys (int is_inst, unsigned long vaddr, unsigned long *paddr) +{ + struct vcpu_tr_regs *trs = &cur_ctx->regs.tr; + struct ia64_tr_entry *tr; + int i; + int num; + + /* Search in tr. */ + if (is_inst) { + tr = trs->itrs; + num = sizeof (trs->itrs) / sizeof (trs->itrs[0]); + } + else { + tr = trs->dtrs; + num = sizeof (trs->dtrs) / sizeof (trs->dtrs[0]); + } + for (i = 0; i < num; i++, tr++) { + int ps_val = (tr->itir >> ITIR_PS_SHIFT) & ITIR_PS_MASK; + unsigned long ps_mask = (-1L) << ps_val; + + if ((tr->vadr & ps_mask) == (vaddr & ps_mask)) { + *paddr = ((tr->pte & (PTE_PPN_MASK << PTE_PPN_SHIFT)) & ps_mask) | + (vaddr & ~ps_mask); + return 0; + } + } + return -1; +} + +unsigned long * +get_reg_addr (const char *name) +{ + if (strcmp (name, "ip") == 0) + return &cur_ctx->regs.ip; + else if (strcmp (name, "psr") == 0) + return &cur_ctx->regs.psr; + else if (strcmp (name, "iip") == 0) + return &cur_ctx->regs.cr.iip; + else if (strcmp (name, "b0") == 0) + return &cur_ctx->regs.b[0]; + else + return 0; +} + +enum prio_expr {EXPR_BASE, EXPR_SUM, EXPR_LOGIC, EXPR_PROD}; + +int parse_expr (char **buf, unsigned long *res, enum prio_expr prio); + +int next_char (char **buf) +{ + char *b; + + b = *buf; + while (isspace ((unsigned char)*b)) + b++; + *buf = b; + return *b; +} + +int parse_unary (char **buf, unsigned long *res) +{ + char c; + + c = next_char (buf); + switch (c) { + case '0' ... '9': + { + char *e; + *res = strtoul (*buf, &e, 0); + if (e == *buf) { + printf ("bad literal\n"); + return -1; + } + *buf = e; + } + break; + case '+': + (*buf)++; + return parse_unary (buf, res); + case '$': + { + char *b; + char *e; + char c; + unsigned long *reg; + int len; + + b = *buf; + e = b + 1; + + while ((*e >= 'a' && *e <= 'z') || + (*e >= 'A' && *e <= 'Z') || + (*e >= '0' && *e <= '9') || + (*e == '_' || *e == '.')) + e++; + + if (b == e) { + printf ("identifier missing after '$'\n"); + return -1; + } + + b++; + len = e - b; + + c = b[len]; + b[len] = 0; + reg = get_reg_addr (b); + b[len] = c; + + if (reg != NULL) + *res = *reg; + else if (strncmp (b, "d2p", len) == 0 || + strncmp (b, "i2p", len) == 0) { + unsigned long vaddr; + + *buf = e; + if (parse_unary (buf, &vaddr) != 0) + return -1; + if (virt_to_phys (*b == 'i', vaddr, res) != 0) { + printf ("cannot find vaddr %016lx in tr\n", vaddr); + return -1; + } + return 0; + } + else { + printf ("unknown symbol\n"); + return -1; + } + *buf = e; + } + break; + case '(': + (*buf)++; + if (parse_expr (buf, res, EXPR_BASE) != 0) + return -1; + + if (next_char (buf) != ')') { + printf ("missing ')'\n"); + return -1; + } + else + (*buf)++; + break; + default: + printf ("unknown operand '%c' in expression\n", c); + return -1; + } + + return 0; +} + +int parse_expr (char **buf, unsigned long *res, enum prio_expr prio) +{ + unsigned long val = 0; + unsigned long val1; + char c; + + if (parse_unary (buf, &val) != 0) + return -1; + + while (1) { + c = next_char (buf); + switch (c) { + case '+': + case '-': + if (prio > EXPR_SUM) + return 0; + (*buf)++; + if (parse_expr (buf, &val1, EXPR_SUM) < 0) + return -1; + if (c == '+') + val += val1; + else + val -= val1; + break; + case '*': + if (prio > EXPR_PROD) + return 0; + + (*buf)++; + if (parse_expr (buf, &val1, EXPR_SUM) < 0) + return -1; + + val *= val1; + break; + default: + *res = val; + return 0; + } + } +} + +char *parse_arg (char **buf) +{ + char *res; + char *b = *buf; + + /* Eat leading spaces. */ + while (isspace ((unsigned char)*b)) + b++; + + res = b; + while (*b && !isspace ((unsigned char)*b)) + b++; + + /* Set the NUL terminator. */ + if (*b) + *b++ = 0; + + *buf = b; + return res; +} + +vcpu_guest_context_any_t vcpu_ctx_any[MAX_VIRT_CPUS]; + +int vcpu_setcontext (int vcpu) +{ + int ret; + + ret = xc_vcpu_setcontext (xc_handle, domid, vcpu, &vcpu_ctx_any[vcpu]); + if (ret < 0) + perror ("xc_vcpu_setcontext"); + + return ret; +} + +enum cmd_status { CMD_ERROR, CMD_OK, CMD_REPEAT, CMD_QUIT }; + +struct command_desc +{ + const char *name; + const char *help; + enum cmd_status (*cmd)(char *line); +}; + +static enum cmd_status +cmd_registers (char *line) +{ + print_ctx (cur_ctx); + return CMD_OK; +} + +static enum cmd_status +cmd_sstep (char *line) +{ + /* Set psr.dd and psr.id to skip over current breakpoint. */ + cur_ctx->regs.psr |= PSR_SS | PSR_DD | PSR_ID; + cur_ctx->regs.psr &= ~PSR_TB; + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + + if (wait_domain (cur_vcpu, cur_ctx) < 0) { + perror ("wait_domain"); + return CMD_ERROR; + } + + print_ctx (cur_ctx); + + return CMD_REPEAT; +} + +static enum cmd_status +cmd_go (char *line) +{ + unsigned long n = 1; + + if (*line != 0) { + if (parse_expr (&line, &n, 0) < 0) + return CMD_ERROR; + } + while (n > 0) { + /* Set psr.dd and psr.id to skip over current breakpoint. */ + if ((cur_ctx->regs.psr & (PSR_SS | PSR_TB | PSR_DB)) != 0) { + cur_ctx->regs.psr &= ~(PSR_SS | PSR_TB); + cur_ctx->regs.psr |= PSR_DD | PSR_ID; + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + } + + if (wait_domain (cur_vcpu, cur_ctx) < 0) { + perror ("wait_domain"); + return CMD_ERROR; + } + print_ctx (cur_ctx); + n--; + } + + return CMD_REPEAT; +} + +static enum cmd_status +cmd_cb (char *line) +{ + if ((cur_ctx->regs.psr & (PSR_SS | PSR_TB)) != PSR_TB) { + cur_ctx->regs.psr &= ~PSR_SS; + cur_ctx->regs.psr |= PSR_TB; + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + } + + if (wait_domain (cur_vcpu, cur_ctx) < 0) { + perror ("wait_domain"); + return CMD_ERROR; + } + + print_ctx (cur_ctx); + + return CMD_REPEAT; +} + +static int quit_paused; + +static enum cmd_status +cmd_quit (char *line) +{ + if (!strcmp (line, "paused")) + quit_paused = 1; + return CMD_QUIT; +} + +static enum cmd_status +cmd_echo (char *line) +{ + printf ("%s", line); + return CMD_OK; +} + +static enum cmd_status +cmd_disassemble (char *args) +{ + static unsigned long addr; + unsigned long end_addr = addr + 16; + + if (*args != 0) { + if (parse_expr (&args, &addr, 0) < 0) + return CMD_ERROR; + if (*args != 0) { + if (parse_expr (&args, &end_addr, 0) < 0) + return CMD_ERROR; + } + else + end_addr = addr + 16; + } + target_disas (stdout, addr, end_addr - addr); + addr = end_addr; + return CMD_REPEAT; +} + +static enum cmd_status +cmd_dump (char *args) +{ + static unsigned long addr; + unsigned long end_addr = addr + 256; + unsigned long p; + + if (*args != 0) { + if (parse_expr (&args, &addr, 0) < 0) + return CMD_ERROR; + if (*args != 0) { + if (parse_expr (&args, &end_addr, 0) < 0) + return CMD_ERROR; + } + else + end_addr = addr + 256; + } + for (p = addr; p < end_addr; p += 16) { + int i; + printf ("%016lx:", p); + for (i = 0; i < 16; i++) { + unsigned char *m = target_map_memory (p + i); + printf ("%c%02x", i == 8 ? '-' : ' ', *m); + } + printf ("\n"); + } + addr = end_addr; + return CMD_REPEAT; +} + +static enum cmd_status +cmd_break (char *args) +{ + unsigned long addr; + int i; + + for (i = 0; i < 4; i++) + if (cur_ctx->regs.ibr[2 * i] == 0 && cur_ctx->regs.ibr[2 * i + 1] == 0) + break; + + if (i == 4) { + printf ("no availabe break points\n"); + return CMD_ERROR; + } + + if (parse_expr (&args, &addr, 0) < 0) + return CMD_ERROR; + + cur_ctx->regs.ibr[2 * i] = addr; + cur_ctx->regs.ibr[2 * i + 1] = 0x87fffffffffffff0UL; + cur_ctx->regs.psr |= PSR_DB; + + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else + return CMD_OK; +} + +static enum cmd_status +cmd_watch (char *args) +{ + unsigned long addr; + unsigned long mask; + int i; + + for (i = 0; i < 4; i++) + if (cur_ctx->regs.dbr[2 * i] == 0 && cur_ctx->regs.dbr[2 * i + 1] == 0) + break; + + if (i == 4) { + printf ("no availabe watch points\n"); + return CMD_ERROR; + } + + if (parse_expr (&args, &addr, 0) < 0) + return CMD_ERROR; + + if (*args == 0) + mask = 3; + else if (parse_expr (&args, &mask, 0) < 0) + return CMD_ERROR; + + cur_ctx->regs.dbr[2 * i] = addr; + cur_ctx->regs.dbr[2 * i + 1] = ~((1UL << mask) - 1) | (0xc7UL << 56); + cur_ctx->regs.psr |= PSR_DB; + + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else { + printf ("Watchpoint %d set\n", i); + return CMD_OK; + } +} + +static enum cmd_status +cmd_delete (char *args) +{ + unsigned long num; + + if (parse_expr (&args, &num, 0) < 0) + return CMD_ERROR; + + if (num < 4) { + cur_ctx->regs.ibr[2 * num] = 0; + cur_ctx->regs.ibr[2 * num + 1] = 0; + } + else if (num < 8) { + num -= 4; + cur_ctx->regs.dbr[2 * num] = 0; + cur_ctx->regs.dbr[2 * num + 1] = 0; + } + else { + printf ("breakpoint out of range\n"); + return CMD_ERROR; + } + + cur_ctx->regs.psr |= PSR_DB; + + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else + return CMD_OK; +} + +static enum cmd_status +cmd_disable (char *args) +{ + unsigned long num; + + if (parse_expr (&args, &num, 0) < 0) + return CMD_ERROR; + + if (num >= 4) { + printf ("breakpoint out of range\n"); + return CMD_ERROR; + } + + cur_ctx->regs.ibr[2 * num + 1] &= ~(1UL << 63); + + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else + return CMD_OK; +} + +static enum cmd_status +cmd_enable (char *args) +{ + unsigned long num; + + if (parse_expr (&args, &num, 0) < 0) + return CMD_ERROR; + + if (num >= 4) { + printf ("breakpoint out of range\n"); + return CMD_ERROR; + } + + cur_ctx->regs.ibr[2 * num + 1] |= 1UL << 63; + + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else + return CMD_OK; +} + +static enum cmd_status +cmd_print (char *args) +{ + unsigned long addr; + + if (parse_expr (&args, &addr, 0) < 0) + return CMD_ERROR; + + printf ("res: 0x%016lx = %ld\n", addr, addr); + + return CMD_OK; +} + +struct bit_xlat { + unsigned int bit; + const char *name; +}; + +static const struct bit_xlat debug_flags[] = { + { XEN_IA64_DEBUG_ON_KERN_SSTEP, "sstep" }, + { XEN_IA64_DEBUG_ON_KERN_DEBUG, "debug" }, + { XEN_IA64_DEBUG_ON_KERN_TBRANCH, "tbranch" }, + { XEN_IA64_DEBUG_ON_EXTINT, "extint" }, + { XEN_IA64_DEBUG_ON_EXCEPT, "except" }, + { XEN_IA64_DEBUG_ON_EVENT, "event" }, + { XEN_IA64_DEBUG_ON_PRIVOP, "privop" }, + { XEN_IA64_DEBUG_ON_PAL, "pal" }, + { XEN_IA64_DEBUG_ON_SAL, "sal" }, + { XEN_IA64_DEBUG_ON_EFI, "efi" }, + { XEN_IA64_DEBUG_ON_RFI, "rfi" }, + { XEN_IA64_DEBUG_ON_MMU, "mmu" }, + { XEN_IA64_DEBUG_ON_BAD_MPA, "mpa" }, + { XEN_IA64_DEBUG_FORCE_SS, "ss" }, + { XEN_IA64_DEBUG_FORCE_DB, "db" }, + { XEN_IA64_DEBUG_ON_TR, "tr" }, + { XEN_IA64_DEBUG_ON_TC, "tc" }, +#if 0 + { XEN_IA64_DEBUG_ON_KEYS, "keys" }, + { XEN_IA64_DEBUG_ON_MOV_TO_CR, "mov_to_cr" }, + { XEN_IA64_DEBUG_ON_VHPT, "vhpt" }, + { XEN_IA64_DEBUG_ON_IOSAPIC, "iosapic" }, +#endif + { 0, NULL } +}; + +static enum cmd_status +cmd_disp (char *arg) +{ + if (strcmp (arg, "br") == 0) + print_br (cur_ctx); + else if (strcmp (arg, "regs") == 0) + print_regs (cur_ctx); + else if (strcmp (arg, "cr") == 0) + print_cr (cur_ctx); + else if (strcmp (arg, "ar") == 0) + print_ar (cur_ctx); + else if (strcmp (arg, "tr") == 0) + print_tr (cur_ctx); + else if (strcmp (arg, "rr") == 0) + print_rr (cur_ctx); + else if (strcmp (arg, "db") == 0) + print_db (cur_ctx); + else if (strcmp (arg, "psr") == 0) { + printf ("psr:"); + print_bits (psr_bits, cur_ctx->regs.psr); + printf ("\n"); + } + else if (strcmp (arg, "ipsr") == 0) { + printf ("ipsr:"); + print_bits (psr_bits, cur_ctx->regs.cr.ipsr); + printf ("\n"); + } + else if (strcmp (arg, "break") == 0) { + int i; + + for (i = 0; i < 4; i++) + if (cur_ctx->regs.ibr[2 * i + 1]) + printf ("%d: 0x%016lx %s\n", i, cur_ctx->regs.ibr[2 * i], + (cur_ctx->regs.ibr[2 * i + 1] & (1UL << 63)) ? + "enabled" : "disabled"); + for (i = 0; i < 4; i++) + if (cur_ctx->regs.dbr[2 * i + 1]) + printf ("%d: 0x%016lx %s\n", i, cur_ctx->regs.dbr[2 * i], + (cur_ctx->regs.dbr[2 * i + 1] & (1UL << 63)) ? + "enabled" : "disabled"); + } + else if (strcmp (arg, "domain") == 0) { + xc_dominfo_t dominfo; +#ifdef HAVE_DEBUG_OP + xen_ia64_debug_op_t debug_op; + int i; +#endif + if (xc_domain_getinfo (xc_handle, domid, 1, &dominfo) < 0) { + perror ("xc_domain_getinfo"); + return 0; + } + + printf ("id=%d nr_pages=%lu shared_info_frame=%lu max_mem=%luKB\n", + dominfo.domid, dominfo.nr_pages, dominfo.shared_info_frame, + dominfo.max_memkb); + printf (" nr_online_vcpu=%u max_vcpu_id=%u\n", + dominfo.nr_online_vcpus, dominfo.max_vcpu_id); + printf (" status:"); + if (dominfo.dying) + printf (" dying"); + if (dominfo.crashed) + printf (" crashed"); + if (dominfo.shutdown) + printf (" shutdown(%u)", dominfo.shutdown_reason); + if (dominfo.paused) + printf (" paused"); + if (dominfo.blocked) + printf (" blocked"); + if (dominfo.running) + printf (" running"); + if (dominfo.hvm) + printf (" hvm"); + if (dominfo.debugged) + printf (" debug"); + printf ("\n"); + +#ifdef HAVE_DEBUG_OP + if (do_ia64_debug_op (xc_handle, XEN_IA64_DEBUG_OP_GET_FLAGS, + domid, &debug_op) < 0) { + perror ("xc_domain_getinfo"); + return 0; + } + printf ("debug flags: %08lx: ", debug_op.flags); + for (i = 0; debug_flags[i].name; i++) + if (debug_flags[i].bit & debug_op.flags) + printf (" %s", debug_flags[i].name); + printf ("\n"); +#endif + } + else if (*arg == 0) + printf ("choose among br, regs, cr, ar, tr, rr, db\n"); + else { + printf ("cannot disp '%s'\n", arg); + return CMD_ERROR; + } + return CMD_OK; +} + +static enum cmd_status +cmd_bev (char *arg) +{ + xen_ia64_debug_op_t debug_op; + int i; + + if (do_ia64_debug_op (xc_handle, XEN_IA64_DEBUG_OP_GET_FLAGS, + domid, &debug_op) < 0) { + perror ("get debug flags"); + return CMD_ERROR; + } + if (arg == NULL || arg[0] == 0) { + printf ("debug flags: %08lx:\n", debug_op.flags); + for (i = 0; debug_flags[i].name; i++) + printf (" %c%s\n", + (debug_flags[i].bit & debug_op.flags) ? '+' : '-', + debug_flags[i].name); + return CMD_OK; + } + else { + char *p = strtok ((char *)arg, " "); + + while (p != NULL) { + unsigned int flag = 0; + + for (i = 0; debug_flags[i].name; i++) + if (strcmp (p, debug_flags[i].name) == 0 + || ((p[0] == '-' || p[0] == '+') + && strcmp (p + 1, debug_flags[i].name) == 0)) { + flag = debug_flags[i].bit; + break; + } + if (flag == 0) { + printf ("unknown event %s\n", p); + return CMD_ERROR; + } + if (p[0] == '-') + debug_op.flags &= ~flag; + else + debug_op.flags |= flag; + + p = strtok (NULL, " "); + } + if (do_ia64_debug_op (xc_handle, XEN_IA64_DEBUG_OP_SET_FLAGS, + domid, &debug_op) < 0) { + perror ("set debug flags"); + return CMD_ERROR; + } + /* Disabling force_SS and force_DB requires setting psr. */ + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else + return CMD_OK; + } +} + +static enum cmd_status +cmd_set (char *line) +{ + char *reg; + unsigned long *addr; + unsigned long val; + + reg = parse_arg (&line); + + addr = get_reg_addr (reg); + if (addr == NULL) { + printf ("unknown register %s\n", reg); + return CMD_ERROR; + } + + if (parse_expr (&line, &val, 0) < 0) + return CMD_ERROR; + + *addr = val; + + if (vcpu_setcontext (cur_vcpu) < 0) + return CMD_ERROR; + else + return CMD_OK; +} + +const struct command_desc commands[]; + +static enum cmd_status +cmd_help (char *line) +{ + int i; + + for (i = 0; commands[i].name; i++) + printf ("%s -- %s\n", commands[i].name, commands[i].help); + + return CMD_OK; +} + +const struct command_desc commands[] = { + { "registers", "display current registers", cmd_registers }, + { "sstep", "single step", cmd_sstep }, + { "go", "resume execution", cmd_go }, + { "quit", "quit debugger", cmd_quit }, + { "echo", "display parameters", cmd_echo }, + { "disassemble", "disassemble memory", cmd_disassemble }, + { "dump", "dump memory", cmd_dump }, + { "break", "set a break point", cmd_break }, + { "watch", "set a watch point", cmd_watch }, + { "cb", "resume until branch", cmd_cb }, + { "delete", "delete a break point", cmd_delete }, + { "disable", "disable a break point", cmd_disable }, + { "enable", "enable a break point", cmd_enable }, + { "print", "print an expression", cmd_print }, + { "disp", "disp br/regs/cr/ar/tr/rr/db/psr/break/domain", cmd_disp}, + { "bev", "break on event", cmd_bev}, + { "set", "set reg val", cmd_set}, + { "help", "disp help", cmd_help }, + { NULL, NULL, NULL } +}; + + +enum cmd_status do_command (int vcpu, char *line) +{ + char *cmd; + char *args; + int i; + const struct command_desc *desc; + static const struct command_desc *last_desc; + enum cmd_status status; + int flag_ambiguous; + + cur_vcpu = vcpu; + cur_ctx = &vcpu_ctx_any[vcpu].c; + + /* Handle repeat last-command. */ + if (*line == 0) { + if (last_desc != NULL) + return (*last_desc->cmd)(""); + else + return CMD_OK; + } + last_desc = NULL; + + cmd = parse_arg (&line); + args = line; + + desc = NULL; + flag_ambiguous = 0; + + for (i = 0; commands[i].name; i++) { + const char *n = commands[i].name; + char *c = cmd; + + while (*n == *c && *n) + n++, c++; + + if (*c == 0) { + /* Match. */ + if (desc != NULL) { + if (!flag_ambiguous) + printf ("ambiguous command: %s", desc->name); + printf (", %s", commands[i].name); + flag_ambiguous = 1; + } + else + desc = &commands[i]; + } + } + + if (flag_ambiguous) { + printf ("\n"); + return CMD_ERROR; + } + else if (!desc) { + printf ("command not found, try help\n"); + return CMD_ERROR; + } + + status = (*desc->cmd)(args); + if (status == CMD_REPEAT) + last_desc = desc; + return status; +} + +void xenitp (int vcpu) +{ + int ret; + struct sigaction sa; + + cur_ctx = &vcpu_ctx_any[vcpu].c; + + xc_handle = xc_interface_open (); /* for accessing control interface */ + + if (xc_domain_setdebugging (xc_handle, domid, 1) != 0) + perror ("setdebugging"); + + ret = xc_domain_pause (xc_handle, domid); + if (ret < 0) { + perror ("xc_domain_pause"); + exit (-1); + } + + ret = xc_ia64_vcpu_getcontext (xc_handle, domid, vcpu, cur_ctx); + if (ret < 0) { + perror ("xc_ia64_vcpu_getcontext"); + exit (-1); + } + + print_ctx (cur_ctx); + + /* Catch ctrl-c. */ + sa.sa_handler = &ctrl_c_handler; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + if (sigaction (SIGINT, &sa, NULL) != 0) + perror ("sigaction"); + + while (1) { + char buf[128]; + int len; + + printf ("XenITP> "); + fflush (stdout); + + if (fgets (buf, sizeof (buf), stdin) == NULL) + break; + + len = strlen ((char *)buf); + if (len >= 1 && buf[len - 1] == '\n') + buf[len - 1] = 0; + + ret = do_command (vcpu, buf); + if (ret == CMD_QUIT) + break; + } + + /* Clear debug bits. */ + if ((cur_ctx->regs.psr & (PSR_SS | PSR_TB | PSR_DB)) != 0) { + cur_ctx->regs.psr &= ~(PSR_SS | PSR_TB | PSR_DB); + cur_ctx->regs.psr |= PSR_DD | PSR_ID; + vcpu_setcontext (cur_vcpu); + } + + /* Disable debugging. */ + if (xc_domain_setdebugging (xc_handle, domid, 0) != 0) + perror ("setdebugging"); + + if (!quit_paused) { + ret = xc_domain_unpause (xc_handle, domid); + if (ret < 0) { + perror ("xc_domain_unpause"); + exit (-1); + } + } + + xc_interface_close (xc_handle); + if (ret < 0) { + perror ("xc_interface_close"); + exit (-1); + } +} + +static void usage (void) +{ + printf ("usage:\n"); + printf (" xenitp [VCPU]\n"); + +} + +int main (int argc, char **argv) +{ + int ch; + static const char *sopts = "h" + ; + static const struct option lopts[] = { + {"help", 0, NULL, 'h'}, + {0, 0, 0, 0} + }; + int vcpu = 0; + + while ((ch = getopt_long (argc, argv, sopts, lopts, NULL)) != -1) { + switch (ch) { + case 'h': + usage (); + exit (-1); + case '?': + fprintf (stderr, "%s --help for more options\n", argv[0]); + exit (-1); + } + } + + argv += optind; + argc -= optind; + + if (argc < 1 || argc > 2) { + usage (); + exit (-1); + } + + domid = atoi (argv[0]); + if (domid == 0) { + fprintf (stderr, "cannot trace dom0\n"); + exit (-1); + } + + if (argc == 2) + vcpu = atoi (argv[1]); + + xenitp (vcpu); + + return 0; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/examples/Makefile b/tools/examples/Makefile new file mode 100644 index 0000000..3931039 --- /dev/null +++ b/tools/examples/Makefile @@ -0,0 +1,132 @@ +XEN_ROOT = ../../ +include $(XEN_ROOT)/tools/Rules.mk + +# Init scripts. +XEND_INITD = init.d/xend +XENDOMAINS_INITD = init.d/xendomains +XENDOMAINS_SYSCONFIG = init.d/sysconfig.xendomains + +# Xen configuration dir and configs to go there. +XEN_CONFIG_DIR = /etc/xen +XEN_READMES = README +XEN_READMES += README.incompatibilities +XEN_CONFIGS = xend-config.sxp +XEN_CONFIGS += xm-config.xml +XEN_CONFIGS += xmexample1 +XEN_CONFIGS += xmexample2 +XEN_CONFIGS += xmexample3 +XEN_CONFIGS += xmexample.hvm +XEN_CONFIGS += xmexample.hvm-stubdom +XEN_CONFIGS += xmexample.hvm-dm +XEN_CONFIGS += xmexample.pv-grub +XEN_CONFIGS += xmexample.nbd +XEN_CONFIGS += xmexample.vti +XEN_CONFIGS += xend-pci-quirks.sxp +XEN_CONFIGS += xend-pci-permissive.sxp + +# Xen script dir and scripts to go there. +XEN_SCRIPT_DIR = /etc/xen/scripts +XEN_SCRIPTS = network-bridge vif-bridge +XEN_SCRIPTS += network-route vif-route +XEN_SCRIPTS += network-nat vif-nat +XEN_SCRIPTS += block +XEN_SCRIPTS += block-enbd block-nbd +XEN_SCRIPTS += blktap +XEN_SCRIPTS += vtpm vtpm-delete +XEN_SCRIPTS += xen-hotplug-cleanup +XEN_SCRIPTS += external-device-migrate +XEN_SCRIPTS += vscsi +XEN_SCRIPT_DATA = xen-script-common.sh locking.sh logging.sh +XEN_SCRIPT_DATA += xen-hotplug-common.sh xen-network-common.sh vif-common.sh +XEN_SCRIPT_DATA += block-common.sh vtpm-common.sh vtpm-hotplug-common.sh +XEN_SCRIPT_DATA += vtpm-migration.sh vtpm-impl + +XEN_HOTPLUG_DIR = /etc/hotplug +XEN_HOTPLUG_SCRIPTS = xen-backend.agent + +UDEV_RULES_DIR = /etc/udev +UDEV_RULES = xen-backend.rules + +DI = $(if $(DISTDIR),$(shell readlink -f $(DISTDIR)),) +DE = $(if $(DESTDIR),$(shell readlink -f $(DESTDIR)),) +ifeq ($(findstring $(DI),$(DE)),$(DI)) +HOTPLUGS=install-hotplug install-udev +else +ifeq ($(shell [ -x /usr/bin/udevinfo ] && [ `/usr/bin/udevinfo -V | sed -e 's/^[^0-9]* \([0-9]\{1,\}\)[^0-9]\{0,\}/\1/'` -ge 059 ] && echo 1),1) +HOTPLUGS=install-udev +else +HOTPLUGS=install-hotplug +endif +endif + +.PHONY: all +all: + +.PHONY: build +build: + +.PHONY: install +install: all install-readmes install-initd install-configs install-scripts $(HOTPLUGS) + +.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-initd +install-initd: + [ -d $(DESTDIR)/etc/init.d ] || $(INSTALL_DIR) $(DESTDIR)/etc/init.d + [ -d $(DESTDIR)/etc/sysconfig ] || $(INSTALL_DIR) $(DESTDIR)/etc/sysconfig + $(INSTALL_PROG) $(XEND_INITD) $(DESTDIR)/etc/init.d + $(INSTALL_PROG) $(XENDOMAINS_INITD) $(DESTDIR)/etc/init.d + $(INSTALL_PROG) $(XENDOMAINS_SYSCONFIG) $(DESTDIR)/etc/sysconfig/xendomains + +.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: install-scripts +install-scripts: + [ -d $(DESTDIR)$(XEN_SCRIPT_DIR) ] || \ + $(INSTALL_DIR) $(DESTDIR)$(XEN_SCRIPT_DIR) + set -e; for i in $(XEN_SCRIPTS); \ + do \ + $(INSTALL_PROG) $$i $(DESTDIR)$(XEN_SCRIPT_DIR); \ + done + set -e; for i in $(XEN_SCRIPT_DATA); \ + do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(XEN_SCRIPT_DIR); \ + done + +.PHONY: install-hotplug +install-hotplug: + [ -d $(DESTDIR)$(XEN_HOTPLUG_DIR) ] || \ + $(INSTALL_DIR) $(DESTDIR)$(XEN_HOTPLUG_DIR) + set -e; for i in $(XEN_HOTPLUG_SCRIPTS); \ + do \ + $(INSTALL_PROG) $$i $(DESTDIR)$(XEN_HOTPLUG_DIR); \ + done + +.PHONY: install-udev +install-udev: + [ -d $(DESTDIR)$(UDEV_RULES_DIR) ] || \ + $(INSTALL_DIR) $(DESTDIR)$(UDEV_RULES_DIR)/rules.d + set -e; for i in $(UDEV_RULES); \ + do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(UDEV_RULES_DIR); \ + ln -sf ../$$i $(DESTDIR)$(UDEV_RULES_DIR)/rules.d; \ + done + +.PHONY: clean +clean: diff --git a/tools/examples/README b/tools/examples/README new file mode 100644 index 0000000..e2e8f43 --- /dev/null +++ b/tools/examples/README @@ -0,0 +1,51 @@ +Xen 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 +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 +network-bridge - xen network start/stop script when using bridging +network-nat - xen network start/stop script when using NAT +network-route - xen network start/stop script when using routing +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 +vtpm - called by xen-backend.agent to bind/unbind vTPM devices +vtpm-common.sh - common code for vTPM handling +vtpm-delete - remove an entry from the vTPM table given the + domain's name +vtpm-hotplug-common.sh - sourced by vtpm +vtpm-migration.sh - sourced by external-device-migrate +xen-backend.agent - calls block, vif-*, vtpm scripts to add, remove, hotplug + devices +xen-backend.rules - hotplug script rules +xend-config.sxp - default xend configuration file +xen-hotplug-common.sh - sourced by vif-common.sh +xen-network-common.sh - sourced by vif-common.sh +xen-script-common.sh - sourced by network-bridge, 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 diff --git a/tools/examples/README.incompatibilities b/tools/examples/README.incompatibilities new file mode 100644 index 0000000..bb067bd --- /dev/null +++ b/tools/examples/README.incompatibilities @@ -0,0 +1,38 @@ +Command 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. diff --git a/tools/examples/blktap b/tools/examples/blktap new file mode 100644 index 0000000..01a0f6c --- /dev/null +++ b/tools/examples/blktap @@ -0,0 +1,93 @@ +#!/bin/bash + +# Copyright (c) 2005, XenSource Ltd. + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" +. "$dir/block-common.sh" + +findCommand "$@" + +## +# check_blktap_sharing file mode +# +# Perform the sharing check for the given blktap and mode. +# +check_blktap_sharing() +{ + local file="$1" + local mode="$2" + + local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE" + for dom in $(xenstore-list "$base_path") + do + for dev in $(xenstore-list "$base_path/$dom") + do + params=$(xenstore_read "$base_path/$dom/$dev/params" | cut -d: -f2) + if [ "$file" = "$params" ] + then + + if [ "$mode" = 'w' ] + then + if ! same_vm "$dom" + then + echo 'guest' + return + fi + else + local m=$(xenstore_read "$base_path/$dom/$dev/mode") + m=$(canonicalise_mode "$m") + + if [ "$m" = 'w' ] + then + if ! same_vm "$dom" + then + echo 'guest' + return + fi + fi + fi + fi + done + done + + echo 'ok' +} + + +t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING') +if [ -n "$t" ] +then + p=$(xenstore_read "$XENBUS_PATH/params") + # if we have a ':', chew from head including : + if echo $p | grep -q \: + then + p=${p#*:} + fi +fi +# some versions of readlink cannot be passed a regular file +if [ -L "$p" ]; then + file=$(readlink -f "$p") || fatal "$p link does not exist." +else + file="$p" +fi + +if [ "$command" = 'add' ] +then + [ -e "$file" ] || { fatal $file does not exist; } + + FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id") + FRONTEND_UUID=$(xenstore_read "/local/domain/$FRONTEND_ID/vm") + mode=$(xenstore_read "$XENBUS_PATH/mode") + mode=$(canonicalise_mode "$mode") + + if [ "$mode" != '!' ] + then + result=$(check_blktap_sharing "$file" "$mode") + [ "$result" = 'ok' ] || ebusy "$file already in use by other domain" + fi + + success +fi + +exit 0 diff --git a/tools/examples/block b/tools/examples/block new file mode 100644 index 0000000..8c61744 --- /dev/null +++ b/tools/examples/block @@ -0,0 +1,381 @@ +#!/bin/bash + +dir=$(dirname "$0") +. "$dir/block-common.sh" + +expand_dev() { + local dev + case $1 in + /*) + dev=$1 + ;; + *) + dev=/dev/$1 + ;; + esac + echo -n $dev +} + + +## +# check_sharing device mode +# +# Check whether the device requested is already in use. To use the device in +# read-only mode, it may be in use in read-only mode, but may not be in use in +# read-write anywhere at all. To use the device in read-write mode, it must +# not be in use anywhere at all. +# +# Prints one of +# +# 'local': the device may not be used because it is mounted in the current +# (i.e. the privileged domain) in a way incompatible with the +# requested mode; +# 'guest': the device may not be used because it already mounted by a guest +# in a way incompatible with the requested mode; or +# 'ok': the device may be used. +# +check_sharing() +{ + local dev="$1" + local mode="$2" + + local devmm=$(device_major_minor "$dev") + local file + + if [ "$mode" = 'w' ] + then + toskip="^$" + else + toskip="^[^ ]* [^ ]* [^ ]* ro[, ]" + fi + + for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ') + do + if [ -e "$file" ] + then + local d=$(device_major_minor "$file") + + if [ "$d" = "$devmm" ] + then + echo 'local' + return + fi + fi + done + + local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE" + for dom in $(xenstore-list "$base_path") + do + for dev in $(xenstore-list "$base_path/$dom") + do + d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "") + + if [ "$d" = "$devmm" ] + then + if [ "$mode" = 'w' ] + then + if ! same_vm $dom + then + echo 'guest' + return + fi + else + local m=$(xenstore_read "$base_path/$dom/$dev/mode") + m=$(canonicalise_mode "$m") + + if [ "$m" = 'w' ] + then + if ! same_vm $dom + then + echo 'guest' + return + fi + fi + fi + fi + done + done + + echo 'ok' +} + + +## +# check_device_sharing dev mode +# +# Perform the sharing check for the given physical device and mode. +# +check_device_sharing() +{ + local dev="$1" + local mode=$(canonicalise_mode "$2") + local result + + if [ "x$mode" = 'x!' ] + then + return 0 + fi + + result=$(check_sharing "$dev" "$mode") + + if [ "$result" != 'ok' ] + then + do_ebusy "Device $dev is mounted " "$mode" "$result" + fi +} + + +## +# check_device_sharing file dev mode +# +# Perform the sharing check for the given file mounted through the given +# loopback interface, in the given mode. +# +check_file_sharing() +{ + local file="$1" + local dev="$2" + local mode="$3" + + result=$(check_sharing "$dev" "$mode") + + if [ "$result" != 'ok' ] + then + do_ebusy "File $file is loopback-mounted through $dev, +which is mounted " "$mode" "$result" + fi +} + + +## +# do_ebusy prefix mode result +# +# Helper function for check_device_sharing check_file_sharing, calling ebusy +# with an error message constructed from the given prefix, mode, and result +# from a call to check_sharing. +# +do_ebusy() +{ + local prefix="$1" + local mode="$2" + local result="$3" + + if [ "$result" = 'guest' ] + then + dom='a guest ' + when='now' + else + dom='the privileged ' + when='by a guest' + fi + + if [ "$mode" = 'w' ] + then + m1='' + m2='' + else + m1='read-write ' + m2='read-only ' + fi + + release_lock "block" + ebusy \ +"${prefix}${m1}in ${dom}domain, +and so cannot be mounted ${m2}${when}." +} + + +t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING') + +case "$command" in + add) + phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING') + if [ "$phys" != 'MISSING' ] + then + # Depending upon the hotplug configuration, it is possible for this + # script to be called twice, so just bail. + exit 0 + fi + + if [ -n "$t" ] + then + p=$(xenstore_read "$XENBUS_PATH/params") + mode=$(xenstore_read "$XENBUS_PATH/mode") + fi + + case $t in + phy) + dev=$(expand_dev $p) + FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id") + FRONTEND_UUID=$(xenstore_read_default \ + "/local/domain/$FRONTEND_ID/vm" 'unknown') + + if [ -L "$dev" ] + then + dev=$(readlink -f "$dev") || fatal "$dev link does not exist." + fi + test -e "$dev" || fatal "$dev does not exist." + test -b "$dev" || fatal "$dev is not a block device." + + claim_lock "block" + check_device_sharing "$dev" "$mode" + write_dev "$dev" + release_lock "block" + exit 0 + ;; + + file) + # Canonicalise the file, for sharing check comparison, and the mode + # for ease of use here. + file=$(readlink -f "$p") || fatal "$p does not exist." + test -f "$file" || fatal "$file does not exist." + mode=$(canonicalise_mode "$mode") + + claim_lock "block" + + if [ "$mode" = 'w' ] && ! stat "$file" -c %A | grep -q w + then + release_lock "block" + ebusy \ +"File $file is read-only, and so I will not +mount it read-write in a guest domain." + fi + + loopdev='' + for dev in /dev/loop* + do + if [ ! -b "$dev" ] + then + continue + fi + + f=$(losetup "$dev" 2>/dev/null) || f='' + + if [ "$f" ] + then + # $dev is in use. Check sharing. + if [ "x$mode" = 'x!' ] + then + continue + fi + + f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g') + + # $f is the filename, as read from losetup, but the loopback + # driver truncates filenames at 64 characters, so we need to go + # trawling through the store if it's longer than that. Truncation + # is indicated by an asterisk at the end of the filename. + if expr index "$f" '*' >/dev/null + then + found="" + for dom in $(xenstore-list "$XENBUS_BASE_PATH") + do + for domdev in $(xenstore-list "$XENBUS_BASE_PATH/$dom") + do + d=$(xenstore_read_default \ + "$XENBUS_BASE_PATH/$dom/$domdev/node" "") + if [ "$d" = "$dev" ] + then + f=$(xenstore_read "$XENBUS_BASE_PATH/$dom/$domdev/params") + found=1 + break 2 + fi + done + done + + if [ ! "$found" ] + then + # This loopback device is in use by someone else, so skip it. + log debug "Loopback sharing check skips device $dev." + continue + fi + fi + + # Canonicalise the filename for the comparison. + + # I have seen this readlink fails because the filename given by + # losetup is only the basename. This cannot happen when the loop + # device is set up through this script, because file is + # canonicalised above, but it may happen when loop devices are set + # up some other way. This readlink may also conceivably fail if + # the file backing this loop device has been removed. + + # For maximum safety, in the case that $f does not resolve, we + # assume that $file and $f are in the same directory. + + # If you create a loopback filesystem, remove it and continue to + # run on it, and then create another file with the same name, then + # this check will block that -- don't do that. + + # If you create loop devices through some other mechanism, use + # relative filenames, and then use the same filename through this + # script, then this check will block that -- don't do that either. + + f=$(readlink -f "$f" || echo $(dirname "$file")/$(basename "$f")) + + + if [ "$f" = "$file" ] + then + check_file_sharing "$file" "$dev" "$mode" + fi + else + # $dev is not in use, so we'll remember it for use later; we want + # to finish the sharing check first. + + if [ "$loopdev" = '' ] + then + loopdev="$dev" + fi + fi + done + + if [ "$loopdev" = '' ] + then + release_lock "block" + fatal 'Failed to find an unused loop device' + fi + + if LANG=C losetup -h 2>&1 | grep read-only >/dev/null + then + roflag="-$mode"; roflag="${roflag#-w}"; roflag="${roflag#-!}" + else + roflag='' + fi + do_or_die losetup $roflag "$loopdev" "$file" + xenstore_write "$XENBUS_PATH/node" "$loopdev" + write_dev "$loopdev" + release_lock "block" + exit 0 + ;; + + "") + claim_lock "block" + success + release_lock "block" + ;; + esac + ;; + + remove) + case $t in + phy) + exit 0 + ;; + + file) + node=$(xenstore_read "$XENBUS_PATH/node") + losetup -d "$node" + exit 0 + ;; + + "") + exit 0 + ;; + esac + ;; + +esac + +# If we've reached here, $t is neither phy nor file, so fire a helper script. +[ -x /etc/xen/scripts/block-"$t" ] && \ + /etc/xen/scripts/block-"$t" "$command" $node diff --git a/tools/examples/block-common.sh b/tools/examples/block-common.sh new file mode 100644 index 0000000..a0ebc9b --- /dev/null +++ b/tools/examples/block-common.sh @@ -0,0 +1,116 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" + +findCommand "$@" + +if [ "$command" != "add" ] && + [ "$command" != "remove" ] +then + log err "Invalid command: $command" + exit 1 +fi + + +XENBUS_PATH="${XENBUS_PATH:?}" + + +ebusy() +{ + xenstore_write "$XENBUS_PATH/hotplug-error" "$*" \ + "$XENBUS_PATH/hotplug-status" busy + log err "$@" + exit 1 +} + + +## +# Print the given device's major and minor numbers, written in hex and +# separated by a colon. +device_major_minor() +{ + stat -L -c %t:%T "$1" +} + + +## +# Write physical-device = MM,mm to the store, where MM and mm are the major +# and minor numbers of device respectively. +# +# @param device The device from which major and minor numbers are read, which +# will be written into the store. +# +write_dev() { + local mm + + mm=$(device_major_minor "$1") + + if [ -z $mm ] + then + fatal "Backend device does not exist" + fi + + xenstore_write "$XENBUS_PATH/physical-device" "$mm" + + success +} + + +## +# canonicalise_mode mode +# +# Takes the given mode, which may be r, w, ro, rw, w!, or rw!, or variations +# thereof, and canonicalises them to one of +# +# 'r': perform checks for a new read-only mount; +# 'w': perform checks for a read-write mount; or +# '!': perform no checks at all. +# +canonicalise_mode() +{ + local mode="$1" + + if ! expr index "$mode" 'w' >/dev/null + then + echo 'r' + elif ! expr index "$mode" '!' >/dev/null + then + echo 'w' + else + echo '!' + fi +} + + +same_vm() +{ + local otherdom="$1" + # Note that othervm can be MISSING here, because Xend will be racing with + # the hotplug scripts -- the entries in /local/domain can be removed by + # Xend before the hotplug scripts have removed the entry in + # /local/domain/0/backend/. In this case, we want to pretend that the + # VM is the same as FRONTEND_UUID, because that way the 'sharing' will be + # allowed. + local othervm=$(xenstore_read_default "/local/domain/$otherdom/vm" \ + "$FRONTEND_UUID") + + [ "$FRONTEND_UUID" = "$othervm" ] +} + diff --git a/tools/examples/block-enbd b/tools/examples/block-enbd new file mode 100755 index 0000000..67faa84 --- /dev/null +++ b/tools/examples/block-enbd @@ -0,0 +1,27 @@ +#!/bin/bash + +# Usage: block-enbd [bind server ctl_port |unbind node] +# +# The node argument to unbind is the name of the device node we are to +# unbind. +# +# This assumes you're running a correctly configured server at the other end! + +dir=$(dirname "$0") +. "$dir/block-common.sh" + +case "$command" in + add) + for dev in /dev/nd*; do + if nbd-client $2:$3 $dev; then + write_dev $dev + exit 0 + fi + done + exit 1 + ;; + remove) + nbd-client -d $2 + exit 0 + ;; +esac diff --git a/tools/examples/block-nbd b/tools/examples/block-nbd new file mode 100644 index 0000000..b29b315 --- /dev/null +++ b/tools/examples/block-nbd @@ -0,0 +1,27 @@ +#!/bin/bash + +# Usage: block-nbd [bind server ctl_port |unbind node] +# +# The node argument to unbind is the name of the device node we are to +# unbind. +# +# This assumes you're running a correctly configured server at the other end! + +dir=$(dirname "$0") +. "$dir/block-common.sh" + +case "$command" in + add) + for dev in /dev/nbd*; do + if nbd-client $2 $3 $dev; then + write_dev $dev + exit 0 + fi + done + exit 1 + ;; + remove) + nbd-client -d $2 + exit 0 + ;; +esac diff --git a/tools/examples/bochsrc b/tools/examples/bochsrc new file mode 100644 index 0000000..d80884b --- /dev/null +++ b/tools/examples/bochsrc @@ -0,0 +1,20 @@ +#megs: 32 +#romimage: file=$BXSHARE/BIOS-bochs-latest, address=0xf0000 +#vgaromimage: $BXSHARE/VGABIOS-lgpl-latest +floppya: 1_44=a.img, status=inserted +floppyb: 1_44=b.img, status=inserted +# if you don't use absolute paths below, bochs looks under the cwd of xend, +# which is usually "/" +#ata0-master: type=disk, path=/var/images/min-el3-i386.img, cylinders=800, heads=4, spt=32 +i440fxsupport: enabled=1 +ne2k: ioaddr=0x300, irq=9, mac=b0:c4:22:01:00:00, ethmod=linux, ethdev=eth0 +ata0-master: type=disk, path=/var/images/1g-el3-i386.img, mode=flat, cylinders=2048, heads=16, spt=63 +boot: c + +log: /tmp/bochsout.txt +#debug: action=report +info: action=report +error: action=report +panic: action=ask + +mouse: enabled=0 diff --git a/tools/examples/external-device-migrate b/tools/examples/external-device-migrate new file mode 100644 index 0000000..a411348 --- /dev/null +++ b/tools/examples/external-device-migrate @@ -0,0 +1,98 @@ +#!/bin/bash + +# Copyright (c) 2005 IBM Corporation +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +set -x + +# This script is called by XenD for migration of external devices +# It does not handle the migration of those devices itself, but +# passes the requests on to further applications +# It handles the low-level command line parsing and some of the +# synchronization + +dir=$(dirname "$0") +. "$dir/logging.sh" + + +function ext_dev_migrate_usage() { +cat < : n-th migration step +-host : the destination host +-domname : name of the domain that is migrating +-type : the type of device that is migrating +-subtype : the subtype of the device +-recover : indicates recovery request; an error + occurred during migration +-help : display this help screen +EOF +} + +# Parse the command line paramters. The following parameters must be +# passed as the first ones in the sequence: +# -step [required] +# -host [required] +# -domname [required] +# -type [required] +# -subtype [optional] +# -recover [optional] +# The remaining ones will be passed to the called function. +function evaluate_params() +{ + local step host domname typ recover filename func stype + stype="" + while [ $# -ge 1 ]; do + case "$1" in + -step) step=$2; shift; shift;; + -host) host=$2; shift; shift;; + -domname) domname=$2; shift; shift;; + -type) typ=$2; shift; shift;; + -subtype) stype=$2; shift; shift;; + -recover) recover=1; shift;; + -help) ext_dev_migrate_usage; exit 0;; + *) break;; + esac + done + + if [ "$step" = "" -o \ + "$host" = "" -o \ + "$typ" = "" -o \ + "$domname" = "" ]; then + echo "Error: Parameter(s) missing (-step/-host/-type/-domname)" 1>&2 + echo "" 1>&2 + echo "$0 -help for usage." 1>&2 + exit 1 + fi + + filename="$dir/$typ$stype-migration.sh" + if [ ! -r $filename ]; then + echo "Error: Could not find script '$filename'" + return + fi + . "$filename" + + if [ "$recover" = "1" ]; then + func="$typ"_recover + eval $func $host $domname $step $* + else + func="$typ"_migration_step + eval $func $host $domname $step $* + fi +} + +evaluate_params "$@" diff --git a/tools/examples/init.d/sysconfig.xendomains b/tools/examples/init.d/sysconfig.xendomains new file mode 100644 index 0000000..e93b1a4 --- /dev/null +++ b/tools/examples/init.d/sysconfig.xendomains @@ -0,0 +1,137 @@ +## Path: System/xen +## Description: xen domain start/stop on boot +## Type: string +## Default: +# +# The xendomains script can send SysRq requests to domains on shutdown. +# If you don't want to MIGRATE, SAVE, or SHUTDOWN, this may be a possibility +# to do a quick and dirty shutdown ("s e i u o") or at least sync the disks +# of the domains ("s"). +# +XENDOMAINS_SYSRQ="" + +## Type: integer +## Default: 100000 +# +# If XENDOMAINS_SYSRQ is set, this variable determines how long to wait +# (in microseconds) after each SysRq, so the domain has a chance to react. +# If you want to a quick'n'dirty shutdown via SysRq, you may want to set +# it to a relatively high value (1200000). +# +XENDOMAINS_USLEEP=100000 + +## Type: integer +## Default: 5000000 +# +# When creating a guest domain, it is sensible to allow a little time for it +# to get started before creating another domain or proceeding through the +# boot process. Without this, the booting guests will thrash the disk as they +# start up. This timeout (in microseconds) specifies the delay after guest +# domain creation. +# +XENDOMAINS_CREATE_USLEEP=5000000 + +## Type: string +## Default: "" +# +# Set this to a non-empty string if you want to migrate virtual machines +# on shutdown. The string will be passed to the xm migrate DOMID command +# as is: It should contain the target IP address of the physical machine +# to migrate to and optionally parameters like --live. Leave empty if +# you don't want to try virtual machine relocation on shutdown. +# If migration succeeds, neither SAVE nor SHUTDOWN will be executed for +# that domain. +# +XENDOMAINS_MIGRATE="" + +## Type: string +## Default: /var/lib/xen/save +# +# Directory to save running domains to when the system (dom0) is +# shut down. Will also be used to restore domains from if # XENDOMAINS_RESTORE +# is set (see below). Leave empty to disable domain saving on shutdown +# (e.g. because you rather shut domains down). +# If domain saving does succeed, SHUTDOWN will not be executed. +# +XENDOMAINS_SAVE=/var/lib/xen/save + +## Type: string +## Default: "--halt --wait" +# +# If neither MIGRATE nor SAVE were enabled or if they failed, you can +# try to shut down a domain by sending it a shutdown request. To do this, +# set this to "--halt --wait". Omit the "--wait" flag to avoid waiting +# for the domain to be really down. Leave empty to skip domain shutdown. +# +XENDOMAINS_SHUTDOWN="--halt --wait" + +## Type: string +## Default: "--all --halt --wait" +# +# After we have gone over all virtual machines (resp. all automatically +# started ones, see XENDOMAINS_AUTO_ONLY below) in a loop and sent SysRq, +# migrated, saved and/or shutdown according to the settings above, we +# might want to shutdown the virtual machines that are still running +# for some reason or another. To do this, set this variable to +# "--all --halt --wait", it will be passed to xm shutdown. +# Leave it empty not to do anything special here. +# (Note: This will hit all virtual machines, even if XENDOMAINS_AUTO_ONLY +# is set.) +# +XENDOMAINS_SHUTDOWN_ALL="--all --halt --wait" + +## Type: boolean +## Default: true +# +# This variable determines whether saved domains from XENDOMAINS_SAVE +# will be restored on system startup. +# +XENDOMAINS_RESTORE=true + +## Type: string +## Default: /etc/xen/auto +# +# This variable sets the directory where domains configurations +# are stored that should be started on system startup automatically. +# Leave empty if you don't want to start domains automatically +# (or just don't place any xen domain config files in that dir). +# Note that the script tries to be clever if both RESTORE and AUTO are +# set: It will first restore saved domains and then only start domains +# in AUTO which are not running yet. +# Note that the name matching is somewhat fuzzy. +# +XENDOMAINS_AUTO=/etc/xen/auto + +## Type: boolean +## Default: false +# +# If this variable is set to "true", only the domains started via config +# files in XENDOMAINS_AUTO will be treated according to XENDOMAINS_SYSRQ, +# XENDOMAINS_MIGRATE, XENDOMAINS_SAVE, XENDMAINS_SHUTDOWN; otherwise +# all running domains will be. +# Note that the name matching is somewhat fuzzy. +# +XENDOMAINS_AUTO_ONLY=false + +## Type: integer +## Default: 300 +# +# On xendomains stop, a number of xm commands (xm migrate, save, shutdown, +# shutdown --all) may be executed. In the worst case, these commands may +# stall forever, which will prevent a successful shutdown of the machine. +# If this variable is non-zero, the script will set up a watchdog timer +# for every of these xm commands and time it out after the number of seconds +# specified by this variable. +# Note that SHUTDOWN_ALL will not be called if no virtual machines or only +# zombies are still running, so you don't need to enable this timeout just +# for the zombie case. +# The setting should be large enough to make sure that migrate/save/shutdown +# can succeed. If you do live migrations, keep in mind that live migration +# of a 1GB machine over Gigabit ethernet may actually take something like +# 100s (assuming that live migration uses 10% of the network # bandwidth). +# Depending on the virtual machine, a shutdown may also require a significant +# amount of time. So better setup this variable to a huge number and hope the +# watchdog never fires. +# +XENDOMAINS_STOP_MAXWAIT=300 + diff --git a/tools/examples/init.d/xend b/tools/examples/init.d/xend new file mode 100755 index 0000000..4bfc799 --- /dev/null +++ b/tools/examples/init.d/xend @@ -0,0 +1,66 @@ +#!/bin/bash +# +# xend Script to start and stop the Xen control daemon. +# +# Author: Keir Fraser +# +# chkconfig: 2345 98 01 +# description: Starts and stops the Xen control daemon. +### BEGIN INIT INFO +# Provides: xend +# Required-Start: $syslog $remote_fs +# Should-Start: +# Required-Stop: $syslog $remote_fs +# Should-Stop: +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Default-Enabled: yes +# Short-Description: Start/stop xend +# Description: Starts and stops the Xen control daemon. +### END INIT INFO + +if ! grep -q "control_d" /proc/xen/capabilities ; then + exit 0 +fi + +# Wait for Xend to be up +function await_daemons_up +{ + i=1 + rets=10 + xend status + while [ $? -ne 0 -a $i -lt $rets ]; do + sleep 1 + echo -n . + i=$(($i + 1)) + xend status + done +} + +case "$1" in + start) + xend start + await_daemons_up + ;; + stop) + xend stop + ;; + status) + xend status + ;; + reload) + xend reload + ;; + restart|force-reload) + xend restart + await_daemons_up + ;; + *) + # do not advertise unreasonable commands that there is no reason + # to use with this device + echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}" + exit 1 +esac + +exit $? + diff --git a/tools/examples/init.d/xendomains b/tools/examples/init.d/xendomains new file mode 100644 index 0000000..5c2e492 --- /dev/null +++ b/tools/examples/init.d/xendomains @@ -0,0 +1,531 @@ +#!/bin/bash +# +# /etc/init.d/xendomains +# Start / stop domains automatically when domain 0 boots / shuts down. +# +# chkconfig: 345 99 00 +# description: Start / stop Xen domains. +# +# This script offers fairly basic functionality. It should work on Redhat +# but also on LSB-compliant SuSE releases and on Debian with the LSB package +# installed. (LSB is the Linux Standard Base) +# +# Based on the example in the "Designing High Quality Integrated Linux +# Applications HOWTO" by Avi Alkalay +# +# +### BEGIN INIT INFO +# Provides: xendomains +# Required-Start: $syslog $remote_fs xend +# Should-Start: +# Required-Stop: $syslog $remote_fs xend +# Should-Stop: +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Default-Enabled: yes +# Short-Description: Start/stop secondary xen domains +# Description: Start / stop domains automatically when domain 0 +# boots / shuts down. +### END INIT INFO + +# Correct exit code would probably be 5, but it's enough +# if xend complains if we're not running as privileged domain +if ! [ -e /proc/xen/privcmd ]; then + exit 0 +fi + +LOCKFILE=/var/lock/subsys/xendomains +XENDOM_CONFIG=/etc/sysconfig/xendomains + +test -r $XENDOM_CONFIG || { echo "$XENDOM_CONFIG not existing"; + if [ "$1" = "stop" ]; then exit 0; + else exit 6; fi; } + +. $XENDOM_CONFIG + +# Use the SUSE rc_ init script functions; +# emulate them on LSB, RH and other systems +if test -e /etc/rc.status; then + # SUSE rc script library + . /etc/rc.status +else + _cmd=$1 + declare -a _SMSG + if test "${_cmd}" = "status"; then + _SMSG=(running dead dead unused unknown) + _RC_UNUSED=3 + else + _SMSG=(done failed failed missed failed skipped unused failed failed) + _RC_UNUSED=6 + fi + if test -e /etc/init.d/functions; then + # REDHAT + . /etc/init.d/functions + echo_rc() + { + #echo -n " [${_SMSG[${_RC_RV}]}] " + if test ${_RC_RV} = 0; then + success " [${_SMSG[${_RC_RV}]}] " + else + failure " [${_SMSG[${_RC_RV}]}] " + fi + } + elif test -e /lib/lsb/init-functions; then + # LSB + . /lib/lsb/init-functions + if alias log_success_msg >/dev/null 2>/dev/null; then + echo_rc() + { + echo " [${_SMSG[${_RC_RV}]}] " + } + else + echo_rc() + { + if test ${_RC_RV} = 0; then + log_success_msg " [${_SMSG[${_RC_RV}]}] " + else + log_failure_msg " [${_SMSG[${_RC_RV}]}] " + fi + } + fi + else + # emulate it + echo_rc() + { + echo " [${_SMSG[${_RC_RV}]}] " + } + fi + rc_reset() { _RC_RV=0; } + rc_failed() + { + if test -z "$1"; then + _RC_RV=1; + elif test "$1" != "0"; then + _RC_RV=$1; + fi + return ${_RC_RV} + } + rc_check() + { + return rc_failed $? + } + rc_status() + { + rc_failed $? + if test "$1" = "-r"; then _RC_RV=0; shift; fi + if test "$1" = "-s"; then rc_failed 5; echo_rc; rc_failed 3; shift; fi + if test "$1" = "-u"; then rc_failed ${_RC_UNUSED}; echo_rc; rc_failed 3; shift; fi + if test "$1" = "-v"; then echo_rc; shift; fi + if test "$1" = "-r"; then _RC_RV=0; shift; fi + return ${_RC_RV} + } + rc_exit() { exit ${_RC_RV}; } + rc_active() + { + if test -z "$RUNLEVEL"; then read RUNLEVEL REST < <(/sbin/runlevel); fi + if test -e /etc/init.d/S[0-9][0-9]${1}; then return 0; fi + return 1 + } +fi + +if ! which usleep >&/dev/null +then + usleep() + { + if [ -n "$1" ] + then + sleep $(( $1 / 1000000 )) + fi + } +fi + +# Reset status of this service +rc_reset + +## +# Returns 0 (success) if the given parameter names a directory, and that +# directory is not empty. +# +contains_something() +{ + if [ -d "$1" ] && [ `/bin/ls $1 | wc -l` -gt 0 ] + then + return 0 + else + return 1 + fi +} + +# read name from xen config file +rdname() +{ + NM=$(xm create --quiet --dryrun --defconfig "$1" | + sed -n 's/^.*(name \(.*\))$/\1/p') +} + +rdnames() +{ + NAMES= + if ! contains_something "$XENDOMAINS_AUTO" + then + return + fi + for dom in $XENDOMAINS_AUTO/*; do + rdname $dom + if test -z $NAMES; then + NAMES=$NM; + else + NAMES="$NAMES|$NM" + fi + done +} + +parseln() +{ + if [[ "$1" =~ "\(domain" ]]; then + name=;id= + else if [[ "$1" =~ "\(name" ]]; then + name=$(echo $1 | sed -e 's/^.*(name \(.*\))$/\1/') + else if [[ "$1" =~ "\(domid" ]]; then + id=$(echo $1 | sed -e 's/^.*(domid \(.*\))$/\1/') + fi; fi; fi + + [ -n "$name" -a -n "$id" ] && return 0 || return 1 +} + +is_running() +{ + rdname $1 + RC=1 + name=;id= + while read LN; do + parseln "$LN" || continue + if test $id = 0; then continue; fi + case $name in + ($NM) + RC=0 + ;; + esac + done < <(xm list -l | grep '(\(domain\|domid\|name\)') + return $RC +} + +start() +{ + if [ -f $LOCKFILE ]; then + echo -n "xendomains already running (lockfile exists)" + return; + fi + + saved_domains=" " + if [ "$XENDOMAINS_RESTORE" = "true" ] && + contains_something "$XENDOMAINS_SAVE" + then + mkdir -p $(dirname "$LOCKFILE") + touch $LOCKFILE + echo -n "Restoring Xen domains:" + saved_domains=`ls $XENDOMAINS_SAVE` + for dom in $XENDOMAINS_SAVE/*; do + if [ -f $dom ] ; then + HEADER=`head -c 16 $dom | head -n 1 2> /dev/null` + if [ $HEADER = "LinuxGuestRecord" ]; then + echo -n " ${dom##*/}" + xm restore $dom + if [ $? -ne 0 ]; then + rc_failed $? + echo -n '!' + else + # mv $dom ${dom%/*}/.${dom##*/} + rm $dom + fi + fi + fi + done + echo . + fi + + if contains_something "$XENDOMAINS_AUTO" + then + touch $LOCKFILE + echo -n "Starting auto Xen domains:" + # We expect config scripts for auto starting domains to be in + # XENDOMAINS_AUTO - they could just be symlinks to files elsewhere + + # Create all domains with config files in XENDOMAINS_AUTO. + # TODO: We should record which domain name belongs + # so we have the option to selectively shut down / migrate later + # If a domain statefile from $XENDOMAINS_SAVE matches a domain name + # in $XENDOMAINS_AUTO, do not try to start that domain; if it didn't + # restore correctly it requires administrative attention. + for dom in $XENDOMAINS_AUTO/*; do + echo -n " ${dom##*/}" + shortdom=$(echo $dom | sed -n 's/^.*\/\(.*\)$/\1/p') + echo $saved_domains | grep -w $shortdom > /dev/null + if [ $? -eq 0 ] || is_running $dom; then + echo -n "(skip)" + else + xm create --quiet --defconfig $dom + if [ $? -ne 0 ]; then + rc_failed $? + echo -n '!' + else + usleep $XENDOMAINS_CREATE_USLEEP + fi + fi + done + fi +} + +all_zombies() +{ + name=;id= + while read LN; do + parseln "$LN" || continue + if test $id = 0; then continue; fi + if test "$state" != "-b---d" -a "$state" != "-----d"; then + return 1; + fi + done < <(xm list -l | grep '(\(domain\|domid\|name\)') + return 0 +} + +# Wait for max $XENDOMAINS_STOP_MAXWAIT for xm $1 to finish; +# if it has not exited by that time kill it, so the init script will +# succeed within a finite amount of time; if $2 is nonnull, it will +# kill the command as well as soon as no domain (except for zombies) +# are left (used for shutdown --all). +watchdog_xm() +{ + if test -z "$XENDOMAINS_STOP_MAXWAIT" -o "$XENDOMAINS_STOP_MAXWAIT" = "0"; then + exit + fi + usleep 20000 + for no in `seq 0 $XENDOMAINS_STOP_MAXWAIT`; do + # exit if xm save/migrate/shutdown is finished + PSAX=`ps axlw | grep "xm $1" | grep -v grep` + if test -z "$PSAX"; then exit; fi + echo -n "."; sleep 1 + # go to kill immediately if there's only zombies left + if all_zombies && test -n "$2"; then break; fi + done + sleep 1 + read PSF PSUID PSPID PSPPID < <(echo "$PSAX") + # kill xm $1 + kill $PSPID >/dev/null 2>&1 +} + +stop() +{ + # Collect list of domains to shut down + if test "$XENDOMAINS_AUTO_ONLY" = "true"; then + rdnames + fi + echo -n "Shutting down Xen domains:" + name=;id= + while read LN; do + parseln "$LN" || continue + if test $id = 0; then continue; fi + echo -n " $name" + if test "$XENDOMAINS_AUTO_ONLY" = "true"; then + eval " + case \"\$name\" in + ($NAMES) + # nothing + ;; + (*) + echo -n '(skip)' + continue + ;; + esac + " + fi + # XENDOMAINS_SYSRQ chould be something like just "s" + # or "s e i u" or even "s e s i u o" + # for the latter, you should set XENDOMAINS_USLEEP to 1200000 or so + if test -n "$XENDOMAINS_SYSRQ"; then + for sysrq in $XENDOMAINS_SYSRQ; do + echo -n "(SR-$sysrq)" + xm sysrq $id $sysrq + if test $? -ne 0; then + rc_failed $? + echo -n '!' + fi + # usleep just ignores empty arg + usleep $XENDOMAINS_USLEEP + done + fi + if test "$state" = "-b---d" -o "$state" = "-----d"; then + echo -n "(zomb)" + continue + fi + if test -n "$XENDOMAINS_MIGRATE"; then + echo -n "(migr)" + watchdog_xm migrate & + WDOG_PID=$! + xm migrate $id $XENDOMAINS_MIGRATE + if test $? -ne 0; then + rc_failed $? + echo -n '!' + kill $WDOG_PID >/dev/null 2>&1 + else + kill $WDOG_PID >/dev/null 2>&1 + continue + fi + fi + if test -n "$XENDOMAINS_SAVE"; then + echo -n "(save)" + watchdog_xm save & + WDOG_PID=$! + mkdir -p "$XENDOMAINS_SAVE" + xm save $id $XENDOMAINS_SAVE/$name + if test $? -ne 0; then + rc_failed $? + echo -n '!' + kill $WDOG_PID >/dev/null 2>&1 + else + kill $WDOG_PID >/dev/null 2>&1 + continue + fi + fi + if test -n "$XENDOMAINS_SHUTDOWN"; then + # XENDOMAINS_SHUTDOWN should be "--halt --wait" + echo -n "(shut)" + watchdog_xm shutdown & + WDOG_PID=$! + xm shutdown $id $XENDOMAINS_SHUTDOWN + if test $? -ne 0; then + rc_failed $? + echo -n '!' + fi + kill $WDOG_PID >/dev/null 2>&1 + fi + done < <(xm list -l | grep '(\(domain\|domid\|name\)') + + # NB. this shuts down ALL Xen domains (politely), not just the ones in + # AUTODIR/* + # This is because it's easier to do ;-) but arguably if this script is run + # on system shutdown then it's also the right thing to do. + if ! all_zombies && test -n "$XENDOMAINS_SHUTDOWN_ALL"; then + # XENDOMAINS_SHUTDOWN_ALL should be "--all --halt --wait" + echo -n " SHUTDOWN_ALL " + watchdog_xm shutdown 1 & + WDOG_PID=$! + xm shutdown $XENDOMAINS_SHUTDOWN_ALL + if test $? -ne 0; then + rc_failed $? + echo -n '!' + fi + kill $WDOG_PID >/dev/null 2>&1 + fi + + # Unconditionally delete lock file + rm -f $LOCKFILE +} + +check_domain_up() +{ + name=;id= + while read LN; do + parseln "$LN" || continue + if test $id = 0; then continue; fi + case $name in + ($1) + return 0 + ;; + esac + done < <(xm list -l | grep '(\(domain\|domid\|name\)') + return 1 +} + +check_all_auto_domains_up() +{ + if ! contains_something "$XENDOMAINS_AUTO" + then + return 0 + fi + missing= + for nm in $XENDOMAINS_AUTO/*; do + rdname $nm + found=0 + if check_domain_up "$NM"; then + echo -n " $name" + else + missing="$missing $NM" + fi + done + if test -n "$missing"; then + echo -n " MISS AUTO:$missing" + return 1 + fi + return 0 +} + +check_all_saved_domains_up() +{ + if ! contains_something "$XENDOMAINS_SAVE" + then + return 0 + fi + missing=`/bin/ls $XENDOMAINS_SAVE` + echo -n " MISS SAVED: " $missing + return 1 +} + +# This does NOT necessarily restart all running domains: instead it +# stops all running domains and then boots all the domains specified in +# AUTODIR. If other domains have been started manually then they will +# not get restarted. +# Commented out to avoid confusion! + +restart() +{ + stop + start +} + +reload() +{ + restart +} + + +case "$1" in + start) + start + rc_status + if test -f $LOCKFILE; then rc_status -v; fi + ;; + + stop) + stop + rc_status -v + ;; + + restart) + restart + ;; + reload) + reload + ;; + + status) + echo -n "Checking for xendomains:" + if test ! -f $LOCKFILE; then + rc_failed 3 + else + check_all_auto_domains_up + rc_status + check_all_saved_domains_up + rc_status + fi + rc_status -v + ;; + + *) + echo "Usage: $0 {start|stop|restart|reload|status}" + rc_failed 3 + rc_status -v + ;; +esac + +rc_exit diff --git a/tools/examples/locking.sh b/tools/examples/locking.sh new file mode 100644 index 0000000..6ff58e7 --- /dev/null +++ b/tools/examples/locking.sh @@ -0,0 +1,98 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# +# Serialisation +# + +LOCK_SLEEPTIME=1 +LOCK_SPINNING_RETRIES=5 +LOCK_RETRIES=100 +LOCK_BASEDIR=/var/run/xen-hotplug + + +claim_lock() +{ + local lockdir="$LOCK_BASEDIR/$1" + mkdir -p "$LOCK_BASEDIR" + _claim_lock "$lockdir" +} + + +release_lock() +{ + _release_lock "$LOCK_BASEDIR/$1" +} + + +_claim_lock() +{ + local lockdir="$1" + local owner=$(_lock_owner "$lockdir") + local retries=0 + + while [ $retries -lt $LOCK_RETRIES ] + do + mkdir "$lockdir" 2>/dev/null && trap "release_lock $1; sigerr" ERR && + _update_lock_info "$lockdir" && return + + local new_owner=$(_lock_owner "$lockdir") + if [ "$new_owner" != "$owner" ] + then + owner="$new_owner" + retries=0 + fi + + if [ $retries -gt $LOCK_SPINNING_RETRIES ] + then + sleep $LOCK_SLEEPTIME + else + sleep 0 + fi + retries=$(($retries + 1)) + done + _steal_lock "$lockdir" +} + + +_release_lock() +{ + trap sigerr ERR + rm -rf "$1" 2>/dev/null || true +} + + +_steal_lock() +{ + local lockdir="$1" + local owner=$(cat "$lockdir/owner" 2>/dev/null || echo "unknown") + log err "Forced to steal lock on $lockdir from $owner!" + _release_lock "$lockdir" + _claim_lock "$lockdir" +} + + +_lock_owner() +{ + cat "$1/owner" 2>/dev/null || echo "unknown" +} + + +_update_lock_info() +{ + echo "$$: $0" >"$1/owner" +} diff --git a/tools/examples/logging.sh b/tools/examples/logging.sh new file mode 100644 index 0000000..c1bc699 --- /dev/null +++ b/tools/examples/logging.sh @@ -0,0 +1,22 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +log() { + local level="$1" + shift + logger -p "daemon.$level" -- "$0:" "$@" || echo "$0 $@" >&2 +} diff --git a/tools/examples/network-bridge b/tools/examples/network-bridge new file mode 100755 index 0000000..9d7be4e --- /dev/null +++ b/tools/examples/network-bridge @@ -0,0 +1,310 @@ +#!/bin/bash +#============================================================================ +# Default Xen network start/stop script. +# Xend calls a network script when it starts. +# The script name to use is defined in /etc/xen/xend-config.sxp +# in the network-script field. +# +# This script creates a bridge (default ${netdev}), adds a device +# (defaults to the device on the default gateway route) to it, copies +# the IP addresses from the device to the bridge and adjusts the routes +# accordingly. +# +# If all goes well, this should ensure that networking stays up. +# However, some configurations are upset by this, especially +# NFS roots. If the bridged setup does not meet your needs, +# configure a different script, for example using routing instead. +# +# Usage: +# +# network-bridge (start|stop|status) {VAR=VAL}* +# +# Vars: +# +# bridge The bridge to use (default ${netdev}). +# netdev The interface to add to the bridge (default gateway device). +# antispoof Whether to use iptables to prevent spoofing (default no). +# +# Internal Vars: +# pdev="p${netdev}" +# tdev=tmpbridge +# +# start: +# Creates the bridge as tdev +# Copies the IP and MAC addresses from pdev to bridge +# Renames netdev to be pdev +# Renames tdev to bridge +# Enslaves pdev to bridge +# +# stop: +# Removes pdev from the bridge +# Transfers addresses, routes from bridge to pdev +# Renames bridge to tdev +# Renames pdev to netdev +# Deletes tdev +# +# status: +# Print addresses, interfaces, routes +# +#============================================================================ + + +dir=$(dirname "$0") +. "$dir/xen-script-common.sh" +. "$dir/xen-network-common.sh" + +findCommand "$@" +evalVariables "$@" + +is_network_root () { + local rootfs=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $3; }}' /etc/mtab) + local rootopts=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $4; }}' /etc/mtab) + + [[ "$rootfs" =~ "^nfs" ]] || [[ "$rootopts" =~ "_netdev" ]] && has_nfsroot=1 || has_nfsroot=0 + if [ $has_nfsroot -eq 1 ]; then + local bparms=$(cat /proc/cmdline) + for p in $bparms; do + local ipaddr=$(echo $p | awk /nfsroot=/'{ print substr($1,9,index($1,":")-9) }') + if [ "$ipaddr" != "" ]; then + local nfsdev=$(ip route get $ipaddr | awk /$ipaddr/'{ print $3 }') + [[ "$nfsdev" == "$netdev" ]] && return 0 || return 1 + fi + done + fi + return 1 +} + +find_alt_device () { + local interf=$1 + local prefix=${interf%[[:digit:]]} + local ifs=$(ip link show | grep " $prefix" |\ + gawk '{ printf ("%s",substr($2,1,length($2)-1)) }' |\ + sed s/$interf//) + echo "$ifs" +} + +netdev=${netdev:-$(ip route list 0.0.0.0/0 | \ + sed 's/.*dev \([a-z]\+[0-9]\+\).*$/\1/')} +if is_network_root ; then + altdevs=$(find_alt_device $netdev) + for netdev in $altdevs; do break; done + if [ -z "$netdev" ]; then + [ -x /usr/bin/logger ] && /usr/bin/logger "network-bridge: bridging not supported on network root; not starting" + exit + fi +fi +netdev=${netdev:-eth0} +bridge=${bridge:-${netdev}} +antispoof=${antispoof:-no} + +pdev="p${netdev}" +tdev=tmpbridge + +get_ip_info() { + addr_pfx=`ip addr show dev $1 | egrep '^ *inet' | sed -e 's/ *inet //' -e 's/ .*//'` + gateway=`ip route show dev $1 | fgrep default | sed 's/default via //'` +} + +do_ifup() { + if ! ifup $1 ; then + if [ -n "$addr_pfx" ] ; then + # use the info from get_ip_info() + ip addr flush $1 + ip addr add ${addr_pfx} dev $1 + ip link set dev $1 up + [ -n "$gateway" ] && ip route add default via ${gateway} + fi + fi +} + +# Usage: transfer_addrs src dst +# Copy all IP addresses (including aliases) from device $src to device $dst. +transfer_addrs () { + local src=$1 + local dst=$2 + # Don't bother if $dst already has IP addresses. + if ip addr show dev ${dst} | egrep -q '^ *inet ' ; then + return + fi + # Address lines start with 'inet' and have the device in them. + # Replace 'inet' with 'ip addr add' and change the device name $src + # to 'dev $src'. + ip addr show dev ${src} | egrep '^ *inet ' | sed -e " +s/inet/ip addr add/ +s@\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+/[0-9]\+\)@\1@ +s/${src}/dev ${dst} label ${dst}/ +s/secondary// +" | sh -e + # Remove automatic routes on destination device + ip route list | sed -ne " +/dev ${dst}\( \|$\)/ { + s/^/ip route del / + p +}" | sh -e +} + +# Usage: transfer_routes src dst +# Get all IP routes to device $src, delete them, and +# add the same routes to device $dst. +# The original routes have to be deleted, otherwise adding them +# for $dst fails (duplicate routes). +transfer_routes () { + local src=$1 + local dst=$2 + # List all routes and grep the ones with $src in. + # Stick 'ip route del' on the front to delete. + # Change $src to $dst and use 'ip route add' to add. + ip route list | sed -ne " +/dev ${src}\( \|$\)/ { + h + s/^/ip route del / + P + g + s/${src}/${dst}/ + s/^/ip route add / + P + d +}" | sh -e +} + + +## +# link_exists interface +# +# Returns 0 if the interface named exists (whether up or down), 1 otherwise. +# +link_exists() +{ + if ip link show "$1" >/dev/null 2>/dev/null + then + return 0 + else + return 1 + fi +} + +# Set the default forwarding policy for $dev to drop. +# Allow forwarding to the bridge. +antispoofing () { + iptables -P FORWARD DROP + iptables -F FORWARD + iptables -A FORWARD -m physdev --physdev-in ${pdev} -j ACCEPT +} + +# Usage: show_status dev bridge +# Print ifconfig and routes. +show_status () { + local dev=$1 + local bridge=$2 + + echo '============================================================' + ip addr show ${dev} + ip addr show ${bridge} + echo ' ' + brctl show ${bridge} + echo ' ' + ip route list + echo ' ' + route -n + echo '============================================================' +} + +op_start () { + if [ "${bridge}" = "null" ] ; then + return + fi + + if link_exists "$pdev"; then + # The device is already up. + return + fi + + create_bridge ${tdev} + + preiftransfer ${netdev} + transfer_addrs ${netdev} ${tdev} + if ! ifdown ${netdev}; then + # If ifdown fails, remember the IP details. + get_ip_info ${netdev} + ip link set ${netdev} down + ip addr flush ${netdev} + fi + ip link set ${netdev} name ${pdev} + ip link set ${tdev} name ${bridge} + + setup_bridge_port ${pdev} + + add_to_bridge2 ${bridge} ${pdev} + do_ifup ${bridge} + + if [ ${antispoof} = 'yes' ] ; then + antispoofing + fi +} + +op_stop () { + if [ "${bridge}" = "null" ]; then + return + fi + if ! link_exists "$bridge"; then + return + fi + + transfer_addrs ${bridge} ${pdev} + if ! ifdown ${bridge}; then + get_ip_info ${bridge} + fi + ip link set ${pdev} down + ip addr flush ${bridge} + + brctl delif ${bridge} ${pdev} + ip link set ${bridge} down + + ip link set ${bridge} name ${tdev} + ip link set ${pdev} name ${netdev} + do_ifup ${netdev} + + brctl delbr ${tdev} +} + +# adds $dev to $bridge but waits for $dev to be in running state first +add_to_bridge2() { + local bridge=$1 + local dev=$2 + local maxtries=10 + + echo -n "Waiting for ${dev} to negotiate link." + ip link set ${dev} up + for i in `seq ${maxtries}` ; do + if ifconfig ${dev} | grep -q RUNNING ; then + break + else + echo -n '.' + sleep 1 + fi + done + + if [ ${i} -eq ${maxtries} ] ; then echo -n '(link isnt in running state)' ; fi + echo + + add_to_bridge ${bridge} ${dev} +} + +case "$command" in + start) + op_start + ;; + + stop) + op_stop + ;; + + status) + show_status ${netdev} ${bridge} + ;; + + *) + echo "Unknown command: $command" >&2 + echo 'Valid commands are: start, stop, status' >&2 + exit 1 +esac diff --git a/tools/examples/network-nat b/tools/examples/network-nat new file mode 100644 index 0000000..d9c62c6 --- /dev/null +++ b/tools/examples/network-nat @@ -0,0 +1,119 @@ +#!/bin/bash -x +#============================================================================ +# Default Xen network start/stop script when using NAT. +# Xend calls a network script when it starts. +# The script name to use is defined in /etc/xen/xend-config.sxp +# in the network-script field. +# +# Usage: +# +# network-nat (start|stop|status) {VAR=VAL}* +# +# Vars: +# +# netdev The gateway interface (default eth0). +# antispoof Whether to use iptables to prevent spoofing (default no). +# dhcp Whether to alter the local DHCP configuration (default no). +# +#============================================================================ + +dir=$(dirname "$0") +. "$dir/xen-script-common.sh" +. "$dir/xen-network-common.sh" + +findCommand "$@" +evalVariables "$@" + +netdev=${netdev:-eth0} +# antispoofing not yet implemented +antispoof=${antispoof:-no} + +# turn on dhcp feature by default if dhcpd is installed +if [ -f /etc/dhcpd.conf ] +then + dhcp=${dhcp:-yes} +else + dhcp=${dhcp:-no} +fi + + +if [ "$dhcp" != 'no' ] +then + dhcpd_conf_file=$(find_dhcpd_conf_file) + dhcpd_init_file=$(find_dhcpd_init_file) + if [ -z "$dhcpd_conf_file" ] || [ -z "$dhcpd_init_file" ] + then + echo 'Failed to find dhcpd configuration or init file.' >&2 + exit 1 + fi +fi + + +function dhcp_start() +{ + if ! grep -q "subnet 10.0.0.0" "$dhcpd_conf_file" + then + echo >>"$dhcpd_conf_file" "subnet 10.0.0.0 netmask 255.255.0.0 {}" + fi + + "$dhcpd_init_file" restart +} + + +function dhcp_stop() +{ + local tmpfile=$(mktemp) + grep -v "subnet 10.0.0.0" "$dhcpd_conf_file" >"$tmpfile" + if diff "$tmpfile" "$dhcpd_conf_file" >&/dev/null + then + rm "$tmpfile" + else + mv "$tmpfile" "$dhcpd_conf_file" + fi + + "$dhcpd_init_file" restart +} + + +op_start() { + echo 1 >/proc/sys/net/ipv4/ip_forward + iptables -t nat -A POSTROUTING -o ${netdev} -j MASQUERADE + [ "$dhcp" != 'no' ] && dhcp_start +} + + +op_stop() { + [ "$dhcp" != 'no' ] && dhcp_stop + iptables -t nat -D POSTROUTING -o ${netdev} -j MASQUERADE +} + + +show_status() { + echo '============================================================' + ifconfig + echo ' ' + ip route list + echo ' ' + route -n + echo '============================================================' + +} + +case "$command" in + start) + op_start + ;; + + stop) + op_stop + ;; + + status) + show_status + ;; + + *) + echo "Unknown command: $command" >&2 + echo 'Valid commands are: start, stop, status' >&2 + exit 1 +esac diff --git a/tools/examples/network-route b/tools/examples/network-route new file mode 100755 index 0000000..574441e --- /dev/null +++ b/tools/examples/network-route @@ -0,0 +1,27 @@ +#!/bin/bash +#============================================================================ +# Default Xen network start/stop script. +# Xend calls a network script when it starts. +# The script name to use is defined in /etc/xen/xend-config.sxp +# in the network-script field. +# +# Usage: +# +# network-route (start|stop|status) {VAR=VAL}* +# +# Vars: +# +# netdev The gateway interface (default eth0). +# antispoof Whether to use iptables to prevent spoofing (default yes). +# +#============================================================================ + +dir=$(dirname "$0") +. "$dir/xen-script-common.sh" + +evalVariables "$@" + +netdev=${netdev:-eth${vifnum}} + +echo 1 >/proc/sys/net/ipv4/ip_forward +echo 1 >/proc/sys/net/ipv4/conf/${netdev}/proxy_arp diff --git a/tools/examples/vif-bridge b/tools/examples/vif-bridge new file mode 100755 index 0000000..1b698d7 --- /dev/null +++ b/tools/examples/vif-bridge @@ -0,0 +1,100 @@ +#!/bin/bash +#============================================================================ +# /etc/xen/vif-bridge +# +# Script for configuring a vif in bridged mode. +# The hotplugging system will call this script if it is specified either in +# the device configuration given to Xend, or the default Xend configuration +# in /etc/xen/xend-config.sxp. If the script is specified in neither of those +# places, then this script is the default. +# +# Usage: +# vif-bridge (add|remove|online|offline) +# +# Environment vars: +# vif vif interface name (required). +# XENBUS_PATH path to this device's details in the XenStore (required). +# +# Read from the store: +# bridge bridge to add the vif to (optional). Defaults to searching for the +# bridge itself. +# ip list of IP networks for the vif, space-separated (optional). +# +# up: +# Enslaves the vif interface to the bridge and adds iptables rules +# for its ip addresses (if any). +# +# down: +# Removes the vif interface from the bridge and removes the iptables +# rules for its ip addresses (if any). +#============================================================================ + +dir=$(dirname "$0") +. "$dir/vif-common.sh" + +bridge=${bridge:-} +bridge=$(xenstore_read_default "$XENBUS_PATH/bridge" "$bridge") + +if [ -z "$bridge" ] +then + bridge=$(brctl show | cut -d " +" -f 2 | cut -f 1) + + if [ -z "$bridge" ] + then + fatal "Could not find bridge, and none was specified" + fi +else + # + # Old style bridge setup with netloop, used to have a bridge name + # of xenbrX, enslaving pethX and vif0.X, and then configuring + # eth0. + # + # New style bridge setup does not use netloop, so the bridge name + # is ethX and the physical device is enslaved pethX + # + # So if... + # + # - User asks for xenbrX + # - AND xenbrX doesn't exist + # - AND there is a ethX device which is a bridge + # + # ..then we translate xenbrX to ethX + # + # This lets old config files work without modification + # + if [ ! -e "/sys/class/net/$bridge" ] && [ -z "${bridge##xenbr*}" ] + then + if [ -e "/sys/class/net/eth${bridge#xenbr}/bridge" ] + then + bridge="eth${bridge#xenbr}" + fi + fi +fi + +RET=0 +ip link show $bridge 1>/dev/null 2>&1 || RET=1 +if [ "$RET" -eq 1 ] +then + fatal "Could not find bridge device $bridge" +fi + +case "$command" in + online) + setup_bridge_port "$vif" + add_to_bridge "$bridge" "$vif" + ;; + + offline) + do_without_error brctl delif "$bridge" "$vif" + do_without_error ifconfig "$vif" down + ;; +esac + +handle_iptable + +log debug "Successful vif-bridge $command for $vif, bridge $bridge." +if [ "$command" == "online" ] +then + success +fi diff --git a/tools/examples/vif-common.sh b/tools/examples/vif-common.sh new file mode 100644 index 0000000..ee67ee2 --- /dev/null +++ b/tools/examples/vif-common.sh @@ -0,0 +1,151 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" +. "$dir/xen-network-common.sh" + +findCommand "$@" + +if [ "$command" != "online" ] && + [ "$command" != "offline" ] && + [ "$command" != "add" ] && + [ "$command" != "remove" ] +then + log err "Invalid command: $command" + exit 1 +fi + +case "$command" in + add | remove) + exit 0 + ;; +esac + + +# Parameters may be read from the environment, the command line arguments, and +# the store, with overriding in that order. The environment is given by the +# driver, the command line is given by the Xend global configuration, and +# store details are given by the per-domain or per-device configuration. + +evalVariables "$@" + +ip=${ip:-} +ip=$(xenstore_read_default "$XENBUS_PATH/ip" "$ip") + +# Check presence of compulsory args. +XENBUS_PATH="${XENBUS_PATH:?}" +vif="${vif:?}" + + +vifname=$(xenstore_read_default "$XENBUS_PATH/vifname" "") +if [ "$vifname" ] +then + if [ "$command" == "online" ] && ! ip link show "$vifname" >&/dev/null + then + do_or_die ip link set "$vif" name "$vifname" + fi + vif="$vifname" +fi + + +frob_iptable() +{ + if [ "$command" == "online" ] + then + local c="-A" + else + local c="-D" + fi + + iptables "$c" FORWARD -m physdev --physdev-in "$vif" "$@" -j ACCEPT \ + 2>/dev/null || + [ "$c" == "-D" ] || + log err \ + "iptables $c FORWARD -m physdev --physdev-in $vif $@ -j ACCEPT failed. +If you are using iptables, this may affect networking for guest domains." +} + + +## +# Add or remove the appropriate entries in the iptables. With antispoofing +# turned on, we have to explicitly allow packets to the interface, regardless +# of the ip setting. If ip is set, then we additionally restrict the packets +# to those coming from the specified networks, though we allow DHCP requests +# as well. +# +handle_iptable() +{ + # Check for a working iptables installation. Checking for the iptables + # binary is not sufficient, because the user may not have the appropriate + # modules installed. If iptables is not working, then there's no need to do + # anything with it, so we can just return. + if ! iptables -L -n >&/dev/null + then + return + fi + + if [ "$ip" != "" ] + then + local addr + for addr in $ip + do + frob_iptable -s "$addr" + done + + # Always allow the domain to talk to a DHCP server. + frob_iptable -p udp --sport 68 --dport 67 + else + # No IP addresses have been specified, so allow anything. + frob_iptable + fi +} + + +## +# ip_of interface +# +# Print the IP address currently in use at the given interface, or nothing if +# the interface is not up. +# +ip_of() +{ + ip addr show "$1" | awk "/^.*inet.*$1\$/{print \$2}" | sed -n '1 s,/.*,,p' +} + + +## +# dom0_ip +# +# Print the IP address of the interface in dom0 through which we are routing. +# This is the IP address on the interface specified as "netdev" as a parameter +# to these scripts, or eth0 by default. This function will call fatal if no +# such interface could be found. +# +dom0_ip() +{ + local nd=${netdev:-eth0} + local result=$(ip_of "$nd") + if [ -z "$result" ] + then + fatal +"$netdev is not up. Bring it up or specify another interface with " \ +"netdev= as a parameter to $0." + fi + echo "$result" +} diff --git a/tools/examples/vif-nat b/tools/examples/vif-nat new file mode 100644 index 0000000..75bdf5c --- /dev/null +++ b/tools/examples/vif-nat @@ -0,0 +1,192 @@ +#!/bin/bash +#============================================================================ +# /etc/xen/vif-nat +# +# Script for configuring a vif in routed-nat mode. +# The hotplugging system will call this script if it is specified either in +# the device configuration given to Xend, or the default Xend configuration +# in /etc/xen/xend-config.sxp. If the script is specified in neither of those +# places, then vif-bridge is the default. +# +# Usage: +# vif-nat (add|remove|online|offline) +# +# Environment vars: +# vif vif interface name (required). +# XENBUS_PATH path to this device's details in the XenStore (required). +# +# Parameters: +# dhcp Whether to alter the local DHCP configuration to include this +# new host (default no). +# +# Read from the store: +# ip list of IP networks for the vif, space-separated (default given in +# this script). +#============================================================================ + + +dir=$(dirname "$0") +. "$dir/vif-common.sh" + +# turn on dhcp feature by default if dhcpd is installed +if [ -f /etc/dhcpd.conf ] +then + dhcp=${dhcp:-yes} +else + dhcp=${dhcp:-no} +fi + +if [ "$dhcp" != 'no' ] +then + dhcpd_conf_file=$(find_dhcpd_conf_file) + dhcpd_init_file=$(find_dhcpd_init_file) + dhcpd_arg_file=$(find_dhcpd_arg_file) + if [ -z "$dhcpd_conf_file" ] || [ -z "$dhcpd_init_file" ] || [ -z "$dhcpd_arg_file" ] + then + echo 'Failed to find dhcpd configuration or init or args file.' >&2 + exit 1 + fi +fi + + +domid=$(xenstore_read "$XENBUS_PATH/frontend-id") +vifid=$(xenstore_read "$XENBUS_PATH/handle") +vifid=$(( $vifid + 1 )) + + +ip_from_dom() +{ + local domid1=$(( $domid / 256 )) + local domid2=$(( $domid % 256 )) + + echo "10.$domid1.$domid2.$vifid/16" +} + + +routing_ip() +{ + echo $(echo $1 | awk -F. '{print $1"."$2"."$3"."$4 + 127}') +} + + +dotted_quad() +{ + echo\ + $(( ($1 & 0xFF000000) >> 24))\ +.$(( ($1 & 0x00FF0000) >> 16))\ +.$(( ($1 & 0x0000FF00) >> 8 ))\ +.$(( $1 & 0x000000FF )) +} + + +if [ "$ip" = "" ] +then + ip=$(ip_from_dom) +fi + +router_ip=$(routing_ip "$ip") + +# Split the given IP/bits pair. +vif_ip=`echo ${ip} | awk -F/ '{print $1}'` + +hostname=$(xenstore_read "$XENBUS_PATH/domain" | tr -- '_.:/+' '-----') +if [ "$vifid" != "1" ] +then + hostname="$hostname-$vifid" +fi + +dhcparg_remove_entry() +{ + local tmpfile=$(mktemp) + sed -e "s/$vif //" "$dhcpd_arg_file" >"$tmpfile" + if diff "$tmpfile" "$dhcpd_arg_file" >/dev/null + then + rm "$tmpfile" + else + mv "$tmpfile" "$dhcpd_arg_file" + fi +} + +dhcparg_add_entry() +{ + dhcparg_remove_entry + local tmpfile=$(mktemp) + # handle Red Hat, SUSE, and Debian styles, with or without quotes + sed -e 's/^DHCPDARGS="*\([^"]*\)"*/DHCPDARGS="\1'"$vif "'"/' \ + "$dhcpd_arg_file" >"$tmpfile" && mv "$tmpfile" "$dhcpd_arg_file" + sed -e 's/^DHCPD_INTERFACE="*\([^"]*\)"*/DHCPD_INTERFACE="\1'"$vif "'"/' \ + "$dhcpd_arg_file" >"$tmpfile" && mv "$tmpfile" "$dhcpd_arg_file" + sed -e 's/^INTERFACES="*\([^"]*\)"*/INTERFACES="\1'"$vif "'"/' \ + "$dhcpd_arg_file" >"$tmpfile" && mv "$tmpfile" "$dhcpd_arg_file" + rm -f "$tmpfile" +} + +dhcp_remove_entry() +{ + local tmpfile=$(mktemp) + grep -v "host $hostname" "$dhcpd_conf_file" >"$tmpfile" + if diff "$tmpfile" "$dhcpd_conf_file" >/dev/null + then + rm "$tmpfile" + else + mv "$tmpfile" "$dhcpd_conf_file" + fi + dhcparg_remove_entry +} + + +dhcp_up() +{ + claim_lock "vif-nat-dhcp" + dhcp_remove_entry + mac=$(xenstore_read "$XENBUS_PATH/mac") + echo >>"$dhcpd_conf_file" \ +"host $hostname { hardware ethernet $mac; fixed-address $vif_ip; option routers $router_ip; option host-name \"$hostname\"; }" + dhcparg_add_entry + release_lock "vif-nat-dhcp" + "$dhcpd_init_file" restart || true +} + + +dhcp_down() +{ + claim_lock "vif-nat-dhcp" + dhcp_remove_entry + release_lock "vif-nat-dhcp" + "$dhcpd_init_file" restart || true # We need to ignore failure because + # ISC dhcpd 3 borks if there is nothing + # for it to do, which is the case if + # the outgoing interface is not + # configured to offer leases and there + # are no vifs. +} + + +case "$command" in + online) + if ip route | grep -q "dev $vif" + then + log debug "$vif already up" + exit 0 + fi + + do_or_die ip link set "$vif" up arp on + do_or_die ip addr add "$router_ip" dev "$vif" + do_or_die ip route add "$vif_ip" dev "$vif" src "$router_ip" + echo 1 >/proc/sys/net/ipv4/conf/${vif}/proxy_arp + [ "$dhcp" != 'no' ] && dhcp_up + ;; + offline) + [ "$dhcp" != 'no' ] && dhcp_down + do_without_error ifconfig "$vif" down + ;; +esac + + +handle_iptable + +log debug "Successful vif-nat $command for $vif." +if [ "$command" = "online" ] +then + success +fi diff --git a/tools/examples/vif-route b/tools/examples/vif-route new file mode 100755 index 0000000..f5fd88e --- /dev/null +++ b/tools/examples/vif-route @@ -0,0 +1,56 @@ +#!/bin/bash +#============================================================================ +# /etc/xen/vif-route +# +# Script for configuring a vif in routed mode. +# The hotplugging system will call this script if it is specified either in +# the device configuration given to Xend, or the default Xend configuration +# in /etc/xen/xend-config.sxp. If the script is specified in neither of those +# places, then vif-bridge is the default. +# +# Usage: +# vif-route (add|remove|online|offline) +# +# Environment vars: +# vif vif interface name (required). +# XENBUS_PATH path to this device's details in the XenStore (required). +# +# Read from the store: +# ip list of IP networks for the vif, space-separated (default given in +# this script). +#============================================================================ + +dir=$(dirname "$0") +. "$dir/vif-common.sh" + +main_ip=$(dom0_ip) + +case "$command" in + online) + ifconfig ${vif} ${main_ip} netmask 255.255.255.255 up + echo 1 >/proc/sys/net/ipv4/conf/${vif}/proxy_arp + ipcmd='add' + cmdprefix='' + ;; + offline) + do_without_error ifdown ${vif} + ipcmd='del' + cmdprefix='do_without_error' + ;; +esac + +if [ "${ip}" ] ; then + # If we've been given a list of IP addresses, then add routes from dom0 to + # the guest using those addresses. + for addr in ${ip} ; do + ${cmdprefix} ip route ${ipcmd} ${addr} dev ${vif} src ${main_ip} + done +fi + +handle_iptable + +log debug "Successful vif-route $command for $vif." +if [ "$command" = "online" ] +then + success +fi diff --git a/tools/examples/vnc/Xservers b/tools/examples/vnc/Xservers new file mode 100644 index 0000000..adc5748 --- /dev/null +++ b/tools/examples/vnc/Xservers @@ -0,0 +1,5 @@ +# 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 :1 \ No newline at end of file diff --git a/tools/examples/vnc/Xvnc-xen b/tools/examples/vnc/Xvnc-xen new file mode 100755 index 0000000..15058e6 --- /dev/null +++ b/tools/examples/vnc/Xvnc-xen @@ -0,0 +1,53 @@ +#!/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 diff --git a/tools/examples/vscsi b/tools/examples/vscsi new file mode 100644 index 0000000..5ac2614 --- /dev/null +++ b/tools/examples/vscsi @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (c) 2007, FUJITSU Limited +# Based on the block scripts code. +# + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" + +findCommand "$@" + +case "$command" in + add) + success + ;; + remove) + # TODO + exit 0 + ;; +esac + +exit 0 diff --git a/tools/examples/vtpm b/tools/examples/vtpm new file mode 100644 index 0000000..38a4532 --- /dev/null +++ b/tools/examples/vtpm @@ -0,0 +1,22 @@ +#!/bin/bash + +dir=$(dirname "$0") +. "$dir/vtpm-hotplug-common.sh" + +vtpm_fatal_error=0 + +case "$command" in + add) + vtpm_create_instance + ;; + remove) + vtpm_remove_instance + ;; +esac + +if [ $vtpm_fatal_error -eq 0 ]; then + log debug "Successful vTPM operation '$command'." + success +else + fatal "Error while executing vTPM operation '$command'." +fi diff --git a/tools/examples/vtpm-common.sh b/tools/examples/vtpm-common.sh new file mode 100644 index 0000000..a45868e --- /dev/null +++ b/tools/examples/vtpm-common.sh @@ -0,0 +1,448 @@ +# +# Copyright (c) 2005 IBM Corporation +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +dir=$(dirname "$0") +. "$dir/logging.sh" +. "$dir/locking.sh" + +VTPMDB="/var/vtpm/vtpm.db" + +#In the vtpm-impl file some commands should be defined: +# vtpm_create, vtpm_setup, vtpm_start, etc. (see below) +if [ -r "$dir/vtpm-impl.alt" ]; then + . "$dir/vtpm-impl.alt" +elif [ -r "$dir/vtpm-impl" ]; then + . "$dir/vtpm-impl" +else + function vtpm_create () { + true + } + function vtpm_setup() { + true + } + function vtpm_start() { + true + } + function vtpm_suspend() { + true + } + function vtpm_resume() { + true + } + function vtpm_delete() { + true + } + function vtpm_migrate() { + echo "Error: vTPM migration accross machines not implemented." + } + function vtpm_migrate_local() { + echo "Error: local vTPM migration not supported" + } + function vtpm_migrate_recover() { + true + } +fi + + +#Find the instance number for the vtpm given the name of the domain +# Parameters +# - vmname : the name of the vm +# Return value +# Returns '0' if instance number could not be found, otherwise +# it returns the instance number in the variable 'instance' +function vtpmdb_find_instance () { + local vmname ret instance + vmname=$1 + ret=0 + + instance=$(cat $VTPMDB | \ + awk -vvmname=$vmname \ + '{ \ + if ( 1 != index($1,"#")) { \ + if ( $1 == vmname ) { \ + print $2; \ + exit; \ + } \ + } \ + }') + if [ "$instance" != "" ]; then + ret=$instance + fi + echo "$ret" +} + + +# Check whether a particular instance number is still available +# returns "0" if it is not available, "1" otherwise. +function vtpmdb_is_free_instancenum () { + local instance instances avail i + instance=$1 + avail=1 + #Allowed instance number range: 1-255 + if [ $instance -eq 0 -o $instance -gt 255 ]; then + avail=0 + else + instances=$(cat $VTPMDB | \ + gawk \ + '{ \ + if (1 != index($1,"#")) { \ + printf("%s ",$2); \ + } \ + }') + for i in $instances; do + if [ $i -eq $instance ]; then + avail=0 + break + fi + done + fi + echo "$avail" +} + + +# Get an available instance number given the database +# Returns an unused instance number +function vtpmdb_get_free_instancenum () { + local ctr instances don found + instances=$(cat $VTPMDB | \ + gawk \ + '{ \ + if (1 != index($1,"#")) { \ + printf("%s ",$2); \ + } \ + }') + ctr=1 + don=0 + while [ $don -eq 0 ]; do + found=0 + for i in $instances; do + if [ $i -eq $ctr ]; then + found=1; + break; + fi + done + + if [ $found -eq 0 ]; then + don=1 + break + fi + let ctr=ctr+1 + done + echo "$ctr" +} + + +# Add a domain name and instance number to the DB file +function vtpmdb_add_instance () { + local res vmname inst + vmname=$1 + inst=$2 + + if [ ! -f $VTPMDB ]; then + echo "#Database for VM to vTPM association" > $VTPMDB + echo "#1st column: domain name" >> $VTPMDB + echo "#2nd column: TPM instance number" >> $VTPMDB + fi + res=$(vtpmdb_validate_entry $vmname $inst) + if [ $res -eq 0 ]; then + echo "$vmname $inst" >> $VTPMDB + fi +} + + +#Validate whether an entry is the same as passed to this +#function +function vtpmdb_validate_entry () { + local res rc vmname inst + rc=0 + vmname=$1 + inst=$2 + + res=$(cat $VTPMDB | \ + gawk -vvmname=$vmname \ + -vinst=$inst \ + '{ \ + if ( 1 == index($1,"#")) {\ + } else \ + if ( $1 == vmname && \ + $2 == inst) { \ + printf("1"); \ + exit; \ + } else \ + if ( $1 == vmname || \ + $2 == inst) { \ + printf("2"); \ + exit; \ + } \ + }') + + if [ "$res" == "1" ]; then + rc=1 + elif [ "$res" == "2" ]; then + rc=2 + fi + echo "$rc" +} + + +#Remove an entry from the vTPM database given its domain name +#and instance number +function vtpmdb_remove_entry () { + local vmname instance VTPMDB_TMP + vmname=$1 + instance=$2 + VTPMDB_TMP="$VTPMDB".tmp + + $(cat $VTPMDB | \ + gawk -vvmname=$vmname \ + '{ \ + if ( $1 != vmname ) { \ + print $0; \ + } \ + '} > $VTPMDB_TMP) + if [ -e $VTPMDB_TMP ]; then + mv -f $VTPMDB_TMP $VTPMDB + vtpm_delete $instance + else + log err "Error creating temporary file '$VTPMDB_TMP'." + fi +} + + +# Find the reason for the creation of this device: +# Returns 'resume' or 'create' +function vtpm_get_create_reason () { + local resume + resume=$(xenstore_read $XENBUS_PATH/resume) + if [ "$resume" == "True" ]; then + echo "resume" + else + echo "create" + fi +} + + +#Create a vTPM instance +# If no entry in the TPM database is found, the instance is +# created and an entry added to the database. +function vtpm_create_instance () { + local res instance domname reason uuid + uuid=$(xenstore_read "$XENBUS_PATH"/uuid) + reason=$(vtpm_get_create_reason) + + claim_lock vtpmdb + + instance="0" + + if [ "$uuid" != "" ]; then + instance=$(vtpmdb_find_instance $uuid) + fi + if [ "$instance" == "0" ]; then + domname=$(xenstore_read "$XENBUS_PATH"/domain) + instance=$(vtpmdb_find_instance $domname) + fi + + if [ "$instance" == "0" -a "$reason" != "create" ]; then + release_lock vtpmdb + return + fi + + if [ "$instance" == "0" ]; then + #Try to give the preferred instance to the domain + instance=$(xenstore_read "$XENBUS_PATH"/pref_instance) + if [ "$instance" != "" ]; then + res=$(vtpmdb_is_free_instancenum $instance) + if [ $res -eq 0 ]; then + instance=$(vtpmdb_get_free_instancenum) + fi + else + instance=$(vtpmdb_get_free_instancenum) + fi + + vtpm_create $instance + + if [ $vtpm_fatal_error -eq 0 ]; then + if [ "$uuid" != "" ]; then + vtpmdb_add_instance $uuid $instance + else + vtpmdb_add_instance $domname $instance + fi + fi + else + if [ "$reason" == "resume" ]; then + vtpm_resume $instance + else + vtpm_start $instance + fi + fi + + release_lock vtpmdb + + xenstore_write $XENBUS_PATH/instance $instance +} + + +#Remove an instance when a VM is terminating or suspending. +#Since it is assumed that the VM will appear again, the +#entry is kept in the VTPMDB file. +function vtpm_remove_instance () { + local instance reason domname uuid + #Stop script execution quietly if path does not exist (anymore) + xenstore-exists "$XENBUS_PATH"/domain + uuid=$(xenstore_read "$XENBUS_PATH"/uuid) + + claim_lock vtpmdb + + instance="0" + + if [ "$uuid" != "" ]; then + instance=$(vtpmdb_find_instance $uuid) + fi + + if [ "$instance" == "0" ]; then + domname=$(xenstore_read "$XENBUS_PATH"/domain) + instance=$(vtpmdb_find_instance $domname) + fi + + if [ "$instance" != "0" ]; then + vtpm_suspend $instance + fi + + release_lock vtpmdb +} + + +#Remove an entry in the VTPMDB file given the domain's name +#1st parameter: The name of the domain +function vtpm_delete_instance () { + local instance + + claim_lock vtpmdb + + instance=$(vtpmdb_find_instance $1) + if [ "$instance" != "0" ]; then + vtpmdb_remove_entry $1 $instance + fi + + release_lock vtpmdb +} + +# Determine whether the given address is local to this machine +# Return values: +# "-1" : the given machine name is invalid +# "0" : this is not an address of this machine +# "1" : this is an address local to this machine +function vtpm_isLocalAddress() { + local addr res + addr=$(ping $1 -c 1 | \ + gawk '{ print substr($3,2,length($3)-2); exit }') + if [ "$addr" == "" ]; then + echo "-1" + return + fi + res=$(ifconfig | grep "inet addr" | \ + gawk -vaddr=$addr \ + '{ \ + if ( addr == substr($2, 6)) {\ + print "1"; \ + } \ + }' \ + ) + if [ "$res" == "" ]; then + echo "0" + return + fi + echo "1" +} + +# Perform a migration step. This function differentiates between migration +# to the local host or to a remote machine. +# Parameters: +# 1st: destination host to migrate to +# 2nd: name of the domain to migrate +# 3rd: the migration step to perform +function vtpm_migration_step() { + local res=$(vtpm_isLocalAddress $1) + if [ "$res" == "0" ]; then + vtpm_migrate $1 $2 $3 + else + vtpm_migrate_local + fi +} + +# Recover from migration due to an error. This function differentiates +# between migration to the local host or to a remote machine. +# Parameters: +# 1st: destination host the migration was going to +# 2nd: name of the domain that was to be migrated +# 3rd: the last successful migration step that was done +function vtpm_recover() { + local res + res=$(vtpm_isLocalAddress $1) + if [ "$res" == "0" ]; then + vtpm_migrate_recover $1 $2 $3 + fi +} + + +#Determine the domain id given a domain's name. +#1st parameter: name of the domain +#return value: domain id or -1 if domain id could not be determined +function vtpm_domid_from_name () { + local id name ids + ids=$(xenstore-list /local/domain) + for id in $ids; do + name=$(xenstore-read /local/domain/$id/name) + if [ "$name" == "$1" ]; then + echo "$id" + return + fi + done + echo "-1" +} + +#Determine the virtual TPM's instance number using the domain ID. +#1st parm: domain ID +function vtpm_uuid_by_domid() { + echo $(xenstore-read /local/domain/0/backend/vtpm/$1/0/uuid) +} + + +# Determine the vTPM's UUID by the name of the VM +function vtpm_uuid_from_vmname() { + local domid=$(vtpm_domid_from_name $1) + if [ "$domid" != "-1" ]; then + echo $(vtpm_uuid_by_domid $domid) + return + fi + echo "" +} + +#Add a virtual TPM instance number and its associated domain name +#to the VTPMDB file and activate usage of this virtual TPM instance +#by writing the instance number into the xenstore +#1st parm: name of virtual machine +#2nd parm: instance of associated virtual TPM +function vtpm_add_and_activate() { + local domid=$(vtpm_domid_from_name $1) + local vtpm_uuid=$(vtpm_uuid_from_vmname $1) + if [ "$vtpm_uuid" != "" -a "$domid" != "-1" ]; then + vtpmdb_add_instance $vtpm_uuid $2 + xenstore-write backend/vtpm/$domid/0/instance $2 + fi +} diff --git a/tools/examples/vtpm-delete b/tools/examples/vtpm-delete new file mode 100644 index 0000000..b75b95b --- /dev/null +++ b/tools/examples/vtpm-delete @@ -0,0 +1,18 @@ +#!/bin/bash + +# This scripts must be called the following way: +# vtpm-delete +# or +# vtpm-delete --vmname + +dir=$(dirname "$0") +. "$dir/vtpm-common.sh" + +if [ "$1" == "--vmname" ]; then + vtpm_uuid=$(vtpm_uuid_from_vmname $2) + if [ "$vtpm_uuid" != "" ];then + vtpm_delete_instance $vtpm_uuid + fi +else + vtpm_delete_instance $1 +fi diff --git a/tools/examples/vtpm-hotplug-common.sh b/tools/examples/vtpm-hotplug-common.sh new file mode 100644 index 0000000..9fd35e7 --- /dev/null +++ b/tools/examples/vtpm-hotplug-common.sh @@ -0,0 +1,35 @@ +# +# Copyright (c) 2005 IBM Corporation +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" + +findCommand "$@" +if [ "$command" != "online" ] && + [ "$command" != "offline" ] && + [ "$command" != "add" ] && + [ "$command" != "remove" ] +then + log err "Invalid command: $command" + exit 1 +fi + + +XENBUS_PATH="${XENBUS_PATH:?}" + +. "$dir/vtpm-common.sh" diff --git a/tools/examples/vtpm-impl b/tools/examples/vtpm-impl new file mode 100644 index 0000000..4f9a1fd --- /dev/null +++ b/tools/examples/vtpm-impl @@ -0,0 +1,208 @@ +#!/bin/bash +# =================================================================== +# +# Copyright (c) 2005, Intel Corp. +# 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 Intel Corporation nor the names of its +# 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. +# =================================================================== + +# | SRC | TAG | CMD SIZE | ORD |mtype|strt +TPM_CMD_OPEN=\\x00\\x00\\x00\\x00\\x01\\xc1\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\x01\\x01\\x01 +TPM_CMD_RESM=\\x00\\x00\\x00\\x00\\x01\\xc1\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\x01\\x01\\x02 +TPM_CMD_CLOS=\\x00\\x00\\x00\\x00\\x01\\xc1\\x00\\x00\\x00\\x0e\\x01\\x00\\x00\\x02 +TPM_CMD_DELE=\\x00\\x00\\x00\\x00\\x01\\xc1\\x00\\x00\\x00\\x0e\\x01\\x00\\x00\\x03 + +TPM_TYPE_PVM=\\x01 +TPM_TYPE_HVM=\\x02 + +TPM_SUCCESS=00000000 + +TX_VTPM_MANAGER=/var/vtpm/fifos/from_console.fifo +RX_VTPM_MANAGER=/var/vtpm/fifos/to_console.fifo + +VTPM_MIG=/usr/bin/vtpm_migrator + +# -------------------- Helpers for binary streams ----------- + +function str_to_hex32() { + printf "%0.8x" $1 +} + +function hex32_to_bin() { + local inst=$(str_to_hex32 $1); + + local n1=`echo $inst | sed 's/\(..\)....../\\\\x\1/'` + local n2=`echo $inst | sed 's/..\(..\)..../\\\\x\1/'` + local n3=`echo $inst | sed 's/....\(..\)../\\\\x\1/'` + local n4=`echo $inst | sed 's/......\(..\)/\\\\x\1/'` + + echo "$n1$n2$n3$n4" +} + +function vtpm_manager_cmd() { + local cmd=$1; + local inst=$2; + local inst_bin=$(hex32_to_bin $inst); + + claim_lock vtpm_mgr + + #send cmd to vtpm_manager + printf "$cmd$inst_bin" > $TX_VTPM_MANAGER + + #recv response + set +e + local resp_hex=`dd skip=10 bs=1 count=4 if=$RX_VTPM_MANAGER 2> /dev/null | xxd -ps` + set -e + + release_lock vtpm_mgr + + #return whether the command was successful + if [ $resp_hex -ne $TPM_SUCCESS ]; then + vtpm_fatal_error=1 + false + else + true + fi +} + +# Helper to get vm type to pass to vtpm_manager open/resume +function vtpm_get_type() { + local inst=$(xenstore_read $XENBUS_PATH/frontend-id) + local vm=$(xenstore_read /local/domain/$inst/vm) + if [ "$vm" != "" ]; then + local ostype=$(xenstore-read $vm/image/ostype) + if [ "$ostype" == "hvm" ]; then + echo $TPM_TYPE_HVM; + else + echo $TPM_TYPE_PVM; + fi + fi +} + +# ------------------ Command handlers ----------------- + +# Create new vtpm instance & set it up for use +function vtpm_create () { + # Creation is handled implicitly by the manager on first setup + # so just set it up for use + $(vtpm_start $1) +} + +# Setup vtpm instance for use. +function vtpm_start() { + local vmtype=$(vtpm_get_type); + $(vtpm_manager_cmd $TPM_CMD_OPEN$vmtype $1) +} + +function vtpm_resume() { + local vmtype=$(vtpm_get_type); + $(vtpm_manager_cmd $TPM_CMD_RESM$vmtype $1) +} + +# Reset the vtpm AKA clear PCRs +function vtpm_reset() { + #not used by current implemenation + true +} + +# Shutdown the vtpm while the vm is down +# This could be a suspend of shutdown +# we cannot distinquish, so save the state +# and decide on startup if we should keep is +function vtpm_suspend() { + $(vtpm_manager_cmd $TPM_CMD_CLOS $1) +} + + +function vtpm_delete() { + local inst=$1 + if $(vtpm_manager_cmd $TPM_CMD_DELE $inst); then + rm -f /var/vtpm/vtpm_dm_$1.data + true + else + vtpm_fatal_error=1 + false + fi +} + +# Perform a migration step. This function differentiates between migration +# to the local host or to a remote machine. +# Parameters: +# 1st: destination host to migrate to +# 2nd: name of the domain to migrate +# 3rd: the migration step to perform +function vtpm_migrate() { + local instance res + + instance=$(vtpmdb_find_instance $2) + if [ "$instance" == "" ]; then + log err "VTPM Migratoin failed. Unable to translation of domain name" + echo "Error: VTPM Migration failed while looking up instance number" + fi + + case "$3" in + 0) + #Incicate migration supported + echo "0" + ;; + + 1) + # Get Public Key from Destination + # Call vtpm_manager's migration part 1 + claim_lock vtpm_mgr + $VTPM_MIG $1 $2 $instance $3 + release_lock vtpm_mgr + ;; + + 2) + # Call manager's migration step 2 and send result to destination + # If successful remove from db + claim_lock vtpm_mgr + $VTPM_MIG $1 $2 $instance $3 + release_lock vtpm_mgr + ;; + + 3) + if `ps x | grep "$VTPM_MIG $1"`; then + log err "VTPM Migration failed to complete." + echo "Error: VTPM Migration failed to complete." + fi + ;; + esac + +} + + +function vtpm_migrate_recover() { + echo "Error: Recovery not supported yet" +} + +function vtpm_migrate_local() { + echo "Error: local vTPM migration not supported" +} diff --git a/tools/examples/vtpm-migration.sh b/tools/examples/vtpm-migration.sh new file mode 100644 index 0000000..7e38ae2 --- /dev/null +++ b/tools/examples/vtpm-migration.sh @@ -0,0 +1,19 @@ +# +# Copyright (c) 2005 IBM Corporation +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +dir=$(dirname "$0") +. "$dir/vtpm-common.sh" diff --git a/tools/examples/xen-backend.agent b/tools/examples/xen-backend.agent new file mode 100755 index 0000000..5cb536a --- /dev/null +++ b/tools/examples/xen-backend.agent @@ -0,0 +1,39 @@ +#! /bin/bash + +PATH=/etc/xen/scripts:$PATH + +. /etc/xen/scripts/locking.sh + +claim_lock xenbus_hotplug_global + +case "$XENBUS_TYPE" in + tap) + /etc/xen/scripts/blktap "$ACTION" + ;; + vbd) + /etc/xen/scripts/block "$ACTION" + ;; + vtpm) + /etc/xen/scripts/vtpm "$ACTION" + ;; + vif) + [ -n "$script" ] && $script "$ACTION" + ;; + vscsi) + /etc/xen/scripts/vscsi "$ACTION" + ;; +esac + +case "$ACTION" in + add) + ;; + remove) + /etc/xen/scripts/xen-hotplug-cleanup + ;; + online) + ;; + offline) + ;; +esac + +release_lock xenbus_hotplug_global diff --git a/tools/examples/xen-backend.rules b/tools/examples/xen-backend.rules new file mode 100644 index 0000000..fe21fc1 --- /dev/null +++ b/tools/examples/xen-backend.rules @@ -0,0 +1,9 @@ +SUBSYSTEM=="xen-backend", KERNEL=="tap*", RUN+="/etc/xen/scripts/blktap $env{ACTION}" +SUBSYSTEM=="xen-backend", KERNEL=="vbd*", RUN+="/etc/xen/scripts/block $env{ACTION}" +SUBSYSTEM=="xen-backend", KERNEL=="vtpm*", RUN+="/etc/xen/scripts/vtpm $env{ACTION}" +SUBSYSTEM=="xen-backend", KERNEL=="vif*", ACTION=="online", RUN+="$env{script} online" +SUBSYSTEM=="xen-backend", KERNEL=="vif*", ACTION=="offline", RUN+="$env{script} offline" +SUBSYSTEM=="xen-backend", KERNEL=="vscsi*", RUN+="/etc/xen/scripts/vscsi $env{ACTION}" +SUBSYSTEM=="xen-backend", ACTION=="remove", RUN+="/etc/xen/scripts/xen-hotplug-cleanup" +KERNEL=="evtchn", NAME="xen/%k" +KERNEL=="blktap[0-9]*", NAME="xen/%k" diff --git a/tools/examples/xen-hotplug-cleanup b/tools/examples/xen-hotplug-cleanup new file mode 100644 index 0000000..f7337e4 --- /dev/null +++ b/tools/examples/xen-hotplug-cleanup @@ -0,0 +1,22 @@ +#! /bin/bash + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" + +# Claim the lock protecting /etc/xen/scripts/block. This stops a race whereby +# paths in the store would disappear underneath that script as it attempted to +# read from the store checking for device sharing. +# Any other scripts that do similar things will have to have their lock +# claimed too. +# This is pretty horrible, but there's not really a nicer way of solving this. +claim_lock "block" + +# remove device frontend store entries +xenstore-rm -t \ + $(xenstore-read "$XENBUS_PATH/frontend" 2>/dev/null) 2>/dev/null || true + +# remove device backend store entries +xenstore-rm -t "$XENBUS_PATH" 2>/dev/null || true +xenstore-rm -t "error/$XENBUS_PATH" 2>/dev/null || true + +release_lock "block" diff --git a/tools/examples/xen-hotplug-common.sh b/tools/examples/xen-hotplug-common.sh new file mode 100644 index 0000000..980a627 --- /dev/null +++ b/tools/examples/xen-hotplug-common.sh @@ -0,0 +1,93 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + + +dir=$(dirname "$0") +. "$dir/logging.sh" +. "$dir/xen-script-common.sh" +. "$dir/locking.sh" + +exec 2>>/var/log/xen/xen-hotplug.log + +export PATH="/sbin:/bin:/usr/bin:/usr/sbin:$PATH" +export LANG="POSIX" +unset $(set | grep ^LC_ | cut -d= -f1) + +fatal() { + xenstore_write "$XENBUS_PATH/hotplug-error" "$*" \ + "$XENBUS_PATH/hotplug-status" error + log err "$@" + exit 1 +} + +success() { + # Tell DevController that backend is "connected" + xenstore_write "$XENBUS_PATH/hotplug-status" connected +} + +do_or_die() { + "$@" || fatal "$@ failed" +} + +do_without_error() { + "$@" 2>/dev/null || log debug "$@ failed" +} + +sigerr() { + fatal "$0 failed; error detected." +} + +trap sigerr ERR + + +## +# xenstore_read + +# +# Read each of the given paths, returning each result on a separate line, or +# exit this script if any of the paths is missing. +# +xenstore_read() { + local v=$(xenstore-read "$@" || true) + [ "$v" != "" ] || fatal "xenstore-read $@ failed." + echo "$v" +} + + +## +# xenstore_read_default +# +# Read the given path, returning the value there or the given default if the +# path is not present. +# +xenstore_read_default() { + xenstore-read "$1" 2>/dev/null || echo "$2" +} + + +## +# xenstore_write ( )+ +# +# Write each of the key/value pairs to the store, and exit this script if any +# such writing fails. +# +xenstore_write() { + log debug "Writing $@ to xenstore." + xenstore-write "$@" || fatal "Writing $@ to xenstore failed." +} + + +log debug "$@" "XENBUS_PATH=$XENBUS_PATH" diff --git a/tools/examples/xen-network-common.sh b/tools/examples/xen-network-common.sh new file mode 100644 index 0000000..7014333 --- /dev/null +++ b/tools/examples/xen-network-common.sh @@ -0,0 +1,118 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + + +# Gentoo doesn't have ifup/ifdown, so we define appropriate alternatives. + +# Other platforms just use ifup / ifdown directly. + +## +# preiftransfer +# +# @param $1 The current name for the physical device, which is also the name +# that the virtual device will take once the physical device has +# been renamed. + +if ! which ifup >/dev/null 2>/dev/null +then + preiftransfer() + { + true + } + ifup() + { + false + } + ifdown() + { + false + } +else + preiftransfer() + { + true + } +fi + + +first_file() +{ + t="$1" + shift + for file in $@ + do + if [ "$t" "$file" ] + then + echo "$file" + return + fi + done +} + +find_dhcpd_conf_file() +{ + first_file -f /etc/dhcp3/dhcpd.conf /etc/dhcpd.conf +} + + +find_dhcpd_init_file() +{ + first_file -x /etc/init.d/{dhcp3-server,dhcp,dhcpd} +} + +find_dhcpd_arg_file() +{ + first_file -f /etc/sysconfig/dhcpd /etc/defaults/dhcp /etc/default/dhcp3-server +} + +# configure interfaces which act as pure bridge ports: +setup_bridge_port() { + local dev="$1" + + # take interface down ... + ip link set ${dev} down + + # ... and configure it + ip addr flush ${dev} +} + +# Usage: create_bridge bridge +create_bridge () { + local bridge=$1 + + # Don't create the bridge if it already exists. + if [ ! -e "/sys/class/net/${bridge}/bridge" ]; then + brctl addbr ${bridge} + brctl stp ${bridge} off + brctl setfd ${bridge} 0 + fi +} + +# Usage: add_to_bridge bridge dev +add_to_bridge () { + local bridge=$1 + local dev=$2 + + # Don't add $dev to $bridge if it's already on a bridge. + if [ -e "/sys/class/net/${bridge}/brif/${dev}" ]; then + ip link set ${dev} up || true + return + fi + brctl addif ${bridge} ${dev} + ip link set ${dev} up +} + diff --git a/tools/examples/xen-script-common.sh b/tools/examples/xen-script-common.sh new file mode 100644 index 0000000..f6841ac --- /dev/null +++ b/tools/examples/xen-script-common.sh @@ -0,0 +1,44 @@ +# +# Copyright (c) 2005 XenSource Ltd. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + + +set -e + + +evalVariables() +{ + for arg in "$@" + do + if expr 'index' "$arg" '=' '>' '1' >/dev/null + then + eval "$arg" + fi + done +} + + +findCommand() +{ + for arg in "$@" + do + if ! expr 'index' "$arg" '=' >/dev/null + then + command="$arg" + return + fi + done +} diff --git a/tools/examples/xend-config.sxp b/tools/examples/xend-config.sxp new file mode 100644 index 0000000..5465c39 --- /dev/null +++ b/tools/examples/xend-config.sxp @@ -0,0 +1,255 @@ +# -*- sh -*- + +# +# Xend configuration file. +# + +# This example configuration is appropriate for an installation that +# utilizes a bridged network configuration. Access to xend via http +# is disabled. + +# Commented out entries show the default for that entry, unless otherwise +# specified. + +#(logfile /var/log/xen/xend.log) +#(loglevel DEBUG) + +# Uncomment the line below. Set the value to flask, acm, or dummy to +# select a security module. + +#(xsm_module_name dummy) + +# The Xen-API server configuration. +# +# This value configures the ports, interfaces, and access controls for the +# Xen-API server. Each entry in the list starts with either unix, a port +# number, or an address:port pair. If this is "unix", then a UDP socket is +# opened, and this entry applies to that. If it is a port, then Xend will +# listen on all interfaces on that TCP port, and if it is an address:port +# pair, then Xend will listen on the specified port, using the interface with +# the specified address. +# +# The subsequent string configures the user-based access control for the +# listener in question. This can be one of "none" or "pam", indicating either +# that users should be allowed access unconditionally, or that the local +# Pluggable Authentication Modules configuration should be used. If this +# string is missing or empty, then "pam" is used. +# +# The final string gives the host-based access control for that listener. If +# this is missing or empty, then all connections are accepted. Otherwise, +# this should be a space-separated sequence of regular expressions; any host +# with a fully-qualified domain name or an IP address that matches one of +# these regular expressions will be accepted. +# +# Example: listen on TCP port 9363 on all interfaces, accepting connections +# only from machines in example.com or localhost, and allow access through +# the unix domain socket unconditionally: +# +# (xen-api-server ((9363 pam '^localhost$ example\\.com$') +# (unix none))) +# +# Optionally, the TCP Xen-API server can use SSL by specifying the private +# key and certificate location: +# +# (9367 pam '' /etc/xen/xen-api.key /etc/xen/xen-api.crt) +# +# Default: +# (xen-api-server ((unix))) + + +#(xend-http-server no) +#(xend-unix-server no) +#(xend-tcp-xmlrpc-server no) +#(xend-unix-xmlrpc-server yes) +#(xend-relocation-server no) +(xend-relocation-server yes) +#(xend-relocation-ssl-server no) + +#(xend-unix-path /var/lib/xend/xend-socket) + + +# Address and port xend should use for the legacy TCP XMLRPC interface, +# if xend-tcp-xmlrpc-server is set. +#(xend-tcp-xmlrpc-server-address 'localhost') +#(xend-tcp-xmlrpc-server-port 8006) + +# SSL key and certificate to use for the legacy TCP XMLRPC interface. +# Setting these will mean that this port serves only SSL connections as +# opposed to plaintext ones. +#(xend-tcp-xmlrpc-server-ssl-key-file /etc/xen/xmlrpc.key) +#(xend-tcp-xmlrpc-server-ssl-cert-file /etc/xen/xmlrpc.crt) + + +# Port xend should use for the HTTP interface, if xend-http-server is set. +#(xend-port 8000) + +# Port xend should use for the relocation interface, if xend-relocation-server +# is set. +#(xend-relocation-port 8002) + +# Port xend should use for the ssl relocation interface, if +# xend-relocation-ssl-server is set. +#(xend-relocation-ssl-port 8003) + +# SSL key and certificate to use for the ssl relocation interface, if +# xend-relocation-ssl-server is set. +#(xend-relocation-server-ssl-key-file /etc/xen/xmlrpc.key) +#(xend-relocation-server-ssl-cert-file /etc/xen/xmlrpc.crt) + +# Whether to use ssl as default when relocating. +#(xend-relocation-ssl no) + +# Address xend should listen on for HTTP connections, if xend-http-server is +# set. +# Specifying 'localhost' prevents remote connections. +# Specifying the empty string '' (the default) allows all connections. +#(xend-address '') +#(xend-address localhost) + +# Address xend should listen on for relocation-socket connections, if +# xend-relocation-server is set. +# Meaning and default as for xend-address above. +#(xend-relocation-address '') + +# The hosts allowed to talk to the relocation port. If this is empty (the +# default), then all connections are allowed (assuming that the connection +# arrives on a port and interface on which we are listening; see +# xend-relocation-port and xend-relocation-address above). Otherwise, this +# should be a space-separated sequence of regular expressions. Any host with +# a fully-qualified domain name or an IP address that matches one of these +# regular expressions will be accepted. +# +# For example: +# (xend-relocation-hosts-allow '^localhost$ ^.*\\.example\\.org$') +# +#(xend-relocation-hosts-allow '') +(xend-relocation-hosts-allow '^localhost$ ^localhost\\.localdomain$') + +# The limit (in kilobytes) on the size of the console buffer +#(console-limit 1024) + +## +# To bridge network traffic, like this: +# +# dom0: ----------------- bridge -> real eth0 -> the network +# | +# domU: fake eth0 -> vifN.0 -+ +# +# use +# +# (network-script network-bridge) +# +# Your default ethernet device is used as the outgoing interface, by default. +# To use a different one (e.g. eth1) use +# +# (network-script 'network-bridge netdev=eth1') +# +# The bridge is named xenbr0, by default. To rename the bridge, use +# +# (network-script 'network-bridge bridge=') +# +# It is possible to use the network-bridge script in more complicated +# scenarios, such as having two outgoing interfaces, with two bridges, and +# two fake interfaces per guest domain. To do things like this, write +# yourself a wrapper script, and call network-bridge from it, as appropriate. +# +(network-script network-bridge) + +# The script used to control virtual interfaces. This can be overridden on a +# per-vif basis when creating a domain or a configuring a new vif. The +# vif-bridge script is designed for use with the network-bridge script, or +# similar configurations. +# +# If you have overridden the bridge name using +# (network-script 'network-bridge bridge=') then you may wish to do the +# same here. The bridge name can also be set when creating a domain or +# configuring a new vif, but a value specified here would act as a default. +# +# If you are using only one bridge, the vif-bridge script will discover that, +# so there is no need to specify it explicitly. +# +(vif-script vif-bridge) + + +## Use the following if network traffic is routed, as an alternative to the +# settings for bridged networking given above. +#(network-script network-route) +#(vif-script vif-route) + + +## Use the following if network traffic is routed with NAT, as an alternative +# to the settings for bridged networking given above. +#(network-script network-nat) +#(vif-script vif-nat) + +# dom0-min-mem is the lowest permissible memory level (in MB) for dom0. +# This is a minimum both for auto-ballooning (as enabled by +# enable-dom0-ballooning below) and for xm mem-set when applied to dom0. +(dom0-min-mem 196) + +# Whether to enable auto-ballooning of dom0 to allow domUs to be created. +# If enable-dom0-ballooning = no, dom0 will never balloon out. +(enable-dom0-ballooning yes) + +# In SMP system, dom0 will use dom0-cpus # of CPUS +# If dom0-cpus = 0, dom0 will take all cpus available +(dom0-cpus 0) + +# Whether to enable core-dumps when domains crash. +#(enable-dump no) + +# The tool used for initiating virtual TPM migration +#(external-migration-tool '') + +# The interface for VNC servers to listen on. Defaults +# to 127.0.0.1 To restore old 'listen everywhere' behaviour +# set this to 0.0.0.0 +#(vnc-listen '127.0.0.1') + +# The default password for VNC console on HVM domain. +# Empty string is no authentication. +(vncpasswd '') + +# The VNC server can be told to negotiate a TLS session +# to encryption all traffic, and provide x509 cert to +# clients enalbing them to verify server identity. The +# GTK-VNC widget, virt-viewer, virt-manager and VeNCrypt +# all support the VNC extension for TLS used in QEMU. The +# TightVNC/RealVNC/UltraVNC clients do not. +# +# To enable this create x509 certificates / keys in the +# directory /etc/xen/vnc +# +# ca-cert.pem - The CA certificate +# server-cert.pem - The Server certificate signed by the CA +# server-key.pem - The server private key +# +# and then uncomment this next line +# (vnc-tls 1) + +# The certificate dir can be pointed elsewhere.. +# +# (vnc-x509-cert-dir /etc/xen/vnc) + +# The server can be told to request & validate an x509 +# certificate from the client. Only clients with a cert +# signed by the trusted CA will be able to connect. This +# is more secure the password auth alone. Passwd auth can +# used at the same time if desired. To enable client cert +# checking uncomment this: +# +# (vnc-x509-verify 1) + +# The default keymap to use for the VM's virtual keyboard +# when not specififed in VM's configuration +#(keymap 'en-us') + +# Script to run when the label of a resource has changed. +#(resource-label-change-script '') + +# Rotation count of qemu-dm log file. +#(qemu-dm-logrotate-count 10) + +# Path where persistent domain configuration is stored. +# Default is /var/lib/xend/domains/ +#(xend-domains-path /var/lib/xend/domains) diff --git a/tools/examples/xend-pci-permissive.sxp b/tools/examples/xend-pci-permissive.sxp new file mode 100644 index 0000000..1a3fb90 --- /dev/null +++ b/tools/examples/xend-pci-permissive.sxp @@ -0,0 +1,27 @@ +############################################################################### +# Configuration file for granting quiry PCI devices full write access to their +# configuration space. This file should only be used when you are unable to +# determine the exact registers required by your device. Even so, it should +# be used only temporarily. +# +# SEND A MESSAGE TO xen-devel@lists.xensource.com IF YOU USE THIS FILE. +# +# Using this file should NOT be necessary. If you must use it to make some +# device work, send a message to the above list with as much information about +# your device as possible so the developers can make accomodations for it. +# Once developers make the necessary updates you can remove the corresponding +# entry for your device. +############################################################################### +# Entries are formated as follows: :[::] +# +# Example: Appending to an existing list +# +# (unconstrained_dev_ids +# ('XXXX:XXXX:XXXX:XXXX' # existing entry +# 'YYYY:YYYY:YYYY:YYYY' # new entry 1 +# 'ZZZZ:ZZZZ') # new entry 2 +# ) +############################################################################### +(unconstrained_dev_ids + #('0123:4567:89AB:CDEF') +) diff --git a/tools/examples/xend-pci-quirks.sxp b/tools/examples/xend-pci-quirks.sxp new file mode 100644 index 0000000..6bce4b8 --- /dev/null +++ b/tools/examples/xend-pci-quirks.sxp @@ -0,0 +1,96 @@ +############################################################################### +# Configuration file for quirky PCI devices that require write-access to +# parts of the configuration space. Use this file to specific PCI device +# IDs and the configuration space fields to which those devices must be +# able to write. +# +# Length is important, so be sure to match new entries with the +# lengths of comparable existing entries. +# +# Additions to this file take effect as soon as a new domain with a +# matching device is started. However, to remove a field that was +# previously applied to a device you must unbind the device from +# pciback. +############################################################################### +# This is a bogus entry to show how a new device would be added to the list +# +# (new_quirky_dev_name +# (pci_ids +# ('0123:4567:890A:BCEF') +# ) +# +# (pci_config_space_fields +# ('12345678:1:00000000') +# ) +# ) +############################################################################### + +(tg3 + (pci_ids + # Entries are formated as follows: + # :[::] + ('14e4:1644' # Broadcom Tigon3 5700 + '14e4:1645' # Broadcom Tigon3 5701 + '14e4:1646' # Broadcom Tigon3 5702 + '14e4:1647' # Broadcom Tigon3 5703 + '14e4:1648' # Broadcom Tigon3 5704 + '14e4:164d' # Broadcom Tigon3 5702FE + '14e4:1653' # Broadcom Tigon3 5705 + '14e4:1654' # Broadcom Tigon3 5705_2 + '14e4:165d' # Broadcom Tigon3 5705M + '14e4:165e' # Broadcom Tigon3 5705M_2 + '14e4:16a6' # Broadcom Tigon3 5702X + '14e4:16a7' # Broadcom Tigon3 5703X + '14e4:16a8' # Broadcom Tigon3 5704S + '14e4:16c6' # Broadcom Tigon3 5702A3 + '14e4:16c7' # Broadcom Tigon3 5703A3 + '14e4:1696' # Broadcom Tigon3 5782 + '14e4:169c' # Broadcom Tigon3 5788 + '14e4:169d' # Broadcom Tigon3 5789 + '14e4:170d' # Broadcom Tigon3 5901 + '14e4:1649' # Broadcom Tigon3 5704S_2 + '14e4:166e' # Broadcom Tigon3 5705F + '14e4:1658' # Broadcom Tigon3 5720 + '14e4:1659' # Broadcom Tigon3 5721 + '14e4:1676' # Broadcom Tigon3 5750 + '14e4:1677' # Broadcom Tigon3 5751 + '14e4:167c' # Broadcom Tigon3 5750M + '14e4:167d' # Broadcom Tigon3 5751M + '14e4:167e' # Broadcom Tigon3 5751F + '14e4:1600' # Broadcom Tigon3 5752 + '14e4:1601' # Broadcom Tigon3 5752M + '14e4:16f7' # Broadcom Tigon3 5753 + '14e4:16fd' # Broadcom Tigon3 5753M + '14e4:16fe' # Broadcom Tigon3 5753F + '14e4:1668' # Broadcom Tigon3 5714 + '14e4:1678' # Broadcom Tigon3 5715 + '14e4:166a' # Broadcom Tigon3 5780 + '14e4:166b' # Broadcom Tigon3 5780S + '14e4:16dd' # Broadcom Tigon3 5781 + '1148:4400' # Syskonnect 9DXX + '1148:4500' # Syskonnect 9MXX + '173b:03e8' # Altima AC1000 + '173b:03e9' # Altima AC1001 + '173b:03eb' # Altima AC1003 + '173b:03ea' # Altima AC9100 + '106b:1645') # Apple Tigon3 + ) + + (pci_config_space_fields + # Entries are formated as follows: + # :: + # size is measured in bytes (1,2,4 are valid sizes) + # mask is currently unused; use all zero's + ('00000078:4:00000000' # TG3PCI_REG_BASE_ADDR + '0000007c:4:00000000' # TG3PCI_MEM_WIN_BASE_ADDR + '00000080:4:00000000' # TG3PCI_REG_DATA + '00000084:4:00000000' # TG3PCI_MEM_WIN_DATA + '00000090:4:00000000' # TG3PCI_MISC_LOCAL_CTRL + '00000068:4:00000000' # TG3PCI_MISC_HOST_CTRL + '0000009C:4:00000000' # TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_LOW + '00000098:4:00000000' # TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_HIGH + '000000a4:4:00000000' # TG3PCI_RCV_RET_RING_CON_IDX + TG3_64BIT_REG_LOW + '000000a0:4:00000000' # TG3PCI_RCV_RET_RING_CON_IDX + TG3_64BIT_REG_HIGH + '00000070:4:00000000') # TG3PCI_PCISTATE + ) +) diff --git a/tools/examples/xeninfo.pl b/tools/examples/xeninfo.pl new file mode 100644 index 0000000..47431c6 --- /dev/null +++ b/tools/examples/xeninfo.pl @@ -0,0 +1,284 @@ +#!/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': '/usr/lib/xen/boot/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(); diff --git a/tools/examples/xm-config.xml b/tools/examples/xm-config.xml new file mode 100644 index 0000000..943b74d --- /dev/null +++ b/tools/examples/xm-config.xml @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/tools/examples/xmexample.hvm b/tools/examples/xmexample.hvm new file mode 100644 index 0000000..aa014b5 --- /dev/null +++ b/tools/examples/xmexample.hvm @@ -0,0 +1,312 @@ +# -*- mode: python; -*- +#============================================================================ +# Python configuration setup for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# You use a separate script for each domain you want to create, or +# you can set the parameters for the domain on the xm command line. +#============================================================================ + +import os, re +arch = os.uname()[4] +if re.search('64', arch): + arch_libdir = 'lib64' +else: + arch_libdir = 'lib' + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "/usr/lib/xen/boot/hvmloader" + +# The domain build function. HVM domain uses 'hvm'. +builder='hvm' + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 128 + +# Shadow pagetable memory for the domain, in MB. +# If not explicictly set, xend will pick an appropriate value. +# Should be at least 2KB per MB of domain memory, plus a few MB per vcpu. +# shadow_memory = 8 + +# A name for your domain. All domains must have different names. +name = "ExampleHVMDomain" + +# 128-bit UUID for the domain. The default behavior is to generate a new UUID +# on each call to 'xm create'. +#uuid = "06ed00fe-1162-4fc4-b5d8-11993ee4a8b9" + +#----------------------------------------------------------------------------- +# The number of cpus guest platform has, default=1 +#vcpus=1 + +# Enable/disable HVM guest PAE, default=1 (enabled) +#pae=1 + +# Enable/disable HVM guest ACPI, default=1 (enabled) +#acpi=1 + +# Enable/disable HVM APIC mode, default=1 (enabled) +# Note that this option is ignored if vcpus > 1 +#apic=1 + +# Enable/disable extended power management support within HVM guest, i.e., beyond +# S3, S4, S5 within guest like exposing battery meter. +# 0 (default option, extended power management support disabled) +# 1 (pass-through mode; uses pass-through as needed; efficient but limited in scope) +# 2 (non pass-through mode; extended scope, likely to work on all applicable environment +# but comparitively less efficient than pass-through mode) +# xen_extended_power_mgmt=0 + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 + +# Optionally define mac and/or bridge for the network interfaces. +# Random MACs are assigned if not given. +#vif = [ 'type=ioemu, mac=00:16:3e:00:00:11, bridge=xenbr0, model=ne2k_pci' ] +# type=ioemu specify the NIC is an ioemu device not netfront +vif = [ 'type=ioemu, bridge=xenbr0' ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +#disk = [ 'phy:hda1,hda1,r' ] +disk = [ 'file:/var/images/min-el3-i386.img,hda,w', ',hdc:cdrom,r' ] + +#---------------------------------------------------------------------------- +# Configure the behaviour when a domain exits. There are three 'reasons' +# for a domain to stop: poweroff, reboot, and crash. For each of these you +# may specify: +# +# "destroy", meaning that the domain is cleaned up as normal; +# "restart", meaning that a new domain is started in place of the old +# one; +# "preserve", meaning that no clean-up is done until the domain is +# manually destroyed (using xm destroy, for example); or +# "rename-restart", meaning that the old domain is not cleaned up, but is +# renamed and a new domain started in its place. +# +# In the event a domain stops due to a crash, you have the additional options: +# +# "coredump-destroy", meaning dump the crashed domain's core and then destroy; +# "coredump-restart', meaning dump the crashed domain's core and the restart. +# +# The default is +# +# on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# For backwards compatibility we also support the deprecated option restart +# +# restart = 'onreboot' means on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'destroy' +# +# restart = 'always' means on_poweroff = 'restart' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# restart = 'never' means on_poweroff = 'destroy' +# on_reboot = 'destroy' +# on_crash = 'destroy' + +#on_poweroff = 'destroy' +#on_reboot = 'restart' +#on_crash = 'restart' + +#============================================================================ + +# Device Model to be used +device_model = '/usr/' + arch_libdir + '/xen/bin/qemu-dm' + +#----------------------------------------------------------------------------- +# boot on floppy (a), hard disk (c), Network (n) or CD-ROM (d) +# default: hard disk, cd-rom, floppy +#boot="cda" + +#----------------------------------------------------------------------------- +# write to temporary files instead of disk image files +#snapshot=1 + +#---------------------------------------------------------------------------- +# enable SDL library for graphics, default = 0 +sdl=0 + +#---------------------------------------------------------------------------- +# enable OpenGL for texture rendering inside the SDL window, default = 1 +# valid only if sdl is enabled. +opengl=1 + +#---------------------------------------------------------------------------- +# enable VNC library for graphics, default = 1 +vnc=1 + +#---------------------------------------------------------------------------- +# address that should be listened on for the VNC server if vnc is set. +# default is to use 'vnc-listen' setting from /etc/xen/xend-config.sxp +#vnclisten="127.0.0.1" + +#---------------------------------------------------------------------------- +# set VNC display number, default = domid +#vncdisplay=1 + +#---------------------------------------------------------------------------- +# try to find an unused port for the VNC server, default = 1 +#vncunused=1 + +#---------------------------------------------------------------------------- +# set password for domain's VNC console +# default is depents on vncpasswd in xend-config.sxp +vncpasswd='' + +#---------------------------------------------------------------------------- +# no graphics, use serial port +#nographic=0 + +#---------------------------------------------------------------------------- +# enable stdvga, default = 0 (use cirrus logic device model) +stdvga=0 + +#----------------------------------------------------------------------------- +# serial port re-direct to pty deivce, /dev/pts/n +# then xm console or minicom can connect +serial='pty' + + +#----------------------------------------------------------------------------- +# Qemu Monitor, default is disable +# Use ctrl-alt-2 to connect +#monitor=1 + + +#----------------------------------------------------------------------------- +# enable sound card support, [sb16|es1370|all|..,..], default none +#soundhw='sb16' + + +#----------------------------------------------------------------------------- +# set the real time clock to local time [default=0 i.e. set to utc] +#localtime=1 + + +#----------------------------------------------------------------------------- +# set the real time clock offset in seconds [default=0 i.e. same as dom0] +#rtc_timeoffset=3600 + +#----------------------------------------------------------------------------- +# start in full screen +#full-screen=1 + + +#----------------------------------------------------------------------------- +# Enable USB support (specific devices specified at runtime through the +# monitor window) +#usb=1 + +# Enable USB mouse support (only enable one of the following, `mouse' for +# PS/2 protocol relative mouse, `tablet' for +# absolute mouse) +#usbdevice='mouse' +#usbdevice='tablet' + +#----------------------------------------------------------------------------- +# Set keyboard layout, default is en-us keyboard. +#keymap='ja' + +#----------------------------------------------------------------------------- +# Configure guest CPUID responses: +# +#cpuid=[ '1:ecx=xxxxxxxxxxx00xxxxxxxxxxxxxxxxxxx, +# eax=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' ] +# - Unset the SSE4 features (CPUID.1[ECX][20-19]) +# - Default behaviour for all other bits in ECX And EAX registers. +# +# Each successive character represent a lesser-significant bit: +# '1' -> force the corresponding bit to 1 +# '0' -> force to 0 +# 'x' -> Get a safe value (pass through and mask with the default policy) +# 'k' -> pass through the host bit value +# 's' -> as 'k' but preserve across save/restore and migration +# +# Expose to the guest multi-core cpu instead of multiple processors +# Example for intel, expose a 8-core processor : +#cpuid=['1:edx=xxx1xxxxxxxxxxxxxxxxxxxxxxxxxxxx, +# ebx=xxxxxxxx00010000xxxxxxxxxxxxxxxx', +# '4,0:eax=001111xxxxxxxxxxxxxxxxxxxxxxxxxx'] +# - CPUID.1[EDX][HT] : Enable HT +# - CPUID.1[EBX] : Number of vcpus * 2 +# - CPUID.4,0[EAX] : Number of vcpus * 2 - 1 +#vcpus=8 +# +# Example for amd, expose a 5-core processor : +# cpuid = ['1:ebx=xxxxxxxx00001010xxxxxxxxxxxxxxxx, +# edx=xxx1xxxxxxxxxxxxxxxxxxxxxxxxxxxx', +# '0x80000001:ecx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1x', +# '0x80000008:ecx=xxxxxxxxxxxxxxxxxxxxxxxxxx001001'] +# - CPUID.1[EBX] : Threads per Core * Cores per Socket (2 * #vcpus) +# - CPUID.1[EDX][HT] : Enable HT +# - CPUID.0x80000001[CmpLegacy] : Use legacy method +# - CPUID.0x80000008[ECX] : #vcpus * 2 - 1 +#vcpus=5 +# +# Downgrade the cpuid to make a better compatibility for migration : +# Look like a generic 686 : +# cpuid = [ '0:eax=0x3,ebx=0x0,ecx=0x0,edx=0x0', +# '1:eax=0x06b1, +# ecx=xxxxxxxxxx0000xx00xxx0000000xx0, +# edx=xx00000xxxxxxx0xxxxxxxxx0xxxxxx', +# '4:eax=0x3,ebx=0x0,ecx=0x0,edx=0x0', +# '0x80000000:eax=0x3,ebx=0x0,ecx=0x0,edx=0x0'] +# with the highest leaf +# - CPUID.0[EAX] : Set the highest leaf +# - CPUID.1[EAX] : 686 +# - CPUID.1[ECX] : Mask some features +# - CPUID.1[EDX] : Mask some features +# - CPUID.4 : Reply like the highest leaf, in our case CPUID.3 +# - CPUID.0x80000000 : No extension we are on a Pentium III, reply like the +# highest leaf (CPUID.3). +# +# Configure host CPUID consistency checks, which must be satisfied for this +# VM to be allowed to run on this host's processor type: +#cpuid_check=[ '1:ecx=xxxxxxxxxxxxxxxxxxxxxxxxxx1xxxxx' ] +# - Host must have VMX feature flag set +# +# The format is similar to the above for 'cpuid': +# '1' -> the bit must be '1' +# '0' -> the bit must be '0' +# 'x' -> we don't care (do not check) +# 's' -> the bit must be the same as on the host that started this VM + + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] diff --git a/tools/examples/xmexample.hvm-dm b/tools/examples/xmexample.hvm-dm new file mode 100644 index 0000000..de1619b --- /dev/null +++ b/tools/examples/xmexample.hvm-dm @@ -0,0 +1,14 @@ +# Not to be started directly, +# See xmexample.hvm-stubdom and stubdom/README for more details + +kernel = "/usr/lib/xen/boot/ioemu-stubdom.gz" + +# Must be the same as in xmexample.hvm-stubdom, with a prepended vif for TCP/IP +# networking in the stubdomain itself, here just '' +vif = [ '', 'type=ioemu, bridge=xenbr0' ] + +# Set here instead of in xmexample.hvm-stubdom +disk = [ 'file:/var/images/min-el3-i386.img,hda,w', ',hdc:cdrom,r' ] + +# Actual output via PVFB +vfb = [ 'type=sdl' ] diff --git a/tools/examples/xmexample.hvm-stubdom b/tools/examples/xmexample.hvm-stubdom new file mode 100644 index 0000000..bfceef3 --- /dev/null +++ b/tools/examples/xmexample.hvm-stubdom @@ -0,0 +1,320 @@ +# -*- mode: python; -*- +#============================================================================ +# Python configuration setup for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# You use a separate script for each domain you want to create, or +# you can set the parameters for the domain on the xm command line. +#============================================================================ +# +# This is a version using a stubdomain for device model, see +# xmexample.hvm-dm and README.stubdom for more details +# The differences with xmexample.hvm are marked with "STUBDOM" + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "/usr/lib/xen/boot/hvmloader" + +# The domain build function. HVM domain uses 'hvm'. +builder='hvm' + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 128 + +# Shadow pagetable memory for the domain, in MB. +# If not explicictly set, xend will pick an appropriate value. +# Should be at least 2KB per MB of domain memory, plus a few MB per vcpu. +# shadow_memory = 8 + +# A name for your domain. All domains must have different names. +name = "xmexample.hvm" + +# 128-bit UUID for the domain. The default behavior is to generate a new UUID +# on each call to 'xm create'. +#uuid = "06ed00fe-1162-4fc4-b5d8-11993ee4a8b9" + +#----------------------------------------------------------------------------- +# The number of cpus guest platform has, default=1 +#vcpus=1 + +# Enable/disable HVM guest PAE, default=1 (enabled) +#pae=1 + +# Enable/disable HVM guest ACPI, default=1 (enabled) +#acpi=1 + +# Enable/disable HVM APIC mode, default=1 (enabled) +# Note that this option is ignored if vcpus > 1 +#apic=1 + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 + +# Optionally define mac and/or bridge for the network interfaces. +# Random MACs are assigned if not given. +#vif = [ 'type=ioemu, mac=00:16:3e:00:00:11, bridge=xenbr0, model=ne2k_pci' ] +# type=ioemu specify the NIC is an ioemu device not netfront +vif = [ 'type=ioemu, bridge=xenbr0' ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. +# +# STUBDOM: do not put it here but in stubdom-ExampleHVMDomain + +#disk = [ 'phy:hda1,hda1,r' ] +#disk = [ 'file:/var/images/min-el3-i386.img,hda,w', ',hdc:cdrom,r' ] + +#---------------------------------------------------------------------------- +# Configure the behaviour when a domain exits. There are three 'reasons' +# for a domain to stop: poweroff, reboot, and crash. For each of these you +# may specify: +# +# "destroy", meaning that the domain is cleaned up as normal; +# "restart", meaning that a new domain is started in place of the old +# one; +# "preserve", meaning that no clean-up is done until the domain is +# manually destroyed (using xm destroy, for example); or +# "rename-restart", meaning that the old domain is not cleaned up, but is +# renamed and a new domain started in its place. +# +# In the event a domain stops due to a crash, you have the additional options: +# +# "coredump-destroy", meaning dump the crashed domain's core and then destroy; +# "coredump-restart', meaning dump the crashed domain's core and the restart. +# +# The default is +# +# on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# For backwards compatibility we also support the deprecated option restart +# +# restart = 'onreboot' means on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'destroy' +# +# restart = 'always' means on_poweroff = 'restart' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# restart = 'never' means on_poweroff = 'destroy' +# on_reboot = 'destroy' +# on_crash = 'destroy' + +#on_poweroff = 'destroy' +#on_reboot = 'restart' +#on_crash = 'restart' + +#============================================================================ + +# Device Model to be used +# +# STUBDOM: this is a script that creates the stub domain running the device +# model +device_model = '/usr/lib/xen/bin/stubdom-dm' + +#----------------------------------------------------------------------------- +# boot on floppy (a), hard disk (c), Network (n) or CD-ROM (d) +# default: hard disk, cd-rom, floppy +#boot="cda" + +#----------------------------------------------------------------------------- +# write to temporary files instead of disk image files +#snapshot=1 + +#---------------------------------------------------------------------------- +# enable SDL library for graphics, default = 0 +# +# STUBDOM: always disable since the stub domain doesn't have direct X access +sdl=0 + +#---------------------------------------------------------------------------- +# enable OpenGL for texture rendering inside the SDL window, default = 1 +# valid only if sdl is enabled. +# +# STUBDOM: always disable for the same reason +opengl=0 + +#---------------------------------------------------------------------------- +# enable VNC library for graphics, default = 1 +vnc=0 + +#---------------------------------------------------------------------------- +# address that should be listened on for the VNC server if vnc is set. +# default is to use 'vnc-listen' setting from /etc/xen/xend-config.sxp +#vnclisten="127.0.0.1" + +#---------------------------------------------------------------------------- +# set VNC display number, default = domid +#vncdisplay=1 + +#---------------------------------------------------------------------------- +# try to find an unused port for the VNC server, default = 1 +#vncunused=1 + +#---------------------------------------------------------------------------- +# enable spawning vncviewer for domain's console +# (only valid when vnc=1), default = 0 +#vncconsole=0 + +#---------------------------------------------------------------------------- +# set password for domain's VNC console +# default is depents on vncpasswd in xend-config.sxp +vncpasswd='' + +#---------------------------------------------------------------------------- +# no graphics, use serial port +#nographic=0 + +#---------------------------------------------------------------------------- +# enable stdvga, default = 0 (use cirrus logic device model) +stdvga=0 + +#----------------------------------------------------------------------------- +# serial port re-direct to pty deivce, /dev/pts/n +# then xm console or minicom can connect +# +# STUBDOM: always disable as the stub domain doesn't have access to dom0's +# ptys +#serial='pty' + + +#----------------------------------------------------------------------------- +# Qemu Monitor, default is disable +# Use ctrl-alt-2 to connect +#monitor=1 + + +#----------------------------------------------------------------------------- +# enable sound card support, [sb16|es1370|all|..,..], default none +# +# STUBDOM: not supported +#soundhw='sb16' + + +#----------------------------------------------------------------------------- +# set the real time clock to local time [default=0 i.e. set to utc] +#localtime=1 + + +#----------------------------------------------------------------------------- +# set the real time clock offset in seconds [default=0 i.e. same as dom0] +#rtc_timeoffset=3600 + +#----------------------------------------------------------------------------- +# start in full screen +#full-screen=1 + + +#----------------------------------------------------------------------------- +# Enable USB support (specific devices specified at runtime through the +# monitor window) +#usb=1 + +# Enable USB mouse support (only enable one of the following, `mouse' for +# PS/2 protocol relative mouse, `tablet' for +# absolute mouse) +#usbdevice='mouse' +#usbdevice='tablet' + +#----------------------------------------------------------------------------- +# Set keyboard layout, default is en-us keyboard. +#keymap='ja' + +#----------------------------------------------------------------------------- +# Configure guest CPUID responses: +# +#cpuid=[ '1:ecx=xxxxxxxxxxx00xxxxxxxxxxxxxxxxxxx, +# eax=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' ] +# - Unset the SSE4 features (CPUID.1[ECX][20-19]) +# - Default behaviour for all other bits in ECX And EAX registers. +# +# Each successive character represent a lesser-significant bit: +# '1' -> force the corresponding bit to 1 +# '0' -> force to 0 +# 'x' -> Get a safe value (pass through and mask with the default policy) +# 'k' -> pass through the host bit value +# 's' -> as 'k' but preserve across save/restore and migration +# +# Expose to the guest multi-core cpu instead of multiple processors +# Example for intel, expose a 8-core processor : +#cpuid=['1:edx=xxx1xxxxxxxxxxxxxxxxxxxxxxxxxxxx, +# ebx=xxxxxxxx00010000xxxxxxxxxxxxxxxx', +# '4,0:eax=001111xxxxxxxxxxxxxxxxxxxxxxxxxx'] +# - CPUID.1[EDX][HT] : Enable HT +# - CPUID.1[EBX] : Number of vcpus * 2 +# - CPUID.4,0[EAX] : Number of vcpus * 2 - 1 +#vcpus=8 +# +# Example for amd, expose a 5-core processor : +# cpuid = ['1:ebx=xxxxxxxx00001010xxxxxxxxxxxxxxxx, +# edx=xxx1xxxxxxxxxxxxxxxxxxxxxxxxxxxx', +# '0x80000001:ecx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1x', +# '0x80000008:ecx=xxxxxxxxxxxxxxxxxxxxxxxxxx001001'] +# - CPUID.1[EBX] : Threads per Core * Cores per Socket (2 * #vcpus) +# - CPUID.1[EDX][HT] : Enable HT +# - CPUID.0x80000001[CmpLegacy] : Use legacy method +# - CPUID.0x80000008[ECX] : #vcpus * 2 - 1 +#vcpus=5 +# +# Downgrade the cpuid to make a better compatibility for migration : +# Look like a generic 686 : +# cpuid = [ '0:eax=0x3,ebx=0x0,ecx=0x0,edx=0x0', +# '1:eax=0x06b1, +# ecx=xxxxxxxxxx0000xx00xxx0000000xx0, +# edx=xx00000xxxxxxx0xxxxxxxxx0xxxxxx', +# '4:eax=0x3,ebx=0x0,ecx=0x0,edx=0x0', +# '0x80000000:eax=0x3,ebx=0x0,ecx=0x0,edx=0x0'] +# with the highest leaf +# - CPUID.0[EAX] : Set the highest leaf +# - CPUID.1[EAX] : 686 +# - CPUID.1[ECX] : Mask some features +# - CPUID.1[EDX] : Mask some features +# - CPUID.4 : Reply like the highest leaf, in our case CPUID.3 +# - CPUID.0x80000000 : No extension we are on a Pentium III, reply like the +# highest leaf (CPUID.3). +# +# Configure host CPUID consistency checks, which must be satisfied for this +# VM to be allowed to run on this host's processor type: +#cpuid_check=[ '1:ecx=xxxxxxxxxxxxxxxxxxxxxxxxxx1xxxxx' ] +# - Host must have VMX feature flag set +# +# The format is similar to the above for 'cpuid': +# '1' -> the bit must be '1' +# '0' -> the bit must be '0' +# 'x' -> we don't care (do not check) +# 's' -> the bit must be the same as on the host that started this VM + + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] diff --git a/tools/examples/xmexample.nbd b/tools/examples/xmexample.nbd new file mode 100644 index 0000000..7fdb74e --- /dev/null +++ b/tools/examples/xmexample.nbd @@ -0,0 +1,26 @@ +# -*- mode: python; -*- +# +# xm create configuration example. +# +# This configuration is appropriate for using Network Block Device (NBD) +# filesystems. +# +# Each of these parameters will need changing to match your setup. +# + +kernel = "/boot/vmlinuz-2.6.13-15b-xen" +ramdisk = "/boot/initrd-2.6.13-15b-xen" + +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 128 + +name = "nbd4" +vif = [ '' ] +# Please change PORT +disk = [ 'nbd:134.100.233.115 20004,hda1,w' ] +dhcp = "dhcp" +hostname= "nbd4" +root = "/dev/hda1 ro" +extra = "3" diff --git a/tools/examples/xmexample.pv-grub b/tools/examples/xmexample.pv-grub new file mode 100644 index 0000000..2d647c9 --- /dev/null +++ b/tools/examples/xmexample.pv-grub @@ -0,0 +1,186 @@ +# -*- mode: python; -*- +#============================================================================ +# Python configuration setup for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# You use a separate script for each domain you want to create, or +# you can set the parameters for the domain on the xm command line. +#============================================================================ + +#---------------------------------------------------------------------------- +# PV GRUB image file. +kernel = "/usr/lib/xen/boot/pv-grub.gz" + +# Optional provided menu.lst. +#ramdisk = "/boot/guests/menu.lst" + +# Sets path to menu.lst +extra = "(hd0,0)/boot/grub/menu.lst" +# can be a TFTP-served path (DHCP will automatically be run) +# extra = "(nd)/netboot/menu.lst" +# can be configured automatically by GRUB's DHCP option 150 (see grub manual) +# extra = "" + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 128 + +# A name for your domain. All domains must have different names. +name = "ExampleDomain" + +# 128-bit UUID for the domain. The default behavior is to generate a new UUID +# on each call to 'xm create'. +#uuid = "06ed00fe-1162-4fc4-b5d8-11993ee4a8b9" + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 + +# Number of Virtual CPUS to use, default is 1 +#vcpus = 1 + +#---------------------------------------------------------------------------- +# Define network interfaces. + +# By default, no network interfaces are configured. You may have one created +# with sensible defaults using an empty vif clause: +# +# vif = [ '' ] +# +# or optionally override backend, bridge, ip, mac, script, type, or vifname: +# +# vif = [ 'mac=00:16:3e:00:00:11, bridge=xenbr0' ] +# +# or more than one interface may be configured: +# +# vif = [ '', 'bridge=xenbr1' ] + +vif = [ '' ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +disk = [ 'phy:hda1,hda1,w' ] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ 'type=sdl' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ 'type=sdl,xauthority=/home/bozo/.Xauthority,display=:1' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ 'type=vnc' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ 'type=vnc,vnclisten=127.0.0.1,vncdisplay=1' ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ 'type=vnc,vnclisten=0.0.0.0,vncunused=1' ] +# +# You can override the password: +# +# vfb = [ 'type=vnc,vncpasswd=MYPASSWD' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. + +#---------------------------------------------------------------------------- +# Define to which TPM instance the user domain should communicate. +# The vtpm entry is of the form 'instance=INSTANCE,backend=DOM' +# where INSTANCE indicates the instance number of the TPM the VM +# should be talking to and DOM provides the domain where the backend +# is located. +# Note that no two virtual machines should try to connect to the same +# TPM instance. The handling of all TPM instances does require +# some management effort in so far that VM configration files (and thus +# a VM) should be associated with a TPM instance throughout the lifetime +# of the VM / VM configuration file. The instance number must be +# greater or equal to 1. +#vtpm = [ 'instance=1,backend=0' ] + +#---------------------------------------------------------------------------- +# Configure the behaviour when a domain exits. There are three 'reasons' +# for a domain to stop: poweroff, reboot, and crash. For each of these you +# may specify: +# +# "destroy", meaning that the domain is cleaned up as normal; +# "restart", meaning that a new domain is started in place of the old +# one; +# "preserve", meaning that no clean-up is done until the domain is +# manually destroyed (using xm destroy, for example); or +# "rename-restart", meaning that the old domain is not cleaned up, but is +# renamed and a new domain started in its place. +# +# In the event a domain stops due to a crash, you have the additional options: +# +# "coredump-destroy", meaning dump the crashed domain's core and then destroy; +# "coredump-restart', meaning dump the crashed domain's core and the restart. +# +# The default is +# +# on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# For backwards compatibility we also support the deprecated option restart +# +# restart = 'onreboot' means on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'destroy' +# +# restart = 'always' means on_poweroff = 'restart' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# restart = 'never' means on_poweroff = 'destroy' +# on_reboot = 'destroy' +# on_crash = 'destroy' + +#on_poweroff = 'destroy' +#on_reboot = 'restart' +#on_crash = 'restart' + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] + +#============================================================================ + diff --git a/tools/examples/xmexample.vti b/tools/examples/xmexample.vti new file mode 100644 index 0000000..3169e52 --- /dev/null +++ b/tools/examples/xmexample.vti @@ -0,0 +1,180 @@ +# -*- mode: python; -*- +#============================================================================ +# Python configuration setup for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# You use a separate script for each domain you want to create, or +# you can set the parameters for the domain on the xm command line. +#============================================================================ + +import os, re +arch = os.uname()[4] +arch_libdir = 'lib' + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "/usr/lib/xen/boot/guest_firmware.bin" + +# The domain build function. VTI domain uses 'hvm'. +builder='hvm' + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 256 + +# A name for your domain. All domains must have different names. +name = "ExampleVTIDomain" + +# the number of cpus guest platform has, default=1 +#vcpus=1 + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 + +# Log2 of VHPT size, default=23 (8MB), minimum=15 (32KB). +# In Windows OS, smaller size shows better performance. +#vhpt = 23 + +# Optionally define mac and/or bridge for the network interfaces. +# Random MACs are assigned if not given. +#vif = [ 'type=ioemu, mac=00:16:3e:00:00:11, bridge=xenbr0, model=ne2k_pci' ] +# type=ioemu specify the NIC is an ioemu device not netfront +vif = [ 'type=ioemu, bridge=xenbr0' ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +#disk = [ 'phy:hda1,hda1,r' ] +disk = [ 'file:/var/images/xenia64.img,hda,w', ',hdc:cdrom,r' ] + +#---------------------------------------------------------------------------- +# Set according to whether you want the domain restarted when it exits. +# The default is 'onreboot', which restarts the domain when it shuts down +# with exit code reboot. +# Other values are 'always', and 'never'. + +#restart = 'onreboot' + +#============================================================================ + +# New stuff +device_model = '/usr/' + arch_libdir + '/xen/bin/qemu-dm' + +#----------------------------------------------------------------------------- +# boot on floppy (a), hard disk (c) or CD-ROM (d) +# default: hard disk, cd-rom, floppy +#boot="cda" + +#----------------------------------------------------------------------------- +# write to temporary files instead of disk image files +#snapshot=1 + +#---------------------------------------------------------------------------- +# enable SDL library for graphics, default = 0 +sdl=1 + +#---------------------------------------------------------------------------- +# enable VNC library for graphics, default = 1 +vnc=0 + +#---------------------------------------------------------------------------- +# set VNC display number, default = domid +#vncdisplay=1 + +#---------------------------------------------------------------------------- +# try to find an unused port for the VNC server, default = 1 +#vncunused=1 + +#---------------------------------------------------------------------------- +# set password for domain's VNC console +# default is depents on vncpasswd in xend-config.sxp +vncpasswd='' + +#---------------------------------------------------------------------------- +# no graphics, use serial port +#nographic=0 + +#---------------------------------------------------------------------------- +# enable stdvga, default = 0 (use cirrus logic device model) +stdvga=0 + +#----------------------------------------------------------------------------- +# serial port re-direct to pty deivce, /dev/pts/n +# then xm console or minicom can connect +serial='pty' + +#----------------------------------------------------------------------------- +# Qemu Monitor, default is disable +# Use ctrl-alt-2 to connect +#monitor=1 + +#----------------------------------------------------------------------------- +# enable sound card support, [sb16|es1370|all|..,..], default none +#soundhw='sb16' + +#----------------------------------------------------------------------------- +# set the real time clock to local time [default=0 i.e. set to utc] +#localtime=1 + +#----------------------------------------------------------------------------- +# start in full screen +#full-screen=1 diff -r 42cab8724273 tools/libxc/xc_ia64_stubs.c + +#----------------------------------------------------------------------------- +# Enable USB support (specific devices specified at runtime through the +# monitor window) +#usb=1 + +# Enable USB mouse support (only enable one of the following, `mouse' for +# PS/2 protocol relative mouse, `tablet' for +# absolute mouse) +#usbdevice='mouse' +#usbdevice='tablet' + +#----------------------------------------------------------------------------- +# Set keyboard layout, default is en-us keyboard. +#keymap='ja' + +#----------------------------------------------------------------------------- +# Enable optimization features for the specified OS type. (Specific to the +# OS running in the guest domain. Other OSes may not run correctly +# if the wrong OS type is specified.) +# +# Default is "default", which should work for all supported guest OSes. +# +# Known values: +# 'linux' - All Linux variants +# 'windows' - All Windows variants (Windows Server 2003/2008) +# +#guest_os_type='default' + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] diff --git a/tools/examples/xmexample1 b/tools/examples/xmexample1 new file mode 100644 index 0000000..fe382d4 --- /dev/null +++ b/tools/examples/xmexample1 @@ -0,0 +1,211 @@ +# -*- mode: python; -*- +#============================================================================ +# Python configuration setup for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# You use a separate script for each domain you want to create, or +# you can set the parameters for the domain on the xm command line. +#============================================================================ + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "/boot/vmlinuz-2.6.10-xenU" + +# Optional ramdisk. +#ramdisk = "/boot/initrd.gz" + +# The domain build function. Default is 'linux'. +#builder='linux' + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 64 + +# A name for your domain. All domains must have different names. +name = "ExampleDomain" + +# 128-bit UUID for the domain. The default behavior is to generate a new UUID +# on each call to 'xm create'. +#uuid = "06ed00fe-1162-4fc4-b5d8-11993ee4a8b9" + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 + +# Number of Virtual CPUS to use, default is 1 +#vcpus = 1 + +#---------------------------------------------------------------------------- +# Define network interfaces. + +# By default, no network interfaces are configured. You may have one created +# with sensible defaults using an empty vif clause: +# +# vif = [ '' ] +# +# or optionally override backend, bridge, ip, mac, script, type, or vifname: +# +# vif = [ 'mac=00:16:3e:00:00:11, bridge=xenbr0' ] +# +# or more than one interface may be configured: +# +# vif = [ '', 'bridge=xenbr1' ] + +vif = [ '' ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +disk = [ 'phy:hda1,hda1,w' ] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ 'type=sdl' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ 'type=sdl,xauthority=/home/bozo/.Xauthority,display=:1' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ 'type=vnc' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ 'type=vnc,vnclisten=127.0.0.1,vncdisplay=1' ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ 'type=vnc,vnclisten=0.0.0.0,vncunused=1' ] +# +# You can override the password: +# +# vfb = [ 'type=vnc,vncpasswd=MYPASSWD' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. + +#---------------------------------------------------------------------------- +# Define to which TPM instance the user domain should communicate. +# The vtpm entry is of the form 'instance=INSTANCE,backend=DOM' +# where INSTANCE indicates the instance number of the TPM the VM +# should be talking to and DOM provides the domain where the backend +# is located. +# Note that no two virtual machines should try to connect to the same +# TPM instance. The handling of all TPM instances does require +# some management effort in so far that VM configration files (and thus +# a VM) should be associated with a TPM instance throughout the lifetime +# of the VM / VM configuration file. The instance number must be +# greater or equal to 1. +#vtpm = [ 'instance=1,backend=0' ] + +#---------------------------------------------------------------------------- +# Set the kernel command line for the new domain. +# You only need to define the IP parameters and hostname if the domain's +# IP config doesn't, e.g. in ifcfg-eth0 or via DHCP. +# You can use 'extra' to set the runlevel and custom environment +# variables used by custom rc scripts (e.g. VMID=, usr= ). + +# Set if you want dhcp to allocate the IP address. +#dhcp="dhcp" +# Set netmask. +#netmask= +# Set default gateway. +#gateway= +# Set the hostname. +#hostname= "vm%d" % vmid + +# Set root device. +root = "/dev/hda1 ro" + +# Root device for nfs. +#root = "/dev/nfs" +# The nfs server. +#nfs_server = '192.0.2.1' +# Root directory on the nfs server. +#nfs_root = '/full/path/to/root/directory' + +# Sets runlevel 4. +extra = "4" + +#---------------------------------------------------------------------------- +# Configure the behaviour when a domain exits. There are three 'reasons' +# for a domain to stop: poweroff, reboot, and crash. For each of these you +# may specify: +# +# "destroy", meaning that the domain is cleaned up as normal; +# "restart", meaning that a new domain is started in place of the old +# one; +# "preserve", meaning that no clean-up is done until the domain is +# manually destroyed (using xm destroy, for example); or +# "rename-restart", meaning that the old domain is not cleaned up, but is +# renamed and a new domain started in its place. +# +# In the event a domain stops due to a crash, you have the additional options: +# +# "coredump-destroy", meaning dump the crashed domain's core and then destroy; +# "coredump-restart', meaning dump the crashed domain's core and the restart. +# +# The default is +# +# on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# For backwards compatibility we also support the deprecated option restart +# +# restart = 'onreboot' means on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'destroy' +# +# restart = 'always' means on_poweroff = 'restart' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# restart = 'never' means on_poweroff = 'destroy' +# on_reboot = 'destroy' +# on_crash = 'destroy' + +#on_poweroff = 'destroy' +#on_reboot = 'restart' +#on_crash = 'restart' + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] + +#============================================================================ + diff --git a/tools/examples/xmexample2 b/tools/examples/xmexample2 new file mode 100644 index 0000000..53ee3aa --- /dev/null +++ b/tools/examples/xmexample2 @@ -0,0 +1,246 @@ +# -*- mode: python; -*- +#============================================================================ +# Example Python setup script for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# +# This is a relatively advanced script that uses a parameter, vmid, to control +# the settings. So this script can be used to start a set of domains by +# setting the vmid parameter on the 'xm create' command line. For example: +# +# xm create vmid=1 +# xm create vmid=2 +# xm create vmid=3 +# +# The vmid is purely a script variable, and has no effect on the the domain +# id assigned to the new domain. +#============================================================================ + +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. + +# This function checks that 'vmid' has been given a valid value. +# It is called automatically by 'xm create'. +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +# Define the 'vmid' variable so that 'xm create' knows about it. +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) + +# Check the defined variables have valid values.. +xm_vars.check() + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "/boot/vmlinuz-2.6.10-xenU" + +# Optional ramdisk. +#ramdisk = "/boot/initrd.gz" + +# The domain build function. Default is 'linux'. +#builder='linux' + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 64 + +# A name for the new domain. All domains have to have different names, +# so we use the vmid to create a name. +name = "VM%d" % vmid + +# 128-bit UUID for the domain. The default behavior is to generate a new UUID +# on each call to 'xm create'. +#uuid = "06ed00fe-1162-4fc4-b5d8-11993ee4a8b9" + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 +#cpus = "%s" % vmid # set based on vmid (mod number of CPUs) + +# Number of Virtual CPUS to use, default is 1 +#vcpus = 1 +vcpus = 4 # make your domain a 4-way + +#---------------------------------------------------------------------------- +# Define network interfaces. + +# By default, no network interfaces are configured. You may have one created +# with sensible defaults using an empty vif clause: +# +# vif = [ '' ] +# +# or optionally override backend, bridge, ip, mac, script, type, or vifname: +# +# vif = [ 'mac=00:16:3e:00:00:11, bridge=xenbr0' ] +# +# or more than one interface may be configured: +# +# vif = [ '', 'bridge=xenbr1' ] + +vif = [ '' ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +# This makes the disk device depend on the vmid - assuming +# that devices sda7, sda8 etc. exist. The device is exported +# to all domains as sda1. +# All domains get sda6 read-only (to use for /usr, see below). +disk = [ 'phy:sda%d,sda1,w' % (7+vmid), + 'phy:sda6,sda6,r' ] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ 'type=sdl' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ 'type=sdl,xauthority=/home/bozo/.Xauthority,display=:1' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ 'type=vnc' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ 'type=vnc,vnclisten=127.0.0.1,vncdisplay=%d' % vmid ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ 'type=vnc,vnclisten=0.0.0.0,vncunused=1' ] +# +# You can override the password: +# +# vfb = [ 'type=vnc,vncpasswd=MYPASSWD' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. + +#---------------------------------------------------------------------------- +# Define to which TPM instance the user domain should communicate. +# The vtpm entry is of the form 'instance=INSTANCE,backend=DOM' +# where INSTANCE indicates the instance number of the TPM the VM +# should be talking to and DOM provides the domain where the backend +# is located. +# Note that no two virtual machines should try to connect to the same +# TPM instance. The handling of all TPM instances does require +# some management effort in so far that VM configration files (and thus +# a VM) should be associated with a TPM instance throughout the lifetime +# of the VM / VM configuration file. The instance number must be +# greater or equal to 1. +#vtpm = ['instance=%d,backend=0' % (vmid) ] + +#---------------------------------------------------------------------------- +# Set the kernel command line for the new domain. +# You only need to define the IP parameters and hostname if the domain's +# IP config doesn't, e.g. in ifcfg-eth0 or via DHCP. +# You can use 'extra' to set the runlevel and custom environment +# variables used by custom rc scripts (e.g. VMID=, usr= ). + +# Set if you want dhcp to allocate the IP address. +#dhcp="dhcp" +# Set netmask. +#netmask= +# Set default gateway. +#gateway= +# Set the hostname. +#hostname= "vm%d" % vmid + +# Set root device. +root = "/dev/sda1 ro" + +# Root device for nfs. +#root = "/dev/nfs" +# The nfs server. +#nfs_server = '192.0.2.1' +# Root directory on the nfs server. +#nfs_root = '/full/path/to/root/directory' + +# Sets runlevel 4 and the device for /usr. +extra = "4 VMID=%d usr=/dev/sda6" % vmid + +#---------------------------------------------------------------------------- +# Configure the behaviour when a domain exits. There are three 'reasons' +# for a domain to stop: poweroff, reboot, and crash. For each of these you +# may specify: +# +# "destroy", meaning that the domain is cleaned up as normal; +# "restart", meaning that a new domain is started in place of the old +# one; +# "preserve", meaning that no clean-up is done until the domain is +# manually destroyed (using xm destroy, for example); or +# "rename-restart", meaning that the old domain is not cleaned up, but is +# renamed and a new domain started in its place. +# +# In the event a domain stops due to a crash, you have the additional options: +# +# "coredump-destroy", meaning dump the crashed domain's core and then destroy; +# "coredump-restart', meaning dump the crashed domain's core and the restart. +# +# The default is +# +# on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# For backwards compatibility we also support the deprecated option restart +# +# restart = 'onreboot' means on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'destroy' +# +# restart = 'always' means on_poweroff = 'restart' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# restart = 'never' means on_poweroff = 'destroy' +# on_reboot = 'destroy' +# on_crash = 'destroy' + +#on_poweroff = 'destroy' +#on_reboot = 'restart' +#on_crash = 'restart' + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] + +#============================================================================ diff --git a/tools/examples/xmexample3 b/tools/examples/xmexample3 new file mode 100644 index 0000000..9928190 --- /dev/null +++ b/tools/examples/xmexample3 @@ -0,0 +1,232 @@ +# -*- mode: python; -*- +#============================================================================ +# Example Python setup script for 'xm create'. +# This script sets the parameters used when a domain is created using 'xm create'. +# +# This is a relatively advanced script that uses a parameter, vmid, to control +# the settings. So this script can be used to start a set of domains by +# setting the vmid parameter on the 'xm create' command line. For example: +# +# xm create vmid=1 +# xm create vmid=2 +# xm create vmid=3 +# +# The vmid is purely a script variable, and has no effect on the the domain +# id assigned to the new domain. +#============================================================================ + +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. + +# This function checks that 'vmid' has been given a valid value. +# It is called automatically by 'xm create'. +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +# Define the 'vmid' variable so that 'xm create' knows about it. +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) + +# Check the defined variables have valid values.. +xm_vars.check() + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "/path/to/domU/kernel" + +# Optional ramdisk. +#ramdisk = "/boot/initrd.gz" + +# The domain build function. Default is 'linux'. +#builder='linux' + +# Initial memory allocation (in megabytes) for the new domain. +# +# WARNING: Creating a domain with insufficient memory may cause out of +# memory errors. The domain needs enough memory to boot kernel +# and modules. Allocating less than 32MBs is not recommended. +memory = 64 + +# A name for the new domain. All domains have to have different names, +# so we use the vmid to create a name. +name = "VM%d" % vmid + +# 128-bit UUID for the domain. The default behavior is to generate a new UUID +# on each call to 'xm create'. +#uuid = "06ed00fe-1162-4fc4-b5d8-11993ee4a8b9" + +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # all vcpus run on cpus 0,2,3,5 +#cpus = ["2", "3"] # VCPU0 runs on CPU2, VCPU1 runs on CPU3 +cpus = "%s" % vmid # set based on vmid (mod number of CPUs) + +#---------------------------------------------------------------------------- +# Define network interfaces. + +# Optionally define mac and/or bridge for the network interfaces. +# Random MACs are assigned if not given. + +vif = [ 'ip=192.168.%d.1/24' % (vmid)] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:UNAME,DEV,MODE +# where UNAME is the device, DEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +# This makes the disk device depend on the vmid - assuming +# tHat devices sda7, sda8 etc. exist. The device is exported +# to all domains as sda1. +# All domains get sda6 read-only (to use for /usr, see below). +disk = [ 'phy:hda%d,hda1,w' % (vmid)] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ 'type=sdl' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ 'type=sdl,xauthority=/home/bozo/.Xauthority,display=:1' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ 'type=vnc' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ 'type=vnc,vnclisten=127.0.0.1,vncdisplay=%d' % vmid ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ 'type=vnc,vnclisten=0.0.0.0,vncunused=1' ] +# +# You can override the password: +# +# vfb = [ 'type=vnc,vncpasswd=MYPASSWD' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. + +#---------------------------------------------------------------------------- +# Define to which TPM instance the user domain should communicate. +# The vtpm entry is of the form 'instance=INSTANCE,backend=DOM' +# where INSTANCE indicates the instance number of the TPM the VM +# should be talking to and DOM provides the domain where the backend +# is located. +# Note that no two virtual machines should try to connect to the same +# TPM instance. The handling of all TPM instances does require +# some management effort in so far that VM configration files (and thus +# a VM) should be associated with a TPM instance throughout the lifetime +# of the VM / VM configuration file. The instance number must be +# greater or equal to 1. +#vtpm = ['instance=%d,backend=0' % (vmid) ] + +#---------------------------------------------------------------------------- +# Set the kernel command line for the new domain. +# You only need to define the IP parameters and hostname if the domain's +# IP config doesn't, e.g. in ifcfg-eth0 or via DHCP. +# You can use 'extra' to set the runlevel and custom environment +# variables used by custom rc scripts (e.g. VMID=, usr= ). + +# Set if you want dhcp to allocate the IP address. +dhcp="off" +ip="192.168.%d.2" % (vmid) +# Set netmask. +netmask="255.255.255.0" +# Set default gateway. +gateway="192.168.%d.1" % (vmid) +# Set the hostname. +hostname= "domain-%d.xeno" % vmid + +# Set root device. +root = "/dev/hda1 ro" + +# Root device for nfs. +#root = "/dev/nfs" +# The nfs server. +#nfs_server = "10.212.4.103" +# Root directory on the nfs server. +#nfs_root = "/path/to/root/filesystem" + +# Sets runlevel 4 and the device for /usr. +extra = "4 VMID=%d" % vmid + +#---------------------------------------------------------------------------- +# Configure the behaviour when a domain exits. There are three 'reasons' +# for a domain to stop: poweroff, reboot, and crash. For each of these you +# may specify: +# +# "destroy", meaning that the domain is cleaned up as normal; +# "restart", meaning that a new domain is started in place of the old +# one; +# "preserve", meaning that no clean-up is done until the domain is +# manually destroyed (using xm destroy, for example); or +# "rename-restart", meaning that the old domain is not cleaned up, but is +# renamed and a new domain started in its place. +# +# In the event a domain stops due to a crash, you have the additional options: +# +# "coredump-destroy", meaning dump the crashed domain's core and then destroy; +# "coredump-restart', meaning dump the crashed domain's core and the restart. +# +# The default is +# +# on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# For backwards compatibility we also support the deprecated option restart +# +# restart = 'onreboot' means on_poweroff = 'destroy' +# on_reboot = 'restart' +# on_crash = 'destroy' +# +# restart = 'always' means on_poweroff = 'restart' +# on_reboot = 'restart' +# on_crash = 'restart' +# +# restart = 'never' means on_poweroff = 'destroy' +# on_reboot = 'destroy' +# on_crash = 'destroy' + +#on_poweroff = 'destroy' +#on_reboot = 'restart' +#on_crash = 'restart' + +#----------------------------------------------------------------------------- +# Configure PVSCSI devices: +# +#vscsi=[ 'PDEV, VDEV' ] +# +# PDEV gives physical SCSI device to be attached to specified guest +# domain by one of the following identifier format. +# - XX:XX:XX:XX (4-tuples with decimal notation which shows +# "host:channel:target:lun") +# - /dev/sdxx or sdx +# - /dev/stxx or stx +# - /dev/sgxx or sgx +# - result of 'scsi_id -gu -s'. +# ex. # scsi_id -gu -s /block/sdb +# 36000b5d0006a0000006a0257004c0000 +# +# VDEV gives virtual SCSI device by 4-tuples (XX:XX:XX:XX) as +# which the specified guest domain recognize. +# + +#vscsi = [ '/dev/sdx, 0:0:0:0' ] + +#============================================================================ diff --git a/tools/firmware/Makefile b/tools/firmware/Makefile new file mode 100644 index 0000000..a9bc54d --- /dev/null +++ b/tools/firmware/Makefile @@ -0,0 +1,33 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +# hvmloader is a 32-bit protected mode binary. +# It belongs in /usr/lib, not /usr/lib64. +TARGET := hvmloader/hvmloader +INST_DIR := $(DESTDIR)/usr/lib/xen/boot + +SUBDIRS := +SUBDIRS += rombios +SUBDIRS += vgabios +#SUBDIRS += etherboot +SUBDIRS += hvmloader + +.PHONY: all +all: + @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 package version >= 0.16.14 to build firmware!"; \ + echo "(visit http://www.cix.co.uk/~mayday for more information)"; \ + echo "***********************************************************"; \ + else \ + $(MAKE) subdirs-$@; \ + fi + + +.PHONY: install +install: all + [ -d $(INST_DIR) ] || $(INSTALL_DIR) $(INST_DIR) + [ ! -e $(TARGET) ] || $(INSTALL_DATA) $(TARGET) $(INST_DIR) + +.PHONY: clean +clean: subdirs-clean diff --git a/tools/firmware/Rules.mk b/tools/firmware/Rules.mk new file mode 100644 index 0000000..6cf0dfa --- /dev/null +++ b/tools/firmware/Rules.mk @@ -0,0 +1,21 @@ +# Firmware is a 32-bit target +override XEN_TARGET_ARCH = x86_32 + +# User-supplied CFLAGS are not useful here. +CFLAGS := + +include $(XEN_ROOT)/tools/Rules.mk + +ifneq ($(debug),y) +CFLAGS += -DNDEBUG +endif + +CFLAGS += -Werror + +# Disable PIE/SSP if GCC supports them. They can break us. +CFLAGS += $(call cc-option,$(CC),-nopie,) +CFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) +CFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) + +# Extra CFLAGS suitable for an embedded type of environment. +CFLAGS += -fno-builtin -msoft-float diff --git a/tools/firmware/etherboot/Config b/tools/firmware/etherboot/Config new file mode 100644 index 0000000..f578f94 --- /dev/null +++ b/tools/firmware/etherboot/Config @@ -0,0 +1,8 @@ + +NICS = rtl8139 eepro100 + +CFLAGS += -UPXE_DHCP_STRICT +CFLAGS += -DPXE_DHCP_STRICT + +CFLAGS += -UNO_POST_PROMPT +CFLAGS += -DNO_POST_PROMPT diff --git a/tools/firmware/etherboot/Makefile b/tools/firmware/etherboot/Makefile new file mode 100644 index 0000000..1e452c3 --- /dev/null +++ b/tools/firmware/etherboot/Makefile @@ -0,0 +1,38 @@ + +override XEN_TARGET_ARCH = x86_32 +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk +include Config + +D=gpxe +T=gpxe-git-snapshot.tar.gz + +ROMS = $(addprefix $D/src/bin/, $(addsuffix .rom, $(NICS))) + +.PHONY: all +all: eb-roms.h + +%.rom: $D/src/arch/i386/Config + $(MAKE) -C $D/src bin/$(*F).rom + +eb-roms.h.new: $(ROMS) + cat $^ | ../hvmloader/mkhex etherboot >$@ + +eb-roms.h: Config + $(MAKE) $@.new + mv -f $@.new $@ + +$D/src/arch/i386/Config: $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/Config + $(MAKE) -C $D/src bin/NIC + +.PHONY: clean +clean: + rm -rf $D *~ diff --git a/tools/firmware/etherboot/README b/tools/firmware/etherboot/README new file mode 100644 index 0000000..c1c713c --- /dev/null +++ b/tools/firmware/etherboot/README @@ -0,0 +1,28 @@ + +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. diff --git a/tools/firmware/etherboot/eb-roms.h b/tools/firmware/etherboot/eb-roms.h new file mode 100644 index 0000000..3f7b4f4 --- /dev/null +++ b/tools/firmware/etherboot/eb-roms.h @@ -0,0 +1,6371 @@ +unsigned etherboot[] = { +0xe963aa55, 0x009f0090, 0x00000000, 0x00000000, +0x00000000, 0x00800000, 0x0038001c, 0x52494350, +0x813910ec, 0x001c0000, 0x00000203, 0x00010063, +0x00638000, 0x00000000, 0x506e5024, 0x00000201, +0x00008d00, 0x00580000, 0x0002006d, 0x0000f400, +0x02810000, 0x00000000, 0x70747468, 0x652f2f3a, +0x72656874, 0x746f6f62, 0x67726f2e, 0x58506700, +0x50280045, 0x78204943, 0x78783a78, 0x0029782e, +0x49444e55, 0x00003316, 0x03310201, 0x2cf82cf8, +0x435006d4, 0x1e605249, 0x0fa00f06, 0x1f0efca8, +0xfb89eb8e, 0x310214be, 0x0333e8ff, 0xbf0365e8, +0x5fe80077, 0x7106c603, 0x20b02000, 0xffe8ff31, +0xe8c88c02, 0x53660331, 0x57665266, 0xb101b8f9, +0x29721acd, 0x50fa8166, 0x75204943, 0x75e48420, +0x0233be1c, 0xf6e8ff31, 0xe8f88802, 0x2eb00319, +0x8802cde8, 0x0306e8d8, 0x7303ff80, 0xa90f0e03, +0x5a665f66, 0xc3f75b66, 0x2775000f, 0x3f812666, +0x506e5024, 0x38be1d75, 0xe8ff3102, 0xff2602c1, +0x681e1b77, 0x606a027f, 0x0d5fff26, 0x8508c483, +0xbe2474c0, 0xff310247, 0x3102a4e8, 0x66c08ec0, +0x6436ff26, 0x068f6600, 0xa80f02c3, 0x66028668, +0x64068f26, 0xbe08eb00, 0xff31023d, 0xbb0280e8, +0x0f43dfff, 0x8e008584, 0x812666c3, 0x2400003e, +0x754d4d50, 0x31d231ed, 0xb60f26f6, 0x2600050e, +0xe2c200ac, 0xbedb75fa, 0xff310242, 0x6a0250e8, +0xff6a6606, 0x00006866, 0x006a0002, 0x071eff26, +0x0cc48300, 0xff31d089, 0x89024ce8, 0x85027916, +0x663974d2, 0x8ec03160, 0x3e8b66c0, 0x31660277, +0xb60f66f6, 0x6600020e, 0xf309e1c1, 0x8966a467, +0x31027b3e, 0xb9f631db, 0x0e880004, 0xe1c10002, +0xc300ac09, 0x1e28fbe2, 0x61660006, 0xff3120b0, +0x8c01cde8, 0x01ffe8e8, 0x020eb60f, 0x09e1c100, +0xf631c08e, 0x2ef3ff31, 0x0fa90fa4, 0x611f07a1, +0xcb0020b8, 0x45585067, 0x74682820, 0x2f3a7074, +0x6874652f, 0x6f627265, 0x6f2e746f, 0x20296772, +0x2000202d, 0x00494350, 0x506e5020, 0x42422000, +0x50200053, 0x20004d4d, 0x31544e49, 0x200d0039, +0x20202020, 0x20202020, 0x20202020, 0x20202020, +0x20202020, 0x20202020, 0x20202020, 0x20202020, +0x20202020, 0x00000a0a, 0x00000000, 0x00004000, +0x5ce80e00, 0x1f0ecb00, 0x3102c7be, 0x014fe8ff, +0xe8006dbe, 0xddbe0149, 0x0143e802, 0xe8df42bb, +0xbe9c00d2, 0xff31024e, 0x9d0134e8, 0xe80e0475, +0x2e66002f, 0x6602c3a1, 0x0574c085, 0xc32eff2e, +0x0018cd02, 0x50000000, 0x73736572, 0x74204220, +0x6f62206f, 0x6620746f, 0x206d6f72, 0x2e2e2e00, +0xbe1f0e00, 0xff31006d, 0xbe00f4e8, 0xeee8031c, +0x4c686600, 0x8c544552, 0x31e589d2, 0xbcd08ec0, +0x8b667c00, 0x66027736, 0x027b3e8b, 0xe8024fe8, +0xd38e0282, 0x502cf8bc, 0xcb030b68, 0x61747320, +0x6e697472, 0x78652067, 0x74756365, 0x0a6e6f69, +0x66566600, 0x53061e57, 0xe3891f0e, 0x127f8b36, +0x14478e36, 0x5d8b2657, 0x458b260c, 0x368b660e, +0x8b660277, 0xe8027b3e, 0x665f023a, 0x00557968, +0x6a680e00, 0xa7685003, 0x5b5bcb02, 0x661f075b, +0xcb5e665f, 0x01b45051, 0x067416cd, 0x16cdc031, +0x48b9f4eb, 0x15784900, 0xb40015e8, 0x7416cd01, +0x38f820f4, 0x9c0674c3, 0x16cdc031, 0xc359589d, +0xa00f5066, 0x8e0040b8, 0xa16466e0, 0xfb9c006c, +0x64669df4, 0x006c063b, 0xa10ff474, 0x00c35866, +0x85555350, 0x880574ff, 0x0feb4705, 0xb40007bb, +0x750a3c0e, 0xb010cd04, 0x5d10cd0d, 0x50c3585b, +0x74c084ac, 0xffd8e805, 0xc358f6eb, 0x10c8c166, +0x660004e8, 0x8610c8c1, 0x0002e8c4, 0xc8c0c486, +0x0003e804, 0x5004c8c0, 0x0a3c0f24, 0xe82f691c, +0xc358ffae, 0xe8c48650, 0x3ab0ffe4, 0x88ffa1e8, +0x03e8c0e0, 0xb0ffd7e8, 0xff94e82e, 0x0724e088, +0x58ffd4e8, 0x9009ebc3, 0x90909090, 0x90909090, +0x0000001f, 0x00000000, 0x0000ffff, 0x00009b00, +0x0000ffff, 0x00009300, 0x0000ffff, 0x00cf9300, +0x00000000, 0x00000000, 0x81e58955, 0x830028ec, +0x9c66f0e4, 0xa00fa80f, 0x0e161e06, 0x51050868, +0x07165756, 0x8d0440be, 0xb9ffd8be, 0x2ef30028, +0x595e5fa4, 0xf886010f, 0x665066ff, 0xc0316657, +0xc166d08c, 0x0f6604e0, 0x6667fdb7, 0xd838848d, +0x66ffffff, 0xffda8689, 0xe0bfc88c, 0x005ae8ff, +0xe8bfd08c, 0x0052e8ff, 0x58665f66, 0x0ffa5066, +0xffd89601, 0x0cc0200f, 0xc0220f01, 0x0804e1ea, +0x0010b800, 0x18b8d08e, 0x8ed88e00, 0x8ee08ec0, +0xff5866e8, 0x8ed08cd0, 0x8ec08ed8, 0x0fe88ee0, +0xfe24c020, 0xcbc0220f, 0x0f071f17, 0x0fa90fa1, +0xfff89601, 0xec899d66, 0xc0c1c35d, 0x02438904, +0xf0026383, 0x80044388, 0xc30f0463, 0x67f35166, +0xc35966a4, 0x053db850, 0x58ff2de8, 0x665166c3, +0x00d2e857, 0x66d9f766, 0x3150d101, 0xaa67f3c0, +0xc6836658, 0xe683660f, 0x665f66f0, 0x40b8c359, +0x64e08e00, 0xc10013a1, 0xd02d06e0, 0x6e2d5002, +0xe8c15000, 0x13a36406, 0xc35b5800, 0x57665666, +0x66ffdbe8, 0xbf66f631, 0x00400000, 0x660005e8, +0xc35e665f, 0x061e6066, 0xf68566fc, 0xce8c0675, +0x04e6c166, 0xc0c68166, 0x66000006, 0xb70f6657, +0xe7c166f8, 0xd4b96604, 0x66000006, 0x73e8ca89, +0xb70f66ff, 0xe7c166fb, 0x1eb96604, 0x66000004, +0x002cf8ba, 0xff5ce800, 0xdb8e5f66, 0x2694b966, +0xba660001, 0x00033908, 0xa3ff49e8, 0x1eff0392, +0x96a30390, 0xe4686603, 0xff0000fd, 0x6603941e, +0x5057665a, 0xe8052cb8, 0x6658fe5e, 0x901eff5f, +0x661f0703, 0x5066c361, 0x51665366, 0x66fc5566, +0x4d66ed31, 0xa46707eb, 0x75db0166, 0x006ce803, +0x3166f472, 0xe84066c0, 0x1166005d, 0x0057e8c0, +0x8366f573, 0x127203e8, 0x08e0c166, 0x66068a67, +0xf0836646, 0x665174ff, 0x3166c589, 0x0037e8c9, +0xe8c91166, 0x11660031, 0x661175c9, 0x0027e841, +0xe8c91166, 0xf5730021, 0x41664166, 0x00fd8166, +0x66fffff3, 0x6601d183, 0x8d666756, 0xf3003d74, +0x66a42667, 0x6691eb5e, 0x0b75db01, 0x1e8b6667, +0xfcee8366, 0xc3db1166, 0x59665d66, 0x58665b66, +0x000000c3, 0x00000000, 0x00000000, 0x00000000, +0xffb06477, 0x900323e9, 0xfa10cdfb, 0x9c1604c3, +0xefff065a, 0x203c7fdb, 0x01b90772, 0x1909b400, +0x0eb4db31, 0x8eda8e1f, 0x8ee28ec2, 0xf63dbfea, +0x89d28eff, 0x006a50cc, 0x0e58c3cb, 0x31d21350, +0x68520ee4, 0xed310100, 0xc0b7b7ed, 0x31c9312b, +0xff31f60f, 0xfb20ed31, 0xff369090, 0x06b76fdb, +0x56f9071b, 0x0372fb13, 0x26662066, 0xfe1eb70f, +0xb7c3077d, 0x55ffffff, 0x5250e589, 0x66f9000c, +0x0050f468, 0x19e80e00, 0x9c067002, 0x271eff2e, +0xff468f05, 0x06ffddff, 0x15ff7e80, 0x568a1274, +0x750808fc, 0x406a1e09, 0x75168a1f, 0xec891f00, +0xdb51cf5d, 0x756fdbf6, 0x009afbc1, 0xc483357c, +0x3236c304, 0xff664df0, 0x60ffffed, 0xa00f061e, +0xa166a80f, 0x256607e8, 0x00200cd7, 0x3609a366, +0x07f42689, 0xfe07c0bc, 0x0ffff77f, 0x07a10fa9, +0x6661661f, 0x118b369d, 0xeca03650, 0x04a22e07, +0x5800eb01, 0x0d8bffcd, 0x1024cdbb, 0x24449c66, +0x845b1633, 0x518d5a0d, 0x51fe15ac, 0xbb4f02b8, +0x08520003, 0xe7dd6c05, 0x5bbe0142, 0xcfcf0117, +0x9f3dfd0d, 0x3a098c83, 0x5100fb5d, 0xb0adfffb, +0x0355503d, 0x89665366, 0x0058bbf8, 0xbb005fe8, +0xc37b0560, 0x91596ded, 0x04963e89, 0x8c60dff7, +0x0a90a3c8, 0x7edfed85, 0xbb04e0c1, 0x003f1978, +0x1c8d6667, 0x2f1e1e38, 0x7fd8cb24, 0xa32ed81b, +0x801c0637, 0x6ed0d222, 0x52d29edd, 0x04068103, +0xdfed008b, 0x5b4bb1fe, 0x89cb5866, 0xc82e0247, +0x04478810, 0x73cf6788, 0xc1dbb785, 0xa12eadc0, +0x66d88e41, 0x85047036, 0x9a676f6f, 0x10c1833e, +0xd58ced5e, 0x5b75dc66, 0x4fd4b6f7, 0x345b61e8, +0xfa262b14, 0xbb5dff8d, 0x57160115, 0x00881e05, +0x0cc0200f, 0x07220f01, 0xedbf37b3, 0x696370ea, +0xfe240f08, 0x008e2eff, 0xdd8e1c5a, 0x8ec08efe, +0x8ee88ee0, 0x31d4894b, 0x5b47c366, 0x43285fe3, +0x161e0647, 0x10ec830e, 0xbb4e1738, 0x08b7f705, +0xfc004603, 0xaf48b966, 0xe94bed24, 0xde170b64, +0x5619ffbe, 0x085e0152, 0x701f142d, 0x15be17a1, +0x886e720a, 0xec24648b, 0x4766cb9f, 0x8fedeeba, +0x612b9206, 0x600516ff, 0x6419203a, 0x36dc3e39, +0x2c08ff29, 0xff926875, 0xfedcf6de, 0x66582dbf, +0x45524c3d, 0xcb017554, 0xdd1a18cd, 0x5f247755, +0x84866dd8, 0xe4e55446, 0x065417eb, 0xfb7c5357, +0x8b67fede, 0x8e12245c, 0x247c06c3, 0x260e0b10, +0x5f5bffcf, 0x2fff6ffc, 0x9ccb5c2a, 0x7556503d, +0xbb070e0d, 0x564eb8b7, 0x02caf89d, 0xfd2b9d00, +0x2edf6fd2, 0xf4fb06cc, 0x1acdf914, 0x072a0273, +0x1a7b6cfe, 0xee481608, 0xffb415c1, 0xc215cdc3, +0x0aeecbb7, 0x085d9c15, 0x03c2bb2e, 0x85bed08c, +0xbef1bfd8, 0xc05d2e03, 0xfb575303, 0x340000cb, +0x6db736d0, 0x8b188e12, 0x24265d14, 0xb6ff4bdb, +0x1102012d, 0x543b66d1, 0x3b707504, 0x8b8b7304, +0x6b5beeed, 0x3b02030a, 0x05750c4c, 0x7176085c, +0xb5addbed, 0x09030b8b, 0x192b2966, 0x816473d1, +0xdc15ce0b, 0x168d66db, 0xc1dedd05, 0xdbdbfe17, +0xd20a0ae3, 0x105fb866, 0x03b0be00, 0x83ffa9e8, +0x82d9ebc1, 0x9d0ac2fd, 0x1c5e8961, 0x25296166, +0x2be46410, 0x8325fb05, 0xffdbff10, 0xade81066, +0xffd4e8ff, 0x743c003d, 0x39c3dbeb, 0x7503e01e, +0xb6c75714, 0xbe56ffee, 0x14b91ce2, 0x5ea4f300, +0xf743065f, 0x067619d0, 0xdbe68dfb, 0xff0506c7, +0x753e83ff, 0xde2683a4, 0x9f0c001c, 0x741cb371, +0x02753e0a, 0x6651fbf9, 0xb5fda152, 0x111ebc6d, +0x06d02ff9, 0x356ac672, 0x417f1c15, 0x0874534d, +0x51eb74f9, 0x3d376f89, 0x5a661dde, 0x80c35966, +0x05515756, 0xe8fe89a1, 0x1eecfe9a, 0x647bbf07, +0x5e5f59a4, 0xe972ff8d, 0xdedf0bb7, 0x555666c1, +0x855e5d56, 0xa0be31f6, 0x37ff55c6, 0x72ddddbb, +0x8b26e041, 0x04550305, 0x4d085d04, 0xf3cae80c, +0xfea2db74, 0x89158907, 0xb46f8989, 0x83e8dfba, +0xfe8110c6, 0x17f804dc, 0xb656eb89, 0xa26fdeed, +0x665df85b, 0xe8abad5e, 0x29194f9f, 0xe377cb63, +0x75ed7d83, 0x080c0710, 0x6db4589d, 0xddebec61, +0x2a0cb2f8, 0x742b24d2, 0x6c2e3afb, 0x204e14de, +0xd2ba98e8, 0x8b70ac66, 0x16009bfd, 0xe8e78907, +0x1e29ffb2, 0x697e7361, 0x4d47305b, 0x2e36fb1e, +0xffc5e81f, 0x2761ff1f, 0x9c095938, 0x91fe8514, +0x7fe8da87, 0x7ecaf676, 0x1e9d1f05, 0x3d15fe16, +0xbdbfbf69, 0x81be75db, 0x9dfa6afa, 0x013dabeb, +0x80b107e8, 0xcf1edb29, 0xb0c888fc, 0x1d000037, +0x9093fbac, 0x45585021, 0x16800e58, 0x05610335, +0x3d03b35c, 0x2cf400ff, 0xec2f216c, 0xd4072cf8, +0x5edf0006, 0x395e2b72, 0x3802012b, 0x00000326, +0x646c36d8, 0x40470b4f, 0x00000049, 0x00240008, +0x0000ff00, 0x00000000, 0x00000000, 0x00000000, +0x9287ffb0, 0x58506700, 0x2e302045, 0x2b332e39, +0xffff6f0b, 0x7474689f, 0x2f2f3a70, 0x65687465, +0x6f6f6272, 0x726f2e74, 0x00022d67, 0xeed9c837, +0xffff3760, 0x07cf9f04, 0x8f20b093, 0x009b0f2c, +0x850fdd07, 0x028f007d, 0x42690000, 0x35d05446, +0xf641f601, 0x454600fe, 0x5359534e, 0x01012fa3, +0xbedd0712, 0x00503fb2, 0x011000a0, 0x4a01021f, +0x11000304, 0x008053d8, 0x4f660103, 0x2ca3ec88, +0x36010400, 0x1051c76f, 0x93b60096, 0x5204d84d, +0x070a05a7, 0x4b0ba6ff, 0x61450081, 0x1180002f, +0x02002ec8, 0x00000000, 0xff400000, 0x00000000, +0x9bffffff, 0x53565700, 0x347504a8, 0x16e01d8b, +0xeb830001, 0x2c738b2c, 0x538b18eb, 0xffd88940, +0xffed6fb7, 0x0f070452, 0x42890447, 0xf3891002, +0x832c768b, 0xde8d2cee, 0x7bf7fede, 0x2bff812c, +0x5e5bda75, 0xbb3dc35f, 0xeb0c23d0, 0xfd8d8d3d, +0xa137dbf6, 0x2c708916, 0x46c72c43, 0xbe350c04, +0x05fefb3f, 0x8934438d, 0x12563802, 0x0b74c085, +0x76ff4659, 0xc3834ffb, 0x14fb8144, 0xbb722724, +0x53c3c351, 0x0d8bc389, 0xbbf6fffe, 0x12eb36e8, +0x75085939, 0x3b0c4129, 0x31047502, 0x8b0febc0, +0xfff98109, 0x19fff77f, 0xc5b8e675, 0x5bd2fa9f, +0x0cec837d, 0x0f0c588b, 0xe68333b6, 0x02e6c10f, +0xfdadfff6, 0x890c4b8b, 0xae05240c, 0xc604244c, +0x00082444, 0x88094b8a, 0xfdbfbb0b, 0x408b09ff, +0x66d82910, 0xc486f029, 0x0a168966, 0xb9c2b70f, +0xe289fd0c, 0xed6fb9ad, 0x060a13e8, 0xc483c00e, +0x904e7d0c, 0xdaddf232, 0x224e2404, 0x06055003, +0x6fdbdb09, 0x68500c3f, 0x6a7c0347, 0x26a268ff, +0x9549e806, 0xf6efb837, 0x39097b6d, 0xe900b120, +0x5500ffc1, 0xf6779157, 0xc710dc1e, 0x80c75b2c, +0x6e1d0407, 0xff42bfff, 0x338bbd64, 0x438b1deb, +0x7d69e808, 0x0b138b19, 0x09d083ee, 0x7ce8d85f, +0x36669075, 0x0ac7ddb4, 0x8bdbe40d, 0xe907f83d, +0xb786030f, 0x5f422ec3, 0xcd8d545c, 0x2e98ba0c, +0x76e705ef, 0x42825031, 0x840f5117, 0xfbbecee5, +0x86c2fdbe, 0x10cac1d6, 0x78d28504, 0x14ff7d0a, +0xd08938eb, 0xdb39bb25, 0x3dc007ed, 0x17758004, +0xe28120ff, 0xd872d811, 0xfa81e0bb, 0x08ff171c, +0xe4f65bb7, 0xba086a6e, 0xc781e5a8, 0x399e1b17, +0xb8ba04b9, 0x8b3682cd, 0xddddd66c, 0x748b2e6e, +0x3b18b81a, 0x8f74b5e8, 0xf75a99c3, 0x0775f85f, +0xceacfab8, 0xf8895ceb, 0x137c6fe8, 0xb7f8247b, +0x0d2bd1ad, 0x73f46b31, 0x18744614, 0xb42feca1, +0x1d2afbf7, 0xfa03c705, 0xeb18101c, 0x976b1017, +0x580ba1b1, 0x0b10c703, 0x9fc6dded, 0x83087f3f, +0xf08408ef, 0xe2850f0b, 0xc27c8ffe, 0xbd31868e, +0xc35d8d10, 0xc5892880, 0x3f1b1bf0, 0x0c708bff, +0x0c9014b0, 0x14b9db31, 0xf6ffbbf1, 0xfcaff71d, +0x06c6aaf3, 0x4646c645, 0x2b661045, 0xdec40c45, +0x4610ff0d, 0xa0a16602, 0xa36640c9, 0xcdb61206, +0x463d6ffd, 0x428a4008, 0x09468808, 0x86484bde, +0x8d8c7b4e, 0xfff9aa37, 0x9d15b774, 0x5b5a8b1c, +0x785c287e, 0x7c4233c8, 0x687d1042, 0x1ffffb83, +0x8fdbb0a1, 0x128b08f4, 0x83dcc253, 0x635a247c, +0xa43cb77b, 0x917dd184, 0x63f5f220, 0x4cd0b6f7, +0x8b155428, 0x754d2042, 0xc37c5912, 0x12487f0d, +0x8d14508b, 0x3b04e842, 0xcd866395, 0xa343c9c1, +0x57c2285b, 0xf0f034e1, 0xb766e5a2, 0xcb2c75e3, +0x0d04010c, 0x8a5e0e00, 0xfb6eba69, 0x7fe08321, +0x0a0f0688, 0x23100722, 0xb86beef4, 0x9e22eb11, +0x04504f20, 0x50ba5028, 0xcef08377, 0xe844c29f, +0x4a919563, 0x74004096, 0xdb1bf8e7, 0x40288b15, +0xe88911fa, 0x9dfcb3e8, 0x08fb4314, 0xba07f837, +0x37e8f02a, 0xed070607, 0x0a58d8b6, 0xb644b949, +0x03e12b40, 0xedc2ddb8, 0xefbb6646, 0x4e0fd7d6, +0xa5eeb56f, 0xd90a2e56, 0x528d2e8b, 0x3cde8908, +0xe9c7050b, 0x976e2702, 0x50c1c228, 0x994e684e, +0x824c9550, 0x8c52dbe3, 0x0f13fdf1, 0x01dbe386, +0xc743e5bb, 0x40f0a016, 0x12d040f8, 0xebbee283, +0x8d0fee0e, 0x2553ad1c, 0xcb3912bd, 0x6eb5870f, +0x07edd987, 0x9b9bda89, 0xa385f406, 0x854df711, +0x02556685, 0xd839c2b2, 0x1092820f, 0x637f6ddf, +0xca584d8b, 0x390c552b, 0x0f8232d0, 0xd129c229, +0x7ba16b89, 0xf4ba136c, 0xffa6c7af, 0x5bf0b904, +0x5d0306fb, 0xf76602b0, 0xb83f0646, 0x1bade084, +0x540de3ff, 0x018b3beb, 0x75723b66, 0xdb6f7b30, +0xa8920a37, 0x718b2875, 0xfa547eb3, 0xb6ff562b, +0x670cb7ee, 0xff250643, 0x33c2901f, 0xe8c89001, +0x858771c3, 0xe9066eff, 0x3049790d, 0xb330e983, +0x2ddbfcc0, 0x2abab3b5, 0x2078163f, 0x61f285e6, +0xb4f76d6b, 0x991b38b8, 0xb8c68930, 0x97768863, +0x05066d0c, 0x05dc3437, 0xb56e1918, 0x4460cdc2, +0x52c283bc, 0xe1695a05, 0x8bbbcacd, 0x0f0d1078, +0xa6f78008, 0xe1419763, 0x6ddac338, 0x3218468d, +0xb75e0600, 0x942ce9f4, 0xa31046ab, 0xc2158204, +0xdaffbec6, 0x5655300d, 0x0440c730, 0x74d7a30f, +0x97ee6d35, 0x245c8d28, 0x448db930, 0x63660adf, +0xd1343343, 0xb3d6d830, 0xcc008d34, 0x625bb160, +0x1c1e243c, 0xd0bb4010, 0x142c58d0, 0xd00956a5, +0xe3a16ebb, 0xc7141011, 0xb26585d9, 0x67932feb, +0x538dd93b, 0xa6048da9, 0x74d83a46, 0xb833980d, +0xb8280a0c, 0xda46dffe, 0x12e331d8, 0x0090aa50, +0x013840f6, 0xb706c0bf, 0x1c50ff74, 0x48420c6a, +0x44c08344, 0xc0dfc083, 0xc9eac139, 0x118b0aeb, +0xc818418b, 0x07b107de, 0x0d042a12, 0x6edb9b2e, +0x8326420e, 0x365b124b, 0xb06f510e, 0x2210bf7d, +0x10e3f8a1, 0x10371810, 0x6f780575, 0x5139f06d, +0x40317414, 0x3d08e82b, 0xad06cffd, 0x2e3d3435, +0xbb1250ae, 0x66c1ba65, 0x09752939, 0x0453f200, +0xb02ddf0e, 0x75108341, 0xe6721990, 0x776c7ee8, +0xb47e15c3, 0x544cc25b, 0x0f6ee899, 0x70785fe3, +0x6d5843ff, 0xb90eebb3, 0xf4f79f60, 0x1f0ade85, +0x5ed2dd18, 0xc3b56b44, 0x4eee0e74, 0x85ca04bc, +0xd3921bdb, 0x040fc985, 0xff87dfb6, 0x03eb4c40, +0x5bea5004, 0xe17e37e9, 0x5e5419b9, 0x8d3c50dc, +0x744c3c40, 0xd785b108, 0x52c8167d, 0xba33cc5b, +0xc0f6b767, 0x70d66002, 0x75022143, 0x9846eaec, +0x180a5b84, 0xcf1f0435, 0x4777b6f6, 0x065fe868, +0xfe386383, 0x7ba75722, 0xb9b0236f, 0x3e042355, +0x1a89e289, 0x0b4b86f6, 0x8b3646b4, 0xd7f7b4bf, +0x15b13e24, 0xc21c4ede, 0x8ec77539, 0x9d38f989, +0x49d1c6f8, 0xaed7f84b, 0x192c36ed, 0x01cbdf45, +0x77e8fe54, 0x50d0bb20, 0xf7f8fb68, 0x56ffcdfa, +0x86c73650, 0x0937c36e, 0x046b4fd8, 0x9385ea40, +0x82f51afb, 0xf2c7c33e, 0x34236b3e, 0x95f28778, +0x28f6c111, 0xf27dae6c, 0x2b834ce8, 0x965b7e36, +0x34284605, 0xe4977166, 0xf63e3b9e, 0xe8da327b, +0xe85cec1d, 0x53c97faf, 0x10f23d08, 0x3a311a4e, +0xfedfe876, 0x8d8b72e9, 0x16066fef, 0x8c056e47, +0xee8e8354, 0xc13b4de9, 0xb1f44a5e, 0x6ef9fdc0, +0x41890183, 0x418d4002, 0x8d488944, 0x2ed65c51, +0x680bb6b9, 0x41525552, 0x76cf0570, 0x42ff075d, +0x1c42c718, 0x495c1d20, 0x8d6e101b, 0xf871b881, +0x81894efe, 0xf5c80588, 0xf7b41564, 0x173bdfc5, +0xa301428d, 0x94685207, 0x68d30807, 0x507b9bee, +0x688d42e8, 0xe8d231b1, 0x7af0b327, 0x893875da, +0x82a1219c, 0x78fc8d75, 0x0011df28, 0xc707a308, +0xc6ed890d, 0x02966857, 0xf48df26d, 0x110d598b, +0x51b7b636, 0x5a890a02, 0x54271304, 0xc49e1ac0, +0xfd082902, 0xd45751eb, 0x078d97f9, 0x4768779d, +0x8b3d74c6, 0x86468b16, 0xf607f037, 0x5f8b2f74, +0x8906ef20, 0x179850e0, 0xd00a1422, 0xb8ca9633, +0x4aee7f69, 0xb111960d, 0x2434ff1b, 0xfffd00e8, +0x8903f0f7, 0xa4753cff, 0x90eb5a58, 0x0c340b70, +0x0b9ac605, 0xff490000, 0xf217a6ed, 0x7649eb31, +0x3b14412b, 0x3b720841, 0x81fedd8a, 0x9311c776, +0xd11841ff, 0x10794661, 0xb6fb5285, 0x10120741, +0x31989680, 0x6c6b0171, 0x0fae4371, 0x747ac297, +0x1b170970, 0x5c39428d, 0xd8cdf163, 0xf95fa330, +0x2daf7552, 0x4682e5c0, 0xa77dba4b, 0x744b7b83, +0xdd771434, 0x14c2b16d, 0x18115e1e, 0x894806c3, +0xb737f06f, 0x491eeb07, 0x03eac1c2, 0xd131d029, +0xdb1601e9, 0x14c8b633, 0x78832a08, 0xef22752c, +0x00e4b637, 0x051d0b17, 0xf06381e8, 0x4e68e0d6, +0x1b098c3f, 0x75d0900c, 0xb56a506f, 0x77066053, +0xd8070d07, 0x668835bb, 0x03734c20, 0xee8d9851, +0xd689f8fb, 0x73c0a4e8, 0xd6119061, 0x75d5abfc, +0xb742f783, 0xfe1f8fd0, 0xb063897c, 0x77c1f674, +0xc2016905, 0xfeffa5e0, 0x06764d41, 0x4107ea81, +0xdd75d939, 0xc95b7e28, 0x3cc02d7d, 0x17b8b3d5, +0x71d8b6e8, 0x1350e72c, 0x3b28bb3a, 0xa6bc1beb, +0x3bf511a5, 0x104e0f8a, 0x2d5b4b03, 0x82edb522, +0x3734100c, 0x2275a170, 0xf9ab4972, 0x9ffd2e67, +0x4c9389f1, 0x3c4efab4, 0x6bc5cfd2, 0x14b7f6c3, +0x19eb22bb, 0xdd753238, 0x79c250c0, 0x14a13b06, +0x4c4304f9, 0x186701df, 0x7abbb5a4, 0x0a966f44, +0xd6d4520f, 0x913a3a80, 0xc1dd1f40, 0x685f112d, +0x0240453f, 0x160f4125, 0x9abac73b, 0x80bb36d5, +0xc7b81126, 0xf8169add, 0xebe2f39f, 0xf202e02a, +0xc6205f57, 0xb8602bb4, 0x5f40bf19, 0x7f972705, +0x8f1b0ab6, 0xc783e1cc, 0x9df517ab, 0x30e81d31, +0xbcd8fbe0, 0xd1ea3804, 0xb95b4ba7, 0x6af5970a, +0x2c1bb398, 0x80f6fe74, 0x56077417, 0xb7a74bb5, +0xb908ac1f, 0x572a6301, 0x4cd1068b, 0x440b88d7, +0x764b490d, 0x9aa8417f, 0xbfd54ccd, 0x05b99c9d, +0xfffb60ee, 0x7849d62e, 0x75aeac08, 0xf5a18408, +0xc0190410, 0x85760c56, 0xba3dad19, 0xa6bd90d6, +0x9734d9d8, 0x75977d2d, 0xbbdfdc7f, 0x933d7f5b, +0x3d297460, 0x3d090694, 0xec2b1cc8, 0xc81935aa, +0x39d2f349, 0xbb16f693, 0x4043e131, 0xb8055686, +0x6b7111c4, 0x59beec46, 0x40551dd1, 0x5745b103, +0x5d7e37e0, 0xbe3c170a, 0x0cb7a5b6, 0x24dd35be, +0x18057163, 0xde196ef9, 0x1cf40822, 0x8b2de944, +0x99c40810, 0xe94725bb, 0x34d1121a, 0xc876ab7b, +0xabd92550, 0x87187a83, 0x7b78be1f, 0x48febf0a, +0x20eae9e3, 0x69813350, 0x082d9789, 0xcefa1983, +0xbbe8d0e9, 0xd5d6e7ba, 0x3e080f26, 0x1d0c4b0c, +0x18ba9d1d, 0xb40863fb, 0x2ed6e8a5, 0x870ebaf1, +0x8d93588b, 0x73918e73, 0x23b1951b, 0x2320cc5e, +0xbb6df5aa, 0x0ea72d9d, 0x640c585c, 0xd48de970, +0xb52c59c4, 0xf4ef1110, 0xc300478b, 0xcf1434d6, +0x122a43a6, 0x47428189, 0x2f30a21b, 0x3f323448, +0x595b1cd8, 0x48056930, 0xd02e7535, 0x7276f12f, +0x446f6a30, 0xe9891872, 0x45a2f7ba, 0x9a2069a1, +0xe3a31edb, 0x180fdeaf, 0x6505e81a, 0xcba5095f, +0xdb82c09d, 0x7a6dbdb1, 0xb060b661, 0x2a342b40, +0xa25db945, 0xdcf7c6e4, 0xe9b1da1e, 0xe609b689, +0x180708cd, 0xf53481e9, 0xbeda97c3, 0x66dcfada, +0x4a78a240, 0xf0d91850, 0x4d410e8d, 0xf46b8d24, +0x8e8999f5, 0x4b1b81ae, 0xf60a1b5c, 0xb70da967, +0xeb33a38e, 0x03141306, 0xee6c85c6, 0x0578b937, +0x73d085bf, 0x57505604, 0x5e1da568, 0x55a940cd, +0x7187e614, 0x9639afdc, 0x8ea1c288, 0x7709e992, +0xd19819bb, 0x72ffcf08, 0x1f55bae8, 0x3f82a5b8, +0x78cc2b33, 0xc6ff0bce, 0x18ce6784, 0x31e8588d, +0x3a6bebed, 0x1bb7fc40, 0x5a025f5b, 0x03063172, +0x66eb5c75, 0x863c4b01, 0x0270eb92, 0x7763470c, +0x3f2e3112, 0x633cfb4c, 0xeb5e7239, 0x81a0dc0d, +0x06eb93a1, 0xf1e6447b, 0xbfab9b23, 0x3d78c264, +0xb70d2501, 0x9b0bd974, 0x404beb6e, 0xd0ffd83d, +0x46011738, 0x8150a87a, 0xc196b02c, 0x17d4a266, +0x85897571, 0x4a7dc8ed, 0xcf6eb76a, 0xeb200a56, +0xedeba9f1, 0x3b16e903, 0x63b3a26d, 0x8a43c511, +0xaef03102, 0xc404a0ed, 0x0756a8c3, 0xbe0fc302, +0xbfdd8051, 0x010254fd, 0x64b9e5eb, 0xd1c8ba5a, +0xbd8d0bb9, 0x73fbe80f, 0x05ff0986, 0x2f891660, +0x0113e904, 0x39e8207f, 0xe5ada1fa, 0xe914e32e, +0xfa21dc43, 0x4a0513e1, 0xe9c7edcd, 0x70bddb7b, +0x050e9769, 0xf2ee1cba, 0xc2eb14c3, 0x0f00d804, +0xa35fb89c, 0x7e155eb8, 0x6043f6b6, 0x53354b43, +0xd2c48b60, 0xa54fed87, 0x04c183d1, 0x485b1445, +0x7ef02a86, 0x77101dc3, 0xec708d8f, 0xab24868b, +0x40b522ad, 0x0b3cba00, 0x605d4b16, 0x0210776c, +0x5c60464c, 0x1bb7bf6a, 0x6b9e0187, 0xe953f999, +0x10190087, 0xa05bf866, 0x09b2da01, 0x76750506, +0x35b4c142, 0xde46d14c, 0xefd124cc, 0x7db2a142, +0x50325de9, 0x9aa4470a, 0x0feb04fb, 0xffad9848, +0x0168585c, 0xc32f8adf, 0x5a31ea2d, 0x512ff85d, +0x17048a07, 0x47410188, 0x9f29c32c, 0x17e85ec6, +0x01ef7f18, 0xdee184fa, 0xc7908e89, 0x19bb5b70, +0x02410421, 0x1074a105, 0x56236add, 0x300206ac, +0xfa00f77c, 0x4d8b41a0, 0xd836e05a, 0xde861b1d, +0x0cc583c6, 0x5e6ea12b, 0xdd9c188d, 0x4b0468e8, +0x21f17f2a, 0xb58d5806, 0xebd9a7d1, 0x216c515f, +0x95b606eb, 0x1bc24728, 0xad529803, 0x8ad885c1, +0x629a880b, 0xff42a142, 0x841e750f, 0xb87c74c9, +0x06c4da49, 0x073ae36e, 0xa2a34215, 0xee99bed8, +0xeca52aaf, 0x0667d5eb, 0x86085040, 0x5f6a6fd6, +0xe9d25cda, 0x4cff0a02, 0x000ccd60, 0x6b309a7f, +0x07160ad8, 0x080b1f06, 0xbc20d0db, 0x0deb2321, +0xeb53c765, 0x05fd0c33, 0x38810776, 0x27ba2624, +0x2debc1ee, 0xeca0feba, 0xe306be46, 0x28b826eb, +0xe8fda461, 0x891aec75, 0xaceb698d, 0x24080f73, +0xbddb41f8, 0xc38320ff, 0x5ddb9efd, 0x200d046c, +0xcbba0a1e, 0x2181b31c, 0xa8e9ddc4, 0xfde1e91a, +0x81af1435, 0x66ce27c7, 0x17793d83, 0x702175be, +0xf3e9c0f7, 0xe8107817, 0x0f176469, 0xbecb2e04, +0x1000d9ee, 0xb45e1c64, 0x096021dc, 0xee6f5117, +0x5d00046d, 0x80084513, 0x713c1052, 0x598ddf12, +0xf240281f, 0xef2b90be, 0xd8163643, 0x050162de, +0x6c36015d, 0x78586d92, 0xebf88de1, 0x0315801a, +0xf8ad752e, 0xdf42c12f, 0x48c828d7, 0x62d66288, +0xca028803, 0x660a3b11, 0x568a0431, 0x50305f60, +0x758474d7, 0x24d088d4, 0xe8fc2718, 0x892602c6, +0x1c424683, 0xccb9f818, 0xbad2e040, 0x70e52201, +0xfe35368f, 0xeec72b66, 0x6a1c0878, 0xd92bde47, +0xc130435f, 0x6b4c1191, 0xa0d83784, 0x3d2b00bb, +0x957751ff, 0xb7b84e00, 0x40a3ac9b, 0xa3c30c18, +0xa46d50a4, 0x0b05d35f, 0xb957bc1d, 0xc4d8ba04, +0xe9ac7bfb, 0x16398436, 0x06a13f74, 0x65ed21e8, +0x7d1d01b4, 0x040c6850, 0x0c0520b9, 0x2ec70e8c, +0x6a8287f2, 0xb7537c41, 0xc43625ec, 0x15ceedab, +0x1b537de8, 0x70788d89, 0xe1226ac5, 0x1289d65b, +0x5b1e29af, 0x5b1bd456, 0x83011894, 0xed1325fb, +0xd95e7921, 0xf5285ae3, 0x5ff03062, 0xa859ab38, +0x503b55c7, 0x89357670, 0x72077057, 0x0f4628b2, +0x17398347, 0x83876f4a, 0x77f70a0b, 0x47a0ae30, +0xc03ab064, 0xb7e9130d, 0xb0aaa551, 0x725a16db, +0x7440c648, 0x85f5982d, 0x616370d8, 0x7e4706d9, +0xc0bfee7d, 0x86b585d3, 0x20584846, 0x752f3b80, +0x86d04301, 0xc9b7fead, 0xaef2df22, 0x8d49d1f7, +0x2a14186e, 0x07712391, 0xb60cad87, 0xe4dc840f, +0xb4e1b810, 0x94026a6d, 0x53bc0a10, 0xebf61768, +0x8b386e97, 0x505c1447, 0x814ee852, 0x1e030138, +0xb82f816d, 0xf6141c21, 0x24f75c46, 0x1dbb0125, +0x35ff016e, 0x2168079a, 0x6dd3762e, 0x47771f99, +0x74041c29, 0xbab7181d, 0x2210dd3c, 0x52c25737, +0xed80fc45, 0x2261c036, 0x15e85f10, 0xdb5beb8d, +0x2dd15ead, 0xe704f901, 0xf1ab06fc, 0x007ad8ff, +0x8bed3c1b, 0xc6836c7e, 0x2b04ba18, 0xab08e93d, +0x741681f0, 0x6cb8eb23, 0x4e54d70d, 0x0728b843, +0x6c03ddf8, 0xc8426c0f, 0x2690dee1, 0x5c2c05eb, +0xb5e20584, 0xe2c26eda, 0xe874422f, 0xfffff460, +0x80836ac2, 0x30166c19, 0x08de5910, 0xc7e6db04, +0x44ed1043, 0xc2747214, 0xb6ebb025, 0xb05f2486, +0xa6ddbfc0, 0x5aa0728b, 0x83f32910, 0xf4a601fb, +0xd85cd56d, 0x57c985c8, 0x9f5101ec, 0xea50a0d2, +0x1de10ab4, 0xf7599cc1, 0x75a21748, 0x07e88a0c, +0x0b129c28, 0xe0f0a3d6, 0xb9827595, 0xbdbdbe03, +0x6606f5ad, 0x76348e3d, 0x7406090c, 0xdd81310f, +0x1c0305d6, 0x9de90f9c, 0x8b46f862, 0x1c8d7b1b, +0x8004d610, 0xc212ff7b, 0x87f2681f, 0xeb0258da, +0x36eceb57, 0x01eab713, 0x684afc3b, 0xfd20bf1e, +0xa9323476, 0x74ce2ad2, 0xc012756d, 0xd1c8f04f, +0xe90e11e6, 0xc1b39349, 0xdadac5e8, 0xae14f746, +0x0e5ccaef, 0x8ba37269, 0x8cce3455, 0x3fd5c31e, +0x853dc631, 0x16840112, 0x676bef96, 0x3c9cf6dc, +0x585f0d4f, 0xc3037170, 0x0f18c829, 0x88c6118d, +0xb3419c31, 0xb12fcc48, 0xd8558d00, 0xdef64762, +0xc868c629, 0xd639fa6d, 0x9f8b6e77, 0x7175af0f, +0x68610b10, 0x9cd7bbac, 0x2ad01490, 0x1e2148b4, +0xd083d0e5, 0xaa3c5a86, 0x3f6f7fbb, 0xeed9fd75, +0x7933148d, 0x758afca6, 0x645d8d2f, 0xf0a20c56, +0x55e1db1a, 0x68d2944e, 0x9970bfbd, 0x5979da3a, +0xbf6e3286, 0x4e0da500, 0xa0c7e78f, 0xedee1f7c, +0x7be8067e, 0xdd872f5b, 0xb64d5420, 0xcc602617, +0xb82b7603, 0xb8b85b40, 0x7704cabd, 0xe0852b0d, +0x07f75b20, 0xda89adba, 0xc346fdef, 0x19f2a933, +0x356d9d8b, 0x061e5625, 0x6033904c, 0x9c0eebae, +0x0d6d5e1c, 0xa3a0c1ee, 0x2423240c, 0xf129b0e9, +0xfd436360, 0xe9ff4883, 0x1db86d60, 0x00305a8d, +0xc8b71870, 0x1b07a9cf, 0x6383f3de, 0x5320fe5c, +0x04fb333c, 0x0f30b686, 0x0a270a7c, 0x10a154ae, +0x7c0f5a6e, 0x8c68c147, 0xaccb38d3, 0x036d7409, +0x2044ff11, 0xc0608a18, 0x2a4bb747, 0x9e300067, +0xa51b58dd, 0x7624e20c, 0x5056646a, 0xd602b36e, +0x614b0e27, 0x0bd483e3, 0xe58955db, 0x6589c6c3, +0x37fc17c0, 0x89d7391e, 0x83338dc8, 0xe08313c0, +0xacc429fc, 0x610deecb, 0xf0e3830f, 0xba252f37, +0x0783d72c, 0x7299f09f, 0xc67d74ab, 0x9d3f27b7, +0xc845763b, 0x1880e81a, 0xec556616, 0xc50b6f40, +0xe8c931f8, 0x11945dbf, 0x458be666, 0xbad02fec, +0x48753800, 0x804acdbe, 0x52e0ed7f, 0xde44d303, +0x7de2cc45, 0x6c6db5cc, 0x58d02eb7, 0x263f5e14, +0xdb3bf00d, 0x2eb6d49e, 0xc05d50c8, 0xf00dce45, +0xbf0d843c, 0x1a020be0, 0xb80982ff, 0xb7bb4399, +0x8bbc026d, 0xf4658dc1, 0x0fc3c9fa, 0x624350c0, +0x048bf0f7, 0x36af408b, 0x199ccdfe, 0x89efc409, +0x88d62066, 0x5ce0b094, 0x80cb567b, 0xa0c1ed88, +0x2fc01bd7, 0x78aaf123, 0x474777f6, 0x9fd5c008, +0x561780f8, 0x1473ba73, 0x6ac1438d, 0xa9969224, +0x246096d8, 0x13026b2e, 0xba30a9f0, 0xeaf1356f, +0x5ccd1c13, 0x609083c7, 0x73707d51, 0x4bea5de4, +0x38527307, 0xd4e278b9, 0x4878e2f8, 0xd8bed52c, +0x74acb57d, 0x31171874, 0x357596fe, 0x6ee5b14c, +0xc2a721d9, 0x4d88fafe, 0xab587c53, 0x01e8bbc6, +0x703c63fb, 0x63e2280c, 0xf0a5be14, 0xa0cfdddd, +0x086aad5b, 0xb984a068, 0xe8eb06df, 0x57e26bec, +0xc2066a94, 0x0ed9fa45, 0x6439d42c, 0xbeffca02, +0x741cb76f, 0x76d057c5, 0x08b0f68c, 0x51ba5374, +0x7be05b2a, 0x40607195, 0x3936d826, 0x4476faef, +0x3f6b5cd4, 0xc206c06e, 0x7d0a97b0, 0xc183e8f6, +0xe957d035, 0xa7b734ef, 0x7a76bd93, 0xdfec9754, +0x3a55b05f, 0xfcfea1db, 0x8a6debcb, 0x368edb09, +0x08fd7530, 0xb2cbb8e5, 0xa9c362b3, 0x0fc289d8, +0xa2a3ee49, 0xc559dcd8, 0xd809b012, 0xa96e64fe, +0x20d81457, 0x68a01d54, 0x90c020a9, 0x2c734b90, +0x146ff769, 0xa5c781c8, 0xb9a4a566, 0xf4ba6907, +0xc622dfca, 0x49702186, 0xfd7fff08, 0xde2fc4d1, +0x34852a1a, 0x5a5122e0, 0xc1b7b159, 0x0cfd6b88, +0x62a2e837, 0x7665babf, 0x435536c1, 0x5f6f00f0, +0xb7e2472c, 0x4d2b315e, 0x8df0d1f0, 0xffba1304, +0x342e6fea, 0x045163e2, 0xf579d829, 0xd4752b7c, +0x04dc1a3a, 0xfdc7e865, 0x4ae705ec, 0x362e84b8, +0x83f44154, 0xf86defc1, 0x04c63521, 0x4e31ff33, +0xdb7c11dd, 0xe8af1be2, 0x068b17eb, 0x94724239, +0x0d6edb31, 0x10ca2b9a, 0xeb365833, 0x8df186e9, +0x54ea8aed, 0xd8981091, 0xe3e2c5d8, 0x888b3b86, +0x7dfac288, 0xa37d4dc2, 0x8311236f, 0x66103cc2, +0xb1b1c3ef, 0x8befeddd, 0x10b01c98, 0x37c2898b, +0xb5b8ee14, 0x8247c21d, 0x047b1c72, 0x1c8f14b9, +0xe1428b7b, 0x495f6ce3, 0x52838b58, 0x1c6d2f84, +0x08727878, 0xf6e858f3, 0x33a3aa5b, 0xeb38718c, +0x2f72a653, 0x305ad128, 0x7f79c357, 0xd6ae673a, +0x8c3d2f0c, 0x598fe800, 0x5f520736, 0x06283b6d, +0x5046d840, 0x06dec201, 0x22ecf0d8, 0x0f89fc81, +0x9a8a76b6, 0xb8dffe0b, 0xc61dac15, 0x88ecb550, +0x21d2f7da, 0x09cb21d0, 0xf82410d8, 0xee0f1ee6, +0x1dc35e5b, 0x4b7d2817, 0x8b61a818, 0x663e14f9, +0xb6a1b45e, 0x22c84bed, 0x2da30d84, 0xead00af7, +0x2217f268, 0xa589b83f, 0x889d25db, 0x4eb81f74, +0x1b77d988, 0xe0d3fd70, 0x1274f085, 0xe9458d39, +0x089d44bb, 0x7c0a2687, 0x2df34399, 0xa180dab8, +0x47cdadf6, 0x1845031c, 0x6d06163d, 0xda70c0d5, +0x6b1a7113, 0x68e0c1fc, 0xb9157d55, 0xced5bffa, +0x96e642d6, 0x10378122, 0x6834e9e8, 0x8bddf0d1, +0x108c8d2d, 0xce19e004, 0x4fb90b07, 0xf63103dd, +0x4df1293f, 0x5a250444, 0xb90b4309, 0xbe051fbd, +0x20ab56dd, 0x101855bc, 0x7be2eb0c, 0x114fa5f0, +0x1ff3e8f1, 0x35d29587, 0x6797db22, 0xfeb90feb, +0xbdd5e363, 0x5d985d4d, 0x83030734, 0x2e29fc25, +0x0ec14334, 0x994543fc, 0x63b638c2, 0x0ae01163, +0x01a8f837, 0x1c2637ca, 0x587d1b16, 0x93c6f95a, +0x15a005ff, 0x31e0f9f9, 0x4241ee24, 0x2606f983, +0x75f775f5, 0x8a04b8f3, 0x6457f4e8, 0xdbbb1518, +0xebdbeff5, 0x030b8b43, 0xaf1bc005, 0xef513051, +0xfd080cb0, 0x31467a6d, 0x516a840e, 0xc8831444, +0xb670a8ff, 0x060809b3, 0xc20b3d0c, 0x03a45b67, +0xc031400b, 0x30107ab0, 0x4246409c, 0x02f3466c, +0xe962dd86, 0x20008f24, 0xf0722338, 0x1c407e13, +0x00011860, 0x584689d4, 0x3c0cfb89, 0x941cd884, +0xdd4446a8, 0x07c6340d, 0x7aab7d58, 0x75bbdd32, +0x2c781773, 0x39742873, 0xc9102006, 0x6c8f03c2, +0x49d7e820, 0x10440817, 0x40dbc0db, 0x10743013, +0x6e0080ae, 0xe25749d8, 0x400f98eb, 0xc2b14406, +0x0130ec6c, 0xfc06080d, 0xcce02d06, 0x22a70ce7, +0xe1594810, 0x715bb744, 0x025257f2, 0x16305755, +0x8b605358, 0x7cb9dddd, 0x58b2e880, 0xaa244b24, +0xf4fdd830, 0x07ba3d5d, 0x4b34c2ef, 0xa4af0238, +0x7a68dbe8, 0x831eb910, 0x9f2d457d, 0x11dd285c, +0xc64852e0, 0x1c741958, 0x02b4e923, 0x4ee7a472, +0xc10efc57, 0xeb1d8012, 0x06eef089, 0xadbc220d, +0xc8b88bd5, 0xd4b8056f, 0xcd87db08, 0x36d5b074, +0x5bf43cba, 0x7712ac77, 0x274f1c7e, 0x8d088f6c, +0x64304d34, 0x17257f3a, 0x5c28d8cd, 0x3265c0a8, +0x02a74c20, 0x7006adb5, 0x0da917a3, 0x77f8d521, +0x1f8b46bb, 0x13103318, 0x03e18341, 0x84da0291, +0x765f8935, 0x12db78c7, 0x526de46c, 0x5a472c18, +0x5d2fda50, 0xea21ffea, 0x4e0f8bf1, 0xc639f389, +0x8616e576, 0xea2ab872, 0x6c1bc21b, 0xea0cdddb, +0x2e1c0113, 0x7cd3780f, 0x188a50c5, 0x85de29ff, +0x31c975f6, 0xc0028dc0, 0x5a21a5f6, 0x8bec4d0b, +0x0f65057d, 0x41bb40b2, 0x1c77124f, 0xd98957cc, +0x50887a86, 0xb1153461, 0x170b6044, 0x5d6aa517, +0x56fb42ac, 0xe2efbf8a, 0xf819348b, 0x17b064e6, +0x7cc6a752, 0x8d64cdc8, 0x0ea73745, 0xacc739fb, +0x0118b603, 0x0f250656, 0x9dcc3906, 0xb97165e8, +0x9061df0e, 0xb74b296d, 0x0f9059b9, 0x2c4cb700, +0x14702865, 0xcae76ac6, 0x7f3ae10a, 0x36c2a2b9, +0x59f51fc3, 0xff277533, 0xbd551c2f, 0x06ea046c, +0x1cfad00c, 0x2181b80a, 0xd832064c, 0xd9d06f19, +0x2d14aedb, 0x52155108, 0xd84e06b9, 0x6de3b6d6, +0xb57b5bef, 0xc31223f1, 0xc1a04dc9, 0x6aabe93e, +0x5f18f500, 0xb729477a, 0x620908ec, 0x83eed154, +0x0d55a2e6, 0x77e0a016, 0xb6a9c7d7, 0xd12c57ed, +0x545544fe, 0xc28b4611, 0x9b99e9e3, 0xdac57623, +0x8bddaefa, 0xb9e8d113, 0x8dc2292c, 0xef06784a, +0x6526dbc2, 0xc1dbe9d1, 0x079203ed, 0xae956bbb, +0xe3d3d5d0, 0x322a104b, 0x2b96fd04, 0x4889b456, +0xd921280c, 0x9f4dd8ba, 0xd0cf21e0, 0xba295bad, +0xa3460f02, 0x28ce7305, 0x70a59883, 0x13ed4681, +0xd2df9e8b, 0xfc35d888, 0x63956897, 0xd009fa21, +0x34e80188, 0xcd70f734, 0xf17a0bd8, 0x912ed231, +0xadaa8dff, 0x391f2eb6, 0x59601403, 0x6c5828b1, +0xa818c4e8, 0xad6f5a19, 0xd9e0a654, 0xb5c33110, +0x2fb754e1, 0xc6a5c74e, 0x12c0aa08, 0x4f6e04da, +0xfedb3041, 0xc2ed88dd, 0x876c31d9, 0x02ce088b, +0x551ceed7, 0x8b27be40, 0x33c71857, 0x03e1701b, +0x89f089bf, 0xff77d1e3, 0x1cb80750, 0x52a43d12, +0x891378ed, 0x419bbb2c, 0x201624be, 0x0c76ff8d, +0x43c128ad, 0xe0d175cd, 0xac288f03, 0xe031f13b, +0x51362ae6, 0x6ba410b9, 0x518b109b, 0x4812d4de, +0xbb681580, 0x6832ff7e, 0xcc26050a, 0x99941f71, +0x830bf088, 0x0015f7e8, 0xb4b4c700, 0xa7bee9b4, +0x097e02f8, 0x34eb3d22, 0xdd5abbd2, 0xebc60de0, +0x325a8b03, 0x6e640442, 0xf08c7ef1, 0x68530875, +0xc2ed1f77, 0xebbbe60a, 0xe8170791, 0x685063b6, +0xfb73588b, 0x4f622e17, 0x30ff07bc, 0x3b17af68, +0x5bec418e, 0xd3890170, 0x561920a7, 0xea436edf, +0xc219090e, 0xd8794546, 0xf5a2d801, 0xf028ec15, +0x7e98ff9d, 0xf54735cc, 0xeb49bb17, 0x93348b49, +0xec083efb, 0xb8e320cc, 0xf2685614, 0xc76b0753, +0x6a0daade, 0x1566e8fa, 0x64e81c8d, 0x289d5777, +0x06096828, 0x4870e125, 0x6e10563d, 0x70909090, +0x0c02851b, 0x5491f6d7, 0x868c6c1c, 0xaa409274, +0x6907962b, 0x17787fbe, 0x6836ff57, 0xbd99472a, +0x088075eb, 0xaca1bfc0, 0x2b75d839, 0x06d0756d, +0x148f5a15, 0x02132433, 0x281a7b10, 0xeeedeb72, +0x8941f46e, 0x34aa29df, 0x8b302886, 0xe24f621e, +0x1985be43, 0x607b5064, 0x818ded38, 0x470cce75, +0x834f0458, 0xa131be2a, 0x56ca75c0, 0x505283ca, +0x1ae992ba, 0xb833ee9a, 0x2cba267d, 0xa411c329, +0xe790679e, 0x8daa1a85, 0x320dd908, 0x76e5e87d, +0x07ade85a, 0xe608df84, 0xc5a3aae9, 0xa36fb780, +0x2e49c8a5, 0x0e451e60, 0xbc2324f0, 0xc79ef635, +0x70f0a123, 0xd235ab69, 0x08336283, 0x462ee890, +0x87cd67b9, 0xe83dfb1f, 0x36fca519, 0x65c3374f, +0x585951f7, 0xc907250e, 0x42d66e9e, 0x33620e74, +0xc1c5bb1d, 0x240ebabe, 0xfc95341d, 0x2e150001, +0xf4a79833, 0x80a9850e, 0x49c94c93, 0x1f834474, +0x0afdd84b, 0x0aebaaac, 0xa0148c15, 0x63222917, +0x5320eba5, 0xc932709f, 0xf31620a0, 0x924074f8, +0x8f1bd82f, 0x0ca34083, 0x9e013061, 0x04696a16, +0xab49b821, 0x38dc7c60, 0x084aab31, 0xf9856eff, +0x63b7ec1e, 0xdee844eb, 0x68121e13, 0x393d1d5d, +0x590fbb18, 0x4e4f27eb, 0xbf5dca14, 0x196f90c6, +0x16267714, 0xcba9b8ca, 0xd6101a2a, 0xe0e85589, +0xc5360456, 0xeb5926e4, 0x04e21315, 0x8be4a7df, +0x412b903c, 0x6edc1f2a, 0x233b42ce, 0x43e5e675, +0xb183bdb4, 0xe7e57c02, 0x8c07c6f0, 0x7e2650fb, +0x342bc05b, 0x5123b014, 0xc0888da8, 0x11b09bb7, +0x47bc44dc, 0x840e8e68, 0xa3c8e853, 0x51bc46ed, +0xc946c301, 0xd04a753b, 0xd7624327, 0xeb630e84, +0x4903658b, 0x6b420846, 0xc035c042, 0x42ddc8c9, +0xe852f009, 0x436d082a, 0xa53cebef, 0x42784e4b, +0x486e45ed, 0x35570fb4, 0xbb146e25, 0xebef6fe1, +0x83a23617, 0x943e1029, 0xe95c4603, 0xc6497300, +0x7259d192, 0x594a86e0, 0xfe576a93, 0x847d5f58, +0x8b41acbe, 0x47f6931c, 0xf6b103d7, 0x735314ec, +0x1f0e6cb1, 0x420adc1f, 0x12f11f5b, 0xbdd0ee1b, +0x33679830, 0x8c91248c, 0xf890d584, 0x13e9510b, +0x2b600155, 0xf812e265, 0xd70bd893, 0x4c5fdcec, +0x8b2a756e, 0x977cf43d, 0x3aa81a00, 0x01076308, +0x9d41cca8, 0x9b1110da, 0xeac26160, 0x8912e877, +0xc4d5ed85, 0x1c273c8c, 0xaa52828c, 0x1fc262e0, +0xc60d3500, 0xc1fd5c1f, 0x55e8cc3b, 0x7e214148, +0x770bd2bf, 0x37cef51f, 0xf8b7689c, 0x47b76bf0, +0x14c0e786, 0x7438bb25, 0xf1222d0f, 0xb9146f06, +0xc54cf80f, 0x6188f41a, 0xc17b4702, 0x67e16442, +0xaa0b8259, 0x20f85db2, 0xfd835edb, 0x1a722e01, +0x61850206, 0xfebeede1, 0x4bebe34d, 0x2d357257, +0x41b7f558, 0xb9050624, 0x0b36f8be, 0xa7c20db7, +0x41202511, 0x893a88bc, 0x68f0fa91, 0x6b6328ac, +0xc122d829, 0x37c0d76e, 0xfecc0b07, 0x4459f063, +0xf72225e2, 0x52c189c7, 0xc13386ba, 0xd7b7a446, +0x6850c00f, 0xaa0cba70, 0xafc1d593, 0x74c64b83, +0xdc319515, 0x34046b13, 0xeb5f9388, 0x1ad6f82d, +0x9fd9a969, 0x7a1e2e5f, 0x7bf4937b, 0x0208733b, +0x2d526804, 0x10306ae5, 0xd8c8df21, 0x024bc35b, +0x7cc71d6f, 0x5bc2f7e4, 0xd23129e9, 0x1b5ed54a, +0xd2bbe3e8, 0x478b6fb2, 0x479d2988, 0xe0198578, +0xec816c8e, 0x634ee200, 0x78d8606f, 0x8942bf24, +0xe222b9e6, 0x7c8e2561, 0x1a794d55, 0xf7cf55b8, +0x30a2df21, 0x5610eb47, 0x3d32bb14, 0xfe449f78, +0x40c4810c, 0xf30103b1, 0x1acff675, 0x04819f17, +0x1a40c61e, 0x9f3f7eeb, 0xf448c08d, 0x119fe807, +0xfeebc3b8, 0xf812164c, 0xeb69cb31, 0x0693324b, +0x278f9892, 0x30b48e37, 0x2b0be6c2, 0x18bab619, +0x0bfe0a2d, 0x69ff2789, 0x0fd1896d, 0x00b8d0bf, +0x06e8f673, 0x08aee960, 0x37750b46, 0xd1d87d49, +0x0007e40d, 0xf590048b, 0xa3ae56ee, 0x93801503, +0x2512428d, 0x6b564d0c, 0x51ac078a, 0x1575fa63, +0x689729b7, 0x29210d41, 0xc7329dc1, 0x48f2253a, +0xce399be8, 0x3e6d758c, 0x1368bf16, 0x85668cc1, +0x64a1580b, 0x934280ae, 0x0876ab81, 0x0ee43540, +0x8fc3a4eb, 0x5b9e32cf, 0xec6a67ab, 0x51f010f1, +0xa3f4169b, 0xa4369a85, 0xdae8bdb8, 0x3f7d0f7b, +0x04b8d25c, 0xd9bd150b, 0xde111b4e, 0x3a2e3bef, +0x5fb40902, 0x9a20124b, 0xefe8c733, 0x5a9e1fd2, +0xcd4a0185, 0xd2324e37, 0xc3283412, 0x385a9b57, +0x8c68d057, 0x595df938, 0xc54b1174, 0x6a401579, +0x116e856a, 0xa2562a0b, 0xcaf95211, 0x08e334b0, +0xcd5d006a, 0x2b7b480c, 0x7f911256, 0x40bddb7d, +0xc54e9c1d, 0x7dc716c2, 0xa8dc55ed, 0x042af211, +0x9adeb705, 0x4297c4c2, 0x89034eca, 0x1042c7ef, +0x0ca0fa4f, 0xe98ed0dd, 0x08fd616b, 0xc08f45cd, +0xd9e910c0, 0xdb5fd9b6, 0x9758c0bb, 0xe2a520b8, +0xf0b99309, 0x4fa24185, 0x573fc621, 0x672eb000, +0x51d32c50, 0x4503650a, 0x6a978647, 0x83f61ecd, +0x8afc19cb, 0xf0418fd8, 0x0ff98387, 0xde240576, +0x0a516c10, 0x555d852c, 0xc07d8040, 0xe2d41b1c, +0x2b4151cd, 0x54ad9727, 0xbb3c2d2f, 0x68941a3c, +0x558c00b7, 0xbd758b2f, 0x01a13c8d, 0x05a8f2f9, +0x40722d17, 0x500920d8, 0xaf05caf6, 0xda1683ad, +0xe088111e, 0x497dde8f, 0xfe3180c3, 0x458d0874, +0x1339e810, 0x0b6a5958, 0x44cf4b0e, 0xc28be330, +0xece00e09, 0xcfb63c3f, 0xf27aa4d9, 0x02131a03, +0x48efb0de, 0xb86c063c, 0x24cd1003, 0xb18e12f1, +0xfb03b915, 0xd6eefcf3, 0xe84c83ba, 0x02b81549, +0x13eee821, 0xdda2f67e, 0x6af46168, 0x7615bd01, +0xd8fffffd, 0x35c39c4a, 0x2d0b06bd, 0x7c2c2c1d, +0xb6f8c1b9, 0x99ee4d68, 0x961d2c18, 0xf12a8a1c, +0x2eff89cb, 0xb50edcb1, 0x4efe5206, 0xcee479bd, +0x7e0ee8e8, 0xdaf78902, 0xbc306d08, 0x5bc9b977, +0xff6a4f7c, 0x76836833, 0x1bb0e8b9, 0x1afcf0d4, +0x2aeaa9ff, 0x5032bcf7, 0x0dfd16b8, 0xe02edb9f, +0xd805b54a, 0xbf68ef2a, 0xfeec55c2, 0xbee81631, +0xce585e19, 0x946d35cc, 0x280324db, 0xbb8f1217, +0xd8542b01, 0xe81600bc, 0x1563745f, 0xfdf53aa7, +0x269e2058, 0x883ead6e, 0x048a0af8, 0x3e24740d, +0x5551b44c, 0x44402153, 0x004c0bac, 0x570c3192, +0xc019755b, 0x96b65b1c, 0xf0f4e3b8, 0x75607d8e, +0x258d0c98, 0xbbede91d, 0x3d7af82b, 0x3d1b6141, +0x54070642, 0xff6dde25, 0xeb1c7518, 0x0fef394a, +0x7799fe09, 0x0419eb01, 0xec404ae8, 0x0ceb840f, +0xb3178629, 0x679eedb3, 0xfe3910da, 0xddbfd018, +0x0e870837, 0x5ea8564a, 0x64e81bb4, 0x48860281, +0x449140ce, 0x401dc06d, 0x356ae91d, 0xb8da73bf, +0xc304c9e3, 0x3bceb3c3, 0x6d3bc208, 0xc15d02a1, +0xb4671a74, 0xde840347, 0xfa68df44, 0x338461b0, +0xb4047b89, 0x1d058bd1, 0x04a040c7, 0x33fba6c4, +0x036806bf, 0x5837290b, 0x74bfc4c3, 0x95d68008, +0x433bc030, 0xbd3e7408, 0x2f4ba640, 0xcb79e189, +0xa1fcb60f, 0xfe747e1d, 0x25f0ad8b, 0x6886e2b7, +0xd27e051b, 0x4215e283, 0x0450be67, 0xabbbf062, +0x1268522e, 0xf863e74f, 0xe87c5303, 0x558eff28, +0xa2997734, 0x136059c0, 0x58d70538, 0xa6251e2a, +0x4b046abb, 0x958b140b, 0x12b44ab7, 0xeb1a0f15, +0xef0253e9, 0x546dd508, 0x43e6d017, 0xc949042a, +0x42216ab9, 0x5a951cf6, 0x1b6e0d06, 0x741c3b99, +0x651fe5ea, 0xb30a0fb0, 0x355039c3, 0xc451b620, +0x7cb83940, 0x21b7b607, 0xa306b5fd, 0x6c90312b, +0x5b8e4960, 0xac696b5a, 0x2bb91da9, 0xe76c0310, +0x226639e8, 0xb3566457, 0xb37a2978, 0xa1828eb3, +0x1652b35b, 0x4efb38b1, 0xe9f9208c, 0xb765ed48, +0x1c63fed1, 0x6601e383, 0xd1981a89, 0x0ae29a4e, +0x5a0b47b6, 0x7ea50911, 0xb41a020b, 0xd3892ac1, +0x68ad7e1c, 0xf8d100dd, 0xe1638406, 0xebc2c0b6, +0x44c25d16, 0xe0982102, 0x999dd23e, 0xe288069b, +0x0725d350, 0x6d7e4edc, 0x908b2355, 0xe2800913, +0x8de2dd04, 0x8bcf2901, 0x36380b18, 0x48038abc, +0x50031e08, 0x0e14e489, 0x3f1561fa, 0xdce8cf89, +0xc50cd8ab, 0xcbda6e12, 0x05c65629, 0x01590368, +0xd1463bde, 0xd1db453f, 0xc71c0023, 0x14464a0b, +0xb02b4440, 0x1c14166c, 0xd0fe120b, 0xeb4b3c0a, +0x0fc42dce, 0x1b0bd2be, 0x85370f0c, 0xc265a9e9, +0x831d347e, 0x8a17e2a5, 0x8b07b97a, 0x7f481c42, +0x02c57e24, 0x425f18f3, 0xd149cd10, 0x13046e0b, +0x62e9d071, 0xb845a94c, 0x0ef7ad88, 0x69b8ab18, +0x89f92814, 0x0bc6e6e8, 0x16da0dd1, 0x4b06bd16, +0x61767571, 0x3fe88a41, 0x0c7a0d2e, 0xd0d8cdf6, +0x8a467931, 0x2fc08406, 0x97fb47e7, 0x731c1d82, +0x18483b13, 0xc31ce073, 0x060502c3, 0xc3aefe00, +0x45c23836, 0x1de37b0f, 0x726fa104, 0xe08957c7, +0x0b5ef6e8, 0x4807138e, 0x3342ceb2, 0xb86efece, +0xe902ed08, 0xe8c94ab9, 0xb45151b2, 0x0aec88e4, +0x64d3ff5e, 0xe6d8a2c3, 0x0d26c34a, 0xa9b60509, +0xeb5a3510, 0x86d10915, 0xbc46e805, 0x6604ef10, +0xc268c414, 0x405f1aa5, 0xe89aab05, 0x17285f7e, +0x7608fad8, 0x518ba104, 0xc6f6d222, 0x08dd6fb2, +0x5189c209, 0xc1c9154c, 0x1dee13e1, 0xbdd1c4db, +0xca54e281, 0xc0401889, 0x7561b459, 0xc34e4783, +0x1d9831be, 0xc99e7806, 0x0f7b7b0d, 0xd7e8919b, +0x770f15fc, 0xa81052ff, 0xd934d1db, 0x82b50c0d, +0x101ca348, 0x920ef8b0, 0x3a18a3c1, 0xc350a6a1, +0x10dbc164, 0x00f6d395, 0x2427236c, 0xc70ab961, +0xb5238045, 0x5d899489, 0x000445fc, 0xa39da24f, +0x084d6812, 0xbb18d11a, 0x6bbe05f4, 0x62577675, +0xd8894533, 0x9f2006af, 0x712e6c14, 0xc2172406, +0xb5460010, 0xa3750192, 0x30d14a0b, 0x512470ea, +0x69680d1a, 0x2cf008db, 0x4a28aee4, 0xc4e20d01, +0x6500c229, 0xbfbbb462, 0x7ec9859e, 0x08c82910, +0x04c71579, 0x8817b4b0, 0x29d0ec17, 0x037e37f0, +0xbbde1101, 0x894806dc, 0xf045e834, 0x6daf5fb8, +0x8405c58c, 0xe445e47d, 0x103b307b, 0xf7301700, +0xbb892f2b, 0xf0f1a81f, 0x3d5503c1, 0xd33ae8e4, +0xf4036fdb, 0x4320187b, 0x4b8bc918, 0xd02253bb, +0xf8c56a85, 0xb550fd84, 0x89362c44, 0x180121e4, +0x628f6dad, 0x294716e9, 0x44da2471, 0x0120f67f, +0x8b2df045, 0x53e8f04d, 0x30e865d8, 0xcf8cba09, +0x42e95553, 0x6f3a5d88, 0xc1ebdb03, 0x7a32dba6, +0xc3470ee9, 0x15e845d9, 0x46b03eee, 0x418db371, +0xc183fe18, 0x1466012a, 0xd0cd6083, 0x706310bd, +0x34ea5835, 0x46c9e83d, 0xd4ad4bb6, 0x95afdb0b, +0x83c8e444, 0xddf44d56, 0x0b31430d, 0x57ec75af, +0xfe834ce8, 0x04bc161c, 0x3a59e87a, 0x6ff8223d, +0xffe70f00, 0x1ffabf78, 0xeb58cefc, 0x046e0048, +0x1538a307, 0xc4d01e04, 0xe80ef6cf, 0x5642af18, +0x76c16eb7, 0xf0be0725, 0xfd491a03, 0xf98070ae, +0x453e1ac6, 0x7586d2b9, 0xd6ce2c50, 0x720cfff8, +0x9fd4d03f, 0x2755bfc2, 0xac09b40d, 0x0b10c662, +0x2b6edfcf, 0xeef8001a, 0x2d5cbf62, 0x615b9a04, +0x2b2d3e15, 0xebb6de01, 0xb885d82d, 0x3bc3bffd, +0xf662d85d, 0x630104f7, 0xe8020be8, 0xb94c4522, +0x08f03142, 0xbb1b0a10, 0x611516f9, 0x98ba07b1, +0x20ac193a, 0xe851777c, 0x0fb018c3, 0xe91d7e68, +0x04bb9afd, 0x1603e92e, 0x5cda0ef9, 0x8c9b5b07, +0x53264027, 0x67d81af1, 0xa084ddc0, 0xbe8ffbe8, +0x72d8e84e, 0x3f04d76e, 0xe0e80d74, 0x3c9652ad, +0xc6391ceb, 0xb9b0f50e, 0x56b1848c, 0x470db91d, +0x7f5af9b7, 0xf7225859, 0xfa9c1236, 0xe03bf6d1, +0x935d2d03, 0xa6248cb4, 0x4183baa4, 0x414a1520, +0x42d0dee3, 0xbad9fd8c, 0xcf142508, 0x6ea3803d, +0x1b9a77be, 0x4ddc6853, 0x24848b31, 0xa087d803, +0xebd90681, 0x1cb3332b, 0x9b187272, 0xa238f818, +0x0afdc008, 0x5ee9b034, 0x68e60438, 0xe6160c15, +0x6d18c5bb, 0xd258b825, 0x589e785e, 0x43a09b26, +0xd15dc58b, 0x86e827e8, 0xd7e61e33, 0xe80c3813, +0xc613df16, 0xfe7948cf, 0xe2e0ffff, 0x964f3968, +0x309d05ba, 0x007051df, 0x91a580b5, 0x20333676, +0x046fbf4a, 0x52719e72, 0xda8d6f26, 0x8c4388b8, +0x0b1c18dd, 0xed5586b0, 0xff003033, 0x51e95b53, +0x3ff07e3d, 0x20452f06, 0x231e7502, 0x1fcbb807, +0xa90f5df1, 0xe887b3b7, 0x6277432a, 0x41dc2552, +0xb801ff7f, 0xffcc77e9, 0x63e833ff, 0x0dc530cb, +0x3f76058a, 0x82001dac, 0x5f0efbf1, 0xc05af876, +0x435b0285, 0xfbc6a1a1, 0x676c1f98, 0x54465317, +0x8b605006, 0xdbea86f2, 0x38094c6e, 0x7f7402a8, +0x010c760c, 0x3bb46f47, 0xa809790c, 0xb1bf3f01, +0xcd9df006, 0x7ebfeec4, 0x855e8b0f, 0xb90546e2, +0xd8d410a0, 0xd77673c8, 0x1c045540, 0x857d5357, +0x583a077a, 0x8b3b38c4, 0x78de1415, 0x08eada8b, +0xf60fa6be, 0x03740242, 0x16bb1541, 0x14b2f034, +0xd838fa81, 0xe972fc5c, 0xdc6c7449, 0xad9eee66, +0xff543710, 0x4fa73070, 0x8a542abf, 0x348d65b0, +0x05ddc43b, 0xcdfd75f3, 0xf6df1415, 0x7b0b4d43, +0x68df3818, 0x6f0211d3, 0x32343ab2, 0x12dd6fc4, +0x031e59ef, 0x36eaea1b, 0x0be3e86e, 0xfcb09e58, +0x412871e9, 0x3f34a013, 0x76284fdd, 0xb97bb395, +0xbb071353, 0x5aa8da86, 0xe631303f, 0xdd56b611, +0x568eb7e8, 0xc268de92, 0xc458a1d1, 0x7eac2b9e, +0x5dbc7680, 0x0f3b907d, 0x0f421b4f, 0x9676281f, +0x24b837f1, 0xf86638b6, 0x543ab072, 0xcef0bf0a, +0x19cc19e9, 0x7586c180, 0xf819b81e, 0x0652bee5, +0x3bb3d60c, 0xaf4a23a7, 0x7eee3b0f, 0xf7b54e9d, +0xac56a374, 0x66aa5be9, 0x63ac22fa, 0xca18dfd1, +0xdabce95a, 0x6dc5912f, 0x151a958e, 0x0d1d2337, +0x0f4158f4, 0x91e6d7ea, 0x9c087321, 0xbbdf0530, +0x083d8196, 0xd07f7503, 0xcd5e91a0, 0x2e3128e8, +0xcf9ea958, 0x0c0a6f7c, 0xba394468, 0x6fad640c, +0xac6446a9, 0xc36412f7, 0x68df3693, 0xba991383, +0x46d8c9e8, 0x02ef99ec, 0x68431086, 0xbf7c1c90, +0x25bb74af, 0x040a26c0, 0xb347ae59, 0x1d586516, +0x6863bddb, 0xb4b9126c, 0x060f777b, 0x44d52739, +0xb1ffc28d, 0xa65378f8, 0xa85563be, 0xe9766ceb, +0x812b6f4b, 0x820b5089, 0x0c48f4d7, 0x1cb142de, +0x19cf3090, 0xb514a1c3, 0x7209e877, 0x1b29d483, +0x156a646b, 0xfc1bfb89, 0xee50722e, 0x9157edf5, +0xd346b841, 0x5e79556d, 0x5ad8140e, 0x75567815, +0x8b0acd77, 0x8cf5581b, 0x77000a5b, 0xcab0bf70, +0x54f6e9c1, 0x0180577a, 0xf6a5f303, 0x377c21c2, +0xa5c86d1a, 0xa4016bbe, 0x15c856a4, 0x491daadc, +0x22b623be, 0x8b673fc3, 0x51794802, 0x17d0e22c, +0x677b8ad0, 0x0fdf5ec0, 0x68f01ad8, 0x9f251afc, +0x1da56804, 0x069d2dbb, 0xf3601d2d, 0x4c11e3c1, +0xc9aa3f7e, 0x25096816, 0x95e024f4, 0x37995182, +0xf512c125, 0x8b4b678d, 0x07cb9e0c, 0x3fb54bed, +0x833ceb4e, 0xca5ec7f9, 0x0932eb08, 0x9bc4b52f, +0xebf7e216, 0x75e25128, 0xa80c7709, 0x0c6d0b02, +0xd981b6f8, 0xb9b20cd3, 0x8fd81364, 0xf621f72c, +0x39439137, 0x89b175fb, 0x40a16c58, 0x02ef4608, +0x84108a74, 0x0ba34015, 0x992ef9b6, 0xbac3c2fa, +0x68115800, 0xe65f940e, 0x5a316b6a, 0x5001d042, +0xf6db1b6f, 0x1b03777e, 0x4ec50dc3, 0x770c0d47, +0x2dca2616, 0x5b208511, 0xaa6478c7, 0x1bb84263, +0x2914ca42, 0x5fbb358a, 0xc08b6775, 0xbdba7558, +0xb801135b, 0xd0039071, 0xf08306c1, 0xb6f43d1b, +0xfa55c491, 0xcb44b8c2, 0x3613dff5, 0x13751dca, +0x311a682c, 0xd6295bd1, 0x57222384, 0x60706194, +0x89b85d54, 0x364c4069, 0xf86e1ff1, 0x3b307b81, +0xb7860f94, 0x812c5395, 0x5b01f1c2, 0x320fdf03, +0x414bb901, 0x2f81cf1f, 0x6c50ce12, 0x726448f0, +0x0c929953, 0xbd6d550e, 0x01ff16f4, 0x7ffd8877, +0x0d7b1d7e, 0x13eaaf68, 0x225091e0, 0xc085016c, +0x1305a7bb, 0xd88d809d, 0x2b394c15, 0x84cdc5b7, +0x8a500cdb, 0x00dcc856, 0x296dba0a, 0x40145c70, +0x5419091f, 0x06bff701, 0x3b376043, 0x30773043, +0x264b4df6, 0xb8b76e6b, 0x05eb4400, 0x64433d06, +0x377392d6, 0x302b3099, 0x947b5b0a, 0xa16d9755, +0xff88eb99, 0x6555e9d1, 0xffaf302b, 0x8377a0ec, +0x8d60d5bd, 0x03588281, 0x00c1815c, 0xe72bb771, +0x54041767, 0xb5e88b8b, 0x7beed888, 0xb8854256, +0x53164cfa, 0xbbec192c, 0xdb2bc1ed, 0x640c6826, +0x8ce8d189, 0x3bec6228, 0x43688f99, 0x8d642c03, +0xee07b708, 0x462008c6, 0x76dde017, 0x1276dddb, +0x2d665816, 0x34786609, 0x80212e80, 0x8eef7587, +0x1802011a, 0x0310765c, 0x38ce0362, 0xeeefbae0, +0xfb6620eb, 0x2da33f6c, 0x5c6c216e, 0x6d4f7df0, +0x603b426a, 0x05922213, 0x8c87b830, 0xfc8a5494, +0x71485ca0, 0xf85489a1, 0xc45b70df, 0x34ca6c12, +0x5aba897c, 0x9c5da028, 0xe807c2a0, 0xb684af70, +0x70170502, 0xa4087b5d, 0x03c54921, 0xbf1164c7, +0x7f9341f8, 0x30b8e140, 0xc7246c76, 0x0ee08c1e, +0xa1b56eb9, 0x0ea50e29, 0x50e3bda1, 0x93082db8, +0xa4ba1c19, 0xc689d681, 0x24dd84d8, 0x5d813427, +0xb80b7766, 0x10301f0d, 0x19c3e844, 0xfe3c5f67, +0xb6e804c9, 0x572070b4, 0x76e6adea, 0xfbc90e05, +0xb7a407c0, 0xf1680b85, 0x10b8abb9, 0x855ba362, +0x72c7b624, 0x546dfcda, 0xffec2db1, 0xe230f933, +0x55b740b7, 0xcf012a2c, 0x30462394, 0x37ab9b04, +0x07048d37, 0x6d7cf835, 0xdf25b400, 0x0c28abfa, +0x8b588bc6, 0x6528e52c, 0x8b9e15a3, 0x8c8c2855, +0x430c8ee2, 0x63791584, 0xdaf835e9, 0x325ee000, +0x89a50534, 0xcefb8286, 0x0a115863, 0x04708b08, +0x16f0a250, 0xe0023d66, 0x86abdbd5, 0x644131ae, +0x158e68b8, 0x5751d605, 0x147702e8, 0xff640fe9, +0xe3c51737, 0x4862180b, 0x08c3817e, 0xd05c248d, +0x61c05230, 0x04763147, 0x46786349, 0xa802588d, +0x5c6dde05, 0x1a35ba0a, 0x010fd5e2, 0x6154c45f, +0xfe45f960, 0xbad9c80c, 0xfebb193c, 0x0380ba2b, +0x4640678e, 0x2d11fda1, 0x887d8f67, 0x33243a78, +0x40ba6069, 0xc1e18a79, 0x0f48b1a3, 0x4fee8e74, +0x6f7406e2, 0x742f0e04, 0xbb32f6f9, 0x7399c3c7, +0x1aeafb38, 0x8174008a, 0x4d3cdbff, 0x3c147f1b, +0x3c1c7447, 0x5255754b, 0xffe1fab8, 0xd571bddf, +0x3ccf87e9, 0x3c2e746b, 0x3c13746d, 0x0f3d7567, +0x620ad1a4, 0x7b269db2, 0x3a54890a, 0xc2164664, +0xec6c2987, 0x54510ae0, 0xf13c8316, 0x83ba8040, +0x41bdff08, 0x03274e01, 0x395c21dc, 0x3d58adc1, +0xaed1e844, 0x7ef988a3, 0xb4280b27, 0xfbf63e04, +0xb4d801de, 0x19f560c5, 0x3c9cc601, 0xe5038d6d, +0xb985e813, 0xb602143d, 0x7d03fa3e, 0xafaae7e3, +0xb552c41b, 0x05309237, 0xb44f7765, 0x00c739c0, +0x813b9328, 0x083b0672, 0x8e0bf582, 0x89157764, +0xfbf576f8, 0xe8de5e01, 0x0e6b8486, 0x8130ef81, +0x36a258eb, 0x89bf9c8e, 0x70726c64, 0x033fb17c, +0xf989161d, 0x8c320d2b, 0x13486368, 0xd6dfc7f2, +0x9a6a401a, 0x28076cee, 0xa9e62c70, 0xa31d160e, +0x2d1c09f8, 0xc1bc182f, 0x55a54ceb, 0xff2e6820, +0xc08e1690, 0x60b8dc1d, 0x750bf48b, 0xcad127fc, +0x307883c6, 0xdc277604, 0x61bed803, 0x115a412b, +0x518a0360, 0x07068804, 0x29f0725b, 0x8168bac4, +0x63d245c1, 0xa21476c7, 0x4e51ba14, 0x035e1046, +0x40e27b1f, 0x64f54dba, 0x834f751f, 0xdad8677e, +0xc7077c0f, 0x5bf83446, 0xfabf305e, 0x5d8d2bfb, +0x894d4772, 0x699ae9d9, 0x762a2983, 0xab3287c1, +0x57f7e963, 0xf0d7007c, 0xb9b70418, 0x264243c6, +0x0180b8a3, 0xd177aed0, 0x4f9f51a0, 0x008c4034, +0x343ad068, 0x7834aad1, 0x4d685b0e, 0x20a502c1, +0xd4eade71, 0x5e835575, 0xa82d9198, 0x58de2981, +0xe93188e1, 0x0a0d16fb, 0x7733050f, 0x943b8126, +0xf001dea6, 0x4b861e77, 0xb7348b0c, 0x0ce12b60, +0x15f312da, 0x9b802d04, 0x36760e58, 0x39450ceb, +0xdb8d67a2, 0x75376c3b, 0xa39a64ca, 0x4063b3e0, +0x140e6d9b, 0x07377ddb, 0xc00708bf, 0x3c0c2a10, +0xb45d23ba, 0x7d64bb72, 0x85051cb2, 0xeff6299d, +0x0835556f, 0x810ae1c1, 0x9a8ddfe9, 0x8d32642a, +0x033efeed, 0x6eb82850, 0x50dc2589, 0x6256258b, +0xcddff52d, 0x6851e1cb, 0x6a525329, 0x7b36ff06, +0xd620a5bb, 0x41238b12, 0x5de876ee, 0xb3d9dc83, +0x29db7313, 0x30830007, 0xb26ac16c, 0x1a8f008c, +0x309e03a1, 0x29043418, 0x5e8f1abc, 0x2b30d34f, +0x8fb2bcfb, 0x083b62cd, 0xae20cd11, 0x5c6308e5, +0xe03db2a8, 0x96be7706, 0xa214536e, 0x06890957, +0x45e07071, 0x00ba8bb5, 0x15421f36, 0x4367735e, +0xd0057e7d, 0xbbc51c20, 0xded4747d, 0xca15d015, +0x880dd420, 0x7c3f809e, 0x8a13ebb8, 0x8186880f, +0x9082869c, 0x81f1e8d9, 0xf5857377, 0x842016e9, +0x36fc1133, 0x126cf670, 0x073625d3, 0xc0ba8fb8, +0x86df8c43, 0x1e3f6896, 0x09c11b32, 0x436c1d9e, +0x53e804e4, 0x0728d12d, 0x02c935f6, 0x10c32918, +0x0e4605e2, 0xaa408dc6, 0x3b7643a3, 0xe04f63d9, +0x1d57ba01, 0x6a1242b5, 0x060f5bba, 0xa5c8ff82, +0x58eb5375, 0x043445c7, 0xd40b0911, 0xff3ded4b, +0x044097fe, 0xc23038c4, 0x9690e035, 0x000835a1, +0x4da01c0d, 0xc1f06e2c, 0x3cf72548, 0x2c55834d, +0xd7eee374, 0x0fe810e3, 0xbb0febe1, 0x31d178c2, +0xd4ba6eac, 0xa9187d83, 0x37c9a0eb, 0x4348f49c, +0xb538788b, 0x5ee8d166, 0x77682c19, 0xe8d71c57, +0xfeeb0683, 0xb61fda8a, 0x345e71c2, 0xbfe8e031, +0xbecd4ff4, 0x6dcf8047, 0x48100101, 0xd6d24dc5, +0x6279d100, 0x0c107b14, 0xc0cec3c0, 0x327ebfa0, +0xf4761f34, 0x283c9382, 0x3b09f5ef, 0x97a118b7, +0x1a870f99, 0xc1afc38e, 0xd2d2a2ef, 0x9c138f52, +0x85bf9103, 0xcdefe63b, 0x8a6c52e8, 0xb00204bc, +0xe57c22ad, 0x3e751bec, 0x20ff841c, 0x59d9bb8f, +0x033f1fe0, 0x010c0617, 0x08f8a398, 0x9c890f46, +0x7890ef24, 0x19f4b5b7, 0x044910eb, 0x753600d3, +0x75a5e987, 0xd80affb0, 0x0122d434, 0xa9663e00, +0xaa5a3651, 0x21bd23bb, 0x3fa29d7c, 0xc4a9c8f4, +0x948b7574, 0xb5077712, 0x2b4a24cf, 0x3c8d4706, +0x71ab1010, 0x661858e1, 0x297d0674, 0xab6510d3, +0x29b759a5, 0x7c1c15fb, 0x1558d9bc, 0x40954a24, +0x1f8213be, 0x21da630b, 0xc3f57ef1, 0x2bebefd8, +0xfa797e03, 0xc5f36c14, 0xbdb01820, 0x3846ed0d, +0x7d8935a9, 0x08bd5627, 0x54b8152e, 0x5c99d17c, +0x040b0128, 0xc2fd49e3, 0xc5fdc085, 0x42c7e9c4, +0x9e3a268f, 0x20b019c8, 0x1d44fe40, 0xc83252a0, +0x5650d829, 0x96bc4b62, 0x758c4b9e, 0xa25b2b89, +0x059c3833, 0x057a761b, 0xc6382c10, 0x5834ffe1, +0x714cf089, 0xa3c2d66b, 0x4099aeb4, 0x2016fc38, +0x3dd0cd17, 0x43c78160, 0xbb54da2f, 0x6e6258b9, +0xbf3eedca, 0x4d80c714, 0x80906502, 0xbf000c09, +0x12a84fa6, 0x10ce748d, 0xd536e801, 0x291832a3, +0x022d4c80, 0xa2ef07a2, 0x9af74083, 0xeb19b0f5, +0xf7ab6177, 0x7408611b, 0x1407c77e, 0x404e7619, +0x6db6dd0a, 0x844f0257, 0x0c5e024e, 0xd15fe8f4, +0x062b4117, 0x57c7561b, 0x0547c710, 0xaaafe2de, +0xdd2c4583, 0x20460b06, 0xf12cbb53, 0xd9ac0f0a, +0x044deb9e, 0x6de28710, 0x15d92d7b, 0x563c461c, +0x65ee050c, 0x833f7ed9, 0xd023ffd2, 0xff0845ea, +0xd0e05b81, 0x14c683c6, 0x546e1cc7, 0x03822c0d, +0x7b820f78, 0x379b1410, 0x1d6c6f55, 0xc3cac3f2, +0x1328418b, 0xe8c47613, 0x1043747f, 0xe0584a0d, +0xc799097d, 0x09000740, 0xd0eb2dc1, 0xeddc91a1, +0x63d9c438, 0xba51eeab, 0x873628d3, 0xd12ded6f, +0xd708c6c0, 0xc0b45989, 0x8ba10c2d, 0x05c2f4d5, +0x54a1a284, 0x1c04e98d, 0x7c1b8995, 0x6f0ceb77, +0x72083b24, 0x10c07f0a, 0x05b63ae0, 0xee7528b8, +0x5cd8e043, 0xa05fc3dc, 0x58022861, 0x7b8d04d1, +0x2daa0d10, 0x73df39fc, 0x54de8907, 0xe07e33a4, +0x2216fb68, 0xfdff0f7c, 0xb699fc0c, 0x6bbb85dd, +0x7e518b3b, 0x39438920, 0x282abc45, 0xfdc2e751, +0xc25dd85f, 0xca00e770, 0x81b0086d, 0x0b40d1fd, +0x508ee99c, 0x0bb44c0a, 0x2ce023bd, 0x1013f5ba, +0xb070a814, 0x8259637a, 0x388204e6, 0xfd972392, +0x068082dc, 0x00205011, 0xe659ef4b, 0x25115460, +0x782bc009, 0x50081e80, 0x7c1a1d03, 0xd062bb3f, +0x136f535b, 0xe8539015, 0xa29ae059, 0x6e1f130b, +0x0b86c1d3, 0xfc77744c, 0x3c3d548e, 0xcbbd2f78, +0xc5bb654e, 0x137b7a7f, 0x10aca6e8, 0x270dddbf, +0xe81ddee8, 0x5bd80dc8, 0x27629476, 0x9d68a19c, +0x920984f5, 0x075805ad, 0x31a67a7b, 0x6681f162, +0x295dbd1c, 0x59c51bc5, 0x4d0585d0, 0x9c0c737b, +0x1d9b7041, 0xb7efcb11, 0x6fb81c5d, 0x6068f607, +0x0aaf27d6, 0x13c48c6d, 0x686cb8c8, 0x8e70e88b, +0x08fdbb92, 0xab3980d1, 0x97ecc60d, 0xd8d5b14d, +0x0018e21b, 0x0a690903, 0xb7ee4bd9, 0x0fe77e47, +0xcc0d8b31, 0x90535b12, 0x518b5cab, 0x6d4d4c67, +0x0c98176d, 0x0f99b653, 0x6e0419f6, 0xc41ad003, +0xe8b81d26, 0xc0d4b8ef, 0x2a9a8252, 0xf219d431, +0x2358d8de, 0x370a11c4, 0x11ff47a3, 0x57028a60, +0xd37fbfc5, 0x0d00885b, 0x376f5e81, 0x07855c1a, +0x55496864, 0x5aee7493, 0x55bba9e0, 0x81955a75, +0xbfeec0e2, 0x05cee52d, 0x25460e02, 0xaf0fc209, +0x85091857, 0x21cfe85a, 0xe3831c01, 0xba1add3f, +0x12e39b5b, 0x8006468f, 0xd8e05e06, 0x127751b0, +0x282650a2, 0x4e88e8da, 0x08af10b0, 0x0b056847, +0xfcfcfbea, 0xe2183a43, 0xb8c6f2b1, 0xe15f4101, +0x7ca91eab, 0xa56568f2, 0xf85c3d13, 0xc7371063, +0xebe27adf, 0xaa559f2f, 0x01c0d685, 0x9f0e46d0, +0x803f50d7, 0x9f6af320, 0x6d3dba1b, 0xdc86c08b, +0xe15aa135, 0xea582160, 0x11f4815e, 0x8d8be55f, +0x446dcf40, 0xa027e220, 0xe3ae0396, 0x1b75c2c7, +0xc16bc45b, 0x3eba0b27, 0x4327b859, 0x427a920a, +0x5bb7dd9e, 0x40a018c5, 0x0472cf08, 0x52d6e6c1, +0x02c49f62, 0x895ed6d7, 0x0b00a5e7, 0x13d6b6e0, +0x1be2f2d7, 0x1ae601f0, 0xc7ca813b, 0x32521552, +0x1765a075, 0xd3ffb2a0, 0x009ddef8, 0x1cdb3311, +0x428a3021, 0x5afc1e29, 0x204a2217, 0xaee99218, +0xb7f4b746, 0x74d1340a, 0x104d3b15, 0xdf0f9b6d, +0x43b7f57a, 0x20518830, 0xfa97b3e9, 0x3c187770, +0xeb6ffb6e, 0x88447401, 0x74020f9c, 0x27033c42, +0x7fb70b4d, 0x40ebf5b7, 0xc63a423c, 0x3c1177c1, +0x3c7d7415, 0x01351741, 0x75db6fb6, 0x3c128dcf, +0x07b31a43, 0xad201448, 0xb97ac5b7, 0x61165562, +0xd8de3ae9, 0x50488bd6, 0x95100706, 0x1c5be4ce, +0xf80f02eb, 0x2c481425, 0xb774a818, 0xb09749b5, +0x25438a5c, 0xd1be6ec1, 0x29776406, 0x214bb243, +0xb45e75b8, 0x8a90849d, 0x62201200, 0x177c2dce, +0x48164502, 0x40247466, 0x3fb3f806, 0xe9152082, +0x66d100e8, 0xaa1c7981, 0xdd08e15d, 0xd9ca855d, +0x05d71c41, 0xc7b08624, 0xe9e801a0, 0x659500c2, +0x286eb40d, 0x5d8b6522, 0xa37db572, 0x1cbc15b6, +0x0548221b, 0x8898061e, 0x061a021d, 0xb03479e2, +0x5a738941, 0x7d0e740a, 0x3a0d00b4, 0x2816b912, +0x0163181a, 0x0413c2e2, 0x174453a7, 0xd0d05b30, +0x4ab94bb9, 0x5c7ee8e5, 0x4361ec04, 0x7f84e9fb, +0xf76c0aef, 0x7c08ebd8, 0xfe2c6283, 0x866c6c8a, +0x8129417f, 0x08002c49, 0x3d6dd970, 0x6fda20fd, +0x46c7b83b, 0x33c71398, 0xb93f7600, 0x31c7399b, +0xc4eb41c0, 0x14149609, 0xf6845570, 0xf7d41a96, +0x370a3e65, 0xc7f88968, 0x57e7ef9e, 0xc9e2016a, +0x2e41d12b, 0xd10cdd83, 0xff658016, 0xeb4dbfa8, +0x0dfe8939, 0xb786f858, 0xbe3494f4, 0x80d9f722, +0x7477117c, 0xdfab5adf, 0x1144b623, 0x07f88d05, +0xe783060f, 0x26faa73f, 0x4015ae07, 0xd375c639, +0xced045fb, 0x6b8903f6, 0x051c0818, 0xde1055de, +0x8b2f75ef, 0x76443443, 0xd0ca8303, 0xcf05a5e0, +0x73afcd73, 0x45b83d1c, 0xf6de01b0, 0x67003db1, +0xd1602828, 0xdd080a04, 0xbf423238, 0x88173ddf, +0xd280c8c5, 0x6bbdba05, 0x667389f0, 0x3c9a014a, +0xb5b16bff, 0xa60cb42f, 0x89800c12, 0xe9d1190d, +0x1105aadf, 0x8d037797, 0x0f880148, 0xc286e06c, +0xa100d02a, 0x8df81ba0, 0x0b40a125, 0xc481051d, +0x74618025, 0xbf048322, 0x03a09345, 0x02033f98, +0x584aff48, 0x40edb6b8, 0xf67dfa83, 0x654ac173, +0x80b6e0b4, 0xd8209064, 0xb77098f2, 0x6fa1afd7, +0x7037880a, 0x2099b307, 0x5290fd2e, 0x3f5606b1, +0x8bef0168, 0x83b91798, 0xb3bf8889, 0x0c62b391, +0x70276a68, 0x6772b00c, 0x78272327, 0xb42dac7a, +0x6767677f, 0xc414c006, 0xc628ca06, 0x88b5c681, +0x0740bbd0, 0xba055e8a, 0xc23be007, 0x00aeaded, +0x1088c328, 0xc607358b, 0x41a11fa0, 0xba091e4e, +0x5e09282c, 0x06fbc49b, 0x24bb3fb8, 0xe044b05c, +0xda8a6fc3, 0xd034b668, 0x6087180e, 0x1de81438, +0xff61cc5a, 0x035d3e61, 0x9cb2e9c9, 0xc41a0000, +0xef0cc88c, 0x2559c5a2, 0xc550c8ea, 0xb3d66340, +0x022c20be, 0x89e22d2b, 0x78f5e4ee, 0x2cad84dd, +0xe150517e, 0x8d1fe84a, 0xd8930b54, 0xa2ef83b9, +0x89d83325, 0xeb542ae6, 0x1834ea28, 0xa0f41b33, +0x460ceecd, 0x9ab42358, 0x69522242, 0x93d8800d, +0x1a8c8b10, 0x36b69f8e, 0x15341c82, 0x87191ea8, +0x815b36f2, 0x0f181378, 0x347377f3, 0x0be516df, +0x290ba407, 0xf236cc01, 0x822775b7, 0x0f0201e9, +0x35db3b6f, 0x900f09e0, 0x0e6ae9e9, 0x560d1807, +0x348f6fba, 0x15050b19, 0xd9a1ac06, 0x656b24b3, +0x0974740e, 0xed9d836d, 0x4c521809, 0x67b2180f, +0x72863763, 0x727e84de, 0xb00d1b69, 0x0f6d9db3, +0x340e097d, 0xedb30e1d, 0x18126db0, 0x0b10729d, +0x9db6157b, 0x2411b735, 0x0e7602f8, 0x09841813, +0x76cf6711, 0x81df1814, 0x9734710e, 0x77c948ee, +0x58250b7b, 0x35b33401, 0x2921d2cf, 0xac32200f, +0xd9db6700, 0x1a22fc11, 0xd8230918, 0x682beced, +0x320e1318, 0x89014518, 0x36d9db3d, 0x151b0b30, +0x166e2431, 0x9dac1b66, 0x0918330e, 0xad551870, +0xebb7b6fb, 0x00e13d7a, 0x31c40615, 0xba0b7517, +0x93beeb67, 0x730b0c92, 0x0b6d3123, 0x16d75f76, +0xe02d8323, 0xc9521bca, 0xbeeb3dcc, 0x3ce40e01, +0xe23d77e8, 0x6375150b, 0x24e3eecd, 0x0ec101f6, +0xc0cf18e5, 0x098dd3dd, 0xccdd18e6, 0xd3c0be04, +0x03c42f61, 0xbf20d70c, 0xdf15000c, 0x9bddefee, +0xbe45eb0e, 0xbee81cfd, 0xafe906db, 0xcfbbc817, +0xa5097cf3, 0xd8359bd3, 0xf7be2824, 0x22bb7ee9, +0x108ae906, 0x5009d594, 0xcf55d796, 0x9e4f7cf3, +0x8e210e76, 0xe7cdab67, 0x09daba79, 0xbf4e9340, +0xe69f38d9, 0x2e643cf3, 0xd030db80, 0xf76fbfdc, +0x13882634, 0x1c4a0ebf, 0x6ddd2a0e, 0x8375afbe, +0x061f3d32, 0x88bf0510, 0xf76fbf34, 0xbe04fc1f, +0x09ed18ae, 0xf6d7de87, 0x90f7df7d, 0xd9228442, +0x2342820e, 0x79efbef9, 0xc30315be, 0x0e3247ba, +0xef9efbb4, 0x121da67b, 0x4c2ca50e, 0x79ef0996, +0xa3c9efbe, 0x82d4098c, 0xe7f32cdf, 0x561683fb, +0x0beabe7b, 0x4dbe6f11, 0xe69fddde, 0xbe632a4f, +0x5710e05d, 0x39d2f4be, 0x6ea8ef77, 0x98beda07, +0xebc6bf09, 0xf951be41, 0x0b77be7e, 0xadbe1deb, +0xbe2e0a12, 0x80080b25, 0x22fcd3f3, 0x06d1a0be, +0x1854be16, 0xaa6fba23, 0x28be0aeb, 0xf5020b54, +0x41477802, 0x962f1681, 0x6cffffe4, 0xb99101d4, +0xc681ffa0, 0x447896ed, 0x177ee704, 0xb906848b, +0x891767c1, 0xa00a2870, 0x687e5300, 0xb4fcaf12, +0x923ae861, 0x2be0c56c, 0x9c000844, 0x40fa8cf0, +0x24c48643, 0x4e5709ab, 0xc36f476a, 0x91db1926, +0x846556f1, 0x2141d00a, 0x5aec749d, 0x3cb2426c, +0x2cb93b53, 0xfb58f7df, 0x137ee3ff, 0x50ec9888, +0x2f5a14ca, 0x506688d1, 0x81c22ad0, 0x338130c2, +0xb90d2d7a, 0xb76c02c3, 0x37c78815, 0x1d625e5b, +0x9c64306b, 0x0125b340, 0xdb0fb00e, 0x6208f714, +0x60908d1a, 0x4c0ad866, 0xd0f10f4c, 0xea97be4f, +0x0ffea877, 0xbb3b7768, 0x7c9e11eb, 0x0db1012f, +0xa8128068, 0x4c1fc766, 0x002ac706, 0x0e2b2900, +0x43e0cb86, 0xe0210198, 0xe4062996, 0x1a2fbe79, +0x33c52194, 0x79e3ee16, 0xaa6f22a2, 0xe560b71a, +0x24b58a46, 0x140348ab, 0xdfcfc0d8, 0xbcdb1c8d, +0xfc5168b7, 0x048af631, 0xfa7d111e, 0xb8ea5138, +0x0b74d0f4, 0xdfe8dc0f, 0x7fc24632, 0x6b57438b, +0x4b8627c3, 0x438a012c, 0x73747629, 0x773edb47, +0x2a723814, 0x813c4ed7, 0xed05c7ff, 0x4f39a185, +0x6a740b3c, 0x993c0a77, 0xaffed998, 0x3c4a0f91, +0x3c6b7430, 0x80830d4c, 0x701d7237, 0x5af510a3, +0x5c3c2881, 0x0a8b6de0, 0x0d06c6b2, 0x752f7b80, +0xdddeeb5d, 0xeb0a1f5a, 0xe8200504, 0x4c3e0a64, +0x7a3a8f0a, 0xca25b61c, 0x720d2002, 0xb9df1537, +0x763beb24, 0x25a1e82d, 0x905ca15a, 0x04004cb2, +0x2b6009fd, 0x537b59e8, 0x3443c74c, 0x2bdb437a, +0x39064e49, 0x63835855, 0x3a6e1a15, 0x6be84406, +0x0e71ba26, 0x9e11e8e9, 0xf990fc14, 0xba36422f, +0x5fea6f63, 0xc70f22be, 0x74066e8d, 0xa7188a14, +0x200646c6, 0x136eec19, 0xb40dd7f9, 0x0e0beb1f, +0x9f60e1c8, 0x0045c6bd, 0x0dba453d, 0x19e34f00, +0x407e2077, 0x965e6cae, 0xea4f0da2, 0x5e5c4112, +0x502c202c, 0xed040b2c, 0x8901cb42, 0x61281e05, +0x4881a25a, 0xe243179c, 0xd3775bb1, 0x24ff2577, +0x2f017485, 0xcc66085d, 0xe8435f18, 0x25ba6c49, +0x46202403, 0x27808bd5, 0x0e4f8f77, 0x6bb80643, +0x38b81008, 0x805dba0c, 0x0498e9ee, 0x57077d60, +0x0647031c, 0x85bb566d, 0xe9eb114f, 0x19123a37, +0x1b42e2ff, 0x1c3ce7c1, 0xec07148d, 0x80acabdd, +0x42e09c92, 0xbc355703, 0xe1c10484, 0xa4262504, +0x3711ddd2, 0xe61fe953, 0x5d558be6, 0x0839957a, +0x3e104279, 0x7c548e08, 0x59066a1a, 0x00e28d1e, +0x5b88cc9f, 0x50bdd360, 0x194e682a, 0x32e80201, +0x16ab78b6, 0x8908de6f, 0xd661b441, 0xcb177dc8, +0x5d203ae9, 0xb403e076, 0x5bfd9a13, 0x09e3c124, +0xe2471ff9, 0x6a93463a, 0x898a0b9a, 0x7b5563f1, +0xf045beab, 0x482f028d, 0x754d0d63, 0xfac7534c, +0x7972e7c8, 0x84c0dc23, 0xe950750e, 0x89470354, +0xf75ef6f6, 0x67832447, 0xe10e456a, 0x1cdae88b, +0x86f86685, 0x74cbe938, 0xc3e80875, 0xf43807f6, +0x5e8f8359, 0xc6374f5a, 0xa9432847, 0xc537b0c0, +0x6cf2cd1d, 0x836eb820, 0x1a014360, 0x3836dbc1, +0x4dc4c2c2, 0x321c42bb, 0x03007801, 0x685c58c4, +0x40fb1806, 0x89e96e04, 0x087be8e6, 0x5f4f204f, +0x418d1c43, 0x75015a12, 0xac411c05, 0x8ba3e8de, +0xec551cba, 0xec010232, 0x2e43005d, 0x8920f37c, +0xb0708ef4, 0xaa703708, 0x025d5202, 0x47b86e21, +0x117b8166, 0xf4024e93, 0x071edd6b, 0x159d131c, +0x5214bd89, 0x7a6098dd, 0x8e0ccf6b, 0xc2017467, +0x9f76e4b7, 0xf4858d8b, 0x736ee81b, 0x810ae43c, +0x350a7bb7, 0x956b0486, 0x23bd650e, 0x2a9c7b84, +0x8cc107f8, 0x60442d2a, 0x29826e20, 0x56ad41c7, +0x11e6ae98, 0xf44650fc, 0xd8045d60, 0x2c751041, +0xfd5ddeba, 0x89c87a04, 0x89f3f4d3, 0xd92f8185, +0x876bcd52, 0x1d006d00, 0xebfc5272, 0x73f73915, +0xff0e3e05, 0x8d8ef46e, 0x39bb8b20, 0x68180bbd, +0x0a8016ff, 0xa98cbe31, 0x6f8dc0c2, 0x8bd3bd80, +0x4a8b1c52, 0xf847b068, 0x198ef91d, 0x0c4ce903, +0xe9d41d54, 0x100e41f1, 0x4ec1f11c, 0x1f87a0fb, +0x5c0001a6, 0xe81d64e9, 0x18652847, 0x2b91b84d, +0xb8351041, 0x33292c4b, 0xb981ec95, 0xa90f7ace, +0x8d1bad04, 0xd0b0a1cc, 0x4a444062, 0xf0df1b9a, +0x6fb7d8e7, 0x24f1e7f0, 0x448d9d8b, 0xdf141b0b, +0x8de781f6, 0xd4250179, 0xa3414118, 0x85ca613b, +0xd5be14d0, 0xd040d63f, 0x8bb5dfb7, 0x438d4163, +0x7b5f8b07, 0xe88163dd, 0x8d72dcc4, 0x8b66103a, +0x63bb0023, 0x16ade831, 0xafd69bba, 0xbaf74c00, +0x1695c729, 0x7a97a20c, 0x7dea6175, 0xe99fe8a3, +0x72be0fec, 0x4236fbb8, 0x35b9a310, 0x83c6f8e0, +0x2c5159e2, 0xd74517eb, 0xc664e200, 0x43c64e30, +0x38506d21, 0x6f53958c, 0xdc11d4ea, 0x7faf0774, +0xc286e576, 0x9bf5a8b8, 0x2543d0c8, 0x07740f6a, +0xdce81aeb, 0x1d7effb8, 0x812415aa, 0xed7426fa, +0x9626d8a1, 0x11a22714, 0x4da684cf, 0xba704b10, +0xb2b7e8c3, 0x51a36fc0, 0x2c1dda85, 0x9dbc24eb, +0x3e51b06d, 0xfd5f0c9b, 0x12ebfa38, 0x542dece2, +0x133ba302, 0x404c8900, 0xc9f46a33, 0x78db7fef, +0x03598911, 0x0c693d61, 0x89107189, 0x1fb81479, +0x17a8fc00, 0x0a8ba28b, 0x75b1b16e, 0x6a076243, +0x7a2572c0, 0x452acf14, 0x53efae05, 0x07c5240c, +0x9cdf13f8, 0x1e8b5550, 0x0b716c01, 0x6a4ab8da, +0xbc2f9b18, 0xfeedcb50, 0x8eb023ff, 0x8ec08ed8, +0x8ee88ee0, 0x5dec01d0, 0x2bc39d58, 0x80b34eea, +0xee1ec25d, 0x3f1e10b8, 0x6f73c136, 0x29ec2924, +0xb8472843, 0x7807df10, 0x258b3fc0, 0xba2dd4bc, +0x1b45ca69, 0x15086e6f, 0x89cc29b8, 0x1c8fe1e7, +0x2fed917f, 0xc34ac405, 0x1444c183, 0x2bc76600, +0x8229b70f, 0x4f28011b, 0xe6393cef, 0x3b01d337, +0x53358976, 0xea6730b8, 0x2fc5f183, 0xe8b905e0, +0x109c892f, 0x701d5750, 0xb958c003, 0x4dd0d248, +0xe22d891d, 0x244060a5, 0x9aee6887, 0x10fb7a01, +0xb8ad6194, 0x49165ad4, 0xf0b769b0, 0x06711d06, +0xa36210e8, 0xbd1e89e4, 0x0d8db1ea, 0xaaffbc78, +0x508158c3, 0x15ff5251, 0xcab01520, 0xd7b7fa45, +0x3f423140, 0x030c7b6a, 0xea247506, 0xc39505e8, +0xe12f2b90, 0x1c9f584e, 0x09d18c4e, 0x163c22c1, +0x020974c4, 0x9c17ebdb, 0xb92d15bf, 0x4ec65d2c, +0x11c00153, 0x16bb77d2, 0x11c901ff, 0x79d285db, +0x2411e5f4, 0x12773b3b, 0xdb77a272, 0x463b05db, +0x04290b77, 0xce010f19, 0x5bfddf11, 0x014fde8b, +0x0579ead1, 0x09dd89eb, 0x62d675cd, 0xf012823f, +0xb6240406, 0x89696352, 0x516e89c7, 0x86825e2e, +0x76fad418, 0x577157a8, 0x784186a3, 0xfa83aa0a, +0x148be01b, 0x1ca8ec6d, 0x08bdb884, 0x8d047f0e, +0xecb9087b, 0xa916fe15, 0x2be22743, 0x8d7d745b, +0xc4b1d042, 0x1df3206f, 0x83bb0c8d, 0x0b795679, +0x2154b7db, 0x066b2104, 0x36107d0a, 0xd0dbb60a, +0x58eb0789, 0x479a3b29, 0xd16825f6, 0x7659fe78, +0x2343c748, 0x3b6036d4, 0xe03d3feb, 0x2a548bf7, +0x8d2a099a, 0x285d2dda, 0x7f039454, 0xeafff81e, +0xa3bb0587, 0x1a2720eb, 0x07eb0b8b, 0x858d0b00, +0x831adf10, 0x46a64511, 0x50f3712c, 0xb03c014a, +0xbac23b3b, 0x50cbe92f, 0x712802dc, 0x08bb071b, +0xfb762878, 0xa05f0eeb, 0x7a9364cc, 0x2994c64a, +0x0057bea1, 0x0103a99a, 0xdbd14317, 0x438adc37, +0x12820472, 0x8b05eac1, 0xfa93e1e1, 0xfb1f808c, +0xd0901c23, 0x401ce41b, 0x039f1bc3, 0xba47fb9b, +0xe0b81e10, 0x06eb0209, 0x98df468d, 0x9e02b173, +0xb0cc08aa, 0x24104047, 0xbb00ea82, 0x3ba82df1, +0x1fc383e6, 0x728d5dc1, 0x12d43805, 0x3905ee7f, +0x071b74f3, 0xe089dab5, 0x022302d5, 0xe3708e7a, +0xcef0dfff, 0x078921eb, 0x14ea6f89, 0x8b0d269d, +0x203511fe, 0x83437bac, 0x66e404c2, 0x7237dc54, +0x56f77aef, 0x525c0b40, 0x157449b9, 0xd30ddf77, +0xf8bb76ed, 0x0a835421, 0x935a8836, 0x141b534d, +0x7444961b, 0x6bd2ffee, 0x6e22e681, 0x610cfbd5, +0x2517e572, 0x73b45aab, 0x39335f15, 0xb7597dfb, +0x0d81d0ff, 0xdb31e327, 0xcbd0e8f4, 0x1dbb6447, +0xb602950f, 0x52c20dc0, 0x4575a0b4, 0x34093f40, +0xa363bb9d, 0x35e34441, 0x16e8e814, 0x2be827b0, +0xc3d6dba0, 0x01d03f8d, 0x6852c3f0, 0xd609c181, +0x7402e850, 0x7a0c3a05, 0x050413f5, 0x13a35317, +0x1cc4107e, 0x3a7cc37b, 0xe046fbd4, 0x34a41742, +0x05111d06, 0x38270b1e, 0x61eca6d5, 0x2e16b559, +0x90451652, 0x6b625d08, 0x7dd740a3, 0x342e7d10, +0xbf6c5138, 0x703b4805, 0x98207630, 0x098a9b2c, +0x8982a734, 0xd4fedf5e, 0xd585723a, 0x89b11e52, +0x82e6ed21, 0x399ef51a, 0xd70e4103, 0xf9e80b62, +0x01717ee8, 0x21bbeb19, 0x72bc3715, 0xd372550b, +0x46d5348d, 0x490870c5, 0x82c047f8, 0x025160b6, +0xb88c30a0, 0x22959100, 0xc7db0bd5, 0x5914ab68, +0xe8cf1cf6, 0x925a16b5, 0x78d70e8a, 0x183975cb, +0x194a9b10, 0x1c042769, 0x062fb850, 0x565872cc, +0x221636bd, 0x3dc709b3, 0x3374d7d8, 0x3786b0dc, +0x206e8944, 0x2846b221, 0x52c8c360, 0x1824287c, +0x88061278, 0xb56024c2, 0x2abf8afa, 0x09698e55, +0xd041c289, 0xdbf94c51, 0x864d7615, 0x53c34e87, +0xfd082a36, 0xd10153ff, 0x3e46d100, 0x376a16e9, +0xbac720ca, 0x14449e60, 0xe100eadb, 0xc7a8410c, +0xc7530237, 0x4ae31f16, 0xfe280453, 0x3532a64f, +0x80088a40, 0xf17420f9, 0x96803fee, 0x04ee187e, +0x01eb439a, 0xdf8a3015, 0x19e41075, 0xddebf475, +0x2946d889, 0x32d236e2, 0x9e14afeb, 0x289c0d0f, +0x528a4054, 0x2c3c3013, 0xe8ce1480, 0x8a9129c9, +0x0fcbd02f, 0xd87cfb39, 0x88040f15, 0x5811b7bb, +0xe874c031, 0x180647f7, 0x844a22a9, 0x5dfafb9b, +0x028b5a88, 0x4187bac5, 0x7f882305, 0x58bc4441, +0x8aba0270, 0x11c559dc, 0x9822ef9a, 0x66d41765, +0x13e08a67, 0xe865204c, 0xc2a5d491, 0x488d221f, +0x811abfc1, 0x6303c6e8, 0xf075da25, 0x517510bb, +0x68f3533b, 0x7402518c, 0xb16c94c3, 0x5572890d, +0xcc78ab1d, 0x79db780c, 0xb414eddc, 0x1f9be8d0, +0x850b08eb, 0xbfa07010, 0xd8140183, 0x01a7d5c0, +0x68057e47, 0x751b6e09, 0x545465f8, 0x89400328, +0xd3d7be50, 0x013f55ce, 0x0033f005, 0x358838bb, +0x8f880540, 0xe4a921d5, 0x2bd58a00, 0x0f82a083, +0xe872fefc, 0xef936856, 0xdf8c2b97, 0x050f86fd, +0x0eebf797, 0x54045361, 0xb62b1000, 0xfee311fe, +0x83c45263, 0xfebde8c2, 0x4bbc7f10, 0xd1fde500, +0xe5132ee8, 0x937af808, 0x60853adb, 0x16a21959, +0x6d23c72a, 0x0884aeb9, 0x5cb36825, 0x46f41103, +0x233880ec, 0xe8da592d, 0x8fe20329, 0xe896e8e8, +0xd66d0bc6, 0x8e0301db, 0x061efbe9, 0xf70b81b0, +0xf44d751b, 0xf40f175f, 0x4c7016f6, 0x0eeab811, +0xf52f7f3f, 0xb96bb405, 0x96b08dd9, 0xedc2a619, +0xe8c37268, 0xbbc335a5, 0x2c12b1db, 0x6ac87908, +0x3925b9e8, 0x748a83f0, 0x2bde72c3, 0x89fb8902, +0x21f8df43, 0xc1a80da0, 0x8a53137d, 0x462d3a80, +0x33688340, 0x0284b7d5, 0x00d03003, 0x509d8801, +0x19a15011, 0x22d76216, 0x8204b244, 0x6db10816, +0x0f3205ee, 0xd38ac98d, 0x1fc0982d, 0x2b408f74, +0x0c017880, 0xb5b8cce4, 0xd3142080, 0x8b6e4509, +0xde6acd19, 0x4feee00b, 0x8a763920, 0x3d3c1304, +0x01134cdd, 0x574406ba, 0x48651cf5, 0x402d406d, +0x8391c75d, 0x3c30a27d, 0x7878000f, 0x3de87f08, +0xba14c13d, 0xe991504b, 0x191a1175, 0x0d82111b, +0xd5af795b, 0x409d47e9, 0x684b6c07, 0x3e338ac9, +0x2992b60d, 0x6646b247, 0x7fad0c2f, 0x1913d9f8, +0x462ce9c7, 0x16eb10c5, 0x4d70652b, 0xc12ceff5, +0x893056fd, 0x8b5de928, 0x5016ed11, 0x85e4f480, +0xebbb2fd2, 0x313680be, 0xd33f43f8, 0x64f06853, +0xc53631c6, 0x6e3d83ca, 0xbb770a7f, 0x0170f7ed, +0x8d092de2, 0x0d87471c, 0xfc688a46, 0x41520bff, +0xf7743a3c, 0x3a3bbe0f, 0x8d777513, 0x8efd014b, +0x6457c55b, 0x74a18022, 0x5ec0c21a, 0xe658b2c6, +0x1527ebbe, 0x37424e7e, 0x3c002a2d, 0x1a754efe, +0xeed010c0, 0x685716fb, 0x08f6ffd7, 0xbfe8b701, +0x315bb03a, 0xd8b7f8c9, 0x51eb7e80, 0x071a118a, +0xb66de9e4, 0x49411dde, 0xcba303be, 0xe2812e34, +0x9432f9ef, 0x83cb723f, 0xe9ec70cf, 0x028971e1, +0x21d14b6d, 0x785b5175, 0x5a583c14, 0x77ce0dd6, +0xe0a32d51, 0x288b3cf8, 0xc0b0d502, 0xd4d10e16, +0xddcb970d, 0x0add0c53, 0x1a281054, 0x5e360fea, +0x10b070a8, 0x71d02904, 0xd2d1bb72, 0xf31f8f26, +0x56ad1043, 0xbd140302, 0xc30e60d8, 0x4502c40a, +0x2916eaac, 0xa58195d7, 0xf6e18978, 0x17033fb1, +0x92bd58c0, 0xd75db8d2, 0xcba30550, 0x27ad04f9, +0x1560605d, 0xff8ae83a, 0x20c01a2d, 0x51d00eab, +0xa2bb4166, 0x1cca7545, 0x41553699, 0x8998982a, +0xc10132d5, 0x7e1dad02, 0x72ad453c, 0x83ca4634, +0xcd1176a0, 0x00bfb108, 0x81800700, 0x0ea611f8, +0xe3f7c8b4, 0x9857e86b, 0xb8bae083, 0xfcd4470f, +0xc8c8cf3c, 0x0102bb37, 0x3833c2f8, 0x14b15e42, +0x171179be, 0xd176f445, 0x83c72547, 0xd1bd151e, +0x9360f9e9, 0x1ed3e8fa, 0xebe80680, 0x4f806fd0, +0xd5300066, 0x457a17ba, 0x1cca8ed9, 0xa8ba3475, +0x1ea9400f, 0x3a1496e8, 0x3d1a4f10, 0xd04fb406, +0xffd983e9, 0xb0c7f1ab, 0xb8ef9b0e, 0x164a8830, +0x44e8c7b1, 0x4289ae7e, 0xd8a30c5a, 0xa1289c06, +0x6b070d5e, 0x40760c20, 0x15601014, 0x5a37419a, +0x8d67dd51, 0xd937d293, 0xe8ab93cf, 0xc0c7a77c, +0xbfee9c79, 0xc950c6f4, 0x3847c632, 0x8d504d8b, +0x748d2657, 0xc503b6d0, 0x3c557502, 0x0730a024, +0x4d83fdb5, 0xac2a1854, 0xf6cd6c14, 0x7d3d8b69, +0x022a3758, 0x5023581e, 0x372eb60c, 0xd06c741d, +0x146cb749, 0x0c10325c, 0xf8a8ee5b, 0x74938026, +0x358b4cd9, 0xc6fe1ea8, 0x8d12c6a6, 0x5db610be, +0x542d4360, 0x0d86e1a5, 0x2c5063e8, 0x2b989504, +0xbfb07002, 0xff47c175, 0x968d1c4d, 0xb8c30132, +0x9eb1e2fd, 0xe9077954, 0xd889c348, 0xa3044f86, +0x0452b6cc, 0x7058da41, 0xeb4beb10, 0x6f1fd2c5, +0x242828c6, 0xdeeb3450, 0x20e083d1, 0x5978823a, +0xf47c5bbe, 0xb47a28ba, 0xbda245e3, 0x5f7237e8, +0xc81c7b06, 0xfe810cc6, 0xd8a0f128, 0x57490ea3, +0xbb5cee16, 0x4840f604, 0xebc21139, 0x4534743d, +0x49d9d8e8, 0x8b2d6b08, 0x72085c9c, 0x2d8a46d1, +0x003ae89d, 0x474768e5, 0x846a3456, 0x12b75ceb, +0x102918c6, 0x001f7106, 0xca0e0260, 0x89b7223b, +0xe9fc75a8, 0x2350145c, 0xf02f8472, 0x4272e51b, +0x1cb3ba28, 0x1be82f1a, 0x1205b605, 0x220f14b3, +0x5d4b7adf, 0x88509c44, 0x50da0815, 0x491cd9b0, +0xe8a9106a, 0xee89baa4, 0x7510e021, 0x158bacf4, +0x116e7cf4, 0xf48d631d, 0x521bf01b, 0x899800cf, +0x7ba5a8e8, 0x8b68a3f2, 0xe2e8fed0, 0x91512215, +0x8a31f401, 0xec2b56ef, 0xf40004b7, 0x0a88465d, +0xaf9a3a39, 0x0a337dfa, 0xbece1016, 0xe4577a25, +0x0e811c16, 0x447e807a, 0x063f0777, 0x1cd0105e, +0x16bd7e80, 0xd800ec11, 0x1ef37a42, 0xa004468d, +0xe8166306, 0xe05cdab9, 0x3cb81c62, 0x36416232, +0x378ee283, 0xdf0072b7, 0xf4277250, 0x43d129df, +0x796de82c, 0x88263da5, 0x379a1c5b, 0xff09b7bb, +0x1e009813, 0xc4384fbe, 0xbee5d4f3, 0x28f00a0b, +0x10bb2475, 0x30bf1f26, 0x03086dd5, 0x59ac0252, +0xa2c12628, 0xed25777e, 0x012205c7, 0xeac01200, +0x743260f9, 0x0e20bb27, 0xbf660b3b, 0x73488dc3, +0xef8135ea, 0x903d8207, 0xe830f390, 0xa2a10b87, +0x076c5f19, 0x441e2278, 0x2e8f5277, 0x3171d866, +0xdc3f0acd, 0x48cf0978, 0x390b2b0c, 0xfbdf3ad1, +0x7570dad5, 0x4f30c388, 0x6fab521e, 0xe21e00d5, +0x0268e918, 0x6a02da3e, 0x40013911, 0x7c5b150b, +0xa4035833, 0xccba4bfc, 0x0d043622, 0x25d4cfe8, +0x6c152896, 0x38f814d8, 0x43004a8a, 0x443378e3, +0x5a3fb02b, 0xd7895700, 0x1a0310b5, 0x57b308e8, +0xe18df8ba, 0x51e9753e, 0xa418737b, 0x9a190ad3, +0xe10c09e2, 0x70ea5346, 0xfe45335d, 0x18c1a216, +0x82cd9c9c, 0xb7a92966, 0xf2dd63dd, 0x112bfa4b, +0x95e95f2a, 0x0382901a, 0x1790a460, 0x21ac3103, +0x5a6035c0, 0x8e84debf, 0x7cc21019, 0xb41d6800, +0x5dff4120, 0x6083cb28, 0x44397883, 0x8a1bc1c7, +0x7f3ec517, 0x400c17a8, 0x06941427, 0xd4478ae1, +0x132b7a16, 0xd6d6fa60, 0x7e03eea6, 0x06be5734, +0xbe01d7e8, 0x6f88c9c1, 0x45a2f9df, 0xe1044603, +0x10233c1b, 0xff63abe8, 0x843b04c6, 0xad440b46, +0x25302e89, 0x68b915d5, 0x04465fb7, 0x36c55b12, +0x804d3636, 0xdf0d097c, 0x109f1348, 0xe3aaaae1, +0x595a04bf, 0x019a16bb, 0x78a2d558, 0xa1b5b4e8, +0x68047627, 0x9a167d4f, 0xb53da8bb, 0x1044f015, +0x03b315c1, 0xc3aa0d5f, 0xcb597512, 0xa0e8725d, +0xcae7a938, 0xbab44dec, 0xa028d83f, 0x11e8d85a, +0x4ca547c5, 0x0eedd754, 0x67eac085, 0xe2aba108, +0x840de829, 0x2d86e801, 0x1c00375b, 0x788df7a4, +0xca834a96, 0x42e18463, 0xb1101d3b, 0x2800eb54, +0x8d4fc6fc, 0x21d9f72e, 0xbe438bd1, 0xc5dff044, +0x78fe29c6, 0x0b2c8d3b, 0x7404e853, 0x02e36a13, +0x89f42305, 0xe1133970, 0x38011500, 0x68a9a4fe, +0xe9c17689, 0x6e0a71f6, 0x27a3e22d, 0x14fc3d29, +0x26e7cae8, 0x811befc1, 0xa9755cfb, 0x17997844, +0x7076fbef, 0xe67c728d, 0x4cc289f0, 0xc863200d, +0x2bf0a8d1, 0xb3aa598b, 0x4b456d59, 0x1933e715, +0x92e91215, 0x3306bea0, 0xb0084189, 0x0ac6dcca, +0x6a796d54, 0xabcd56ab, 0x17f455e8, 0x5bb0b2e0, +0xd89ec5a3, 0x3042012d, 0x1de9fe1a, 0xef8b3501, +0xe9f0e283, 0xa8bafb7d, 0x06226c36, 0xe915f4b8, +0xc44d40e9, 0xd3275610, 0x752eda27, 0xeb348357, +0xf7049e1c, 0x1d066826, 0xbb65d188, 0x02d10286, +0xdd56306d, 0x4bdb5535, 0x2777fd2a, 0x776ffc07, +0x2dbd10c8, 0xd9084628, 0xa3f2c339, 0x8903e380, +0x5a5e609b, 0xe3e03140, 0x1ae8690f, 0x0a2f446f, +0xc0aca702, 0x856289e9, 0x57087459, 0x041423d1, +0xdb09815b, 0xb9bfd957, 0xa3675e08, 0xf7488dae, +0xd41bf983, 0x7717e0b3, 0x1fa9f414, 0xc1027470, +0x28068293, 0x818cc3c0, 0x8a1c2f74, 0x80f74a95, +0x5bed0e29, 0x5b168951, 0x30fa8022, 0xfa5f5b32, +0xeabe66c6, 0x8a4317eb, 0x3c20c8d4, 0xb5bee978, +0xce003f17, 0x06be4306, 0xb7b7db49, 0xfaccf70a, +0xeaf87660, 0x0912eb57, 0x2f2fff40, 0x08eb373f, +0x30037739, 0x0873f239, 0x01c6af0f, 0x6c4143d0, +0xd5ebc0df, 0x1f890757, 0x8a0a03eb, 0x92289430, +0x300720ed, 0x29a6e829, 0x96241c8b, 0x340522df, +0x19955c8a, 0x3c74d839, 0x40a02e06, 0xfbd36591, +0xfffab7dd, 0x17097f02, 0x402a752e, 0x08e6c133, +0x1347d609, 0x7a2a4501, 0xbdc57504, 0xaa897b70, +0xc8c14301, 0x45890410, 0x559c4526, 0xe0d50230, +0xebd06c2a, 0xe5c8265f, 0x6d3e019f, 0xf322b283, +0xff13d71d, 0xe9508800, 0x07e5f8df, 0x4f563549, +0xe83811df, 0xee1205d2, 0x03770af0, 0x08e56b84, +0xc9e84858, 0x1be9ee22, 0x3df0297c, 0x76084240, +0x3da06812, 0x2bc644ae, 0x5b84420d, 0x0baf4c06, +0x4ebf41a2, 0xa021ba74, 0xb5536e98, 0xb6716ec5, +0x30822b09, 0x0f06c002, 0x2420ee1c, 0x470bb780, +0xf42a60bb, 0x4776d9eb, 0x636e8915, 0xe33b581e, +0x55b80050, 0x28482758, 0x5fd50a11, 0x04f2332a, +0xca39421a, 0xc506b975, 0x107c984b, 0x37488920, +0xefb40af4, 0x1002890c, 0x38024308, 0x1914ba74, +0x8784301c, 0xa86b9446, 0x24e2b63c, 0x342833a6, +0xba8af2e8, 0x607445bf, 0xd907c7b5, 0x585c4b04, +0x24cbc0e0, 0x1f5301ce, 0x30a600c0, 0x1faaff10, +0x250aa510, 0xed89e015, 0x306af965, 0x79f7bdb5, +0xdb297b8b, 0x1982d808, 0x1773529c, 0x5fc5b621, +0x3cf9a766, 0xc7c77703, 0xf11aa13c, 0xebe057cf, +0xcc3e803e, 0xd0175063, 0x4bc47b6d, 0x904df228, +0xbc52c5da, 0x228b3230, 0x26b5f273, 0x350e97f0, +0x2f1f9aed, 0x235ab554, 0x6b8b46e4, 0xba93e90c, +0x3bf52428, 0x05aeebb5, 0x1b6ef148, 0x30a50c73, +0xb4a847e9, 0x10bba886, 0xfc30408d, 0xbc4d9be8, +0x37880834, 0x1e8a2c54, 0x7b79b5ba, 0x3b3f7081, +0xb65f8b1e, 0x20478b1b, 0x131e6147, 0x91ef83e8, +0x21978a58, 0xe573030d, 0x06213b08, 0xde75e081, +0x1b90ad89, 0x35b8097c, 0x7ef8bb2c, 0x33391aeb, +0x6a978d81, 0x3902385a, 0xb60b6a43, 0xa09a82e8, +0x1408d010, 0xd0bd400c, 0xde726893, 0xc3f6dff8, +0x76c45238, 0x3c0004ca, 0x1618011e, 0x5d8ce096, +0x40f6dd12, 0x05ca2937, 0x420bca86, 0x789d6ee8, +0xb764bb25, 0x93084667, 0xc48df9d0, 0xe07450af, +0xe75194f0, 0x623316bb, 0x03df40d7, 0x8831d7e5, +0x36d07115, 0x88fc224b, 0x1254beb1, 0xaeff864d, +0xa15f427a, 0x82f02f44, 0xbec7c889, 0x02865ed1, +0xbe040f74, 0x11744a1e, 0x1d23eba4, 0x8bc5f13b, +0x21ae9111, 0x08ff4b09, 0x00d6f43d, 0x04498671, +0x44ff8c8b, 0x820cae76, 0xa86dab47, 0xa1e392c6, +0x39e16938, 0x31f8a07e, 0x757bc3ac, 0x135be8ee, +0x8edd306a, 0xf5dfe668, 0x1042a2f7, 0x8e280a6a, +0x14a01aa5, 0x3334bd04, 0xef120b5e, 0x53c315a6, +0x301885e8, 0xc282d893, 0xe5cbffa6, 0x51493c11, +0x02dbe85f, 0x62829740, 0xb0eb768f, 0x8223e803, +0x23beb9a1, 0x3fba0416, 0x743785e6, 0x0e5e292e, +0xbf02e94f, 0x2c772b1d, 0x752c4239, 0x98147af0, +0x65eb3a00, 0x378909f5, 0xfe221118, 0x203fdf8b, +0xe4e8bf75, 0xb175ed9e, 0xc567c031, 0x571f92d2, +0xd442bee8, 0x0820b6d6, 0x284a0289, 0x42abbb60, +0x1043e294, 0x62749e3b, 0x5edd4560, 0x25c71446, +0xb621fd00, 0xfffff6da, 0x8816314c, 0xd709bdc1, +0xd368cdc3, 0x453a42f6, 0xef77fd83, 0xc8d6f551, +0xe5276f44, 0x9c880f7c, 0xe80105bd, 0x24e823a6, +0x9b7574fa, 0xce21f44e, 0x7d94cfbb, 0x04616843, +0xc5d9331d, 0x6fa7408d, 0xfc2954c9, 0x02896d39, +0x4f11198d, 0x1b9cd267, 0xeec12ab9, 0xbd0105e8, +0x35e21473, 0xff4b0ea0, 0x3921753b, 0x1589743f, +0x30573ec4, 0x6b620138, 0x82a30f61, 0xd911ab1a, +0x03457012, 0x0c462fe8, 0x89f5b960, 0xaa2496e8, +0xd5482697, 0x0315eb19, 0x0545a209, 0xd5a8d488, +0x0e1be142, 0xaa03c16c, 0x3cc71258, 0x841bf94b, +0x796c4520, 0x9a31c3e8, 0x18b5ad12, 0x022d4166, +0xede16132, 0x6161e8e1, 0x74fb0c5e, 0x5b0cf743, +0x904a8851, 0x445bce0c, 0xe1fd54e9, 0x2e8ee850, +0x84f940b8, 0x14031fc0, 0x514dd556, 0x1175101b, +0x776c689e, 0x01816607, 0xeb20247e, 0x146dba92, +0xe83d935b, 0xb106e58d, 0x16dc255a, 0x5827d441, +0x2899f7dc, 0x75057ae4, 0x6bf7f695, 0xa15d5fa0, +0x050de45c, 0xa41c01c7, 0x89dca2be, 0xcc0821d7, +0xfb99c8c8, 0x9f55e80c, 0x4d031d26, 0x07aaae54, +0xe019a0b3, 0xed23820c, 0x4f03a2ce, 0xc0ab5135, +0x76aceed7, 0xbf0352e1, 0x2b74c125, 0xdc540440, +0x596151c8, 0x0be9fcb7, 0x54635235, 0x00e59046, +0x2746da09, 0x48891c52, 0x2141ca8d, 0x4ad65b43, +0x46f57d0f, 0xd1cb16d1, 0xf802e9a9, 0x1d050d81, +0x2079909e, 0x8dc0fb8d, 0x058ad516, 0x45b50453, +0xc8c48589, 0x437aec5b, 0x16a058d1, 0xc7f47b2c, +0x4df45047, 0x26c76e44, 0x00447d83, 0xf015ca0b, +0x637b03ab, 0x85bfac40, 0x036a20e9, 0xa0e7e857, +0x4235e8d0, 0x009c09a0, 0x46c63463, 0xf859ba4e, +0x4a38e95d, 0xf3c8d416, 0x0b0373e4, 0x082d1438, +0xff15975b, 0x418d0169, 0xf76ce845, 0x6a95f4c8, +0x44d4558a, 0x35f5408e, 0xb8351d1e, 0xbe6f60b4, +0x14ac63e1, 0xb778137a, 0xd0aa4d8c, 0x2f97c0a1, +0x153eec0e, 0x9911dba1, 0x4447c0be, 0x442f73a0, +0x8de8da96, 0xd6ff960b, 0x1cb30303, 0x18efe27f, +0x4eec6a89, 0x69c117f3, 0xb673695e, 0xd5a68184, +0x445148b8, 0xd2f58763, 0x99be62f6, 0x0df44c70, +0x99700319, 0x11addb70, 0x88adcbbf, 0x9b206809, +0xc1b78394, 0xb71874da, 0xe80e549c, 0xa8acbdfb, +0x44f7811b, 0x1c1d0f01, 0xb748da8e, 0x9fb31059, +0x3ef2ae98, 0xc503eaab, 0x5d5fc221, 0x007d5968, +0xadcff12d, 0xd3045d72, 0x42b413fb, 0x3eac315f, +0x00908e11, 0x0bb416ff, 0x7fc2dc3f, 0x488722df, +0xd110f930, 0x886f4409, 0x32b8e8f4, 0x287c209e, +0x8dc37b16, 0x2613fa58, 0x431bdc9a, 0xcda31dea, +0x0de1b05c, 0xd03d20a2, 0x400d735b, 0x9074e234, +0xf50f747e, 0x55432118, 0xd26c1203, 0xde13c08a, +0x9b240401, 0x23f59a37, 0x9a921184, 0xc4d16337, +0x4b0d5148, 0x37ccb481, 0xde174262, 0xd1792b43, +0x36e45abc, 0x2443c411, 0xe1adcd26, 0x0b25b619, +0xfe3d4f89, 0xdb847451, 0xf609e38b, 0xf828f185, +0x86378efc, 0xb9ec8000, 0xd9852710, 0xfd83b12f, +0xa849ecc9, 0xf37440a8, 0xd01a0dc3, 0xf6c28837, +0x4becf8b2, 0x85fb0446, 0x02757f92, 0xa31708b0, +0xaa0c761e, 0x3803e7c6, 0x625020a8, 0x2abd056b, +0x0de5ee60, 0x554634dc, 0x754b753d, 0xf8b097e2, +0x83eef89f, 0xee22fbba, 0xf8b9aab0, 0xeceeca2d, +0x4b75aa3c, 0x35df2fb7, 0x75550759, 0xfe01b043, +0x1e3b75c8, 0x36b6f9b1, 0x2f1b60c8, 0x8407a227, +0xdf83bedf, 0x03b01fc2, 0xb2effbb2, 0xfab2eef9, +0xfcb20247, 0xbad04dee, 0xec4ababf, 0x75a3fdb2, +0x82e8ebae, 0x3b17f548, 0xa713ff09, 0x517aaf04, +0x5d0e4cef, 0x75486910, 0xb1c7bd50, 0x09750e03, +0x6839c0c0, 0xa4c00b14, 0x044d841b, 0xa8d13568, +0xea41801f, 0x13148d16, 0xa6ea5faa, 0x3c237446, +0x5a8d0920, 0xa1688901, 0x3afa950b, 0xed326d8b, +0x5deb802d, 0x5a0c5524, 0x20a31539, 0x8c5a1541, +0x02efbe8d, 0x537f80a6, 0x197db8c2, 0xe9802663, +0xa9c309d0, 0x68a6b1d5, 0x5e9bbe35, 0x8f544c1c, +0xd2a06553, 0xbad6d928, 0xab132dab, 0x5b6c1516, +0xfeefe123, 0x755bf279, 0xdffaa0eb, 0x460253b6, +0xc5b8dd71, 0x00d2f3df, 0x4f43106d, 0x94d59c29, +0x72663204, 0x0add1435, 0xd1c07502, 0xff42a8ea, +0x17c5461d, 0x6ac7d915, 0xd4b63a9a, 0x63326a31, +0x930a7861, 0x15b8ce7f, 0xb9565c00, 0x91cebbc3, +0xdd810dc8, 0xbdeac625, 0x2b277401, 0xb9077ec3, +0x6aef5027, 0x35be28a1, 0x06891ff8, 0xba0355bc, +0x708b0e2a, 0x1c14b69a, 0x01aadd44, 0xcb39cdae, +0xba0c1b71, 0x2a5ac826, 0xa5e845cb, 0x60e83f44, +0x8511784b, 0x00c8c129, 0xd3be2f4d, 0x032323e8, +0x4880f062, 0xa4722276, 0x740968d8, 0xc3945aaa, +0xe1818208, 0xba54cd25, 0x801df548, 0x5f980b07, +0x98e30c98, 0xb82b3c2d, 0x143e758b, 0x574b6c21, +0x3921992a, 0xd876157f, 0x1b011a16, 0x45025e39, +0x5e0fb5ae, 0xdf75c33c, 0xb7027be7, 0x24584c35, +0xeb8938f8, 0x7ff7a5eb, 0xa04c3a46, 0x0d106a87, +0x20f0d622, 0xe922a114, 0xfdd9fe91, 0x32ba130a, +0x102dc3c2, 0x884ad133, 0x1aa09757, 0xa70de2ae, +0x07042d0b, 0xd2566ef2, 0xe3157811, 0x47249b14, +0x43b38285, 0x155adf13, 0x685d8be1, 0x0fc268ec, +0xdac5142d, 0xc9962d10, 0x2a581c80, 0xa004c766, +0x5baf3273, 0xef409f95, 0x4dc6e8ba, 0x3fe1e8e4, +0x68897978, 0x65d68833, 0x38625bec, 0x26af100d, +0xefb453ed, 0x1e215161, 0x508ba114, 0x025b55d4, +0xc1ff3ae4, 0xe283450b, 0x048aa339, 0x51fe0132, +0xe107c72d, 0x5fba05eb, 0x288a5b5b, 0xcbe93940, +0x0bdd6e83, 0xf8298384, 0x05013650, 0x778906e8, +0x018aeed0, 0xff3bffc7, 0x75f05d39, 0xaa4360ad, +0xfb89735b, 0x26f0cb2e, 0xfd847918, 0x7a75951b, +0x8c81ffff, 0xd12a92ec, 0xdc5a0fdd, 0x08d58e31, +0xeaf04d3e, 0x31d4fcb1, 0xceeaac86, 0xdd52cd50, +0x8087056b, 0xbcd7c6f8, 0x87454ae6, 0x88317627, +0xa517e884, 0x3b0e8058, 0x4b140e4a, 0x8b096fdf, +0xa3a48b12, 0x3aa26a63, 0x72d7c12b, 0xaa7c5abb, +0xebd35d6f, 0xdb88e83e, 0xc62935eb, 0xa99eb408, +0x26bf7a10, 0xa938e560, 0x9bb2676d, 0xd4f86b07, +0x0f8f44c8, 0x89c974d5, 0x6ed105ca, 0xcc4b1fd4, +0xc0bfd582, 0xbaa2c8e9, 0x893e441d, 0xd5cdc3e2, +0xc4d0482b, 0xb835a25a, 0xfe2d520d, 0x96ebe378, +0x16f55f76, 0x56f12950, 0x59646597, 0x329d155b, +0xe0884acd, 0x6ec79c42, 0x07a6e890, 0x329ee801, +0x4e22b51b, 0x40a6e689, 0x676ec6f1, 0x4f99efb3, +0xf189370d, 0x36d2184c, 0x5e2de830, 0xbbcdf0a0, +0x8be00938, 0x51b6ef7c, 0xb94468e4, 0xc26b6325, +0xfe418c46, 0x76094a53, 0xea061ba1, 0x2077dee8, +0x7629085a, 0xe81148d0, 0x0843f494, 0xe985268c, +0x71e58a09, 0xd2200258, 0x58fab08d, 0x75ac4423, +0x45f5bbcb, 0x06d0af8b, 0x720337ff, 0xf0012a97, +0xf3595e48, 0x65800448, 0x568de8fb, 0x05463e0c, +0x4da9114a, 0xc3e2ae14, 0xc1fafa07, 0xa80407fe, +0x13e2e920, 0xc60a0000, 0xc8e32637, 0x241e0015, +0x5589d8f0, 0xaf8820d4, 0x6579add5, 0xc7cff1dc, +0x8cfe8ab0, 0x01718d61, 0xf677418d, 0x459f7829, +0x8900c7d4, 0x337d8bd6, 0x888e70ba, 0x80ba079e, +0x8a68d9b1, 0xd838f16c, 0x3a04b563, 0x2f4b7442, +0x7302cd3d, 0xfdd005d6, 0xc608ebf6, 0x3a38de11, +0x0768381b, 0x456ab175, 0x406bb4e0, 0x0a2e05cf, +0x9c8ec156, 0x65f2a02b, 0x10fb8495, 0x0a75c0f0, +0xbe5fd3b0, 0x758c6498, 0x16f2801b, 0x7457283b, +0x1b46c1fe, 0xfee6c6ce, 0xebe872df, 0x64ed5276, +0x89a58aef, 0xeb03ec7d, 0xc6c0b232, 0x0e8b4146, +0xd8d28dc1, 0x76809476, 0x5bed8d3f, 0x77028f09, +0xbc55155b, 0x54110c5a, 0xe0dbba58, 0xa8fa800b, +0xebf0803d, 0x7be0cfce, 0x4edef53b, 0x6a14bb28, +0xbce894d9, 0x3f066b01, 0x83650413, 0x2e6ae772, +0x8bc360ed, 0xf5085932, 0xebb1891b, 0x70987597, +0x0feb560c, 0x8849cd0c, 0x3ee28a58, 0xed4490dc, +0x619354e0, 0x2c51e789, 0xcbaf7268, 0x68255577, +0x10651356, 0x11b39353, 0xa8cf04aa, 0xe74d30c3, +0x2cd3b064, 0xe2ad0d4a, 0x0688a7a8, 0x9d140db7, +0x6c84b111, 0x1193cfc3, 0xf1e23264, 0x02348348, +0x12c1c446, 0x856cb6c0, 0xc50c2611, 0x884626e8, +0x040a10e5, 0xbe070bbf, 0x3aed6896, 0x90c91775, +0x1ee4f247, 0x3b08d22b, 0x1c0beb4e, 0xb71315c7, +0x792d81d7, 0x657766f0, 0x22d156e8, 0xfc561201, +0x14a28786, 0x48c08191, 0x50af7711, 0xbae87876, +0x8b55bcfb, 0xc132582e, 0xc5d10243, 0x5c425146, +0xa2ab44f6, 0xc26f6289, 0xf8bfe08f, 0x8a0c5256, +0x04618803, 0xeeeb4342, 0xb0f6aab7, 0x2febc164, +0xb6fa75b3, 0x39617a97, 0xcb458a53, 0xdb0f1a2a, +0x9411bb55, 0x40037bd7, 0xb0ff7732, 0xeb51a646, +0x8a434602, 0x8a12f916, 0x45b85f0b, 0x400c1d75, +0xdf2fdfe0, 0x1e74c838, 0xe6f055bb, 0x69dfe283, +0xd207138a, 0x27b7d029, 0xc667e1ba, 0x40b5c984, +0x02d13855, 0x75d8ea25, 0x4a4075f2, 0xf06819e8, +0xf4f98a6e, 0xd558c829, 0x8142816d, 0x13f045f1, +0x56f5d002, 0xc22901e9, 0x41430b29, 0x6feddd4e, +0x0506209b, 0xc6ebffd9, 0x05420142, 0x38e081af, +0x082475d8, 0x8b3044d8, 0xb45bbac2, 0x8e80b701, +0x82ca392d, 0x756600cb, 0x83b7f1ab, 0x4b00dfea, +0x04991787, 0x2f410018, 0x0a1b40b1, 0xe9a49056, +0xa040f8be, 0xc38994fa, 0x380b04d4, 0x97feb2d4, +0x39480782, 0x31f773d8, 0x68c65bc0, 0x682a1058, +0x37c06ada, 0x75eb5007, 0x8926eb37, 0x3146e0d1, +0x4acd7218, 0x47fe240c, 0x208d9e20, 0x434ddfc7, +0x7d242c3b, 0xfa7e31e9, 0x2b78e520, 0x0326f8a1, +0x05c72deb, 0xf88ec517, 0x13554c09, 0x8d8cccff, +0x1a0658b5, 0x0c0c179c, 0x7fb9683d, 0xe1727447, +0xe8c3feeb, 0xc100ffc4, 0x4710fbb0, 0x86eee88e, +0x2906e7e8, 0xc0159dd8, 0xf2f5d342, 0xa868089f, +0x086f0208, 0x11800c6e, 0xe04c0af9, 0x1f613217, +0x16f08c11, 0x2c5463da, 0x1c29284a, 0x41e0a5ee, +0x87e9df9d, 0x8a934ee9, 0xfdd0a326, 0x5ba306c7, +0x8b890107, 0xa26aebd6, 0xd778aade, 0x3cf2eb93, +0x32758b25, 0x71d12e56, 0x036a6624, 0x746819c2, +0xbd142608, 0xadb32e08, 0x7aaa15db, 0x33c2884d, +0x2b42b318, 0x3c8de265, 0x0f0b8306, 0xd8d88a50, +0xff758df4, 0x024304d2, 0x153a17c2, 0x011277dd, +0x081715ce, 0x013a5bb1, 0x29f5db8b, 0x0a90078a, +0x99a6db78, 0x048b7429, 0x88479f24, 0x8aa41150, +0x8168ebfb, 0xc648d2ed, 0x46f7eb2d, 0x14bf91ca, +0x4b5ed9e5, 0x07247619, 0xb7ae1c9f, 0x76473697, +0x742db414, 0x745f040f, 0x342dca0a, 0xfa881df6, +0x511e757e, 0xa7425372, 0x013ffc68, 0xc601103a, +0x558ac329, 0x6fea4500, 0xc8adb428, 0xa0111d08, +0x89df29e0, 0xb0300ae8, 0x74c474f8, 0x35b4000e, +0xe92bc381, 0xdc346fbb, 0x0c418ba7, 0x71b11a3a, +0x367b6808, 0x30465552, 0x52e8e223, 0x6c4f6207, +0x41382b3b, 0xe9122608, 0x2d502181, 0xbb30e414, +0x553dd17e, 0x04981f34, 0xbd544710, 0xb466d014, +0x448dd877, 0xe011aa1d, 0x05e8066e, 0xe427112a, +0x83a15df2, 0x7d1a20c4, 0x2006e410, 0x1d3bb617, +0xcb188768, 0x81764793, 0x55187637, 0x436d06af, +0x8e1c908e, 0xb2e42023, 0x41209b21, 0x9a24206d, +0x89ec2ecb, 0x28204c41, 0x82d9c818, 0x57538d59, +0x40d22d1e, 0xd8897a82, 0x7a0c9b12, 0x68791713, +0xe6b4e82d, 0x41d016c4, 0xe94b0cea, 0x0116e02d, +0xc6162217, 0xa1187a2c, 0xc5087176, 0x72d023ba, +0x81189d04, 0x0d3b0a09, 0x207c498a, 0xc1355828, +0x19677c4f, 0x7672151d, 0xef01708d, 0xb85b500a, +0x704e4b2f, 0xfbd3e90c, 0x6b05385b, 0xf32fcb93, +0xf7363fba, 0x21393120, 0x84bf248c, 0x80409fca, +0x1b02b96f, 0x8d3ce8de, 0xc8c00273, 0x3ce8126d, +0xf52efae4, 0x5206820d, 0x538df130, 0xd0597301, +0x8907700b, 0x960a95d7, 0xc6f8d528, 0xda00ff40, +0xd9203610, 0x142dd9c9, 0xa63d40ba, 0xac3b7a23, +0x14e9a144, 0xb4198ccb, 0x0c39356a, 0x58f6f1a4, +0x2157012b, 0xfa6fe81e, 0x578832b1, 0xd6761c07, +0xc0d6a20f, 0x998be7dd, 0x44c8cad5, 0x123bf6d7, +0x044203c5, 0x06fe6140, 0xa0c8c00d, 0xd371d8fc, +0x1308042c, 0x7c2a1b4f, 0x13514b20, 0xb52f3a80, +0x25bb7d48, 0xe98e7e10, 0x4ec1b78b, 0xe07d4683, +0xeb97ac5a, 0xddabb218, 0x32b827b1, 0x27112f3c, +0x300b750e, 0x6d9d887d, 0x20d5d3af, 0x0e6e3f3c, +0x3405b6c2, 0x0402c6d1, 0x3018234b, 0x3b80e962, +0x80e3c2cc, 0x6a284986, 0x85eb2f44, 0x2c9c45a1, +0x525053e2, 0x61609775, 0xe56714e2, 0x005b6479, +0xad202f9f, 0x7a832cd0, 0x6825450f, 0x01d33579, +0x005dd448, 0xe089664e, 0x741d5dc2, 0xaba54c83, +0x270020f4, 0x48d9e23d, 0xe8667516, 0x604158e9, +0x03734c2a, 0x3e201b68, 0x4828439f, 0xb62d1aeb, +0x45835c08, 0x2554440d, 0x16dd17ba, 0x19d304eb, +0x386c7d31, 0x713c42c5, 0xe3c4c82f, 0x1018d470, +0x7a582cea, 0xae286fa1, 0xcd860848, 0x86069d66, +0x45b5bddf, 0x86fc4503, 0x308bd7ee, 0xa0884c6a, +0x9e229b80, 0xeedee9e7, 0x04520f50, 0x0b0c0d0e, +0x6d506edc, 0xc9040a40, 0x53db0351, 0xb154ce04, +0x02a129c7, 0x7246f6c7, 0xa8032700, 0x1464b2d6, +0xb8aa85bc, 0xa37cc10c, 0xaac7a060, 0x894bc39b, +0xdeeff0fe, 0x8a0f4a15, 0x0934bf80, 0x0f0388f0, +0x5e04caac, 0xfa028b62, 0xe1a509ab, 0xe097e889, +0x59dc942f, 0x3003c64b, 0x7cf63b40, 0x9142f6f5, +0x50d6f681, 0xc458ce83, 0xf0ce0152, 0x9aff4388, +0x1703bd1b, 0x03466145, 0x10eaa3e4, 0x28034368, +0x08cf0113, 0xe925cb1a, 0x010b807a, 0x0525034d, +0x6efed0be, 0xc8b26017, 0x461365e9, 0x068aed31, +0xbc5b233c, 0xeaa60060, 0x52303c04, 0x8867b646, +0x357b9516, 0x741b162c, 0x1020824f, 0x6b0a126b, +0xdbd4ce10, 0x22a1d083, 0x7feeb9e5, 0xa5dbf439, +0x493068b6, 0x066c3c0a, 0x3c09eb41, 0x9bb6d87a, +0xf1b99abb, 0xc6e42018, 0x16202730, 0x376f4e82, +0x44756301, 0xf28b047b, 0x1c755a22, 0x0627f37c, +0x3bdf154c, 0x7573fa90, 0x521b1a17, 0x0226c36c, +0x09d0bb33, 0xd81bc6e9, 0x707b112d, 0x26361375, +0x226a498d, 0xa86d08ff, 0x1434eb3e, 0x3475583c, +0xf63c018a, 0x07da088d, 0x0b9d9076, 0x7a045b8b, +0x8b402b77, 0x310b3d91, 0xe2372ddb, 0x2026a17d, +0x3155d509, 0xfde8d91f, 0x838476c1, 0x5471ebfa, +0x75699264, 0x0a205e4f, 0x85132d9f, 0x07d2f0d2, +0xe007daf7, 0x99bb82f7, 0x0d6ebd4b, 0xd0ffa878, +0x83fdf799, 0x138830c2, 0x846eea3a, 0x048685db, +0xf76e2d8a, 0x280460d0, 0x952c2898, 0x36042067, +0xd0831388, 0xdfa4b6ea, 0x6062263e, 0x4d1b406c, +0x165f7372, 0xd10308fb, 0xcfe2d05e, 0x3e0884d0, +0xf0680402, 0x4c177d46, 0x730c483b, 0x14883406, +0xa781b008, 0xcae98859, 0x2d95d37b, 0x35518899, +0x71e094a2, 0x6552c5aa, 0x2e7cf41c, 0xa655ab44, +0xd039cbd5, 0x86a17058, 0x0e7cd18a, 0xc8859400, +0x30d46015, 0x502aa568, 0xd23102ec, 0xe8dc153a, +0xffa0a45a, 0xa8552c24, 0x835a836b, 0xd4474d5e, +0x1117356f, 0x43b45aa2, 0x3a778861, 0xd1d398ca, +0x10a08114, 0x994f6ce4, 0x954ea23a, 0x549ae0a0, +0xcb039853, 0x2e823c1d, 0xd00f8f41, 0x907325e9, +0x700831c3, 0x93069e13, 0x502ab405, 0xa7d0eae2, +0x4b9fbdba, 0x56652910, 0xdcbf9c14, 0x76fb9048, +0x355e5b0e, 0x602c0bca, 0x0da0c1d0, 0x5201e7ed, +0x322f7ecf, 0x33e7de15, 0x40f85d5f, 0xec0710db, +0xe451f030, 0x8d66a683, 0xcb25b168, 0x50ced550, +0x6d406042, 0x67a9508c, 0x8e7a6df5, 0x8505f122, +0x95d8f8ff, 0x92eb0954, 0xb90b93db, 0x104e9eb0, +0x92f4d829, 0xfb28b93f, 0x87b9c7e9, 0xd61b39e8, +0x664b1934, 0xece41c03, 0x320c51ff, 0x6b57c30c, +0xc011928b, 0x0fc840c3, 0x16814a00, 0xcd06e0e2, +0x7dadb882, 0xaa7ccef1, 0xe1180058, 0x90ff6972, +0x7f023643, 0xe6b19c5f, 0x9b3d92c0, 0xe6a26904, +0x6738e49c, 0xdeba1847, 0x6f415356, 0x2b1b3906, +0x643d085c, 0xc0ee50c3, 0x1547610a, 0x88170565, +0xdaab5d21, 0x41ba0666, 0xd7e62e38, 0x6fcb9dc7, +0x0ac59700, 0x1ecbf973, 0x53081435, 0xd94209c1, +0xbdd150a0, 0xa5aae8ae, 0x98ed5f44, 0x1d0c4040, +0x7456ea55, 0x10a97318, 0x700540a1, 0x47dfe8c0, +0xd7b8c302, 0xa3000110, 0x401b60bb, 0x4a6ccdeb, +0x71046b39, 0xbc48129f, 0x4e276e89, 0xe8fad90e, +0xa19ef2f4, 0x38825f77, 0xe0fb8120, 0x31da7225, +0xa92c0adb, 0xe30c0980, 0x6aabb427, 0xffa45da2, +0x111a7406, 0x4717bb87, 0x20508d12, 0x468a1b18, +0x8f6f99e0, 0xb04058b8, 0x16f913ea, 0x51379dda, +0xfea814bc, 0x8369e9ce, 0xa3700c40, 0x6ef00146, +0x430a20c2, 0x723d2a2d, 0xf0562893, 0x8a028097, +0x42881246, 0x05376de3, 0x8ae6c5d0, 0x05100e41, +0x45a02257, 0x0063d6b5, 0x8bda15ad, 0xee2f0329, +0x4cd6dca8, 0xa36c3ca4, 0xd81f7383, 0x0e48a431, +0x89e8201c, 0x03443219, 0x157b1b77, 0x2d620f04, +0x3329b6f5, 0x44142b01, 0x0c50c85e, 0xb9982e0a, +0x10511260, 0xcea44363, 0x2c41476d, 0xb1dbada2, +0x36e45ad2, 0x7df34092, 0x8a172444, 0x584c50d6, +0x30bed4a5, 0x41370fe8, 0x168b0fdf, 0x4239661d, +0x7d04da0c, 0x08c67ea1, 0x72f6fe81, 0x1bb011e9, +0x2d4c05ab, 0x0b772079, 0x66fa6f05, 0x268b473b, +0x04538a13, 0xc2efdf6b, 0x8582573a, 0x05430b1a, +0x0fef3af6, 0x628e8656, 0xc36b180a, 0x19ec6242, +0x000d4cf5, 0x90fe381a, 0x1db44a0a, 0x16eb49a4, +0xe19a4b1f, 0x87d1baaa, 0xe0a39e0b, 0x05eafde2, +0xfdf7e228, 0xaf8e14f9, 0x72147a8d, 0x6208d708, +0xb4e57440, 0xe191131b, 0xa148e22d, 0x189adf40, +0x0371b1fb, 0xb226e2c1, 0x13a34013, 0xf7bf3c7e, +0xba898183, 0x468d5f12, 0xb1ed4b3e, 0x54050dce, +0x15001554, 0x436a2a06, 0x841372a9, 0x2f5d7597, +0x4287bc01, 0x050d1d02, 0x8f01c201, 0x86904eda, +0x84153b70, 0xef8bdff7, 0x0686078a, 0xf8e2aa46, +0x1ba1b952, 0xe7d90ba1, 0x2748cda2, 0xc7547b50, +0xc78b26ee, 0xd06c50e8, 0x0a11000c, 0xbc635838, +0x541874d7, 0xbb148833, 0xa8512863, 0xa9c09f0a, +0x252b6a6a, 0x98f6aa59, 0x622bba2d, 0x8a086af1, +0x2a990c11, 0xfd3cf062, 0x48b60776, 0x1bfc1a0a, +0x02488d0a, 0x3178cb29, 0x6fc282de, 0xd20ff32d, +0x2a74fa39, 0x56a2d459, 0x6d0b7f8d, 0x2d11bd68, +0x75f7ed88, 0x41678100, 0x4678512f, 0x02ecc1cb, +0xa235ce01, 0xad2f5b8d, 0x67fd04be, 0x1988111a, +0x294d03af, 0xa294582a, 0xec64da37, 0x038492ed, +0x8d3a5113, 0x6ab87247, 0x423a39bf, 0x78c172c5, +0x68c6010f, 0x39b10740, 0xb5735f56, 0x8711b4d1, +0xe5ce9a14, 0xf6a83730, 0x031e5048, 0xd9a73003, +0x53a9743b, 0x0476cb07, 0x4602508a, 0x65daac55, +0xfeada1c0, 0x1dc0824d, 0x126a381c, 0xdc5438cb, +0xd72ae713, 0x7236a0b5, 0xdb011089, 0x72c58452, +0x15c2bbff, 0x9e5e2218, 0xd4467a01, 0x5818eb19, +0x74b6feb5, 0x79c04f08, 0xd7eb6d0a, 0x6320cac2, +0x028af731, 0xca14c748, 0x81ddaf0b, 0xc13ada80, +0x099c08ea, 0x1d08803e, 0x22424b2d, 0x7d2ffc05, +0xb9016a34, 0x014b01d4, 0x5eea831d, 0xf888554d, +0x8dc16dba, 0x644118be, 0xdcc54579, 0xc7081f18, +0x8114b206, 0x8b7ca48f, 0x769b0b02, 0x557c2b09, +0x3b2203fe, 0xf11b4292, 0xbf1976ee, 0x6a740034, +0xb88a0374, 0xd8d6386b, 0x40895d9c, 0x00141808, +0xe876f678, 0x7414102a, 0x3df80198, 0xb0cd1b44, +0x012a3735, 0x28f82f60, 0x0537441a, 0x0a3c8d70, +0xc7f000be, 0xf9296805, 0x02348d43, 0x83203aef, +0x54eb6279, 0xebcbfd95, 0x742d0156, 0x3fce067c, +0xee8a88e8, 0xa6d82a6b, 0x48a08a10, 0x50098bf7, +0x285f8aaf, 0xa5f5a035, 0x222d4b52, 0xe0034978, +0x1ec18771, 0x117701f5, 0x5af29b6a, 0x423e0a51, +0x996be8a1, 0xb1ebd374, 0x93005350, 0x5f0181b1, +0x6de90744, 0xc1027ec8, 0x9031786a, 0x241300b1, +0x400aa2a1, 0x03de4a18, 0x776a0dea, 0xfc9fde77, +0x820191e3, 0x0e427016, 0xc50757c4, 0x931840cd, +0x6a9b06c2, 0x10d14220, 0x9905f649, 0x6f9ef9e7, +0x02030404, 0xd49cf801, 0x70dda042, 0x19f765e0, +0x099f783e, 0xc40cb824, 0xadabd074, 0x28c46a95, +0x138b055b, 0x55b41288, 0x54744685, 0xbc85046a, +0x045bd3b8, 0x0c9612ba, 0x317a8b0a, 0x8c691054, +0x009083c0, 0x608d83b0, 0x3e2b9ff3, 0x300c29f7, +0x7bb66e00, 0x56b4d80c, 0x1c7e0cf0, 0x22d828fd, +0xd1dd2d2a, 0x140baa0c, 0x90136819, 0x69949050, +0xfaff8830, 0x1d62cbd3, 0xba1e2ea3, 0xe737fd58, +0xc01a0af3, 0xa45f590d, 0xf4b46a9b, 0xf13957ae, +0xebe01776, 0xa2654f72, 0xede93cae, 0xa157c3e5, +0xef790012, 0x1d152053, 0xff9e93fb, 0x12402bff, +0x8d860545, 0x5c26c635, 0x7f0f4e5a, 0xe6dcd8db, +0xc4ef225a, 0x74c3392b, 0x48dceb67, 0xdc232d6c, +0x494960c8, 0x3a21dbc8, 0x6afd13de, 0x45f089fa, +0x63cc2041, 0xb7d82df1, 0xf619771a, 0x06c3c3d3, +0x6aa181c8, 0x02b80185, 0xb55d1137, 0x4440c60d, +0x113ddf6b, 0x000e2aff, 0x3d54423c, 0x1afb61b4, +0x5676276c, 0x582ac306, 0x8a15aa44, 0xd1395c7e, +0x08c40676, 0x036001d6, 0xa7d75cbb, 0xd2ed11bd, +0x3958faf0, 0xebc85002, 0x28b807b5, 0x0f00b642, +0xa28674b0, 0x38f5ab83, 0x14382980, 0x2118294e, +0xd73c48c3, 0x31b9054f, 0x270d0116, 0x9e020620, +0x7361d880, 0x20006118, 0x1d2c7188, 0x0101df0c, +0x307137c4, 0x2c75d295, 0x2ea11deb, 0x5c26b175, +0x0347669b, 0xae8aac76, 0x44214141, 0xb10d5bde, +0x66293dc7, 0xd82d1ba1, 0xdc9ea11d, 0x0b54b840, +0x147440c6, 0x447d1d4c, 0x404a612c, 0x4aa2e70b, +0xfc98b402, 0x5f80b203, 0x64b8ac5c, 0x17d21203, +0x5a22db1c, 0x16ba1521, 0x9e31fecc, 0x7fae38d8, +0x640452e8, 0x0057808b, 0x3223f69d, 0x07083100, +0x16c5e20d, 0xf80f0606, 0x50a0a2fd, 0xc6082d74, +0x2db8bab5, 0x0d035009, 0xfe0e1d14, 0xc851be91, +0xc26a8745, 0xa36951e8, 0x4470bb0a, 0x52105e3b, +0xa5a0b100, 0xc834c708, 0xbb160363, 0x595a1b47, +0xd77610d3, 0x17e94004, 0xd16858d1, 0x9813e0af, +0x8b63093d, 0x5f40566b, 0xfe4916c1, 0x22e8c654, +0x186e4f56, 0xc3303c9d, 0xd93f530d, 0x2b1d1051, +0x05539c1d, 0x86000785, 0x54d5611e, 0xf86d610e, +0x48296fd0, 0x3a11ecc0, 0x5e8a5e6c, 0xaf68c00b, +0x8727a392, 0xe905b040, 0x51078d14, 0x7070f0a2, +0xc3167e83, 0x2d15d7bd, 0x1672560c, 0xcbd18a5c, +0x86448d44, 0x00b8deee, 0x43bbf706, 0xf7ecda44, +0xbfd01cd5, 0xc5219ea0, 0x88adc5f7, 0x513b8007, +0xa803ebf8, 0x67d14650, 0x0c25bfe0, 0x110161e9, +0x753829bc, 0xe8644467, 0x0470d063, 0x0a999c23, +0x22b8e9ae, 0xb14ec543, 0x3801681a, 0xca0e0d40, +0xf0776bbe, 0x49b7b63e, 0x688102ef, 0x06bfdc10, +0x56e0adc3, 0x53d7e833, 0xa23af839, 0xf855c160, +0xb65c105b, 0x9ae880b1, 0x8e08315c, 0x2aa61702, +0x1a744eec, 0xdf140ffa, 0xcb85f822, 0x40c602fc, +0x02406001, 0x16dbb405, 0x06ebf688, 0xdb707b83, +0x31efa37e, 0x615a246f, 0x57086b3c, 0xb6de37b9, +0x2d765408, 0x03403502, 0xaac3540a, 0x68e973a5, +0x08808b55, 0x64c6a27e, 0x427860ec, 0x5f144208, +0x080a9c53, 0x463b8943, 0xc1590e43, 0x4c435b22, +0xd25dd633, 0x8a584025, 0xd6a42aaf, 0x78a82d06, +0x3e640c61, 0x04b6d820, 0x875c1e0d, 0x7e03c70e, +0x568bcdec, 0xb866da76, 0x5f851975, 0x444b8de2, +0x10c783d7, 0x5a782757, 0x5785bb16, 0x02eb1dce, +0x7a6106c1, 0x83f0b3a9, 0xa04efdc0, 0x0e2abd23, +0xa9fe04b9, 0x2f914459, 0x0c1ab247, 0x2bd57fd2, +0x683b4c68, 0x6ff27650, 0x2296171a, 0xffc6d51a, +0x21c74ce2, 0x85e0d045, 0x747066d7, 0xead0f428, +0x5048b013, 0xec544bd3, 0x02e1680b, 0xa4558d4c, +0xd85cda01, 0xaf14e1bf, 0x0908e7c1, 0x1feb447b, +0x0d054127, 0x8224cc41, 0xd68511ed, 0x02888438, +0x370a22f2, 0x8358e10c, 0x06c5f44b, 0xa9dec905, +0x3802bc10, 0xf3238a08, 0xe3680ebc, 0xa963cb15, +0x43b675d9, 0xc74d75ed, 0x48060d09, 0x6a58890b, +0xef314dde, 0xd453de22, 0x36fbcdf8, 0x71421666, +0xe8b9ef57, 0x52b464be, 0x0222ddbd, 0xf613d924, +0x353d02c4, 0x53f63b25, 0xf0c2424c, 0x44909bfe, +0x05a36d82, 0x02444656, 0x3a7a0248, 0x5ceb36a9, +0x4de0ac59, 0xc2cb99f0, 0x99fffffc, 0x2ea81eac, +0x85d12e90, 0x38c40132, 0x2b78d2bf, 0x58574038, +0x9c9610d4, 0xee6c7b77, 0x4410093c, 0x02480661, +0x06b6eb36, 0x6bfc10c1, 0x02897f4c, 0xfdd8f813, +0x8c83c76c, 0xe1a6d416, 0x511e7620, 0x98ff4d03, +0x20adff2a, 0xd2023776, 0xa0900257, 0xe146defa, +0xe831dace, 0x76ea6c9f, 0xbb0afd41, 0x5486ccc9, +0x425f021d, 0xa30f4928, 0x6a3c105c, 0x1f3d0737, +0x05a025d8, 0x703e19c4, 0x88543649, 0x540ea86a, +0xedf9815c, 0x05ff09c8, 0x40c71875, 0xcbba4071, +0xfc21d6d4, 0x52e9b3e3, 0xbee9d23e, 0xe03003fb, +0x2b96f123, 0x8b4472b2, 0x2ee9b368, 0xa7104788, +0xef50fe87, 0x0c45481b, 0x1c3cf025, 0xfef799d6, +0x390fba6f, 0xf883e4e3, 0xc8398f22, 0x14a2870f, +0x2a87bf74, 0xb9e7c3b7, 0xfb1a5847, 0x1274a508, +0x211d3345, 0x0f306550, 0xaa58d343, 0xed108318, +0xb25b45c1, 0x245aae80, 0x82758501, 0x088acb4b, +0x0e457834, 0x83050bda, 0x0d456c23, 0x8f150688, +0x55192dd0, 0x1044ac14, 0x82ba8aec, 0x28a29d1e, +0x41c15fab, 0xaa27ebe6, 0x8d733c27, 0x037c152e, +0xd71aeb42, 0x18843c06, 0x0cc57775, 0xeb28cbeb, +0x0f2c0504, 0xf6f460b5, 0x3a543bc2, 0xad8bd372, +0x2b44142b, 0x9843717a, 0xcaa3406a, 0x60d31b2b, +0x330b0c21, 0x52048de9, 0x0dc2c413, 0x3bffbebf, +0x7768f6c7, 0xb60fee05, 0x10a84283, 0xd889ad74, +0x47f92654, 0x4405fc6f, 0x113b1741, 0x0b1c1135, +0x93d75c35, 0x2bf63074, 0x026c29ab, 0x4c40bed8, +0x4643f63c, 0x680a150b, 0x53c57aa2, 0x2a292cc2, +0x82965050, 0x512e9479, 0xd0242438, 0xe8568113, +0x6c285804, 0xf9abba3d, 0x67dfd21d, 0x48453c8f, +0x503f7404, 0xb0344a74, 0x2f2b6775, 0x552b343b, +0x99a90bda, 0xba618e0f, 0xafdfb3c7, 0x60f9086b, +0xe90bbe9b, 0x0d7366e5, 0x58eaa775, 0xec95ee29, +0xee39797a, 0xfec001da, 0xca7cd672, 0xf001207f, +0xa84607a0, 0x849c0c72, 0xbc104d7d, 0xe32945ab, +0x85b40170, 0x29b888e0, 0x16c41b03, 0x05c3957a, +0xe622b1dc, 0x39b77200, 0xb71c7554, 0xc76eb79f, +0xf7411e93, 0x6ae0e86f, 0x11213b46, 0xf76da0b8, +0xb941f044, 0x4f0e1302, 0x18cf85f2, 0xa936d42b, +0x60e17439, 0x307deb3b, 0x1b12942c, 0x31112229, +0x3e02e6d2, 0x970fe0d8, 0x2c470bb3, 0xc7c636f0, +0xe03ddb25, 0x0078e90f, 0xb7751b1c, 0xdd289f5c, +0xa5f66e60, 0xaa2fc4be, 0xaac20403, 0x20b142c9, +0x8ce66630, 0x6ba05d37, 0x1e1d660b, 0xeb2d2966, +0x4870541e, 0x1180d217, 0x3802ba2c, 0xd3d813a0, +0xe5906dc7, 0x2ad78027, 0x8cc360a1, 0x09f6e2c1, +0x743c3966, 0xc358d411, 0xc59c9200, 0xd426f0d1, +0xc184700b, 0xf9ff310d, 0x8a5aa41a, 0xed5118a2, +0x7a58588e, 0x791468fd, 0xba33497b, 0xbad423b1, +0xc8ee1748, 0xf30a0975, 0x6db406be, 0xf0d25aeb, +0xda1e2c3d, 0x36b84a82, 0xdb5ef38e, 0x84992c18, +0xda4d5e1c, 0xac6047c7, 0xb102b9d0, 0xbcbc2b03, +0x15430604, 0x0a6ca68b, 0x100de2a6, 0x06c3835a, +0x8a129453, 0xf989f067, 0x696440ba, 0x9b510d60, +0x482d55bd, 0xcf769ee8, 0xb6e04f21, 0x1003488e, +0xf070c748, 0xe0785813, 0xf87ded5b, 0x437dfc75, +0xf09b0442, 0xb831d3a6, 0x47f15248, 0xcbc2e893, +0x2f02480f, 0x7ebf0f54, 0x683a788d, 0x0fa9ee04, +0x2c120968, 0x0a02160a, 0x4c28883c, 0x93071c04, +0x10f4225e, 0x281c3ae8, 0xc12892f4, 0xc28eba28, +0xff4aca0d, 0x820b1c0f, 0xc7afcc29, 0xbc706019, +0x04602433, 0xa1a02800, 0x79031b1e, 0x0781a0f0, +0x5ec1aa41, 0x6c458248, 0x40a729af, 0xc4669621, +0xc8ad2ea4, 0x11a17a38, 0x5a6fe5ae, 0x6507fa83, +0xd9d141e8, 0xa6d69a0d, 0x9b7de2db, 0x39e011ff, +0x82ddebd7, 0x27d8c507, 0x0d1574d6, 0x1ab54dd6, +0xc4e8c9c1, 0xe8bcf45e, 0x358d1822, 0x5b375c03, +0x602c5cd8, 0x890a57a0, 0x6c5f5807, 0x470b1863, +0x081c5751, 0x1f70d43b, 0x6005b24b, 0xb794683e, +0x064b67ee, 0x8d2e0f0a, 0x831f206b, 0x6c1cb9bb, +0xe3412384, 0x25a7df79, 0x078b590f, 0xc504ba75, +0x63e214d2, 0x90548124, 0x0342eba4, 0xd16a345b, +0x76d8c6c3, 0x02429d08, 0xb1164b05, 0xe77a02e0, +0x051c62f4, 0x9f057434, 0x6d6d85d1, 0x83e1b4b1, +0x3cbf163b, 0xc10f41f0, 0x4197b63b, 0xc710ffbf, +0xffc65e38, 0x862041ff, 0x0eccae15, 0x6d561724, +0x92e04bf0, 0x0140193c, 0x017fd29e, 0x5de383dc, +0xc3d3f22c, 0x972820a1, 0x81017f66, 0x0824a10e, +0xae112468, 0xea044260, 0x4c257aa0, 0xe13fca0b, +0x4bc30a6d, 0x657d2b03, 0x0ca4858b, 0x5b333554, +0x0c1d218b, 0x8f1e0a43, 0xa8bd8bee, 0x5b104c26, +0x95fa0fb8, 0xd56812a0, 0xf0cb7dee, 0xd8784463, +0x9d8b0102, 0xc18d9e34, 0xc8e2c3ea, 0x9803891c, +0x50fdd8f2, 0x312fb67c, 0x1add26a8, 0x0a070c8d, +0x429f075c, 0x2d56c629, 0x74964780, 0x83f1ade3, +0x750e48fd, 0x7a876eff, 0x8df3a840, 0x601ea498, +0xb6500881, 0x4bd0ae89, 0xea4d2640, 0x40fd81ea, +0x0553ef47, 0x8806ba05, 0x0f73f730, 0x86c7a516, +0xc6e80ad4, 0xd8903b86, 0x3905f111, 0x800173ea, +0xe1b04a2a, 0x34f253ea, 0xc5e09356, 0xc7b22b46, +0xb0398286, 0x2263491b, 0x140f9086, 0x66b27d92, +0x1c1040a0, 0x0c24048b, 0x177bdcc9, 0x6c948611, +0x8c170399, 0x28828cd0, 0x1d08ea2b, 0x7c2d0ebc, +0x70bfe3e9, 0x84dd9060, 0xd4830663, 0x01180989, +0xe411beaa, 0xe8786d1c, 0x19a832a9, 0x39b6c704, +0x763c6ffe, 0xd15e153b, 0x0773e880, 0xb56be850, +0x7b8a36c6, 0x82580763, 0x8f33e445, 0x53e85c07, +0x54826642, 0x49c17bf5, 0x9334c73c, 0x5413084c, +0x0a858534, 0x1b7c346a, 0x0d813001, 0xef87b0e6, +0x187883b5, 0xc1407400, 0x8439100f, 0x4b80e16d, +0x02454301, 0x01854655, 0x40f8486f, 0xb54d8589, +0x080ec032, 0x460bdf6f, 0x9c85531c, 0xc243dd25, +0x638564d2, 0x10c4bd8d, 0x2c151ba5, 0xf1380072, +0x15fc0003, 0x8003304d, 0x04316ae8, 0x7d1ca815, +0x2310522f, 0x15e84cdd, 0x9206dddb, 0xe9c6f1d4, +0x2e19fdf2, 0x7613fbba, 0x22b3a5fc, 0x252e2b4a, +0x0ea5d59d, 0x2bbcf2c4, 0x6d587bec, 0x7843a299, +0x58462157, 0x2736e1e2, 0xb528d3e8, 0x64854897, +0xc24ecc00, 0x0b75a376, 0x46d004a7, 0xc889659a, +0x22a5dddc, 0x0c61272e, 0x113a42bc, 0x927d8f70, +0x127442dd, 0x3144c8c6, 0x1dbda8ac, 0xf0862136, +0x3b4228a6, 0x4940f687, 0xc2808501, 0x4b563247, +0xdc2a5b60, 0x834bca12, 0x22065028, 0x83b010b6, +0x95053a7e, 0x28548420, 0xadeed784, 0x6ae880db, +0x50025104, 0x100f5568, 0x7955d46c, 0x11e44f24, +0x567fc0b4, 0x46f628c4, 0x001d2e49, 0x63b70018, +0x70d61b9a, 0x945f68a2, 0x5dd30601, 0x9643b90b, +0x606a1f22, 0x80cf2376, 0x25a5416d, 0x68317010, +0xe450c0a3, 0x41b68606, 0xdf7aa184, 0xc6ee2938, +0x10f6e41e, 0x234ce852, 0xbe3b4a47, 0x1150bb84, +0x724f8700, 0x80fb75d1, 0x12b9b64d, 0x086f2625, +0x476a6b74, 0xb2d91ef8, 0x71ba6446, 0xbf27e3fe, +0x0811a3b6, 0x3737148b, 0xd8606d64, 0xd8bd02c3, +0xd87273e3, 0x1165dec0, 0x226ad975, 0xd1bd80b5, +0x2f74702c, 0x9001006a, 0x687bbba7, 0xdd297fcf, +0x80343455, 0x60a30554, 0xd2080438, 0x8dd99eb1, +0x24047b80, 0xb1d24d29, 0x83c72a02, 0x12a0832a, +0x431ffa50, 0x4455d98a, 0x7c250362, 0x4142880f, +0x05198240, 0x83104531, 0x42c7ff82, 0x27004008, +0x072d1923, 0x83ef19d2, 0x4e83422d, 0x172072c8, +0x1c428342, 0xaae7b60c, 0xc34f9c55, 0x974a0c3f, +0x468b3cc1, 0x303e0d36, 0x1439ee11, 0x023804b0, +0xcc687111, 0xaeb1013c, 0x018314eb, 0x5976ffb8, +0xced061c5, 0x105faaff, 0x2025516f, 0x1a75adc3, +0x8401c189, 0x2c55ca89, 0x11c2f4d9, 0x8b9c47a0, +0xff8a2b12, 0x75cbd4ff, 0x4f1e3302, 0xa47005e6, +0x9389160f, 0x343083e8, 0x6a8d2888, 0xea116436, +0x6510d476, 0x0bebd28a, 0x6f4b4408, 0x1c4cac61, +0x41832c3f, 0xec6fdbe5, 0x72ebe8c1, 0x740f3d0e, +0x76281bee, 0xa1064e37, 0x8007c289, 0xd8f70aab, +0x228dd4aa, 0x0b03e10e, 0x37770013, 0xc0d5badc, +0x2de248e9, 0xe889f08f, 0xc0a2908d, 0x32e99d08, +0x196e63e2, 0x1c3b0a00, 0xba8036e0, 0x65393faa, +0x3efe745f, 0x4f9fa238, 0x8b19878d, 0x18802868, +0x12440d06, 0xef6e029a, 0xc1df866b, 0x81df0bcb, +0xe2dd00ef, 0x72f1d105, 0x53e2b5af, 0xad583d8d, +0xe1878b64, 0x885c5d50, 0x18c80362, 0xccea01ee, +0xeb04b800, 0x9c2b896f, 0xd24bc3b0, 0xbea89f8b, +0x0d086f47, 0x34553d86, 0xd4fc466e, 0x58b555b0, +0x40d93840, 0x5a807de6, 0x1df22b07, 0x3541e936, +0x22cb182c, 0x15265e58, 0x185b6a3c, 0xf15e6cc7, +0x993d4da1, 0x868dd5ae, 0xdd24e854, 0xce381705, +0x35ada427, 0x023de820, 0x0e887ac1, 0xe26d00a2, +0xa02e858c, 0x8bfb8db6, 0x50dc4dae, 0x7727fc40, +0xa0c56809, 0xc3014387, 0xd46e2314, 0x85b526df, +0xbf330fb5, 0x02bed1a0, 0xff348e0c, 0x9cbf3b0b, +0x458b03ad, 0xf7d880d4, 0xbfce253b, 0x420426b3, +0xd850bdc8, 0x609a07ef, 0x147ebf03, 0x50570ee0, +0xe87aab12, 0x586ee22f, 0x60084600, 0x58efd78f, +0xe08bb389, 0x7480f102, 0x13a37185, 0xb6b080d0, +0xf7279783, 0x0223ec44, 0x0c168db2, 0xab0bc2c2, +0x1f127800, 0x83429824, 0x377402f6, 0x63811cee, +0x88d47c48, 0x913c3540, 0x3427ea2b, 0xb0e2245a, +0xf3be182a, 0x82be0c90, 0x876943c2, 0x66629160, +0x4bfe0f18, 0x816e4340, 0x19c3e885, 0x76809f0f, +0xc2147ff4, 0x232801cc, 0x6c447501, 0x0ee59590, +0x3eedd423, 0x6e028a8c, 0xbd180247, 0xf0d7753c, +0x8b1fd103, 0xbff5e8a5, 0x741385bc, 0xd88a4c2f, +0x02aae9b8, 0x089f856e, 0x03c54f62, 0x874efc19, +0x523b4073, 0x4c002ca5, 0x78a63ebc, 0x9dae315f, +0xa023ac23, 0xd4335575, 0x0bb5b425, 0x27db155a, +0x83a00235, 0x90641e8d, 0x2b491b1e, 0x6ea9029a, +0x0faa8319, 0xdd5246a2, 0xe9dd4551, 0xdb84aaee, +0x4d10c101, 0x152e0708, 0x368b11df, 0x91243489, +0x5c71e9aa, 0xe91013d4, 0xc0b73b53, 0xa006d8ed, +0x14e4e991, 0x5b8a5018, 0x53c16f07, 0x3c121425, +0xdec7f003, 0x8e97b7e2, 0xf2ba251a, 0x16fa802c, +0xbd187722, 0xfe06e0dd, 0x13eb97ca, 0x0777040c, +0x68a0b485, 0xfdb80bb7, 0x85be9a06, 0xeb8748ef, +0x22be0605, 0xc2907f30, 0x95007b71, 0x25795601, +0x11262a31, 0xc44e9360, 0xa838155b, 0x07484545, +0x2ee800c6, 0x0814eb34, 0x7c45810c, 0x53047141, +0x6c430cf8, 0x42fa15a2, 0x832aebfe, 0xfcce3a7d, +0x95866c15, 0xb72d4c26, 0x52700e43, 0x66da20db, +0x1aff8867, 0x4d151ea0, 0x0aa5c04b, 0x74f8445a, +0x02b38f2b, 0x51516d3f, 0x8ac295a4, 0x67a60704, +0x21c23e31, 0x8dd57592, 0x4118338d, 0x97d10268, +0x79802003, 0x810dfc02, 0xc9cc8d42, 0x26050dc3, +0xdc03218c, 0x6076d799, 0x85907b5b, 0xd9f69075, +0xc8448ad0, 0x43cfac36, 0x09c885c7, 0x432a8d2c, +0xfc97e934, 0x19318b0d, 0x619d908d, 0xb2324e10, +0x982c940f, 0x07d49d00, 0xf4eb70db, 0x6fff2796, +0x1be93a74, 0x1c11e02b, 0x033a9324, 0x226ce85c, +0x7c291c6e, 0xedafe937, 0xadede89d, 0xff092715, +0x3c248d59, 0x787dcd01, 0xfe2ddd45, 0xa17f1340, +0x9075b805, 0xe80d0e0a, 0xc9fb0004, 0x806e20f1, +0x86f0303a, 0x0978017a, 0x6f446c77, 0x8dd34680, +0x538b6695, 0x00a0e838, 0x60c6709e, 0x101a8246, +0x043a4229, 0x87a85b7c, 0x83071088, 0x7a1302c3, +0x4ffe2707, 0x468d0794, 0x950aa678, 0x27c3dc82, +0xa59a624b, 0x05db6edc, 0x18b61c03, 0x7c13c1e8, +0xa1741b16, 0xcc808648, 0xa1ef48ab, 0x73216056, +0x111311ce, 0x7155adc0, 0x51d9efe8, 0xec406433, +0x111906c2, 0xa8efebfa, 0x47c56225, 0x93a05941, +0x7d48f40e, 0xe82a03dd, 0xb4df83f3, 0xca0a2cb5, +0x9d14d007, 0xa02c53e8, 0x81aab4bc, 0xbb239d8f, +0x758d3f45, 0xc0504578, 0xd82ea674, 0x5b0b8440, +0x9e2abf44, 0x94172388, 0x45b511f5, 0x01547e34, +0x8a096a13, 0x266f9781, 0x44158188, 0x2c04028d, +0x5551bbe2, 0xcc6589c8, 0x862ab0a7, 0x8def2050, +0x2af75112, 0xc05d8ad0, 0x8b1fc2e7, 0xa38e84a6, +0x3a3c0332, 0x13c6f175, 0xfe25c015, 0x955c8943, +0x056f42d4, 0x8c168075, 0xe8d417be, 0x2763d136, +0x0e6c5018, 0xa5dc1275, 0x3a31543b, 0xbb4dbbcf, +0x8aae7c06, 0x1c0cbc77, 0x0140dfdf, 0x87013a0b, +0x285894db, 0xc8bba1de, 0x9a892d9e, 0x135d8089, +0x1c9ae65d, 0x19b0cbe2, 0xddd76855, 0x896845c6, +0x521a0266, 0xe80e56b9, 0xe406a37b, 0x46a6bbe8, +0x1ba4eb28, 0x0ceb3701, 0x02d705ea, 0x8b1b4442, +0xe1f0ace2, 0xd68e2893, 0xf68334b8, 0xc44c04ba, +0xc40dcb59, 0x0c0b7106, 0x896a7241, 0x38a030af, +0xdd897c14, 0xe8382e83, 0x0cc2838e, 0x31c152b8, +0x18b3b5aa, 0x0ba061c2, 0xfea27b9c, 0xa21f4508, +0x01a209b7, 0x35dd28f1, 0x9d74052c, 0x303de7d4, +0x0a07342d, 0x0bfa7bbe, 0x77ed42f1, 0x712722eb, +0xffcffde8, 0xc44731ff, 0x76748487, 0x18460dd6, +0x68549a22, 0x6b191d57, 0xeb4fede2, 0x4974ed09, +0x581bd374, 0xa9ac6ee8, 0x0dc5234c, 0xeb48515c, +0xed06293c, 0x8725aa9e, 0xc75ec1e7, 0x90b89114, +0x63b42873, 0x2a8ea17d, 0x5641a3eb, 0x08eb1851, +0xe1dd36c3, 0xc12c21eb, 0x91fc1aeb, 0xebd143de, +0x0fb4c9ed, 0xec14816b, 0x5e18dd80, 0xe3f2fa5f, +0xb3c5c455, 0x1824c38c, 0xce5e00ac, 0x8ccd4124, +0xa631dd87, 0x01146455, 0x925cc9f3, 0x01c3c150, +0x65f32466, 0x837b7000, 0x063d573e, 0xe50b2462, +0x8013fa69, 0x7981ca39, 0x06a98929, 0x83cff7b3, +0x3d3f02c7, 0x05b0b283, 0xe0d1f45b, 0x3bcb6039, +0x407392e9, 0x54e4348f, 0x6231e571, 0x0b0919b4, +0x3e4e5f00, 0x52c107b9, 0xe3c809b9, 0x3e9a8b2d, +0x381ab1fd, 0x1b753014, 0x707c6246, 0x1c6c07e2, +0x0306ebe0, 0x415745f8, 0xa47052bb, 0x897b0519, +0x1549f5d8, 0x7d026124, 0xc4b51182, 0xabe0ff58, +0x09c1fc5f, 0x72281a08, 0xa23eaf10, 0x210637d1, +0x18584ce9, 0x743f0516, 0x91bee635, 0x06d17aba, +0x5ceb789d, 0x6304a33b, 0x67331ee1, 0xad9fbe20, +0x843fc61c, 0x9f0403c5, 0x4f60be42, 0xa324b073, +0x411a03b6, 0x8b360221, 0x601743c8, 0x0c58d622, +0x716c682b, 0x09108a8b, 0xaff989b9, 0xb127a11a, +0x1bcfd6ff, 0x658b4023, 0xfff014f4, 0x63f1bbff, +0xf2fd8f63, 0x3c8b37eb, 0x0789f801, 0x8275503b, +0x21c0e237, 0xab55ac01, 0x40c30118, 0xe9f34074, +0x1653cbfc, 0xf0184314, 0x20dd3d2c, 0x08f83513, +0x43f0f295, 0x242383fb, 0x4c45efa8, 0x25038940, +0x620261ce, 0x24071d7f, 0x191b6c8b, 0x838228b0, +0x65308a16, 0x585a8931, 0x0c281d9a, 0xc63f442c, +0x025c2f6f, 0xe4508d9a, 0x10e11252, 0x21d65a20, +0x45a20922, 0x45c009c6, 0x8591d3ab, 0xf1520829, +0x1ba70a75, 0xf4c0b3a2, 0xf9e38b20, 0xab687f05, +0xfa39f8e8, 0xcbef0a73, 0x88ed9fe9, 0x8ecb053a, +0x805adf89, 0x12d0a0a1, 0xdb77824e, 0x04436eaf, +0x631cec8d, 0x17635382, 0x40e86895, 0x3f889c40, +0x9b700b2b, 0x808a087a, 0x0388ded8, 0x34689f17, +0xf983bd04, 0x0aa07610, 0x8d1a0b6e, 0x4b88d217, +0xfe30ed02, 0x747bc41e, 0x702e7dfd, 0xae5158db, +0x178b99ed, 0x1fa5145c, 0x6f6f62a8, 0x37b4b37a, +0x40063e0a, 0xba016aaa, 0xde20cf35, 0x35e8b693, +0x000c88f7, 0x08370330, 0x4614200c, 0x0a986b86, +0x04220203, 0xae06df4c, 0x5b018be1, 0xc0754113, +0x6d930144, 0xe8d75db3, 0x4693548d, 0x200d7a12, +0x308a35d7, 0xc138393a, 0x03e264d7, 0x921ea7e8, +0xfb330699, 0xbfea3710, 0xce49b24c, 0x00975089, +0xfc758895, 0x163cb3cb, 0x54dc2202, 0xed57e3eb, +0x51704718, 0xea412a1c, 0x8a91044b, 0x04e86e49, +0xe2c083b1, 0xd6f42248, 0x5484ea08, 0x86e9a937, +0x54b89cb2, 0x01e00c18, 0xbb6db5b6, 0x6a22f036, +0x8d71ce89, 0x9c94c8a4, 0x40522a12, 0x89eaec1e, +0xe41e4ce7, 0x4c7e6e40, 0xb60a188a, 0x3c060e56, +0x0a2a2673, 0x80cd1f51, 0xe8dfeee2, 0x5046b942, +0xb92b8068, 0x0aac1ea0, 0x9db7c16e, 0x21f6b846, +0x8d1db111, 0x580db446, 0xd40a2a13, 0x2273a723, +0x1591bc48, 0x02505f8b, 0x5ba1dbd3, 0xf9e9f4aa, +0x52c0c002, 0x5acbbb31, 0xe3183671, 0x54e8b085, +0xa005dbc0, 0x6f1546c5, 0x08de0210, 0x40466a5e, +0xc45bc237, 0x1aa4c37f, 0xa42cda04, 0x55fa29bb, +0x1c582b44, 0x228ea603, 0x110a36cc, 0x023404a2, +0xc27a1000, 0xe0db7c18, 0x40aafe29, 0x79b430e8, +0x0a053610, 0xcbeb5d4e, 0x0127a842, 0x9f409e0b, +0x9b147b85, 0x045dfea9, 0x1855ed83, 0x8a9becc0, +0xe8d05717, 0x00003551, 0x75a0a313, 0x0654b5db, +0x1e1c84c2, 0x0cd9c146, 0x1ec02034, 0x204f0dbb, +0x922052ce, 0x5a0d1397, 0x197dfa08, 0x9b2b461d, +0x4c32e810, 0x8295045b, 0xe8046d46, 0x6d4d8f61, +0x92b2e006, 0x43e9e3fe, 0x8baa2415, 0xf2604038, +0x1abf1872, 0xa22fb2ab, 0x427c01b3, 0x2bd4cae1, +0xb60611b1, 0x02c01488, 0xe1433e38, 0x0cdab301, +0x161f4b0d, 0xa3fec3d2, 0x6a20b92a, 0x7236ba04, +0x9c4c34bf, 0x1030c29e, 0x5912de26, 0x5b6222d0, +0x5bab14ca, 0x07370bd8, 0x6a174bcf, 0x3cbaf971, +0x00a46a34, 0x389d8a73, 0x017ddb95, 0xbe337ea5, +0x09b9cf88, 0x6c19061e, 0x18549981, 0x1b0fa492, +0x28457200, 0x4c016d96, 0x693a7ba5, 0x4cb05f2a, +0x98805c2d, 0xaa6a296a, 0xfe0945ba, 0x265b175a, +0x0bc76958, 0xc8c0e8c4, 0x27b1896a, 0x75215016, +0x86547d19, 0x0d856f1f, 0xa43dd029, 0x2364860f, +0x5a882a3c, 0x96051552, 0x0427901c, 0xcf00f701, +0x07e51e1d, 0x88941880, 0x09f63081, 0x33a4e81e, +0xc22e2415, 0x90ec7735, 0x3b200833, 0x62ae4735, +0x06816d90, 0x395cd920, 0x88cd3bdb, 0xe09a79f3, +0xfd49c483, 0x3c45627e, 0x9d02ba61, 0xd42d8ff7, +0xc0771735, 0x9370922b, 0x7375ab0f, 0x67b0d86f, +0xff799787, 0x9390398f, 0x00201401, 0x39a8d78d, +0x12ca2024, 0x023db15d, 0x8d9224c3, 0x37a86bc2, +0x21146356, 0x78944c09, 0x6c774620, 0x0637db31, +0x5c0583b9, 0x7d118624, 0xa8ada428, 0x829060a9, +0x74b53898, 0x02dd3fd1, 0x5b18420c, 0x1901fe83, +0x8a145ac9, 0xf0fee4b6, 0xa9345363, 0x5418dbf8, +0x1f006803, 0xa0c144ef, 0xf9ba92b8, 0xe86bb747, +0x8907885f, 0x5da50c68, 0xb30034d4, 0xd4d001b6, +0x61341d2e, 0x3404c2d9, 0x3f0d2ba8, 0x2dba10a9, +0x44130633, 0xae0e3292, 0xf2e1e963, 0x7d484d08, +0x14f71899, 0x32b39250, 0x5ddb7120, 0xb83fd888, +0x0c2d522d, 0x05b95004, 0xf6724c51, 0x4b25afba, +0x0ad402ab, 0x14c73226, 0x3575b148, 0xd42fbb10, +0xce1b3708, 0xe530c00a, 0x2f5f3209, 0x320e006a, +0xb1ba05f6, 0x750941af, 0x1b07a16d, 0x421fbb0f, +0xd47a32b2, 0x12809512, 0x6828b87c, 0x230d0685, +0x46e88555, 0x87775f0f, 0x6ecf60d3, 0x7f3dbad4, +0x2f3d31cb, 0xbb820179, 0x29001e6e, 0xba1f5e1e, +0xf7491ea8, 0xbdd01d43, 0x116a124b, 0x83046132, +0x8998c887, 0x8096a038, 0x617e0582, 0x768da0d0, +0x8bfff0b5, 0x00dbeb56, 0x39d85fd0, 0xebb3bacb, +0x287e832e, 0xa307513e, 0x2b76ff2c, 0x261175b3, +0xff20b060, 0x11f75e8d, 0x04cc88c7, 0x90490e75, +0xa8d41212, 0xdb56b9c3, 0x850047f9, 0x7a17150a, +0xb901fdc8, 0xffff42bb, 0xd1098c45, 0x8a1f004c, +0x65c93113, 0x01a004c5, 0x2037a962, 0x7ea91b1a, +0x28467196, 0x115d8382, 0x81104088, 0xf0838c9a, +0x0ce4b9e0, 0xc7306ac0, 0x707fc638, 0x6e65e028, +0x0228ba14, 0x36cf63f8, 0xbe107ea8, 0x14488b3c, +0x8d563e2b, 0x2ca8be02, 0x8c688851, 0xa7578a9b, +0xadfd83e8, 0x0d14f816, 0x1043d31f, 0xf1431c2d, +0x378033ce, 0xfc88b8f0, 0xabffffaa, 0xa024f43f, +0xb63c8ac3, 0x34e82814, 0x63cc2a2f, 0x2ce82c07, +0x6c270029, 0x3d903651, 0x0751a054, 0x70b80612, +0xa847bf15, 0x0113bc3b, 0xa2c34110, 0xa80663c6, +0xc0019e25, 0x6c4ea585, 0x6069eb0b, 0xe3b80030, +0x34d57d77, 0x00c6e153, 0x503b7cb5, 0x71d111a0, +0x4f597501, 0x5dd0f044, 0x3b572258, 0xb2547736, +0xceb9b440, 0x3c074b41, 0xa0c71ae1, 0x7b408ba1, +0xb31148d7, 0x80c6ebae, 0x440e3288, 0x62553839, +0x1575d6ab, 0x78eb4e2d, 0x06a093a4, 0x41fca600, +0x8b8041df, 0x012a06cb, 0x40eb4dc3, 0xe78a2ddb, +0xeed1fe61, 0xa2b6af54, 0xe87eac22, 0xfaaa7f2e, +0xee81c6ef, 0x83597407, 0x504c383d, 0xa0a8eee8, +0x58083410, 0x04dab447, 0xdcc02a64, 0xb9002c43, +0x1aed8338, 0x5fd17d16, 0x36645aa3, 0xd374b9e8, +0xbbd542d8, 0x05c7ab14, 0x81f8dc40, 0xe85b2c78, +0xdb5fac95, 0x7114d889, 0x818382b2, 0x21a008ec, +0xce505a7a, 0xce227146, 0x7def04a8, 0xea7c40d1, +0x39bfd001, 0xed7f56cd, 0x5d52c155, 0x485d7a62, +0x6bc001f4, 0x44902125, 0x88b04004, 0x380b7f0a, +0x833359b4, 0x720451ae, 0x28013b21, 0x72ee7d10, +0x0e3e8b1d, 0xed04a10c, 0x7781370b, 0xef01000d, +0xd4abee29, 0xa570b414, 0xbdc1412f, 0x9140d761, +0xc4750939, 0x81b9fc0f, 0x0cb402c4, 0x06bf8ee8, +0xe828ce2d, 0x1f9f3458, 0x88da0a39, 0x0c43ec0a, +0x3016fa0a, 0xac3c8e9f, 0x9752ffff, 0x8da632fa, +0x75d43445, 0x3aed68d4, 0x8df10a8b, 0xdaca8118, +0x59bf466a, 0xc2e8b10a, 0x07182b1e, 0x05d512d8, +0xe01f7528, 0x708b1b53, 0x1dad2886, 0x81e283c2, +0x040ef3da, 0x8b81160d, 0xafe8c307, 0xe02c81e1, +0xf038ba2d, 0xc3fce0da, 0xb6ce86ea, 0x29bae11d, +0xae6a5709, 0xff0d75a8, 0x05ca83c2, 0x74d03966, +0x0c19caea, 0x73bbe655, 0x271b21ad, 0x0d08000d, +0x8c5d3c40, 0x1f6380b7, 0x16771177, 0x6940690b, +0x545bd518, 0x02b3ae1b, 0x1dfab9d8, 0x0164db9e, +0x13a0a30f, 0x571ce958, 0x2c1d42d5, 0xf2b88ddb, +0xe31c513a, 0x84e2ab42, 0x41b90ae3, 0x60d42daa, +0xd4308afe, 0x844ba652, 0x677e8a51, 0x7c325b52, +0xe77152c1, 0x0d1a7507, 0x4a8c0eb6, 0xb33b9db9, +0x88142662, 0x1a27fe60, 0x66b70e6d, 0xabb8200f, +0x748ec228, 0x17baf1b4, 0x4602af77, 0x18641d60, +0x3feeba8a, 0x870ffdf1, 0x5389714c, 0x10eac148, +0x08284a06, 0x4e79505a, 0xd9314463, 0x1120e7c9, +0x19493c69, 0xb54b01f4, 0xdcbe1b69, 0x0c1468f2, +0x7cc20a06, 0xb71beded, 0xed447bf3, 0xfce2831c, +0x14eb444e, 0x8a3a8d0d, 0xfe064096, 0xdbfe9b43, +0x04e2e222, 0x0204d974, 0xc77e24fe, 0x6b710057, +0x125fe40f, 0xd13c1fe0, 0x1071dd37, 0x6aeac168, +0xade85310, 0x116c102e, 0x204a159c, 0x456a0b84, +0x983d4cab, 0x39cd0051, 0xa9065702, 0x2b609414, +0xf88997e9, 0xc82a5682, 0x028df374, 0xf481ae40, +0x35b7ec4c, 0x6bd2a858, 0x43341eb8, 0x0ba6dba0, +0x640468db, 0x69055589, 0x0e820e34, 0x2bbec9ab, +0x14bf0f5a, 0x8d42eb5f, 0x0389fd14, 0x15bdb8cd, +0x11668536, 0x3b660686, 0x02217563, 0xd2aa9993, +0x40ac4a0f, 0x89eb60b5, 0x89023c73, 0xc80a6f4b, +0x4715a339, 0xc11d7e3b, 0x014e203b, 0x4773dee5, +0x0342a717, 0x0ddfec82, 0xaaad8e2c, 0x2fb00c53, +0x9b116bfe, 0x1df91466, 0x1e0e7608, 0x54800da0, +0x5d306ba9, 0xb84fa2ee, 0xdad90a49, 0xfbbf5bfc, +0x68e49ece, 0x4355f103, 0xf4b2eb13, 0x9390f014, +0x1c53d7e9, 0x9a5a5f40, 0x09c14c50, 0x002142c6, +0x5282bb43, 0x7b80d4a2, 0x51f208e3, 0xc7b8057f, +0x5be28fbf, 0x024b307e, 0x24be0ba2, 0x51c20e5c, +0x532504d0, 0x8f74446b, 0x8f83c017, 0x8bc561ad, +0xdd991060, 0xfd83566b, 0x24448b03, 0x7a400c28, +0x1d46611d, 0x8ed9d018, 0x61bd5e8b, 0xff50c663, +0xce0c46ab, 0x1c5885cf, 0xd0d72d83, 0x60ff77d2, +0x681ff41e, 0x0b044633, 0x010f0846, 0xe93981fb, +0x583e452a, 0xbea30489, 0x60044058, 0x58189ea5, +0x0504edd7, 0x170ea210, 0xba20b220, 0x043680b3, +0x8b98f663, 0xe5ef3042, 0xba0ab61c, 0x8b0673ec, +0x88d1c754, 0x8f938e5b, 0x83b61091, 0x144f56d2, +0xc289fcba, 0x249448bd, 0x385d40ea, 0x45e15aee, +0x4c4355dd, 0x7ecff631, 0xac6e01f7, 0x9dd99bbb, +0x211f74b5, 0x1c6a3311, 0x6ede4324, 0x87c1b518, +0xbcadd6ff, 0x4a0a5dbb, 0x89162a1b, 0x8a891a2c, +0x60807701, 0x4692c01e, 0xf6b6e897, 0x5fcbaf0f, +0x0c14c330, 0x4160fffe, 0x43ff8b0e, 0x83b58808, +0x83145350, 0x8fdce989, 0x72f76eb0, 0x66166228, +0xc46f1b37, 0x02c086fd, 0x468af825, 0x24401b2e, +0xc93b2888, 0xf84a0d94, 0x29c8182a, 0xfccb6f1b, +0x8018111a, 0x05b50237, 0xf723a5f4, 0x5813ffd1, +0x02cd2f08, 0x5b85880a, 0x14047881, 0xb2f06800, +0x3691d447, 0x6aa85f34, 0x78b5da04, 0xc5771302, +0x0bba0004, 0x4142060d, 0x5255d403, 0x1414b600, +0xb637825f, 0xe9c17b20, 0x585067b3, 0xe7113d45, +0x007f5d42, 0xdaa0c02a, 0xc68b5197, 0x292ab70f, +0xec1551a8, 0x510f1894, 0xedec1a05, 0x726a2977, +0xf045c106, 0x1918f097, 0x9aa28e04, 0x28c71638, +0x156ca3f8, 0x06c798e0, 0x137227f4, 0xee66d8f7, +0x8dcdf366, 0x85323540, 0x669a090a, 0x068a2bd0, +0x5c8a11fa, 0x7c808677, 0x6a85e802, 0xdb0ec111, +0x033410b8, 0x40198931, 0xcc036d3c, 0xe2a33008, +0x02482e16, 0x41b728e8, 0xdde0b65d, 0x3a2a3cab, +0x7d6625b2, 0xf64d123d, 0x66445fb2, 0x66c0b60f, +0x725b5a45, 0xb7e00040, 0x145b35cc, 0x30400850, +0x062b46a2, 0xd1a8b7a3, 0x0302c9d6, 0xf1135d4b, +0x4ec0c2aa, 0x660ba160, 0xc91153a2, 0x04061724, +0xe4607d81, 0x89736b4f, 0x457fe865, 0x06c93848, +0xe2540246, 0x7f0f8205, 0xe8fb50e8, 0x4f4380bc, +0x0ec62641, 0x9b0049c2, 0xb0d1a023, 0xaa0e6712, +0x0f0b8407, 0xd9200216, 0x14a0b8c9, 0x880092c9, +0x9f279771, 0xd86fc1e2, 0x81605e12, 0x8a41c227, +0xf4098fda, 0x464f9f27, 0x32028271, 0xdf411702, +0x828d06fd, 0x3d17fdfd, 0x207701fc, 0x38000c9c, +0xa87ea2aa, 0x962d1506, 0xdbbd102b, 0x2902962d, +0xca02bf32, 0x1b821c2c, 0xc5027301, 0x062e9ed1, +0xe8a7463c, 0x587b3282, 0xffbb65a2, 0x01b00063, +0xf9e811eb, 0x80fe1f08, 0x4158c46b, 0x58043c91, +0x12d47fec, 0xda79b905, 0xff6a14e8, 0x971415ff, +0x1244f24a, 0x9e69fdd3, 0x42f1d07e, 0x1cc5018d, +0x7c0f8683, 0x2bafb410, 0x840982e6, 0x218472d0, +0x3800d45c, 0x014500b7, 0x6f43e912, 0x85648fc6, +0x8d40d9f2, 0xdd8d7470, 0xf6fed456, 0xc6693499, +0x509eeb9a, 0x870000c0, 0x80a200b8, 0x146dfc05, +0x19b94414, 0x35a1fa89, 0xb4041de0, 0x68b514ff, +0x83aa24e7, 0x0b67d0d0, 0xa3a62575, 0x082edd88, +0x472b66f4, 0x6da25058, 0x3f04be85, 0x57f78e8c, +0x48c82ac2, 0x06be0576, 0x23190498, 0x06e80880, +0x01fbe30d, 0xa769202d, 0x2a079073, 0x08b9ae58, +0xf76edc19, 0x3aba66c8, 0xa1948ca3, 0xc02a04a0, +0x90a3df3d, 0x04949809, 0xdabbbbf1, 0x8b1686e9, +0x810ca80d, 0xf91ffff9, 0x9d0aa0e5, 0xa11d4caf, +0xab5f1f38, 0xebde1eb7, 0x003f0222, 0xb78a295d, +0xa6c2a1bb, 0x0fa4a104, 0xb605a340, 0x39999560, +0x68c86821, 0x57925a29, 0x9a9576b6, 0x7784c400, +0x07405420, 0xb48fe865, 0xe296887d, 0x9a0bd676, +0xc14d80bf, 0x7f3fc029, 0x6d820558, 0x890d78ae, +0xe86d8409, 0x09d9b7bd, 0x131fac88, 0xd404c0c2, +0x1ed0e0be, 0x814500bb, 0x289ff442, 0x80e4f378, +0x742f007d, 0x382a9407, 0xc00dc011, 0x2505762f, +0x4af9df86, 0xb405372a, 0x30870a12, 0xd3d76850, +0xff4302ab, 0x1e0aa19e, 0x062051b8, 0x20b6bfa6, +0xa57eaad0, 0x1fe878fe, 0x608a2bb8, 0xbe587055, +0x891d9a00, 0xc2cf7fc3, 0xcf1d7883, 0xd8c17325, +0xa9238b1d, 0x8d1c0a36, 0x6a4a1448, 0xc142a4a3, +0xbc950da2, 0xd3846fe2, 0x01e84987, 0xfa84c5a7, +0x2efc00c3, 0x7e9ca1c2, 0x23ebe42b, 0xd98a8689, +0x6ae3a283, 0x3e5989b5, 0x21a6d9fb, 0x08ebdb49, +0x5bddeb22, 0xec62745e, 0x6f020635, 0xd3957711, +0xd6a33643, 0x868b5f75, 0x05b30c19, 0x8210218c, +0x4416d822, 0x106d1847, 0x5b16c98b, 0xfb81efc2, +0xe5ed7493, 0xc030749f, 0xc182d09e, 0x8095fef3, +0x90022bb5, 0x37fed441, 0x82a47447, 0x66c0e8c1, +0x616f506b, 0x96770a4e, 0x840979a6, 0x9e60ae0b, +0xfde77575, 0xf2432525, 0xdbe84421, 0x1094a0a5, +0x271ddb84, 0x1f268c86, 0xd8380846, 0xe3eb1c09, +0x05e242d8, 0xd748833a, 0x0c722be0, 0xabed907d, +0x98dd0a48, 0x0d03098b, 0xf6223615, 0x9a05890b, +0x6080072d, 0x390f3fa9, 0xb02772d9, 0xe0205f31, +0x2c238f60, 0x0c578b18, 0x1b707154, 0x191029ad, +0x6e0b852d, 0x84658072, 0xb1bffa34, 0xd59fb506, +0x04a3539e, 0xdbf03d83, 0x2aad7aa0, 0x053907a3, +0xf6bc4306, 0x060c7341, 0xa0c146aa, 0xf9acbc0b, +0xb09a3d11, 0xd5082b7e, 0x27ff9090, 0xa30c788b, +0xa39af40c, 0x86d67d09, 0x26970a4b, 0xb3b716b7, +0x8b83200c, 0xe52f5a43, 0x0c532576, 0x75c6ebae, +0xeb037b13, 0x66782017, 0x0975d739, 0xd005982b, +0x41870192, 0x28150288, 0xf435e3a3, 0xec8f10d7, +0x6489e7c9, 0x292415c0, 0x89d37fa2, 0xd4302454, +0x015c0094, 0xdd33853c, 0xe14d072e, 0x0208660c, +0xe04efc5b, 0xebeea374, 0x080b05ca, 0x05b4f815, +0x340eee0d, 0xbd8c17e0, 0xa8c158eb, 0x40ebb17d, +0xa318909f, 0x103112aa, 0x5480e08d, 0x6437bbc5, +0x01d2d9e3, 0x3ffa7d3c, 0x82c4bd13, 0x9d0b120d, +0x4636dc07, 0x700b82ac, 0x034d30d2, 0xfdea7309, +0x333da385, 0x18f0b987, 0x2609ca1f, 0x315181d1, +0xb55b5a91, 0xa8b1ae86, 0x07642cbe, 0x45d516e3, +0xebd5f8f5, 0xb9420867, 0xbc414f74, 0xd3c6c5ed, +0x585a8b12, 0xeab9a504, 0x63f02315, 0x270ebc63, +0xb905cd13, 0xa006757c, 0xd8c8c822, 0x060c130a, +0x8bd0b235, 0xdb2cce0f, 0x0d1e8830, 0x53199826, +0xc4808900, 0x50ffc7bf, 0xdb0a5860, 0x4c518bc4, +0x65b65089, 0x0502b659, 0x0a580654, 0x02080e02, +0x0a38c8b6, 0x0458c51b, 0xb1504769, 0x582e158d, +0xb9035ef0, 0x5bfb1b0e, 0x07d15de9, 0xeb583d07, +0x66cac63d, 0xa2225042, 0x03b4188b, 0x001755a3, +0x2225fb38, 0x12b66688, 0xcb20e574, 0xa9050508, +0x06cb13a0, 0x77530c14, 0x0d61e881, 0xeb4946a7, +0x6b173c25, 0x3b034c17, 0x413b0b10, 0xc2dc2424, +0xff0607d4, 0x2c960001, 0x06c58602, 0x03335916, +0x9fd206c7, 0x09d02be8, 0x4a23aa09, 0x4140bcd9, +0x31638c2f, 0x13ffd7d2, 0x12cfc6e8, 0xb5106472, +0x7329bc48, 0xe9009ef6, 0x146aba0f, 0xe3285ec3, +0xd434d8d9, 0x002440ff, 0x812826d8, 0xea29aae8, +0x5fb151fb, 0xc1a186d2, 0x056a213c, 0xc725030b, +0xb07bc09d, 0x1460a16a, 0x050d8b4c, 0xf60b8e8a, +0x0026d543, 0x9060013c, 0x8dc4196a, 0x06f84956, +0xe743c75b, 0xffff60e8, 0x93d6aa38, 0x8bdbcf0e, +0xb951c14a, 0x51495e31, 0x281d6624, 0x51aad706, +0xdc0612d8, 0x0ee2bba5, 0x05011b70, 0x08126aa1, +0xdbfd6808, 0x833135a1, 0x8a8d24c6, 0xbd973b87, +0x371c0f31, 0x3cf087aa, 0x012e053b, 0x57a90030, +0x48ec7016, 0x323d8b99, 0xf1954cc7, 0xd0358bac, +0x205bdd51, 0xf10df468, 0xb9800e86, 0xb829e850, +0xab3e1283, 0x064f55cd, 0x7b8dd116, 0x1058801a, +0x143638aa, 0xa118120c, 0xec026441, 0xd28f1cc6, +0x1370f740, 0xc36a36a7, 0x3c458ad2, 0xf4327214, +0xd8835310, 0x4020a620, 0x89ef9e51, 0x0928c122, +0x2eed1e60, 0x148079e0, 0x6c01c7e3, 0x30401b09, +0xa2c85e80, 0xace0001c, 0x0ac724a6, 0xaaa908a9, +0xe1dbc300, 0x2a980502, 0x5f8fe8f8, 0x688ab6a5, +0x34b726d1, 0x4537eb53, 0x4408eed4, 0xd42812cc, +0x70048df4, 0x16f58344, 0xf99484e8, 0x04d8bb12, +0xe9c8043c, 0x0c70fc12, 0x38056efe, 0x4cb7a324, +0x0a247441, 0xfa00b176, 0x73d20c4b, 0x2b6af201, +0xe86fcf72, 0x2df26628, 0xa99d9b36, 0x02e82c81, +0x06e804e8, 0x80843607, 0xf5f82f02, 0x5e968396, +0xc5922445, 0xb8cfca72, 0x747857e1, 0x7a801373, +0x2c75c003, 0x088d0142, 0x26d02028, 0x06dcc52b, +0xe0a5e739, 0x4506b95c, 0x4689ca03, 0xbb94170c, +0x2b06eb06, 0x86a23325, 0x6530c682, 0xc4132e08, +0x9bfe2704, 0xc596aa46, 0xea039387, 0x752685e9, +0x4b17ca00, 0x8c6c19aa, 0x2011f827, 0x1701010c, +0xff0c03e9, 0x47c87da1, 0x64268842, 0x2a6e4ae4, +0x56044636, 0xc6730506, 0x15ed0e40, 0x0f03e801, +0x02c37ba9, 0x82e028ce, 0x87400158, 0x26d9fac3, +0x7521ebf9, 0xffff25da, 0x2006d218, 0x672612d6, +0xc5013d0c, 0x1dcd7a02, 0x81d520b9, 0x398f140b, +0xc08313ce, 0x5913293c, 0xa015b7be, 0x023217a3, +0x6105f60a, 0x1a8e9326, 0x01ba8a4d, 0x1aaf136b, +0x0341bee9, 0x78c908af, 0xff81d712, 0xbef567ea, +0x06bf8100, 0xf849e0aa, 0xf98b545d, 0xff5d58e8, +0x0d144eff, 0xe8814512, 0x48b844c8, 0xdb45587f, +0xe322c3ad, 0x0c45c945, 0xdfb72933, 0xebc1ecbd, +0x8b660b2a, 0x083d6623, 0x05207406, 0x2a223580, +0xa774a500, 0x047a2864, 0xba0b91b7, 0x516355b9, +0x0ceb4f97, 0x20d70503, 0xac52ef70, 0xef0374cd, +0x064bab0b, 0x4697084e, 0x289ab80c, 0xb2500370, +0x880e5688, 0x550b81ac, 0x91b2525e, 0x83e71295, +0x3dd74090, 0xc289826c, 0xcf0416ae, 0xa78b099b, +0x8256c176, 0xd31cc246, 0x6b54c985, 0xe310e073, +0x535cff13, 0x3f06d407, 0x09dad0c7, 0x690e8841, +0x5fc2c031, 0xd3d10843, 0x030c42f6, 0x26c41c42, +0xd4023ec1, 0xe71fd542, 0x04b56d18, 0xd2568250, +0x800d8246, 0xc8c9b357, 0x8515027f, 0xca31fb7a, +0xbb31d021, 0xc1a2a17d, 0x21d0315f, 0xd0890cc1, +0xfc456108, 0xf70d31ad, 0x31c809d1, 0x0725c0c2, +0x9d20d57d, 0xaa23fb8b, 0x57aa386d, 0x064fe7b3, +0x37055cd8, 0xc65f8d31, 0x57054758, 0x14d59ed0, +0xfab3711b, 0x4fdc0dff, 0xede01b0c, 0xfbc1eb89, +0x03e3c104, 0x2080b38d, 0x87007009, 0xa9d5949d, +0x1cbbd155, 0xcd1193ff, 0x45ba5f7b, 0xf1d5af70, +0xe25c0105, 0x0104030f, 0xadab60d4, 0x6b8b0164, +0x1970d6db, 0x19930dc9, 0x7b100d8d, 0xd37411c9, +0x45537cc0, 0x0a3e4c8b, 0xa2340032, 0x9beea027, +0x40fd837e, 0x1c720e74, 0x83b55d41, 0x8ceb5f76, +0x790701ec, 0x56c15601, 0x6e2a11b0, 0x4ca2ce03, +0x010c7d10, 0xcb78450a, 0x506cff01, 0xd71ee910, +0x6745233c, 0x89ba6f11, 0xefcdab47, 0xbadcfe17, +0xd001ed98, 0x547602be, 0x77061032, 0x10002106, +0x30f95483, 0xd2e826c4, 0x5457b740, 0xfebe3f2c, +0x5e476f16, 0x595dce29, 0xda11c801, 0x65bc4789, +0x1b7e35b8, 0x4e0b743b, 0x5f290d76, 0x30012d14, +0x18ea1b40, 0xebd88b1d, 0xa2c00c48, 0xd13ba15a, +0x3579c413, 0x121c8535, 0xd55155bc, 0x9240b919, +0x367440fd, 0x67582081, 0xf8ee8340, 0x02bbb043, +0x77503ffe, 0x5a6306df, 0x6c50ffff, 0xc3280ac0, +0x0416f893, 0x938d690b, 0x37b90614, 0x0bbe375d, +0x8d80027f, 0x79df017a, 0x07c10b16, 0x0882ba80, +0xfaf1d819, 0x89f06282, 0x0538b9f7, 0x51ae50cc, +0xeb2c00b5, 0xa0025f08, 0x1da6cc50, 0xaf1deac1, +0x2745a8be, 0x638e000b, 0xefe0f051, 0xcc94de89, +0x583140df, 0x075dfd01, 0x57602089, 0x82388bd8, +0xc5506585, 0x88015313, 0x896e27f6, 0xf1d529cd, +0x07f8229b, 0x150476b0, 0x7500243c, 0x49a01018, +0xd06f1fb9, 0x51c8807e, 0x398ba704, 0x541576c8, +0x0c5f3e04, 0xa6046a20, 0x29f047ba, 0x3a1705fd, +0x731936a0, 0xd5304b3e, 0x2170b08d, 0x087e4d82, +0x0474db03, 0xe937c102, 0x348d303c, 0x225ccc10, +0x4b23b178, 0x8a59280c, 0x7a153aad, 0x058f08f0, +0x310c241c, 0xff2dc4c9, 0x2c3617ff, 0xcad1ffa0, +0xed00174b, 0x860c8375, 0xc15e1a76, 0x775ef502, +0xbb022811, 0x0ae5d141, 0xc071249d, 0x136fb7c6, +0x1d7f5474, 0x57740406, 0xa3fa657f, 0x4eb451b8, +0x3beb9267, 0xffabfb74, 0x04667476, 0xeb337506, +0x43fa816e, 0x0f7f666e, 0x6afedb10, 0x39740b7b, +0x1ac9330e, 0x460926eb, 0x9e59e218, 0x48073eed, +0x0033442d, 0xb6a16b94, 0x236b0246, 0xff3ab05c, +0x49ffe6f1, 0xad150cdc, 0x0d51b231, 0xc1befe7a, +0xd223d449, 0x08733eeb, 0x369b15eb, 0x65eee101, +0x08edeb48, 0x28915139, 0x0473f0f7, 0xa9f23940, +0x174fa81a, 0xdfb2fd68, 0xa986f43d, 0xb6eabe60, +0x3538eb59, 0xcf016d40, 0xd4c9e414, 0x15d1156f, +0xfb832c01, 0xa61b7640, 0x3542373f, 0x112312b6, +0x7c889b68, 0xe8ddfc15, 0x5a430ea7, 0x750fc3f6, +0x38f82def, 0xc0ddf756, 0x8b2a1968, 0x4857581b, +0xaacc0b75, 0xee445105, 0x6b31684a, 0x1e4dbe4d, +0x521ae66c, 0xc355f7d1, 0xe6a05340, 0x608defc0, +0xff83cd7b, 0x3a8ff9d7, 0x9b331d44, 0x87db7425, +0x0235f631, 0x8d284360, 0x23ef055a, 0x06809ab1, +0x8005e80f, 0x05040810, 0xc3949f36, 0xf7eeb661, +0xa63eee6f, 0x7514c2a3, 0x68db30d8, 0xaef136a2, +0x438a2b7a, 0x0b044c00, 0x30aa23ad, 0x7c0e0b2d, +0xb511d885, 0x0d74c2e9, 0x40f9811c, 0x2000ed72, +0x3a5d8646, 0x6c4135d4, 0x34dbf794, 0x20c88d7f, +0x515236b6, 0x4f5c0f10, 0x90c1bf8a, 0xff535eae, +0xce680470, 0x86e0fb94, 0x606840b6, 0xc866bdb7, +0xcc0d6d80, 0x16d9ad15, 0xd21c13a6, 0x0cb810ef, +0x4c37c35b, 0xb44e82bf, 0xba281e0b, 0xa674d417, +0x278d0fb1, 0x4156ef0a, 0xc52dd703, 0x3c36ba15, +0xfe228ce0, 0x08eae250, 0x4ab8585f, 0xd20e283d, +0xf1a8f85f, 0x175c000c, 0xd46825e8, 0x09b021ae, +0x0efee81d, 0x35553bb1, 0x13234356, 0x21d8cf13, +0x231ddf41, 0xd882da2a, 0x5d073282, 0xbb8a8f50, +0x0a26e8cd, 0x6e54c234, 0x6a532447, 0x8f3cc823, +0xb4052a7d, 0x68180dad, 0xa1c4a664, 0xe8ccca9b, +0x458e6740, 0x07548d9c, 0xb9242291, 0xe118e82f, +0x8ae4ad83, 0xa86e8820, 0xe8f6eb46, 0x0c550c6f, +0x4c5b2c6b, 0xd464e13b, 0xb9db8163, 0x9c9c3108, +0x05f6c1a2, 0x2093c262, 0x0a9d5000, 0xa6e20e9d, +0x0ba987b7, 0xa20fd803, 0x2dd54785, 0x890a3312, +0x449f3616, 0x08805dd4, 0x4c3166c7, 0x22ff810f, +0x077fb81a, 0x01b00776, 0x2a585317, 0x693fa4fe, +0x1234dcc0, 0xaffe42f7, 0xd1849a00, 0x61e4c3b7, +0xe281c288, 0xab8055bf, 0xd088292b, 0xd2b261e6, +0xb0b8552d, 0x06d34005, 0xd444b042, 0x2204c7da, +0x6df0fa50, 0x68a8d8aa, 0x7a66e976, 0x682904c6, +0x828f057b, 0x828184b1, 0x7a68b7c9, 0xe07c4d18, +0x3c549200, 0xa22868db, 0x10c8b350, 0x3a22a65a, +0x740451db, 0xec4c0ddf, 0x3e095209, 0x388268d1, +0x83a24114, 0xff4d8927, 0x2ce5fbc4, 0x39ef0508, +0xf7ec185e, 0x3608e3d8, 0x329068d3, 0xc46b79e1, +0x2cc52305, 0xa7acf2cf, 0x3b47b842, 0xaa2c1462, +0x2e040623, 0xf8a91854, 0x059fba02, 0xe1f79816, +0x141df13e, 0x598921a1, 0xa109a40d, 0x730461a8, +0xfc0a1725, 0x8b3aeb21, 0xa76b8b3b, 0x42297723, +0x42411dad, 0x7aab0c53, 0x80ae028b, 0x44117725, +0x29b05689, 0x9f1dfdf8, 0x74bc0038, 0xba4c2bd1, +0x7dff10ea, 0x3c722ad0, 0x5439c810, 0x0addd62c, +0x688bbc75, 0xaa6d71a3, 0xe5027e2b, 0x39e9ce7f, +0xd1114616, 0x28182bfe, 0x89f8468b, 0x0e11aea0, +0x6217ba66, 0x36f4020f, 0x353bdc50, 0x22505a35, +0x0dad2866, 0x6cec0105, 0xc7084cac, 0x50370033, +0x8bd88809, 0x88021a9c, 0xf90b5106, 0x6d5cd014, +0x4f670114, 0x4cad230f, 0x29bb286d, 0x195133c5, +0x76b7f830, 0xf539d9cb, 0xef890773, 0xbf740d2e, +0x24043d83, 0x6d2d8994, 0x3b111aeb, 0x9eaeb3fb, +0x7a3b0c76, 0x8952ebd2, 0x6f288bf5, 0x4507b36b, +0x097f8bf8, 0x7ef6153f, 0x6409c2df, 0xe241f4eb, +0xfc419a9c, 0xe8231d98, 0xbc9aa009, 0x6ef6148b, +0x0170837d, 0xa9d139c1, 0x352f1589, 0x808bf636, +0xefa1caee, 0x1f4d1be8, 0xe8c8f821, 0x75000245, +0x55cd8303, 0x08228d81, 0x90acc4c1, 0x027741e5, +0x27fdf6d5, 0x889008ed, 0x7c1229a0, 0x7087222a, +0xa1195011, 0x21245b3b, 0x52b815de, 0x2c243e3e, +0x52a9b7a0, 0x173501c1, 0x1c0458d7, 0x8b66d1fb, +0xff296626, 0x2c0ec96a, 0x3b3dd241, 0x08b9cc47, +0x873b1b3e, 0xa82dc5d4, 0xc5a09cd8, 0xdf774a98, +0xe6df64ea, 0x0360e480, 0xaf03a864, 0xb5de283c, +0xd8391807, 0x688aeb72, 0xb34a31e8, 0xa0908d71, +0xb80981f7, 0xbd6a8351, 0xde8ac229, 0x5b435ead, +0x40291205, 0x7fa007a3, 0x853ddc54, 0x49085bc9, +0xf19ce0eb, 0x63d52116, 0xdeac3d80, 0x782254e6, +0x0c05d95f, 0xe00e88e6, 0xc99cb645, 0x7c38817a, +0x34a8a1d8, 0x097f4245, 0x86c40f11, 0x8b9be9ff, +0x3b40da03, 0x66ebdd56, 0x9868684c, 0x7c882211, +0xfa76924e, 0x81307487, 0xa8b1ce5f, 0xfbf8157b, +0x1425e8dd, 0x64e6d1b0, 0xdf081ce8, 0x7c4260e6, +0x08f7515d, 0x140a11ff, 0x1f253917, 0xbe84a821, +0xfe2592e4, 0x737ed679, 0xc883c608, 0x0692e602, +0x18fefb29, 0x03039ea2, 0xec0f9517, 0x4335154a, +0x4c8d4f68, 0x45a25b30, 0xdefe6740, 0xe2a2d868, +0xdff0395d, 0x4f1fb110, 0xc181b288, 0x14e181c5, +0x110c9188, 0xc7c3b6c8, 0x0d8321a8, 0x8289c50a, +0xbbe210c2, 0x82c745a8, 0xb70003ac, 0x4750f8e7, +0x03b83b80, 0x509eebe1, 0x612b38b5, 0x8375278a, +0xc2ce2f00, 0xc85c0000, 0x2a30eb20, 0x3dc1b878, +0xb87f10b8, 0xe088eae8, 0x56fffdd0, 0x1048d7b8, +0xc3ccb198, 0x08df7086, 0xe3eb84c4, 0x21cf6de9, +0xe94602fe, 0xe8c26605, 0x8046af84, 0x8b92901d, +0xda8b281d, 0xb5661362, 0x80077510, 0xa640176e, +0x0ad39278, 0xc12df907, 0xd039680b, 0x11c15673, +0xb0b130f7, 0x5072060d, 0x9d2d878b, 0xcecfaf68, +0x841efb7e, 0x5093d436, 0xe801b99b, 0x159b6803, +0xf21f0274, 0xd674749f, 0x2ff155b4, 0x7501e5f5, +0xd8096620, 0x8801f944, 0x60dd6b6d, 0xcdc602d1, +0x4506837b, 0x3ffc4d7a, 0xc0d81600, 0x64d85100, +0xe4c15188, 0x3aa12deb, 0x8c355764, 0xaff66b82, +0xc8773350, 0xd0d26684, 0x12704228, 0x003e0a1d, +0x438b103f, 0xbff63128, 0x430d700a, 0xb3967544, +0x017f0bdf, 0x4d4150ba, 0xa468f396, 0xa073fe65, +0xdfbe1645, 0x75153dde, 0x7955a57a, 0xada887c8, +0xd28337f1, 0x0ace3b11, 0x8b89d453, 0x2f0087a7, +0x0e48b5b4, 0x5113440c, 0x83aa9904, 0x82671f59, +0xc4150bc1, 0xfc06f50a, 0x84ea8e77, 0xd9990f81, +0x114b7f1b, 0x83887a87, 0x76011cb9, 0x051b8814, +0x410b4e6f, 0x1041b604, 0x2b1e0be3, 0x7414410b, +0x82480aee, 0x1cf10524, 0xc3c50724, 0x10bf21ee, +0xa4001406, 0xa0bde064, 0x14903556, 0xa5445261, +0x095b96ca, 0x575e9d40, 0x75723309, 0x79a96883, +0xf801abc3, 0xc0310da6, 0x9785e9e1, 0x83ffffff, +0x140625e2, 0x5a8be005, 0xc036d0bc, 0xc5b1f741, +0xaa5c0829, 0x8d6b676f, 0x36a2e87d, 0x04422a0e, +0x895df837, 0x4df7e465, 0x21aa86e9, 0xfc25fd15, +0x25000001, 0xfd8c4d8b, 0x240f6df6, 0xe381b935, +0x06fc2b04, 0x14b6bed5, 0x1775ff06, 0x41d4e258, +0x6a0d6a51, 0x2b712a28, 0xcd085d9f, 0xd00a889b, +0x21711e79, 0xda8a324d, 0x598b6747, 0x4480807c, +0x27909071, 0xb1d370d1, 0x00b0b9c0, 0xb403c4ba, +0x6b267818, 0x00ba69f8, 0x1913b4b9, 0x10b004a6, +0x3c79c88b, 0x7e1f22c8, 0xccaa681b, 0xc23f7231, +0x22e40141, 0x59622c00, 0x147fea2a, 0x4270a17b, +0xb28d1b14, 0xc428eb54, 0xa9152be0, 0x7c626671, +0x02a20046, 0x21753be8, 0x28897c6c, 0x75f0b1ad, +0x010f8b19, 0x00a0bdda, 0xff5c6105, 0x45e25d0d, +0x770bbc70, 0x8011f57b, 0xffee4021, 0xd23722c4, +0x666b8b66, 0x5b4479e7, 0x0f5c6880, 0xa30e0622, +0x60b22a5e, 0xd0009d04, 0x0c25be8b, 0x8b113a37, +0x5005e20d, 0x1988d659, 0xc28997bb, 0xe13cc996, +0x81e9bfa7, 0x428ac814, 0x16818816, 0xf0056d04, +0x5e250f03, 0x104ab528, 0x89110e56, 0xbc55e350, +0x2851885e, 0x86e92c50, 0x4f170a54, 0xde4a5833, +0xbffc4149, 0x42020c6c, 0xf875da39, 0x5b094828, +0xa0e80d83, 0x4bce8522, 0x1548383a, 0x5ca2c2ee, +0xb5780a78, 0xecc9fad0, 0xf111db1a, 0xffdff3cf, +0x560aebce, 0x20a1e8e0, 0xa2a30867, 0xe0a6f65a, +0xa3dd62b5, 0x5fc3b8bc, 0x77f50bd2, 0xe88037ec, +0xffff99e6, 0x051d8b18, 0x32d1a4b9, 0x283fdff1, +0x69f9f799, 0xcb290ac8, 0xa14ed369, 0x36823511, +0x787fb342, 0x5f2de801, 0x0b802515, 0x6f0e828d, +0x7fde896c, 0x5904a13a, 0x229f5922, 0x1ad3ff53, +0x82d4a825, 0xbae062b1, 0x758a8dd4, 0xa08828ff, +0xdde045c7, 0x456d031e, 0xd726ef30, 0x16a004b8, +0xf045aa70, 0xbc1d110e, 0xc376b5dc, 0x91d1584b, +0x02147e39, 0xb04126cd, 0x97094891, 0x2217d413, +0x79e82479, 0xc2ea617f, 0x0c118004, 0x4cd8e1e9, +0x83a2291f, 0xca0e0c41, 0xed420e22, 0x128529c2, +0x050b0ac3, 0xb5720614, 0x05fba911, 0x89430261, +0x2ea00cf6, 0xad02b8d4, 0x6d971445, 0x8a70b443, +0x0842be45, 0xc4038940, 0x05010876, 0x0d108806, +0xb5149e82, 0xb4668a40, 0xe2f40e85, 0x00d00910, +0xee09c26e, 0x9813788d, 0x8a0a4188, 0x8a0143e6, +0xb6a54bba, 0x0c02066d, 0x6165050d, 0x0a19ca56, +0x41088d5c, 0x2256b782, 0xe03c16dd, 0xa0e708f0, +0x5d1f2976, 0xa31d1a2c, 0x02b9468a, 0x1344a488, +0xe1d00140, 0x0de2b7da, 0x8be8af37, 0x3445ec49, +0x8bf1a4e8, 0xc4548f55, 0x170a10b7, 0x1e70b950, +0x7bff12f3, 0xffac3e2b, 0xcc488d52, 0x518b1865, +0xcb27c247, 0x41425d82, 0x0bcb3041, 0xe3780213, +0xe983c3b3, 0xdf52fe74, 0x47541b6c, 0xc70c4f43, +0xf3c41c46, 0x51560680, 0xf00480cd, 0x6ce8875e, +0xa2d01989, 0x75aa1052, 0xc566107d, 0x817ee691, +0x715569a6, 0xe6df68a4, 0xbd34f6cb, 0x897c6375, +0x154a751d, 0x020f234c, 0x2c45cdee, 0x067f173e, +0x5445c71b, 0xdbc55581, 0x5006ebeb, 0xf0f7f530, +0xe9a0ac0b, 0x04438a4c, 0xb00437ae, 0x3c5805ad, +0xe23e7565, 0xe2841b9d, 0x940ab904, 0x1454833a, +0x0f800f45, 0xbe108afe, 0x5d60eeea, 0xe8211850, +0x45881f1a, 0xc52a8316, 0x0310382c, 0x62eb62a8, +0xb0fea488, 0x6a84bbe3, 0xc4e8bf5b, 0xba976888, +0xb1572803, 0xbc81158b, 0x836e7de2, 0xd0085509, +0xe004a30f, 0x8eb2c046, 0xa724536e, 0xfba8703a, +0x68aa3fff, 0xdb264102, 0x16db7d01, 0xe8e55cff, +0x30078791, 0xf7200c3d, 0xf174f559, 0x15e8101f, +0x5056026f, 0x8031163b, 0xae145d56, 0xf02109a8, +0x1e202ed5, 0x1086669c, 0x1185d14b, 0xfd821077, +0x6c401f88, 0x8058bb4a, 0x60a077e2, 0xf025920b, +0x4460e297, 0x00d2e9f5, 0xf6396664, 0x428dd11d, +0x4674bf85, 0x16433a04, 0x1f183c0b, 0x06fcb3c0, +0x20433bbb, 0x0449c685, 0x91870135, 0x06da2dc5, +0xc40666e0, 0x02c02453, 0xc19b70e0, 0xa3bb8650, +0x1a8b44e3, 0x06f657f1, 0xad882e04, 0x97d6401c, +0xb6d2c10a, 0x799527e4, 0x0591ff9e, 0x4fe82dfa, +0xc1fd896d, 0x034082e5, 0x8f284309, 0x58c6a22e, +0xff1fa542, 0xdb02c1aa, 0x2cbae983, 0x6859cd7c, +0x6386e147, 0x23031656, 0xeac4e8dc, 0xd36fbfc4, +0x312c6b01, 0x39014cd2, 0x6aa65111, 0x5a5b5ce8, +0x3b0685f8, 0x06d2443d, 0x9623a0a9, 0x8de0274b, +0x0b81bbb0, 0x13ff1f85, 0x32b5bb37, 0x54d18c98, +0x43dd4307, 0x5b790055, 0xe218c765, 0x7d1117eb, +0x70d862a2, 0xf4837b48, 0x7c5b8890, 0x04c23883, +0x41478858, 0xd839adc9, 0x645504bd, 0x303d0ca5, +0xdcf2750b, 0xa32b1155, 0xe9810419, 0x3a35f551, +0x8d050684, 0xb87dea80, 0xdba3c80b, 0x110f0510, +0x9d7e08b3, 0xd99372e9, 0x4d5c00ad, 0xd64ccde3, +0x5158dd0b, 0x31d6c13b, 0x65417037, 0x0304e20a, +0x91024933, 0x39d26228, 0xbc503c18, 0x29d2eac4, +0xf953c189, 0x053750fa, 0x45a3e887, 0xc08d140d, +0x8967d454, 0x0c98e944, 0x9a297159, 0xd7d7a39e, +0x5422e802, 0xfd3e665b, 0xfd10620d, 0x3906d72d, +0xba0776ce, 0x2e0a9814, 0xe0cbea8f, 0x4adae002, +0xd73b77db, 0x81ee0121, 0x610308b6, 0x3bb66240, +0x1ab2b703, 0xa7472762, 0x0dcfc289, 0x8903a289, +0xd2c537d0, 0xdcab4164, 0x049ad80c, 0xd491b16c, +0x15c9e480, 0xc058239b, 0xe006e4e4, 0x7e22b78d, +0x96d47dc5, 0x1ee07d0b, 0xe962c551, 0x7508742b, +0x567f70af, 0x35aaadb7, 0xb3e83b8b, 0x5feb4c0f, +0xd5ea7d89, 0x8df0f688, 0xca13e455, 0xc5bc1521, +0x3678262d, 0x408dd0c7, 0x181b6c19, 0x25d9994a, +0xbe4f6a78, 0xfa890d9d, 0x8b150046, 0x5b6df130, +0x81799609, 0x8168ff6f, 0x3bea02c3, 0x2deb14cd, +0xe823c053, 0x833fe9c9, 0xa804d59b, 0xe389c67e, +0x9d6a5251, 0x4d88f88d, 0xb7c59b2d, 0xd68d2717, +0x01e0aa04, 0x0c10a6c5, 0x0d5cba8a, 0xbd15bdc3, +0xf2d91496, 0xdd527541, 0x88d4e8cc, 0xff610d68, +0xc1938aa0, 0x565321eb, 0x44f66a5c, 0x3c26ead5, +0xe6d1445e, 0x7250502a, 0x936216f1, 0xbb4fc30e, +0x66278de0, 0xe8be5037, 0x271ccb19, 0xaba25a08, +0xb5c3eaba, 0x44183478, 0xc6d48713, 0x1483042f, +0x0c46db0f, 0x58381cbd, 0xe9667d05, 0x31548ba9, +0x692cd056, 0xe8163c8f, 0x14b27c1b, 0xca831492, +0x551886e0, 0xf89540d4, 0xff6d1c2f, 0x7505ddb7, +0x0f100a0c, 0xeb30b014, 0x8834b002, 0x8b911917, +0x552c11c8, 0x03c2d544, 0x1c2cf00c, 0x93574445, +0x20bc570d, 0xe017143e, 0xfc234d8f, 0xd8fafcf4, +0x22007b5f, 0x14187294, 0x288bb924, 0xd7c00f12, +0xd7a1842b, 0x1f896d96, 0x84c87b43, 0x00f60e8e, +0x06c65b35, 0x8404ec11, 0xa1543e87, 0xbed836be, +0x44c73b84, 0x1803edb9, 0x46ab17f6, 0x1bc62d45, +0x1843c774, 0x889f1049, 0x8b2a75b1, 0x2d8ed066, +0x97560eeb, 0x7caa7500, 0x42cec8a3, 0x9dbc4618, +0x4064265e, 0x2420039a, 0x11875124, 0x3a206f01, +0xa819a003, 0x1a289a6e, 0x2c43181a, 0x78aebc2c, +0x171b8f55, 0xd8877bc4, 0x12847b17, 0x4d03ee31, +0x069555fc, 0xe26e64e8, 0x4623628c, 0xc8a3c940, +0x58b10c1c, 0x07c9e8f2, 0xb7f3b316, 0x061cb741, +0x5614249c, 0x4497775d, 0xdde04c89, 0xd548d339, +0x703d12f4, 0x0ff13d1c, 0x225ff789, 0xb8242c3e, +0xd586e831, 0x77eb394b, 0xea20f827, 0x1a1eebf1, +0x42f7e107, 0x0bebecd5, 0x18148d8b, 0x1c2362e8, +0xfd200176, 0x5134de72, 0xfb1a354b, 0x774e9c3b, +0x6f0ac665, 0x74dc82ae, 0x05890511, 0x002b7403, +0xe9c135f3, 0x2a0af047, 0xf0167426, 0x8ef5fdab, +0x046f19ca, 0x906e1023, 0x0ceb5851, 0x67322983, +0xbdd50025, 0x20ab0c54, 0x22a0cf1b, 0xb6f01049, +0x9e1e09f1, 0x888d2704, 0xd08205eb, 0x448045b0, +0x6e8809ce, 0xd5e1a8c3, 0xf303c7e7, 0x5a377beb, +0x0018b6b4, 0x779c2265, 0xa1713069, 0x779ff315, +0x7c986a62, 0x55f280b7, 0xc11b130c, 0x2816b700, +0xa9ff62c2, 0xa050a493, 0x582350c0, 0xb7a86675, +0x5b1aba09, 0x83781417, 0xbe334532, 0x2eea810c, +0xf0a8bef5, 0x72881ee2, 0x10d78112, 0x0a1022e5, +0x4566ac2a, 0xffd71d1f, 0xd412c56e, 0x2326550d, +0x0f868df9, 0x754aebd4, 0xa991c7f2, 0x456c9ca0, +0x29ebbef5, 0x15e181f9, 0x0cb04217, 0x723b6e2a, +0x000ee324, 0xa4e40182, 0x012e0d50, 0x67030125, +0x6b804736, 0xa07e28c7, 0x56214044, 0x12950214, +0xb47e202c, 0x019731ca, 0x2d04dbda, 0x4dce0fd5, +0x204d1675, 0x7c45d5d0, 0x6669205b, 0x704789f1, +0x0fe0b450, 0xb9c9e4bd, 0xeb600304, 0xe27cdfc5, +0x0e0067c8, 0x6c3c1985, 0x4af6e0cf, 0x64f4af1d, +0x24e971f9, 0xaf7ab9a0, 0x9dacf88d, 0x3e2cee44, +0xd61fb90e, 0x7451570e, 0x0ef12ff4, 0x5f4d535f, +0x13d13f75, 0x33685bb6, 0x2bc8b3da, 0xc2813ecc, +0x0181cb36, 0xdb8fbb5a, 0xc454bc51, 0x42ff4a86, +0x3a28da0a, 0xfd0f3f81, 0x925a8a5e, 0x8327e3ef, +0xa1103305, 0xf5457ee5, 0x88a23d05, 0x3446a5b8, +0x3066418a, 0x7b622cdf, 0x36aee9c5, 0xbb073726, +0x78a2de09, 0x09c4a324, 0x421fa32a, 0xecc88ae0, +0x8b5beb41, 0xbc2a1468, 0xd204250d, 0xde25ff0c, +0x8d011628, 0x77d3081c, 0xfe7a8d5d, 0xa6bad901, +0x1a8a750b, 0x7866011d, 0x0541f80e, 0x682ee225, +0xd401528d, 0x12fc5b0e, 0x393aeb64, 0x343a76fa, +0xa5770be1, 0x003ad328, 0x848e043b, 0x87c3b5e8, +0x0172cf73, 0x3e76e4e6, 0x00588510, 0x3b2f7304, +0xf0b18605, 0x8c727dfd, 0x10d0d2b6, 0x068740c9, +0xc9ca0288, 0x5a8a3401, 0x5140a501, 0x03135b84, +0x3580c315, 0xe9c4ae05, 0x6f00007b, 0x3b3f6a1f, +0x3c2d1260, 0x03005003, 0x21233c07, 0xf4a05f6d, +0x65787067, 0x8801642e, 0x105c090a, 0xd3300eed, +0x13001f03, 0x330e16d0, 0x1b9760d8, 0x071c0b19, +0x0dfea51f, 0x251f2258, 0x6020400b, 0x6ffe7b10, +0x747081ff, 0x05010602, 0xb8000703, 0x21cd4cff, +0x464c457f, 0xb74d9601, 0x185d6b5d, 0x01be03a7, +0x9b2c0d5e, 0x931a69a6, 0x275f12fe, 0xd364eeba, +0x42624330, 0x7a03630b, 0x69a6d9ed, 0x60f7e6d4, +0x0361041b, 0x9aebafb5, 0x2c0b172f, 0x0188ff03, +0xc8bfffdc, 0x010202be, 0x78000001, 0x56d76aa4, +0xdbe8c7b7, 0xf8242070, 0xeeffffff, 0xafc1bdce, +0x2af57c0f, 0x134787c6, 0x01a83046, 0xd8fd4695, +0xaf698098, 0xb18b44f7, 0x2b712fff, 0xbed7befa, +0x6b901122, 0xfd987193, 0xff79438e, 0xa6ffffff, +0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, +0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xffa1e681, +0xd8ffffff, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, +0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, +0xff6f02d9, 0x67feb7ff, 0x8d2a4c8a, 0x81113942, +0x228771f6, 0x0c6d9d61, 0x44fde538, 0xa9a4beea, +0xffff8ecf, 0x604bdeff, 0x70f6bb4b, 0xc6bebfbc, +0xfa289b7e, 0x85eaa127, 0x05d4ef30, 0xfe22881d, +0xd4ffffff, 0xdb99e5d9, 0xa27cf8e6, 0xac56651f, +0x292244c4, 0x2aff97f4, 0x9423a743, 0x93a039ab, +0xffffc3fc, 0x653fadff, 0x8f0ccc92, 0xffeff47d, +0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xb0014314, +0xa3ffffff, 0x4e0811a1, 0xf7537e82, 0xbd3af235, +0x2ad7d2bb, 0xeb86d391, 0x16110c07, 0x207e4003, +0x0e09053f, 0x100b0414, 0x81a21a17, 0x0f0a06fc, +0x6c3eb415, 0xe805ec3e, 0x022e6425, 0x76504900, +0x63fffe34, 0x0770890b, 0x64646120, 0x73736572, +0x74656e00, 0x6b73616d, 0xcfbac7f6, 0x62757314, +0x4410200f, 0x75616665, 0xadf80c6c, 0x6167ff7b, +0x61776574, 0x3a462879, 0x54544855, 0xf7d82f50, +0x47b62837, 0x25205445, 0x12200173, 0xffff6bff, +0x0d302e31, 0x6573550a, 0x67412d72, 0x29746e65, +0x2e302f85, 0x6b332e39, 0x2bb7b9ad, 0x736f4818, +0x01092b12, 0x6ed76d42, 0x7468001f, 0x6f438b74, +0x4c2d2c29, 0xe25bee04, 0x687467ed, 0x00534e74, +0x20077310, 0x01aded49, 0x1b0276b4, 0x2f2f3a26, +0xbb7fed39, 0x636b736b, 0x742f636f, 0x6b6c6200, +0x657a6973, 0x6cd4b70e, 0x7403926b, 0xaf6df40a, +0x6d6fb9d6, 0xa8bc6369, 0x46540063, 0x156d3b62, +0xbdd6b05e, 0x6578be41, 0xd8b21b52, 0x495ec360, +0x97414400, 0x00fc2b7a, 0x2a726373, 0x6c747206, +0xbb39324f, 0x07678dfd, 0x64c73933, 0x33356566, +0x636d1e38, 0x86db6f16, 0x2d31317d, 0x65180931, +0xdf23613a, 0x0086dac5, 0xd772745a, 0x39362e0b, +0x7e787430, 0x390addbb, 0x00303208, 0x6c1e7876, +0x6465696c, 0xc7fb58f3, 0x776e661d, 0x33303633, +0x3038091e, 0xd8205c30, 0x3a6c636d, 0xb86f2d65, +0xc2ed6173, 0x3a4c2f0d, 0x0a39200a, 0x6531410a, +0x50b6946d, 0x746f0bf8, 0x6f62206f, 0x65319b6f, +0x0a15afc7, 0x157379ee, 0x1415ac53, 0x60bb6fed, +0x5b203032, 0x65706fc4, 0x044f0a5d, 0xd8b5adb7, +0x0e2c736e, 0x16205ede, 0xaeb4666e, 0x6769fb0f, +0x0d617275, 0x7a6c6f73, 0xb70c564e, 0x63016f63, +0x20362068, 0x0a224122, 0xb9b705a0, 0x20641217, +0x76566c6e, 0xdacd7c6d, 0xaa33685b, 0x471c5f73, +0x81ed6c6f, 0x1d3c20a4, 0x633a728e, 0xdb5b3e65, +0x43b06d82, 0x42613767, 0x77d97739, 0x726ff64f, +0x201f206b, 0x20497375, 0x50434844, 0x966cb67a, +0x7d601d6c, 0x60b761b2, 0x2520651f, 0x706cce7c, +0x0361367a, 0x5d7b5b36, 0xed002e0d, 0x0585c276, +0x7303250a, 0x4b6963db, 0xd95b9f64, 0x55926465, +0xd08c6e5d, 0x166ddb4a, 0x6c366944, 0xb4281705, +0x74e3b685, 0x9a6647b7, 0x74bb3ba7, 0x5a000c58, +0x066f6653, 0x21761363, 0x250716bc, 0xd0b846ba, +0x04860b1a, 0x7d8620f9, 0xb1bb46bd, 0x657661a9, +0x08292c2f, 0xb76c2edb, 0x73296d67, 0x714c310a, +0x8b702d74, 0x3c20023d, 0x6d61b814, 0xf63615b0, +0x65354576, 0x15ad3c3d, 0x6136ac36, 0xfa096672, +0x841ae64d, 0x11533673, 0x6e732013, 0x72eab640, +0x3929cf57, 0x023fded6, 0x7c6e2d5b, 0x44382d2d, +0x42b58d3e, 0x2ab0ce21, 0x59025b10, 0xd58b9212, +0x69058f55, 0x146d5732, 0x6e752812, 0xfdfb0c65, +0x65757169, 0x652e2029, 0xb76d2c64, 0x51b3196c, +0xa531196b, 0x828cbd80, 0xe11a3a6e, 0xec96d8ad, +0x1b686374, 0x53106753, 0xda39ab56, 0x1521646f, +0xef061572, 0x6bc68dbd, 0x1d6c7651, 0x78c50749, +0x73cf75ef, 0x3c077307, 0xe3b62c42, 0x4600323d, +0x07840542, 0x6eec580b, 0xa70e7e64, 0x36b45300, +0x0f7985e1, 0x753a7861, 0x35b40cd1, 0x72e07c2e, +0x73763ccd, 0x4f7b3ee7, 0xcec68486, 0xaa043dda, +0x3610a425, 0xd80f7e3d, 0xa7c1adb2, 0x69f53220, +0xb1fb646e, 0x4318602e, 0x6873083d, 0x3600776f, +0x1185a414, 0xe11649d7, 0x728b9dab, 0xbc204e46, +0x30b6b733, 0x43650ebe, 0x702d6312, 0x0b1dbdbf, +0x42426811, 0x6d116f6f, 0x4e415320, 0x7056b03b, +0x8acb3833, 0xb6041f62, 0x49842421, 0x1e0f136e, +0xf93c6c52, 0x9a003ef8, 0x0244beed, 0x422dc70c, +0xad6b4500, 0x0a037b96, 0x1e2f0605, 0x6d4d7c2f, +0x207d5168, 0x6ccf4300, 0xd876432d, 0x641b75b6, +0x648c63d2, 0x0b18001c, 0x583305d9, 0x5f74699e, +0x12c216ef, 0x4e146cad, 0x836d9120, 0x5cb0cc4b, +0x3bd15b1b, 0x6e5ec502, 0x300848bb, 0x4a32036d, +0x0e313b31, 0xdba506da, 0x03333b16, 0x474fd434, +0xb84a054b, 0x8ff9a984, 0x01c24679, 0x25240a89, +0x051b1073, 0xa490325a, 0x7661af70, 0x6f85f08a, +0x6157005b, 0x8ecd7769, 0xfeea76ec, 0x752d6b6e, +0x259c2070, 0x6f6e9a53, 0xd0adb514, 0x3198d75a, +0xd43b6402, 0x1cb494b6, 0x627a046b, 0x32362c1a, +0x7a163d92, 0x0ec43a22, 0xa77ac403, 0x49066c24, +0x971b6cd5, 0xf93cec1d, 0x6f4e1c20, 0x92c46f2e, +0x1e72b010, 0x2dc023d7, 0x65103409, 0x69767bd9, +0x883d2866, 0x61f665d6, 0x081c2402, 0xb7a529d8, +0x128d8920, 0xb3ae0385, 0x108b816e, 0x6492c2e4, +0xfb3a1600, 0x29e3b0d6, 0x174c5b91, 0x202c0b3a, +0xfd065854, 0x646ebf66, 0x52064505, 0x5d0c0558, +0xb850ad0a, 0x107a399d, 0x8e3c7962, 0xdfb9e134, +0x055d0f6d, 0x44414f4c, 0xce094445, 0x60610b8d, +0x5343537f, 0xf0622049, 0xd5ec5940, 0xb8e0d968, +0x7d28c727, 0x5263848d, 0x7838e069, 0x65d85b00, +0x67655226, 0x6b7da241, 0x617dbb68, 0x4f4942bf, +0xc2421e53, 0xc2d2d137, 0x32302325, 0xc3066b78, +0x1b1ec0b6, 0x1c567061, 0x50a390cf, 0xcd2e2e2a, +0x1631d223, 0x74ca635c, 0xd9fd876f, 0x1c87760b, +0x70726e55, 0xb4cc4f25, 0x3ef75ad4, 0x414b8e2d, +0x653b1658, 0x6a6e4d3b, 0x2f85d385, 0x7777671e, +0x705a4771, 0xa273746d, 0x5bb31a69, 0x5db2cb2c, +0x02415b03, 0x42464344, 0xbab6e42c, 0x378c7e33, +0x02767b01, 0x440b858d, 0x7211003d, 0x9bc75965, +0x00e7a1a9, 0x7a620f16, 0xee6a7549, 0x33ce85c2, +0x4d4f3332, 0xd0eb6d05, 0x63385eff, 0x420d7462, +0x00544f4f, 0x0a1fed4d, 0x9c102702, 0x7e8c9d3d, +0x30399ba1, 0x248a6c38, 0x007d007b, 0xb9b582e2, +0x513f9ed8, 0x6addf66e, 0x098515ad, 0x00e6bcd6, +0x1c6c1b4f, 0x96d74866, 0x36695865, 0xe2701849, +0x210a359e, 0x1216ec80, 0x6e1e6bd6, 0x6da3966f, +0x675c3c87, 0x64967784, 0x221c4a31, 0xb73af600, +0x63d846c0, 0x6a2f3d5c, 0x84031d63, 0x0a4b6425, +0xc549be00, 0xf86d4aea, 0x68201593, 0x64d8bc2b, +0xce8cc220, 0x8dbb6f72, 0x46f59260, 0x5e776d62, +0xa75c1c65, 0xa0246a36, 0x0b5da96d, 0x53940b0f, +0x2e2e72b1, 0x433a6769, 0x8853620d, 0xf03a8f0d, +0x1e196e17, 0x0a766e37, 0x454d554e, 0x69e14952, +0xf5a2a143, 0x456c253f, 0x0f57ff25, 0x725b92c7, +0x1d801a1b, 0x852b9adb, 0x0538b8c5, 0xb9ad3631, +0x9d068866, 0x320b265c, 0x0ad4f57d, 0x00e52a51, +0x20c12033, 0x8d0988cd, 0x335900fd, 0x6e1392de, +0xfa2f5346, 0xed9c6d0e, 0x72653e20, 0x73f85035, +0x736d681e, 0x79705106, 0x8c507506, 0x08c103ba, +0xd073cd3b, 0xb6cd0e13, 0xe75825a8, 0xad75e3ed, +0x4000ef6e, 0x0f230be5, 0xec2dac0f, 0x343052c2, +0xff03d804, 0x95031aad, 0x35343310, 0x39383736, +0x8d85ed41, 0xf34342d2, 0x4c163cad, 0x06063e4c, +0x41fbec23, 0x003e5052, 0x6f043a22, 0x875c3468, +0xfdc3be64, 0xdc021eae, 0xa84341a0, 0x2dcf4354, +0x63a3adca, 0x750744f1, 0x3c24c064, 0x2e6ed5fc, +0x39302d51, 0x609d2e8f, 0x3aff8dec, 0x4e4b4e55, +0xe64e574f, 0x04504148, 0xeb14b82c, 0x00511656, +0x72b42d49, 0x0689164e, 0x4b3d1b68, 0xba0e4154, +0x534d3b71, 0x7954296c, 0x164e3d10, 0x4c45bb6e, +0x56754113, 0x3bc35a4d, 0x22b163ec, 0x3d415f4e, +0x164e0a35, 0x163ece59, 0x303d520a, 0x64491478, +0x0678db44, 0x64264843, 0x6dedb69b, 0x741bf8a2, +0x441c8b3d, 0xff746188, 0x9d10c259, 0x5432526c, +0x5265593d, 0xb77b634d, 0x63c1905a, 0x67972776, +0x63f631e8, 0x383d22c0, 0x1e323931, 0x1ff80c42, +0x147353b6, 0x31323632, 0x63253434, 0x707b58b4, +0x65f754e3, 0x137de332, 0x5b5a15ee, 0x15ed4e52, +0x21928640, 0xc56b4313, 0x9f6ee6fe, 0x9531767d, +0x8b554450, 0x88b0724f, 0x6cd1d4ac, 0x1f6e2d81, +0x1b740618, 0x73724527, 0x466fa372, 0xdadbc09e, +0x6c058779, 0x6900d857, 0x10e18082, 0xeca72d6c, +0xc919dcd8, 0x32729d13, 0x193965c1, 0xaf7a9473, +0xb0205210, 0x22d85c61, 0x4822c870, 0x10d9232e, +0xcd683105, 0xdd006138, 0x43da3e41, 0xc9404218, +0xc832adc5, 0x43076720, 0xd0a9524e, 0x432cae80, +0x90417bac, 0x6fbe39e0, 0x68647978, 0x9248047f, +0x55448b31, 0x8c9ea70b, 0x384e239b, 0x2ed24943, +0x62607825, 0x5848fc59, 0x7426a554, 0x543c1263, +0x7a2e7c3f, 0x37f07264, 0x64f49222, 0x410a0035, +0x0b567676, 0x70c49102, 0x8f5bbc2f, 0x0e00d1ed, +0xdd9d0a1b, 0xd700e83c, 0xa33e1960, 0x78672207, +0x66c9b18d, 0x0d68fe11, 0x508d5868, 0x580a6f6d, +0x4402b519, 0x7904198b, 0x810eeef6, 0x6c3e73cb, +0x5d1d000a, 0xde84ec5e, 0x42d7c150, 0x686a2951, +0x2e320458, 0x47476517, 0x0d231f60, 0x4a0d0020, +0xc4d83463, 0x78380528, 0x2b2033c5, 0x0c622fbc, +0x1c0e65ab, 0x88986806, 0x7a696d74, 0x8c3623c0, +0x927ab80c, 0x92c4748e, 0xa063dce8, 0xc1784313, +0x41ed1aa6, 0x69dc8cad, 0xe85a9964, 0x70100892, +0x60422fe3, 0x06cc1a10, 0x75074e5a, 0xe1707008, +0x12544d22, 0x62cd5a06, 0xf0554c2c, 0xd1065ba0, +0x185d741d, 0x5209284d, 0x266d70bb, 0x70435b18, +0xb6661fc3, 0x17d61bdb, 0x5c3d4c6e, 0x74237070, +0xc25f7ec2, 0xe6705fc6, 0x46ca7474, 0x04dad073, +0x9f5cb451, 0xe84005e8, 0x250e456f, 0x4a9647f6, +0x4146240c, 0x0a4c4154, 0xf72504ad, 0x1b746147, +0xb0ffc42d, 0xb217a581, 0x6e6644e8, 0x0f0b6075, +0x2ec6e24a, 0x6d9a0c4d, 0x64f263ad, 0x07509827, +0x4c73b62d, 0x0064ec91, 0xbc120653, 0x780b75ad, +0x05732962, 0xb0c173cb, 0xe20b022f, 0x0005e352, +0xb1906400, 0x0116e0af, 0x20f0e803, 0xf8190726, +0x34d31700, 0xb12906db, 0xdeb40396, 0x764ba2be, +0x179fbb4d, 0xed417b41, 0x6bddb106, 0xa00398bd, +0x030bf098, 0xdbd90764, 0x563b03ea, 0x3500000c, +0xc2dfb21c, 0x5f105a1e, 0x1f109d77, 0xdbe810a7, +0xefb90b5d, 0x1648c6ff, 0x605ec2f6, 0x5796b0b7, +0x61b7189d, 0x176c8e41, 0x669a0095, 0x046b1eeb, +0x730714df, 0xd913792d, 0x19346d97, 0x0005981f, +0x976cfd00, 0x031eb60a, 0x20c1ec6c, 0xab60c503, +0x36c6404c, 0x9ca9b6a1, 0x1f03c9ab, 0xfc000040, +0x8a45ddfb, 0x2910ec6f, 0x07050281, 0x04928139, +0xfcdf37cd, 0x869b8138, 0xa2130011, 0x12111113, +0xdd9612ac, 0x04b4361b, 0x1360152a, 0x403307be, +0xbbe6f9ca, 0x134027c7, 0x13d107d4, 0x59ddab06, +0xe8a11712, 0xb6d8f30a, 0xf20f14ea, 0xfcab0707, +0xb6c8f097, 0x257f8131, 0xa3687a06, 0x9f600b20, +0x0330f61f, 0x6e118451, 0xa34b0000, 0xbb7f55b2, +0x23d9e518, 0x020000b0, 0x31d20332, 0xba6d9176, +0xc8cd0722, 0x53073331, 0x50fb2830, 0x00011aa0, +0x76453700, 0x1b80a043, 0x03487f01, 0x65ba6a8e, +0x074a3ebc, 0x27636df4, 0x3fb01411, 0x3e64c19f, +0x1cb3aaeb, 0x6239b463, 0x0339081b, 0xc276eb9d, +0x0373e84b, 0x0793690c, 0x2f2773f4, 0xb8e768a4, +0x033ff067, 0xdf93b06e, 0x0473e92b, 0xd917f85b, +0x10d9b0ee, 0x2413031c, 0x2b785007, 0x7a67723b, +0x035e7a20, 0x14011c38, 0xdcfe677c, 0x7c770bc9, +0x10031b58, 0x7deb007f, 0x60bbc9b9, 0x8fb17f07, +0xbb095caf, 0x25272321, 0xb7c91f81, 0x9001b84a, +0x008388be, 0xdcf90708, 0xd4035fcc, 0x90971ca0, +0x015ff64c, 0x08040402, 0xb4830004, 0xeceeb175, +0xf42f1d0c, 0x5b00ff37, 0xdd74d3a0, 0x039fabd6, +0x03760770, 0xc9ec7d77, 0xf07e4db3, 0xa0c647a5, +0xcffba4a8, 0x54ee7c35, 0x9eff0347, 0xf6030760, +0x1ed9776c, 0x0b0973ab, 0xff2baa5a, 0x2391db03, +0x069845a0, 0x20573ee9, 0x280b28e3, 0x06690638, +0x30782c69, 0x04501d88, 0x24773469, 0x7414bb76, +0x81582a0b, 0xb3ed4bbf, 0xb6edcef3, 0x03be5417, +0xb0fd1b45, 0x22d7070b, 0x31d2980a, 0xc283072c, +0xab684db9, 0x4507f7ba, 0xaebaec61, 0x17a00778, +0xbd510780, 0xcc191e73, 0x1bc26878, 0x8dc2b05b, +0xdf48b066, 0x0c26c0da, 0x89431f65, 0xbb20f600, +0x03c0e9bd, 0x0f3fbfdc, 0x4f615fab, 0x1f2041ed, +0x1f3f6e21, 0x0c1b4013, 0x41c07839, 0x44bea49e, +0x0102e75e, 0x3aa6203c, 0x03777241, 0x3a2385de, +0x44050030, 0xffdb0a49, 0x311c2bbf, 0x03010d37, +0x0f0c0706, 0x423c2b11, 0xb6cbaf43, 0xb70ce99f, +0xca17cb8e, 0x1bfad003, 0xc0d97d03, 0x73009818, +0x8b218400, 0x2bd8b747, 0xfac05fd8, 0x105fab61, +0xe9000120, 0x0d247b22, 0x63dae91b, 0xe3a24117, +0xc7580ff9, 0xbb0761b6, 0x1be37eec, 0xe43c0ba8, +0x6375d73f, 0x00e27c5f, 0x05078597, 0xf6078e08, +0x0345d813, 0x3be29353, 0xdeadbeef, 0xe90667b3, +0xff16af51, 0xc3380320, 0xe0d44833, 0x7b035bf0, +0xf4507c25, 0xaf020120, 0xee7483b7, 0xf85c14d6, +0x42140b77, 0x75d74c8d, 0xa8d52c1b, 0x7588430b, +0x80b1011d, 0x0f95780b, 0x9d343534, 0x247cacdb, +0x979c07e3, 0xcd34ff07, 0xaf07d972, 0xb6266b06, +0x5b83be59, 0xc447d341, 0xb32b2203, 0x98320cd3, +0x12dbd407, 0x4da67659, 0x472957e2, 0xf228e207, +0xb759dd74, 0x0e7727a0, 0x4d0ffa07, 0xbaed0207, +0xa3a705b7, 0x37c4074f, 0xba611521, 0x07fb33ae, +0x077c0fc9, 0x4016ddf8, 0xec492d53, 0x89aa078f, +0x3f741d5d, 0x0f721360, 0x3a8fe6d2, 0x6c3d9ece, +0x4015fbe6, 0xe0fc8307, 0xe6ed9659, 0x1701c203, +0x12111502, 0x65965965, 0x19222318, 0xc20dd721, +0x33021816, 0x8a3c4be5, 0x5d2ee505, 0x0ba007ee, +0x6cf78300, 0x0f416ee7, 0x1c230714, 0x361d0f25, +0xab774307, 0x4928e002, 0x304e3b07, 0x5d59e697, +0x016c2d07, 0x3cc07b2e, 0x938302eb, 0xb9a13407, +0x0e6ec774, 0x5f15b93c, 0x3507c741, 0xb2e5df4c, +0x67e0aa6b, 0x044303f9, 0xd96e12bc, 0x16b6cb75, +0x035fdb45, 0x076713bd, 0x9ac189b7, 0x100bd1f5, +0xdb4bb450, 0x3b6f6374, 0x071bf42f, 0x2f001c32, +0x6d18f340, 0xd035d455, 0x99240b51, 0x899550d3, +0xfe4303cf, 0xdb73f788, 0xc34b78cf, 0x03a40749, +0x984e7f4f, 0x3b04682a, 0x83769903, 0x5c70ea1a, +0x4b7ea72a, 0x2e27fb4f, 0x0ea28dd7, 0xf744c417, +0x41d7689e, 0x0e06f4d7, 0xa3536b18, 0xe04a636c, +0x475d8eb9, 0xa7c24b08, 0x99639a60, 0xc27a990f, +0xdeb7e4cf, 0xa28847f6, 0xd75db517, 0x13f20fd5, +0xec8003ec, 0x970f18a0, 0x9510db71, 0x37031f76, +0xb797a71d, 0x098c2f67, 0x070a08b7, 0x6d2f0fba, +0x71d8b281, 0x871b0084, 0x49439f12, 0x81ae89cf, +0xd50f6089, 0x6e730e53, 0x4ea88dbb, 0x1b0bd201, +0x86b5078b, 0x067554b7, 0xd4dc0b67, 0xd7480b84, +0xd4e24d35, 0x0be823b8, 0x48360dcc, 0xe1c4ef33, +0xec178d23, 0xe70bd60b, 0x23ee0b23, 0xb6e9ae0b, +0x8aa1f5cd, 0xf90be307, 0xba460753, 0x0359baeb, +0x035ecf5b, 0xa301242c, 0x053dd66b, 0x0f730beb, +0x840b8cc5, 0x9eed9a0f, 0x0bfd4208, 0x050f0401, +0xfc0a3aeb, 0x0f5f0b53, 0x3ee75d35, 0xbb0dd3d7, +0x080f3803, 0xd7006bfe, 0xc3076bd0, 0x16be0f14, +0x14f0450b, 0xef0f11ac, 0x0668eb1f, 0x110fe973, +0x0f3d73ed, 0x06e8eb07, 0x1b213fbe, 0xafbf0f47, +0x0ced9ddb, 0x10590b77, 0x040f015f, 0x49dd6711, +0x80039d54, 0xd03fc28e, 0xb3a77583, 0x43cb2f12, +0xc00ff60b, 0x75db39ae, 0x194f082f, 0x9cc10f13, +0xe138eb0b, 0xbf13a8d9, 0x661b0036, 0xdd0f8c2b, +0x10abaed9, 0x780b6b0b, 0xe7002f16, 0x86baeb85, +0x0f8d0b75, 0xa10b9a93, 0x26740d0f, 0x06b3070e, +0x61c4a009, 0x13860157, 0x03294fc7, 0x73062447, +0xbaa6071c, 0xd629fe0b, 0xfc18ec02, 0xdbeebd4f, +0x3a6bfbee, 0x03047761, 0xd2770356, 0x11d714e0, +0x0bb846ee, 0x1c6700a7, 0x00ac060b, 0xed726b23, +0x0f4f32dc, 0xdf8d6803, 0xea03d733, 0x58b73a3a, +0x8a3bebe9, 0x3ba74ed4, 0x68774d35, 0x0797488b, +0x00181a4d, 0x43d5d75c, 0x072048d8, 0x00009b46, +0x00000000, 0xffff0900, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xe964aa55, 0x00540090, 0x00000000, 0x00000000, +0x00000000, 0x00800000, 0x0038001c, 0x52494350, +0x12298086, 0x001c0000, 0x00000203, 0x00010064, +0x00648000, 0x00000000, 0x506e5024, 0x00000201, +0x00008d00, 0x00580000, 0x0002006d, 0x0000f400, +0x02810000, 0x00000000, 0x70747468, 0x652f2f3a, +0x72656874, 0x746f6f62, 0x67726f2e, 0x58506700, +0x50280045, 0x78204943, 0x78783a78, 0x0029782e, +0x49444e55, 0x00003316, 0x03310201, 0x2cf82cf8, +0x435006d4, 0x1e605249, 0x0fa00f06, 0x1f0efca8, +0xfb89eb8e, 0x310214be, 0x0333e8ff, 0xbf0365e8, +0x5fe80077, 0x7106c603, 0x20b02000, 0xffe8ff31, +0xe8c88c02, 0x53660331, 0x57665266, 0xb101b8f9, +0x29721acd, 0x50fa8166, 0x75204943, 0x75e48420, +0x0233be1c, 0xf6e8ff31, 0xe8f88802, 0x2eb00319, +0x8802cde8, 0x0306e8d8, 0x7303ff80, 0xa90f0e03, +0x5a665f66, 0xc3f75b66, 0x2775000f, 0x3f812666, +0x506e5024, 0x38be1d75, 0xe8ff3102, 0xff2602c1, +0x681e1b77, 0x606a027f, 0x0d5fff26, 0x8508c483, +0xbe2474c0, 0xff310247, 0x3102a4e8, 0x66c08ec0, +0x6436ff26, 0x068f6600, 0xa80f02c3, 0x66028668, +0x64068f26, 0xbe08eb00, 0xff31023d, 0xbb0280e8, +0x0f43dfff, 0x8e008584, 0x812666c3, 0x2400003e, +0x754d4d50, 0x31d231ed, 0xb60f26f6, 0x2600050e, +0xe2c200ac, 0xbedb75fa, 0xff310242, 0x6a0250e8, +0xff6a6606, 0x00006866, 0x006a0002, 0x071eff26, +0x0cc48300, 0xff31d089, 0x89024ce8, 0x85027916, +0x663974d2, 0x8ec03160, 0x3e8b66c0, 0x31660277, +0xb60f66f6, 0x6600020e, 0xf309e1c1, 0x8966a467, +0x31027b3e, 0xb9f631db, 0x0e880004, 0xe1c10002, +0xc300ac09, 0x1e28fbe2, 0x61660006, 0xff3120b0, +0x8c01cde8, 0x01ffe8e8, 0x020eb60f, 0x09e1c100, +0xf631c08e, 0x2ef3ff31, 0x0fa90fa4, 0x611f07a1, +0xcb0020b8, 0x45585067, 0x74682820, 0x2f3a7074, +0x6874652f, 0x6f627265, 0x6f2e746f, 0x20296772, +0x2000202d, 0x00494350, 0x506e5020, 0x42422000, +0x50200053, 0x20004d4d, 0x31544e49, 0x200d0039, +0x20202020, 0x20202020, 0x20202020, 0x20202020, +0x20202020, 0x20202020, 0x20202020, 0x20202020, +0x20202020, 0x00000a0a, 0x00000000, 0x00004000, +0x5ce80e00, 0x1f0ecb00, 0x3102c7be, 0x014fe8ff, +0xe8006dbe, 0xddbe0149, 0x0143e802, 0xe8df42bb, +0xbe9c00d2, 0xff31024e, 0x9d0134e8, 0xe80e0475, +0x2e66002f, 0x6602c3a1, 0x0574c085, 0xc32eff2e, +0x0018cd02, 0x50000000, 0x73736572, 0x74204220, +0x6f62206f, 0x6620746f, 0x206d6f72, 0x2e2e2e00, +0xbe1f0e00, 0xff31006d, 0xbe00f4e8, 0xeee8031c, +0x4c686600, 0x8c544552, 0x31e589d2, 0xbcd08ec0, +0x8b667c00, 0x66027736, 0x027b3e8b, 0xe8024fe8, +0xd38e0282, 0x502cf8bc, 0xcb030b68, 0x61747320, +0x6e697472, 0x78652067, 0x74756365, 0x0a6e6f69, +0x66566600, 0x53061e57, 0xe3891f0e, 0x127f8b36, +0x14478e36, 0x5d8b2657, 0x458b260c, 0x368b660e, +0x8b660277, 0xe8027b3e, 0x665f023a, 0x00581d68, +0x6a680e00, 0xa7685003, 0x5b5bcb02, 0x661f075b, +0xcb5e665f, 0x01b45051, 0x067416cd, 0x16cdc031, +0x48b9f4eb, 0x15784900, 0xb40015e8, 0x7416cd01, +0x38f820f4, 0x9c0674c3, 0x16cdc031, 0xc359589d, +0xa00f5066, 0x8e0040b8, 0xa16466e0, 0xfb9c006c, +0x64669df4, 0x006c063b, 0xa10ff474, 0x00c35866, +0x85555350, 0x880574ff, 0x0feb4705, 0xb40007bb, +0x750a3c0e, 0xb010cd04, 0x5d10cd0d, 0x50c3585b, +0x74c084ac, 0xffd8e805, 0xc358f6eb, 0x10c8c166, +0x660004e8, 0x8610c8c1, 0x0002e8c4, 0xc8c0c486, +0x0003e804, 0x5004c8c0, 0x0a3c0f24, 0xe82f691c, +0xc358ffae, 0xe8c48650, 0x3ab0ffe4, 0x88ffa1e8, +0x03e8c0e0, 0xb0ffd7e8, 0xff94e82e, 0x0724e088, +0x58ffd4e8, 0x9009ebc3, 0x90909090, 0x90909090, +0x0000001f, 0x00000000, 0x0000ffff, 0x00009b00, +0x0000ffff, 0x00009300, 0x0000ffff, 0x00cf9300, +0x00000000, 0x00000000, 0x81e58955, 0x830028ec, +0x9c66f0e4, 0xa00fa80f, 0x0e161e06, 0x51050868, +0x07165756, 0x8d0440be, 0xb9ffd8be, 0x2ef30028, +0x595e5fa4, 0xf886010f, 0x665066ff, 0xc0316657, +0xc166d08c, 0x0f6604e0, 0x6667fdb7, 0xd838848d, +0x66ffffff, 0xffda8689, 0xe0bfc88c, 0x005ae8ff, +0xe8bfd08c, 0x0052e8ff, 0x58665f66, 0x0ffa5066, +0xffd89601, 0x0cc0200f, 0xc0220f01, 0x0804e1ea, +0x0010b800, 0x18b8d08e, 0x8ed88e00, 0x8ee08ec0, +0xff5866e8, 0x8ed08cd0, 0x8ec08ed8, 0x0fe88ee0, +0xfe24c020, 0xcbc0220f, 0x0f071f17, 0x0fa90fa1, +0xfff89601, 0xec899d66, 0xc0c1c35d, 0x02438904, +0xf0026383, 0x80044388, 0xc30f0463, 0x67f35166, +0xc35966a4, 0x053db850, 0x58ff2de8, 0x665166c3, +0x00d2e857, 0x66d9f766, 0x3150d101, 0xaa67f3c0, +0xc6836658, 0xe683660f, 0x665f66f0, 0x40b8c359, +0x64e08e00, 0xc10013a1, 0xd02d06e0, 0x6e2d5002, +0xe8c15000, 0x13a36406, 0xc35b5800, 0x57665666, +0x66ffdbe8, 0xbf66f631, 0x00400000, 0x660005e8, +0xc35e665f, 0x061e6066, 0xf68566fc, 0xce8c0675, +0x04e6c166, 0xc0c68166, 0x66000006, 0xb70f6657, +0xe7c166f8, 0xd4b96604, 0x66000006, 0x73e8ca89, +0xb70f66ff, 0xe7c166fb, 0x1eb96604, 0x66000004, +0x002cf8ba, 0xff5ce800, 0xdb8e5f66, 0x2a34b966, +0xba660001, 0x00035588, 0xa3ff49e8, 0x1eff0392, +0x96a30390, 0xec686603, 0xff0000fe, 0x6603941e, +0x5057665a, 0xe8052cb8, 0x6658fe5e, 0x901eff5f, +0x661f0703, 0x5066c361, 0x51665366, 0x66fc5566, +0x4d66ed31, 0xa46707eb, 0x75db0166, 0x006ce803, +0x3166f472, 0xe84066c0, 0x1166005d, 0x0057e8c0, +0x8366f573, 0x127203e8, 0x08e0c166, 0x66068a67, +0xf0836646, 0x665174ff, 0x3166c589, 0x0037e8c9, +0xe8c91166, 0x11660031, 0x661175c9, 0x0027e841, +0xe8c91166, 0xf5730021, 0x41664166, 0x00fd8166, +0x66fffff3, 0x6601d183, 0x8d666756, 0xf3003d74, +0x66a42667, 0x6691eb5e, 0x0b75db01, 0x1e8b6667, +0xfcee8366, 0xc3db1166, 0x59665d66, 0x58665b66, +0x000000c3, 0x00000000, 0x00000000, 0x00000000, +0xffb06477, 0x900323e9, 0xfa10cdfb, 0x9c1604c3, +0xefff065a, 0x203c7fdb, 0x01b90772, 0x1909b400, +0x0eb4db31, 0x8eda8e1f, 0x8ee28ec2, 0xf63dbfea, +0x89d28eff, 0x006a50cc, 0x0e58c3cb, 0x31d21350, +0x68520ee4, 0xed310100, 0xc0b7b7ed, 0x31c9312b, +0xff31f60f, 0xfb20ed31, 0xff369090, 0x06b76fdb, +0x56f9071b, 0x0372fb13, 0x26662066, 0xfe1eb70f, +0xb7c3077d, 0x55ffffff, 0x5250e589, 0x66f9000c, +0x00539868, 0x19e80e00, 0x9c067002, 0x271eff2e, +0xff468f05, 0x06ffddff, 0x15ff7e80, 0x568a1274, +0x750808fc, 0x406a1e09, 0x75168a1f, 0xec891f00, +0xdb51cf5d, 0x756fdbf6, 0x009afbc1, 0xc483357c, +0x3236c304, 0xff664df0, 0x60ffffed, 0xa00f061e, +0xa166a80f, 0x256607e8, 0x00200cd7, 0x3609a366, +0x07f42689, 0xfe07c0bc, 0x0ffff77f, 0x07a10fa9, +0x6661661f, 0x118b369d, 0xeca03650, 0x04a22e07, +0x5800eb01, 0x0d8bffcd, 0x1024cdbb, 0x24449c66, +0x845b1633, 0x518d5a0d, 0x51fe15ac, 0xbb4f02b8, +0x08520003, 0xe7dd6c05, 0x5e620142, 0xcfcf0117, +0x9f3da10d, 0xde098c83, 0x5100fb5f, 0xb0adffb7, +0x0355503d, 0x89665366, 0x0058bbf8, 0x60bb1be8, +0x6dbdb86f, 0x89915905, 0xf704963e, 0xc88c60df, +0xfdb090a3, 0xc10a6fdb, 0x78bb04e0, 0x67003f19, +0x381c8d66, 0x19641e1e, 0x1b2faffb, 0x37a32ed8, +0x22801c06, 0x9baddb6b, 0x0352d29e, 0x50040681, +0xb7fb196d, 0x4b196c7f, 0xcb58665b, 0x2e024789, +0x478810c8, 0xcf678804, 0x76ede15c, 0x2eadc0c1, +0xd88e41a1, 0xe1703666, 0x04d9dbdb, 0xc1833e9a, +0x8ced5e10, 0xdddc66d5, 0xd46dbdd6, 0x5b61e84f, +0x262b1434, 0xd77fe3fa, 0x1601156e, 0x881e0557, +0xc0200f00, 0x220f010c, 0x6fcdec07, 0x6614ea7b, +0x240f0869, 0x8e2efffe, 0x63875a00, 0xc08effb7, +0xe88ee08e, 0xd4894b8e, 0x47c36631, 0x0a17f8d6, +0x1e064743, 0xec830e16, 0x6e173810, 0x4eedfdc1, +0x00460308, 0x48b966fc, 0x4b9124af, 0xefb785c2, +0x19ff64e9, 0x5e015256, 0x1f142d08, 0x856f8570, +0x6e72a1c2, 0x24648b88, 0xae919fec, 0x66cbfb7b, +0x2b92068f, 0x0516ff61, 0x1c203a60, 0xbd9f6e1f, +0xd008ff29, 0xff926877, 0xff6e7bde, 0x66582ddf, +0x45524c3d, 0xcb017554, 0x811a18cd, 0x2f247758, +0x844336ec, 0xe4e55446, 0x065417eb, 0x6f7dbe57, +0x8b6753ff, 0x8e12245c, 0x247c06c3, 0x260e0b10, +0x5f5bffcf, 0x17ffb7fe, 0x9ccb5c2a, 0x7556503d, +0xbb070e0d, 0x564eb8b7, 0x02caf89d, 0x7e959d00, +0x2edf37e9, 0xf4fb06cc, 0x1acdf914, 0x7f2a0273, +0x078d3db6, 0xee481608, 0xffb415c1, 0xe115cdc3, +0x0a7765db, 0x085d9c15, 0x03c2bb2e, 0x42bed08c, +0xbe78dfec, 0xc05d2e03, 0xfb575303, 0x340000cb, +0xb6db9b68, 0x8b188e12, 0x24265d14, 0x5b7fa5ed, +0x1102012d, 0x543b66d1, 0x3b707504, 0x768b7304, +0x8bb5adf7, 0x3b02030a, 0x05750c4c, 0x7176085c, +0xdad6edf6, 0x09030b8b, 0x192b2966, 0x816473d1, +0xee0ae705, 0x168d66db, 0x0bdedd05, 0xc1ededff, +0xd20a0ae3, 0x105fb866, 0x03b0be00, 0x83ffa9e8, +0x7ec16cc1, 0x9d0aebe1, 0x1c5e8961, 0x25296166, +0x95f23210, 0x8325fb82, 0x7fedff10, 0xade81033, +0xffd4e8ff, 0x743c003d, 0x39c3dbeb, 0x7503e01e, +0xf75b6314, 0xbe5657ff, 0x14b91ce2, 0x5ea4f300, +0xf743065f, 0xfd7619d0, 0x06edf346, 0xff0506c7, +0x753e83ff, 0xde2683a4, 0xb8cf001c, 0x740c8e59, +0x02753e0a, 0x6651fbf9, 0xb6dafed0, 0x111ebc52, +0x06d02ff9, 0x0a9ac672, 0x416abf8e, 0x0874534d, +0x51eb74f9, 0x1e9bb789, 0x5a661def, 0x80c35966, +0x05515756, 0x4d7489a1, 0x1efef67f, 0x647bbf07, +0x5e5f59a4, 0xe972ff8d, 0x6f6f85db, 0x555666c1, +0x855e5d56, 0xa0be31f6, 0xdd9b55c6, 0x72ffeeee, +0x8b26e041, 0x04550305, 0x4d085d04, 0xba79e50c, +0xfea2e8ed, 0x89158907, 0x5a378989, 0x83e86fdd, +0xfe8110c6, 0x17f804dc, 0x76dbeb89, 0xa256b7ef, +0x665df85b, 0xe8abad5e, 0x29194f9f, 0x71bbe5b1, +0x75ed7d83, 0x080c0710, 0x30b6da9d, 0xddeb58f6, +0x2a0cb2f8, 0x742b24d2, 0x352f1d7d, 0x20b816de, +0xd2ba98e8, 0xfec5b866, 0x1600accd, 0xe8e78907, +0x1e29ffb2, 0xb4bf7361, 0x4d47182d, 0x2e36fb1e, +0xffc5e81f, 0x13b0ff1f, 0x9c09591c, 0x91fe8514, +0x3be8da87, 0x7fbf657b, 0x1e9d1f05, 0x3d15fe16, +0xdedfdf69, 0x81be756d, 0x9dfa6afa, 0x013dabeb, +0x94b107e8, 0x80e78f6d, 0xb0c888fc, 0x83000037, +0x90c9fa6d, 0x45585021, 0x1680001a, 0x6b80ac35, +0x3d0303b6, 0x2cf400ff, 0x3d85e42d, 0xd4072cf8, +0x6e4bdb06, 0x395e0085, 0x3802012b, 0x8d86db26, +0x000003ec, 0x40470b4f, 0x00000049, 0x00048001, +0x0000ff00, 0x00000000, 0x00000000, 0x00000000, +0x9287ffb0, 0x58506700, 0x2e302045, 0x2b332e39, +0xffff6f0b, 0x7474689f, 0x2f2f3a70, 0x65687465, +0x6f6f6272, 0x726f2e74, 0x00022d67, 0xeed9c837, +0xffff3760, 0x07cf9f04, 0x8f20b093, 0x009b0f2c, +0x850fdd07, 0x028f007d, 0x42690000, 0x35d05446, +0xf641f601, 0x454600fe, 0x5359534e, 0x01012fa3, +0xbedd0712, 0x00503fb2, 0x011000a0, 0x4a01021f, +0x11000304, 0x008053d8, 0x4f660103, 0x2ca3ec88, +0x36010400, 0x1051c76f, 0x93b60096, 0x5204d84d, +0x070a05a7, 0x4b0ba6ff, 0x61450081, 0x1180002f, +0x02002ec8, 0x00000000, 0xff400000, 0x00000000, +0x9bffffff, 0x53565700, 0x347504a8, 0x19e01d8b, +0xeb830001, 0x2c738b2c, 0x538b18eb, 0xffd88940, +0xffed6fb7, 0x0f070452, 0x42890447, 0xf3891002, +0x832c768b, 0xde8d2cee, 0x7bf7fede, 0x2bff812c, +0x5e5bda75, 0xbb3dc35f, 0xeb0c2770, 0xfd8d8d3d, +0xa137dbf6, 0x2c708916, 0x46c72c43, 0xbe350c04, +0x05fefb3f, 0x8934438d, 0x12563802, 0x0b74c085, +0x6dff4659, 0xc3834ff7, 0xb4fb8144, 0x51bb7244, +0x8953c3c3, 0xe80d8bc3, 0x77edfffd, 0x3912eb36, +0x29750859, 0x023b0c41, 0xc0310475, 0x098b0feb, +0xfffff981, 0x7519ffee, 0x9fc5b8e6, 0x7d5bd2fa, +0x8b0cec83, 0xb60f0c58, 0x0fe68333, 0x8b02e6c1, +0xfb5bffed, 0x0c890c4b, 0x4cae0524, 0x44c60424, +0x8a000824, 0x0b88094b, 0xfffb7f77, 0x10408b09, +0x2966d829, 0x66c486f0, 0x0f0a1689, 0x0cb9c2b7, +0xe8e289fd, 0xdadf735b, 0x0e060a13, 0x0cc483c0, +0x04904e7d, 0xb5bbe464, 0x03224e24, 0x09060550, +0x7856fdbd, 0x0b68500c, 0x68ff6a7c, 0xe8062a42, +0xdf6e966d, 0xb837b7b6, 0xb1203909, 0xffc1e900, +0x67575500, 0x91fdc1ef, 0x5b2cc710, 0x040780c7, +0x742bfff6, 0xbd64ff1d, 0x1deb338b, 0xe808438b, +0xe0197e8d, 0x8bed083e, 0xd85f0913, 0x907820e8, +0xb0ac7ddb, 0xe40d3666, 0xf83d8bdb, 0x030fe907, +0x42ec3b7c, 0x545c5f42, 0xba0ccd8d, 0x382f2838, +0x315c3377, 0x17428374, 0xe5840f51, 0x7fedf7dd, +0xd686c2ce, 0x0410cac1, 0x0a78d285, 0xeb14ff7d, +0xf76ed9cd, 0x25d08938, 0x043dc007, 0xff177580, +0xdec39620, 0x11e281dd, 0x1cfa81e0, 0x3308ff17, +0xb7c37727, 0x48ba086a, 0x9eccc709, 0x04170ddc, +0x82f158ba, 0xddbbbb82, 0x6c8b3636, 0x1a748b2e, +0xe83b18b8, 0xee8f7759, 0xc3adf0bf, 0xb8077599, +0xebceacfa, 0xe8f8895c, 0x6f137d93, 0x7bb5a35b, +0x310d2b24, 0x1473f46b, 0xf7ef6846, 0xa11874f1, +0x051d2aec, 0x1cfa03c7, 0x5f632ed6, 0x17eb1810, +0x03580ba1, 0xbb0b10c7, 0x3f213f8d, 0xef83087f, +0x0bf08408, 0x1d84850f, 0xfee2db0d, 0x10bd318f, +0x80c35d8d, 0xf87e3637, 0xffc58928, 0xb00c708b, +0x310c9014, 0xe1edff77, 0x1d14b9db, 0xf3fcaff7, +0x4506c6aa, 0x454646c6, 0x1bbd6610, 0x452be3fe, +0x0246100c, 0xc940a166, 0x06a36640, 0x88dffb9b, +0x08463d12, 0x08428a40, 0xde094688, 0x1b18484b, +0x4e866c6f, 0x74fff9aa, 0x509d15b7, 0x1cf6f0b8, +0xc85b5a8b, 0x427c4233, 0x61427d10, 0x8368fdb7, +0xf48ffffb, 0x53128b08, 0x6ef6c6c2, 0x7c83dc3f, +0x84a43c24, 0x20917dd1, 0xa16deef2, 0x284cf5b5, +0x428b1554, 0x1b0d4d20, 0x1275c6fe, 0x8b124859, +0x428d1450, 0xeba1a8e8, 0xf85ee8d0, 0xc1cd8663, +0x81cdc2c9, 0xf0f034e1, 0xf8edd9b9, 0xcb2c75a2, +0x0d04010c, 0x8a5e0e00, 0x7edbae9a, 0x7fe08321, +0x0a0f0688, 0x23100722, 0x6e1afbbe, 0x9e22eb11, +0x04504f20, 0xf0ba5028, 0xf841bb26, 0x4409ece6, +0x919687e8, 0x0040964a, 0xed8dfc73, 0x288b1574, +0x8911fa40, 0xfcb3e8e8, 0x7da1149d, 0x07f89b84, +0xe8f02aba, 0x07060737, 0xac6c5b76, 0x44b9490a, +0xe12b40b6, 0xe16edc03, 0xbb6646f6, 0x0fd7d6ef, +0xf7b571f2, 0x0a172b52, 0x8d2e8bd9, 0xde890852, +0x6382859e, 0x6e2702e9, 0x60c22897, 0x4e342728, +0xc1955099, 0x4ca96df1, 0x13fdf18c, 0xdbe3860f, +0xa1f2dd01, 0xf0a01663, 0xd040f840, 0xebbe8312, +0x0fe2fc22, 0x00951c8d, 0x12bd2553, 0x870fcb39, +0xd9876eb5, 0xda8907ed, 0xf4069b9b, 0xf711a385, +0x6685854d, 0xc2b20255, 0x820fd839, 0x6ddf1092, +0x4d8b637f, 0x552bca58, 0x32d0390c, 0xc2290f82, +0x6b89d129, 0x136c7ba1, 0xc7aff4ba, 0xb904ffa6, +0x06fb5bf0, 0x02b05d03, 0x0646f766, 0xe084b83f, +0xe3ff1bad, 0x3beb540d, 0x3b66018b, 0x7b307572, +0xb7edb7db, 0x750c0704, 0xb3718b28, 0x2bfa547e, +0x43670c56, 0xf75b7fb7, 0x1fff2506, 0x0133c290, +0x67e8c890, 0x0de90674, 0x7fc2c3d9, 0x83304979, +0xc0b330e9, 0xda2abafc, 0x3fda16ed, 0xe620d616, +0xb861f285, 0xb6b5b638, 0x303d1b7b, 0x63b8c689, +0x30050688, 0x37864bbb, 0x1805dc34, 0x5ae670bd, +0xbc4466e1, 0x0552c283, 0xad4bbbca, 0x788bf0b4, +0x080f0d10, 0xdf854180, 0x3bb1b80b, 0x63277ec3, +0x06003218, 0xfbaf6d2c, 0x46ab94a7, 0x04e5e810, +0x0dc21582, 0xdf634b30, 0x3056557f, 0x0f0440c7, +0x2874d7a3, 0x369a9a8d, 0x30245cf7, 0xdf448db9, +0x30d1660a, 0x19a1b1ad, 0x34b3d6d8, 0x6862008d, +0x3cd8b066, 0x101c1e24, 0x5da07158, 0xa5142c68, +0x11d00956, 0xd0b75dec, 0xd9c71410, 0xebb26585, +0xa9538d2f, 0x9db3e476, 0x46a6048d, 0x6fd73ade, +0xae9b1b06, 0x0a0cb881, 0x31feb8cc, 0x48dbf6e3, +0xaa50121b, 0x40f60090, 0xff740138, 0xe0d817f8, +0x0c6a1c50, 0x83444842, 0xc13944c0, 0x1bf81060, +0x0aebc9ea, 0x418b118b, 0xfbc1c818, 0x2a12f620, +0x772e0d04, 0x42cddb73, 0x124b8326, 0x510e365b, +0x0dfe2210, 0xf8a1efb6, 0x181010e3, 0x05751037, +0x74145139, 0x0dadef06, 0xe82b4031, 0x34fd3d08, +0xb5a0d1e0, 0xc9ae2e3d, 0xdc1ba65b, 0x2939665a, +0xf2000975, 0x1b02c153, 0x830e04b4, 0xc3307510, +0x83fd8e15, 0xb422e8e6, 0x544cc25b, 0x70785f99, +0x0f12e877, 0x6d5843ff, 0xb90eebb3, 0x181f0a60, +0xf4f79fe3, 0x5ed2dd85, 0xbc4e6b44, 0xc3b5de04, +0x85ca0e74, 0xb6041bdb, 0xd392eedf, 0xff87c985, +0x03eb4c40, 0xfcea5004, 0x5b0fec42, 0x006ebde9, +0x3c50dc5e, 0x0b3c408d, 0x4c6efbaf, 0xc8160874, +0x33cc5b52, 0x0581edba, 0xd6606762, 0x8d214370, +0x026f0930, 0x0a5bec75, 0x6d043518, 0x1fd48eef, +0x5fe868cf, 0x38638306, 0x465722fe, 0xa7ed7360, +0x0423557b, 0x1697893e, 0x89e2df68, 0x3646f61a, +0xf7b4bf8b, 0xbc2b62d7, 0x1c4e240d, 0xc77539c2, +0x7d8df13a, 0xd1f9898e, 0xd7f84b49, 0x7032586d, +0xcbdf45ae, 0xa1fe5401, 0xe8dad0a1, 0xf8fb2077, +0x8ecdfaf7, 0xff76dd0d, 0x37c35056, 0x6df3d809, +0x35f72704, 0xf5ea406c, 0xc7c33e82, 0xf06846f2, +0xf2873e0a, 0x5cd81195, 0xf6c1d6fb, 0x8470e8f2, +0x5b7e362b, 0x5068508c, 0x97716696, 0xf7ec7ce4, +0xda329e0a, 0x5cec1de8, 0xe480d3e8, 0xc9769c21, +0x311a0853, 0x16e5775e, 0xdfe87b1b, 0x066feffe, +0x1d70eb16, 0x05d3d3dd, 0x3bf1548c, 0xf44a5ec1, +0x06ddf3b1, 0x8901c007, 0x8d400241, 0x5d894441, +0x48fb6d72, 0x0b5c518d, 0x52555268, 0x0eba7041, +0xcf05adfe, 0x42c71842, 0x5c20c01c, 0xe3101b49, +0x6eedfdf0, 0x894e818d, 0xc8058881, 0xbf8b64f5, +0x54157077, 0x01428d17, 0x685207a3, 0x37dc0794, +0xd308eef7, 0x8e66e850, 0xd231b168, 0xb4b34be8, +0xf0d070eb, 0xa1219c89, 0xbe5076a6, 0xfc8df423, +0x07a30800, 0xd0af8dc7, 0x96890df0, 0x8df26d02, +0x6f6c6cf4, 0x0d598bdb, 0x890a0251, 0x2713045a, +0x05893c54, 0x0829c022, 0x2f51ebfd, 0x57340f1b, +0x68779dd4, 0x3d74c647, 0xf20fe06f, 0x468b168b, +0x8b2f74f6, 0x06ef205f, 0x4450e089, 0x980c1428, +0xdc9633d0, 0xca2e1a95, 0x11966c5c, 0x34ff1bb1, +0x1207e124, 0xfd00e8ff, 0x753cffff, 0xeb5a58a4, +0xeec30d02, 0x73e87090, 0xd2000b9a, 0x00e9bb7f, +0xeb31f21a, 0x412b7649, 0x08413b14, 0xb78a3b72, +0xc75da07f, 0x41ff9311, 0x4661d118, 0xd4a11079, +0x07416dbe, 0x96801012, 0x01713198, 0x50dc5b1a, +0xc2970fae, 0xc570747a, 0x09d0a346, 0xf1635c39, +0xd7e8cc35, 0x7552f9cd, 0x09c02daf, 0xfdba4b46, +0x7b83c169, 0x1434744b, 0xb16ddd77, 0x5e1e14c2, +0x06c31811, 0xf06f8948, 0xeb07b737, 0xc1c2491e, +0xd02903ea, 0x01e9d131, 0xb633db16, 0x2a0814c8, +0x752c7883, 0xb637f722, 0x0b1a00e4, 0xa5e8051d, +0x6b786383, 0x3f4e3470, 0x0c1b098c, 0x37bad090, +0x53b53528, 0x07770660, 0xddec070d, 0x2066c41a, +0x5103734c, 0x7df74698, 0xe8d6897c, 0x6173c0a4, +0xfcd61190, 0xc1baead5, 0xd0b742f7, 0xc4fe1f8f, +0x7cba5831, 0x0577c1f6, 0xd2c20169, 0xe0a0ff7f, +0x8106764d, 0x394107ea, 0x28dd75d9, 0xbee4adbf, +0xd53cc02d, 0xe817b8b3, 0x963c34b6, 0x3a135073, +0xcf29c8bb, 0xd2d35e1b, 0x8a3bf508, 0x03104e0f, +0x1116ada5, 0x0c82edb5, 0xd0b81b10, 0x7222d4ba, +0x6a9dab49, 0xc49ffd2e, 0xf1da2649, 0xd23c4efa, +0xfb61b5cf, 0xbb14c5db, 0x3819eb22, 0xc0dd7532, +0x9d833c50, 0xf914c2d0, 0xdfec4304, 0xd20c3380, +0x447abb59, 0xa90702b5, 0x80d6d46f, 0x07743c9d, +0x111d28b7, 0x453f6b03, 0xec580240, 0x41257f1c, +0x36d59aba, 0x6b2681df, 0x113fe05a, 0xf39fc7b8, +0xe02aebe2, 0x5f57f202, 0x76e180ae, 0xbf19c620, +0x27055f40, 0xd23c6c2a, 0xe1cc80bb, 0x17abc783, +0x80c3a0f5, 0xfb319ddb, 0xb804bcd8, 0x38752e9e, +0x970ab95b, 0x0b062099, 0xfe0f2a9d, 0xd2fe80f6, +0x74ecede9, 0xac1f5607, 0x6301b908, 0x311ae92a, +0x0b717bd3, 0x2f6d0d44, 0x4b9a3508, 0x6c4ccd9a, +0xd5eedfff, 0xb99c9dbf, 0x49d62e05, 0xaeac0878, +0xa1840875, 0xd0aec1f5, 0x1904101d, 0x3dad19c0, +0xdb3b14ba, 0x5890d68a, 0x7b7d2d97, 0x97d7ab77, +0x3d7f7f19, 0x29746093, 0x5d06943d, 0x09fb86b5, +0x191cc83d, 0x62f349c8, 0xd2856637, 0x43e19339, +0x88cd8640, 0x0556dedd, 0xbe11c4b8, 0xe81dd159, +0x556e3c0a, 0x7e370340, 0xf4b60a5d, 0x3c17b636, +0xdd35be0c, 0xaf5be324, 0x0572d7db, 0x94082218, +0xe651e94a, 0x8cbe496e, 0xe9470810, 0xaad1123e, +0x3471321d, 0x2fd92550, 0xabdedede, 0x87187a83, +0x48febf0a, 0x20eae9e3, 0x87c20b65, 0x6c253350, +0xeefa1983, 0xcee275b9, 0xd5e8d0e9, 0x3e080f26, +0xaebc6ba9, 0x59ac4b0c, 0xb40863fb, 0xd1dbaf12, +0x8732e8a5, 0x5193588b, 0x8ded6b19, 0x23918e73, +0x39dba85e, 0x2320ccb7, 0x0ea72daa, 0x2c59585c, +0xa56eddf5, 0xb58de971, 0xd6c31110, 0xf4efc4d4, +0xcf14348b, 0x1b4743ca, 0x122a0047, 0x2f30a289, +0x42815b1c, 0x59323448, 0x76f16930, 0x4805d83f, +0x722e7535, 0x986f6a30, 0x442ff0bb, 0xe9891872, +0x204580ba, 0x1e82fee8, 0xdeafe3a3, 0xe81a180f, +0x095f67a9, 0xc09dcba5, 0xbdb1db82, 0xb6617a91, +0x2b40b060, 0xb9452a34, 0xc6e4a25d, 0xda1edcf7, +0xb689e9b1, 0x08cde609, 0x81e91807, 0x97c3f534, +0xfadabeda, 0xa24066dc, 0x18504a78, 0x0e8df0d9, +0x8d244d41, 0xbdf5f46b, 0xa6030d8a, 0x0c5c4b1b, +0x0da9671a, 0x33a38eb7, 0x171906eb, 0x6c85c603, +0x75ad4dfb, 0x0a6fbf05, 0x50560473, 0x1da56857, +0xaa5a3abd, 0x64891455, 0x5fb971c4, 0xc2882c73, +0xe9928ea1, 0x33457709, 0xcf08a330, 0x6e0772ff, +0x70d2b0e9, 0xcccf33d1, 0x31bfc278, 0xce6a72e1, +0xe8588d18, 0x6bebed31, 0xc6edff3a, 0x025f40d6, +0x0631725a, 0xeb5c7503, 0x3c4b0166, 0x1c3ae4a1, +0x636b0c02, 0xcb8c1277, 0x3cfb930f, 0x5e723963, +0x683703eb, 0xeb93a160, 0xe6447b06, 0x6feae6c8, +0x78c26795, 0x0d25013d, 0xa6c2f65b, 0x4beb74db, +0xffd83d40, 0x011a38d0, 0x542a1e46, 0x96b02c60, +0xf52899c1, 0x89757185, 0x7dc8ed85, 0xb3dbadda, +0x200a564a, 0xeba9f1eb, 0xc5ba03ed, 0x57a29b4e, +0x43c51166, 0x6bbc028a, 0x0431683b, 0x56a8c3c4, +0x0fc30207, 0xf76014be, 0x0254fd2f, 0xb9e5eb01, +0x68ba5a64, 0x6342eed1, 0x1fe80f6f, 0xff098675, +0xe2166005, 0x137a410b, 0xe8207f01, 0xb96bfa39, +0x14a178cb, 0x21dc67e9, 0x8144f8fa, 0xc7edcd52, +0x5c2f9fe9, 0x0e7e75ba, 0xee1cba05, 0xeb14c3f2, +0xc03601c2, 0x5f589c43, 0x855e58a3, 0x433dad9f, +0x354b4360, 0xc48b6053, 0x53fb61f4, 0xc183d1a5, +0x5b144504, 0xdfbc876c, 0x102a8770, 0x708d8f77, +0x24868bec, 0x2d48ab6a, 0x3cba0040, 0x1817520b, +0x107716db, 0x60464c02, 0xc6edb95c, 0x9e016ae1, +0x53f9996b, 0x02df3ee9, 0x1019d80d, 0x09b2da66, +0xa60a0506, 0x7675c1ad, 0xde46d14c, 0x0a177ecc, +0x7db22415, 0x50325de9, 0xdcd5220a, 0x0feb048f, +0xc2ad9848, 0xff380b42, 0xc32f8adf, 0x7fc2ea2d, +0x5131eae1, 0x17048a07, 0x47410188, 0xf629c32c, +0x9fd0bf42, 0x01ef7f18, 0xdee184fa, 0xdb908e89, +0xc730cdda, 0x02410421, 0x1074a105, 0x82b11b56, +0x300206ac, 0x5a0d077c, 0x4d00f7ec, 0xd836e05a, +0xd8d6f430, 0x0cc583c6, 0xe06ead2b, 0x5eef46ec, +0x4b04688d, 0x21f17f2a, 0x3e8dac06, 0xebd958c5, +0x216c515f, 0x6944adb0, 0x1bc247eb, 0x0d6f9803, +0x8ad8342e, 0xe89a880b, 0x850b0f3a, 0x1e75fd0a, +0x7c74c984, 0x3ada49b8, 0x8db81b12, 0xa3421507, +0xfb2ad8a2, 0xa5bfba66, 0x67d5ebec, 0x08504006, +0xbc5cd686, 0xd2697da9, 0xff0a02e9, 0x0ccd604c, +0xc29a7f00, 0x162b61ac, 0x0b1f0607, 0x436c2108, +0xeb23f083, 0x53c7650d, 0xf60c33eb, 0x811dd817, +0xba262438, 0xebc1ee27, 0x06feba2d, 0xf91bb280, +0xb826ebe3, 0xfda46128, 0x6bb1d7a3, 0xeb698d89, +0x080f73ac, 0x07e090bd, 0x8320ff6d, 0x6f9efdc3, +0x0d11b177, 0xba0a1e20, 0xe9b31ccb, 0x108604a8, +0xe1e91a77, 0xbf1435fd, 0xce9f1e06, 0x793d8366, +0xe975be17, 0xddc087f3, 0x10781703, 0x17670de8, +0xb8103cbe, 0x00d9ee2c, 0x781ffc10, 0xf88772d1, +0xbc511709, 0x0011b7b9, 0x0845135d, 0x3c105280, +0x377f1271, 0x40287d66, 0x2e34bef2, 0x016243ef, +0x7da058da, 0x36e96405, 0x636d926c, 0xf83785e1, +0x15801aeb, 0xad752e03, 0x04bcd7f8, 0xc8287d0b, +0xd6628848, 0x02880362, 0x28ec4729, 0x8a043166, +0x7d83d756, 0x847440c1, 0xd088d475, 0x9c63c624, +0x2602a3f0, 0x42468389, 0xe060401c, 0xd2e032e7, +0x952281ba, 0x35da3dc3, 0xc72b6822, 0x7021e2ee, +0x2bde47a8, 0x8430627d, 0x7011460f, 0x8937f000, +0x2b0bba0d, 0x7751ff3d, 0xe0ac0095, 0xa3bb7b84, +0xc30c1b40, 0x6d50a4a3, 0x5d35fa4e, 0x575c1d0b, +0x78ba04b9, 0xbfb75ac4, 0x39849ac7, 0xa13f7416, +0xed21e806, 0x42685065, 0x0cd1d01b, 0x0520b905, +0xe8ccf20c, 0x83abec70, 0x537d656a, 0x6256d8b7, +0xceedab43, 0x1b562d15, 0xe0f11b12, 0xe1226be9, +0x1123d65b, 0x65c25389, 0x7b01d456, 0x83128b63, +0xed1325fb, 0x1a285ae3, 0x992b9192, 0xff025930, +0xc7a8b385, 0x70503b55, 0x57893576, 0x20784670, +0x6b0f8b27, 0x70845d84, 0x0b76f4a1, 0x3077f70a, +0x6447a0ae, 0xdc03ab05, 0x545be913, 0xbb06aaa5, +0x4872a16d, 0x2d7440c6, 0x5d61f598, 0xd9370d88, +0x7d7e4706, 0xfeebb585, 0x46863c0b, 0x80205848, +0x01752f3b, 0x22c9d043, 0x7fead868, 0xf7aef2df, +0x6e8d49d1, 0x912a1418, 0x771236d1, 0x0fb60c70, +0x10e4dc84, 0x4e1b866a, 0x109402db, 0x6853bc0a, +0xbf6b3817, 0x478be97e, 0x52505c14, 0x388272e8, +0x211e0301, 0x82f816d6, 0x46f6141c, 0x2524f75c, +0xdbb01901, 0x9a35ffe1, 0x2e216807, 0xdd376d43, +0x29477796, 0x1d74041c, 0xab6c1018, 0x3722d3cb, +0x4552c257, 0x02bb2220, 0x5f109806, 0x5beb8d65, +0x7457ab76, 0x04f9012d, 0xfc06fce7, 0xabdeb63f, +0xed3c1b00, 0x836c7e8b, 0x04ba18c6, 0x4237806a, +0x90bc14ab, 0xa9ae1a23, 0x4eb8eb56, 0x0728b843, +0x07d81ed8, 0xc8426cf8, 0xb858dee1, 0x65b4bb08, +0xb5e205eb, 0x5fc584da, 0xe874420a, 0xfffff460, +0x2cd833c2, 0x30548edc, 0x08117d10, 0xc7e601b6, +0xe8ed1043, 0x68bc4b84, 0xb6ebb014, 0x7f812486, +0xa65fe5bb, 0x5aa0728b, 0x83f32910, 0xaaa601fb, +0xf461b0b9, 0x57c985c8, 0xa15101ec, 0x9fdb69d4, +0x1de10ad2, 0x911b9cc1, 0x75a2402e, 0x2a8d8a0c, +0x759e0f0a, 0xb90b129c, 0xdbb98276, 0x033d6f5a, +0x3d6606be, 0x0c76348e, 0xdbdd6dd8, 0x0f740609, +0x9c1c0305, 0xb49de90f, 0x621311b8, 0x101c8d7b, +0x6f8681fc, 0x7b8004d6, 0xda87f2ff, 0x57eb0258, +0x212b7133, 0x3b01eaeb, 0x23474afc, 0x1e686ec3, +0xd2a920bf, 0x8ee4782a, 0x75f0f241, 0x60e6d112, +0x1127e074, 0xc549e90e, 0xc9e577da, 0xf746dad9, +0x72690e5c, 0x6ae18ba3, 0x3455d70a, 0xc6313fce, +0x8f4635f7, 0x0112853d, 0xf6dc6784, 0x0b01b89c, +0x0d4f3ccb, 0xc829c35f, 0xb82c18c4, 0x9c8d0f18, +0x08c68041, 0xcc48b363, 0xfd36d855, 0x47625897, +0xc629def6, 0x6e77d639, 0xaf0f9f8b, 0xe4345634, +0xbb107175, 0x85a45ad7, 0x14909c30, 0x2d431e21, +0xd0e51568, 0xf76caa3c, 0x80df6841, 0x148d3f6f, +0xfca67933, 0xbaed8d8a, 0x8d2f75fe, 0x0c56645d, +0x9450f9e1, 0x511d4cd2, 0xdabd6878, 0x4e0d5979, +0xfe10d8bf, 0x8fbf6e32, 0x00ed16e7, 0x7ea0c7a5, +0xf84c0206, 0x2f5edc3e, 0x5420dd87, 0x6c9b7b60, +0x7603cc2f, 0xca40b82b, 0x2b0d7704, 0x78ee26b6, +0x0a01e085, 0xefdaf7c2, 0x76c346fd, 0x33e81db6, +0x2535f2a9, 0xf6061e56, 0x4c2c67ae, 0xc09c0e90, +0x6d00005d, 0x03a33830, 0x240c5db1, 0x90d8d823, +0xb0e9247b, 0x4883fd29, 0x476ee9ff, 0x5a603c63, +0x18700030, 0x7786c1b7, 0xf3f3c81b, 0xfe5c6383, +0xa13c5320, 0x33ea4c2d, 0x0a7c0ffb, 0x28552b27, +0x5a6e1081, 0xa31a300f, 0x38d37c82, 0xc0db5dcb, +0xff11ac51, 0x9b182044, 0x8a026d81, 0x7b8a4e4b, +0x82946d30, 0xe20c9e01, 0x740acc24, 0x648e7663, +0x2f27d656, 0x70168180, 0x8955db98, 0x0f8ce4e5, +0x89c6c352, 0xd717c065, 0x78dff037, 0x338dc889, +0x8313c083, 0xc429fce0, 0xe3830f61, 0xbb2eb3c2, +0x27d337f0, 0xbdd72cba, 0x0dd8ab73, 0x7d747c1e, +0x4527b7c6, 0xe9ff1ac8, 0x1845895c, 0xec556616, +0xe8c93140, 0xc6285b78, 0x11946063, 0x458be666, +0xd68178ec, 0x48753805, 0x804acdbe, 0x97076d7f, +0xde44d31a, 0x7de2cc45, 0x4adb2ecc, 0x58d0bac4, +0x0da52db8, 0x527b3bf0, 0xc82e5b0b, 0x45c06043, +0xfc360dce, 0x3cf06f82, 0xff1a020b, 0xedb80982, +0x9911b6de, 0xc18bbc02, 0xfaf4658d, 0x0d43c3c9, +0xf70f0d89, 0x02048bf0, 0xfe00dabd, 0xbf199ccd, +0x092d9a27, 0x9488d620, 0x595f84b0, 0x8812032d, +0x8ca0c1ed, 0xd7eeabc4, 0xf678c01b, 0xe0ff4777, +0x0847be03, 0x97561ac0, 0x8d147306, 0x4891ab43, +0xd8a99656, 0xacb89196, 0xf0130205, 0xc4d5bea9, +0x13ea3083, 0xc75ccd1c, 0xcda09083, 0x5160eb91, +0x2b4bea5d, 0x60aea713, 0xe2f8d53f, 0xabee4878, +0xd52cc5cd, 0x187474ac, 0xf72d8a17, 0x96fe31c5, +0x21d96e75, 0x61aa6068, 0xc653c2a7, 0x8fafe0c6, +0xe8bbc6ab, 0x0628fb01, 0xe02c63a0, 0xa5be1497, +0xdd575bf0, 0x6aadcfdd, 0x84a06808, 0xeb06dfb9, +0xec6494e8, 0x066ae26b, 0xd9fa45c2, 0x2d34020e, +0xffca39d4, 0x5b573362, 0xd01cb76f, 0xb0f68c76, +0xe0537408, 0x51ba397b, 0x6071952a, 0x2606ef40, +0x76fa36d8, 0x6b5cd444, 0x6e3f57c2, 0x2e97b0c0, +0x83e8f67e, 0xb82a8fe9, 0x0910efee, 0x9793a7b7, +0x4ed7bb67, 0xa15fdfec, 0xebcbfcfe, 0x4ab60606, +0x75098a6d, 0xdb7b15fd, 0xb8e508d1, 0x89b3b2cb, +0x58ab0fc2, 0xee49386c, 0xb012a2a3, 0x3c0f028a, +0x64bf642d, 0x54d804d8, 0xa25b20f8, 0x4b909066, +0x152d028d, 0x81c82c73, 0xfeed3718, 0xa566a5c7, +0x6907b9a4, 0xe8caf4ba, 0x7849713e, 0x088b7c6b, +0xd1fd7fff, 0x8034852a, 0x12230626, 0x595a51bf, +0xd90cfd6b, 0x37dec705, 0xbf63c6e8, 0xf04357da, +0xe97adf00, 0x2c5f6f96, 0xf04d2b31, 0x048df0d1, +0x1b3f1113, 0x0effba89, 0xd8290451, 0x76e0d179, +0x2b7cf5a1, 0xe8650475, 0xa425c2c7, 0x05ecfdd6, +0xbbbf362e, 0x4154573a, 0xc61e9cf4, 0xd5ff3304, +0x0c6f8b50, 0xaf11dd06, 0x8b17ebe8, 0xf0ae6806, +0x7242396d, 0xcadb3194, 0xa6753310, 0x365835bb, +0x8e8ae9eb, 0x86e13406, 0xd898f186, 0x50893bd8, +0xeebb1158, 0xe5039aa5, 0xe81cf0b9, 0x4ee904ec, +0x8a6aa60a, 0xc0649e08, 0xd0b76ff0, 0xb0efdfde, +0x8e340202, 0x150d8bac, 0xc1fa9bf5, 0x514300b8, +0xef663802, 0xed66ca89, 0xcd6d2034, 0xc30608f4, +0xa2e6d722, 0x40792b1f, 0x02b80ec3, 0x4c209b48, +0xb8c3eed8, 0x4647e8e0, 0x341203b8, 0x136ef40b, +0x358bed6d, 0xa8f8d3f9, 0x9b65be8a, 0x793278eb, +0xc65b0606, 0xd84ec729, 0xce830acd, 0x70f61301, +0x61d1ed4b, 0xe5f0bfb1, 0x3203e8c1, 0x4fc50901, +0x2174b379, 0xccd60816, 0x6d72b3d4, 0x6f6c535a, +0xef684c04, 0xfd711e04, 0xba6edcbf, 0x35e80be8, +0xc1d1e8f6, 0xe0c110e1, 0xdcb70d15, 0x083adc21, +0x1008c109, 0xbeefc7ed, 0x7ef9981c, 0x5610b8fb, +0x8bed1d7d, 0x892c768d, 0x0f75b24e, 0xddc08a4b, +0xe8bb706d, 0x591f7933, 0x2ca93d5e, 0xfbcb7410, +0x21b03377, 0xc15f24c3, 0xc2095de2, 0x2b17ca81, +0x04bb0ae4, 0x1c60d109, 0x9f6ec847, 0x1f78d2b1, +0xe2605a58, 0x2d6f1bff, 0xecd30ae7, 0x41271584, +0x7565f983, 0x420fecf2, 0xb6dff037, 0x41090ed8, +0x7310f981, 0xb551f27e, 0xdf300182, 0xde9041d9, +0xd536b6b9, 0x89b45f08, 0x3a44d1d3, 0xbf2b42ea, +0xcf6de9f8, 0x83b7f8d0, 0x72ad3016, 0x501c8b44, +0xeb438a14, 0x59a6eb58, 0x4c418804, 0xfa1bba9b, +0xddd75bde, 0xfce80603, 0xfe082572, 0xf6043d0f, +0xa7d0027b, 0xd3bd062d, 0x040e0deb, 0x807e8f4b, +0xbf40bd01, 0xb1d7dbd0, 0x258db705, 0x44f06555, +0xf8b2bce8, 0xc2c4bc1d, 0x057f0f4d, 0x010c5c94, +0x5f8143d7, 0xf719bed5, 0x7ceb393c, 0x706d2ed7, +0x52871bd4, 0x99c5d40a, 0x850bf9f7, 0x44752d2e, +0x3483d96f, 0xff8603b2, 0x883a1bed, 0x3c431e04, +0x66d77506, 0xbabaff81, 0xb42e3274, 0x29c785dd, +0xa8e70610, 0x31f33177, 0x43c685db, 0xd86bd532, +0x97b367b2, 0xe827f75e, 0x35dd6eb6, 0x568d5d17, +0xd68e1904, 0x35bbb9b1, 0xc9e2c127, 0x998ae20d, +0x8eefa5e8, 0xeca1e802, 0x05e83929, 0x46600758, +0xfb80c181, 0x985bf632, 0xd6bfb933, 0xf8334e8d, +0xdf3b1f0a, 0x2c6e32ee, 0x3310918d, 0xb41a194e, +0x10bb5417, 0x59a3062d, 0xbb976eb9, 0x0b128004, +0x07188023, 0x9bee00ff, 0x1c12d22e, 0x05f81e08, +0x07149089, 0xc280d7ec, 0x7844c381, 0x05c28104, +0x71360b76, 0x757b10fb, 0x3f1089b7, 0xed7fd830, +0x0812057b, 0x896094c0, 0x033f140d, 0xb92bd8d6, +0xcd01a02c, 0x7257d3fd, 0x28356359, 0xbf7da460, +0xe26433ae, 0xe06d0129, 0x7aa14d2a, 0xdafa3b7c, +0xa32f6005, 0x1ad243e4, 0x17fbb858, 0x048a08c1, +0xe8828810, 0xfa834211, 0x6d2dea90, 0x2d7db86c, +0x9fc686fa, 0xb716fe3f, 0x05741e0d, 0x27750a04, +0x1fe683fe, 0x7f5317ba, 0x83a8a4e8, 0xcb81c39c, +0x4785b822, 0x679d0d87, 0xd7dd7db5, 0x2061cc35, +0x6279dfc2, 0x18497b6d, 0x6640027b, 0x8b0076c7, +0xb6f6f690, 0x13646eee, 0x056905c6, 0x206c0688, +0x2cd2d906, 0x77806dcd, 0xcb0d7b48, 0x7ddd090e, +0xbd200506, 0x018ff710, 0xace8906e, 0x66c37376, +0x536ed3a1, 0xc066637c, 0x8d115ee8, 0x68044d93, +0xc239fe05, 0xfb89e672, 0xc0a2c9a2, 0xb9e2661c, +0x7fad0b3d, 0x448def98, 0xd09e6818, 0x9ee8ef21, +0xf0fdd7fa, 0xeb59cf07, 0x01c72e0f, 0x31b88b80, +0xa58a415b, 0x86582c8b, 0x801b1869, 0x636015c3, +0x35828bd0, 0x806d59dc, 0x33009783, 0x43710785, +0x8aa7d8fc, 0x03e1812e, 0xbd104b89, 0x1bb7225e, +0xe8be0e20, 0xc33a1e78, 0x3ff188e8, 0x104f2f69, +0xc9078958, 0x8dc07418, 0x085e7742, 0x03e07f18, +0x0600c069, 0x03e2c9a5, 0xf6a47689, 0x36b852c6, +0xef664b7b, 0x1e0457b8, 0xf249e930, 0x40bd287b, +0xc1538c01, 0x8f9702e9, 0xb936a5bd, 0xb287adf9, +0xa9e0d325, 0xf66250b0, 0x00001606, 0x4f5c80bc, +0xc0cf0670, 0x04b0531e, 0x245eee50, 0xba16b49d, +0x81180221, 0xe9142dc2, 0x0cfa1292, 0x087741f0, +0xfd51b90f, 0x3e0569fa, 0x706d5005, 0x0503e918, +0xceea5185, 0xb46d6e01, 0xa1fb6d92, 0x07722b01, +0xdf83101b, 0x30750206, 0x0adc1beb, 0x0c19db00, +0xffffaceb, 0x13c3e92e, 0x2090c0ae, 0x70d86225, +0x2ddb3563, 0xc8feea45, 0x7b55f5e3, 0x2201a9b4, +0x6a171a22, 0x06977700, 0x83d3528b, 0x6c408b0a, +0x085ef72b, 0xdc08138b, 0x520e1053, 0x4fd15a92, +0x4e400c61, 0x4b158c63, 0x35d5b3ae, 0xf8dbe95b, +0x22ec19db, 0x77022080, 0x5f1515f0, 0xfdaf6dd7, +0x061c548d, 0xf41f0f09, 0xb6440e05, 0x035a05d7, +0xf81dc96c, 0x34285da7, 0x1ddbe318, 0xe35527fc, +0x5db45ff0, 0x5de36374, 0xda72dbbe, 0x84bf81ee, +0x12826f0b, 0xc339458d, 0xe8edd4f9, 0x2e17697e, +0xf19a45c3, 0xd8f06408, 0x6ec9049a, 0x22ed7520, +0xa0516e27, 0x08903e87, 0x7e888047, 0x043c0769, +0xebc295c6, 0x0c51ffd2, 0x8011a9c3, 0x33c3f611, +0xfd3235c7, 0x81ff6d6c, 0x28e8b026, 0x747aefe3, +0xd59d86a1, 0xe0c81c48, 0x5c305bd5, 0xf35a952d, +0xee342bd2, 0x52e998b3, 0xb8323809, 0x043705ea, +0xb58b0e2d, 0xd4b732f2, 0x553d276d, 0x6ef06b0e, +0x00121504, 0xfd0b002c, 0x88da5101, 0xace35f7f, +0x09d86596, 0x00005066, 0x49527bf5, 0xa88bc613, +0xb83cba57, 0x609eeff4, 0x56bd7a90, 0x0e5a8d0c, +0x720c5e89, 0xe42c0c4a, 0x8b975c76, 0x53c9ce7d, +0xc050a253, 0x02132156, 0x4bf2190e, 0x99369e0a, +0x5ac1e139, 0x7f63535a, 0x89df0482, 0xc03d83ce, +0xfd58fe74, 0xdabf7d43, 0x34c6e9f5, 0xffe273e8, +0xc8f015ff, 0x15166d56, 0x26d820af, 0xdee08c73, +0x901c40b8, 0x203e061c, 0x9b9a2ec9, 0x06bf6fd1, +0xcbee30b9, 0x5db9bc79, 0x187359ed, 0x1928a30d, +0x38a2288f, 0x036f45b1, 0x28b8ea7b, 0x85b11876, +0xbea11db7, 0xebd359c5, 0x1c057a4a, 0x680b61af, +0x56020724, 0xee127b54, 0x240957ef, 0x15ff3da1, +0x0e1975e4, 0x388b5761, 0x72708e92, 0xcb84bc01, +0x19eb5f7e, 0xfb991c54, 0x24d2fd58, 0xf059223c, +0x32ff3148, 0x20081468, 0x41d30bbd, 0xe805854c, +0xa6e615f7, 0x58a382fb, 0x7e02f80f, 0xe0472209, +0xebd4b45d, 0x6abb3434, 0x5a8b03eb, 0xdf807816, +0xe804421d, 0x08c25d29, 0xa47881e8, 0x5c1ff46e, +0x17090ac2, 0xfb051ae8, 0x9a2e056d, 0x6ff35895, +0x8ff7cc4f, 0xffab604c, 0x17b96830, 0x5e2d11db, +0xab3870d5, 0xb0dddbf4, 0xb93cc02d, 0xc2190c18, +0x0d794546, 0xb701602d, 0xbdc16ca3, 0x8fc33cff, +0xbd0ebab9, 0xeb4941c2, 0x93348b49, 0x07df61ea, +0x8de07c61, 0xfc685614, 0xf96b8753, 0x6ab45bd8, +0x1566e892, 0x8ce81c8d, 0x1ddd21c0, 0x1368cc80, +0x30612509, 0x12889172, 0x02ddf193, 0x187694b1, +0xe0921ce4, 0x21807b1f, 0x78962b4e, 0xefff5717, +0x36ef9a41, 0x19473468, 0xa175ebbd, 0x162025ac, +0x75d839f0, 0x75fe112b, 0x3350b45d, 0x8424d714, +0x0b09ff05, 0xeb162888, 0x29df8941, 0x88348dc7, +0x86bbbb00, 0x1e8b3028, 0x08dfab62, 0x5bf20bac, +0xb8608550, 0x470cce6e, 0x0eadd354, 0x834f3883, +0xddd3cac6, 0xcc06821b, 0x9cba5052, 0x2921b833, +0x5d3cf25d, 0xc3292c43, 0x1a29ae11, 0x0cf3cfc7, +0xe80831b4, 0x46d714e5, 0x07b64f30, 0xa3aae9ad, +0x820790cf, 0xa33780f9, 0xe1191d00, 0x451ef23d, +0xf6353908, 0xb7f8c79e, 0xa12327ae, 0x6283f590, +0xeb08588b, 0x002ee80a, 0x8766b3dc, 0x883dfb1f, +0xeddbaf19, 0x65c34946, 0xd14b0a01, 0x0e58e16d, +0x9e6cf3d9, 0x5a58c22f, 0x021db36c, 0xc437d83c, +0x341da40e, 0x32cdfc95, 0x15ba7983, 0xa9858efe, +0xe49c94c9, 0x83447420, 0x30afdd84, 0xebaaac1f, +0x148c150a, 0x322b37a0, 0xf3eba5b6, 0x32709e53, +0x162040c9, 0xe474f8f3, 0xe705f60b, 0xa3408334, +0xc130610c, 0x699aa58e, 0xa4ab21a8, 0x1b8f8c49, +0xab314f67, 0x6cff0b54, 0xfd834285, 0x44eb6316, +0x1e13dee8, 0x16dd3412, 0xbd1dbe43, 0x4e4f275d, +0x8c1c8cb7, 0xbdc1ca14, 0x19268114, 0x96c86310, +0x16f4beca, 0x4539205f, 0xfc5589ec, 0x71bbe44d, +0x6518ff88, 0x458b15eb, 0x903c8be4, 0x8ddb16cf, +0x4284e549, 0xf675233b, 0xed80c65f, 0x1251f065, +0xf0e77ca6, 0x7eda07c6, 0xfb8c6d4d, 0x14342bd6, +0x5a75c2b0, 0x64ac44d5, 0xbb549b50, 0x47378a3b, +0x28119868, 0x8048e853, 0xc1b6c301, 0x454683b6, +0xd04a753b, 0x70788b0a, 0xe8eee030, 0x658beb63, +0x5755a0f0, 0x93093919, 0xdd426096, 0xaaed0942, +0xf6f03826, 0x3ceb93e8, 0xa10406ea, 0x6ee94ba5, +0xc27abf48, 0x570f58b3, 0x026ba535, 0xb95717eb, +0x8d3663e0, 0xea29830c, 0x0d020b00, 0x7300e9e1, +0x4d6572f7, 0x6a803926, 0xba11f757, 0xacbea22a, +0x931c8b41, 0x1d9ccc10, 0xd747f86b, 0x31735314, +0xde8b1a1f, 0xe81f1f40, 0x74d912c8, 0xdc336798, +0x8cbc34ee, 0xa40c9b24, 0x843023fa, 0x5393e9f5, +0xf836d8de, 0xd553081a, 0x0bd8314e, 0x17f73a7b, +0x2a756e4c, 0x7c743d8b, 0x3ab2a097, 0x84cd1bb1, +0xcc2022dd, 0xb11d9d41, 0x10da8430, 0xe81beac2, +0xa06af030, 0x3c8c6812, 0xae0980ca, 0x0d828c58, +0x2c68891f, 0xd035f014, 0xffc03112, 0xe8c7bc60, +0x21414855, 0xf51ffab8, 0xbb97e8ce, 0xf7ee7861, +0x70f8c182, 0x2514c06a, 0x1cdfe27f, 0x0f7438dc, +0xb914408d, 0xe8fa310f, 0xb58b5f81, 0xeba615e8, +0x8588677b, 0x8259c2c8, 0xbb65aa0b, 0x5e7fc1f0, +0x2e01fd83, 0x02061a72, 0xdbdb0ddf, 0xe34dfebe, +0x74bf4beb, 0xc8b90c35, 0x76e10506, 0x62b9cc02, +0x11a7369c, 0xbc01bc25, 0x0ee82007, 0x68089388, +0x5ed88eb6, 0x92e3283c, 0x8015d820, 0x37035dbb, +0xfecc0b07, 0x5b59f063, 0x9346c588, 0x0c52752b, +0x9a239c35, 0x0f7bb76f, 0x83406fc0, 0x93aa0cd3, +0x7403f8d9, 0x83afc193, 0xdc3b9515, 0xeb5f6993, +0x8a1411b8, 0xe87d9c2d, 0xfbf6f7a0, 0x1e749831, +0x7bba92e8, 0x04020873, 0xc62d5c68, 0x65c86a3a, +0x024bc379, 0x46f90e17, 0x5b471d79, 0x6d3129e9, +0xd2be4ed6, 0x1b1b554a, 0x04a1474a, 0x2992becb, +0xe977471d, 0x713b21c1, 0x4e79ec81, 0x6fc268f8, +0x428e97ec, 0x22b9e689, 0x5ccd55e2, 0xc1be43ec, +0xf1e81a3e, 0x30ac55b9, 0x9e651dc7, 0x5610ebef, +0xa2b2c514, 0x0f5d7c0c, 0x40c481de, 0x1ad9f6b1, +0x40e32797, 0xebe09fc0, 0xfa8c3f7e, 0x079f0240, +0x5c119fe8, 0x60477590, 0x0d08ebc3, 0x244b4b31, +0x00e43931, 0x7b8f321e, 0x270d2784, 0x2b0be637, +0xd51be819, 0xd2df890b, 0xf3573718, 0x8f676d67, +0xd189feee, 0xb8d0bf0f, 0xaee95da0, 0x750b9d08, +0x3d87eb0e, 0x07e40dd1, 0x04cb0000, 0xf5c8eb95, +0x20b2a390, 0xd593111b, 0x2512d5be, 0xac07fffc, +0xdb9b3f51, 0x1729b22a, 0x290d2166, 0x9875d1c1, +0xbbe98d63, 0xbc9be848, 0x758cce13, 0xbf1c3e6d, +0x8cc11368, 0x580b9126, 0xc05264a1, 0x4f8b803d, +0xb6dc7edb, 0x75d61dc2, 0xd132cf8f, 0x301cf95b, +0xf010f1f6, 0x775c36df, 0x1cba2ec5, 0xe8c2b824, +0xb8d2dfda, 0xd1d7b304, 0xd3040b78, 0x841b4ed9, +0x2ebc5237, 0x4b5e273a, 0xcce68812, 0xd2efe871, +0xd05a9e1f, 0xdbb346a3, 0x85a5cb4a, 0x57676836, +0x385a6850, 0x18f107d0, 0x008bf938, 0x340a79fd, +0x40b90b15, 0x192a6a0e, 0xe78d56ff, 0xff5211c2, +0x006a0876, 0xd481ac5d, 0x080ccda5, 0x85a07f21, +0xdb7d56f7, 0x161d40bd, 0x38817dc7, 0x96ed213d, +0x0504e0c1, 0x04c83adf, 0xca423b68, 0x4a89034e, +0xe184724f, 0xf242c708, 0x6be98e2f, 0x73823f58, +0xc0c08f97, 0xb53de910, 0x2ef6d7d9, 0xb8975870, +0x09e2a520, 0x10617c93, 0xc54fb9ea, 0x00573fc6, +0x14592eb0, 0x0a5134cb, 0xe185a765, 0xcd45db06, +0xcb835610, 0x063f6219, 0x87f0790b, 0x760ff983, +0xb0572405, 0x2cae8145, 0x2c730185, 0x40557b54, +0x71e27d80, 0x4e2b415b, 0x8901db2e, 0xbb3c2dcb, +0xd4681a3c, 0x9f1ad4ae, 0x61758b2f, 0x2142791b, +0x05a8f29d, 0xe45b2017, 0x5009b080, 0xaf05caf6, +0x2d435b1e, 0xe08811b4, 0x497d828f, 0x68c6ac74, +0x458d08f5, 0x58130810, 0xb0eae808, 0x3044cf94, +0xc1208b22, 0xb97a0d87, 0x0007bad2, 0xf0026ce7, +0xbe03f2e8, 0xefb00213, 0x2bde9dc1, 0xcd1003db, +0x8e12f124, 0x03b915b1, 0xb7fcf3fb, 0x4c83b852, +0x7a1549e8, 0xb3eee821, 0x1376ed53, 0x6af46b68, +0x61156101, 0x76f61cfb, 0x35fffffd, 0x0b2988bd, +0xb074b92d, 0xf8c1f0b0, 0xee4d68b6, 0x74b01899, +0x2a8a7258, 0xff89cbf1, 0x3b72c4b8, 0xfe5206b5, +0xe479bd4e, 0x395b4746, 0xdaf7427e, 0xcbbd6d08, +0x5b30764d, 0xff6a4f7c, 0x548d6833, 0x76e0dd87, +0x1afcf0d4, 0xe7b804ff, 0x50324dcd, 0x0dfd16b8, +0xd56a95c0, 0xd805db43, 0xfdda092a, 0xc968ef5d, +0xbee81631, 0x72585e19, 0x801a369a, 0x280324fd, +0x2db01817, 0x838f12e6, 0x9100bcce, 0x8586d3a0, +0xa1f53a5f, 0xba1f56b7, 0x889e2058, 0x048a0af8, +0xda86740d, 0xf924132a, 0x6605c553, 0x44401f01, +0x60173136, 0x558cd60e, 0x49b4bf5b, 0x2eafb1de, +0x7560edb8, 0x258d0c98, 0x1e9f00dd, 0x3d7ae91d, +0x3d1bbc41, 0xffb6ef42, 0x540706f6, 0xeb1c7518, +0x0fef394a, 0x7799fe09, 0x8519eb01, 0x0412f620, +0x0ceb840f, 0x4f76d9cd, 0x67178674, 0xfe3910da, +0x841bd018, 0x0ebfd9c3, 0x5ea8564a, 0xee168602, +0xcef71bb4, 0x64405d40, 0xc1449181, 0x2dc9e3e9, +0x40356ac4, 0xc3c30473, 0xda5d16b3, 0x083bceb8, +0x74c13bc2, 0xeec30b1a, 0x47b4a16d, 0x0e046803, +0x0062e1b0, 0x7a107452, 0x047b8933, 0x4440c7b4, +0x3631e36f, 0x28400500, 0xd2290db2, 0xb79b1161, +0xd624c358, 0x142e9995, 0x3bc030ff, 0x3e740843, +0x79e189d3, 0x16f5d1f8, 0xfcb60f6f, 0xf0ad8bfe, +0x85f81425, 0x86e2b776, 0x15e283d2, 0x0ea56742, +0x50be6da8, 0x0752d204, 0x7cc0266c, 0x2881674f, +0x8a606cff, 0x34558e3e, 0x511399a0, 0x380b3814, +0x1b15d705, 0xbb4aa3d0, 0x2fac040e, 0x003922dd, +0x12b44a8b, 0xef0253e9, 0x6a1c41c0, 0xaaa37908, +0x9c9432ad, 0x39c9ed70, 0x9ac41280, 0x0d06fef6, +0x3b991b6e, 0xe5ea741c, 0x87b0651f, 0x39c3570a, +0xb6209550, 0x39404510, 0xb6077c81, 0xb5fd21b7, +0x312ba306, 0x49606c90, 0x6b5a5b8e, 0x1da9ac69, +0x03102bb9, 0x39e8e76c, 0x64572244, 0xd239b356, +0x82759d9b, 0x52b35ba1, 0xc58b8c16, 0xf92077d9, +0x65ed48e9, 0xf68dba1c, 0x01e3831f, 0x981a8966, +0xe29a4ed1, 0x3db4110a, 0x8509d45a, 0x96020b7e, +0x2d120da2, 0x66541cd3, 0x2a55b8d8, 0xc8838465, +0xdc331637, 0xc25d5816, 0x98210244, 0x4d063e80, +0x887333ba, 0x25d35082, 0xc9db9107, 0x8b2355af, +0x80091390, 0x5ba23782, 0xcf2901bc, 0x032e188b, +0xc7016848, 0x031e0886, 0x14e48950, 0x0c3f54c2, +0xe8cf89e3, 0x9b82dcdc, 0xda6e58ab, 0x1056296f, +0x980b8da3, 0x463bfb7d, 0xb60023d1, 0x1cb6803f, +0x46660bc7, 0x14164014, 0x5815a2af, 0x66120b1c, +0x815a1fc6, 0xc42dceeb, 0x0bd2be0f, 0xe1837185, +0x65a9e9e1, 0x1d347ec2, 0x01a07a83, 0x075d56fd, +0x481c428b, 0x76c48d23, 0x1a18f358, 0x04cd1006, +0xc17a8131, 0xe9d07113, 0x952df062, 0x9b51530a, +0x231b140e, 0xf9283741, 0xc68ae889, 0x8add1616, +0x061c016e, 0xe875154b, 0x082c86db, 0x7a0d2eba, +0xc19031d0, 0x467919be, 0xc084068a, 0x68e0e72f, +0x1c1d52ff, 0x483b1373, 0x1ce07318, 0xa0587861, +0xaefe0006, 0x4c0236c3, 0x877bea98, 0x340b428c, +0x37f44a5c, 0x5d09e0b0, 0x900e270b, 0x42ceb208, +0x1d70ddf4, 0x02ed0833, 0xc94ab9e9, 0x2b3bb223, +0x0a51513e, 0x62d3ff5e, 0x6492fb9b, 0x21d2f7c3, +0x05090d26, 0x8ad456a6, 0x04158ffe, 0x09daf0bd, +0x961086d1, 0x18660458, 0xb8b4b84d, 0x05405f1a, +0xefdd9aab, 0xd817850b, 0x047608fa, 0x22518ba1, +0xf658ded2, 0x0908dd0d, 0x4c5189c2, 0xe1c1c915, +0xd89b63bd, 0x81bdd113, 0x89ca54e2, 0x368b3818, +0x837540cc, 0x37d84e47, 0x061d1306, 0x0dc99e78, +0x7361ef6f, 0xfcd7e891, 0xff770f15, 0x9aed1052, +0x0da87b26, 0x0b22b50c, 0x7c58bca3, 0xc104c907, +0xa13ab8a3, 0xb261a1a6, 0x9510db60, 0xc04f2cd3, +0x2487d4b6, 0xc7aeb961, 0x688ea91b, 0xfc5d9745, +0xd127d145, 0x1247411c, 0x8e1a4d68, 0xf408805d, +0x756bbe05, 0x99285e18, 0x21dba58a, 0x202c061c, +0x027c0615, 0xbb2406b1, 0x750010c2, 0x4ad55baa, +0x2470afa3, 0xc3502b6d, 0xf008bef5, 0xa0b7132c, +0x28aee4a5, 0x00c229ee, 0x8a1406fe, 0xc9859e09, +0xc829107e, 0xeed1885e, 0xc7157908, 0xd0ecb004, +0xd01bf029, 0x7edb225e, 0x48110103, 0xef781789, +0x45e83472, 0xaf5fb8f0, 0xc0e47d84, 0x451631b5, +0xb8307be4, 0x2c26ef02, 0xf12f2b9b, 0x1276c639, +0xf7713e82, 0x3d5503c1, 0xd33ae8e4, 0x6d6e187b, +0x43c46dfb, 0x4b8bc918, 0x0cc553bb, 0x68d884e8, +0x16d42274, 0xd58ae489, 0x0121b112, 0xb7d9e918, +0x47163db6, 0x20247129, 0x2df04501, 0xfd436004, +0xe8f04d8b, 0xe8656053, 0x21305d73, 0x3af953ba, +0x42e97a6f, 0xebdb0388, 0x458ee9c1, 0x32dba646, +0xe845d9c3, 0xef857115, 0x8db3b03e, 0x83fe1841, +0xe1832ac1, 0x0402e02b, 0xd0cdcca3, 0x45b756b5, +0x34eac621, 0x0b451a3d, 0x52b52ed8, 0x4495afdb, +0x5683c640, 0x3777e1a0, 0xaf0e3b43, 0xe857ec75, +0xfe145ccc, 0x9782c381, 0x3a59e87a, 0xe0e72c3d, +0x7feeff00, 0x1ffabf19, 0xeb58cefc, 0x09c4a348, +0x15dcadc1, 0xfb0e9a04, 0xf3aab701, 0xb7565009, +0xefbe19eb, 0xf0b60b6c, 0x61491a03, 0xc545481a, +0x39980702, 0x61b4ced2, 0x672e4c0d, 0x2c56d4ff, +0xc29f0fdc, 0xf4275fbf, 0x3026d0ac, 0xcf0bb4a0, +0x6a05bb7d, 0x62eeb700, 0x042d66bf, 0x01856e6b, +0x2d2b2de2, 0xaedb7807, 0xfdb8853c, 0x5d3bc3bf, +0xdfd98b60, 0xe8630104, 0x22e8020b, 0x72982d45, +0x25e8f0c7, 0x6b150a10, 0xf81b637d, 0xe05b87b1, +0xac193a98, 0xef9bc320, 0xb0180a2e, 0x1d88680f, +0xa403e969, 0x16735f33, 0x275a0ef9, 0xd821dd40, +0xf15326da, 0x3fe8a0f8, 0x7bc067d8, 0xe84ebe8e, +0x0d7472d8, 0xb0ac44e8, 0x5204d76e, 0x1ceb3ca0, +0xb48cb939, 0x84f7d8de, 0x1d2638b1, 0x6f865ac3, +0xb3473a38, 0x1236f722, 0xe203e03b, 0x2db68fd4, +0x8cb4935d, 0x26a9a624, 0x95c42c18, 0xa2dee3b3, +0xd93ece6d, 0x4f14a8ba, 0xaa273c80, 0xbddef9b5, +0x4de66853, 0xe0848bb1, 0x2f057ec3, 0x2beb7d1c, +0x90e5cd33, 0x181bb893, 0xc17d0f02, 0x33320e04, +0x9b0b0459, 0x68e682ef, 0xbb66161f, 0xc4d258c5, +0xd0006ba8, 0xd826589e, 0xc55a1c01, 0x27e8745d, +0xf19c3746, 0x3813d7e6, 0x4816e80c, 0x7e309efb, +0xfffffe79, 0x4368e2e0, 0x34e8164f, 0x3fc48f52, +0x760070e8, 0x1ab90236, 0xe02033b4, 0xeec6719e, +0x265c37ef, 0xb8ba59ef, 0x300b1fb8, 0x7fb85585, +0x53ed21c4, 0x3d51e95b, 0x6a05e07d, 0x02c47806, +0xa5a91e75, 0x0723c7f6, 0xf11fcbb8, 0x419887b3, +0x89701f77, 0xebfde20e, 0x31e9b8dc, 0xd3f7a368, +0x33ffffc9, 0x1f05c810, 0x7641b8a6, 0x0e1daaa3, +0xc31e0469, 0xc078767c, 0x06c35e5b, 0x382145f9, +0x465327c6, 0x07f6fa54, 0x605006db, 0x094c6e8b, +0x7402a838, 0xabbc8eed, 0x0f800c23, 0x09830c01, +0xfbb101a8, 0xbf3f1bd1, 0xbff009bb, 0x5e8b0f88, +0x33674568, 0x7846e285, 0x2e4475dd, 0x0455e4d8, +0x8c53571c, 0x7d9cf9e2, 0x3858fa8f, 0x2a07dadf, +0xea14b810, 0x6e34be08, 0x0f6f8a5f, 0x582442f6, +0xfcf04103, 0xb216bb15, 0x72fa8114, 0xfc7449e9, +0x6c5cdd41, 0x0b3566dc, 0x963070ff, 0xa742b679, +0x8daac94f, 0xc22951d7, 0xfdc43b34, 0x777c15d7, +0xdf94cc17, 0x0b4d43f6, 0x8211dd68, 0xe061edbe, +0xe73ab26f, 0x6deb6f12, 0x684a64b8, 0x63e86e18, +0x824d9e1e, 0xfcb0a86e, 0xe33471e9, 0x11a255d8, +0x397bb381, 0x3d6aab52, 0xbb07b7a9, 0x46bad43f, +0x7f3160a6, 0x8a568e87, 0x92ad6c23, 0x9ec268de, +0x43a89b2b, 0x217e5091, 0xa82f3b90, 0xb60f7aa3, +0xf0fb3a40, 0x37f16281, 0x38b624b8, 0x1d67543a, +0xbf0a072f, 0x40e9cef0, 0x021e1900, 0xb8586c18, +0xf113f819, 0xd6b0652b, 0xa257bbb3, 0xb75e9446, +0x9d7d6e54, 0xa7ddb54e, 0xf8acad85, 0x0466a8ee, +0xeb982210, 0xfb337508, 0xaff8bce9, 0x33cdc58f, +0x871506d6, 0xe9742723, 0xbf543a0d, 0x08732191, +0x83829640, 0x3d81def8, 0x23750308, 0x8d0683cd, +0x3128e8f4, 0x0aa9582e, 0x7be67cf3, 0x394e680c, +0x2d6e0c3a, 0x001b7ef9, 0x64124891, 0x19138d68, +0xb49e1c30, 0x588088ba, 0x1d10ec45, 0x43177eed, +0xfc1c9a68, 0x040d1056, 0xfb1d04aa, 0xae59a57d, +0xe516bd47, 0x126c6856, 0xeed8ec70, 0x75fbb4b9, +0xc239060f, 0x380238ff, 0x78df5329, 0x7e2ad153, +0x6ceb4c47, 0xdbc04576, 0x30256df3, 0xfed7820b, +0xaf069c48, 0x0b032994, 0xb541c319, 0x726020d7, +0x6a6d8d83, 0x152978a1, 0x1b0fe889, 0xee0ae506, +0x6de8aa02, 0xbe41421b, 0x5c432556, 0x58145e79, +0x68cefc01, 0x4e4d155a, 0x0740581b, 0x8c992787, +0xcab0bf5b, 0x0b146dde, 0xeb3075ff, 0xf6a5f303, +0x1ba97bc2, 0xa5c82195, 0x01741f80, 0xe20ea4a4, +0x1d56e556, 0x41c7be49, 0x6741fe19, 0x7948028b, +0x12895e51, 0x1fe2d0b5, 0x7efa0ce1, 0xf01a86c0, +0x251afc68, 0x9b1d049f, 0xb868f801, 0x4d00e0d0, +0x5557efc1, 0xba8de3aa, 0x52250968, 0xf4c9a010, +0xa8373d24, 0x25bc40be, 0x0c8b4b0b, 0x27f6a99e, +0x4e076fd8, 0xf9833ceb, 0x78965ec7, 0x08ca7db3, +0x160932eb, 0x28ebf7e2, 0xe85ee251, 0x0975a5d2, +0xf8e2b777, 0x024081b6, 0xaa66c833, 0x8fd81308, +0x4cdc2ec4, 0x3943912c, 0x5889b177, 0x3e2ee825, +0x1ce0a16c, 0xb984108a, 0xde8df532, 0xb60ba340, +0x00bac3c2, 0xf262d658, 0x0e68115d, 0x425a5f94, +0xc04dedb6, 0x7ef856d0, 0xc31b0377, 0x28dbc50d, +0x474e36de, 0x16770c0d, 0x0a5b2029, 0x6b951cc6, +0x1ac5b842, 0xc30a6a3c, 0x605fbbca, 0x19418a2c, +0xc838ba75, 0x5bbd45b3, 0xc1d00313, 0xdeae46db, +0x01f08306, 0xfa55c475, 0xf728d8c2, 0xcbe4b8d0, +0x13751df5, 0x4f7e1358, 0x311a682c, 0x572223d1, +0xa56ee175, 0x89706194, 0xb84c4069, 0xda5583e1, +0x3b307b81, 0xb7860f94, 0x7fc43f7c, 0x812c5395, +0x3201f1c2, 0x414bb901, 0x6fc1b01f, 0x2f81cf0d, +0x72644812, 0xf5929953, 0xb2073bd2, 0x01ff160e, +0x7ffd8877, 0x01e0097e, 0xb77b1db0, 0x03616084, +0xc05091da, 0xd88d80bb, 0x13a7260e, 0x84394c15, +0x2b809ddb, 0x8a500c89, 0xc5737656, 0x00dcc8cd, +0xc0365c0a, 0x5ffb1f40, 0x1909dd03, 0x37604354, +0x7730433b, 0x4c4df630, 0x4b80db80, 0x994400b8, +0x19931bb9, 0x99644303, 0xb6302b30, 0x0ad1ab50, +0x9994bf68, 0x0e1188eb, 0xd1ffcb80, 0x35bb53e9, +0x8ca32c01, 0x8d4d4c9e, 0x41ce8281, 0x035806ef, +0x00c1815c, 0xe80c1767, 0x5404576e, 0x35e88b8b, +0xb183e687, 0xb885f7dd, 0x53164cfa, 0xdb2b192c, +0xdb77d932, 0x640c6826, 0x0ce8d189, 0xc45f8f28, +0x436877d8, 0x92642c03, 0x111bbbc6, 0x4620086e, +0x1276e017, 0x1d665816, 0x2db6edbb, 0x34786609, +0x80212e80, 0xdd02011a, 0x18deeb0f, 0x0310765c, +0x38ce0362, 0xfb6620eb, 0x75c0da6c, 0x2da33fdf, +0x5c6c216e, 0xe119426a, 0x603b9efb, 0x05922213, +0x61435494, 0xfc8a0f70, 0x69f85ca0, 0xf842e290, +0xc45b70df, 0x2510897c, 0x5aba94d8, 0x2da1a028, +0xd9d0344e, 0x2584af6a, 0x82815b01, 0xa4087b5d, +0xe80c90c9, 0x7f1108c7, 0xa0f80394, 0x7eb8e140, +0x4bc83d8f, 0x6eb90e02, 0xa51133a1, 0x7b37a10e, +0x0870abc7, 0xba1c1937, 0xee8481a4, 0xddb18d13, +0x81342724, 0xbf0dcfe8, 0x1f02ddd9, 0xe8441030, +0x04c919c3, 0x17d9c0e8, 0x70b4b68f, 0x5aea5720, +0x4d4db9ab, 0x07c0fbc9, 0x75e1f1a4, 0xab100a51, +0xa92410b8, 0xb656e8d8, 0xfcda72c7, 0x4002db90, +0x9d33ff6c, 0x232c5530, 0x2a740b7e, 0x2394cf01, +0x12373046, 0x8d7ab9b0, 0xf8350704, 0x42d58e7c, +0x4ffa5b40, 0xbc60c928, 0xe52c8b88, 0x5a365828, +0x28558be1, 0x15e28c8c, 0x4430c8ee, 0x35e96379, +0x4daf8c5e, 0x0534d600, 0x6f6389a5, 0x58efb828, +0x8b080a11, 0x3d661670, 0x25010a02, 0xd5d5e00a, +0xe86abdbe, 0x68b86441, 0x0205158e, 0x1d611477, +0x0fe98575, 0x1737ff64, 0xee8f1634, 0x48330aeb, +0xd85c247e, 0xd0230e05, 0x84c0523a, 0x85b76347, +0xea78c55d, 0x5c02588d, 0xf03fba0a, 0x1a781600, +0xfd3e55e2, 0x8515f015, 0xba459d60, 0x80cfe446, +0xfe3b199c, 0x0480ba2b, 0x640678e8, 0x2d11fd21, +0xd8f67278, 0x33243a87, 0x4aba6069, 0x3c2d0a79, +0x8e747478, 0x53044fee, 0x0eee80dc, 0xf6f9742f, +0xf77b7332, 0xfb383878, 0x008a1aea, 0x971b4d3c, +0x7f7ff02e, 0x74473c14, 0x754b3c1c, 0xfab85255, +0x2fe9d571, 0x87bbfffc, 0x746b3ccf, 0x746d3c2e, +0x75673c13, 0xd1a40f3d, 0x890a620a, 0xd3b65d54, +0x46643a64, 0x0ae0c216, 0x8d8530f7, 0x83165451, +0x3b08833c, 0xff50581e, 0x21014161, 0xe9d5395c, +0xadc18064, 0x08803d58, 0x88a3da3d, 0x21277ef9, +0x3e968570, 0x36b1fbf6, 0x40a9b002, 0xc6011904, +0x1a0ac79c, 0x8013e507, 0x80ad3d8b, 0x03fa0fad, +0x4ee7e37d, 0x14b6f5f8, 0x454237b5, 0x39650530, +0x89eee1c7, 0x93280016, 0x0672813b, 0x62348e3b, +0x77647eb0, 0x76f88915, 0xfbf525f1, 0x6b710a01, +0x30ef810e, 0xa8d8eb81, 0xbf9c8e36, 0x166c6489, +0x3fb17c70, 0x89161d03, 0x320d2bf9, 0x4863688c, +0xdfc7f213, 0x6a401ad6, 0x076cee9a, 0xe62c7028, +0x1d160ea9, 0x1c09f8a3, 0xbc182f2d, 0xa54cebc1, +0x2e682055, 0x8e1690ff, 0xb8dc1dc0, 0x0bf48b60, +0xd127fc75, 0x7883c6ca, 0x27760430, 0xbeda03dc, +0x5a412b61, 0x8a036011, 0x06880451, 0xf0776207, +0x68ba39ab, 0x445de881, 0xd8d9f8d2, 0xba14a28e, +0x5e10ce51, 0x3757ba40, 0xf59f18f0, 0x4f5443e4, +0x07677e83, 0xad8446c7, 0x9834c0fd, 0xbf305e5b, +0x4d2bfbfa, 0x5128297b, 0xe9e87972, 0x0fa02981, +0xab2a1a65, 0xe1f7e963, 0x1aec0831, 0xb9b7007c, +0x017443c6, 0x26c2ae03, 0xd177aed0, 0x02689f3e, +0x008c4044, 0x9a1d5068, 0xe634aa68, 0x0e685b0e, +0x20528162, 0xb56bde71, 0xd4af41a3, 0x0bcc9198, +0x78a604a7, 0x3188e163, 0x345befe9, 0x33050f28, +0x3b812677, 0x437a9894, 0x2a1e77c0, 0x348b0c4b, +0x84ad81b7, 0xf312da33, 0x370e0415, 0x76626e02, +0x450ceb36, 0x766c3bdd, 0x376e359f, 0x9a64ca75, +0xe92b24a3, 0x6788140e, 0xfd7f8847, 0x2a100735, +0x9c40dddb, 0xc7ba914e, 0xc0b27d66, 0x74006f05, +0x9d13b8b1, 0xc108d929, 0xf63efee1, 0xe9810aef, +0xce9a8ddf, 0x50033266, 0x896eb828, 0xcddf2025, +0x8b50ed8d, 0xcb625625, 0x296851e1, 0x2dd62053, +0x066a52f5, 0x127b36ff, 0x2d9e238b, 0xee41a5b8, +0x73138576, 0x1d1029db, 0x8007cee4, 0x068c3083, +0x1225abdb, 0x34a11a8f, 0x0c2780cc, 0x1abc2904, +0xd3c41e8f, 0xbcfb2b34, 0xb36a54b2, 0xcda9aed8, +0xb2e5ae20, 0x1718c228, 0x7706e03d, 0xdbad96be, +0x09fba514, 0x8b710689, 0x781c17ba, 0x7336a454, +0x855087f7, 0x7e7d4367, 0x527dbb05, 0x501a089a, +0x206e9588, 0x089bdd77, 0x9e880d78, 0x0f8a1380, +0xae818688, 0x9c0f87f0, 0xd9908286, 0xe9f5f1e8, +0xf02d9e16, 0x3384206e, 0xd312fc11, 0xd0db34a5, +0xb807ce06, 0x96c0ba8f, 0x6d1e3f68, 0x32f18868, +0xe409c11b, 0x5053e804, 0x2d83b3b9, 0x760728d1, +0x0293c635, 0x8d863182, 0x05e0aa40, 0x42230e46, +0xba013b76, 0x63d9c861, 0x41351d4f, 0x65ba6a12, +0x4bd4250f, 0x5375ff8f, 0x45c758eb, 0xedbaa434, +0x30feff3d, 0x3b5b09c2, 0xe0350440, 0xc40dab90, +0x1ca19638, 0x3e667f20, 0x2c06a918, 0x4d3cf725, +0x0ddaf52b, 0xe32c5583, 0x9be12910, 0xebb8dd2e, +0x78c2bb0f, 0x7d83d4d1, 0x3deba918, 0xa0ab0c52, +0x788b43c9, 0xbb577738, 0x2966b527, 0x1c195e04, +0x0683e8d7, 0x68a4c2b5, 0x712efeeb, 0xbb6a0b5e, +0xbfa7dbc2, 0x0b734ff4, 0x6d1ef12a, 0x8c100102, +0xda49b975, 0x1462790e, 0x92c0b07b, 0xa0d87801, +0x34327d3f, 0x8ed23c1f, 0xef28705e, 0x993b09f5, +0xf42316ea, 0x8e1a870f, 0xefc1afc3, 0x5a5a545c, +0x039c138f, 0xb7fdef91, 0xe8cdc770, 0xbc8a6c52, +0xadb00204, 0x5c3e751b, 0x1c9caf84, 0x8f20ff84, +0x3b37611f, 0x17033f0b, 0x46010c06, 0x1f5ae7f4, +0x249c890f, 0x19907c98, 0x782210eb, 0x0449b5b7, +0x753600d3, 0x6aa5e987, 0x7c7c3b3d, 0x3e637434, +0x7dbba966, 0x23968d94, 0xc87c21bd, 0xea0773a9, +0x7574c40f, 0x24b6948b, 0x41ddd64a, 0x47062bed, +0x10103c8d, 0x6aed6618, 0x0674385c, 0x10d3297d, +0xe0fb2965, 0x15d6696a, 0xd9bc7c1c, 0xa2415058, +0x3ebee400, 0x63b1f821, 0x7d7121da, 0x7e032bf5, +0xbefd8c3e, 0x6c14fa79, 0x1820c5f3, 0xdb0b3846, +0x35a9d0db, 0x56277c09, 0x847c54bd, 0xd18152e0, +0x01285c99, 0x3040b8fd, 0xc085c29e, 0x2b08c5fd, +0xf42c7e9a, 0x19689e3a, 0x524020b0, 0x01d51e22, +0xfec46c32, 0x5613da5a, 0x038c499e, 0xb72b8901, +0x9c706744, 0x7a761b05, 0x70591005, 0x34ffc38c, +0x4cf08958, 0x85acd6ee, 0x99aeb4a3, 0xab063840, +0x10a036fe, 0x81603d97, 0xfc3220c7, 0x58a8bd0e, +0xc7146e62, 0xb7000680, 0x65024dfb, 0xf3098090, +0xbfdf7400, 0x748d12a8, 0xa2ca3024, 0x16a61832, +0xa2298801, 0x1074ef07, 0x57117878, 0x77eb19b0, +0x577ef56c, 0x7e740805, 0x191407c7, 0xdba14e76, +0x57402db6, 0x4e844f02, 0xf40c5e02, 0x42fa89fd, +0x1b062b41, 0x10576b56, 0x5bc047c7, 0x834e44fc, +0x06dd2c45, 0x976c460b, 0x0a20be25, 0x9ed9ac0f, +0x10044deb, 0xf6dbc435, 0x1c15ec1e, 0x0c563c46, +0xb2cbdd05, 0xd2833ffd, 0xead023ff, 0x81ff0845, +0xa1c0b683, 0xc714c68d, 0x0d546e1c, 0xf0070458, +0x107b820f, 0x003b9b14, 0xf21dd8de, 0x8b67cac3, +0x3b098941, 0x7fe828e2, 0x0d104374, 0xbef02c4a, +0x40c79984, 0xc1090007, 0xd0f7ef2d, 0x38eddc48, +0xa4e93f28, 0x5b1e069f, 0x3628196d, 0x08c66f87, +0x08896f6d, 0xb45989d7, 0x02a5d58b, 0xc2f4616e, +0x00e98d05, 0x040d0428, 0x04a2981c, 0x0c45ebbf, +0x083b246f, 0x15420a72, 0x6e016d8e, 0xee7528e0, +0x5cd8e043, 0x340bffdc, 0x281cc3ba, 0x8d04d158, +0xdf39107b, 0xa2890773, 0xde85b550, 0x2233a454, +0xdf6d1c17, 0xff0f7c22, 0x99fc0cfd, 0xdc8b3b6b, +0x5170bbb6, 0x4389207e, 0xc2bc4539, 0x8d101efd, +0x138b03ea, 0x00184bbb, 0xb0086dca, 0xc8e9fd81, +0x8e816819, 0xd15b0a50, 0x0b9af42e, 0x1013f5ba, +0xc1c3a814, 0x82598dea, 0x388204e6, 0x5c8e4bdc, +0x068082f6, 0x00205011, 0x9967bd2c, 0x25115460, +0x062bc009, 0x50207bdd, 0xa6261d03, 0xe2555310, +0x136f2bb3, 0x31539015, 0x035c0b29, 0xd36e1fb7, +0x8170de9b, 0x8d607774, 0x2c7c3d54, 0xbb07c703, +0xe97bc06e, 0x7b7a7fc5, 0xaca6e813, 0x0942e827, +0x1de5afc4, 0x5bd80d90, 0xa51d969c, 0x9d68a1d8, +0x070984f5, 0x016b649a, 0x31a67a7b, 0x1f1628bc, +0x5dbd03e8, 0x591bc529, 0x055d509c, 0x0c9579cd, +0x0509c01d, 0xefcb11b7, 0x81c15d5b, 0x68f676fb, +0xaf27d660, 0xc6dfc80a, 0x6cb83c48, 0x70e88b68, +0x1c37d108, 0x3980dbbb, 0xecc60dab, 0xe24d0c05, +0x628f0018, 0x0903b1ab, 0x7ed90a69, 0x6fdc976b, +0x8b310fe7, 0x5b12100d, 0x4cb75153, 0x95721302, +0x5a530c4d, 0x1aedada3, 0x19f60f98, 0x5726c404, +0x1d006dc8, 0x9eefe8b8, 0x2e08d615, 0x19d40f35, +0x1811c4f2, 0x0a3637a8, 0xa647a337, 0x057fe043, +0x7fbfc5fb, 0x6f5e5bd3, 0x00340230, 0x6864c75e, +0xab83553c, 0x74937d49, 0xdca555bb, 0x5a750b5d, +0xc0e28195, 0xd9fd05ce, 0x0e02b7fd, 0xc2092546, +0x1857af0f, 0x1c012109, 0x5c3fe383, 0xdd0b50a2, +0x468fba1a, 0x6b624e06, 0x5e068073, 0x50a2d8e0, +0xe8ab9568, 0x48732826, 0x27d5d582, 0xe3d0f747, +0xf10ca45a, 0xf243b458, 0x4101b8c6, 0x1c0063d5, +0x68f2205f, 0x220c7f65, 0x3d13a57c, 0x7adfc737, +0x1a2febe2, 0x9f0b8038, 0x46d0aa55, 0xd0b007ea, +0xf3209f0e, 0xd8119dce, 0xba1b1af0, 0x9f99dc3d, +0xb02bdd5a, 0x2160e16d, 0xe55f11f4, 0x04588d8b, +0x86408d4b, 0xfa095127, 0x039640b9, 0x19611b75, +0x730f1d72, 0x3eba0b27, 0xc2ce2d0a, 0x42de923d, +0x402c189e, 0x4e54adf7, 0xc10472cf, 0xb552d6e6, +0xd7b127d8, 0xe7895ed6, 0xe01300a5, 0xd7adb802, +0xf01be2f2, 0xb9a0ca01, 0x52c74ec6, 0x75325215, +0x8976580a, 0xf8d3ffbc, 0xd0464dde, 0x211c9b88, +0x29428a30, 0x175afc1e, 0x18204a22, 0x46aee992, +0x2037f4b7, 0x1574d1d8, 0x6d104d3b, 0x7adf0f9b, +0x3043b7f5, 0xe9205188, 0x70fa97b3, 0x683c1877, +0x01eb6ffb, 0x9c884474, 0x4274020f, 0x4d27033c, +0x26eb6eff, 0x423c40ed, 0x77c1c63a, 0x74153c11, +0xdf413c7d, 0x176eebb6, 0x8dcf0135, 0x1a433c12, +0x6f4807b3, 0x146cf58b, 0x5562b920, 0xb1bc6116, +0x8be95bad, 0x07065048, 0xd6389510, 0xe4ce7405, +0x1425f80f, 0x6b6ee948, 0x49182cb7, 0x8a5cb097, +0x6ec12543, 0x50db51be, 0x0ad00977, 0xb8214bb2, +0x9db45e75, 0x008a9084, 0xce622012, 0x02177c2d, +0x66481645, 0x06402474, 0x823fb3f8, 0xe8e91520, +0x8166d100, 0x5daa1c79, 0x5ddd08e1, 0x41d9ca85, +0x2405d71c, 0x4467d843, 0x00c2e940, 0x5a066595, +0x65221437, 0xb5725d8b, 0xde0adb51, 0x221b1c7d, +0x07200548, 0x021e8ec4, 0x403c061a, 0xa9b55d03, +0x0e740ac1, 0xd00b47de, 0x16b9123a, 0x85c21a28, +0xb7209631, 0xd053a704, 0xb3017447, 0xb94bb9d0, +0x8e54a05c, 0x61ec04ee, 0x47e9fb43, 0x6caef7f8, +0x08ebd8f7, 0x2c62837c, 0x29418afe, 0xf866c6c3, +0x002c4981, 0x6dd97008, 0x86b8fd3d, 0xc7b6fda8, +0xc7139846, 0x802de733, 0xc7394fdd, 0xeb41c031, +0x827514c4, 0x84550525, 0x42a5bdb1, 0x0a3e659b, +0x5a099e37, 0xe7effe22, 0xe2016a57, 0x2575fec9, +0xdd56683a, 0xbf16d10c, 0x1fecb016, 0x8939eb4d, +0xa0f40dfe, 0x94f0df0a, 0xf722be34, 0x117c80d9, +0xed6f8377, 0x44b61ddf, 0x688d0511, 0x060f0701, +0xae3fe783, 0x937d53fb, 0xc6394015, 0x03fbd375, +0x08186b89, 0x67682a6f, 0x7593051c, 0x02438b2f, +0x34082aef, 0x83037644, 0x6808cfca, 0xcd73d2f0, +0x3d1c73af, 0x582a5c30, 0x3d55f682, 0x33a1d100, +0xae041415, 0xdedfbf08, 0x3d191c6e, 0xc8c58817, +0x89f06b80, 0x02e95a73, 0x014a66dd, 0x6bff3c9a, +0xdad8880c, 0x0c12a617, 0x190d8980, 0x6ff495d1, +0x7797b5d5, 0x01488d03, 0xa46c0f88, 0xe87c0d82, +0x40c39cc9, 0xe1d1e41f, 0x1d0b9637, 0x22c48105, +0x86021682, 0xb603a883, 0x984d16ff, 0x4802033f, +0x83404aff, 0x02f67dfa, 0x73dae162, 0xaf654ac1, +0x64da2be9, 0xaff25090, 0x305d6fa1, 0x880a6ee1, +0xfd077037, 0xa2be5290, 0x060815cd, 0xfb988b56, +0x17f01683, 0x888983b9, 0x3bf20c62, 0x6a68391b, +0xb00c7027, 0x32767672, 0xac7a7872, 0xfb06b42d, +0xc0767677, 0xca06c414, 0xc681c628, 0x11160740, +0x5e8ab42a, 0x47e00781, 0xaeb5bdb8, 0x88c32800, +0x07358b10, 0xc834a0c6, 0x091f63c9, 0x09282cba, +0x20df7893, 0xbb3fb85e, 0x7c085c24, 0x8ab06df8, +0x34b668da, 0x10180ed0, 0x8c82af0c, 0x3d2f5a1d, +0x5dcce7cc, 0x9bdac903, 0xcb300000, 0x531a610c, +0x5b59c584, 0x482cdb07, 0x6340c550, 0x20beb3d6, +0x2d2b022c, 0xe4ee89e2, 0x84dd50f5, 0x517e2cad, +0x8f4ae150, 0x4f3d303a, 0x1d84ac04, 0x1992d683, +0xe689efec, 0x28eb542a, 0x0e9d4be7, 0xcda0f463, +0x9dd2b423, 0x429a08c1, 0x10695222, 0x7b1010b0, +0x321a8c8b, 0xd6d3ef1c, 0xa8153446, 0xf287191e, +0x2b66dc13, 0xf30f1810, 0xa50b7377, 0xd90216f9, +0x01290ba4, 0xbf91b667, 0xe9822775, 0x6f0f0201, +0xaed9db09, 0xe9900f01, 0x070e6ae9, 0xb06d8f18, +0x19347dd2, 0x0615050b, 0xcd0d6724, 0x0e656b9e, +0x09097474, 0x6cec1b6c, 0x0f4c5218, 0x3b72b218, +0xde31bb1b, 0x69727e84, 0x830f0d1b, 0x7d6ced9d, +0x1d340e09, 0x6d9d120e, 0x9d186d87, 0x7b0b1072, +0xedb31115, 0xf824b9ac, 0x130e7602, 0xa0098418, +0x14b67b38, 0x0e81df18, 0x74b9a671, 0x7b776dc7, +0x0158250b, 0x79ad9e34, 0x0f292196, 0x11ac3220, +0xcedb3801, 0x181a22fc, 0xc1682309, 0x185f676e, +0x18320e13, 0xec890145, 0x30b6ced9, 0x31151b0b, +0x0e166e24, 0xed60db35, 0x70091833, 0x6deb5518, +0x7abdb7dd, 0x1500e13d, 0x1731c406, 0xd1930b75, +0x92f75b3d, 0x23730b0c, 0x760b6d31, 0x18b6bafd, +0xcae02d83, 0x01c9521b, 0xf759ee67, 0xe83ce40e, +0x0be23d77, 0x1bace315, 0xf624766b, 0xe50ec101, +0x0009cf18, 0xe66e9eee, 0x04ccdd18, 0xeed4c8be, +0x361f5db0, 0x0c7bd5df, 0x0ea31500, 0xf7f76fbe, +0x05be45eb, 0xe3bee806, 0x17afe923, 0xdda509d0, +0xdb79e7dd, 0x24d93d9b, 0x062abe28, 0x74fba7e9, +0x9c108abf, 0x9e5009d6, 0xdd9e55d8, 0x76be79e7, +0x678e290e, 0x4009dbb3, 0x3cf3e69e, 0xdac74e9b, +0x4f9a6c38, 0x882e79f3, 0xddd830dc, 0xb7dfba26, +0xbf13907b, 0x0e1c4a0e, 0x3283de32, 0xd7df36fb, +0x10061f3d, 0x1f88bf0d, 0x9a7b04fc, 0xb6beb7df, +0x8f09ed18, 0x4298d7df, 0xefbefb5f, 0x0ed92284, +0xf923428a, 0xdf3d0315, 0xc2c33cf7, 0xb40e3247, +0xcf7df7ae, 0x0e121df7, 0x964c34a5, 0xc1a3d109, +0x8cdf3cf7, 0xe782dc09, 0xf9a7162c, 0x7b56fdf3, +0x110bf2be, 0xdf55be6f, 0x4fee832a, 0x65be63f3, +0xbe5710e1, 0x7739d3fc, 0x5477bbda, 0x09a0beb7, +0x41ebc6bf, 0xeb0b59be, 0x3f7cfe1d, 0x12b5bedf, +0x2dbe2e0a, 0xbe22080b, 0xc037d2a8, 0x160669f9, +0xebaa5cbe, 0x8c40be0a, 0x56ccdd11, 0x02f5020b, +0xa0abbc48, 0xe496d316, 0x22b9ffff, 0xa080ea36, +0xb5c681ff, 0xa8bc506b, 0x848b17ff, 0x9f06e41a, +0x28708917, 0x5300a00a, 0xbc49a1f8, 0xe861b4fc, +0x15b1909e, 0x0844af83, 0x51f09c00, 0x860d041f, +0x49096fc4, 0x578de8ed, 0x3f1926c3, 0x508cf190, +0x4156da01, 0xec749d21, 0xb2a84d8e, 0x3bc0053c, +0xf7df2cb9, 0xe3fffb58, 0x9888137e, 0x14ca50ec, +0x88d12f5a, 0x2ad05066, 0x30c281c2, 0x2d7a3381, +0x02c3b90d, 0x2c44b76c, 0x5e5b37c7, 0x306b1d62, +0xb3409c64, 0xb00e0125, 0xf714db0f, 0x8d1a6208, +0xd6666090, 0x0f4c4c0a, 0x4bdf59f1, 0xfe9e8095, +0x3b77680f, 0x8097bebb, 0xb111eb27, 0x1280680d, +0x638326a8, 0x2ac7664f, 0x2b290000, 0x0fcd7e0c, +0x2001860e, 0x2995448b, 0xbbe79e40, 0x21941a2f, +0x9e3ee129, 0x22a23367, 0x561a0e6f, 0xb768a46e, +0x48ab24b5, 0x0c0d8140, 0x1c8ddfcf, 0xc5468bdb, +0xf631bc3f, 0xb51e048a, 0x20371d7d, 0xd038fa7e, +0xe8900b74, 0x714632df, 0xc24a2ae8, 0x6e27c36b, +0x866fe8ee, 0x8a012c4b, 0x3edb2943, 0x72381477, +0xc0b8ff2a, 0x3c4ed78e, 0x39a185ed, 0x740b3c4f, +0xf035ffdb, 0x3c0a776a, 0x4a0f9199, 0x6b74303c, +0x830d4c3c, 0x330e03ae, 0xf510a380, 0xf16dbc5a, +0x3c288146, 0x06c6b20a, 0xbb7b800d, 0x2f0b8b5b, +0x0a1f5d75, 0x200504eb, 0x638f47e8, 0x3e0a64dd, +0x24360a4c, 0xe2a602ca, 0x0d2051fb, 0x3beb24b9, +0x520b9476, 0xa1e82dee, 0x454cb225, 0x002b456d, +0x1b59e804, 0x46aeeeda, 0x43c74c06, 0x064e492b, +0xd3585539, 0x83d1a031, 0xe8441563, 0xc149266b, +0x7bba70ef, 0x7990e911, 0x063f422e, 0xba36e11c, +0x302da263, 0x74066e8d, 0xde26dd14, 0xa7188ad4, +0x200646c6, 0xb40dd7f9, 0x337b3e1f, 0x0e0bebd8, +0x0045c6c8, 0x0dba453d, 0xc35c8000, 0x19e34fc1, +0x255e7677, 0x96fc4082, 0x5e5c0da2, 0xd49e85da, +0x502c202c, 0x8901cb2c, 0x97034405, 0x4828c208, +0xef4c179c, 0xe243b4c2, 0x24ff25b1, 0x95617485, +0x36a5dd6a, 0x18cc6608, 0x4f42c35f, 0x2403d362, +0x8bd54620, 0xb93f0e80, 0x06437c7b, 0x10086bb8, +0xe90c38b8, 0xedd40498, 0x7d607402, 0x031c5707, +0xb36a0647, 0x114f2dda, 0x3a37e9eb, 0x17f8cc12, +0xe7c11b17, 0x148d1c3c, 0x5eecec07, 0x9c361125, +0x040342e0, 0x25c15d55, 0x2678e1c1, 0xcdb82f74, +0x43e953a4, 0x558be62d, 0x0e657a5d, 0x10429e42, +0x15230c3e, 0x066a1a5f, 0x0e28bbd9, 0x881aa370, +0x050b605b, 0x4ed3d688, 0x610201bd, 0xe8d0778b, +0x084dbab2, 0x61b44189, 0xdc8d6fcb, 0x203ae977, +0x03e0765d, 0x46c1245b, 0xe3d9a13b, 0x471ff909, +0x63e29ae2, 0x8a0ba934, 0xe0d3f189, 0x25b55637, +0x2f028d9f, 0xda234cc8, 0xc75354d0, 0x1d67c8fa, +0x30370836, 0x50750ee1, 0x470354e9, 0xfdd7bdab, +0x83244789, 0x0e456a67, 0xe15b8be1, 0x8559e21b, +0x7574cbe9, 0xf6c3e808, 0x3e0d6707, 0x5a5e389a, +0x0dc6374f, 0x47d2c308, 0x1dc53728, 0xbae08171, +0x6083f20d, 0xc11a0143, 0xb30b08e0, 0xbb4dc4db, +0x0c011c42, 0x0132db10, 0xec685c58, 0x06e01103, +0xe689e96e, 0x3c813ce8, 0x435f7b61, 0x12418d1c, +0x207044d4, 0xde92415a, 0x06ea2e8f, 0x32ec551c, +0x0c010102, 0x7ceca0b9, 0xc1c220f3, 0xf4897422, +0x02aa7037, 0x1ee1b852, 0x66025d39, 0x93117b81, +0x7b75024e, 0x1cf4841c, 0x89159d13, 0xe98263bd, +0x6b5214ad, 0x678e0ccf, 0x7ddb9274, 0x8bc20176, +0x1bf4858d, 0x3c736ee8, 0xdc29eede, 0x86350ae4, +0x0e956b04, 0x05ee108e, 0xf82a9c65, 0x2a8cc107, +0xf5b88181, 0xc729822d, 0xba615a41, 0xfc118a46, +0x15358350, 0x417c46b4, 0xf5777510, 0xba2cd047, +0xd389c81e, 0x8589f3f4, 0x7b354b64, 0x00876b81, +0xf149c86d, 0x15eb00bf, 0x0573f739, 0x3bd10e3e, +0x20ff7636, 0xbd39bb8b, 0xc42a020b, 0xff6818b8, +0xbe378cbe, 0xc24dda01, 0x528bd3bd, 0x684a8b1c, +0xe477c3b0, 0x03198e03, 0xd40c4ce9, 0x1c100ef1, +0xf1f17dcb, 0x87a04ec1, 0x01a6411f, 0x28e95c00, +0x64fb1865, 0xb84de81d, 0x2c4bb891, 0x10412b47, +0xec953329, 0x04a90f81, 0xadceb935, 0xd0cc8d1b, +0xa17a4062, 0xe79a4a44, 0x1bb0b7d8, 0xe7f06fdf, +0x9d8b24f1, 0xf081f6df, 0x1b0b448d, 0x01798de7, +0x4118d425, 0x14613bb4, 0x14d085ca, 0x5d6383be, +0xdf8b5d04, 0x41638b59, 0xf63dd7b5, 0x8b07438d, +0xdcc4e881, 0x103a8d72, 0xf63bb002, 0xe8318b66, +0x9bba16ad, 0xaf74c0d6, 0xc7282f3b, 0x220c1695, +0x0645b5ea, 0xe8a355ea, 0x5ee1caf8, 0xfbece99f, +0xa3104236, 0x3f1be380, 0x59e283b9, 0x17eb2c51, +0xc3199345, 0x4e00d7d4, 0x412143c6, 0x958a30e1, +0xd3716f53, 0x18eab41d, 0xe5767faf, 0x48b8c286, +0x03414744, 0xc256a5af, 0x77c033f7, 0xe81aeb46, +0x24521a5c, 0x1126fa81, 0x74fdc08d, 0x2b1ca1ed, +0xddf4cf96, 0x148438a8, 0xb735fcba, 0xca346df8, +0x1dda85b2, 0xb724eb2c, 0x51160db3, 0x5f0c9b3e, +0xebfa38fd, 0x004bbd9c, 0x3ba304a4, 0x2b910013, +0x3a6a4660, 0x2f1b6ffd, 0x598911c9, 0x693d6103, +0x1071890c, 0xb8147989, 0xe2f51f80, 0x8ba28b1f, +0xb6362d0a, 0x0762430e, 0x2572c06a, 0xc0a8a559, +0xefae147a, 0x7f00f853, 0xdf240ce2, 0x8b55509c, +0xb86c011e, 0x4ab71b14, 0xd39b186a, 0x76cb5013, +0xb091ffff, 0xc08ed88e, 0xe88ee08e, 0xec01d08e, +0xc39d585d, 0x86f2ea2b, 0x1ee82245, 0x041e7208, +0x3fcdbdcf, 0x29ec2924, 0xe01f2843, 0xb847db01, +0x258b3f10, 0x5a2dd45c, 0x7db9bc6d, 0x1508ca69, +0x89cc2958, 0xfc8fe1e7, 0x2f17b645, 0xc34a6405, +0x9800c183, 0x2b44731d, 0x0429b70f, 0x82513ca0, +0x07393cef, 0xe66dd8ec, 0x53358937, 0xea6730b8, +0x1780bf83, 0xe8b9f14c, 0x109c8793, 0x5dc07550, +0xb9586414, 0x24d0d248, 0x4d5f88b6, 0x244060a5, +0xede80687, 0x10ee6877, 0xb8ad6194, 0x4d165d78, +0xb8e007bb, 0x15615cd4, 0x6210e80d, 0x55e8f4a3, +0x8db12887, 0x1d57fd0d, 0x8158784e, 0xff525150, +0x2e558015, 0xb7fa20e2, 0xaa11dd7e, 0x68006a3f, +0x05e8030c, 0xa2475065, 0x2b90c395, 0x84ee12fc, +0x8c4e1c9f, 0x74c109d1, 0x63c22b09, 0xebdba644, +0xff2c9c17, 0x5d92d15b, 0x01534ec6, 0x01d211c0, +0x7ddb11c9, 0x85f16bb7, 0xe5f479d2, 0x3b3b2411, +0x05721277, 0xb77a2d3b, 0x0b7746bd, 0x0f190429, +0xdf11ce01, 0xb5bfd14f, 0xead101e8, 0x89eb0579, +0x75cd09dd, 0x23f628d6, 0x0406f028, 0x352db624, +0x89c79696, 0x5e2e516e, 0x88682afa, 0x57a87641, +0x6a357716, 0xaa0a78e5, 0xec1bfa83, 0xd148be07, +0xb8841ca8, 0xe0abd404, 0x087b8df0, 0xe249ecb9, +0x273a916f, 0x745b2be2, 0xd0428d7d, 0x06fc4b40, +0x0c8d1df3, 0x567983bb, 0x7db0bd79, 0x2104c50b, +0x7d0a066b, 0x6d89d010, 0x07bb6201, 0x3b2958eb, +0x6378473e, 0xfe16825f, 0xc7487659, 0x42eb2343, +0x3fb6036d, 0x8bf7e03d, 0xa8018d54, 0x2ddaa099, +0x5685dba7, 0xf81e2345, 0xa8eba3ff, 0x20b0587e, +0x0b8b1a27, 0xb00207eb, 0xdf1058d0, 0x7111831a, +0xc46a6453, 0x3b4a50f3, 0xbb03d128, 0xe92fbac2, +0x5436de4b, 0x071b02dc, 0x0eeb9fbb, 0x2878087a, +0xc64aa65f, 0x9364cc9a, 0xa9212994, 0x57be8a03, +0x43170100, 0x37db88d1, 0x047243dc, 0xeac11282, +0xe1e18b05, 0xea4c101f, 0xe3d36c9f, 0xd0901c23, +0x73906ee0, 0x039f1b67, 0xee6e1e10, 0x09a5409f, +0x8d06eb02, 0x7398df46, 0x78e74408, 0x3bb0ccaa, +0x682dea7b, 0xf3632683, 0x7f12e6d5, 0xc383f1bb, +0x8d5dc11f, 0x05ee0572, 0x1b74f339, 0x3802d507, +0x23dab5d4, 0xe281ffe3, 0xf0df3402, 0x8921ebce, +0xea6f8907, 0x708e2216, 0x0d269d14, 0x1310508b, +0x0aff6e80, 0x04c28343, 0x1eef7237, 0x722e045d, +0x0a1b52f7, 0xdf76a681, 0xedd30db8, 0xe098bb76, +0x8b624beb, 0xf19345a8, 0x61b15053, 0xee74e801, +0x8b99d2ff, 0x810fbdd8, 0x721aacfb, 0xae2517e5, +0x15ced16a, 0xec39335f, 0xffdd65f7, 0x270d81d0, +0xf4db31e3, 0x1dcbd0e8, 0x0f76ed91, 0xc0b60295, +0x4052c20d, 0x15d682d1, 0x9d34093f, 0x068a85a2, +0x41802e44, 0x13ebb00d, 0xb8dbb015, 0xa0d8c3ab, +0xc38d3f0a, 0xbcac6852, 0x8219b009, 0xe850d61f, +0x13098882, 0x2ba3f09f, 0x10170504, 0xc37b1cc4, +0x1bef2ca1, 0xc23a4f81, 0x06b4a415, 0x2c78111d, +0xd538509c, 0x5a61eca6, 0x591548b8, 0x89904516, +0x08d68dad, 0x107dd740, 0xfdb12e7d, 0x38347416, +0x30703b48, 0x2c982076, 0x3604c54d, 0x8980a758, +0x1d6adf5e, 0xd5fec2b9, 0x89b11e52, 0x41737621, +0x399ef50d, 0x6b0e4103, 0xf9f414b1, 0x01157ee8, +0x86efeb19, 0xe8e8bc54, 0x72550b72, 0x08708dd3, +0x155b54d1, 0xc04798ed, 0x5182d53c, 0xda6da0a6, +0x03e23052, 0xdb0bd522, 0xa31e2a59, 0xcf1cf6ad, +0x6c0eb5e8, 0xd749685a, 0x4a75cb78, 0xe4186bbd, +0xa4cb4160, 0x45886c1f, 0x5872bee1, 0xdbbb5756, +0xc70958db, 0x861d783d, 0x6e4214b7, 0xdd14b220, +0x46b2210c, 0x49287c28, 0x182a26c0, 0xd56010f8, +0xb585105d, 0x97e8f89e, 0xfc526ebe, 0xc2890969, +0x3e7f06f9, 0x1864d7d8, 0x3653f98b, 0x10fd082a, +0xff6d104d, 0xe9e24653, 0x5ca376a1, 0x60bac7c4, +0x4ead449e, 0x0c146e10, 0x7535a8e5, 0x3747b16c, +0x534ae31f, 0x6ff10804, 0x357f0432, 0x80088a40, +0xf17420f9, 0x51b410ff, 0x04921822, 0x01eb439a, +0x73aefc51, 0x19e41015, 0xddebf475, 0xb714d889, +0x32468691, 0x9e14afeb, 0x0aa144e0, 0xf18a40b3, +0x526c0161, 0xe8ce1719, 0x15232b49, 0x0fcbe05f, +0xd87cfb39, 0x581f3715, 0x58117710, 0xe818c031, +0x0c8e2177, 0x844a5230, 0xbbecfb9b, 0x028bb510, +0x4191bac5, 0x3e201a85, 0x70fc4446, 0x7294ba02, +0x9a471567, 0x6598216f, 0x80135d17, 0x1eede029, +0xda926513, 0xc0306c54, 0xc1488d22, 0x3d116305, +0xa50703c6, 0x1da8ba88, 0x3bf07500, 0xca61ba53, +0x9668f35d, 0x71b16c51, 0x3c558e87, 0x0ccc7201, +0xf679db78, 0xd0aada20, 0x081f3fe8, 0xebee5038, +0x0abf0b08, 0x8342e06c, 0x4701a7d5, 0x3704b47e, +0xf8754502, 0xa0015465, 0x505402c4, 0xa9ddd7be, +0x0501941f, 0xbb009470, 0xa1a952a0, 0x4eeb74d8, +0xc5017212, 0xd51fd516, 0xfdf81fcf, 0xe8988359, +0xef9d6856, 0xdf8c2a17, 0x2a0858fd, 0xea31f705, +0x1b0d6c56, 0x54045320, 0xfee311fe, 0x2001078a, +0xfebde8c2, 0x04c7ca00, 0xf0fd7fb4, 0xd19778f5, +0x93132ee8, 0x44323adb, 0x168511cb, 0xc15d722a, +0x0884c7b2, 0x47d88d25, 0x5cb368da, 0x23388003, +0xd11f592d, 0xe8dae881, 0xe8968c29, 0xc4420603, +0x8e110bc6, 0xb7adee17, 0x061efbe9, 0xf44d751b, +0x60e02d5f, 0x4c0f1703, 0x3505b811, 0x0eeaedfc, +0xe82f7f3f, 0xb0a8205f, 0x3fa6b854, 0x43e8193a, +0x35a52711, 0xc2563b77, 0x79082cc3, 0x39e86ac8, +0x6e915025, 0x72f03978, 0x89022bde, 0x3f1b89fb, +0x0d437404, 0x137dc1a8, 0x68088a53, 0x3a80ed10, +0x19d5332d, 0xb7c60064, 0x115e00d0, 0xf48aa41b, +0xc42c19a1, 0xb2df41ae, 0x08168204, 0x0b8ae6db, +0x178d0f32, 0xc0982dd3, 0x1448f741, 0x017880cf, +0x080be40c, 0xb8ccf142, 0xb6e409d3, 0x6acd5198, +0xfee80bde, 0x76395204, 0x3c13048a, 0xa04cdd3d, +0x60340ae8, 0xa8651cf5, 0x265758eb, 0x8391406d, +0x0f30a27d, 0x3c05a10f, 0x3de87f0f, 0x0977c13d, +0xe991000a, 0xb01a1175, 0x19428b61, 0xd5b9791b, +0x6d8047e9, 0x689d4229, 0x32338ac9, 0x3ee808e5, +0x0f46b20d, 0x6656db3f, 0x19130c2f, 0x462ce9c7, +0xbdfea9c5, 0x16eb10f5, 0xc12c652b, 0x893056fd, +0xb65de928, 0x8bae680b, 0x85e42f11, 0x75dd2fd2, +0x3136c05f, 0xd33f4378, 0xe3fa6853, 0x64e29b18, +0x6e3d83ca, 0xdd770a7f, 0x01387bf6, 0x8d092de2, +0x0d87471c, 0xfffe3446, 0x41528a85, 0xf7743a3c, +0x3a3bbe0f, 0x8d777513, 0xc7fd014b, 0x642be2ad, +0x74a18022, 0x2f60c21a, 0xe6585963, 0x1527ebbe, +0x1ba1273f, 0x3c002a2d, 0x1a754efe, 0x77c04308, +0x685716fb, 0x3dff1216, 0x080dc042, 0x5bb03abf, +0x362dc931, 0xebf89fa0, 0x1a118a51, 0x77ade407, +0x6de9c403, 0xa303be31, 0xf8a234cb, 0x3238be7b, +0xcb723f14, 0x7a7dcf83, 0x8914dc78, 0xdb4b6d02, +0xa126f521, 0x51838b91, 0x11f45a58, 0xa2cd050d, +0xf877ce6f, 0xa3aa0420, 0xc0b0fa72, 0x472e1b16, +0xddcbb251, 0xa2345153, 0x0add0ca9, 0xa8e154ea, +0xa5b0b320, 0xb4bc6c4d, 0x71d02904, 0xf31f8f72, +0x76c1b143, 0x56ad10a3, 0xc30e0302, 0x29d5580a, +0x2916c47a, 0x8a2cf14b, 0xf6e1897b, 0x32abe031, +0x92bd0411, 0x0b8182a8, 0x8ab0b8d2, 0xcba36bae, +0x27ad04f9, 0x2e90603a, 0xff8ae830, 0xd15d0eab, +0x51d00d16, 0x204a7545, 0x1ba0b315, 0x01609899, +0x8998aa9b, 0x56a2add5, 0x721d8099, 0x9e3f0466, +0x83ca4634, 0x7c40b1a0, 0x00638a9b, 0xf1a61100, +0x0ec003b5, 0x9857e8b4, 0xe4622f83, 0x58bae0fb, +0xc1c8470f, 0x7cf10aa0, 0xb0bb370f, 0x54423833, +0x13315ecc, 0xd176f4be, 0x171179c2, 0x83c72547, +0xe9d1bd1e, 0x93609d54, 0x80d3e8fa, 0xeb15e806, +0x30006fd0, 0x4f801ed5, 0xa09e17ba, 0x457a661e, +0x10f212f5, 0x77681ea9, 0x3a13160f, 0x18f44f10, +0xb40d6950, 0xffd983e9, 0x683ac31f, 0xb8ef9bab, +0xa31e8830, 0x164ac547, 0x4289ae22, 0x701b625a, +0xa1280cc6, 0x30810d5e, 0x40078dd8, 0x15601014, +0xf115da0e, 0x9b3c46e8, 0x9693938d, 0xab7ec9be, +0xc7a77ce8, 0x509c79c0, 0x37a5ff70, 0x47c632c9, +0x504d8b38, 0x8d26578d, 0x9db7d074, 0x55751628, +0x83a024e0, 0xeda83983, 0x2a18544d, 0x658b14ac, +0x3d4fb66b, 0x2a37587d, 0x1b581e02, 0x2eb0628b, +0x6c181d37, 0xba4e8114, 0x10325c65, 0xc6805b0c, +0x9337c5de, 0x4ceb86d8, 0xc6a6358b, 0xfe1ea854, +0x10be8d12, 0x23a55db6, 0xe12d4362, 0x63e80d86, +0xb150d62a, 0x6502a184, 0xc152ff47, 0x1c4dfec1, +0x0132968d, 0x79fdb8c3, 0x7ac7a004, 0xc348e907, +0x4170d889, 0xb6113e18, 0xab410452, 0xebc16368, +0xd2c5eb4b, 0xbc7e08cc, 0x34502419, 0x83d1deeb, +0x8380a278, 0x5bbe59e8, 0xd1f17d7a, 0x45e3b4eb, +0x7b06bda2, 0xc8dfbb62, 0x0cc6c81c, 0x20c8fe81, +0x7349e672, 0x5783c6ed, 0x4840f604, 0x67631139, +0xebc2b85b, 0x4934743d, 0xc22d6b08, 0x8ba11744, +0x160a469c, 0x01711da3, 0x473ae8ef, 0x726a3456, +0xb69412dd, 0x102918c6, 0x88607881, 0x15ca30d5, +0x3b804c00, 0x76e45475, 0xdce9a014, 0x446a1714, +0x1f90d38e, 0x189cb3e5, 0x45484e42, 0x201f88d3, +0xb340b6f7, 0xa1220f14, 0xb66c7830, 0x893325de, +0x531c0815, 0x8875106a, 0xe8a91436, 0x743aae24, +0x740014d7, 0x94158b60, 0x5cf93063, 0x1f94312a, +0xa4d41f90, 0xe889019e, 0xf603a5a8, 0xd02f03e4, +0x15e2e8fe, 0xa24456f4, 0x80ec3122, 0xe8d2df70, +0xddf400ef, 0x43c2c1a9, 0xfaaf2fe4, 0xa382ce10, +0x25be466f, 0xdd0e817a, 0xfaec6fc1, 0x06ec280a, +0x1cd0105e, 0x003b7e80, 0x16bdd136, 0x1ef37a42, +0xf405468d, 0xe8160458, 0xe05c176c, 0xb87e83be, +0x62740f3c, 0xe2a13641, 0x755b378e, 0x9d508300, +0xa9dc37d0, 0xe82c2349, 0x3da577d1, 0xca68716e, +0xb7bb3726, 0x0213ff09, 0x4f22f878, 0xcaf3c4d8, +0xd46c177d, 0x7528343d, 0x29b0bb24, 0xaa617e1f, +0x520308db, 0xc859ac02, 0x15fa8b29, 0xaded25e8, +0x12000122, 0x0583e7ab, 0xbb277432, 0x983b0ec0, +0x8d030efd, 0x35ea7348, 0x2cf6081f, 0xf3909081, +0x9a07e830, 0xa162ddd1, 0xa109f807, 0xf527441e, +0xd80a2a68, 0xc3cd3171, 0x0a70978d, 0x2b0c48cf, +0x3ad1390b, 0xf887570d, 0xc3d5fbdf, 0xfa1e4f30, +0x52a02d56, 0xe918e2c2, 0xa08d0268, 0x393eb116, +0xc5b154a5, 0x583340a7, 0x2cfca403, 0xefb04362, +0xcfe80dba, 0x528962d4, 0x14d86cc1, 0x5004aa01, +0x378743f8, 0xb082b443, 0x57005a3f, 0xa0310b89, +0x08e8d781, 0xdfba57b3, 0x7553ee18, 0x737b51e9, +0x8653a418, 0xb23a58a6, 0xea5346e1, 0x541cd75c, +0x41a216a2, 0xb3939e18, 0x29d7ca13, 0xb44bb729, +0xfa5bac7b, 0x5f2a112b, 0x901a15e9, 0x00704c90, +0x3103178c, 0x688a61ac, 0xc18b1600, 0x09220331, +0x22327cc2, 0x2b104186, 0x3b788360, 0x39cb89fe, +0x478ac744, 0xe2c183c5, 0x1b142740, 0x9416287f, +0x16d44706, 0x2b7a855c, 0xd6d66013, 0x0392a00e, +0xbe57347e, 0xfaa049c1, 0x01d7e806, 0x7af9df13, +0xa2a25c46, 0x10044627, 0x6b51abe8, 0xc6b0a178, +0x89843b04, 0xd1adda2e, 0xd525d412, 0x1204465f, +0x2e500d8d, 0x7c80f15b, 0xb16aba09, 0x48df0d8d, +0x01e3aa13, 0xf7603342, 0x595a627a, 0xc4f4d558, +0x78a2d76e, 0x6804b4e8, 0xb6967d59, 0x9a763e02, +0x106b48bb, 0x75411bc1, 0x83b3a7b8, 0x76597512, +0xcbabf527, 0xcae7725d, 0x18e8166c, 0xa8d83fba, +0xe8d85aa4, 0x0947c511, 0xedd104b5, 0xeac0850e, +0x20a1c9cb, 0xab13037a, 0x86e80184, 0x78a90700, +0x8df75bd1, 0xd0b8ae78, 0x834a960d, 0xb01d3bca, +0xfc6fc27a, 0x4feb5411, 0xd9f72e8d, 0x438bd121, +0x800dff04, 0xfe29c6c5, 0x2c8d3b78, 0xe05aa20b, +0x04e8534b, 0x3d231374, 0xd115ca09, 0xdadc1f27, +0xa2d41785, 0xf6e9c1a4, 0x25a80c9e, 0x29120a71, +0x45bd7c3d, 0xca2d881c, 0xeefb811b, 0x5cf824dc, +0x7844a975, 0x728d7099, 0xdf7de2fe, 0x89f0e67c, +0x200d4cc2, 0x598b2b63, 0x151a3902, 0xe759b3aa, +0xa968adb7, 0x12151933, 0x2d893306, 0x41d4465d, +0xdcca5408, 0x8b58de79, 0x564f6aaa, 0x413817cd, +0x5684a09d, 0xb46b7603, 0x012dd89e, 0x44013042, +0x35bd3fc3, 0xe283ef8b, 0xfb7de9f0, 0x86d512ba, +0x38b8064d, 0xfae9e915, 0x1889a80e, 0x4957d327, +0x83a5db11, 0x9e1ceb34, 0x6826b604, 0x6500ad06, +0xd11286bb, 0xfa306d02, 0xdb5535dd, 0x77fd2a4b, +0x6ffc0727, 0xbd10c877, 0x0846282d, 0xf2c339d9, +0x21e38452, 0x85609b2d, 0x31e2a934, 0x46d0690f, +0xa604a2f4, 0x9856aca7, 0xe9c0f745, 0xd1570889, +0x15b01423, 0x57db2098, 0xe08b9bd9, 0xaea36745, +0x2830428d, 0x83f748fe, 0x147717f9, 0x5278e79a, +0x74dc8370, 0xc3c02806, 0x204385ee, 0x4a958a8c, +0x2b2980f7, 0x0e90312a, 0xdf4b5b16, 0x80227db8, +0x663230fa, 0x17ebeabe, 0xc8d48a43, 0xeb6007c5, +0xe9783c20, 0x4306cebe, 0x03b50ab7, 0xccf7496a, +0x3f2f60fa, 0xf876b7db, 0x12eb57ea, 0xeb374009, +0x03773908, 0x2fffc0df, 0x73f23930, 0xc6af0f08, +0xeb43d001, 0x890757d5, 0x3003eb1f, 0x0a6c4560, +0x0cd5ab8a, 0x07c29ba2, 0x241c8b29, 0xd0148b3a, +0x955c8ac0, 0x0280b819, 0x74d8397d, 0x7765913c, +0xd31beadf, 0x097f02ff, 0x2a752e17, 0xe6c13340, +0xea031408, 0x47d609ed, 0xc5750413, 0xaa85ed61, +0xc1e70106, 0x150410c8, 0x89c15671, 0x15023045, +0xc6820d0a, 0x00c8b280, 0xe570369f, 0x7322bc83, +0xf4a8441c, 0xff13d7cf, 0x07e5f8df, 0x6f51da49, +0xe8381100, 0xeeb47052, 0xb1ee100d, 0xb9e80af0, +0xa7485808, 0xe8ddf06f, 0xf0292249, 0x0842403d, +0xa6681276, 0x84e95e3d, 0x2a1c2edc, 0x4c065b84, +0x35fa0d89, 0xba744eaf, 0x36562721, 0x6deaa657, +0x02268b71, 0xe01b0008, 0xee1c0f2b, 0xc02ede00, +0x60bb4720, 0xd9ebf42a, 0x91a3a247, 0x5849e376, +0x0f90903b, 0x9b0498dc, 0xdc98bbac, 0x85edae1a, +0xda3339da, 0x433902b8, 0xa0360bea, 0x1080ea03, +0x0c940850, 0x4f16f504, 0x78de72e8, 0xe0c3f6df, +0xeedb1108, 0x9e3c0004, 0xdade1801, 0x12ddb380, +0x3776f65d, 0x56f8f289, 0x668b03d4, 0x2a04bb25, +0x9c55dc47, 0xe813e2cb, 0xe5b7d0d0, 0xe074e0c6, +0x1e183470, 0x884c51bb, 0x0315a82e, 0xc4462b65, +0xb6d05749, 0x182f87cb, 0x1254be1b, 0x78af86cd, +0x4817df16, 0x02f02faf, 0x585fc889, 0x7476a2da, +0x1e3efaaa, 0x1d11744a, 0xa463e278, 0x118b23eb, +0xbea1ae11, 0x098eeb78, 0x7100ff4b, 0x8b044986, +0x5657ff0c, 0xc7c42440, 0x75e0ddab, 0xd0a16d3b, +0x10509268, 0x5021d3c4, 0xeeccc8e7, 0x534598df, +0xe80eddb0, 0xf7f5dfe6, 0x134583a2, 0x8d0e288a, +0x1c309640, 0xaee8e10c, 0x4114e588, 0xd8009de0, +0x60cd9885, 0xeb307e09, 0x65cb7f1b, 0x4564f044, +0x02dbe883, 0x8a0adc56, 0xb0eb9a0f, 0x87bffd21, +0x04be3908, 0xe6d3d988, 0x2e743785, 0xd482c3b7, +0x4f8e5e29, 0x392c772b, 0xeb6ad68d, 0x70752c42, +0x1810fc7a, 0x56c67400, 0x119837c3, 0xbf13fc44, +0xbf7520cc, 0xed1ee4e8, 0xc031b175, 0x25a16293, +0x3ee8309f, 0x905b6b6a, 0x02890842, 0xddb5284a, +0x62b82354, 0x9e3b1043, 0xa040c474, 0x1446deba, +0xe04456eb, 0xf85a3617, 0x6d62cba8, 0xbdc67ac5, +0x89c35789, 0x25edc2e8, 0xfd8345ba, 0xbbbdef77, +0x41c8d6f5, 0x7c650f6f, 0x3d9c880f, 0x86e28905, +0xfba4e8a3, 0x1e2eddc9, 0x3bce21f4, 0x437eb8db, +0x74148568, 0x40c710ec, 0xcb408dc5, 0xa1fbe8e8, +0x892c7bb9, 0xd21a3002, 0x884c6e03, 0x05e81b1c, +0x1a8e7ff6, 0x621473b0, 0x21753bb5, 0x5930e139, +0xe8743fe8, 0x6b62d0ed, 0x5cf8468c, 0xd9a30f61, +0x189ff012, 0x30456a0b, 0x89f53960, 0x84916fd5, +0x2c8f1ce8, 0x15eb198b, 0x1603c407, 0xf768e827, +0x85a34554, 0x6c0e1bc2, 0xb13783c1, 0xcbbc8e04, +0xcdbd9bfa, 0x2106c3e8, 0x9ab11108, 0x6b40b866, +0x022dc10d, 0xb061e8b2, 0x617b44b0, 0x43180c5e, +0x416e0cf7, 0x8c5bc805, 0x2bc7394e, 0x9306aa1a, +0x79e88dc7, 0x701d1ca2, 0x039fc204, 0x80010314, +0x75103fbb, 0x6342c211, 0x3ee6a9bb, 0x20247e01, +0xc19392eb, 0x3dd8a16d, 0x06e50de8, 0xa3555ab1, +0x62d2126a, 0xf78942c1, 0xe305f955, 0x1f7e8bd0, +0xdd76c8fd, 0x0de7f4a1, 0xd101c705, 0x1cfb9503, +0x0845d789, 0x9d5a8039, 0x8af20c4c, 0x8c8c8800, +0x08031d26, 0x2b195176, 0x172740d7, 0x6d13abd5, +0x2ecf22ce, 0x604d6819, 0x253f1bee, 0x150c74c1, +0x402b1766, 0x37595404, 0x85c51634, 0xc60be97c, +0x42742d90, 0xd2006563, 0x3048091c, 0x8da035b1, +0x8f4ad64a, 0x94112d15, 0x82d14bfd, 0xcd28da86, +0x901ef901, 0x26836355, 0x245420f9, 0x8d1aa29a, +0x6d05ae42, 0x901151c0, 0x68ad19a6, 0xecdbdaa4, +0xef349620, 0x0d1bd220, 0x6e47c7f4, 0x7d832647, +0x8f808e44, 0x03cf00a2, 0x0c25fdfb, 0x20698751, +0xe857036a, 0x00b1afe7, 0x0920a062, 0x80b6319c, +0xbace8016, 0x1ea23259, 0xe9ddf88b, 0x0008f548, +0xbf5043ae, 0x7f52c512, 0x01698860, 0xba45418d, +0xd6d075f8, 0x95f44825, 0x43400e6a, 0x19406285, +0x442b3438, 0x6ff8478d, 0x0e13fabe, 0x7818fb9b, +0x106724b7, 0x18a12f20, 0xff8e2555, 0xaec41691, +0x47c0ead1, 0x03301644, 0xe8daf73a, 0xff960b0d, +0x0e5bb19c, 0xefea7f30, 0xb217f4ce, 0xc1aa2c60, +0xd6015e8d, 0xa6d9cda7, 0x5148b8d5, 0x2462f852, +0xbe1d8d11, 0x32277099, 0x08373507, 0xad5bad09, +0xefc42b76, 0x20681d20, 0x1874941b, 0x6de237b7, +0x0e549cb0, 0x36bf7be8, 0xdd154443, 0x0f01bc08, +0xda8e1c1d, 0x82cdb780, 0x2e989f33, 0x457e009f, +0x5f423e14, 0xb15da95d, 0x89ee8c04, 0x881d2dcf, +0x13fb3a60, 0x811de8b4, 0x734815d8, 0x16ff8008, +0x4985b87e, 0x22df7f34, 0x33304887, 0xf916de88, +0xe8f48810, 0x45f856b8, 0x9f9ea284, 0x06588d43, +0xfa41b935, 0x1dea439b, 0x6b70d8c0, 0x054a248d, +0x735b703d, 0x483a700d, 0x747ec00e, 0xaa8ef50f, +0x92985aa7, 0x4b82f66c, 0x9bb61558, 0x12423037, +0x9af71a78, 0xd4566337, 0x13d1846d, 0x98d88230, +0xcc3104d0, 0xb92b435e, 0xf92dc41d, 0x4344be51, +0x6e782624, 0x014d1281, 0xb84f518b, 0x3d6b6219, +0xfde351fe, 0x3af305f6, 0x28471559, 0x90f1fcf8, +0xec1dffb9, 0xfdbaf902, 0x7449ecc9, 0x7440a804, +0x5f15c3f3, 0x420d6620, 0xecf8b2f6, 0x68b7ec11, +0x757f12cb, 0x1708b002, 0x0aa891d8, 0x03e7ea23, +0x15ade038, 0xbd20a878, 0xebee602a, 0x69b81b0d, +0x753dd52c, 0x97e2754b, 0x3ff083b0, 0xfbbaddf1, +0xaab0ee22, 0xca2df8b9, 0xaa3cecee, 0x5f6e4b75, +0x07596bbe, 0xb0437555, 0x75c8fe01, 0xf9b11e3b, +0xc1906d6d, 0xa2272f1b, 0x7dbf8407, 0x1fc2bf07, +0xfbb203b0, 0xeef9b2ef, 0x0247fab2, 0xbaeefcb2, +0x7f75a03a, 0xfdb2ec4a, 0x6bae75a3, 0xea310188, +0xff093b2f, 0x1e082713, 0x4c6fa2f5, 0xd2209a8e, +0x3d50f510, 0xe415f8c7, 0x736009e8, 0x96146884, +0x0485d26a, 0xb568044d, 0x003f51d1, 0x8d166a81, +0xbf541314, 0x746a5dd0, 0x09a03c23, 0x89015a8d, +0x6fff7ae5, 0x8baeeb7a, 0xed830c6d, 0x0c558d0c, +0x1541395a, 0xa395354a, 0x86028d0c, 0xffe26080, +0x800e6353, 0x7db8c2ef, 0xc30950e9, 0x196886b1, +0x9bbeb5a9, 0xd5578f54, 0xa0891c5e, 0x5cbad6d9, +0x132dabf6, 0xb61516ab, 0xc6216246, 0xf279fec5, +0x6dbf8d5b, 0x53eb75d9, 0xdd714602, 0x56dec5b8, +0xd2aa0c30, 0x0a49a973, 0xe69cc553, 0x534560f2, +0x4014354d, 0x7c546ad1, 0x42a8add0, 0x63d995ff, +0x4721dd4b, 0x326a316a, 0x6e786163, 0x0aa3aaa5, +0x56dc7f13, 0xc372c3b9, 0xcebb338a, 0x002f255d, +0x27460c5d, 0x077e432b, 0xa8da38b9, 0xbe4c277a, +0x897156b5, 0x0689b66e, 0xf08b0eaa, 0xa9780154, +0x1c14b61a, 0x8171582e, 0xcb394dba, 0xaa5ac8a6, +0x36e2c1d0, 0xa5e845cb, 0x9a11784b, 0x053e82de, +0xd33ec129, 0x032323e8, 0xa743b244, 0x4b7222e2, +0x240383a0, 0x29945aaa, 0x8be03780, 0x544d086b, +0x83afe8ba, 0xce1bda05, 0x0c98204d, 0x409e16e3, +0x758bdcc0, 0x6ba5143e, 0x1921cc14, 0xa50b3921, +0x157fb600, 0x5e393f76, 0x6c2f07da, 0xc33c4502, +0x7be7df75, 0x7c062c02, 0x3835b7d7, 0xa5ebeb89, +0xc5267f77, 0x6a462643, 0x10788d10, 0xa1221d0a, +0xfe91e922, 0x6b61195d, 0xc30afdd9, 0xd133102d, +0x09d01ba9, 0xe22e1a4a, 0x8096270d, 0xee72c157, +0x88f18856, 0x1b11d285, 0xe0876724, 0xdf136282, +0xd8d0155a, 0x6861045b, 0x142d12c8, 0xbb16b039, +0x2d10dac5, 0xe7662a96, 0xcd019264, 0x75de5baf, +0xe8151559, 0xe8e44dc6, 0x3fad05e1, 0x7978bf29, +0x5bec6589, 0x66d01d5e, 0x536d38e2, 0xba281cb4, +0x21616f0a, 0x16a236a3, 0xbae4820b, 0xa8a9068a, +0xa13906ff, 0xc73780da, 0x4732048a, 0x00516107, +0x05ebfd4c, 0x89db69ba, 0x11b702b7, 0x0383d169, +0xdda0f829, 0x3650badd, 0x06e80501, 0xffc7018a, +0x12b7503b, 0x5d39ffef, 0x73ad75f0, 0xf230fb89, +0xcbae86c0, 0xd91bfd84, 0x151de125, 0xffff7951, +0x0fddd12a, 0x1903ab1c, 0x4d3e085a, 0xb9d549f0, +0xfcb1ea63, 0x4d50ced4, 0x0c63005e, 0x46f8dd52, +0x0a570e8a, 0x7627bc57, 0x15ccb11a, 0x008488b1, +0x2fd1be96, 0x6f4a3b0e, 0x8b128b09, 0x5275a3a4, +0x41e3229c, 0xd4df46d7, 0x5dbb9645, 0xe83eebd3, +0x3d683c88, 0x35eb38b5, 0x7a10a929, 0x118cdb52, +0x676026bf, 0x8b919bb2, 0x6b0771ca, 0xf1a209b3, +0x74d50fa9, 0x05546ec9, 0x439517ab, 0x976f77bf, +0xc8e9c098, 0x8918458b, 0xf14dc3e2, 0x415eaaff, +0xa2daff80, 0xb06dc1a8, 0xe378fe51, 0x7bb696eb, +0x29d037aa, 0x651756f1, 0x4a5b5964, 0x6994ea01, +0x92b4e088, 0xe8763ce0, 0xe80107a6, 0xa8ddb29e, +0xe6897114, 0xf1714026, 0x9b3b7637, 0x370d4f99, +0x184cf189, 0x81b6942d, 0xf0a05e41, 0x19c5bbcd, +0xef7c5f00, 0xa0445136, 0xce25cd8e, 0xe65f6bf0, +0x16c2e8a6, 0xe87609ca, 0x1118f7de, 0x5a201ba1, +0x84682908, 0x94d036e9, 0x09084374, 0x8cd2228a, +0x8d716566, 0x5c4620b1, 0xf5fa4753, 0x6857f44b, +0x06f5bbac, 0xe80337ff, 0xa051064e, 0x085ea20d, +0xb2dd463b, 0x568df47d, 0x05463e0c, 0x0482114a, +0x141da907, 0xfafa0743, 0x83c6528b, 0xc0c24bfd, +0x13e2e920, 0xc80a0000, 0x5b1e0015, 0x244c6f8d, +0x5589d8f0, 0x657920d4, 0xab5f1059, 0xc7cff1dc, +0x40213ee1, 0x01718dfd, 0x459b418d, 0x1100c7d4, +0x89f047ed, 0x337d8b56, 0x0ce1743c, 0x20ba071e, +0xba09f1d8, 0xd8386cd8, 0x2f4bb6e3, 0x527ee8bd, +0xf302cd1c, 0xc608ebf6, 0x3a2bde11, 0x3ab800eb, +0xe7eab11b, 0x45b40c00, 0x0aae34e0, 0x8ab5d011, +0xf0f2c1d6, 0xe5ceabc0, 0xb07d0295, 0xfb800bd3, +0x38bedf04, 0x46167264, 0x3b758c1b, 0xce745728, +0xef64e6c6, 0xdffec1fe, 0x76ebe872, 0x7d89a58a, +0x8b4103ec, 0x32ebed52, 0x140ec0b2, 0x4142c680, +0x77f6d28d, 0x3f76d85b, 0x095bef0d, 0x5abc5515, +0xe0db110c, 0x0bd4022f, 0x3da8fa80, 0x58df0480, +0xceebf0ba, 0x284ee0cf, 0x2027b4bb, 0x13f6add8, +0xbc922213, 0x361b6b01, 0x836599f8, 0x8b6ae772, +0x63085932, 0xf5076970, 0xebb1891b, 0x52eb5697, +0x0f84c3ac, 0x3ee2cd0c, 0xc4164a8f, 0x931310dc, +0x61076a29, 0xcbd3e789, 0x4462db56, 0x44d01177, +0x04a8530e, 0xcf13680d, 0xb1b3b064, 0xd330c328, +0x06884a2c, 0xad0de74d, 0x8cb1b7e2, 0x148d87a8, +0x9d326490, 0xe2cf4310, 0x93c442f1, 0xc183c805, +0x26114012, 0x0cb60234, 0x808342c5, 0xedfde8ed, +0x0be38846, 0x1cbdad12, 0x17753a07, 0x1a4790c9, +0xf2d7c761, 0xeb4e1ee4, 0x3ac71c0b, 0x154565b0, +0x00f07913, 0x66f68098, 0x48536177, 0x7d92c145, +0x0d5eda7c, 0x8102410d, 0xb07650c0, 0x78ee2230, +0x3cfbbae8, 0x5d16aaa5, 0x7543c132, 0x8ba0a4c5, +0xc4f65c42, 0x034552ad, 0x520fc2ef, 0xf17fc161, +0x88038a0c, 0x43420461, 0xc264eeeb, 0xc1ed552e, +0x75332feb, 0x940539fa, 0x8a53ffb6, 0x2acb8808, +0xd7db0f1a, 0xbb5552b0, 0x324003fb, 0x754151ca, +0x02ebff77, 0x168a4346, 0x0b8a12f9, 0xf0510c1d, +0xe0c0b85f, 0x38df2fdf, 0x83e674c8, 0xe1badfe2, +0x8ae9bb1e, 0x29d20713, 0x84c667d0, 0xb7d8eac9, +0x5540350f, 0xf275d138, 0xee4a4075, 0x680d0288, +0x29f47919, 0x682b42c8, 0x71d558f0, 0x6de95645, +0x0213f081, 0x29c22901, 0xd558820b, 0x4e4143f5, +0x4b85eddd, 0xd96dbf00, 0x42c6ebff, 0xd8384201, +0x70a0d0e7, 0xd8576bc1, 0x10602dba, 0x2d0edbcc, +0xc06d2add, 0xcb82ca39, 0xec03b7f1, 0x59aab787, +0x180419df, 0x41434100, 0x72e8e020, 0x18a4b4b3, +0xe962bf54, 0xc38914be, 0x12ffd6f8, 0x380b041f, +0x39480782, 0x31f773d8, 0x0bc65bc0, 0xe85a2542, +0xab9b6ada, 0xf5eb0d0a, 0x622deb37, 0x8926eab0, +0xcacdf2d1, 0xc13c158f, 0x208d240c, 0x434ddf47, +0xfdca41f4, 0x7d242c3b, 0x2b7831e9, 0x032b3ca1, +0xfdf47e28, 0x05c72deb, 0x1374ec09, 0xc5ac6cff, +0x1a064c9a, 0x0c0c179c, 0x623bfdcb, 0xe172743d, +0xe8c3feeb, 0xc100ffc4, 0x423887dd, 0x86eee88e, +0x2906e7e8, 0x80b6020c, 0x16f553d8, 0xe8451340, +0x08ef029f, 0x448d558d, 0xe0cc0a1d, 0xc221ec26, +0x16f00c17, 0x418a2cda, 0xbda9a849, 0x1c681c11, +0x07e95f1d, 0x64c010eb, 0xfdd04ed4, 0x089addc7, +0x8bad8a98, 0x526aebd6, 0xa21ebbc5, 0x3cf2eb93, +0x8e890b25, 0x3275f123, 0x036a6656, 0x8a6819c2, +0x747041f4, 0x4118ae08, 0x806a6bad, 0xc243ea09, +0x42b31833, 0x8de2652b, 0x0b83063c, 0xd897500f, +0x758df4d8, 0x4304d2ff, 0xbfa7c202, 0x44fc0115, +0x0c760181, 0x6d0817e8, 0x0be96ec7, 0x8a29f5db, +0x2999a607, 0x2ac04574, 0x24048be0, 0x211f4bc3, +0xfb8a2416, 0x2dc648eb, 0x05a17beb, 0xca46f7b6, +0x194bbf91, 0xda072476, 0x9f679444, 0x1476c71c, +0x5edeb877, 0x0f742db4, 0x0a745f04, 0x7efa88ca, +0xd0b40075, 0x72511ed8, 0xd6a74253, 0xbafff1b8, +0x29c60110, 0x00558ac3, 0xb575ea45, 0xc0c01d6d, +0x137ae1be, 0xdf29e0a0, 0xd674f889, 0xc42ba0ac, +0x2b430e74, 0xc55770d1, 0x418b27e9, 0x2c1a3a0c, +0xb1beef88, 0x81680871, 0xe8555236, 0x8ce20752, +0xcfc058e0, 0x50a60841, 0x12edb390, 0x302187e9, +0xb541d150, 0x98437ebb, 0xf7459b40, 0x54471084, +0xbb8077b4, 0x8d5846f5, 0xe8061d44, 0x77cb2a05, +0xa19116a9, 0x1a20c483, 0x9ceed883, 0x06e41390, +0x188d681d, 0x5c8205d9, 0x187637cb, 0x4e423955, +0x1c06af1e, 0xb66c868e, 0x2020230d, 0xb0bb6d41, +0x2420cb93, 0x204c418f, 0x2e69660b, 0x53931828, +0x21e8a957, 0x892d1e67, 0x5c4d41d8, 0x799b0348, +0x75482d68, 0x34e8e394, 0x0c3e92e8, 0x0244424b, +0x602de93a, 0x284f1316, 0x9e2cc6e0, 0x235aaf88, +0x23516305, 0x0a897250, 0x13a02f89, 0x5828a03b, +0x3115acef, 0x151d19b5, 0x89f82970, 0x708d7672, +0x742f3801, 0x6bbc10e1, 0x0c704ece, 0x93ebd3e9, +0x6fece4c4, 0xbaf32fcb, 0x3121363f, 0x8c83dca2, +0xca8abf24, 0x409f1284, 0x3ce8de80, 0xe802738d, +0x6dc8d55a, 0x2efae43c, 0xb7d12fd2, 0x8df1b002, +0x07f00153, 0xcfe3d789, 0x0a154125, 0x00ff40c6, +0xa258b7da, 0x20361054, 0x8540ba14, 0x3d672766, +0xe97a23a6, 0x12b0ece4, 0x198ccb14, 0xaad0000c, +0x1df1a4d5, 0xd7ad63d8, 0x6fe81e21, 0x031cb1fa, +0x765e204b, 0x1e26dd56, 0x8be75a88, 0x3e2c1f19, +0xf64656a8, 0xdd060466, 0x61c02811, 0xd8fc06fe, +0x46a9a260, 0x644fd3f1, 0x9b548589, 0x44b71351, +0x3a800f85, 0x7e10b52f, 0xa91c0f8e, 0xb78be96f, +0xadda4ec1, 0x7704eb97, 0x3218a850, 0x2f3c32ab, +0xf63bb5ed, 0x750e2711, 0xd4fd300b, 0x068020d5, +0x3f3cb3b1, 0x2cd10eee, 0x4696d85d, 0x234b0402, +0xc2cc3b80, 0x46030d44, 0x2f4480e3, 0x5930dc41, +0x53a18b6b, 0xa4e25250, 0x7bf59388, 0x05796560, +0x649c4c24, 0xed56ad5b, 0xd0fa4562, 0xf0f97a83, +0x35825445, 0x41880153, 0x66ce01dd, 0xab42e089, +0x4c36e141, 0x00a2b425, 0xa91b3c47, 0x66751627, +0x0858e9e8, 0x73a9850c, 0xc1536d83, 0x2843c32f, +0x2d1aebc8, 0x056b8106, 0x7cc40dc5, 0x54db010d, +0x048eeb16, 0x8a703119, 0x3c7d4a85, 0xb1a22f71, +0x44c8d820, 0x822c0ee5, 0xd8e045da, 0x6808482e, +0x10d42f15, 0xdf86069d, 0xb7b411c5, 0xee86fcb6, +0x80308bd7, 0x198d5bdd, 0x50ee22bf, 0x0e04520f, +0x3cf3cdb7, 0x400b0c0d, 0xc9b71a0a, 0xa825a8ae, +0xfc53db51, 0x02dc08a5, 0x96a1979d, 0x83b1c08b, +0xd6e85940, 0x09282ab2, 0x0cb86f05, 0x28df0441, +0x9baac718, 0xfe894bc3, 0x77bbfd56, 0x808a0fca, +0xf00934c5, 0xac0f0388, 0x947a04ca, 0xab8b625e, +0x89e1a509, 0x9781dce8, 0x4b592fe0, 0x403003c6, +0xf57cf63b, 0xf6f0d6f6, 0x83d09112, 0x88c458ce, +0x5661ff43, 0x1b9ace01, 0x036846e1, 0xe4834507, +0xeaa91a43, 0x37280310, 0xcf010dcb, 0x7ae9a502, +0x80602583, 0xbe85004b, 0xfeabb2d0, 0xe948136e, +0x31461365, 0x3c068aed, 0x6c5a2623, 0x04ea82f1, +0x4652303c, 0x9eda0195, 0x2c357b21, 0x6e0fa016, +0x6b103dd0, 0x106b0a12, 0x3adaa1d0, 0xe5226f03, +0x396e8eb9, 0xee493068, 0x0a976fd2, 0x41066c3c, +0x7a3c09eb, 0x1891b99a, 0x6edb60dd, 0x30c6e420, +0x01162027, 0x02717563, 0x7b44bd3a, 0xfcf28b04, +0xd568886f, 0x100627f3, 0x177573fa, 0x7c543008, +0xecd21b1a, 0x57bb0db1, 0xe909d6bb, 0x75701bc6, +0xbb143613, 0x49265502, 0x1ffb226a, 0xebbe4da1, +0x583c1434, 0x018a3475, 0x9076073c, 0x4114bed1, +0x5b8b0bc1, 0x6ef47a04, 0x3d916805, 0x2ddb310b, +0x2fbc46f0, 0xd5092026, 0xd91f3155, 0x3fb383e8, +0xebfa8ed8, 0x92645471, 0x2d4f7569, 0xe1415bd0, +0xf0d28513, 0xdaf70752, 0x4efd53ff, 0xbd4b993b, +0xf799d0ee, 0x30c283fd, 0xd43c06c3, 0xea3a1388, +0x338a046e, 0x2d42edc2, 0x60d0f76e, 0x20982804, +0xca96145b, 0x13883604, 0x41b9dfa4, 0x263e7568, +0x736c6062, 0x268da027, 0x50fb165f, 0x682b815f, +0x04824fe2, 0x04466803, 0x782ccc8c, 0x483bbea3, +0x3406730c, 0x88081488, 0xd3c0d822, 0xf77bcae9, +0x4c96a996, 0x94a2b5c4, 0x38aa65e0, 0xf41c62d5, +0xa217437c, 0xcbd52655, 0x308ad039, 0xd150b82c, +0x94000e7c, 0x0ae412f6, 0x02e830d4, 0x643ad231, +0x1528162a, 0x760aa1a7, 0x2ca410ed, 0x4497cc55, +0xf73906d7, 0x5a6fd447, 0x222e6b94, 0x986143b4, +0x74ef11c8, 0x6c14d1d3, 0x2340572a, 0x5d011997, +0x13203c01, 0x29aa479a, 0x3c1dcbcc, 0xb8e9d00f, +0xa5bb4059, 0x1ec39045, 0x48a2b506, 0xdd62b704, +0xea5a025e, 0x29104b1f, 0x7d14561f, 0x9c53e83b, +0x5b0ede3f, 0x604a355e, 0x0bc80450, 0x17be0dac, +0xe7ede830, 0xde153201, 0x6c5181a9, 0xf8b7f089, +0xa21b6811, 0x51f030ec, 0xd06c68e4, 0x25b1acd4, +0x40d550cb, 0x084a19c0, 0x9e508c6d, 0x550257d5, +0x8505f122, 0x255257ff, 0x92ebf8a4, 0x62c2e42e, +0x104e9edb, 0x4e4bd360, 0xfb68b93f, 0xe7a0c7e9, +0xd6398c6c, 0x704b1934, 0xe69fb390, 0x320c51ff, +0x4a57c30c, 0x6b0f0052, 0x280064c3, 0x96c82e04, +0xbe0b341b, 0x7dadb806, 0x0362a1f1, 0xe13cce80, +0x08d90ef2, 0x7fff69f0, 0xe6b19c5f, 0x426cf64b, +0xe6a26904, 0x1d9ce49c, 0xdeba0061, 0x6f415356, +0xe08c6ce4, 0x643d08dc, 0x182303b9, 0x1547e1c3, +0x42200258, 0x0ddc0f21, 0xe62329c2, 0xc7d7e62e, +0xd030562c, 0x736fcb9d, 0xb814cbf9, 0xe51eb802, +0xa1adc76a, 0xaed94289, 0x8bb32ae8, 0xc01c025f, +0xa21dfb29, 0x790cc0c3, 0x4610a973, 0xa1b756ff, +0x02470540, 0x13ddb8c3, 0x5aa30001, 0xbb03866f, +0x394a6ca0, 0x4d71046b, 0x9f00db74, 0x0e4e2712, +0x12fdfad9, 0xf4e8e244, 0x20389ef2, 0x2c20fb81, +0xbe842608, 0x31da7225, 0x8e0c0adb, 0x0704b1aa, +0xe8d0dda2, 0xffa4d09e, 0x47177406, 0x5c144512, +0xd620e970, 0xd1cea690, 0x1344faac, 0x96f95858, +0x6f149ddb, 0xfecc1005, 0x55a8e9ce, 0x83e90dc0, +0x50140c40, 0x6edc0a8b, 0x723d20c2, 0xc28dfc14, +0x8a020093, 0x42881246, 0x8a34040d, 0x8a0ac563, +0xdb512808, 0x05100e41, 0xf6856b35, 0x8b63fab5, +0xee2f0329, 0x400735b3, 0x236c3c24, 0x2bc144b6, +0x1c0e9800, 0x07dcc6dd, 0x1909e820, 0x04157b32, +0xd10cca0f, 0x012d62c0, 0xdca5e02b, 0xde44146d, +0xb998c8a5, 0x17632700, 0x248603d0, 0xa2476caa, +0x5b1050ad, 0x4492d2b1, 0x17da0c41, 0x36e444d8, +0xcc24447d, 0xa03b0c58, 0xce548b96, 0xbf8b0f5f, +0x1687f444, 0x4239661d, 0x08c6fe0c, 0xecd8fe81, +0x1676a8c6, 0x4c85e972, 0x1620792d, 0x1e051bc1, +0x0b473b66, 0xddf0bbf7, 0x538a1326, 0x82573a04, +0x430b1a85, 0x8ba1f605, 0xef3adad8, 0x6b980a0f, +0xbd467bc3, 0x0d4c4295, 0xfe381a00, 0xa1076d90, +0xebc90a18, 0xc04b1f16, 0x9a12b46d, 0xbf1e0b87, +0xa04558ef, 0xfdf7e2fe, 0x6d7c70f9, 0x7214fa11, +0xab10d708, 0xb465a1a0, 0x6f1c889b, 0xa1480644, +0x8fd80580, 0x031a3b8d, 0xb226e2c1, 0x13a34013, +0xc40fbdf9, 0xba89017e, 0x468d5f12, 0xf7ec7b52, +0x4b50a4be, 0x15545405, 0xda061500, 0xaadca8d0, +0x75970413, 0xef000b5d, 0x1d02c289, 0xa154058d, +0xc201d380, 0xffbc1490, 0x8b3b780e, 0x86078aef, +0xe2aa4606, 0x819c52f8, 0x75b92080, 0xa126ee42, +0x504827e9, 0xe8c78b7b, 0x78136b2c, 0xb5d0c415, +0xa3188135, 0x1498d980, 0x1580e03b, 0xcd89e335, +0x05046d4d, 0xaa59a52a, 0x6c4477f6, 0xeaf11854, +0x45118a08, 0x0c45be0c, 0x0776fd3c, 0x237f83b6, +0x8d0ac843, 0xcb290248, 0x8bde3178, 0x174151aa, +0x2822d50c, 0x74fa397c, 0xd1ff592a, 0x0b6a2d42, +0x85ed886d, 0x770bd687, 0x67810075, 0xb8c1cb46, +0xec12f411, 0xafce0102, 0x6a89bead, 0xfd04da0b, +0xa2fd5ae7, 0x7c0d68d4, 0x1c97a24d, 0x5a37c151, +0xd1130384, 0x516f6321, 0x72478d3a, 0x51cd42b8, +0x72c5cdfb, 0x010f78c1, 0x5f5668c6, 0x881a00ac, +0xbed13573, 0x388da247, 0x5030e5ce, 0xb83a031e, +0x3003b540, 0xcb3bd927, 0x4b8a9a76, 0x508a049d, +0x98a32602, 0xadab1441, 0xd7020b91, 0x8e386a28, +0x34030c04, 0xd75c1385, 0xb6e74a80, 0x80108972, +0x1be980ad, 0xc2bb1004, 0xa7104c0a, 0x0b3d811e, +0x180facea, 0x5bfeb5d8, 0xc027843a, 0xeb6d0a79, +0x90cac2d7, 0x8a7b98b1, 0x14c74802, 0xeed70bca, +0x3afe4041, 0xce08eac1, 0x08c01f04, 0x424b2d1d, +0x17fe0211, 0x016a347d, 0xcb02d4b9, 0x75158e80, +0x88d54d5e, 0xe0b6ddf8, 0x41183e46, 0xc5457964, +0x815f18dc, 0x14b20363, 0x7c248f81, 0xbb4d8581, +0x7c2b098b, 0x2203fe55, 0x8db6923b, 0x19767778, +0x740034bf, 0x4803746a, 0xc35dc1da, 0x895d9cb1, +0x14180840, 0xb7b17800, 0x14904743, 0xf8019874, +0x68d8443d, 0x2a37ad86, 0xf82f6001, 0xba205028, +0x3c8d7029, 0xaa903e0a, 0x29682e3f, 0x348d43f9, +0x5deff902, 0xeb626407, 0xcbfd9554, 0x1b0656eb, +0xce8e85a0, 0x8a88e83f, 0xcb8a1026, 0x20051d7d, +0x098bf748, 0x54e8a528, 0x75a1b5f1, 0x6a54bea2, +0x034978a9, 0x770171e0, 0x30f46a11, 0xf29ba3d8, +0x46e8515a, 0x6b2847c1, 0xebd37499, 0xea1d31b1, +0x01a5600a, 0xe889f66d, 0x027ec820, 0x13006a41, +0x32062f03, 0x0d4aa124, 0x02080954, 0x402e77f7, +0x9fde41bd, 0x4211e3fc, 0xd040680e, 0x7a7bc402, +0x1819b8a0, 0x1b06c293, 0x440d5310, 0x05f6c922, +0x3cf3286f, 0x030404df, 0xcd890102, 0xe3a05a93, +0x652c2070, 0xe19609f7, 0xb824f783, 0xd074c40c, +0x5adab5c4, 0x055b28a9, 0x2882138b, 0x46855b40, +0x46a540f4, 0xd3b83cd0, 0x6ba045b8, 0x8b0a0c16, +0x56c0317a, 0x83c69105, 0x06170090, 0x9f08d83b, +0x29f73e2b, 0x0100c0b6, 0x580c7be0, 0xcf056b42, +0x28fd1c7e, 0xa22581dd, 0x2a0cd1d2, 0x9118900b, +0x90503681, 0x0699427f, 0xcbd3fa83, 0x31d6201e, +0xfdf8baea, 0x00f3e737, 0x59dc01a2, 0xaa9ba45f, +0x7b6f4b46, 0x1776f139, 0x8272ebe0, 0x3ceaaa4f, +0xe95d51e9, 0x2aff63c3, 0x53ef8f54, 0xffffa013, +0x06b1402b, 0xc512a284, 0xbb1b26c6, 0x5a5cb0c0, +0x5ae6dc4e, 0xe5adef22, 0x2bc46fe1, 0x6774c339, +0xa4dc23eb, 0xc8891d7b, 0x729fc960, 0x98205c6a, +0x89fa7919, 0x055963f0, 0x8b85077b, 0xc3d3f61e, +0x513006c3, 0x01c8b0ad, 0x68b702b8, 0xc621b882, +0x0a254440, 0x7147fa3d, 0x42bc8082, 0xdbb43d54, +0xa7a360d7, 0xc3065676, 0x45ad582a, 0x5c440bf4, +0x0676d139, 0x557a0844, 0x5c3b500b, 0x5da23757, +0xfaf0a7da, 0x50023958, 0x1700ebc8, 0xb6b5a845, +0x45ce9600, 0xab030ff4, 0x053038f5, 0x2972028f, +0x09186423, 0x054fd73c, 0xa02846b9, 0x06442701, +0x39552880, 0x8471a461, 0x8085ba02, 0x37df0c41, +0x01880a71, 0x75d295b0, 0x04b1752c, 0x4a1debc4, +0x44669bfc, 0x472ea141, 0xb1ac7603, 0x45ae8ac7, +0x293dde44, 0x401ba166, 0x2d0d5bb8, 0xf4a11dd8, +0x9e612c0b, 0x7440c6dc, 0x4a20ec14, 0x7db15240, +0x98e78b41, 0xdc5f83fc, 0xb82c4aa0, 0x97920364, +0xd380b25a, 0x79662117, 0x871b13ba, 0xfecc1663, +0x52e87fae, 0xc6202a70, 0x0e406404, 0x1a146620, +0x62d8bc2c, 0x06068741, 0xaa14150f, 0x1774f841, +0x510fa5b7, 0x5009c608, 0x1d140d03, 0x56b90a37, +0x8745fe0e, 0x17e8c26a, 0x2dd2282e, 0x5ebba368, +0x614115b8, 0xeb005210, 0x580d29d4, 0x2350c6ec, +0xd3595a1b, 0x5fa54510, 0xd1d7768c, 0x0ad16858, +0x9f4e043e, 0x6b8b6309, 0xfad057d0, 0x5493e856, +0xd588bac6, 0x9d18ee03, 0x3b0c303c, 0x51d90fb6, +0x9d2bf140, 0x5056539d, 0x1e06aaf8, 0x4d506d61, +0xd27810e5, 0x048c296f, 0x6cba5ecc, 0xe8a3685e, +0x92af00b5, 0x727f85a3, 0x14e90408, 0x8370708d, +0x2510707e, 0xbdc3160a, 0xc2d11d74, 0x5c16f256, +0xbd18a08d, 0xee86444c, 0x600b8dea, 0x4443bbf7, +0xd5f7ecda, 0x09fd00c3, 0xf7c5211e, 0x38512dc5, +0xf8baabe2, 0x502803eb, 0xf4458ebf, 0xe90c2519, +0x66518161, 0x0a6f0467, 0xe3e86444, 0x130a99d1, +0xae08c11c, 0x81b1b8e9, 0x1ab150c8, 0x2d4a0168, +0xbe83454e, 0x1549f76b, 0xefed8fbc, 0x10688102, +0xd506bfdc, 0x33b82370, 0x39ced7e8, 0x1af83af8, +0x5b70582a, 0x5cbe5c10, 0x20846dbb, 0x028e0855, +0xe11a744e, 0xfa0aa985, 0xfdcb140f, 0xfc7e0837, +0x0140c602, 0x050240e0, 0x8306ebb4, 0x05b6d87b, +0x7edb70a2, 0x3c31efa3, 0x56891bd4, 0xb957086b, +0x2db7a937, 0x022df652, 0x0a034035, 0xb0d45273, +0xfe68e96a, 0x20285558, 0x318278c6, 0x08423b19, +0xc35f1442, 0x6702af14, 0x8ec95989, 0x22c190d1, +0x334c435b, 0x749775aa, 0xaf8a5840, 0xb5ada42a, +0x61f80341, 0xbb04640c, 0x0db60827, 0x0eab5c1e, +0xf8568bcd, 0x761f80f1, 0x75b865b6, 0x46614b8d, +0xd74497e1, 0x5710c783, 0xa8577827, 0xaa6ec596, +0xc102eb1d, 0xd854c20c, 0xfdf7f033, 0x7a47404e, +0x04b90e55, 0x883352fe, 0xb2c7af22, 0xffa41834, +0x4c682bd5, 0x7650683b, 0x2f1a6ff2, 0xd534446c, +0x4ce27fc6, 0x83d721c7, 0x660bc1a0, 0xf4287470, +0x27f042c8, 0x4bd35060, 0x8f45e854, 0xe16809e1, +0x01a4558d, 0xccaf145a, 0xc1bfd85c, 0x7b0908e7, +0x271feb44, 0x0538241d, 0xed82410d, 0x85105c84, +0xf20288d6, 0x0a22c961, 0x4b835837, 0xc508def4, +0x10cd0512, 0x01a0af8a, 0x07f32338, 0x376acb1c, +0xa9631b40, 0xc74d7559, 0xb3ab5209, 0x48060d1d, +0x5ea24dde, 0xef31c448, 0x12bbcf78, 0x367b3ef1, +0xe8b9ef66, 0x10b1169a, 0x02b4638a, 0xd0dd52f6, +0xc4f6ea24, 0x4c533d02, 0x3501c242, 0xfef03b25, +0xab44909b, 0xd6a36d82, 0x48024446, 0x7a02705a, +0x595ceb3a, 0xe0ac2e19, 0xfcc2cb4d, 0x9099ffff, +0x3f561c62, 0x85d280b8, 0x2ba0d2bf, 0x58f80019, +0xf10710d4, 0x9c961c15, 0xd82b5adc, 0x615f251e, +0xd7004806, 0xf4f01575, 0x7f4cbc4c, 0xf0260289, +0xc76cfbb1, 0xf8168c83, 0x7620e1a7, 0x9a06a21c, +0xff2a99ff, 0x2eec412d, 0x02575204, 0xbcfaa090, +0xda9dc28d, 0x489fe831, 0xf042f8ea, 0xd449bb15, +0x20f4158b, 0xa1097c08, 0x5ca30f49, 0xdda8f041, +0xd81f3d07, 0x10428125, 0x6d706227, 0x09622ea8, +0x01d40eaa, 0x84e4768d, 0x1875057f, 0x407140c7, +0xebd4cbba, 0xb3f1fe10, 0xd23e52e9, 0x03fbbee9, +0x7891e030, 0x72b215cb, 0xb3688b44, 0x8829c413, +0x7f07cbe9, 0x480df7a8, 0xf0250c45, 0x99d61c3c, +0xdd37fef7, 0xe4e31c87, 0x8f22f883, 0x510fc839, +0x87dfbaaa, 0xc3b72a87, 0x2c8595e7, 0xc9053d8d, +0x33451274, 0x32a2b01d, 0x53430f18, 0xc18c546c, +0x4541ed10, 0x5740592d, 0x8501245a, 0xc565a5c1, +0x78340875, 0x85ed0e45, 0x6c474482, 0x06880d45, +0x16e8478a, 0xac145519, 0x44ec1044, 0x1d87414d, +0xafd52822, 0xeb6660e0, 0x3c27aa27, 0xbe0a3746, +0xeb420373, 0x3c06571a, 0xbbba8c84, 0xcbeb0ce2, +0x0504eb28, 0x305a0f2c, 0x3bc27b7a, 0xd3723a54, +0x0a2bad8b, 0x71bd11a2, 0xa0354cc3, 0x1b2beed1, +0x8610b0d3, 0x8de93305, 0x62035204, 0xbebf06e1, +0x76c73bff, 0xf702bbb4, 0x4283b60f, 0xad7410a8, +0x132a6c44, 0xfc6f47f9, 0x9d8ba0a2, 0x11351105, +0xae1a851c, 0x3074b7eb, 0x29ab2bf6, 0xa05f6c00, +0xf63c4c6c, 0x150b4643, 0x7d507614, 0x2c425345, +0x884e1400, 0xf9a6141a, 0x4ba50a24, 0x13d02414, +0x196c5681, 0x3d16013a, 0x8ff9abba, 0xf7f4875d, +0x0448453c, 0x74503f74, 0x12a62b67, 0x3b2f6c0d, +0x0f552b34, 0x6a42f682, 0xc7ba618e, 0xebf7fab3, +0x9b60f91a, 0xe5e90bbe, 0x29587366, 0xdd4370ee, +0x7aec15a3, 0x72fe3979, 0x0036b82d, 0x7fcbfc56, +0x0f41f096, 0x0cf2448c, 0xbafb849c, 0x692b3820, +0x8870e329, 0xc10b6802, 0x370329b8, 0x15742d88, +0xb1dc05c3, 0x00cc3922, 0x75546ee4, 0xb71fb71c, +0x3d278edc, 0xe86ff741, 0x3b466ae0, 0x41702221, +0xf044f7db, 0x26057241, 0x85f2733c, 0xa85631cf, +0x7439a96d, 0xeb3b60e1, 0x285860fb, 0x22291b12, +0xe0d23111, 0xb07c05cd, 0x0bb3970f, 0x6de02c47, +0xdb258f8c, 0xe90fe03d, 0x361c0078, 0x9fb96eea, +0x6e5fb928, 0x70bea5f6, 0x0407544f, 0x95d12a42, +0x72289a84, 0x9b8b8c30, 0x660bd02e, 0x29661e1d, +0xd21eeb2d, 0xb20c151e, 0xc4112120, 0x008e8b39, +0x27d3d893, 0x7276ee52, 0x2ad72120, 0xb20b00a1, +0xedc58319, 0x743c3966, 0xc3f8d411, 0x8b392400, +0xa84df0d1, 0xc184e017, 0xf9ff310d, 0x11b54834, +0xed511822, 0xf4b0b11d, 0x791468fd, 0xba33497b, +0xa8476374, 0xca6e1748, 0x730a0975, 0xdb680d7d, +0xf0d25aeb, 0x5a1e2c3d, 0x36709715, 0xee2c7b8e, +0x9b0636ee, 0x4d5e893b, 0x0247c7da, 0x2b1822b1, +0xbc2b0374, 0x060504bc, 0x54264403, 0xe2a6f00a, +0xc383da4d, 0x89945306, 0x55e0baf9, 0x64678a12, +0xb60d5f45, 0x2dbd9b51, 0x761ee848, 0x03488ecf, +0xe04f21e0, 0x70c74810, 0x9bed5bf0, 0x7d785813, +0x7dfc75f8, 0x47d3a6f0, 0x55044643, 0x485493b8, +0x42e8f152, 0xa90f0fcd, 0xbf2f0248, 0x3a788d7e, +0xc08d000f, 0x83124310, 0x8a12c150, 0x1087ac3c, +0x94ba28e8, 0x1c3aed7f, 0x04438528, 0xfa1274ee, +0x51830441, 0xff4aca74, 0x381f2129, 0xc92ff042, +0x1e210019, 0x0f6fbc70, 0x1b0004a3, 0x01026003, +0x05f74a8f, 0x41141c07, 0x2c155ec1, 0xa7297b62, +0x34b52140, 0xad2e2623, 0x0bd638c8, 0x6fe5708d, +0x07fa835a, 0xaf432fd9, 0x7d9a8d88, 0x16dd36b6, +0xe011ff9b, 0xd8ebd739, 0x22bc16ed, 0x1574d6a7, +0x60012a1a, 0x7fc9c1aa, 0xc1b1a304, 0x0335bcf4, +0x0c058b5c, 0xa05b375d, 0x6d890a57, 0x079b030c, +0x51470b58, 0xe963ee57, 0x3b081c8b, 0x3e6005b2, +0x896cfd68, 0x0a06941a, 0x6b8d2e0f, 0xe8247020, +0xbb831fd6, 0x79e31cb9, 0x8fbe4cdf, 0x0f25a78d, +0x032c44ba, 0x1f166414, 0x90540923, 0x0342eba4, +0x51a2d8c3, 0x76d8c68b, 0x32181d08, 0xe98b6f28, +0x720260b1, 0x1c6274e7, 0x1034360f, 0x6d85d1c3, +0x8b6c88ea, 0x163b0da5, 0x41f03cbf, 0xde0a410f, +0xffbfbdb1, 0xde38c710, 0x0affffc7, 0xd2043102, +0xb92072cc, 0x4bf06db0, 0xc9e494e0, 0xd29e8100, +0x1ee00bf7, 0xf22c5de3, 0x60a1c3d3, 0x3b6697e7, +0x64a1bd28, 0x142a6808, 0x04527410, 0x2bd4800e, +0xe879d8e1, 0x0a6dbebf, 0x480a6e1a, 0x858b65d7, +0x8c8b6844, 0x21d999aa, 0x186e0c9d, 0x8b78f052, +0x4c2648bd, 0x7ddc43c3, 0x124095fa, 0x63d5bee8, +0xbae07844, 0x02d82df7, 0x349d8b01, 0x6d41629e, +0x771b560c, 0xd8f29803, 0xe287ec2f, 0x264831b3, +0x0c8d1add, 0xe1004207, 0xc629f83a, 0x47802d56, +0x6f1b80a1, 0x48fd8315, 0x6eff750e, 0x215163d4, +0xa4988d73, 0x3a2ac40b, 0xae89361e, 0x013200de, +0x81ea6a4d, 0x1f7a3afd, 0xba058582, 0x9f308806, +0x2500907b, 0x0ad486c7, 0x2ec4c6e8, 0x3b86bf88, +0xf3ea3905, 0x0d828001, 0x532a8757, 0x2f5634f2, +0x93515a36, 0x8286c7b2, 0x0248dd81, 0x90862263, +0x3593ec0f, 0x40a014cb, 0x048b1c10, 0xdee60c24, +0x861190bb, 0x03996c94, 0x11668417, 0xeaab284c, +0x7d0ebc1d, 0xe93fe808, 0x6070bfe3, 0x6384dd90, +0x89d48306, 0x6d011809, 0x1ce411b5, 0x3283786d, +0x548e02b3, 0xeffe3936, 0xf03f63c8, 0xc8fbe814, +0xf3e81cd5, 0xebe85007, 0xa36c7911, 0x5807e37b, +0x9f22dbe8, 0x0741116b, 0xaa844a42, 0x49c12bdf, +0x64b4c8bc, 0x54109842, 0xa3508534, 0x1b859be1, +0x0d813001, 0x537c3d87, 0x18788335, 0xc1407400, +0x6c21100f, 0x4b80370b, 0x02454301, 0x43784655, +0x4085cfc2, 0xb54d8589, 0x78407632, 0x460bdf0b, +0x9c85531c, 0x96121e25, 0x63856406, 0xa8c4bd8d, +0x10eb9160, 0xea3800a5, 0xf1da68af, 0x80033003, +0xa0314ee8, 0xfdab9452, 0xa4620aaf, 0x15e84c9b, +0x0506dddb, 0xb6dd38dc, 0x2e19fdf2, 0xa22307ba, +0xfcfb1d11, 0x40eaaee9, 0x9d253a5c, 0x292bbcf2, +0xec5d513a, 0x57f8587b, 0x96d30514, 0x31274c21, +0xed12c660, 0x48971412, 0x242d384e, 0x84764201, +0xcd1971d0, 0xdb9a465d, 0x27dcc806, 0x156d7009, +0x91c60c85, 0x1d8ee023, 0x1284dadd, 0x9d44c8c6, +0x7b515862, 0xe0862136, 0x3b84514d, 0x4940f687, +0x85004a01, 0x4b56328f, 0xb854b7e0, 0x8351ca25, +0x44065628, 0x8360096c, 0x9505ba7e, 0x51a90856, +0xdc18d7a8, 0x888256db, 0x50025101, 0x0a0f5b68, +0x79aba258, 0x20e4d624, 0xd6ff8160, 0x46f628c4, +0x001d2e49, 0x48e70018, 0xa0f0d61b, 0x01144f68, +0x0b5d5306, 0x229643b9, 0x76606a1f, 0x6d80cf23, +0x1025ab41, 0x8b683170, 0x86e450c0, 0x8445b686, +0x3803fa89, 0x1ec6ee29, 0x5213fce4, 0x47234ce8, +0x84be3b4a, 0xaac542ee, 0xd1724f07, 0x3603ee75, +0x2515bfd9, 0x74086f26, 0xf7476a6b, 0x46d45ac8, +0xfe710c64, 0xdc8e27e3, 0x74be8223, 0x0b0f6137, +0x6d6437d8, 0x73e3d8bd, 0x837b0361, 0xd9751165, +0x46f6226a, 0x70b5c8b3, 0x006a2f74, 0xeeee9e01, +0x7fd56801, 0x3455dd29, 0x2c158034, 0x84384182, +0x467ac748, 0x7b808d59, 0x49292404, 0xaa2008c7, +0x4a2a83c7, 0x8337e940, 0xd98a431f, 0x140d8955, +0x880ffc80, 0x66094142, 0x45b11011, 0x03ffc620, +0x400842c7, 0x8b232700, 0x69fb8883, 0xef3e9039, +0x83422d83, 0x96427517, 0x42834203, 0x3db0661c, +0x4f9cd547, 0x50603fc3, 0x8b3c0cba, 0xf0683646, +0x39ee8981, 0xc027b014, 0x68958831, 0x0e143ccc, +0x835d7588, 0x76ffb801, 0x0e2ac9ce, 0x5faaff83, +0x2a8b7c10, 0x75adc301, 0x0dca891a, 0x554c2026, +0x0247d950, 0x9c008ff4, 0x00e8128b, 0x33ffff8c, +0x71cbd40f, 0x16e6e01e, 0x70018d89, 0x83e893a4, +0xea36ea30, 0x64288896, 0x8a630935, 0x3b755630, +0x080bebd2, 0x171c4c44, 0x3fb7a5ad, 0xe541832c, +0x6c19a2c0, 0x3d0eb1bf, 0x1bee740f, 0xd88aa128, +0xc28938dd, 0x0aab8007, 0xa8ddd8f7, 0xe1329752, +0xbadc0b03, 0xf0003ed5, 0x48e91012, 0x908dade2, +0x883f08f5, 0x9d08c022, 0x82e232e9, 0x8a565b98, +0x155b1c3b, 0x3a8e2b9b, 0x7f74df65, 0x1f510c1f, +0x19878d4f, 0x1528688b, 0x4406830c, 0xb7011d12, +0xdf86eb77, 0xdf0bcbc1, 0x6e006f81, 0xf16880f1, +0xe2b5aff2, 0xac1ec6a9, 0x878b64ad, 0x2e5d50e1, +0xc881b145, 0xea011218, 0x025d5598, 0xab8aefeb, +0x97876138, 0xa89f8bd2, 0x086f47be, 0xa87b0fb7, +0x1d466eb4, 0x50405801, 0x0f40750c, 0x2be63450, +0x07dcb1f2, 0xe9361da8, 0xb6582241, 0x5e82c355, +0x6cc71826, 0xa3c147d4, 0x08ae195e, 0xd5da1f0c, +0xe854868d, 0xeec43924, 0xaebc2682, 0x3de82035, +0x6081500e, 0x6d00a23d, 0x42c620e2, 0xfb8d3613, +0x2680ae8b, 0x277c22ee, 0xc5680977, 0x8bc3d007, +0x35231443, 0x6feedfe6, 0x330fb585, 0xbe07c4bf, +0xbf3b0b02, 0x877eaec0, 0x8b0391c1, 0xce25d445, +0x1b0ad7bf, 0x0426fb10, 0x9a07ef42, 0x08addc85, +0x14a2bf46, 0x70e46b12, 0x777aab05, 0x002c37c7, +0x8f600b9d, 0x8958efd7, 0x817044b3, 0x85748078, +0x40680371, 0x83b623d8, 0x40772797, 0x85a0bac4, +0xc7168d6e, 0xabbc2c20, 0x1f127800, 0x37429824, +0x402f6833, 0x63811cee, 0x8bd47c48, 0x75c35408, +0x3427ea2b, 0x2245a82a, 0xf3be180e, 0x29e0c906, +0x66694342, 0x29160876, 0x81fe0f18, 0xe43401b7, +0x1943e885, 0x03f0f6f4, 0x42147f68, 0x232801cc, +0xa00fdd90, 0xe00295ee, 0x028a0c0e, 0x7a0a476e, +0x1802ba84, 0x1f753cbd, 0xc35e298b, 0xc18725f6, +0xd81385bc, 0x30bdd13d, 0x02aae9b8, 0x03c5856e, +0x8820fd48, 0x88ce7c19, 0x01ccc5a5, 0x4c002ced, +0x99553ebc, 0x9dae7de2, 0x8455ac23, 0xd433d680, +0xd47e3425, 0x27db682e, 0x14640235, 0x82840aa0, +0xee4906a7, 0x68ad1a88, 0xa7528319, 0x5d1eabbb, +0xdb84aa51, 0x5410c101, 0x4d75177c, 0x368b1108, +0x0f243489, 0x91b81c41, 0x0d71e9aa, 0x7363b753, +0xa0066950, 0x14e4e991, 0x02df05bc, 0x538a5018, +0x3c121425, 0x6c6a3a03, 0xdec7f01d, 0xf2ba25e2, +0x5edf8376, 0x16fa802c, 0xfe067722, 0x13eb97ca, +0x62dda20c, 0x077704f4, 0xfdb80b85, 0x5123be06, +0xeb87be88, 0xedbe0605, 0x2216f801, 0x95907f30, +0x80405601, 0x2579c709, 0x61009331, 0x28ce98a8, +0x2f10bbb3, 0x07484569, 0x0814eb34, 0xc510000c, +0x538bca07, 0xe8560cf8, 0x6c67f10b, 0x832aebfe, +0x57ce3a7d, 0x958a19b0, 0x492d4c26, 0xb7f0836d, +0x66da0e43, 0x28ff8867, 0x33a89936, 0xa0011fe8, +0x4a938445, 0xf0b38f2b, 0x51a516d3, 0x8ac295a4, +0xe3160704, 0x21a62c23, 0x8dd57592, 0x6618338d, +0x41797d05, 0x79802003, 0x28107c02, 0xc9cc88d4, +0xc26050c3, 0xdc0321d8, 0x07b5b699, 0x86f6d7d9, +0xd9f69075, 0x6c448ad0, 0x430cfac3, 0x09c885c7, +0x4432a82c, 0xb097e983, 0x7c519318, 0x619d908d, +0xdb2324e1, 0x982c940f, 0xb07d9d00, 0xf4eb070d, +0x6fff2796, 0x1be93a74, 0x41c11e02, 0x033a9324, +0xc6e2265c, 0x7c296891, 0xedafe937, 0xc2715ade, +0xff09e89d, 0x3c248d59, 0xd4578701, 0xfe2dcddd, +0xa17f1340, 0xd0e0a307, 0x680db805, 0x1c9f0004, +0x806e520f, 0x86f0303a, 0x46c7017a, 0x0978b6f4, +0x8dd34680, 0x0a0e8395, 0x538b6670, 0x60c6709e, +0x8055a824, 0xb7c042a9, 0x71ba6805, 0x83071088, +0x77a102c3, 0x4f7e1670, 0x468d0794, 0x2150a678, +0x27a73dc8, 0xedcae24b, 0x059aadb6, 0x18b61c03, +0xb113a5e8, 0x7c5a1741, 0xcc808648, 0x056a48ab, +0x73ef6216, 0x951311ce, 0x11150a2a, 0xd85d5988, +0x3351b800, 0xfa111964, 0xf8acefeb, 0xc1a88802, +0xa91ea059, 0xdd9344af, 0x73e82a03, 0xd121d785, +0x04e1df81, 0x4f768a07, 0x202cd051, 0x35c78999, +0x2341b1f0, 0x8d3f45bb, 0x34457875, 0x45da8246, +0x5c23e0d8, 0x938a8f27, 0x9723c800, 0xd10d116d, +0x547ef594, 0x628a1301, 0x60174420, 0xefda9105, +0x8b028d26, 0x51622ef8, 0x6589c855, 0x218aa7cc, +0x41200114, 0x6fa43dd4, 0x5d8ad02a, 0xa369a2c7, +0x8e84e740, 0x3c0332a3, 0xf0bf8970, 0xc6f1753a, +0x5c894313, 0x6f42d495, 0xa3057505, 0xd497046f, +0x63d136e8, 0xa0039b14, 0xdc127527, 0x120e8c55, +0x4dbd4fc9, 0x0edde2a9, 0x0cbc06bb, 0x4e825f1c, +0x40df9f00, 0x778a1687, 0xbd21dbc0, 0x202d9ec8, +0x89251760, 0x32e65d13, 0x1ae6866c, 0xd768551c, +0x80b9a2dd, 0x3e45c6f8, 0x22defa52, 0x8656b95a, +0xa6bbe8e4, 0xa2a0e446, 0xb60b2803, 0x9d636882, +0x8bd705ea, 0x45067ce2, 0xd60e2cc0, 0x310034b8, +0x76833e0b, 0xd3f8cbbc, 0x04fd3408, 0x40a20a06, +0x48b0ad04, 0xa08a3c50, 0x1741eead, 0x838e881c, +0xe0a10cc2, 0xb6dcd518, 0xe1c218b3, 0x3dce05d0, +0x4508fea2, 0x777ea21f, 0x68f100d4, 0x6c358bf5, +0x44703d05, 0x2d7d49d7, 0xbe0a0774, 0x10bd137b, +0xeb77ed2f, 0xe8712722, 0x358c0efd, 0x7484286c, +0x0defad76, 0x04c23754, 0x1dd768d1, 0x58cdeb4f, +0xed096f13, 0xd3744974, 0x444c581b, 0x234d6377, +0x515c0dc5, 0x4c3ceb48, 0xaaf76831, 0xc1e78725, +0x88a1c75e, 0x4c7385c4, 0xefeb63b5, 0xa354750b, +0x18515641, 0x1eeb08eb, 0x210ee9b6, 0x1aebc12c, +0x8cedebfc, 0x4988bef4, 0xdd6b0fb4, 0x0760a40e, +0xfa5f5e18, 0x071f92c5, 0xa78cd728, 0x0460c124, +0xc124b2f0, 0xec3c60cd, 0x6455a68e, 0xc9f30114, +0x8245925c, 0x244a0e1e, 0x0f8a65f3, 0x0eb7b700, +0x46063d3b, 0xbf801324, 0x39a69e50, 0x297981ca, +0xc783cf89, 0x306a9f02, 0x833d3f7b, 0x04e0d1b2, +0x3945b05b, 0x913bcb60, 0x0f03392e, 0x7154e434, +0x46231ce5, 0x000b099b, 0xbad24e5f, 0x057b93eb, +0x2de3c889, 0xe9ae1a8b, 0x14381fd3, 0x461b7530, +0xe2707c62, 0xc6c10607, 0xf803be01, 0xbb415745, +0x9a470526, 0xd8897b05, 0x446389f5, 0x8d4b0212, +0x0413ff58, 0x2be06a21, 0xf8be0c08, 0x72281a83, +0x213eaf10, 0x6fa344e8, 0x18584ce9, 0x0a0cf135, +0xb5bee67e, 0x5cebfabb, 0x3a0da266, 0x6704a33b, +0xc2c60220, 0xaec3be3d, 0x603fc61c, 0x1faf8b09, +0x1624be42, 0xc7e69ec1, 0x411a03b7, 0x0442b1c8, +0x0c17436c, 0xac44c021, 0x096c682b, 0x1516e24f, +0xb1f989b9, 0x5fe8d6ff, 0x1bcf4235, 0xfff01423, +0x1681fbff, 0xf2f1bbcb, 0x3c8b37eb, 0x1ec6c694, +0x0789f801, 0x0075503b, 0x0e237824, 0x18ab55ac, +0x5c31f301, 0xfce90741, 0x4423534b, 0x2cf08431, +0x0dd2f861, 0x95085132, 0x3624f0f2, 0xa8383fb4, +0x404c45ef, 0x7f250389, 0xaea11b1d, 0x72f3aa22, +0xb6c58228, 0x16830191, 0x1465308a, 0x9a85a893, +0xc0c2831d, 0x6faa6346, 0x25c2f00d, 0x52e450a0, +0x2010e112, 0x209d6a5a, 0xc6c5a292, 0xb45c009f, +0x758591d3, 0x20829e0a, 0x201ba715, 0x02181674, +0x0f05f954, 0xf85d156d, 0x0a73fa39, 0xa7510def, +0x9fe9cbe0, 0x5c890ecb, 0xdfb41132, 0x936c92d0, +0xdde0baff, 0xec8d04f6, 0x5382631c, 0xde404063, +0x9c1a2545, 0x082b3f88, 0xefd8808a, 0xdea6dc02, +0x9f170388, 0x7610f983, 0x405d1a05, 0xd26e0a20, +0xe34682dd, 0xed024b88, 0x7f7dfe30, 0x1ef107bb, +0x99db702e, 0x4521178b, 0x14e456b9, 0x8a8afd25, +0x337a6fbd, 0xf8283798, 0x6ace0048, 0x3c35ba01, +0x36cf7883, 0x88f719e8, 0x0cc0000c, 0x040c20dc, +0xae1b4614, 0x02032a61, 0x8b4c0422, 0x86b81b7e, +0x41135b01, 0x0144c075, 0xcdb64caf, 0x5471129a, +0x5e124693, 0xd75c2ab6, 0x393a308a, 0x96f803b8, +0xb3dc940e, 0x33069992, 0x3d46e21f, 0x488e4cbf, +0x545089ce, 0x24a2f5c2, 0x083cb346, 0x54037088, +0xed57e3eb, 0x58a27144, 0xbec14718, 0x6ac22ac5, +0xe830756d, 0x882a47be, 0xf422041d, 0x84e908d6, +0x06fbbf54, 0x54b89cb4, 0x060c35ac, 0x6db6e5f0, +0x14ce89bb, 0xf110781b, 0x73c9c88d, 0xd2494e4a, +0x21ec21e0, 0xc0f57754, 0x7e6ed6e4, 0x28c8ae4c, +0xeb0d044c, 0x176ff33c, 0xf1438d15, 0x41b942e8, +0x46fa2171, 0x22206850, 0x52224027, 0xdb20b705, +0x76b8469d, 0x0ed888b0, 0x0db4468d, 0x8d14497c, +0xf3f7a3ea, 0xc8de2411, 0x505f8b15, 0x44ede981, +0xe9f4aadb, 0x606002f9, 0xcbbb18a9, 0x8c1b715a, +0xe8b042f1, 0x05dbc054, 0x8aa9c5a0, 0xde020837, +0x23388208, 0x5bc21ba0, 0x865f7fc4, 0xe382b036, +0xf452bb76, 0x482b8821, 0x00a6031c, 0x0a6dd074, +0x40740311, 0x4fd0076d, 0x7a8b0aa9, 0xaafe2960, +0xf0630975, 0xb5b0e840, 0xeb361079, 0x3822942c, +0x409ec2cb, 0x049ea3fa, 0x5d7d059f, 0x0eed8304, +0x55a66c51, 0x6d7bc018, 0xd02a6fb3, 0x003535e8, +0x54b51300, 0xd6805d9c, 0xe384c206, 0x340c1050, +0x055d55bc, 0x1c6250c1, 0x1b762260, 0x52ce409e, +0x169d9220, 0xf4085a0d, 0x463a32fb, 0xe8109b2b, +0x045b4c16, 0xda8d052b, 0x8f61e804, 0x0cda924d, +0xe3fe65c0, 0x241543e9, 0x2a711755, 0x3172f2e0, +0x3252357e, 0x01b3a22f, 0x95c2857c, 0x11b12ba9, +0x21116d06, 0x3eb80280, 0x6603c343, 0x4b0d0cb5, +0x87a4161f, 0xb92a47fd, 0xba046a20, 0x34a37236, +0x853d384c, 0xde261061, 0x6c015912, 0x14cab6c4, +0x1ce70cab, 0xcfcdc2f6, 0x716a174b, 0x343cbaf9, +0x291a2a6e, 0x60389dc0, 0x25df76e5, 0x8ebe337e, +0x1e09b9cf, 0x06418099, 0x9218545b, 0x06c3e91a, +0x96284572, 0x2a4c016d, 0x4e9ee965, 0x114cb05f, +0x162a805c, 0x89546aa6, 0xbf152609, 0x8558c1d6, +0xe8c40bc7, 0x1896a6c0, 0x5055eadb, 0x7d197521, +0xff548885, 0xd0290df1, 0x860fa43d, 0x5a00011e, +0x28bc2327, 0x040515d2, 0x901c96e5, 0x0700f701, +0x1e1dcf09, 0x88941880, 0x3081ec1e, 0x3388e8f6, +0x742e2415, 0x229e0833, 0x3b207736, 0x86af4735, +0x0628ac20, 0x395cd9d9, 0xbdb7ef17, 0xe09a79d3, +0x3c456283, 0x9c435d61, 0x9d02bad4, 0xc07717f7, +0xd8fb0d2b, 0x93709242, 0x6775ab0f, 0xf7301e87, +0xff799786, 0x0032f88f, 0x129bae75, 0x8b13e4be, +0x60208f39, 0xca1af1b8, 0x98245d12, 0xb622a48d, +0xa86bc247, 0xc6ed8e45, 0x944c096a, 0x37db3178, +0x0fa2b906, 0x0583e8c4, 0xaf24285c, 0x30c4934c, +0x8c38a9a8, 0x2b806fa7, 0x8b4c37b3, 0x425b1842, +0x83fa36d1, 0xc91901fe, 0x63f0fee4, 0x8b5b7e33, +0x03541853, 0x0adba068, 0x0ca6c456, 0xc7f9ba92, +0x227f435d, 0x68890708, 0xd45da50c, 0xa6800d34, +0x2eb300be, 0x0861341d, 0xd9b15d70, 0xc0230dc2, +0x665b7421, 0x76441306, 0xc75d0e32, 0x08f2c3d2, +0x997d484d, 0x2b32f718, 0x206724a0, 0xbbb76355, +0xcdb8b151, 0x04ac2d7e, 0xa005b950, 0xbaece498, +0xab2f25af, 0x4c15a804, 0x9114c7b2, 0x106aeb62, +0x08d42fbb, 0x159c3737, 0x09e53080, 0x6a2f5f32, +0x7560c005, 0xafb1baec, 0x2431ed41, 0xe360f46d, +0x421f3b00, 0xa0da8fb2, 0x12003232, 0xad05077c, +0xa30d0650, 0x08d808da, 0x4386df0f, 0xab76761d, +0x7f3dbad4, 0x502f3daf, 0x7936ebbc, 0x1e29001e, +0x48ba1f5e, 0xa1fba429, 0xbdd0687e, 0x116a124b, +0xc1826132, 0x897ce443, 0x400ba038, 0xe17e02c1, +0xbb46d0d0, 0x8bfff05a, 0x00dbeb56, 0x396c2fe8, +0xebb3bacb, 0x287e832e, 0xd183a89f, 0x2b76ff50, +0x131175b3, 0x7f104830, 0x88fb5e8d, 0x04cc4463, +0x90490e75, 0xd46a0912, 0xdb56b9e1, 0x42800bf9, +0x3d971505, 0xb980fee4, 0xffff4197, 0x68098c45, +0x8a0f8026, 0x65c93113, 0xa2340098, 0x4511a914, +0x7ea9a363, 0x8a680096, 0x284623b8, 0x0f084008, +0x9aae382e, 0xc00c84b9, 0x38c777e3, 0x28c7304e, +0x6e8d1380, 0x0228ba14, 0x083f51f8, 0xbecf631c, +0x14488b3c, 0x541f012b, 0xac56be5b, 0xc54dc651, +0xa7570842, 0xadfd83e8, 0x340f868a, 0x1043d316, +0x1bc01c2d, 0xf1437c78, 0x7c88b8ce, 0x70ffffac, +0xab19d006, 0xf13c0ac3, 0xf47025d8, 0x0734e84a, +0x742ce82c, 0x043a0238, 0x0790d254, 0x3d270002, +0xdf388654, 0x778b804f, 0x16c2fa2f, 0xe3411001, +0xc38a8931, 0xe09e25cc, 0xce5142c2, 0x69eb0b6c, +0x005c0008, 0x557d7763, 0x30002382, 0x76fc3534, +0x756e3a22, 0x5975fe89, 0x0b0b444f, 0xd7701404, +0xd777363b, 0x54ba0839, 0x074b40b2, 0x9174e13c, +0xc71a3682, 0xdd48d7fb, 0x11341008, 0x0e3208b3, +0xcebad544, 0x55383975, 0xeb4ead15, 0x54caab78, +0xfc93a46c, 0x50c00ac1, 0x06edef50, 0x2a86efc5, +0xeb4dc301, 0xd1fee1e7, 0x11511bee, 0xfeac54a0, +0x1ddff588, 0x81c65e51, 0x597407ee, 0x20783d83, +0xcc50f02c, 0x58aa8f50, 0xd01686c1, 0x5fe43447, +0x50670722, 0xed2c6c4d, 0x6bd6e11a, 0x645a2014, +0x1d15a236, 0xd573b805, 0xe5458ba9, 0x5c407f4e, +0x2c40fc5b, 0x5faea211, 0x1dd889db, 0x03414938, +0x274bec81, 0x5010f004, 0x750428ce, 0xef1138a3, +0xa9d0017d, 0xbf3e2068, 0x7f56cd39, 0xa8f6ea5d, +0x5dfa6260, 0x9111254f, 0x90a1abe8, 0x80016844, +0x33d90a60, 0x7016ffdc, 0x0451ae83, 0x013b2172, +0x3e8b1d72, 0xfa0ad06e, 0x81210c0e, 0x08e10d77, +0x010017da, 0xabee29ef, 0x29a82ba5, 0xc1412f68, +0xc37bd011, 0x750939ae, 0xb9fc0fc4, 0xd08ec481, +0xbf196805, 0x15345806, 0xc3549c5a, 0x20ec391f, +0x4311b415, 0x1525c30c, 0xbc8e2df4, 0xcaffffad, +0xf52ea415, 0xd434458d, 0x8068d475, 0xf11675c2, +0x356d188d, 0x0ab76fa3, 0x1ea6e8b1, 0x6c07182b, +0x2802ea83, 0x53601f75, 0x4738451b, 0x151d8f15, +0xc2ed40f1, 0x2d040ef3, 0x8d838e82, 0xff72e8c3, +0x5bc05881, 0xdaf03874, 0xeac3fce0, 0x3b6d9d0d, +0x0929bae1, 0xc2ae4e57, 0xfe1aedd4, 0x6605ca83, +0xea74d039, 0xb10c19ca, 0xef9a72a2, 0x27fb04cd, +0x0d08000d, 0x74f38024, 0x1f63de31, 0x16771177, +0xa5034d0b, 0x545b5461, 0x4ebab91b, 0x1dde600a, +0x1364db1e, 0x82a43c04, 0x4f1ce97c, 0x2c750b55, +0x72b88ddb, 0x7140e8c2, 0x84e2ac8c, 0x06e58ae3, +0x60d4b6a9, 0x53308afe, 0x842e994b, 0x677e8a51, +0xc96c5252, 0xe77105f0, 0x0d1a7507, 0x288c0eb6, +0x97ee76e5, 0x88142662, 0x689efe60, 0x66b739b4, +0x74b8200f, 0x3b088235, 0x17baf1b4, 0x060153f9, +0x0ad426c8, 0xf89ff75d, 0x4c870ffd, 0x48538971, +0x0610eac1, 0x2d08284a, 0x47273ca8, 0xe4ec3144, +0x69111073, 0xfa192d3c, 0x69daa580, 0xf2dcbe1b, +0x03060a34, 0xf67cc20a, 0xf35b8df6, 0x1ced447b, +0x4efce283, 0x0d14eb44, 0xcb450d46, 0x437e0640, +0x2eff839b, 0x7404e2f5, 0x0204c6a3, 0xc77e24fe, +0xf100e457, 0x1f33c505, 0xdd374a89, 0xc7681071, +0x53106aea, 0xe08b2e71, 0x4a10e0ac, 0x5a2b8420, +0x3d0b6265, 0xce685198, 0x067b5014, 0x5b04a0a9, +0x8997e901, 0x4402b4f8, 0x8d1774a6, 0x115d7200, +0xc1ec4c74, 0xdb369542, 0x341eb86b, 0xa85d36dd, +0x0468db43, 0x05558964, 0x74113469, 0xbeed1008, +0x30a5f87a, 0x42eb5f4f, 0x89fd148d, 0xb0aded03, +0x6685cdd1, 0x66068611, 0xcc98633b, 0x2175c555, +0xaa4a0ff6, 0xac175b05, 0x023c7389, 0x40577a89, +0x15c73906, 0x017e3b47, 0x1d580a71, 0x1538e5c1, +0x73ded81a, 0xdfec8247, 0x9d54680d, 0xb00c2cba, +0xa334dcaf, 0xf9ff1568, 0x2e76081d, 0x3572e83a, +0x4207d42a, 0xb8c95d30, 0x0ae13e8b, 0x5bfcdad9, +0x7acefbbf, 0xf10da392, 0xeb134355, 0xc053d2b2, +0xd7e99343, 0x01501c53, 0x4c69697d, 0x42c609c1, +0xed0d0021, 0xd4c65a0a, 0x05e37b80, 0xfd47c820, +0x8fbfc7b8, 0x307e5be2, 0x2e88012f, 0x04dc24be, +0x53083825, 0x0c6b7741, 0xc05e3c50, 0x45e22283, +0x99102230, 0xb0fec1ab, 0x448b03dd, 0x400c2824, +0x8c0ea37a, 0xd9d01d35, 0xba8d0b8e, 0xe8c68b30, +0x8e46ab6f, 0x0ceaf6c1, 0xb285d0f2, 0xff77d2d1, +0x2c340ffa, 0x04463360, 0x0f08460b, 0x0f749cc0, +0x62692a01, 0xfd82200c, 0xa3048958, 0x76e515e0, +0x049ea55f, 0x0ca21005, 0x93d60620, 0x10b6b220, +0x04ee880c, 0xfbeff663, 0xe5a2e61c, 0x8b06b61c, +0xace3c754, 0xb3d12e82, 0x2e851091, 0x83b696e2, +0xc289fcd2, 0x13d59750, 0x389448bd, 0x3a891577, +0x4c435aee, 0x6b1bf631, 0x7ecf5178, 0x9dd99bbb, +0x807dda8c, 0x1c1f74b5, 0x706d4324, 0x87dec448, +0x9282d6ff, 0xbcad461b, 0x6ed80a1b, 0x89162a97, +0x8a891a2c, 0xc07dad1e, 0x469240bd, 0x5fcbaf0f, +0xba25e2c3, 0x0c14c330, 0x43fffffe, 0x905814d4, +0xdb148808, 0x8320ed7d, 0x72dce989, 0x66166228, +0x23c94037, 0xc46f1bac, 0x219006fd, 0x248af8b0, +0xcb919283, 0xca3b2888, 0xf8653246, 0xfe0d6f2a, +0xfccb7207, 0x04788190, 0x91d447b2, 0x4a02351a, +0x55be9855, 0x19516fdd, 0x77130278, 0xa8060d0b, +0xa649a001, 0x6f04005a, 0x149a0682, 0xc17b209a, +0xdc13a2e9, 0x506797be, 0x0c064558, 0x65f6af7f, +0x3182bf54, 0x0fc66fc0, 0x546a2ab7, 0x7850a2ad, +0x08468154, 0x9cd1ec7c, 0xdb13f041, 0x7bf04529, +0xa38106f0, 0xc47e869a, 0xdc460a31, 0xfd016e3f, +0x7cf98e09, 0xf713569a, 0x502366d8, 0x66d2b1cd, +0x0a6932d7, 0x73682074, 0x1f069a09, 0x09a19c28, +0xb67c64f5, 0x1fa51722, 0x4f109c2e, 0x34c3b044, +0x03513103, 0x100260c5, 0x4c17ec30, 0x482eb8a8, +0x019a4c02, 0xe0506dca, 0xca3cc7c1, 0x6c8e8a17, +0xa8123d7d, 0x93730f66, 0xc0b6ecbd, 0x5b5a4566, +0x5b355c72, 0x2dfaaa46, 0x8f085014, 0x1040d606, +0x02ad07a2, 0xa08bac03, 0xe4414bd1, 0x1842a1ab, +0x660b09d8, 0x223c53a2, 0x0406e499, 0x981c6ceb, +0xab8f8dc1, 0x50067f3c, 0x46938484, 0x50b15402, +0xe863fa29, 0x828662ec, 0x132087a1, 0x2dc272aa, +0xd014cdaa, 0xcf12b0d9, 0xe8af515c, 0xb8160f6f, +0x93b24000, 0x65c914a0, 0xfbe31000, 0x703b0327, +0x5e12b0df, 0x6a0759fc, 0x18384009, 0xa0c35851, +0x4e285f4f, 0x06e61650, 0x75fd828d, 0xfdbbe828, +0x01fc3d17, 0x16a42077, 0x86472aa6, 0x0ddb7ae2, +0xf01785a8, 0x324582fa, 0x104b82bf, 0x09ca02bd, +0xe0170273, 0xc2a91c4a, 0x77fd094b, 0x317ae031, +0x0063585f, 0x11eb01b0, 0x8a08f9e8, 0x1fdb2d14, +0x206b829a, 0x08c62036, 0xb9e95868, 0xa3ff6792, +0xb0e8da5d, 0x15ffff6b, 0x081e1244, 0x99b754b7, +0x8a023c16, 0xcf20bfa7, 0x0f8683c7, 0xed15437c, +0x0982caeb, 0x2a1dd084, 0xe402e11c, 0x156e281c, +0xdf4df6d1, 0x247e3681, 0xd903cc6b, 0x74708d40, +0x96ec6efe, 0x34995aa2, 0x009ac669, 0x24e80900, +0x99b8eb02, 0xac5054b7, 0xb94c0580, 0x1037a219, +0xa1fa89f0, 0xb514ff18, 0x420ea808, 0x67d09157, +0x2225750b, 0x0a30bb76, 0x2b66f408, 0x15b68947, +0x04a23c8e, 0x5fde703f, 0xac2a4309, 0xbe057648, +0x00846406, 0x4c08983a, 0x07ef8c06, 0x6abc1110, +0xa89073a7, 0xb936b960, 0x23dd1908, 0xbac01d9b, +0x942ca33a, 0x7cf700a1, 0xa30440bb, 0x34380930, +0x68237b04, 0x18a24da9, 0x0c480d8b, 0xbf1af9d0, +0x1ffff981, 0x1d4ce5f9, 0x037a95a1, 0xde821caa, +0xbbe622eb, 0x3ff1fa1b, 0xc2295d00, 0x44a1048a, +0x56a3400f, 0x0578a999, 0x25a2211d, 0xc84c0b69, +0x4c15b657, 0x955a9688, 0xa650745b, 0x73e88449, +0x0e2968b4, 0x0bbada05, 0x87dc029c, 0x3f20bf7e, +0xd78ae663, 0x0d053c14, 0x9b7b096d, 0x6d24d82d, +0x234c2809, 0x0bedc213, 0xb4c0de8e, 0x4500bb1e, +0x82834281, 0x48fb4053, 0xff411782, 0x2f007d80, +0x62c80774, 0x71a14057, 0x40df8609, 0xddfa285b, +0x8756ef42, 0x5374302a, 0xdd685014, 0x350b81e3, +0x20a1029d, 0x9abfc2ea, 0xe0a8d783, 0x19786220, +0x554a0a8c, 0x199ad425, 0x6bfa155e, 0x1ece027f, +0x25c2d702, 0xc418e08f, 0x8b1ddb6b, 0x16488d07, +0x1451b54d, 0x0d876a4a, 0x15243e79, 0x49e2a00a, +0x11a71de8, 0xc59c237e, 0xa1c3fa84, 0x071d7e3c, +0xe42b77e0, 0x868923eb, 0x89b54e8a, 0x141ec90d, +0xdb493e3d, 0x36cfd831, 0xeb2208eb, 0x745e5bdd, +0x63151902, 0x77116faf, 0x9c60ba95, 0x5f75b21e, +0x2119868b, 0x2d98623a, 0x18228210, 0x198efc89, +0x81c9d100, 0xed7493fb, 0x25b16d09, 0x82749fe5, +0x031546a5, 0x95fef3ec, 0x7580826f, 0xfed4416b, +0x74476137, 0xc0e8a5a4, 0x770a4e4a, 0x6f506b60, +0x095da696, 0x0b84219e, 0xe77559ae, 0x25db25fd, +0xe844f243, 0x1d40a5f7, 0x94d88627, 0x268c8410, +0x831c091f, 0xeb380846, 0x2c42bce3, 0x3a05e2ab, +0x722bc4d7, 0xd83e2c0c, 0x382ed146, 0x03098bdd, +0x111b0a0d, 0x05890bfb, 0x15072d7e, 0x0f9fd4b0, +0x2772d939, 0x052f3194, 0x23f33070, 0x578b1890, +0x8db83a5e, 0x1029ad0c, 0x1ce8a119, 0x852db567, +0xfa18680b, 0xbf7ad59f, 0x538206b1, 0x3d8304a3, +0xdbf0f6ad, 0x07a38e8a, 0x73410539, 0x4306bc0c, +0x468e06bc, 0xc1d5f9ac, 0xd9110ba0, 0x9090949b, +0x082b7e3e, 0x788b27e3, 0x60aca30c, 0xb3aa06f2, +0xac4b8639, 0x52eedc37, 0x83b3acb0, 0x5325438b, +0x4402ae0c, 0xc6cfa5eb, 0x5c7b1375, 0xfd60725a, +0x75d73966, 0xa0012b09, 0x8f021302, 0x08221a28, +0x73c78751, 0x86aff48c, 0x89e7c9fe, 0xd315a464, +0x30245489, 0x45648023, 0x80e5a0dc, 0x31698012, +0xdba66e9c, 0x08660ce1, 0x8ea35b02, 0xdf81a0eb, +0x0b05ca09, 0x0eee1508, 0xb6fc5460, 0xa8b68018, +0xd54561bd, 0xebb1ac75, 0x20129f40, 0x958c4868, +0x86c08d10, 0x8de05d5b, 0x104f200c, 0xc665d348, +0x2003a5b3, 0x2f3ffa7d, 0x1b05c4bd, 0x079d1624, +0x088c6cdc, 0xd2701310, 0x2169b730, 0xfdbe06f1, +0x1890a385, 0x30ee67b2, 0x0950e61f, 0x355ad142, +0x5b262a30, 0x35d0de99, 0x642ca216, 0xd5f8e307, +0xa2d767eb, 0x426ca83a, 0xee80ed9d, 0xc6298823, +0x517e12d3, 0x9d09890b, 0xbd5764ff, 0x0ebc0462, +0xa27b600b, 0x065920b9, 0x51130aa0, 0x0c191904, +0x16460a06, 0x2ccef33a, 0x8f4158db, 0x1999a4ae, +0x404480b7, 0x7fc7bf62, 0x0a2c3028, 0x518bc4db, +0xb2db894c, 0x0250db2c, 0x58065405, 0x010e020a, +0x38645b01, 0x2c628d0a, 0x5047cd52, 0x24170a95, +0x035e8df8, 0xfbff0eb9, 0x68aef4ad, 0x582107eb, +0xcac63deb, 0xd1106866, 0xb41842c5, 0x2eaa0703, +0x89fb1d55, 0x6ccd8822, 0x20c9e825, 0x050508af, +0x96274156, 0x5b0c1406, 0xc3d052ee, 0xad46a70d, +0x2f3c25eb, 0x03982ed6, 0x3b0b103b, 0xb8492441, +0x0607a985, 0x960001ff, 0x8b0c0418, 0x67bd1606, +0xd20d8e06, 0xd023e89f, 0x91160809, 0xa02a88ee, +0x1f032c80, 0xc2f647d2, 0x13ffd738, 0xb510c6e8, +0x212cf009, 0xcd28b448, 0xd03b39ec, 0xbb14160f, +0x2f1434d8, 0xffd4f194, 0x00582440, 0xe8e5036c, +0x0a5f29aa, 0xb6a8fdf2, 0x7cc1a1a2, 0xe9ae06bc, +0x14860d25, 0xe8a01460, 0x8b4c09d8, 0x0043f60b, +0xddd0d906, 0x196a0026, 0x13c5b06f, 0xc7568da8, +0x6284e743, 0x90e93dff, 0xcf38ff84, 0x1b4a8bdb, +0xc16aa5e3, 0x6624512d, 0x1a06281d, 0xf6950d85, +0xbba5dc06, 0xad705011, 0xd6a10ec6, 0x4eb70dbf, +0x35a10812, 0x24c68331, 0xd9738a8d, 0x0f958083, +0x73b3cf1c, 0x05aa37b8, 0x0030012e, 0x08748ec7, +0x8b9957a9, 0x4cc7323d, 0x016358ba, 0xdd51d079, +0x10e86f5b, 0xfa6820cf, 0xe850b964, 0x5cdab845, +0x126710d5, 0x05160633, 0xd1b3ea89, 0x9c1a7b8d, +0x88018120, 0x6e258536, 0x64c115cc, 0xf740d2f3, +0xc02d2c36, 0x8a8b1370, 0x72143c45, 0x620d8832, +0x8a10d8a3, 0x3228be20, 0xc9512435, 0xfba79e28, +0x09278e00, 0x0314201e, 0xc702eed8, 0x096c01c7, +0xba6aac5e, 0x1c860401, 0x300ac724, 0xa9ce0004, +0x1f823f08, 0x66c50aaa, 0x16612be8, 0x09a3805d, +0xed34b70a, 0xb788a36e, 0xcc4408eb, 0x7f582812, +0xf4dc4457, 0xe816048d, 0x43f99620, 0x12344700, +0x8380c89f, 0x12e9c04d, 0x240c70e0, 0x56efef41, +0x414cb787, 0x4b0a2474, 0x42adddb3, 0x105f6c94, +0x6ff20173, 0xe0eeaa19, 0x5760c4e8, 0xe52debed, +0xd9b32d03, 0xe802e82c, 0x3935e704, 0x2fb299a8, +0x1b084594, 0x926f557e, 0x5cca72c5, 0x80137374, +0xf088d07a, 0x75c0038c, 0x5220282c, 0x34116dcc, +0xa5e73906, 0xb2603456, 0x2fae5ce0, 0xb9bb9407, +0x06eb0646, 0x8286892b, 0x30330cc6, 0x04c41349, +0xe20b08a2, 0x2ec596aa, 0x0395239b, 0x46007527, +0x17aee9ce, 0x360cd4e8, 0xf8269ec6, 0x010c2011, +0x55f48b80, 0x7da1ff70, 0x93442123, 0x4ac864c8, +0xa31b1537, 0x05065604, 0x0e40c673, 0x74008af6, +0x7b8d0f03, 0x116701c3, 0x015882f0, 0xfd6187a4, +0xeb5d076c, 0x24d27521, 0xa418ffff, 0x0aeb155d, +0x3df06725, 0xf052c501, 0x20b13b9a, 0xb12581d5, +0x1fd8f140, 0x3cc08313, 0xe592130d, 0xa3845b7b, +0x0a023217, 0x66105f66, 0x4d1a728b, 0xb01baa00, +0xe91aa713, 0x70341b56, 0x1278c910, 0xdeac54d7, +0x65eac017, 0x09aa06bf, 0xe0e8a3bf, 0xf4e8f9f0, +0x4effff5e, 0x28a2c1a2, 0x0899e889, 0x58878b17, +0x08716e29, 0xa345eb05, 0xeefd0c45, 0x2933df65, +0x0b2aebc1, 0x66238b66, 0x7406083d, 0x28015120, +0x358005bd, 0xd148a774, 0x9940b823, 0x1a9dba0b, +0x5745ba89, 0x05e70ceb, 0xaf7b8106, 0x58cdac36, +0xba5d585f, 0x084e0603, 0xb80c4697, 0x7a803b81, +0x5688ba9a, 0x5c0d880e, 0x365e42a8, 0x1295934e, +0x66ba0484, 0x826c3de7, 0x1820a157, 0x559b8189, +0x0961412b, 0xc246a78b, 0x16a0016e, 0x10c9e7e3, +0x0736b515, 0x5cff13e3, 0x6d403536, 0x7634c7a3, +0x1fc03109, 0xc290e884, 0x0c42275f, 0x108d4203, +0x601c6d3d, 0x1f22c126, 0x516d1528, 0x55d1184b, +0x5625004b, 0xd82846b6, 0xad977800, 0x4567fac8, +0x315f5e51, 0x31d021ca, 0x37dbbac1, 0xd031672a, +0x890cc121, 0x1d3108d0, 0x0ddfc456, 0xc809d1f7, +0x20a4c231, 0x57d0725a, 0x23df8b81, 0x86daa357, +0x4fe797a3, 0xcd891506, 0x5f8d7055, 0xd54758aa, +0xed05705d, 0xb3551b14, 0xff170c4f, 0xe01bc0df, +0xc1eb89ed, 0xe3c104fb, 0x20b38d03, 0x04404dee, +0x1cb9a782, 0xdea0054b, 0xd51193ff, 0xd2fcaf7b, +0xd5d5822d, 0xe25c0105, 0xad04030f, 0x1b6b8736, +0x66020188, 0x8e930dc9, 0x19dd5da0, 0xd329008d, +0x8aab60c0, 0x45534bd9, 0x063e4c8b, 0x9055df75, +0x40fd8386, 0x1c720e74, 0x013bb41d, 0x8ceb5f25, +0x82b601d0, 0x5d07aaed, 0x6e2a1101, 0xb3e88203, +0x010cb20a, 0x17f80e0a, 0x50504de5, 0xd71ee910, +0x5bd37a6f, 0x6745233c, 0xcdab891c, 0xdcfe17ef, +0xd00198ba, 0x760211be, 0x06103254, 0xed831554, +0xf954065b, 0x874ba09b, 0x57b74094, 0xbe3f1054, +0x111dbc5b, 0x5dce2942, 0xe1960159, 0x11c8f8d6, +0x7e4789da, 0x0b743b1b, 0x04b4544e, 0x290d76f0, +0x2c744014, 0xea233f62, 0xc00c48eb, 0x4f445c3e, +0x79a16310, 0x02f03519, 0x1c69ed50, 0x40b919dd, +0x48d9d103, 0x58201d92, 0xeec05f4b, 0xee8340f6, +0x503ffe02, 0xffeadf77, 0xe02b01b1, 0x0cffff5b, +0x101b93c3, 0x8d4d402c, 0xf8dd14f7, 0xb906e02e, +0x80026337, 0xdf017a8d, 0x68aa1679, 0xc10b760a, +0x0bd8fd08, 0x55bfc18a, 0x38b9f789, 0x05b7b005, +0x2350e2d0, 0x6f0848c3, 0xb0285000, 0xeac11d0a, +0xa2d44f57, 0x00ef0b1d, 0x7828918e, 0xde89eff0, +0x188a3094, 0xe1df80ac, 0x1044835d, 0x8bbcbbb0, +0xa832c2c1, 0x5313a938, 0x13fb4154, 0x29cd8952, +0xfc11f1d5, 0x76ffd801, 0x243cf904, 0x080c7500, +0x1f9d24d0, 0x407fd053, 0xa70428e4, 0x76c83993, +0x1f022a20, 0x15200c5f, 0x4f5d5302, 0xe9e129f0, +0x1b455d0b, 0x4ba27319, 0xb858d530, 0x4d8dc104, +0xdb0308e2, 0xe0810274, 0x303ce91b, 0xb010348d, +0x58bc106e, 0xc4f04b23, 0x9d56c55a, 0x08d45e0a, +0x060e02c7, 0xa8c931f0, 0x1b0bff2d, 0xffffd016, +0x8003cab5, 0x834bbaf6, 0x7e76860c, 0xfa8160af, +0x0c11775e, 0xe8a09f02, 0x24810572, 0xdbe36071, +0x54747737, 0x04061d7f, 0x657f5774, 0x28dc51fa, +0x76674e5a, 0x1bf53beb, 0x7458ba08, 0xeb33a066, +0x43fa816e, 0x7f6f6d5f, 0x0f7f666e, 0x39740b10, +0x1ac9330e, 0xdb7db3cb, 0x460926eb, 0x48073e18, +0x2d72442d, 0x9a333c54, 0x0b950246, 0xff428036, +0x75a2a1f1, 0x49ffcf2d, 0x3835b231, 0x0d9b9a89, +0xb623fe7a, 0xdc2006eb, 0x08733e37, 0xc9ee15eb, +0xd37ee512, 0x08edeb48, 0x0473f039, 0x55d63940, +0x7b2a21f4, 0xa61b0368, 0xfb32d54b, 0x82d8be21, +0xb7cd59b6, 0x38ebd004, 0xe5151a33, 0x46fd4101, +0xfb839011, 0x6d1b7640, 0x992373fa, 0x112312d2, +0xe8889b68, 0xc157e5c3, 0x5a430edf, 0x750fc3f6, +0x259811ef, 0x815f7563, 0xa72a91c0, 0x4857581b, +0x805aacc0, 0xd2845175, 0xbbe4d6b3, 0x1e53684a, +0x151ae688, 0xa7155f7d, 0xfca05340, 0xca2608de, +0xff85695f, 0x139195d7, 0x9b0331d4, 0xebdb7425, +0xb6fb1f12, 0x44437db6, 0x9ce85a8d, 0x53600681, +0xe80f3ef0, 0x831081a1, 0x8ee6c394, 0x9a615040, +0xc2a3a65a, 0xff7de002, 0x30d87514, 0x36a868db, +0x74007a0d, 0x68b07144, 0x685f4dd0, 0x0e0bc51d, +0x74c2857c, 0x32811c0d, 0xf9a82ec4, 0x5ded72a4, +0x31554dc3, 0xdbf7f83a, 0x236b6bba, 0x8d7f5d45, +0x0f1035c8, 0x090c1b5c, 0x5e924f62, 0xa86e0f53, +0x0470fff8, 0x409ad468, 0x6db7a068, 0xd9c41385, +0xad8395ae, 0xc8efd2df, 0x10c21680, 0xc35b0cb8, +0x130bb437, 0x1ebf4c1c, 0x91a6ba28, 0xf3fb1e82, +0x2dd8278d, 0xef0a74d4, 0xba18c556, 0x5fe2ae52, +0x46e03c14, 0x585f08ea, 0x2840e283, 0x3c43d21c, +0xf8d4aaab, 0x6ee817c0, 0x3205a821, 0x0e1d0f94, +0xe8dc0d55, 0xa756371a, 0xec501876, 0x43131323, +0xb882231d, 0xf63033e1, 0xeb5a16ad, 0x8a8f835d, +0x26e8319b, 0xae0c180a, 0xb72a6114, 0xac236bef, +0x3ec79f48, 0x0dadd069, 0xc26e6818, 0x450840ad, +0xe820fa1c, 0x6b8e68dc, 0xd9c4508c, 0x452422ad, +0xcdd47a9f, 0x2645e400, 0x22bacf69, 0xe8da86d3, +0x4ce5ed6f, 0xb0c5563d, 0xb9db653b, 0x4e162008, +0x9c9c1546, 0x1b7ac2a2, 0x20775f6c, 0x0a9d5000, +0x0ba9879d, 0x6e20e122, 0xa20fd803, 0x75dd3369, +0x890add51, 0x08809a16, 0xfb3166c7, 0x544459f7, +0x07ff810f, 0x01b00776, 0xa2284f17, 0x69a35381, +0xd04308c0, 0x1234dce2, 0xfeab9158, 0xf7218e85, +0x61e4c32d, 0xe281c288, 0x51bfb80f, 0xd0888d80, +0xb0b261e6, 0x2dd2da05, 0x06d32455, 0xdc04c742, +0x22d444b0, 0x68a8fa34, 0x023d0376, 0x024d76f8, +0xf304c67c, 0xb414dbf2, 0x6868b182, 0x7be9187a, +0x208a5524, 0x14283ce0, 0x97bf8038, 0x88ba0e88, +0x51db102c, 0xa196bb13, 0x52097468, 0xc9e03e09, +0x68d10377, 0x89b03882, 0x9041424d, 0xfbc4ffe8, +0x39763dd3, 0xe35e390b, 0x68d33608, 0x1c166290, +0x6b7d32f6, 0x8bdccfc4, 0xacf29182, 0x1562629f, +0xe02c9670, 0x105d8092, 0x54835439, 0x2a477805, +0xdb98fa9e, 0x5821a114, 0xc09fb840, 0x09e40d8b, +0x28c87fd7, 0x3aeb2d6a, 0x6b8b3b8b, 0x0285d047, +0x297723a7, 0x50a02b26, 0xab0c536b, 0xeee8097a, +0x117780a2, 0x303bf829, 0x80071376, 0x2bd17497, +0x0fea9e4c, 0x10ee454a, 0xc8f43cff, 0xbbbac58a, +0xbc750a39, 0x71a3688b, 0x4fc5756d, 0xce7fc980, +0x461639e9, 0x447fda22, 0x468b287c, 0x35a089f8, +0x1e24c1c2, 0xde806217, 0xc00fca06, 0x4a35353b, +0x3e450cc4, 0x80050dad, 0x4c158d9d, 0x0033c708, +0x11002a37, 0x1a9c8b3b, 0x6a225066, 0xd814dd01, +0x55228dab, 0x230fb2cb, 0x850da995, 0x33c5299f, +0xb6ff0603, 0xd9cb7651, 0x0773f539, 0x0d2eef89, +0x27b077ee, 0x89942404, 0x1aeb6d2d, 0xd5d67f11, +0x0c763b93, 0xebd27a3b, 0x8bf58952, 0x60f66d6d, +0x8bf84528, 0x153f097f, 0xe1385bef, 0xf4eb64f6, +0x9a9ce241, 0xdd0463b3, 0xa009fc41, 0x148bbc7e, +0x106fadde, 0x39c10154, 0x1589a9d1, 0xa450b62f, +0xa10a6682, 0xf80b1fef, 0xc81be836, 0xe40245e8, +0xcd830375, 0x4dc10822, 0xacc48155, 0xd4527774, +0xfdf6c98d, 0xa008ed8b, 0x90411229, 0x30222a60, +0x7d825011, 0xde212405, 0x58153b87, 0xb7a03e52, +0xc03e5ba9, 0x3501c153, 0xfb1c0417, 0x66d1bb2c, +0x2966268b, 0x482c0ec9, 0x3dd24163, 0x6cce623d, +0x3b1b843b, 0x6ad17587, 0xc5d7bc41, 0xb7ddd298, +0xe6df643a, 0x0360e480, 0xaf03a864, 0xad778a0f, +0xd8391807, 0x3a6eeb72, 0xb3128c78, 0x40908d71, +0x6e028174, 0xbd4e20d4, 0xab77c229, 0x5b6e50d7, +0x40291205, 0x1fe807a3, 0x853db715, 0x49083fc9, +0xbc67e0eb, 0x633902c5, 0xdeec3d80, 0xde0895e6, +0x0c05d917, 0x7803a2ca, 0xc9809a11, 0x1f0e205e, +0x34e8a1d8, 0x097f4229, 0xa1b103c4, 0x6f9be9ff, +0x8ed03603, 0x66eb4154, 0x9868684c, 0x9f22082c, +0x5e782e4e, 0x604c1d21, 0xe8b1ce5f, 0xf77efe05, +0x1425e87b, 0x64e6d1b0, 0xdf081ce8, 0x544260e6, +0x087dd453, 0xf80a11ff, 0xec254117, 0xf6392610, +0xc618fb9b, 0xfe2592e4, 0xc883c608, 0x0692e602, +0xb3c81cf5, 0x03fefb29, 0xc0a85717, 0x43997910, +0x6070681b, 0xaa8d5568, 0x1bfeebb1, 0xde661c54, +0xe3f09d5d, 0xdf0d1109, 0xc1819610, 0x222192c5, +0x14e181f6, 0x6435b6ac, 0x71c33105, 0xf77c420a, +0x8289c518, 0x82c745a8, 0xb70003ac, 0x185008ea, +0x03b83be7, 0x1f0567d4, 0x459eebe1, 0x3755888a, +0x3256f008, 0x5c0000c2, 0x0f0520c8, 0xc1eb5a0d, +0x0d7f106b, 0x1c421c11, 0x03ff61d0, 0x565d1302, +0x1b30b1b8, 0xc31af881, 0xe3cf8486, 0x440620e9, +0xb0cf6dee, 0x2f8d10c8, 0xaf31c24a, 0x0ec02333, +0x281d8b76, 0x6d53b58b, 0x751009b1, 0x05788007, +0xf62003b7, 0x68070ad3, 0xe096fc83, 0x5673d039, +0x30f711c1, 0xd85ef672, 0x878b5006, 0x7ece24e8, +0x89d2d509, 0x7f841efb, 0x3d430b43, 0x03e801b9, +0x0dfff2b8, 0x763be04e, 0x8336d674, 0x207501e5, +0xb6d80966, 0x28155b46, 0xd160ddf9, 0x888d0556, +0x06f631ea, 0x4d5e8a0d, 0x16003ffc, 0xa68363af, +0x886415a0, 0x0a3aa12d, 0x00930516, 0xd55f21cf, +0x8493f630, 0xdccd4059, 0x25d0d266, 0xc108a22c, +0x9427220a, 0x2840fe02, 0xcabff631, 0x8047f811, +0xe596756b, 0x4d4150ba, 0x5efd9efd, 0xa468f396, +0xa0759a65, 0x75153dde, 0xb22f8d7a, 0x7955a5f0, +0xd2833768, 0x6d443c4e, 0x8bb23b11, 0x9855ada7, +0x0e2c6ba2, 0xfc01660e, 0x1301030c, 0x67030451, +0xaa642b10, 0x06f5c182, 0x516e0613, 0x990f7760, +0x1a1c45d9, 0x887affaa, 0x2dfdbc14, 0x011cb967, +0x0b4e1476, 0x8c780441, 0x419a6e20, 0x14416f10, +0x8fc41074, 0x480aee2f, 0x2fbb241c, 0x236b9208, +0x00140610, 0x0f155a82, 0x051964a4, 0x74f78294, +0x9e9dae14, 0x1aed5eab, 0x68830957, 0x8d3b4019, +0x75720da6, 0x31abc379, 0x0625e160, 0xa1e9f801, +0xffffff97, 0x41c00514, 0x8be0e283, 0xaaf7a05a, +0xb136d06f, 0x6b6729c5, 0x37e87d8d, 0xa25c08f8, +0x5d2a0e36, 0xf7e46589, 0xfd15e94d, 0x25ea0412, +0x000001fc, 0x21aafd8c, 0x0f6df609, 0xd5063524, +0x81b94d8b, 0x0da204e3, 0xb6fc2b45, 0x75ff0614, +0x0dc75817, 0x4e22b712, 0x085d836a, 0x8d00a8cd, +0xa81e5da2, 0x5589b47d, 0x8b674ebd, 0x27144859, +0x909060a3, 0x0b1d370b, 0xf0b9a408, 0x03c4ba00, +0x0ca82fc3, 0x00ba54b4, 0x33c458b9, 0x1913f459, +0x0bf0aca6, 0x3c798580, 0x20aa687f, 0xccf91647, +0x223f73cd, 0x0a0e1146, 0x60a04e00, 0x147fcb11, +0x850f065f, 0xb28dffb3, 0x05152b54, 0xa921475d, +0x02626671, 0x5633e144, 0x21753b4c, 0xe36005ad, +0x75f0954b, 0x010f8b19, 0xeed38205, 0xff5c6105, +0xea01bc0d, 0x77ef2712, 0x91d2f57b, 0xff0c008e, +0x416b8b66, 0x66b91623, 0x525c79e7, 0xf356da21, +0x60b20622, 0xf51871b8, 0xd0009d04, 0x8b113a8b, +0x612df5d8, 0x5005e20d, 0xc2899759, 0xcc46b74d, +0x813cc996, 0xa98ac814, 0x42fd3f0d, 0x16818816, +0xf0056d04, 0x104a0f03, 0xf12c4256, 0x89110e42, +0x2851e350, 0xf5e2a878, 0x86e92c50, 0xa5ff5897, +0xde4ab852, 0x42020c6c, 0xf875da39, 0x08491428, +0x5b0948e2, 0x4068aae7, 0x4bcee957, 0x41c1d685, +0x5ca2ded2, 0x7fc9fa78, 0x88abc056, 0xffdfd71a, +0xe80aebce, 0x56888e0a, 0x5a20c9c4, 0xa8c219f1, +0xfca3a6da, 0x158b96bc, 0x0bd2201b, 0x06a1410a, +0x227cd680, 0x18c56bfa, 0xa4b90596, 0xdff132d1, +0xc869efb3, 0x69cb290a, 0x11a14ed3, 0x42368235, +0x017863b3, 0x15432de8, 0x8d0b6425, 0x6c6f0e82, +0x3a7fdf0b, 0x225904a1, 0x37229f59, 0x251ad3ff, +0x158254a8, 0xd4bac462, 0xe3758a8d, 0xc7a08828, +0x1edde045, 0x30455103, 0xb8d726ef, 0x7016a004, +0x0ef0a9a8, 0xc0bc1d11, 0x4ba776b5, 0x3991d148, +0xcde6147e, 0x91b04026, 0x87fb0948, 0x5d2217ab, +0xf515e824, 0xb30ba80d, 0xe90cf52a, 0x7d3360e1, +0x418386a4, 0x22ca0e0c, 0x0bb50a0e, 0xc31269a7, +0x50050b0a, 0x11d5c818, 0x8417fba9, 0xf6ed4c09, +0xba80919c, 0x45ad0250, 0x5db49f14, 0x458ac2d0, +0x40ec42be, 0xdb100389, 0x06e90161, 0x02b71088, +0x4019467a, 0x10e2ca8a, 0xd03a20b5, 0x7900d009, +0xa09bc46f, 0x41889809, 0x43e68a0a, 0xb52a8a01, +0x06ba736d, 0x050d0c02, 0xe045ce65, 0x8d56615a, +0xb5bc41ec, 0x16e55116, 0x08f0e03c, 0x110bb507, +0x1a2c5d83, 0x15ca341d, 0xa46ca338, 0x455a009a, +0xa0dac534, 0xaf21b86f, 0xec4b27e8, 0x9ce83445, +0xdd452a14, 0x0a1059ba, 0xf1035017, 0x9889fd61, +0xffff11eb, 0xcc488d52, 0x5bdc1659, 0x518b1865, +0x41425d47, 0x3e171bc0, 0x0bcb3041, 0xe983c3b3, +0xa0dbfe74, 0x2bb6049a, 0xa0104f43, 0xc70466fa, +0xf4cc1c46, 0x028dbcd5, 0x89546b34, 0x202a8419, +0x7159b401, 0x7d14a8a4, 0x29831aca, 0xa659841a, +0xf4cadf69, 0xd31efbb6, 0x419600f7, 0x4a75893f, +0x3726ec15, 0x0fd0b117, 0x63173e02, 0x52c71b06, +0x45bb6f15, 0x06ebeb54, 0x5bf63850, 0x0682b02f, +0xde8a4ccd, 0x43c2c004, 0xbc05ad04, 0x3e75653c, +0xbb8a1060, 0x0ab904e2, 0x0f80d678, 0x89c48685, +0x108afead, 0x0f455d60, 0x211834a2, 0xeeea2cc5, +0x881fb6e8, 0xa81c1645, 0x102f001a, 0xf1afe8e8, +0xdf74ca84, 0xebe3ff3f, 0x4c9a0d31, 0x3b9a2ae5, +0x65b0ba9f, 0x01306dcf, 0x085509c4, 0x58080fd0, +0x04a3bc56, 0x145e078e, 0x245352dc, 0x0a4dff5f, +0x8a3f54e8, 0x400b6fa0, 0xe55cff1e, 0x3e87ade8, +0xeb1b7eab, 0x740c3d30, 0xe8101ff1, 0x036a2031, +0xcdfa6fe4, 0x5540c281, 0x01115e40, 0x058ba3c4, +0x90ba9cf0, 0x86cadaa2, 0xab107f10, 0x66296d82, +0xbcbb4afd, 0xf1006c11, 0x25f6e280, 0x5efea8a4, +0xd2e99754, 0xba236c00, 0x39661c51, 0x58bf8526, +0xbed803e3, 0x433a0446, 0xfcb30b16, 0x8138bb06, +0x433b0780, 0x45019920, 0x87d0a0db, 0x0666e091, +0xb8ae1c00, 0x9b2453c4, 0x081c74c1, 0xef865058, +0x0395e91a, 0xf6577768, 0xd62e0406, 0xb11c96da, +0x95270a97, 0x05ff9e79, 0x915829fd, 0xfd896d05, +0xd450e5c1, 0x4066bf58, 0x28430903, 0x60584258, +0x1fadd1fb, 0xbae983ff, 0x28cd7c2c, 0x593550dc, +0x03165647, 0x41bf7f23, 0x60e8dced, 0x2c6b015f, +0x014cd231, 0xb6b91139, 0xa65189d4, 0x0685f85a, +0xd0d44741, 0xd2443d3b, 0x28e64b7a, 0x1f0b5208, +0xc2354782, 0x13ff1fee, 0x32b5bb37, 0x32616deb, +0x43dd4346, 0x8a18c765, 0xe2e40155, 0x22d817eb, +0x5489f446, 0x053f7ce4, 0x7c43d20d, 0x04c23883, +0x0ee85504, 0x6439672d, 0xbd4f0562, 0xd03d0c89, +0x46a3f274, 0xfd2a9b87, 0x84e98104, 0xb61f7a06, +0x408d05bf, 0xdba34a0c, 0x2c0f0510, 0xa5a03f82, +0xbd938ee9, 0xc4535700, 0x374ccdc7, 0x3a295456, +0x19d6c11f, 0x3142f882, 0x03047037, 0x8a24ad33, +0x1dda5108, 0x40b12f14, 0x29d2ea18, 0x4dd4c189, +0xf9370f01, 0x473fe86b, 0x3e99f515, 0x898d140d, +0x5c56e944, 0x7ea0700a, 0xbad7a3ba, 0xd7431508, +0x833e663f, 0x61002c18, 0x82a6d72d, 0x39067f4b, +0xba0776ce, 0xc4cbea8f, 0x0506b6b8, 0xd73b773f, +0x00ada07a, 0x61030821, 0x06dc0cdc, 0x1ab25798, +0x0e202762, 0xa763eed8, 0x89cfc289, 0x54165dd0, +0xdc8f6890, 0x2c5b16c0, 0xd491d80c, 0x8239e480, +0xc0c949a5, 0xe22be4e4, 0xe006b157, 0x7ad47da9, +0x78de362c, 0x1ee07d0b, 0xdc08742b, 0xdb7570af, +0x358e551a, 0xb3e83b8b, 0x67ff685d, 0x5feb4c0f, +0x8df07d89, 0xe813e455, 0x2de08a15, 0x780a458e, +0x8dd0c736, 0xc0db6040, 0xd999aea2, 0x3b7c9e25, +0x0d4794d7, 0x0046fa89, 0xb6db8b15, 0x7a30d412, +0xff6f8179, 0x77d41068, 0x14cd81e2, 0xd04780eb, +0xe9c93587, 0x5019673f, 0xc67fa6fd, 0x5251e389, +0xe23ea56a, 0xc5178aab, 0x2b3517b7, 0xe027236a, +0x6ea08fe5, 0x5ca6c5a3, 0xc63fc30d, 0x18ac9aa0, +0xb87f18f3, 0x8aeea93a, 0x88f0e8b0, 0x507f0d68, +0xa577a0c5, 0xfb5321eb, 0x56b0eaa2, 0x582eea5c, +0x4033645e, 0x72505835, 0x354122d0, 0x2bbb4f81, +0xe0bbb78a, 0x35e8228d, 0xa83b1ccb, 0x9b27313a, +0x9a20e8c3, 0x7a3dd014, 0xa08c18eb, 0xf3c6b8c1, +0x7be82adb, 0xbd0c4621, 0x82e9661c, 0x0d41c166, +0xdf69548b, 0x73b18be0, 0x9214b23c, 0xe0ca8314, +0x40b4aa06, 0x13f81886, 0xa2a6edbf, 0x0c75e91c, +0x140f100a, 0x8e4430b0, 0x02ebfb68, 0x178834b0, +0xbe161019, 0xf0395c82, 0xba1c2cf0, 0x15aa2c9a, +0xb820bc57, 0xf3220f00, 0xdafd2b4d, 0xf4a1f003, +0x9422fc04, 0xc6007872, 0x241418fe, 0x2bd78bb9, +0x444b6c84, 0x4303a191, 0xd9a2c87b, 0x8e84b6ba, +0x1106c60e, 0xb5f50aec, 0x8784e8af, 0x6abed836, +0x84a1f01f, 0xf618c73b, 0x9444bc17, 0xc77cfa2b, +0x575b1843, 0x104962d4, 0xd0668b0e, 0x1889f750, +0x0eeb2d8e, 0x6bc47caa, 0xc8870975, 0x20399dce, +0x265e6184, 0x06401120, 0x982424a4, 0x6f187511, +0x82148c20, 0xb6d2aab3, 0x1a282f47, 0xbc2c2c1a, +0x38ae177f, 0x7bc4fb55, 0xd8877d45, 0xee311284, +0x1b135001, 0xdb49f4ed, 0x168ce270, 0x2d1088df, +0x8be8c8a3, 0x8836c9e8, 0x16072183, 0x68921cb7, +0x9c06fe76, 0x89561424, 0xeeebae07, 0x39dde04c, +0x1cd548d3, 0x5e85873d, 0x890ff1a2, 0x31b824f7, +0x528adfea, 0x394bb85f, 0xebf177eb, 0x7827ec1e, +0xe1072222, 0x8d8b0beb, 0xd516f112, 0xfee818f8, +0xab761ef9, 0xde72618a, 0x7b6aed34, 0x3b6d8e21, +0xec774e9c, 0x11740a6b, 0x51cdf805, 0x0305ed90, +0x47e92b74, 0x2651462e, 0x2c2a74f0, 0x610153e0, +0x40ca8f11, 0x19e80a47, 0x4c98acd2, 0x49bb3991, +0x9c250ceb, 0x051862b9, 0x027fbd19, 0x74a04915, +0x24f0f054, 0x27f1b678, 0x05eb888d, 0x4c020d86, +0x09cea882, 0x870d461b, 0xc7e7d588, 0xa2dbf303, +0x7beb75b5, 0x22650018, 0x2e26a2fa, 0xf36962b4, +0x6a62779f, 0x1012ef93, 0x130c5556, 0xd6e0181b, +0x62c22802, 0x4a93a9ff, 0xa42a1811, 0x0ccebc23, +0xba0916f5, 0x14175b1a, 0xa8ae506f, 0x810ca23b, +0x13dea5ea, 0x1ee25415, 0xa0baf088, 0x221272dc, +0xa42a0af4, 0xac23a3e8, 0xc56effd7, 0xcaa1baf6, +0x8ddd23c4, 0x5d7a0f86, 0xc7f28ea9, 0x9ca0a991, +0x97deaa05, 0x81f929eb, 0x961f15e1, 0x428dc540, +0x4724723b, 0x00304001, 0x25c1aac8, 0x012588c0, +0x08e66703, 0x28ab0d70, 0x0a05a062, 0xa144f8af, +0x12952d00, 0xf10120ca, 0x019731a3, 0x68233fda, +0x4dce7ea9, 0x204d1675, 0x2ea0d83f, 0x026920e2, +0x182389e4, 0x0fe05a28, 0x00c9e421, 0xb9f50068, +0x597cdfc5, 0xc1066143, 0xcf6c3c4b, 0x8007dedc, +0x643d8393, 0x04e971f9, 0xaf0f5734, 0x80acf88d, +0x9d87c59e, 0xd61fb90e, 0x8e8a40aa, 0x0e7407fe, +0x5f4d535f, 0x13d13f75, 0x266d0b88, 0x2bc8b3be, +0x80672d0c, 0x361e4586, 0x42f12dbb, 0xeadb2f11, +0x6842ff4a, 0x6544e8a3, 0x297b0f23, 0xef76296a, +0x058327c7, 0xd540fb33, 0x05a110f7, 0xb888aa3d, +0x0628d1a5, 0x88304a95, 0xdf1b15ed, 0x2636aee9, +0xb37826ec, 0x2478a237, 0x4f0904a3, 0x2a1e6042, +0xeb4108a3, 0x14688b5b, 0x250dbc2a, 0xff0cd204, +0xfa285e25, 0x081c8d01, 0x8d5d77d3, 0xd901fe7a, +0x750ba61d, 0x011d1a8a, 0x00907866, 0x8bb88941, +0x528d6841, 0x16c3d401, 0xebc840bf, 0x76fa393a, +0x85e1343a, 0x378a235d, 0x183b003a, 0x42c3e1da, +0x72cf735c, 0x88e4e601, 0x76f42c42, 0x58730400, +0x2f1f7ef8, 0x727d053b, 0xd0d2b68c, 0x41534310, +0xca66c9c3, 0x25451ac9, 0x2189e5a0, 0xf700a5c2, +0x13c31503, 0x71548d80, 0x00ea3f68, 0xa87dbf00, +0x12603bfd, 0x48033c2d, 0x3c070300, 0x70672123, +0xd2d75dec, 0x14266578, 0x29031a16, 0x0000010c, +0x23dbb2ec, 0x201b111d, 0x0b231f11, 0x1b066b01, +0x2c290726, 0xf81ff61f, 0x400b2fbf, 0x50106020, +0x026c7030, 0x03050106, 0xa8160007, 0xffb8ffe5, +0x7f21cd4c, 0x01464c45, 0x6c4b6f0f, 0x03e9a69a, +0xbeb1a562, 0x34d96137, 0xb6a2b34d, 0x64e7d4cb, +0x3bb6cd34, 0x62070be6, 0x8a781e03, 0xdb3baeba, +0xa81b639b, 0xbb03d307, 0xfd91d00b, 0xff03ebf6, +0x68020188, 0x01010202, 0xffff0000, 0xa4787fff, +0xb756d76a, 0x70dbe8c7, 0xceee2420, 0x0fafc1bd, +0xc62af57c, 0x46134787, 0xe201a830, 0x95fff002, +0x98d8fd46, 0xf7af6980, 0xeeb18b44, 0xffffd7be, +0x22b65fff, 0x936b9011, 0x8efd9871, 0x21a67943, +0x6249b408, 0x40f61e25, 0x51c040b3, 0xff265e5a, +0xaaffffff, 0x5de9b6c7, 0x53d62f10, 0x81024414, +0xc8d8a1e6, 0xe6e7d3fb, 0xd621e1cd, 0x87c33707, +0x6ff4d50d, 0xedfffffd, 0x05455a14, 0xf8a9e3e9, +0xd9fcefa3, 0x8a676f02, 0x428d2a4c, 0xf6810939, +0xff0bff71, 0x612287ff, 0x380c6d9d, 0xea44fde5, +0xcfa9a4be, 0xbb4b1ede, 0xbfbc70f6, 0xdf7ec6be, +0x9bffea51, 0xa127fa28, 0xef3085ea, 0x1a8305d4, +0x99e5d9d4, 0xffffd5db, 0x7cf8e6ff, 0x56651fa2, +0x2244c4ac, 0xff97f429, 0x23a7432a, 0xa039ab94, +0xffc3fc93, 0x37bfffff, 0x0ccc9265, 0xeff47d8f, +0x845dd1ff, 0xa87e4f85, 0x2ce6e06f, 0x014314fe, +0x0811a1a3, 0xfff6074e, 0x537e82ff, 0x3af235f7, +0xd7d2bbbd, 0x86d3912a, 0x110c07eb, 0x1f050316, +0x09e40fc8, 0x0b04140e, 0x0a061710, 0x3443400f, +0x3eac1590, 0xbd87cc2a, 0x2e6425e0, 0x50490002, +0x2dd8ffff, 0x077069c2, 0x64646120, 0x73736572, +0x74656e00, 0xfd73616d, 0x6bb3eeb1, 0x62757314, +0x4410200f, 0x75616665, 0xdeeb7e6c, 0x61670cbf, +0x61776574, 0x3a462879, 0x54544855, 0xfdf62f50, +0x47120a05, 0x25205445, 0x12200173, 0x3fffdaff, +0x0d302e31, 0x6573550a, 0x67412d72, 0x29746e65, +0x2e302f85, 0xee6b5a39, 0x2b332eed, 0x736f4818, +0x01092b12, 0xc7dbb5db, 0x74680042, 0x6f438b74, +0x4c2d2c29, 0x7f7dd604, 0x6874677b, 0x00534e74, +0x07736e64, 0x02764920, 0x8035bdad, 0x2f3a2613, +0xed73392f, 0x6b776c6f, 0x2f636f63, 0x6b6ca474, +0x657a6973, 0x6cd4b70e, 0x7403926b, 0xaf6dec0a, +0x6d68ddce, 0xa8bc6369, 0x62465477, 0xeeb5823b, +0x41156db5, 0x526578be, 0xf61b06c5, 0x00495e1b, +0xe15a1744, 0x73004197, 0x062a7263, 0xfc69640b, +0x6fc285ed, 0x282e295f, 0x6d1a2029, 0x6f206465, +0x35a05f75, 0xac6a1428, 0x206c6179, 0x60dad63d, +0x2e58d26f, 0x3277260a, 0x16df279c, 0x6dee44b6, +0x251b6e5c, 0x74322e32, 0x78b5bdad, 0x6e20681d, +0x2f2e256f, 0x17b5b669, 0x796c6736, 0x65636397, +0x232c0e77, 0xdd6db6fc, 0x9d634864, 0x12653621, +0x30316f72, 0xeb7ff730, 0x6e49402e, 0x4519694d, +0x4f525045, 0x6863204d, 0x6e0b2165, 0x6d756df6, +0x5868232c, 0x59d90e2c, 0xd85b5c69, 0x625b4e0a, +0x52ba6fc2, 0xd6bdbb7b, 0x5012350e, 0x64166968, +0xdd577665, 0x656effb6, 0x44202056, 0x38333850, +0x70323034, 0x6d666938, 0x15afec3b, 0x2c70753b, +0x673d2042, 0xb6dbec30, 0x20728d6f, 0x6f3b3332, +0x56c46865, 0xb11b8581, 0x6b6e035b, 0x617445bd, +0x6d6b7645, 0x647b266b, 0x32a6181b, 0x0b640639, +0x903330bf, 0x30383238, 0xc06d4e31, 0xdd613213, +0x0d32162d, 0x13213433, 0x36352132, 0x36737d9c, +0x35386337, 0x74653236, 0x3613610e, 0x2c628761, +0x35640d63, 0xf421215c, 0xbf62090b, 0x3538fec7, +0x076d7131, 0xbb726539, 0xb50d3536, 0x78cb60d9, +0x3d076d5d, 0x8360d81d, 0x7a173207, 0x7d843558, +0xe11d86c0, 0x30303235, 0xf854a10d, 0x415723b7, +0x4e494e52, 0x7039d947, 0xe50a35b1, 0x1205ae6c, +0x192a6043, 0x61721dbe, 0x217a7070, 0xa5efc236, +0x611f59ad, 0x200a3a56, 0x9e410a0c, 0xb46c3e18, +0x94732685, 0xa9846f62, 0xd8285c35, 0xaa7973e7, +0xb1b6296d, 0x155af60b, 0x20303214, 0xf6ec5a5b, +0x4b6fceb6, 0x044f0a5d, 0x0e2c736e, 0x15cd85da, +0x6e1659e0, 0x6a7567f6, 0xc3dadb46, 0x826f730d, +0x55564e42, 0x8db1db1e, 0x2036204d, 0xbb224122, +0x96116e32, 0x56f1641c, 0x0f656576, 0x5f5ed66c, +0x20a4471c, 0xeb76d83c, 0x66391d68, 0xb03e65ce, +0xd8d16743, 0x428d239a, 0xb2977739, 0x3cac9d8f, +0xfb75201f, 0x50434844, 0x2cd96c7a, 0x7d601dd9, +0xc16ec2b2, 0x2520653e, 0x706cce7c, 0x06c26d7a, +0x5d7b5b6c, 0x0e002e0d, 0x050b84c6, 0xf503250a, +0xfb236465, 0x5592cadc, 0x688c6e5d, 0x166c6e05, +0x6c70e944, 0xdb5a170f, 0x2af5d4ad, 0x9a666f47, +0xdd9ddb6c, 0x5a3f3438, 0x066f6684, 0x5e10bb63, +0x2507138b, 0x6db846ba, 0x044356ad, 0x7d90126c, +0xd5af5c75, 0x2f653a3e, 0x09085f2c, 0xe091d330, +0x824cec29, 0xd8b5282d, 0x143c2023, 0x0ad874b8, +0x45767b1b, 0x3c3d6535, 0x561b0ad6, 0x66726136, +0x1cc9fa09, 0x3673c20c, 0xe7131b53, 0x20ae6406, +0x29cf575b, 0x3023fded, 0x6e2d5b39, 0x382d2d7c, +0x142b5844, 0xb0ce3e62, 0x2590102a, 0x8b5bd921, +0x905855d5, 0x6d572326, 0xbf611214, 0x7528ff02, +0x7571696e, 0x652eb365, 0x632d2c64, 0x516d8cb6, +0xf197b06b, 0x82311996, 0xdb1a3a6e, 0x66185d92, +0x1b686328, 0xcd106753, 0x5d106ed1, 0x1521646f, +0xc68dbd72, 0x847274d6, 0x6c76516b, 0x75ef781d, +0xcf0749ef, 0x07730773, 0xc5e3b62c, 0x00323d3c, +0xb6054246, 0x2060f62c, 0xa70e3442, 0x0f0bc26d, +0x0f795300, 0x753a7861, 0xf85839d1, 0x72e00c68, +0x4f373ccd, 0xe7b48486, 0xdacec63e, 0x3610a43d, +0x3daa047b, 0xadb2d87e, 0x20a7c125, 0x2eb1f532, +0x6e690f60, 0x3d431864, 0xf6230b08, 0x776f1906, +0x0cd73600, 0x4948293e, 0x756f728b, 0xb5f185a1, +0x0e33b3c3, 0x58ed4365, 0x6312b5b8, 0x6811702d, +0x056f4242, 0x6fed06f7, 0x4153f811, 0x4238334e, +0xcb6b03b2, 0x841f628a, 0x1b6046c5, 0x3c136e49, +0xe2f024f9, 0x003ef821, 0xd7e20ca4, 0x2dc74bee, +0x03450042, 0x35352d20, 0x6ba14113, 0xdb207d51, +0x00f0bdd6, 0x2d6ccf43, 0xd2641b43, 0x17648c63, +0x1c61d8cc, 0x9e581800, 0xb50b7469, 0xef5f642e, +0x2058146c, 0x085b342e, 0x1b66b091, 0xb6edb95b, +0x023b002d, 0x6d300848, 0x944a3203, 0x317b176e, +0x160e313b, 0x3403333b, 0x1b172d16, 0x84664f8a, +0x52e84080, 0x798ff981, 0x8c962e0a, 0x73257091, +0x41a49010, 0x708146e1, 0x5b6f61af, 0xb845389e, +0x7769617f, 0x9b2dd2cd, 0xfe250669, 0x6e9a5371, +0x8931ad43, 0xfb0ae76f, 0x9b494b78, 0x6b1ce009, +0x23237a04, 0x1a626d49, 0x30ec163d, 0x227a62c0, +0x90667ac4, 0xd5a743a4, 0x43cec16c, 0x20f91bc2, +0x7b01091c, 0x2e6f4ed9, 0xd71e726f, 0x2c434092, +0xd9651023, 0x2cdc0f4a, 0xf66575f4, 0x19b60207, +0xa5290261, 0x2151a3b7, 0x85032009, 0x238410b3, +0xc5006e70, 0xd6920064, 0x168be3b0, 0x5b91293a, +0x0b3a174c, 0xa176dd7e, 0x0658542b, 0x06450564, +0x3b705852, 0x0c05cdfb, 0x7a390a5d, 0xbf796210, +0x3ca15adb, 0x5d0f348e, 0x414f4c05, 0xc0444544, +0x0973c2fe, 0x43538dce, 0x62204953, 0x16d1abf0, +0xe0d940c2, 0xb3091ab8, 0x63c727d8, 0xfa19ac01, +0xad966952, 0x04323b26, 0x7dc452a1, 0x23494270, +0x4fda8d13, 0xc2a91e53, 0xb090b034, 0x6b78ab50, +0xb0c1aa1e, 0x70611b2d, 0xc7055056, 0x3834e533, +0xe15ccd2e, 0x638c7488, 0x876f74ca, 0x30decafd, +0x6e551cda, 0x614f2535, 0xf7d6a5a6, 0x4b8e2d3e, +0xd8b2c20c, 0x6e4d3b65, 0x9c2b532f, 0x77671e2e, +0x73477177, 0xab6b82d2, 0xb31a69ac, 0x5965035b, +0x415bed96, 0x46434402, 0x90b02e42, 0x007e33fb, +0x4e013730, 0x003d4476, 0x16340aa6, 0x59657211, +0x6f1e15f1, 0x0f1600a6, 0x75497a62, 0x52b9a9ce, +0x35437b1d, 0xac386d05, 0x637bff53, 0x420d7462, +0x00544f4f, 0x201ff74d, 0x9c409c08, 0x566c9d3d, +0x304317ff, 0x00786c38, 0x7d007b24, 0x859eec00, +0x636a4158, 0x5985f6c7, 0x096a982f, 0x006fa2a3, +0x036e484f, 0x3bd78753, 0xc2e26958, 0x9e030926, +0x10210a35, 0xd642dd90, 0x6f6e1e6b, 0xedb472c3, +0x84675c3c, 0x2c9b9677, 0x00228946, 0x5c633af6, +0x08d816e0, 0x6d5d2f3d, 0x258463ac, 0x000a4b64, +0x45f66d4a, 0x9df85880, 0x43642015, 0x20c5e15b, +0x72ce8cc2, 0x6ddb7b92, 0x6246f504, 0x655e776d, +0x0cb9241c, 0x6d8154b0, 0x1e165db3, 0xb1532816, +0x692e2e72, 0x1a867467, 0x0d8853c4, 0xc2853a8f, +0x371e32df, 0x454d554e, 0xf19e4952, 0x0da7864b, +0x416c253b, 0x7257fb25, 0x1c3c0680, 0x62861617, +0x8bd6b6c7, 0x0538b4c1, 0x42063631, 0x9919ae6b, +0x320b2658, 0x3c07584d, 0x00e12ab5, 0x19092039, +0x898cd20c, 0x920f00f9, 0x6e2de335, 0xf62f5346, +0xd9c6d70a, 0x72653a0e, 0x73f45035, 0xd681ec02, +0x79705136, 0x08507106, 0x103ba8cb, 0xcc73c937, +0x6cd0e136, 0xe35825a8, 0x1e17ebe9, 0x4000fbaf, +0x230b3f00, 0x304e0b0f, 0x6d601834, 0x03d80461, +0xdd5aadfd, 0xcf323191, 0x39383700, 0xd5434241, +0xef28d85e, 0x4c163ca9, 0x60063e4c, 0xf12f7d84, +0x22003e50, 0x868d043a, 0x606bd0eb, 0xc0f9bfba, +0x9c1b8243, 0x54ae4341, 0x1d6e5143, 0xed63d56d, +0x64750744, 0x6eae56e1, 0x542e6ebc, 0x8b39304c, +0xe127fc6f, 0x553a992e, 0x4f4e4b4e, 0x48e24e57, +0xb7585041, 0x2c046302, 0x49005112, 0xc3413429, +0x4e72b0a5, 0x54513d1b, 0x48b269db, 0x68530e3d, +0x2ddb5429, 0x0c798dd2, 0x13484e3d, 0x4d567541, +0x70b58b1f, 0x4e22bf5a, 0x353d415f, 0x61d8b1f6, +0x0a164e0a, 0x78303d52, 0xc833c114, 0x43644972, +0x44a26d48, 0xf49b442b, 0x593d7417, 0x8bedb6c2, +0x6188441c, 0x5ab79d10, 0x526cff74, 0x593d5432, +0x8c4d5265, 0x637663bd, 0x277b63c1, 0x28e46797, +0x31b61f3d, 0x393138f6, 0x53421e32, 0x36321473, +0x058c1232, 0x343431ee, 0x8654ba56, 0xc1ed616d, +0x137ddf32, 0x57b84c52, 0x15ed4e68, 0x19f63f40, +0xc167864a, 0x953176fe, 0x7dbb99b3, 0x8b554450, +0x88b0724f, 0x52b09c81, 0x1b6e2947, 0x4a724518, +0x736dc034, 0x7e42a372, 0xd6de04f0, 0x576c0587, +0x166900d4, 0x6c870c04, 0x13e8a72d, 0x48cee6c4, +0xc132729d, 0x98c9ce65, 0x10af76a3, 0x0d862052, +0x7022c2e3, 0x724322c8, 0x0510c919, 0x6b420031, +0x41dd09c6, 0x1bc9d63e, 0xc50210c2, 0x419507ad, +0x4e433906, 0xb6181352, 0x69e22872, 0x8841297b, +0x797878e7, 0x047f6864, 0x2cc6484e, 0xa70b5112, +0xae6e327b, 0x49433854, 0x78252ed2, 0x65898048, +0xa15458f1, 0x098dd022, 0x823f50f1, 0xca647a2e, +0x8e88dfc1, 0x003564f4, 0x7276410a, 0x44082d82, +0x0a2f6cc0, 0xe4cd1eb6, 0x9d0787b2, 0x01d13cd9, +0x3e19c1ae, 0xce22039f, 0xc5631af0, 0x68fe1166, +0x1ab0d00d, 0x0a6b6da1, 0x056a3354, 0x04158788, +0x1ddded75, 0x3e73c702, 0x1d000a6c, 0x09d8bc5d, +0xd3c150bd, 0xd4295142, 0x2e08b0d0, 0x8f65132e, +0x1f3ec08e, 0x0d00200d, 0xb068c646, 0x38012889, +0x2033c178, 0xc45f7856, 0x0e65a70c, 0x30d1061c, +0x696de911, 0x6d23bc7a, 0x76701918, 0x89748a8e, +0x63b9d125, 0x744313a0, 0x2304183d, 0x0410adda, +0x6e85a99c, 0x2fe37010, 0x060e063e, 0x4e5ac1a1, +0x861e7503, 0x49170700, 0xa0600e54, 0x48282cd5, +0xba0f0055, 0x741dcd65, 0x84d18559, 0x6cb74e92, +0xb18266dd, 0x1fbf7043, 0x6c6e1766, 0x4c61bdbb, +0x70705c3d, 0x2cc67423, 0x5f25f7ec, 0x7474e670, +0x18dd08ca, 0x6f9f3052, 0x09520a45, 0x082500bd, +0xc8fedee4, 0x54414624, 0x470a4c41, 0xf41b7461, +0x2da095a2, 0x41aeffc0, 0xe8b03609, 0x756e6644, +0x6c0c75de, 0x4d2ec2e1, 0x8e64f20c, 0x27adb349, +0x91075098, 0xc5a16eec, 0x53006476, 0x62781206, +0xb7816029, 0xcb0573b5, 0x52e2c173, 0x45f601f6, +0x000005e3, 0x030119e0, 0x320c8320, 0x9bf8f0e8, +0x00e4f6ed, 0x060e971a, 0xd80397d5, 0x5d929802, +0x07e22dd7, 0x179f03df, 0x88b77c65, 0x0f41f6e5, +0xa099e103, 0x3b0bf090, 0x036edec8, 0x563b04ea, +0x3500000c, 0xf616fd1c, 0x5f105a20, 0x85109d77, +0x1f96df44, 0xefb90b5d, 0xf617c6ff, 0x16483b02, +0x5797d4b7, 0x64720b9d, 0x17b718b3, 0x5b340095, +0x056b08f7, 0x730714df, 0x6cbec02d, 0x193479d3, +0x0005981f, 0xabbb5200, 0x80e61626, 0xcb3d0332, +0xf46fba20, 0x58f20060, 0x3f80f240, 0xd823483b, +0x7f1bb622, 0x1b472992, 0xec765700, 0x2bba3723, +0x3c8f2386, 0x102980f6, 0x103007c1, 0xd11031c8, +0xf23c8f32, 0x33df10c8, 0x1034ed10, 0x091035fb, +0x9f91e479, 0x17103607, 0x38251037, 0x91e42c10, +0x10391e47, 0x3b103a34, 0x9144103b, 0x3c791e47, +0x103d5210, 0x6e103e60, 0xe4791e47, 0x59741051, +0x12097c10, 0x1e122784, 0x8a91e479, 0x29901228, +0x24499912, 0x63c8f6a1, 0xa9242fcf, 0xb1245d07, +0x4fb91050, 0x0d8f2236, 0x106507c7, 0x69ddbfcf, +0x07c786ba, 0x0f305201, 0x89da0333, 0xa6641e32, +0x092f0034, 0xa007d81a, 0x481f68ec, 0x11000113, +0x71800b2a, 0x3d7f776e, 0x5535bb02, 0xa6000018, +0xd34db234, 0xc676039c, 0x33346c71, 0x2eca1f65, +0x1e405307, 0xc8000001, 0x38060aee, 0x7f011f20, +0xa6ecb4ee, 0x41600348, 0x0807eb4a, 0x6d9a664a, +0x68009fbd, 0xfcbdb3b0, 0xc2765463, 0x651c6b3e, +0x0355881b, 0x03768c4b, 0xc515b74c, 0x078e51b0, +0x3c9dcf98, 0x6a5c87b0, 0xba033f90, 0x2b7e4ec1, +0x5ba4768d, 0xb2b00798, 0x03c161dd, 0xf407c413, +0xc2f22b7a, 0x0fd0ee7e, 0x5f7d3803, 0xb26e7d9b, +0x1bf0773f, 0x00803403, 0x8607f8b3, 0x170381e4, +0xe847dc47, 0x27ec215e, 0xed1f8249, 0x6c892b89, +0x834ab783, 0x0301bf00, 0x641c217f, 0x97204074, +0x04040201, 0x32436008, 0x8b0004d9, 0xdb2facd8, +0x94dd6200, 0x3300ff07, 0xd34db3a1, 0x13a0ae75, +0x9b039a07, 0x2784a2a1, 0xa71436cf, 0xcca1ea47, +0x3ec703a5, 0x3ff4ee7c, 0x219eff03, 0xac420301, +0x65ddb3d9, 0x7e0b2d73, 0x03ff2bab, 0xb54073db, +0x3b290817, 0x6c19e39c, 0x5dc8e80b, 0x19d80b68, +0x6ca41bac, 0x0b702318, 0x1baedb28, 0x239874a4, +0x16300b64, 0x7142a507, 0x4bc0b761, 0x78f3b511, +0x899d505d, 0x211b4bdd, 0xf60727b2, 0x6dd34da6, +0xdd0766f8, 0x6bbbcf6e, 0x74d35d76, 0xc47e0d07, +0x75078617, 0xbc660cbe, 0xc38c7330, 0x5893fb1b, +0xc3d48f24, 0xb286137f, 0x431f7452, 0x5d11d689, +0xd10d0060, 0x83db3fc1, 0xab0f41ec, 0x2122c05f, +0x843e803f, 0x1f3f0775, 0xc0050239, 0x2e897d59, +0x02595e3d, 0x40203c01, 0x3d0bbc06, 0xbd72413a, +0x9f00303a, 0xee575514, 0xb60a4944, 0xfe208d31, +0x06036fbf, 0x110f0c07, 0x43423c2b, 0x74f1cbaf, +0xcc8e868e, 0xfb13cb17, 0x6c8503e7, 0x00cc0c60, +0x17920073, 0xd9bfa3c2, 0x00d9c65f, 0x5fad87eb, +0x000123b0, 0x91ec8be9, 0xdbf11b34, 0xaf721f63, +0x580fff8e, 0x865603a7, 0xd83b0ae4, 0x0bb01b65, +0xba3fe544, 0x84fb1bae, 0x8d9700e3, 0x96080507, +0x9fb20307, 0x9bb72ec0, 0xbeef3be3, 0x19b3dead, +0xb58f483b, 0x0124ff19, 0xdae77403, 0xe8710454, +0x7a0387f1, 0x88722c8f, 0x02000124, 0x906eb7af, +0x6414ddce, 0x140b77f9, 0xf6201b42, 0xd52cbaeb, +0x5b430bb0, 0xd60b8000, 0x1081aec0, 0x083e0f95, +0xd00727f7, 0xb2cd3435, 0xa3079d40, 0xd96ab907, +0x0fef7639, 0xfd075729, 0x65cbc828, 0x07b620e7, +0x2dc60bce, 0xc8334dd7, 0xb6e5de20, 0xb9d34dec, +0x3b2bfb36, 0x44fc8607, 0xa6b3cb75, 0x07117f2a, +0x57f10c04, 0x4d97030c, 0xce4707d3, 0x18272e68, +0xe9b6cd5b, 0x0fd33f3a, 0x070d0220, 0xb4cb53e7, +0x2f33365b, 0x6c043fb4, 0xb3d90feb, 0xe7dab127, +0xfbe7748f, 0xdd0742b9, 0x27cd7096, 0x01fa0384, +0xb2150125, 0x02b2cb2c, 0x23181211, 0x2f083722, +0x102119cb, 0xeb330201, 0x74bbb64b, 0xf46a3c5d, +0x060ba007, 0xf6003983, 0xdced9ee1, 0x1c23071a, +0x3c1d0f2b, 0x2ee86b07, 0x4f285c01, 0x634e3b07, +0xd2e60107, 0x01722d3c, 0x980d812e, 0x991b4d67, +0x0ea73407, 0xee97353c, 0x5f18bfd8, 0x3507cd41, +0xb9e0e54c, 0x075d765c, 0x054303f9, 0x19bc15c2, +0x6ebb2dd3, 0x0369db4f, 0x077116c3, 0x3455b4fb, +0xb40bd758, 0xdb7b58b9, 0x83c0046d, 0x071bf42f, +0x2f001c32, 0x1baa20db, 0x29bce7e4, 0xc76f0b5b, +0x7cc8a26e, 0x43004977, 0x9c074ba2, 0xcfd3004a, +0x674b6375, 0x4f03483b, 0x020b5123, 0xfa2bb532, +0x8b47793d, 0x5edc70e8, 0x4f037fcb, 0xafe9bc01, +0xfc526edb, 0x1b039fe8, 0x0e0668a0, 0xae8e6bb8, +0xa353ae83, 0xe04a636c, 0xce7e4b08, 0xa7c8bb75, +0x9adc9b84, 0xe6b7e40f, 0x1450b7f7, 0xc77bbf24, +0x5d76db0f, 0xf413fa57, 0xa0ec8003, 0x1b1e0f1b, +0xb6e32f06, 0xf2031de2, 0x0997a71c, 0x5836f017, +0x12bf0a43, 0x0082912f, 0x8e86eca2, 0x4f12871b, +0x00004943, 0x575d136e, 0x3253db27, 0xfb867273, +0x76ddc089, 0x078c3f0b, 0x859387d9, 0x9d74d899, +0xe80ba817, 0x151b88f8, 0x23d74831, 0xf5f00bee, +0x7306c1ba, 0x23e70bf0, 0xf9ed0bb1, 0x23c17d82, +0x0b23f40b, 0xcebaebfb, 0xeb8bc5b4, 0x770bff53, +0x6e037d13, 0x5baebba4, 0xcc035ecf, 0x6ba30127, +0x53dd600b, 0xc50f73b0, 0x0f840b8c, 0xcd76cd05, +0x010bfd03, 0x829f0f05, 0x53f84d75, 0xd70f5f0b, +0xa6a310dd, 0x03770ceb, 0x04080fd8, 0xb8075de7, +0xb4030dae, 0x0b1c5e0f, 0xcd758317, 0x25430f60, +0x110f2f0b, 0x34758374, 0x0f4373f3, 0xdb273fbe, +0x1b758335, 0x56bf0f4d, 0x5f135f0b, 0x3ba3aea2, +0x1f0a0f01, 0xd320030e, 0x112ceed9, 0x15d63fc8, +0xed43cb2f, 0x0bbac1ba, 0x2fc00ffc, 0x161f4f0e, +0x9cd76cf0, 0x0ba2c10f, 0x36bf16ae, 0x75d5d700, +0x2b6c1b9c, 0x73104f2c, 0xbac30b71, 0x197e6cee, +0x8bfb002f, 0xba0f930b, 0xf775d61a, 0x0fa70ba0, +0x73a251fb, 0x0906e81b, 0x4c13c4a0, 0x2eb0805f, +0x2beda84e, 0x3a0b81c3, 0x22719c76, 0xe72002ad, +0xef8befc5, 0xb3bb09d3, 0x560304fb, 0xe0d27704, +0x5dc2375c, 0xa82fd71a, 0x0b226700, 0xed73372a, +0xd6231e9a, 0x8c03c02b, 0x09076f8e, 0x7baed73a, +0xe3ea6003, 0x69e78a3b, 0x4e6ea6ba, 0x488b3b97, +0xaa4d2c07, 0x1aa6ba86, 0x43fc431e, 0x451a0026, +0x00147226, 0x00000000, 0xff090000, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + +}; diff --git a/tools/firmware/etherboot/gpxe-git-snapshot.tar.gz b/tools/firmware/etherboot/gpxe-git-snapshot.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..b36545649bbaa7319cc79ed820024ad652494b38 GIT binary patch literal 1679784 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Ð<(Gh=W|*0=QkhJX0fHkc9_52 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

13bj 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
ca3*6d&e+x>JaWlGCm%~)A>}(&ew4?xum$l zJPxv#58#9zIyvoOJl-(|9;S2rMay-*L9*DxAn{fc>86%r)&OIhVE5q1aB^uje zQ9qaIeW4L#kh44^cmQz3Jecs{k&#TbVbT?lGaD$~sr)w1<6o#UViq?`4r_MKfZjhPT~I^T zmbulJmF(Rub&yPdbwsa#804%=u2`r-cjZ?iOLEF8mTdga>&CVe5@;Che~a638E{e* zF$;ELz-~Ab)l?o)NUEw0iZX}gMqLv-D7GiX8&mstboz>FgDsTDWG0RPU94U>1c-0w zebp6xRN04oj+4%zm(_aNTa?TOFq|Ym30nsE`@EJ*2nno~NfGK{#Dez8*;}hvVCET# zIF89+3mK`Q1EdNo8D|-T@yu4M6k>{W$u)co-xZ|-JRVcstIhs&`dAr2^ep_$);x)+ zKHB(JC@YUq;6uJ6R3R0ekWo{}9V0BFXvfDDTO^PF>UNq~v*f=P#h^+*;POYdu%O$b z=TPa{`maW(`TD=MN(`GWilfBzS$Y#?D7b-wb@JfJvVMZc(8)b(oTI~1scCZ0DMgV> z_9MF(V6S9MGDEkgw@Tab9d4F9h~I5qQNSW>0B*13^GpI8sfshHA~^DRB4<%2R3~kV z8Cf(#%VIE~N+~cm{-6GHb0a{3|Ni;U@ZbN&2-WX4!w~;N--h&UNZ*E&5kiaqr~kkM zG~g9IbH&dL@R^AI+fU#>)2k`}cZWv6dB^zwu}~=#8VzDX7#D0N!m#k`?&7cE?vf`M z6yUeOfZtg;DP8z-TQem^lKDmEplA-71W27CMAzU!D2ZJMqtSBl`B64CG9^B1pi@8Q zCExm)Jb{aN(_+3PR`ci^<=WS(H=md^k0O~3`*F(S68K4oJ=JeSLtPPsn@N-P4O_-6 zraqqN6_2UaV`A9xR5by0Tq(qhSLtODkwHVR`^)iB`{bv*HY9q4XEs;y!VE7}(j>Rd z4Exl{5hDhv(h9%FF8X2MW0}k~dR(E9!f^x^i7@mcG>qxpyeF0=XK~?LWKED8iFdDP znaF4o3yQKuy3qCH$+ojP(N~f=friCG zEnPC+@3!!bt?x9xGmOV1n#S=Nb0&LkwFPAhDMsG$61H?IkH!1Himg+H3F&Y0b`y%! z8j*|vVV3S~1e0GSIp=B3S&~li%yA3Iig4`w2K5o2vfMdWdeDhRqby}p40=;P* z1Or0*1wfp#lJT10%Gj&Pwk}Q9f^wCGTJL?a;aR*R=U_`<#sqD*Kl7Uf=4bgOTXyZ& zL{+mht+il9bR;BmFq4xkYZP{9sUW6I(VHGpCIhC=sd?Xv#F?2iyz!Y6)p4aBVwjct9t@74^c=c|BoBK;1w^q-QW_U%yHQ=-RlyZH@kAE#eJWTpoeC?%qi-ah z%tAjtUx=cDzl4VZ_0rK|Q9OReb~_sUg&51=voFTCJ@}``->k7!KZPx>;T@B_^N4FW z;?mK#WX{Z0H0;|HjHk2P_4KezgB)El5XoIaI3!6wHHM8Ri)Xs0Z#&JxD7*EvUQde; z*%qhLsZ6leQnhc42xQG3~(&d8HVs z6c#kW>vU4(_Z6{`{7T;IYX(nK?D``L{?s;Z6~q@vagKA8Y!2?sTv^r81dA)$G*3ZP zZk`^aGb06C+UPAH6-_J*`>>3uzz}_FZ4seBXr!AvLi&k)WUwKZ z42^>^N*N*@M3v@AFC?#Eby_c5oz_XSb%?QToz_tUqh*!`ph`!+5(g|U6e6^A)eF6Q zl60v6g$`2TLYAcLTj9!=n-q_A9~O3X0FmWjf1JUVE})O$$nw47g%}X#w*jk+zCLff zT3ByP9rwgwY?5F&(i*!0Ni*>4^W&qm9}_>lLZ`!O4YiSIBrCme?v@HCzL>*pv7z(* z_zsBK(?J7>w-c4=|LWxJpAzqK6kYfu;~IfyFX>`o^8kspSF_pl+13^|6}X(tv7x|L z5)Eh9aQ$xa0OFs`tJ!$8#)e?I!byN`1mSkr-F&*ak8<}_c*%`DH6E=tB7yB38*>V4 zH;NhpKo`Jk#@J8r)>gtx(Hh5`3WuG@GMwn5VTLTYxv(5-ZV72ld!LtFNv}gHW>%I- z90WHHb>lXU&R#bTTIX%db8Md+wtlwBO;mxx0E|EqcZo0LSxlAESF3RYwc?H z>3g(rnF1s;U}O;)iFfKCAdF>n5k?cLV-?=4%gn!QHX};Ay4C>|RuD&1fS5t_lGnq#wQP_eNi!dk;paG-l7E0lB7E?A7iH+jMHkT$tSu${G?;-h! zI1UXzr?BkkdZJLo!-#v%I)e1hQox&`iNR_BTYnH+YV4TJh(*sdj^M&tR8C@cl`6%e zq$Oo;N)?IFDd!`&HQ7*3D-d;PDZL<{t%mKhK|hzWyHYpC>(-H_q~$sl zbrabtd~x#{9(|LSBt5g`xa8Hs42Pk2pi3pXz$%Pz9HM~-lp@K0MpJC8^#KHlX)2nZ zWFXOIz<8Deu{Qfx<251crY$`32D7L0s8!o^r&72f$@BGbFBPoC2Cl;U#H@bI73y%C zaSg&IFVsNl?>4|ra(urU_oNytG>4Jmmnesh8JHi`jm|H<^V4R2Z89Xo7hP|d&#^K#nUU4czCgN=xhjn1 z9m)w5tJFv8(IW#Co4o{Y>~joP{`stNa>&qH%Pix81W7FRMH5D3Yogk!&M=p0w-PAx z!X-+xl%C~RC`Ja$&R_vh4NnoshwUyV{}rpd*;D{bJp?IIlC5TyYpq`gvn zan2WDy(wBJZ;yNL8b@zi-QJnVnj?sJZO7rf(`vnIx87SXQI87LV2iMkyf+`2*%wCb zd!NW)gYHR2u5RkRnyyhEO#*%5&EtY}NlSVS^adR9C9wuBwv87omGLHHC%>;sjy(E? zs!nz(!E4<+#6i}*W|xBwhA|gakyX&~V5Z789!LLx#ra-sbNkOE@SFrQ5ND@>w@;t6 zsXRXW7yuM@6B}7NJ)8Xfh zl&wtU#J%_JLn8>{g;(F({%wpSNB(*6^XIaT9KnAq*3Po#kt6uejm18~!&>bl^i^pC z2_xe-wvgcV+>|nrq!wpWTKR$I@u4olvA{aQtCyiHB-V0pl!~XxH9#!`tT~_pTEV1& zrlE9_`%f*pS4u;lh1(Ffe{P&Lzf6`id4c+CEI8Yhc_ynT|xpAOQ6T$zO7|$do>J zcKEXQ8i=5yqs%Sp4&zPIZ7MMs9mp{%azZo%^hn<>j4&Mxy;YWErb!+CT4R*r=OSiW zczgs_MOb=T7BsA0Yo@=OOw)!-U5eVep_niUB=e9JDugiN5??Z$NC;X!!GyvRSwS;+ zDbN?uR}D`Vu(5R9L&behe#N(}`*VqpRHs#SP?%zCJ!Zw3e(&m$e@7z5lnrU0^iem@4!>(nURlfTfqk^pr}# zNQL1DdW*adK|lf6@q9uG1kk)l8;<(@dAx4o(oa4}i5Z1!+H2(Vxhk<3?T67&9O-P- zttXod=-=}Z8TW|p)IF`yk-Y=}YvQPJam>SZGh5M$rRx;Z(3bT`H;vW$reGa#3S<)W zOFSuU{1WsP;q8`xV|n{E;oj90ub@tWWNh_)ink@@>(2Sh&m~>sRK-$7n(b<^q{rC! zL=eq5{D)v~?<3&N-#<};1nwfa9;m<>w*H#kvTy5==%-G@al1J(j|YPgdp?>{J3e7I zgTObXMRpwJHx}6A*|3zjZY(P;d{|_}#d}#SAqsSS9{Di6gSiO3^D>t#E!1|!il}YA zAg@4zjPaHP_8E)@$)+ONd%sUN?{uNQKkXbMz4IYpBL~LUC&Dfud-rhymR!su8Sr`y z2W<*qtx`Dt8tn`Xg{`nfBBN;jhf5ShwcyG=iu{4;QPZs#MIfiRXKw*<6r^Lc*cOcY zPl>0Pov27D+RMRx6SCN=TfB<=XA<0~XamYaTnvY_OcVz#Dnu|$i6tpVr&?w5a1}UeK!rxeKP!# z_stj!JRn7Xrc39zu{?0K(4ZrMO^j;+2Q1Jz;j7a3f*Sfaycht6QWt}}eOv=Ke{bKqu};uQQzwbJo6ch(rfRA`Fj>cVFVSUvGE z%jE@-$e8^{BDh(4@^w?CNnpCH@tt&JD)%Vo$Qu>+EQ9z0@d_?@HDNt0Ne!sH-^8*+ z1aR(MF71LMj!1zVD=L%nKHgbU%b$w?Ow3;q)lB+Pj2>FU7h_dzQJ)O9HcNsCxIYj8g~ z;d%QlSY*V;Kr%lm?upNwbxvP(8pl|R zR<>eWd>jVz@l;q*Ig1lxhp4jFs-|=S7urci@PAn_m(q|@*;r(1m~dewYB!SO0%kZE z8efsivGqY3Gf)f+hasSlvzoA2uSh^mK%F$PqjYx5(myxflRa7+^I7x?Qzi_7^fNI) zY#t4N^bW0!w-+%&8_?wM#M*aQ{x2DBPJ-E*Do(kCVHR-9N4d5QX2}SwyT^W7f_#Q> z1~;I!h4jKmlAKXRO(Y_ZBNDs~_G_Zzx3t}yfzAow4d$cICQhRwcYJzSi2_n^-sg>& z-C%fc@gCbZ7+%Zov0SLtbRfQ5>M2W0ieg!lppRA3;#*}$_F}*csGEc*E_KUB59#q7 zNKYq8Z5;8VhDD1q$*=F)(h#7E!sICgt>`J}i_9bPsk2V&{QMVt`kZBa^ZuWN{Qzt1 z636CTOsZhV_ZMAL;s(2#qof)O?@G4s4cbxJt#a*i8Xl#ckXMGCENGmGs7xPFg?B29 zu4Lo5f``^QaFI#G*5D__{1+^0jJ990POeH#btCbL=gM^16K)ZD_r@Y z?&C(YbIOr}U3;2v3RYmqRKsF~$}0$X#3*FzEDe?DD7mbAW2UzOht#Hfi2?sd4uruW z$21{9kQl{ZsgW*a!x@$GQQY>a<%%q-&cTtr)03lL5Gf=GiOwjGvPj6Da8)QMHw1Ol z%A=4CjTaGCOVpT@RtV-1>BUg`;5ba#+E9SDRXu}oc>4B9>rk|iCCD1xp7903K~Dc1 z)V9TSTUb9)c8w~P{42>W(Pj=o1+w=P72_dk@gf*S*SY`)7fj-qJS-reF;XU4Hq}*(ZxZ~RHGv<#5raDDP#om_)?h#15WeCE>Wt|QrQxh z|4XJem)Yy9T6L~Rs(wtOD#u*O;#||ItvnZpQKZXrJ^IF8p^FR!>mpi5Op~GLG|WcP z4-r*kUz?$cr%ApH74YFR+Q*CAS0+_m1j8r}^0x@dr9O3ZS~ZOK_}s>!bcM4BeA(}d zxC}qOB&k{3!dop<`cr&;%AmVA+y*PKE!U(<&X3@4Zg&K!DL`weLuC&dN1FeP)np9(O1dh z7mvO%Ja;KpLE9S#W00G<_?0My!h!nY$g4ZXA0c}pHvthJ)BB>pGCIP6CwYTtpX&Ldq~M$Sk@%2fXp+8?w|T|}75v@sn~oBL=WrjTCp zAyS>Z+}J)D4rz8?A>G(EBtDd#Eu{cFdnO$&DIEa4R@7V8h-P~LH2_ckdE*rq^h&oS zSabyed5Ksp7{jPOPi0Ry8$czy36`lAlxzPfi%gJLPPwkc6nqQmO8(r%Q&m?LjR?4a zg`ANt3Q<2gp(1mhUgQgUxkmr3Q+qM^zA+#(j~YSRJmp-Zl#-~vbl?{-NX>i9!@s~G z>yPC$?EiR-Pdp|Peav4A3@EP$rhb_jg0c?f+s+Y7Cw^+Nm@A6h)Wn^vE(c6yG=fvk z#eL|+2i!zMMj+tC^9g4xvl|ZI6fqRv7G-H2^b_PD>SP|vpoKH8rv8vrXBnZ%q zC#UDB$TnQonsoQ5LBmAEPxXDXDXYqBRyia_sx~I*%qLv&m4Kp9J~RBCoJcb3jLC{R zsbSKI+`aS6nn};)7HIEuy5D?o9bc`*Rb{6;t1T9lS-yrnKQ@AQ3eb*_9P8q}3IGkM_ zAlOJe!9vkV=9tbQzTHffA2d1(&Zz|Xm6{6`r(wo*I7>$+)4;$ew2||5<6WzF2nSyN^ca_1q{;c(sL9E5FhvcgsBUqphQcx2wCD-jfzePf<*NFp2BR0%H- zoiXYiO~;dTc|ulvsqql+j%<}qovl>;zHSC`yiH`Lj>5N5Gjf&--)7x3eFE6(?|yP#Ph;pT%Eh9x zf%y?)pG=!sD9Hg}LyrZEbQSa0-TOozivjC4gb4}3L=1>&CLG0^{`UB&cXWFCljX*; zr+{VI9~pd2o~iB;@*#*iLuupQ>G3NNY~D^-Y&{13cqA-iNB-n;jxl$S9x2EQ&$2r& zSIu4@ePeL$i(qygpg$oPljfdC<6o$A?2!`ATgPWdloVmGYKwcr;xSs!`UTdNt(mrJ zJxwy~pj}Kga7@``z29{u)@@H!a zmL8?zQO%mFDP(0<$^w!q3oQ+^MxAJ>UWSQ`K+i3vfEk01;>3e35@nx)MdW7Qnro+& zAW=_O*|wxi>5YTgRWwi;4?^iq#gU0id09BwEEGKuP&4MReU$bQ17> z#7sAB_eWC9KD1V#_IwJfgYe@r?1;gId;>yc$ zek%E?yGhHUu&VN?&JU#aiP#`GnotQ13mg_=QSqkoc;q@zF9lxuQd4B4dt#fMz-%jZ zq5#_rm~dbArlgU$3m91e5rs1r zheK7TO&b{j*U2mfYy%_*!8Lbj!QQUO$wgJ81lNxWZap7>pnl=Y}Fh^7tbJ z*S71sb|k-UwO_qncv4eXPT)4jKuDCI*wE`3_Y7209ETYFEnK@W$t1ywAQSF-=l4IL7L8Y*G+N5m^F+W5yKGd= zhHs(a!bh_pH3B+nxF7U0?~sHY4#wrX5yTi2A_!aSc4->-d4wV%X9QmO1M+0f!VP~E zl8XlPTHs=GOeXqBb1Y?}zZJ|21hrTWhmIU^xup=75rs|s9=|<0Z}-j{omZ`NqZ(qd zLKrYIMX{{vR0j+{`bG?(%k`y|{J4{4hG$U%>LIVPEX^dEP2vzW` z1;5&o1WLOEM!_Y9B~Yd?mhKa!3W*H^3nl83Ee+!^nuClS-3apqg<|1_p(UW1!JU=u zJPU4t%8qMq9<>@LZ!LvKm#j#HZ?LHbMg3q9;sPyVo4jqZ=>l?n3TzV|i<#>_ojv8k zB*VA@7Lk;+HU>HlmE5%0I=kK<`@VEQ=*W#=(%OzXBYQ7`N{47MK}XN_3A$9z+wOQ zp44`}?*C`_DcpbY?`Hq*b09wU_jil-f2US^lHLC&yGSp0pL+SvfB61yefP-w4uqfS zZA_Mw)#jR4hwZ`!5?6jO@(yC`2J*ug4HJ|98iLA6jrfwyezZCN@Z%=Djw6ulE|cF; z(8iL>vMJwl)gT>&i7*jy)o3UIDRwK0ae#3&KyzSdcG^NYQhBFdoxJs4apf!TYz~T5 z-#ZHXSaaR;6C7cRA0}7Y!u%3p=n5FTm#Dyzbobl~LXx3mrO3LN14j{Kk`)(wZ=&$0 zo-A6eUX(IMUPY?ZYO0CIa6Tek28VgyK7W1s_S|cn{NlZDbU?R>xP zz3nz|4O<{#OV8a8#QP+lyZZ6bqpk10^ZpC96|vfbaJGS8ay!F)q8u`I%@oqmM76S#>)U=MtmYd5I5CCOB!Nj00p;Nx5>-<5-EKAePC%zGV1fxn3mHA-((*>19|EZP>19^P#X*U{e7o>-dcX%EeDcTu<$gBuUM9% zRer}Hq5*d$Wh_(>iaj^K^9KWLB`&r-?jvGNaYJ7bv_CJYSs77-&>Y18v|sY~7Xn)w zKN`ainGWdVSujyowt5RF1(9~%IzhRrad_AP&G@hxy)e)`mXsZA(&y`|f5<`fG z6^wboC*>GF{D9=+{WFCW{NEpt9jw7SQsnDDmn4a|n{~~rwe1aDhxj|-td}*W-NN;j zfSo`WuxC(7XJ$B5P;Vp5CeU=0#NXRFyE}<0#43&RfxKst_R8eXv|svMM8hH6z9fdn zGmeVG4`h@z2dA0mi0t==BudS(0+jfC6u`+k3MQ8v**wTRu=FO?5yM!cZ9L_m;fNT5 zhaMH$+C+^_<;$pUIaOJlMdSle*N%ZUF04{;sp6RxMtI|>D(ul|rG54m9ObmppAjft zlAQZH1UmB|piBr$dSsJ_z~3lruB^OJrB~x#BnUReTJ&4%r1O(ZRd}Vm@V@1tH-6+# z6kFk|<L_u|F~Vh!TtS?%v0WmS6dU8 z|B51wKPG>hJYHvT|KdF!`mpPR#~`FVe$0-<6+>4r<}f&*W_j<)E=iNYiT_XMcxmxm;Ej!Kd|C;{YYv4tZ58c*IH9ns>g zPcic_ACu#w--X6_krJ*lPw@LM(#)s`|G(4NheH2i_@h|6lC?=e8p0&q(6E_NSjKQb4j}TeLv;ti9-FXP14(U*ByW{UrVLWE}P> z!PNuek)LLrxD3G}mpTAKPRy)Z-D_TjQ&HH3T(IguFm`}`V6tzxaJtuR9krV0z3x%t zT?^BJ7Y;B8Y(pHjQiC8|*gCz}?Vg<;Wh}c}gFp;S)^IHi!GcK|T7+JD4qt=E$d;Z8 z-<6(Y`bJKIpFL8+r0k)lO0uvT_#g#1oI?00y_$TWgWMSqZ+iqx&f+VdkP;U!ZC#l( z;U}f_&?pPO)_(eh;v7$-#W{Q&-~2lo@zs~}WOI!9@!w|li6k~56w7*f?6CObKw?t} z%MxCObyWZe(lhqjBiVTrT?_FUfG{Z;g9fb829lWJM68N=*UYek+$dV=KF0_Z(613K z3AF>tozb-OXPyW$qfezW--BCVW##;5Q)Ty7fhJI%T{9mHLBsDXelBALm`M<^ZpMFI z5Tx=^OltQh6HpjeTIyG3?ReLbZX{k$#UmvNrj-K0qSDN`EoVlE;v5zw)y;%KTD2+uyynAaAH25HFj_Sgr;MjQO@^AM-|tVa{KfCTZY+8qVP^Y4?i;x07k!2C_?FRf3!DX0!)PNJ z1hp&s|r#Hm3{49128af{&DAVbr$ z^oPnJZ_Z~-zvVqOo*@Wdkjd(YAJPy05g%makZlNAg0MnTu{%80+C;>3_JZk!@w1Y# z0pf%-T4p#k5RasIh8TsT3tpJt{MjTPUd@UKnOWs|fJF8B_tsFso*~5+a0gwJuASv7&LNefuqZ0s`_&ag{Xh4_0 z?|E+)!Q(r1_+Ace%H*ctB*6U^-dz`xnPp-r6WQ*#>|!LLp}j(OLs2mrD!PP>{2Y;z zU9fnj#Dc9WHoQ42RhMi`t@Jzlnq>@?T=tf*^=N0!f~obHQ>RVJC~#jZr=1{eQ`{8D zJ`{TW!w_r6K48XwZ5OytmKa&e8VpSLNrurZ5%QUs6fI@=!scSPi0$dTd&E@9fj_@6 znDy`0!SVHH;ZNTGgBR2NRsnLU|FZL>_O<@YXZR`De_-18;rZjACH}wKZatU(yT7yj z)&KW7e!lwuzWV>Z`u`UD`fM&dyZ?`!c*sFP;Iw>&Y}Mw$Yn(ScdQF=I7+9q^lctz% zO1ru$3Xt@~*K2E1f<&2H>dud7U{oJaZr4cw^r?JE!kkJ|%xb&hnf3fE>z*DUB|oD} zfL7O6Mh$7#z+grE#%=^jzf<18P!dldl0QQ5rSiCH!yH8e8lNjAR;iqbEMd|mfX4%m z;=>RT^vlsf#0wb?L*pF%`Zn6 z{_azBbnQNsA!5dp>AE}gpSA4kn-l5+Y6l2G77wJ9B*2E!o>3u1L}@*IC7UI{q%Hb~ zHBvNARx&|5D=P=-GoZd)Sph_fVNxDef>6Fke<;fv+OYEBu;mR^xZ~-Fd;5{$Ot$^q zo_JvWk+g@W(NEB=;}sB3XK_!twoKDghWbDQ%N$m_Sx!jVX zSNpJaa^8OV3)x9s2|PoL96)Y_`Y$BTMJb>!Q5(cC6oVSEA*(;e zCRv>ODv4s_$BWSi1i;IItFmAPtec|WLDJ*1_^oyu)b`;ydj0L$*=gszi#xDOo^$q4 z=o2imt>(*)`4GV`kAzPFcY+%3i5y>JjNu4N?(+zuhdc_J zw>ZoN^*tMr9f77tTZ(Mn$4%Zvqk{C9>UzaNSm`9!18@QAr!ƑjZg+2X!?l;a{g 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⋘P=0z?NA_Q?tkjKe1R7v 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

s?O+?X;W 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

Vd&f0`Rg_vC;`(L+M*lbcc-#7EdUrV)``{ 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∋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�)9r<=zBJD4memTMM)ar#k$pax?s% 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 `T++U(RLaS7 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%0AX3M}9{{x+dwN&D7(@v3k(+u) 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®QFk~-k`^B?rR+FSwwWv& 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

Dqr54SlHTz0D;yPZ#)*0m!? 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 z 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%0Dyo$p34^Je7z8UBso*A& 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�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 z +#include +#ifdef __sun__ +#include +#endif + +#include "util.h" +#include "config.h" + +#include "../rombios/32bit/32bitbios_flat.h" + +static void relocate_32bitbios(char *elfarray, uint32_t elfarraysize) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfarray; + Elf32_Shdr *shdr = (Elf32_Shdr *)&elfarray[ehdr->e_shoff]; + char *secstrings = &elfarray[shdr[ehdr->e_shstrndx].sh_offset]; + char *jump_table; + uint32_t reloc_off, reloc_size; + char *highbiosarea; + int i, jump_sec_idx = 0; + + /* + * 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]; + + if ( !strcmp(".biosjumptable", secstrings + shdr[i].sh_name) ) + { + /* We do not relocate the BIOS jump table to high memory. */ + shdr[i].sh_flags &= ~SHF_ALLOC; + jump_sec_idx = i; + } + + /* 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 = (char *)(long)e820_malloc(reloc_size, 0); + 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; + } + } + } + + /* Step 5. Find the ROMBIOS jump-table stub and copy in the real table. */ + for ( jump_table = (char *)ROMBIOS_BEGIN; + jump_table != (char *)ROMBIOS_END; + jump_table++ ) + if ( !strncmp(jump_table, "___JMPT", 7) ) + break; + BUG_ON(jump_table == NULL); + BUG_ON(jump_sec_idx == 0); + memcpy(jump_table, (char *)shdr[jump_sec_idx].sh_addr, + shdr[jump_sec_idx].sh_size); + + printf("done\n"); +} + +void highbios_setup(void) +{ + relocate_32bitbios((char *)highbios_array, sizeof(highbios_array)); +} diff --git a/tools/firmware/hvmloader/Makefile b/tools/firmware/hvmloader/Makefile new file mode 100644 index 0000000..79f641d --- /dev/null +++ b/tools/firmware/hvmloader/Makefile @@ -0,0 +1,61 @@ +# +# 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, write to the Free Software Foundation, Inc., 59 Temple +# Place - Suite 330, Boston, MA 02111-1307 USA. +# + +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/firmware/Rules.mk + +SUBDIRS := acpi + +# The HVM loader is started in 32-bit mode at the address below: +LOADADDR = 0x100000 + +CFLAGS += $(CFLAGS_include) -I. + +SRCS = hvmloader.c mp_tables.c util.c smbios.c +SRCS += 32bitbios_support.c smp.c cacheattr.c +ifeq ($(debug),y) +SRCS += tests.c +endif +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +.PHONY: all +all: subdirs-all + $(MAKE) hvmloader + +hvmloader.o: roms.h +smbios.o: CFLAGS += -D__SMBIOS_DATE__="\"$(shell date +%m/%d/%Y)\"" + +hvmloader: $(OBJS) acpi/acpi.a + $(LD) $(LDFLAGS_DIRECT) -N -Ttext $(LOADADDR) -o hvmloader.tmp $^ + $(OBJCOPY) hvmloader.tmp hvmloader + rm -f hvmloader.tmp + +roms.h: ../rombios/BIOS-bochs-latest ../vgabios/VGABIOS-lgpl-latest.bin \ + ../vgabios/VGABIOS-lgpl-latest.cirrus.bin ../etherboot/eb-roms.h + sh ./mkhex rombios ../rombios/BIOS-bochs-latest > roms.h + sh ./mkhex vgabios_stdvga ../vgabios/VGABIOS-lgpl-latest.bin >> roms.h + sh ./mkhex vgabios_cirrusvga \ + ../vgabios/VGABIOS-lgpl-latest.cirrus.bin >> roms.h + cat ../etherboot/eb-roms.h >> roms.h + +.PHONY: clean +clean: subdirs-clean + rm -f roms.h acpi.h + rm -f hvmloader hvmloader.tmp *.o diff --git a/tools/firmware/hvmloader/acpi/Makefile b/tools/firmware/hvmloader/acpi/Makefile new file mode 100644 index 0000000..47a7bea --- /dev/null +++ b/tools/firmware/hvmloader/acpi/Makefile @@ -0,0 +1,67 @@ +# +# Copyright (c) 2004, 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, write to the Free Software Foundation, Inc., 59 Temple +# Place - Suite 330, Boston, MA 02111-1307 USA. +# + +XEN_ROOT = ../../../.. +include $(XEN_ROOT)/tools/firmware/Rules.mk + +C_SRC = build.c dsdt.c static_tables.c +H_SRC = $(wildcard *.h) +OBJS = $(patsubst %.c,%.o,$(C_SRC)) + +IASL_VER = acpica-unix-20080729 +IASL_URL = http://acpica.org/download/$(IASL_VER).tar.gz + +CFLAGS += -I. -I.. $(CFLAGS_include) + +vpath iasl $(PATH) +all: acpi.a + +ssdt_pm.h ssdt_tpm.h: %.h: %.asl + $(MAKE) iasl + iasl -tc $< + mv $*.hex $@ + rm -f *.aml + +dsdt.c: dsdt.asl + $(MAKE) iasl + iasl -tc dsdt.asl + mv dsdt.hex dsdt.c + echo "int DsdtLen=sizeof(AmlCode);" >> dsdt.c + rm -f *.aml + +iasl: + @echo + @echo "ACPI ASL compiler(iasl) is needed" + @echo "Download Intel ACPI CA" + @echo "If wget failed, please download and compile manually from" + @echo "http://acpica.org/downloads/" + @echo + wget $(IASL_URL) + tar xzf $(IASL_VER).tar.gz + make -C $(IASL_VER)/compiler + $(INSTALL_PROG) $(IASL_VER)/compiler/iasl $(DESTDIR)$(BINDIR)/iasl + +acpi.a: $(OBJS) + $(AR) rc $@ $(OBJS) + +%.o: %.c $(H_SRC) + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +clean: + rm -rf *.a *.o $(IASL_VER) $(IASL_VER).tar.gz + +install: all diff --git a/tools/firmware/hvmloader/acpi/README b/tools/firmware/hvmloader/acpi/README new file mode 100644 index 0000000..210d5ba --- /dev/null +++ b/tools/firmware/hvmloader/acpi/README @@ -0,0 +1,24 @@ +ACPI Table for domain firmware + + +INSTALL +----------------- +Simply make is OK. +# make + + +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 diff --git a/tools/firmware/hvmloader/acpi/acpi2_0.h b/tools/firmware/hvmloader/acpi/acpi2_0.h new file mode 100644 index 0000000..cf0ca01 --- /dev/null +++ b/tools/firmware/hvmloader/acpi/acpi2_0.h @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2004, 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ +#ifndef _ACPI_2_0_H_ +#define _ACPI_2_0_H_ + +#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]; +}; + +/* + * The maximum number of entrys in RSDT or XSDT. + */ +#define ACPI_MAX_NUM_TABLES 5 + +/* + * 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_20_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; +}; + +/* + * FADT Boot Architecture Flags. + */ +#define ACPI_LEGACY_DEVICES (1 << 0) +#define ACPI_8042 (1 << 1) + +/* + * 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) + +/* + * 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 + +/* + * 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; +}; + +/* + * 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_2_0_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') + +/* + * Table revision numbers. + */ +#define ACPI_2_0_RSDP_REVISION 0x02 +#define ACPI_2_0_FADT_REVISION 0x04 +#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_1_0_FADT_REVISION 0x01 + +#pragma pack () + +void acpi_build_tables(void); + +#endif /* _ACPI_2_0_H_ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/firmware/hvmloader/acpi/build.c b/tools/firmware/hvmloader/acpi/build.c new file mode 100644 index 0000000..b779c76 --- /dev/null +++ b/tools/firmware/hvmloader/acpi/build.c @@ -0,0 +1,397 @@ +/* + * 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 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "acpi2_0.h" +#include "ssdt_tpm.h" +#include "ssdt_pm.h" +#include "../config.h" +#include "../util.h" + +#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_20_fadt Fadt; +extern struct acpi_20_facs Facs; +extern unsigned char AmlCode[]; +extern int DsdtLen; + +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 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)); +} + +static 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 int construct_bios_info_table(uint8_t *buf) +{ + struct bios_info *bios_info = (struct bios_info *)buf; + + memset(bios_info, 0, sizeof(*bios_info)); + + bios_info->com1_present = uart_exists(0x3f8); + bios_info->com2_present = uart_exists(0x2f8); + + bios_info->hpet_present = hpet_exists(ACPI_HPET_ADDRESS); + + bios_info->pci_min = PCI_MEMBASE; + bios_info->pci_len = PCI_MEMSIZE; + bios_info->xen_pfiob = 0xdead; + + return align16(sizeof(*bios_info)); +} + +static int construct_madt(struct acpi_20_madt *madt) +{ + struct acpi_20_madt_intsrcovr *intsrcovr; + struct acpi_20_madt_ioapic *io_apic; + struct acpi_20_madt_lapic *lapic; + int i, offset = 0; + + 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 = LAPIC_BASE_ADDRESS; + madt->flags = ACPI_PCAT_COMPAT; + offset += sizeof(*madt); + + 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 ( 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; + } + + offset += sizeof(*intsrcovr); + 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 = IOAPIC_ID; + io_apic->ioapic_addr = IOAPIC_BASE_ADDRESS; + offset += sizeof(*io_apic); + + lapic = (struct acpi_20_madt_lapic *)(io_apic + 1); + for ( i = 0; i < get_vcpu_nr(); 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 = LAPIC_ID(i); + lapic->flags = ACPI_LOCAL_APIC_ENABLED; + offset += sizeof(*lapic); + lapic++; + } + + madt->header.length = offset; + set_checksum(madt, offsetof(struct acpi_header, checksum), offset); + + return align16(offset); +} + +static int construct_hpet(struct acpi_20_hpet *hpet) +{ + int offset; + + 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; + offset = sizeof(*hpet); + + hpet->header.length = offset; + set_checksum(hpet, offsetof(struct acpi_header, checksum), offset); + + return offset; +} + +static int construct_secondary_tables(uint8_t *buf, unsigned long *table_ptrs) +{ + int offset = 0, nr_tables = 0; + struct acpi_20_madt *madt; + struct acpi_20_hpet *hpet; + struct acpi_20_tcpa *tcpa; + static const uint16_t tis_signature[] = {0x0001, 0x0001, 0x0001}; + uint16_t *tis_hdr; + + /* MADT. */ + if ( (get_vcpu_nr() > 1) || get_apic_mode() ) + { + madt = (struct acpi_20_madt *)&buf[offset]; + offset += construct_madt(madt); + table_ptrs[nr_tables++] = (unsigned long)madt; + } + + /* HPET. */ + if ( hpet_exists(ACPI_HPET_ADDRESS) ) + { + hpet = (struct acpi_20_hpet *)&buf[offset]; + offset += construct_hpet(hpet); + table_ptrs[nr_tables++] = (unsigned long)hpet; + } + + if ( battery_port_exists() ) + { + table_ptrs[nr_tables++] = (unsigned long)&buf[offset]; + memcpy(&buf[offset], AmlCode_PM, sizeof(AmlCode_PM)); + offset += align16(sizeof(AmlCode_PM)); + } + + /* TPM TCPA and SSDT. */ + tis_hdr = (uint16_t *)0xFED40F00; + if ( (tis_hdr[0] == tis_signature[0]) && + (tis_hdr[1] == tis_signature[1]) && + (tis_hdr[2] == tis_signature[2]) ) + { + memcpy(&buf[offset], AmlCode_TPM, sizeof(AmlCode_TPM)); + table_ptrs[nr_tables++] = (unsigned long)&buf[offset]; + offset += align16(sizeof(AmlCode_TPM)); + + tcpa = (struct acpi_20_tcpa *)&buf[offset]; + memset(tcpa, 0, sizeof(*tcpa)); + offset += align16(sizeof(*tcpa)); + table_ptrs[nr_tables++] = (unsigned long)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; + tcpa->lasa = e820_malloc(ACPI_2_0_TCPA_LAML_SIZE, 0); + if ( tcpa->lasa ) + { + tcpa->laml = ACPI_2_0_TCPA_LAML_SIZE; + memset((char *)(unsigned long)tcpa->lasa, 0, tcpa->laml); + set_checksum(tcpa, + offsetof(struct acpi_header, checksum), + tcpa->header.length); + } + } + + table_ptrs[nr_tables] = 0; + return align16(offset); +} + +static void __acpi_build_tables(uint8_t *buf, int *low_sz, int *high_sz) +{ + struct acpi_20_rsdp *rsdp; + struct acpi_20_rsdt *rsdt; + struct acpi_20_xsdt *xsdt; + struct acpi_20_fadt *fadt; + struct acpi_10_fadt *fadt_10; + struct acpi_20_facs *facs; + unsigned char *dsdt; + unsigned long secondary_tables[16]; + int offset = 0, i; + + /* + * Fill in high-memory data structures, starting at @buf. + */ + + facs = (struct acpi_20_facs *)&buf[offset]; + memcpy(facs, &Facs, sizeof(struct acpi_20_facs)); + offset += align16(sizeof(struct acpi_20_facs)); + + dsdt = (unsigned char *)&buf[offset]; + memcpy(dsdt, &AmlCode, DsdtLen); + offset += align16(DsdtLen); + + /* + * 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 = (struct acpi_10_fadt *)&buf[offset]; + memcpy(fadt_10, &Fadt, sizeof(struct acpi_10_fadt)); + offset += align16(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 = (unsigned long)dsdt; + fadt_10->firmware_ctrl = (unsigned long)facs; + set_checksum(fadt_10, + offsetof(struct acpi_header, checksum), + sizeof(struct acpi_10_fadt)); + + fadt = (struct acpi_20_fadt *)&buf[offset]; + memcpy(fadt, &Fadt, sizeof(struct acpi_20_fadt)); + offset += align16(sizeof(struct acpi_20_fadt)); + fadt->dsdt = (unsigned long)dsdt; + fadt->x_dsdt = (unsigned long)dsdt; + fadt->firmware_ctrl = (unsigned long)facs; + fadt->x_firmware_ctrl = (unsigned long)facs; + set_checksum(fadt, + offsetof(struct acpi_header, checksum), + sizeof(struct acpi_20_fadt)); + + offset += construct_secondary_tables(&buf[offset], secondary_tables); + + xsdt = (struct acpi_20_xsdt *)&buf[offset]; + memcpy(xsdt, &Xsdt, sizeof(struct acpi_header)); + xsdt->entry[0] = (unsigned long)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); + offset += align16(xsdt->header.length); + set_checksum(xsdt, + offsetof(struct acpi_header, checksum), + xsdt->header.length); + + rsdt = (struct acpi_20_rsdt *)&buf[offset]; + memcpy(rsdt, &Rsdt, sizeof(struct acpi_header)); + rsdt->entry[0] = (unsigned long)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); + offset += align16(rsdt->header.length); + set_checksum(rsdt, + offsetof(struct acpi_header, checksum), + rsdt->header.length); + + *high_sz = offset; + + /* + * Fill in low-memory data structures: bios_info_table and RSDP. + */ + + buf = (uint8_t *)ACPI_PHYSICAL_ADDRESS; + offset = 0; + + offset += construct_bios_info_table(&buf[offset]); + rsdp = (struct acpi_20_rsdp *)&buf[offset]; + + memcpy(rsdp, &Rsdp, sizeof(struct acpi_20_rsdp)); + offset += align16(sizeof(struct acpi_20_rsdp)); + rsdp->rsdt_address = (unsigned long)rsdt; + rsdp->xsdt_address = (unsigned long)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)); + + *low_sz = offset; +} + +void acpi_build_tables(void) +{ + int high_sz, low_sz; + uint8_t *buf; + + /* Find out size of high-memory ACPI data area. */ + buf = (uint8_t *)&_end; + __acpi_build_tables(buf, &low_sz, &high_sz); + memset(buf, 0, high_sz); + + /* Allocate data area and set up ACPI tables there. */ + buf = (uint8_t *)e820_malloc(high_sz, 0); + __acpi_build_tables(buf, &low_sz, &high_sz); + + printf(" - Lo data: %08lx-%08lx\n" + " - Hi data: %08lx-%08lx\n", + (unsigned long)ACPI_PHYSICAL_ADDRESS, + (unsigned long)ACPI_PHYSICAL_ADDRESS + low_sz - 1, + (unsigned long)buf, (unsigned long)buf + high_sz - 1); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/firmware/hvmloader/acpi/dsdt.asl b/tools/firmware/hvmloader/acpi/dsdt.asl new file mode 100644 index 0000000..7ded1c5 --- /dev/null +++ b/tools/firmware/hvmloader/acpi/dsdt.asl @@ -0,0 +1,875 @@ +/****************************************************************************** + * 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 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +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 (\_PR) + { + Processor (PR00, 0x00, 0x0000, 0x00) {} + Processor (PR01, 0x01, 0x0000, 0x00) {} + Processor (PR02, 0x02, 0x0000, 0x00) {} + Processor (PR03, 0x03, 0x0000, 0x00) {} + Processor (PR04, 0x04, 0x0000, 0x00) {} + Processor (PR05, 0x05, 0x0000, 0x00) {} + Processor (PR06, 0x06, 0x0000, 0x00) {} + Processor (PR07, 0x07, 0x0000, 0x00) {} + Processor (PR08, 0x08, 0x0000, 0x00) {} + Processor (PR09, 0x09, 0x0000, 0x00) {} + Processor (PR0A, 0x0a, 0x0000, 0x00) {} + Processor (PR0B, 0x0b, 0x0000, 0x00) {} + Processor (PR0C, 0x0c, 0x0000, 0x00) {} + Processor (PR0D, 0x0d, 0x0000, 0x00) {} + Processor (PR0E, 0x0e, 0x0000, 0x00) {} + /* No more than 15 Processor objects, as otherwise Windows 2000 + * experiences a BSOD of KMODE_EXCEPTION_NOT_HANDLED. If we require + * more in some configurations then we should move \_PR scope into a + * SSDT, statically compiled with a range of different numbers of + * processors. We can then link the appropriate one into the RSDT/XSDT + * at HVM guest boot time. */ + } + + /* + * S3 (suspend-to-ram), S4 (suspend-to-disc) and S5 (power-off) type codes: + * must match piix4 emulation. + */ + Name (\_S3, Package (0x04) + { + 0x05, /* PM1a_CNT.SLP_TYP */ + 0x05, /* PM1b_CNT.SLP_TYP */ + 0x0, /* reserved */ + 0x0 /* reserved */ + }) + Name (\_S4, Package (0x04) + { + 0x06, /* PM1a_CNT.SLP_TYP */ + 0x06, /* PM1b_CNT.SLP_TYP */ + 0x00, /* reserved */ + 0x00 /* reserved */ + }) + Name (\_S5, Package (0x04) + { + 0x07, /* PM1a_CNT.SLP_TYP */ + 0x07, /* PM1b_CNT.SLP_TYP */ + 0x00, /* reserved */ + 0x00 /* reserved */ + }) + + Name(PICD, 0) + Method(_PIC, 1) + { + Store(Arg0, PICD) + } + + Scope (\_SB) + { + /* ACPI_PHYSICAL_ADDRESS == 0xEA000 */ + OperationRegion(BIOS, SystemMemory, 0xEA000, 16) + Field(BIOS, ByteAcc, NoLock, Preserve) { + UAR1, 1, + UAR2, 1, + HPET, 1, + Offset(4), + PMIN, 32, + PLEN, 32 + } + + /* 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) + + Method (_CRS, 0, NotSerialized) + { + Name (PRT0, 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, + Cacheable, ReadWrite, + 0x00000000, + 0x000A0000, + 0x000BFFFF, + 0x00000000, + 0x00020000) + + DWordMemory( + ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, + 0xF0000000, + 0xF4FFFFFF, + 0x00000000, + 0x05000000, + ,, _Y01) + }) + + CreateDWordField(PRT0, \_SB.PCI0._CRS._Y01._MIN, MMIN) + CreateDWordField(PRT0, \_SB.PCI0._CRS._Y01._MAX, MMAX) + CreateDWordField(PRT0, \_SB.PCI0._CRS._Y01._LEN, MLEN) + + Store(\_SB.PMIN, MMIN) + Store(\_SB.PLEN, MLEN) + Add(MMIN, MLEN, MMAX) + Subtract(MMAX, One, MMAX) + + Return (PRT0) + } + + Name(BUFA, ResourceTemplate() { + IRQ(Level, ActiveLow, Shared) { 5, 10, 11 } + }) + + Name(BUFB, Buffer() { + 0x23, 0x00, 0x00, 0x18, /* IRQ descriptor */ + 0x79, 0 /* End tag, null checksum */ + }) + + CreateWordField(BUFB, 0x01, IRQV) + + Device(LNKA) { + Name(_HID, EISAID("PNP0C0F")) /* PCI interrupt link */ + Name(_UID, 1) + + Method(_STA, 0) { + And(PIRA, 0x80, Local0) + If(LEqual(Local0, 0x80)) { + Return(0x09) + } Else { + Return(0x0B) + } + } + + Method(_PRS) { + Return(BUFA) + } + + Method(_DIS) { + Or(PIRA, 0x80, PIRA) + } + + Method(_CRS) { + And(PIRA, 0x0f, Local0) + ShiftLeft(0x1, Local0, IRQV) + Return(BUFB) + } + + Method(_SRS, 1) { + CreateWordField(ARG0, 0x01, IRQ1) + FindSetRightBit(IRQ1, Local0) + Decrement(Local0) + Store(Local0, PIRA) + } + } + + Device(LNKB) { + Name(_HID, EISAID("PNP0C0F")) /* PCI interrupt link */ + Name(_UID, 2) + + Method(_STA, 0) { + And(PIRB, 0x80, Local0) + If(LEqual(Local0, 0x80)) { + Return(0x09) + } Else { + Return(0x0B) + } + } + + Method(_PRS) { + Return(BUFA) + } + + Method(_DIS) { + Or(PIRB, 0x80, PIRB) + } + + Method(_CRS) { + And(PIRB, 0x0f, Local0) + ShiftLeft(0x1, Local0, IRQV) + Return(BUFB) + } + + Method(_SRS, 1) { + CreateWordField(ARG0, 0x01, IRQ1) + FindSetRightBit(IRQ1, Local0) + Decrement(Local0) + Store(Local0, PIRB) + } + } + + Device(LNKC) { + Name(_HID, EISAID("PNP0C0F")) /* PCI interrupt link */ + Name(_UID, 3) + + Method(_STA, 0) { + And(PIRC, 0x80, Local0) + If(LEqual(Local0, 0x80)) { + Return(0x09) + } Else { + Return(0x0B) + } + } + + Method(_PRS) { + Return(BUFA) + } + + Method(_DIS) { + Or(PIRC, 0x80, PIRC) + } + + Method(_CRS) { + And(PIRC, 0x0f, Local0) + ShiftLeft(0x1, Local0, IRQV) + Return(BUFB) + } + + Method(_SRS, 1) { + CreateWordField(ARG0, 0x01, IRQ1) + FindSetRightBit(IRQ1, Local0) + Decrement(Local0) + Store(Local0, PIRC) + } + } + + Device(LNKD) { + Name(_HID, EISAID("PNP0C0F")) /* PCI interrupt link */ + Name(_UID, 4) + + Method(_STA, 0) { + And(PIRD, 0x80, Local0) + If(LEqual(Local0, 0x80)) { + Return(0x09) + } Else { + Return(0x0B) + } + } + + Method(_PRS) { + Return(BUFA) + } + + Method(_DIS) { + Or(PIRD, 0x80, PIRD) + } + + Method(_CRS) { + And(PIRD, 0x0f, Local0) + ShiftLeft(0x1, Local0, IRQV) + Return(BUFB) + } + + Method(_SRS, 1) { + CreateWordField(ARG0, 0x01, IRQ1) + FindSetRightBit(IRQ1, Local0) + Decrement(Local0) + Store(Local0, PIRD) + } + } + + 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 */ + ) + }) + } + + Method(_PRT,0) { + If(PICD) { + Return(PRTA) + } + Return (PRTP) + } + + Name(PRTP, Package() { + /* Device 1, INTA - INTD */ + Package(){0x0001ffff, 0, \_SB.PCI0.LNKB, 0}, + Package(){0x0001ffff, 1, \_SB.PCI0.LNKC, 0}, + Package(){0x0001ffff, 2, \_SB.PCI0.LNKD, 0}, + Package(){0x0001ffff, 3, \_SB.PCI0.LNKA, 0}, + + /* Device 2, INTA - INTD */ + Package(){0x0002ffff, 0, \_SB.PCI0.LNKC, 0}, + Package(){0x0002ffff, 1, \_SB.PCI0.LNKD, 0}, + Package(){0x0002ffff, 2, \_SB.PCI0.LNKA, 0}, + Package(){0x0002ffff, 3, \_SB.PCI0.LNKB, 0}, + + /* Device 3, INTA - INTD */ + Package(){0x0003ffff, 0, \_SB.PCI0.LNKD, 0}, + Package(){0x0003ffff, 1, \_SB.PCI0.LNKA, 0}, + Package(){0x0003ffff, 2, \_SB.PCI0.LNKB, 0}, + Package(){0x0003ffff, 3, \_SB.PCI0.LNKC, 0}, + + /* Device 4, INTA - INTD */ + Package(){0x0004ffff, 0, \_SB.PCI0.LNKA, 0}, + Package(){0x0004ffff, 1, \_SB.PCI0.LNKB, 0}, + Package(){0x0004ffff, 2, \_SB.PCI0.LNKC, 0}, + Package(){0x0004ffff, 3, \_SB.PCI0.LNKD, 0}, + + /* Device 5, INTA - INTD */ + Package(){0x0005ffff, 0, \_SB.PCI0.LNKB, 0}, + Package(){0x0005ffff, 1, \_SB.PCI0.LNKC, 0}, + Package(){0x0005ffff, 2, \_SB.PCI0.LNKD, 0}, + Package(){0x0005ffff, 3, \_SB.PCI0.LNKA, 0}, + + /* Device 6, INTA - INTD */ + Package(){0x0006ffff, 0, \_SB.PCI0.LNKC, 0}, + Package(){0x0006ffff, 1, \_SB.PCI0.LNKD, 0}, + Package(){0x0006ffff, 2, \_SB.PCI0.LNKA, 0}, + Package(){0x0006ffff, 3, \_SB.PCI0.LNKB, 0}, + + /* Device 7, INTA - INTD */ + Package(){0x0007ffff, 0, \_SB.PCI0.LNKD, 0}, + Package(){0x0007ffff, 1, \_SB.PCI0.LNKA, 0}, + Package(){0x0007ffff, 2, \_SB.PCI0.LNKB, 0}, + Package(){0x0007ffff, 3, \_SB.PCI0.LNKC, 0}, + + /* Device 8, INTA - INTD */ + Package(){0x0008ffff, 0, \_SB.PCI0.LNKA, 0}, + Package(){0x0008ffff, 1, \_SB.PCI0.LNKB, 0}, + Package(){0x0008ffff, 2, \_SB.PCI0.LNKC, 0}, + Package(){0x0008ffff, 3, \_SB.PCI0.LNKD, 0}, + + /* Device 9, INTA - INTD */ + Package(){0x0009ffff, 0, \_SB.PCI0.LNKB, 0}, + Package(){0x0009ffff, 1, \_SB.PCI0.LNKC, 0}, + Package(){0x0009ffff, 2, \_SB.PCI0.LNKD, 0}, + Package(){0x0009ffff, 3, \_SB.PCI0.LNKA, 0}, + + /* Device 10, INTA - INTD */ + Package(){0x000affff, 0, \_SB.PCI0.LNKC, 0}, + Package(){0x000affff, 1, \_SB.PCI0.LNKD, 0}, + Package(){0x000affff, 2, \_SB.PCI0.LNKA, 0}, + Package(){0x000affff, 3, \_SB.PCI0.LNKB, 0}, + + /* Device 11, INTA - INTD */ + Package(){0x000bffff, 0, \_SB.PCI0.LNKD, 0}, + Package(){0x000bffff, 1, \_SB.PCI0.LNKA, 0}, + Package(){0x000bffff, 2, \_SB.PCI0.LNKB, 0}, + Package(){0x000bffff, 3, \_SB.PCI0.LNKC, 0}, + + /* Device 12, INTA - INTD */ + Package(){0x000cffff, 0, \_SB.PCI0.LNKA, 0}, + Package(){0x000cffff, 1, \_SB.PCI0.LNKB, 0}, + Package(){0x000cffff, 2, \_SB.PCI0.LNKC, 0}, + Package(){0x000cffff, 3, \_SB.PCI0.LNKD, 0}, + + /* Device 13, INTA - INTD */ + Package(){0x000dffff, 0, \_SB.PCI0.LNKB, 0}, + Package(){0x000dffff, 1, \_SB.PCI0.LNKC, 0}, + Package(){0x000dffff, 2, \_SB.PCI0.LNKD, 0}, + Package(){0x000dffff, 3, \_SB.PCI0.LNKA, 0}, + + /* Device 14, INTA - INTD */ + Package(){0x000effff, 0, \_SB.PCI0.LNKC, 0}, + Package(){0x000effff, 1, \_SB.PCI0.LNKD, 0}, + Package(){0x000effff, 2, \_SB.PCI0.LNKA, 0}, + Package(){0x000effff, 3, \_SB.PCI0.LNKB, 0}, + + /* Device 15, INTA - INTD */ + Package(){0x000fffff, 0, \_SB.PCI0.LNKD, 0}, + Package(){0x000fffff, 1, \_SB.PCI0.LNKA, 0}, + Package(){0x000fffff, 2, \_SB.PCI0.LNKB, 0}, + Package(){0x000fffff, 3, \_SB.PCI0.LNKC, 0}, + }) + + Name(PRTA, Package() { + /* Device 1, INTA - INTD */ + Package(){0x0001ffff, 0, 0, 20}, + Package(){0x0001ffff, 1, 0, 21}, + Package(){0x0001ffff, 2, 0, 22}, + Package(){0x0001ffff, 3, 0, 23}, + + /* Device 2, INTA - INTD */ + Package(){0x0002ffff, 0, 0, 24}, + Package(){0x0002ffff, 1, 0, 25}, + Package(){0x0002ffff, 2, 0, 26}, + Package(){0x0002ffff, 3, 0, 27}, + + /* Device 3, INTA - INTD */ + Package(){0x0003ffff, 0, 0, 28}, + Package(){0x0003ffff, 1, 0, 29}, + Package(){0x0003ffff, 2, 0, 30}, + Package(){0x0003ffff, 3, 0, 31}, + + /* Device 4, INTA - INTD */ + Package(){0x0004ffff, 0, 0, 32}, + Package(){0x0004ffff, 1, 0, 33}, + Package(){0x0004ffff, 2, 0, 34}, + Package(){0x0004ffff, 3, 0, 35}, + + /* Device 5, INTA - INTD */ + Package(){0x0005ffff, 0, 0, 36}, + Package(){0x0005ffff, 1, 0, 37}, + Package(){0x0005ffff, 2, 0, 38}, + Package(){0x0005ffff, 3, 0, 39}, + + /* Device 6, INTA - INTD */ + Package(){0x0006ffff, 0, 0, 40}, + Package(){0x0006ffff, 1, 0, 41}, + Package(){0x0006ffff, 2, 0, 42}, + Package(){0x0006ffff, 3, 0, 43}, + + /* Device 7, INTA - INTD */ + Package(){0x0007ffff, 0, 0, 44}, + Package(){0x0007ffff, 1, 0, 45}, + Package(){0x0007ffff, 2, 0, 46}, + Package(){0x0007ffff, 3, 0, 47}, + + /* Device 8, INTA - INTD */ + Package(){0x0008ffff, 0, 0, 17}, + Package(){0x0008ffff, 1, 0, 18}, + Package(){0x0008ffff, 2, 0, 19}, + Package(){0x0008ffff, 3, 0, 20}, + + /* Device 9, INTA - INTD */ + Package(){0x0009ffff, 0, 0, 21}, + Package(){0x0009ffff, 1, 0, 22}, + Package(){0x0009ffff, 2, 0, 23}, + Package(){0x0009ffff, 3, 0, 24}, + + /* Device 10, INTA - INTD */ + Package(){0x000affff, 0, 0, 25}, + Package(){0x000affff, 1, 0, 26}, + Package(){0x000affff, 2, 0, 27}, + Package(){0x000affff, 3, 0, 28}, + + /* Device 11, INTA - INTD */ + Package(){0x000bffff, 0, 0, 29}, + Package(){0x000bffff, 1, 0, 30}, + Package(){0x000bffff, 2, 0, 31}, + Package(){0x000bffff, 3, 0, 32}, + + /* Device 12, INTA - INTD */ + Package(){0x000cffff, 0, 0, 33}, + Package(){0x000cffff, 1, 0, 34}, + Package(){0x000cffff, 2, 0, 35}, + Package(){0x000cffff, 3, 0, 36}, + + /* Device 13, INTA - INTD */ + Package(){0x000dffff, 0, 0, 37}, + Package(){0x000dffff, 1, 0, 38}, + Package(){0x000dffff, 2, 0, 39}, + Package(){0x000dffff, 3, 0, 40}, + + /* Device 14, INTA - INTD */ + Package(){0x000effff, 0, 0, 41}, + Package(){0x000effff, 1, 0, 42}, + Package(){0x000effff, 2, 0, 43}, + Package(){0x000effff, 3, 0, 44}, + + /* Device 15, INTA - INTD */ + Package(){0x000fffff, 0, 0, 45}, + Package(){0x000fffff, 1, 0, 46}, + Package(){0x000fffff, 2, 0, 47}, + Package(){0x000fffff, 3, 0, 16}, + }) + + 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) + { + Return (0x0F) + } + + Name (_CRS, ResourceTemplate() + { + IO (Decode16, 0x0378, 0x0378, 0x08, 0x08) + IRQNoFlags () {7} + }) + } + } + + /****************************************************************** + * 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 + * Other methods are optional: + * _PS0/3: put them here for debug purpose + * + * 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. + */ + + Device (S1F0) + { + Name (_ADR, 0x00060000) /* Dev 6, Func 0 */ + Name (_SUN, 0x00000001) + + Method (_PS0, 0) + { + Store (0x80, \_GPE.DPT2) + } + + Method (_PS3, 0) + { + Store (0x83, \_GPE.DPT2) + } + + Method (_EJ0, 1) + { + Store (0x88, \_GPE.DPT2) + Store (0x1, \_GPE.PHP1) /* eject php slot 1*/ + } + + Method (_STA, 0) + { + Store (0x89, \_GPE.DPT2) + Return ( \_GPE.PHP1 ) /* IN status as the _STA */ + } + } + + Device (S2F0) + { + Name (_ADR, 0x00070000) /* Dev 7, Func 0 */ + Name (_SUN, 0x00000002) + + Method (_PS0, 0) + { + Store (0x90, \_GPE.DPT2) + } + + Method (_PS3, 0) + { + Store (0x93, \_GPE.DPT2) + } + + Method (_EJ0, 1) + { + Store (0x98, \_GPE.DPT2) + Store (0x1, \_GPE.PHP2) /* eject php slot 1*/ + } + + Method (_STA, 0) + { + Store (0x99, \_GPE.DPT2) + Return ( \_GPE.PHP2 ) /* IN status as the _STA */ + } + } + } + } + + Scope (\_GPE) + { + OperationRegion (PHP, SystemIO, 0x10c0, 0x03) + Field (PHP, ByteAcc, NoLock, Preserve) + { + PSTA, 8, /* hotplug controller status reg */ + PHP1, 8, /* hotplug slot 1 control reg */ + PHP2, 8 /* hotplug slot 2 control reg */ + } + OperationRegion (DG1, SystemIO, 0xb044, 0x04) + Field (DG1, ByteAcc, NoLock, Preserve) + { + DPT1, 8, + DPT2, 8 + } + Method (_L03, 0, NotSerialized) + { + /* detect slot and event(remove/add) */ + Name (SLT, 0x0) + Name (EVT, 0x0) + Store (PSTA, Local1) + ShiftRight (Local1, 0x4, SLT) + And (Local1, 0xf, EVT) + + /* debug */ + Store (SLT, DPT1) + Store (EVT, DPT2) + + If ( LEqual(SLT, 0x1) ) + { + Notify (\_SB.PCI0.S1F0, EVT) + } + ElseIf ( LEqual(SLT, 0x2) ) + { + Notify (\_SB.PCI0.S2F0, EVT) + } + } + } +} diff --git a/tools/firmware/hvmloader/acpi/dsdt.c b/tools/firmware/hvmloader/acpi/dsdt.c new file mode 100644 index 0000000..c136828 --- /dev/null +++ b/tools/firmware/hvmloader/acpi/dsdt.c @@ -0,0 +1,567 @@ +/* + * + * Intel ACPI Component Architecture + * ASL Optimizing Compiler version 20060707 [Feb 16 2007] + * Copyright (C) 2000 - 2006 Intel Corporation + * Supports ACPI Specification Revision 3.0a + * + * Compilation of "dsdt.asl" - Tue May 20 14:34:40 2008 + * + * C source code output + * + */ +unsigned char AmlCode[] = +{ + 0x44,0x53,0x44,0x54,0x32,0x11,0x00,0x00, /* 00000000 "DSDT2..." */ + 0x02,0xEC,0x58,0x65,0x6E,0x00,0x00,0x00, /* 00000008 "..Xen..." */ + 0x48,0x56,0x4D,0x00,0x00,0x00,0x00,0x00, /* 00000010 "HVM....." */ + 0x00,0x00,0x00,0x00,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */ + 0x07,0x07,0x06,0x20,0x08,0x50,0x4D,0x42, /* 00000020 "... .PMB" */ + 0x53,0x0B,0x00,0x0C,0x08,0x50,0x4D,0x4C, /* 00000028 "S....PML" */ + 0x4E,0x0A,0x08,0x08,0x49,0x4F,0x42,0x31, /* 00000030 "N...IOB1" */ + 0x00,0x08,0x49,0x4F,0x4C,0x31,0x00,0x08, /* 00000038 "..IOL1.." */ + 0x41,0x50,0x43,0x42,0x0C,0x00,0x00,0xC0, /* 00000040 "APCB...." */ + 0xFE,0x08,0x41,0x50,0x43,0x4C,0x0C,0x00, /* 00000048 "..APCL.." */ + 0x00,0x01,0x00,0x08,0x50,0x55,0x49,0x44, /* 00000050 "....PUID" */ + 0x00,0x10,0x49,0x0C,0x5F,0x50,0x52,0x5F, /* 00000058 "..I._PR_" */ + 0x5B,0x83,0x0B,0x50,0x52,0x30,0x30,0x00, /* 00000060 "[..PR00." */ + 0x00,0x00,0x00,0x00,0x00,0x5B,0x83,0x0B, /* 00000068 ".....[.." */ + 0x50,0x52,0x30,0x31,0x01,0x00,0x00,0x00, /* 00000070 "PR01...." */ + 0x00,0x00,0x5B,0x83,0x0B,0x50,0x52,0x30, /* 00000078 "..[..PR0" */ + 0x32,0x02,0x00,0x00,0x00,0x00,0x00,0x5B, /* 00000080 "2......[" */ + 0x83,0x0B,0x50,0x52,0x30,0x33,0x03,0x00, /* 00000088 "..PR03.." */ + 0x00,0x00,0x00,0x00,0x5B,0x83,0x0B,0x50, /* 00000090 "....[..P" */ + 0x52,0x30,0x34,0x04,0x00,0x00,0x00,0x00, /* 00000098 "R04....." */ + 0x00,0x5B,0x83,0x0B,0x50,0x52,0x30,0x35, /* 000000A0 ".[..PR05" */ + 0x05,0x00,0x00,0x00,0x00,0x00,0x5B,0x83, /* 000000A8 "......[." */ + 0x0B,0x50,0x52,0x30,0x36,0x06,0x00,0x00, /* 000000B0 ".PR06..." */ + 0x00,0x00,0x00,0x5B,0x83,0x0B,0x50,0x52, /* 000000B8 "...[..PR" */ + 0x30,0x37,0x07,0x00,0x00,0x00,0x00,0x00, /* 000000C0 "07......" */ + 0x5B,0x83,0x0B,0x50,0x52,0x30,0x38,0x08, /* 000000C8 "[..PR08." */ + 0x00,0x00,0x00,0x00,0x00,0x5B,0x83,0x0B, /* 000000D0 ".....[.." */ + 0x50,0x52,0x30,0x39,0x09,0x00,0x00,0x00, /* 000000D8 "PR09...." */ + 0x00,0x00,0x5B,0x83,0x0B,0x50,0x52,0x30, /* 000000E0 "..[..PR0" */ + 0x41,0x0A,0x00,0x00,0x00,0x00,0x00,0x5B, /* 000000E8 "A......[" */ + 0x83,0x0B,0x50,0x52,0x30,0x42,0x0B,0x00, /* 000000F0 "..PR0B.." */ + 0x00,0x00,0x00,0x00,0x5B,0x83,0x0B,0x50, /* 000000F8 "....[..P" */ + 0x52,0x30,0x43,0x0C,0x00,0x00,0x00,0x00, /* 00000100 "R0C....." */ + 0x00,0x5B,0x83,0x0B,0x50,0x52,0x30,0x44, /* 00000108 ".[..PR0D" */ + 0x0D,0x00,0x00,0x00,0x00,0x00,0x5B,0x83, /* 00000110 "......[." */ + 0x0B,0x50,0x52,0x30,0x45,0x0E,0x00,0x00, /* 00000118 ".PR0E..." */ + 0x00,0x00,0x00,0x08,0x5F,0x53,0x33,0x5F, /* 00000120 "...._S3_" */ + 0x12,0x08,0x04,0x0A,0x05,0x0A,0x05,0x00, /* 00000128 "........" */ + 0x00,0x08,0x5F,0x53,0x34,0x5F,0x12,0x08, /* 00000130 ".._S4_.." */ + 0x04,0x0A,0x06,0x0A,0x06,0x00,0x00,0x08, /* 00000138 "........" */ + 0x5F,0x53,0x35,0x5F,0x12,0x08,0x04,0x0A, /* 00000140 "_S5_...." */ + 0x07,0x0A,0x07,0x00,0x00,0x08,0x50,0x49, /* 00000148 "......PI" */ + 0x43,0x44,0x00,0x14,0x0C,0x5F,0x50,0x49, /* 00000150 "CD..._PI" */ + 0x43,0x01,0x70,0x68,0x50,0x49,0x43,0x44, /* 00000158 "C.phPICD" */ + 0x10,0x42,0xF1,0x5F,0x53,0x42,0x5F,0x5B, /* 00000160 ".B._SB_[" */ + 0x80,0x42,0x49,0x4F,0x53,0x00,0x0C,0x00, /* 00000168 ".BIOS..." */ + 0xA0,0x0E,0x00,0x0A,0x10,0x5B,0x81,0x21, /* 00000170 ".....[.!" */ + 0x42,0x49,0x4F,0x53,0x01,0x55,0x41,0x52, /* 00000178 "BIOS.UAR" */ + 0x31,0x01,0x55,0x41,0x52,0x32,0x01,0x48, /* 00000180 "1.UAR2.H" */ + 0x50,0x45,0x54,0x01,0x00,0x1D,0x50,0x4D, /* 00000188 "PET...PM" */ + 0x49,0x4E,0x20,0x50,0x4C,0x45,0x4E,0x20, /* 00000190 "IN PLEN " */ + 0x5B,0x82,0x49,0x04,0x4D,0x45,0x4D,0x30, /* 00000198 "[.I.MEM0" */ + 0x08,0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0, /* 000001A0 "._HID.A." */ + 0x0C,0x02,0x08,0x5F,0x43,0x52,0x53,0x11, /* 000001A8 "..._CRS." */ + 0x33,0x0A,0x30,0x8A,0x2B,0x00,0x00,0x0D, /* 000001B0 "3.0.+..." */ + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 000001B8 "........" */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 000001C0 "........" */ + 0x00,0xFF,0xFF,0x09,0x00,0x00,0x00,0x00, /* 000001C8 "........" */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 000001D0 "........" */ + 0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x00, /* 000001D8 "........" */ + 0x00,0x79,0x00,0x5B,0x82,0x4E,0xE8,0x50, /* 000001E0 ".y.[.N.P" */ + 0x43,0x49,0x30,0x08,0x5F,0x48,0x49,0x44, /* 000001E8 "CI0._HID" */ + 0x0C,0x41,0xD0,0x0A,0x03,0x08,0x5F,0x55, /* 000001F0 ".A...._U" */ + 0x49,0x44,0x00,0x08,0x5F,0x41,0x44,0x52, /* 000001F8 "ID.._ADR" */ + 0x00,0x08,0x5F,0x42,0x42,0x4E,0x00,0x14, /* 00000200 ".._BBN.." */ + 0x4E,0x0C,0x5F,0x43,0x52,0x53,0x00,0x08, /* 00000208 "N._CRS.." */ + 0x50,0x52,0x54,0x30,0x11,0x42,0x07,0x0A, /* 00000210 "PRT0.B.." */ + 0x6E,0x88,0x0D,0x00,0x02,0x0E,0x00,0x00, /* 00000218 "n......." */ + 0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00, /* 00000220 "........" */ + 0x01,0x47,0x01,0xF8,0x0C,0xF8,0x0C,0x01, /* 00000228 ".G......" */ + 0x08,0x88,0x0D,0x00,0x01,0x0C,0x03,0x00, /* 00000230 "........" */ + 0x00,0x00,0x00,0xF7,0x0C,0x00,0x00,0xF8, /* 00000238 "........" */ + 0x0C,0x88,0x0D,0x00,0x01,0x0C,0x03,0x00, /* 00000240 "........" */ + 0x00,0x00,0x0D,0xFF,0xFF,0x00,0x00,0x00, /* 00000248 "........" */ + 0xF3,0x87,0x17,0x00,0x00,0x0C,0x03,0x00, /* 00000250 "........" */ + 0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0xFF, /* 00000258 "........" */ + 0xFF,0x0B,0x00,0x00,0x00,0x00,0x00,0x00, /* 00000260 "........" */ + 0x00,0x02,0x00,0x87,0x17,0x00,0x00,0x0C, /* 00000268 "........" */ + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 00000270 "........" */ + 0xF0,0xFF,0xFF,0xFF,0xF4,0x00,0x00,0x00, /* 00000278 "........" */ + 0x00,0x00,0x00,0x00,0x05,0x79,0x00,0x8A, /* 00000280 ".....y.." */ + 0x50,0x52,0x54,0x30,0x0A,0x5C,0x4D,0x4D, /* 00000288 "PRT0.\MM" */ + 0x49,0x4E,0x8A,0x50,0x52,0x54,0x30,0x0A, /* 00000290 "IN.PRT0." */ + 0x60,0x4D,0x4D,0x41,0x58,0x8A,0x50,0x52, /* 00000298 "`MMAX.PR" */ + 0x54,0x30,0x0A,0x68,0x4D,0x4C,0x45,0x4E, /* 000002A0 "T0.hMLEN" */ + 0x70,0x50,0x4D,0x49,0x4E,0x4D,0x4D,0x49, /* 000002A8 "pPMINMMI" */ + 0x4E,0x70,0x50,0x4C,0x45,0x4E,0x4D,0x4C, /* 000002B0 "NpPLENML" */ + 0x45,0x4E,0x72,0x4D,0x4D,0x49,0x4E,0x4D, /* 000002B8 "ENrMMINM" */ + 0x4C,0x45,0x4E,0x4D,0x4D,0x41,0x58,0x74, /* 000002C0 "LENMMAXt" */ + 0x4D,0x4D,0x41,0x58,0x01,0x4D,0x4D,0x41, /* 000002C8 "MMAX.MMA" */ + 0x58,0xA4,0x50,0x52,0x54,0x30,0x08,0x42, /* 000002D0 "X.PRT0.B" */ + 0x55,0x46,0x41,0x11,0x09,0x0A,0x06,0x23, /* 000002D8 "UFA....#" */ + 0x20,0x0C,0x18,0x79,0x00,0x08,0x42,0x55, /* 000002E0 " ..y..BU" */ + 0x46,0x42,0x11,0x09,0x0A,0x06,0x23,0x00, /* 000002E8 "FB....#." */ + 0x00,0x18,0x79,0x00,0x8B,0x42,0x55,0x46, /* 000002F0 "..y..BUF" */ + 0x42,0x01,0x49,0x52,0x51,0x56,0x5B,0x82, /* 000002F8 "B.IRQV[." */ + 0x48,0x08,0x4C,0x4E,0x4B,0x41,0x08,0x5F, /* 00000300 "H.LNKA._" */ + 0x48,0x49,0x44,0x0C,0x41,0xD0,0x0C,0x0F, /* 00000308 "HID.A..." */ + 0x08,0x5F,0x55,0x49,0x44,0x01,0x14,0x1C, /* 00000310 "._UID..." */ + 0x5F,0x53,0x54,0x41,0x00,0x7B,0x50,0x49, /* 00000318 "_STA.{PI" */ + 0x52,0x41,0x0A,0x80,0x60,0xA0,0x08,0x93, /* 00000320 "RA..`..." */ + 0x60,0x0A,0x80,0xA4,0x0A,0x09,0xA1,0x04, /* 00000328 "`......." */ + 0xA4,0x0A,0x0B,0x14,0x0B,0x5F,0x50,0x52, /* 00000330 "....._PR" */ + 0x53,0x00,0xA4,0x42,0x55,0x46,0x41,0x14, /* 00000338 "S..BUFA." */ + 0x11,0x5F,0x44,0x49,0x53,0x00,0x7D,0x50, /* 00000340 "._DIS.}P" */ + 0x49,0x52,0x41,0x0A,0x80,0x50,0x49,0x52, /* 00000348 "IRA..PIR" */ + 0x41,0x14,0x1A,0x5F,0x43,0x52,0x53,0x00, /* 00000350 "A.._CRS." */ + 0x7B,0x50,0x49,0x52,0x41,0x0A,0x0F,0x60, /* 00000358 "{PIRA..`" */ + 0x79,0x01,0x60,0x49,0x52,0x51,0x56,0xA4, /* 00000360 "y.`IRQV." */ + 0x42,0x55,0x46,0x42,0x14,0x1B,0x5F,0x53, /* 00000368 "BUFB.._S" */ + 0x52,0x53,0x01,0x8B,0x68,0x01,0x49,0x52, /* 00000370 "RS..h.IR" */ + 0x51,0x31,0x82,0x49,0x52,0x51,0x31,0x60, /* 00000378 "Q1.IRQ1`" */ + 0x76,0x60,0x70,0x60,0x50,0x49,0x52,0x41, /* 00000380 "v`p`PIRA" */ + 0x5B,0x82,0x49,0x08,0x4C,0x4E,0x4B,0x42, /* 00000388 "[.I.LNKB" */ + 0x08,0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0, /* 00000390 "._HID.A." */ + 0x0C,0x0F,0x08,0x5F,0x55,0x49,0x44,0x0A, /* 00000398 "..._UID." */ + 0x02,0x14,0x1C,0x5F,0x53,0x54,0x41,0x00, /* 000003A0 "..._STA." */ + 0x7B,0x50,0x49,0x52,0x42,0x0A,0x80,0x60, /* 000003A8 "{PIRB..`" */ + 0xA0,0x08,0x93,0x60,0x0A,0x80,0xA4,0x0A, /* 000003B0 "...`...." */ + 0x09,0xA1,0x04,0xA4,0x0A,0x0B,0x14,0x0B, /* 000003B8 "........" */ + 0x5F,0x50,0x52,0x53,0x00,0xA4,0x42,0x55, /* 000003C0 "_PRS..BU" */ + 0x46,0x41,0x14,0x11,0x5F,0x44,0x49,0x53, /* 000003C8 "FA.._DIS" */ + 0x00,0x7D,0x50,0x49,0x52,0x42,0x0A,0x80, /* 000003D0 ".}PIRB.." */ + 0x50,0x49,0x52,0x42,0x14,0x1A,0x5F,0x43, /* 000003D8 "PIRB.._C" */ + 0x52,0x53,0x00,0x7B,0x50,0x49,0x52,0x42, /* 000003E0 "RS.{PIRB" */ + 0x0A,0x0F,0x60,0x79,0x01,0x60,0x49,0x52, /* 000003E8 "..`y.`IR" */ + 0x51,0x56,0xA4,0x42,0x55,0x46,0x42,0x14, /* 000003F0 "QV.BUFB." */ + 0x1B,0x5F,0x53,0x52,0x53,0x01,0x8B,0x68, /* 000003F8 "._SRS..h" */ + 0x01,0x49,0x52,0x51,0x31,0x82,0x49,0x52, /* 00000400 ".IRQ1.IR" */ + 0x51,0x31,0x60,0x76,0x60,0x70,0x60,0x50, /* 00000408 "Q1`v`p`P" */ + 0x49,0x52,0x42,0x5B,0x82,0x49,0x08,0x4C, /* 00000410 "IRB[.I.L" */ + 0x4E,0x4B,0x43,0x08,0x5F,0x48,0x49,0x44, /* 00000418 "NKC._HID" */ + 0x0C,0x41,0xD0,0x0C,0x0F,0x08,0x5F,0x55, /* 00000420 ".A...._U" */ + 0x49,0x44,0x0A,0x03,0x14,0x1C,0x5F,0x53, /* 00000428 "ID...._S" */ + 0x54,0x41,0x00,0x7B,0x50,0x49,0x52,0x43, /* 00000430 "TA.{PIRC" */ + 0x0A,0x80,0x60,0xA0,0x08,0x93,0x60,0x0A, /* 00000438 "..`...`." */ + 0x80,0xA4,0x0A,0x09,0xA1,0x04,0xA4,0x0A, /* 00000440 "........" */ + 0x0B,0x14,0x0B,0x5F,0x50,0x52,0x53,0x00, /* 00000448 "..._PRS." */ + 0xA4,0x42,0x55,0x46,0x41,0x14,0x11,0x5F, /* 00000450 ".BUFA.._" */ + 0x44,0x49,0x53,0x00,0x7D,0x50,0x49,0x52, /* 00000458 "DIS.}PIR" */ + 0x43,0x0A,0x80,0x50,0x49,0x52,0x43,0x14, /* 00000460 "C..PIRC." */ + 0x1A,0x5F,0x43,0x52,0x53,0x00,0x7B,0x50, /* 00000468 "._CRS.{P" */ + 0x49,0x52,0x43,0x0A,0x0F,0x60,0x79,0x01, /* 00000470 "IRC..`y." */ + 0x60,0x49,0x52,0x51,0x56,0xA4,0x42,0x55, /* 00000478 "`IRQV.BU" */ + 0x46,0x42,0x14,0x1B,0x5F,0x53,0x52,0x53, /* 00000480 "FB.._SRS" */ + 0x01,0x8B,0x68,0x01,0x49,0x52,0x51,0x31, /* 00000488 "..h.IRQ1" */ + 0x82,0x49,0x52,0x51,0x31,0x60,0x76,0x60, /* 00000490 ".IRQ1`v`" */ + 0x70,0x60,0x50,0x49,0x52,0x43,0x5B,0x82, /* 00000498 "p`PIRC[." */ + 0x49,0x08,0x4C,0x4E,0x4B,0x44,0x08,0x5F, /* 000004A0 "I.LNKD._" */ + 0x48,0x49,0x44,0x0C,0x41,0xD0,0x0C,0x0F, /* 000004A8 "HID.A..." */ + 0x08,0x5F,0x55,0x49,0x44,0x0A,0x04,0x14, /* 000004B0 "._UID..." */ + 0x1C,0x5F,0x53,0x54,0x41,0x00,0x7B,0x50, /* 000004B8 "._STA.{P" */ + 0x49,0x52,0x44,0x0A,0x80,0x60,0xA0,0x08, /* 000004C0 "IRD..`.." */ + 0x93,0x60,0x0A,0x80,0xA4,0x0A,0x09,0xA1, /* 000004C8 ".`......" */ + 0x04,0xA4,0x0A,0x0B,0x14,0x0B,0x5F,0x50, /* 000004D0 "......_P" */ + 0x52,0x53,0x00,0xA4,0x42,0x55,0x46,0x41, /* 000004D8 "RS..BUFA" */ + 0x14,0x11,0x5F,0x44,0x49,0x53,0x00,0x7D, /* 000004E0 ".._DIS.}" */ + 0x50,0x49,0x52,0x44,0x0A,0x80,0x50,0x49, /* 000004E8 "PIRD..PI" */ + 0x52,0x44,0x14,0x1A,0x5F,0x43,0x52,0x53, /* 000004F0 "RD.._CRS" */ + 0x00,0x7B,0x50,0x49,0x52,0x44,0x0A,0x0F, /* 000004F8 ".{PIRD.." */ + 0x60,0x79,0x01,0x60,0x49,0x52,0x51,0x56, /* 00000500 "`y.`IRQV" */ + 0xA4,0x42,0x55,0x46,0x42,0x14,0x1B,0x5F, /* 00000508 ".BUFB.._" */ + 0x53,0x52,0x53,0x01,0x8B,0x68,0x01,0x49, /* 00000510 "SRS..h.I" */ + 0x52,0x51,0x31,0x82,0x49,0x52,0x51,0x31, /* 00000518 "RQ1.IRQ1" */ + 0x60,0x76,0x60,0x70,0x60,0x50,0x49,0x52, /* 00000520 "`v`p`PIR" */ + 0x44,0x5B,0x82,0x44,0x05,0x48,0x50,0x45, /* 00000528 "D[.D.HPE" */ + 0x54,0x08,0x5F,0x48,0x49,0x44,0x0C,0x41, /* 00000530 "T._HID.A" */ + 0xD0,0x01,0x03,0x08,0x5F,0x55,0x49,0x44, /* 00000538 "...._UID" */ + 0x00,0x14,0x18,0x5F,0x53,0x54,0x41,0x00, /* 00000540 "..._STA." */ + 0xA0,0x0C,0x93,0x5E,0x5E,0x5E,0x48,0x50, /* 00000548 "...^^^HP" */ + 0x45,0x54,0x00,0xA4,0x00,0xA1,0x04,0xA4, /* 00000550 "ET......" */ + 0x0A,0x0F,0x08,0x5F,0x43,0x52,0x53,0x11, /* 00000558 "..._CRS." */ + 0x1F,0x0A,0x1C,0x87,0x17,0x00,0x00,0x0D, /* 00000560 "........" */ + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xD0, /* 00000568 "........" */ + 0xFE,0xFF,0x03,0xD0,0xFE,0x00,0x00,0x00, /* 00000570 "........" */ + 0x00,0x00,0x04,0x00,0x00,0x79,0x00,0x14, /* 00000578 ".....y.." */ + 0x16,0x5F,0x50,0x52,0x54,0x00,0xA0,0x0A, /* 00000580 "._PRT..." */ + 0x50,0x49,0x43,0x44,0xA4,0x50,0x52,0x54, /* 00000588 "PICD.PRT" */ + 0x41,0xA4,0x50,0x52,0x54,0x50,0x08,0x50, /* 00000590 "A.PRTP.P" */ + 0x52,0x54,0x50,0x12,0x49,0x36,0x3C,0x12, /* 00000598 "RTP.I6<." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x01,0x00,0x00, /* 000005A0 "........" */ + 0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0D,0x04, /* 000005A8 "LNKB...." */ + 0x0C,0xFF,0xFF,0x01,0x00,0x01,0x4C,0x4E, /* 000005B0 "......LN" */ + 0x4B,0x43,0x00,0x12,0x0E,0x04,0x0C,0xFF, /* 000005B8 "KC......" */ + 0xFF,0x01,0x00,0x0A,0x02,0x4C,0x4E,0x4B, /* 000005C0 ".....LNK" */ + 0x44,0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF, /* 000005C8 "D......." */ + 0x01,0x00,0x0A,0x03,0x4C,0x4E,0x4B,0x41, /* 000005D0 "....LNKA" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x02, /* 000005D8 "........" */ + 0x00,0x00,0x4C,0x4E,0x4B,0x43,0x00,0x12, /* 000005E0 "..LNKC.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x02,0x00,0x01, /* 000005E8 "........" */ + 0x4C,0x4E,0x4B,0x44,0x00,0x12,0x0E,0x04, /* 000005F0 "LNKD...." */ + 0x0C,0xFF,0xFF,0x02,0x00,0x0A,0x02,0x4C, /* 000005F8 ".......L" */ + 0x4E,0x4B,0x41,0x00,0x12,0x0E,0x04,0x0C, /* 00000600 "NKA....." */ + 0xFF,0xFF,0x02,0x00,0x0A,0x03,0x4C,0x4E, /* 00000608 "......LN" */ + 0x4B,0x42,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 00000610 "KB......" */ + 0xFF,0x03,0x00,0x00,0x4C,0x4E,0x4B,0x44, /* 00000618 "....LNKD" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x03, /* 00000620 "........" */ + 0x00,0x01,0x4C,0x4E,0x4B,0x41,0x00,0x12, /* 00000628 "..LNKA.." */ + 0x0E,0x04,0x0C,0xFF,0xFF,0x03,0x00,0x0A, /* 00000630 "........" */ + 0x02,0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0E, /* 00000638 ".LNKB..." */ + 0x04,0x0C,0xFF,0xFF,0x03,0x00,0x0A,0x03, /* 00000640 "........" */ + 0x4C,0x4E,0x4B,0x43,0x00,0x12,0x0D,0x04, /* 00000648 "LNKC...." */ + 0x0C,0xFF,0xFF,0x04,0x00,0x00,0x4C,0x4E, /* 00000650 "......LN" */ + 0x4B,0x41,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 00000658 "KA......" */ + 0xFF,0x04,0x00,0x01,0x4C,0x4E,0x4B,0x42, /* 00000660 "....LNKB" */ + 0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x04, /* 00000668 "........" */ + 0x00,0x0A,0x02,0x4C,0x4E,0x4B,0x43,0x00, /* 00000670 "...LNKC." */ + 0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x04,0x00, /* 00000678 "........" */ + 0x0A,0x03,0x4C,0x4E,0x4B,0x44,0x00,0x12, /* 00000680 "..LNKD.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x05,0x00,0x00, /* 00000688 "........" */ + 0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0D,0x04, /* 00000690 "LNKB...." */ + 0x0C,0xFF,0xFF,0x05,0x00,0x01,0x4C,0x4E, /* 00000698 "......LN" */ + 0x4B,0x43,0x00,0x12,0x0E,0x04,0x0C,0xFF, /* 000006A0 "KC......" */ + 0xFF,0x05,0x00,0x0A,0x02,0x4C,0x4E,0x4B, /* 000006A8 ".....LNK" */ + 0x44,0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF, /* 000006B0 "D......." */ + 0x05,0x00,0x0A,0x03,0x4C,0x4E,0x4B,0x41, /* 000006B8 "....LNKA" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x06, /* 000006C0 "........" */ + 0x00,0x00,0x4C,0x4E,0x4B,0x43,0x00,0x12, /* 000006C8 "..LNKC.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x06,0x00,0x01, /* 000006D0 "........" */ + 0x4C,0x4E,0x4B,0x44,0x00,0x12,0x0E,0x04, /* 000006D8 "LNKD...." */ + 0x0C,0xFF,0xFF,0x06,0x00,0x0A,0x02,0x4C, /* 000006E0 ".......L" */ + 0x4E,0x4B,0x41,0x00,0x12,0x0E,0x04,0x0C, /* 000006E8 "NKA....." */ + 0xFF,0xFF,0x06,0x00,0x0A,0x03,0x4C,0x4E, /* 000006F0 "......LN" */ + 0x4B,0x42,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 000006F8 "KB......" */ + 0xFF,0x07,0x00,0x00,0x4C,0x4E,0x4B,0x44, /* 00000700 "....LNKD" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x07, /* 00000708 "........" */ + 0x00,0x01,0x4C,0x4E,0x4B,0x41,0x00,0x12, /* 00000710 "..LNKA.." */ + 0x0E,0x04,0x0C,0xFF,0xFF,0x07,0x00,0x0A, /* 00000718 "........" */ + 0x02,0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0E, /* 00000720 ".LNKB..." */ + 0x04,0x0C,0xFF,0xFF,0x07,0x00,0x0A,0x03, /* 00000728 "........" */ + 0x4C,0x4E,0x4B,0x43,0x00,0x12,0x0D,0x04, /* 00000730 "LNKC...." */ + 0x0C,0xFF,0xFF,0x08,0x00,0x00,0x4C,0x4E, /* 00000738 "......LN" */ + 0x4B,0x41,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 00000740 "KA......" */ + 0xFF,0x08,0x00,0x01,0x4C,0x4E,0x4B,0x42, /* 00000748 "....LNKB" */ + 0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x08, /* 00000750 "........" */ + 0x00,0x0A,0x02,0x4C,0x4E,0x4B,0x43,0x00, /* 00000758 "...LNKC." */ + 0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x08,0x00, /* 00000760 "........" */ + 0x0A,0x03,0x4C,0x4E,0x4B,0x44,0x00,0x12, /* 00000768 "..LNKD.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x09,0x00,0x00, /* 00000770 "........" */ + 0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0D,0x04, /* 00000778 "LNKB...." */ + 0x0C,0xFF,0xFF,0x09,0x00,0x01,0x4C,0x4E, /* 00000780 "......LN" */ + 0x4B,0x43,0x00,0x12,0x0E,0x04,0x0C,0xFF, /* 00000788 "KC......" */ + 0xFF,0x09,0x00,0x0A,0x02,0x4C,0x4E,0x4B, /* 00000790 ".....LNK" */ + 0x44,0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF, /* 00000798 "D......." */ + 0x09,0x00,0x0A,0x03,0x4C,0x4E,0x4B,0x41, /* 000007A0 "....LNKA" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x0A, /* 000007A8 "........" */ + 0x00,0x00,0x4C,0x4E,0x4B,0x43,0x00,0x12, /* 000007B0 "..LNKC.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x0A,0x00,0x01, /* 000007B8 "........" */ + 0x4C,0x4E,0x4B,0x44,0x00,0x12,0x0E,0x04, /* 000007C0 "LNKD...." */ + 0x0C,0xFF,0xFF,0x0A,0x00,0x0A,0x02,0x4C, /* 000007C8 ".......L" */ + 0x4E,0x4B,0x41,0x00,0x12,0x0E,0x04,0x0C, /* 000007D0 "NKA....." */ + 0xFF,0xFF,0x0A,0x00,0x0A,0x03,0x4C,0x4E, /* 000007D8 "......LN" */ + 0x4B,0x42,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 000007E0 "KB......" */ + 0xFF,0x0B,0x00,0x00,0x4C,0x4E,0x4B,0x44, /* 000007E8 "....LNKD" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x0B, /* 000007F0 "........" */ + 0x00,0x01,0x4C,0x4E,0x4B,0x41,0x00,0x12, /* 000007F8 "..LNKA.." */ + 0x0E,0x04,0x0C,0xFF,0xFF,0x0B,0x00,0x0A, /* 00000800 "........" */ + 0x02,0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0E, /* 00000808 ".LNKB..." */ + 0x04,0x0C,0xFF,0xFF,0x0B,0x00,0x0A,0x03, /* 00000810 "........" */ + 0x4C,0x4E,0x4B,0x43,0x00,0x12,0x0D,0x04, /* 00000818 "LNKC...." */ + 0x0C,0xFF,0xFF,0x0C,0x00,0x00,0x4C,0x4E, /* 00000820 "......LN" */ + 0x4B,0x41,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 00000828 "KA......" */ + 0xFF,0x0C,0x00,0x01,0x4C,0x4E,0x4B,0x42, /* 00000830 "....LNKB" */ + 0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x0C, /* 00000838 "........" */ + 0x00,0x0A,0x02,0x4C,0x4E,0x4B,0x43,0x00, /* 00000840 "...LNKC." */ + 0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x0C,0x00, /* 00000848 "........" */ + 0x0A,0x03,0x4C,0x4E,0x4B,0x44,0x00,0x12, /* 00000850 "..LNKD.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x0D,0x00,0x00, /* 00000858 "........" */ + 0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0D,0x04, /* 00000860 "LNKB...." */ + 0x0C,0xFF,0xFF,0x0D,0x00,0x01,0x4C,0x4E, /* 00000868 "......LN" */ + 0x4B,0x43,0x00,0x12,0x0E,0x04,0x0C,0xFF, /* 00000870 "KC......" */ + 0xFF,0x0D,0x00,0x0A,0x02,0x4C,0x4E,0x4B, /* 00000878 ".....LNK" */ + 0x44,0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF, /* 00000880 "D......." */ + 0x0D,0x00,0x0A,0x03,0x4C,0x4E,0x4B,0x41, /* 00000888 "....LNKA" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x0E, /* 00000890 "........" */ + 0x00,0x00,0x4C,0x4E,0x4B,0x43,0x00,0x12, /* 00000898 "..LNKC.." */ + 0x0D,0x04,0x0C,0xFF,0xFF,0x0E,0x00,0x01, /* 000008A0 "........" */ + 0x4C,0x4E,0x4B,0x44,0x00,0x12,0x0E,0x04, /* 000008A8 "LNKD...." */ + 0x0C,0xFF,0xFF,0x0E,0x00,0x0A,0x02,0x4C, /* 000008B0 ".......L" */ + 0x4E,0x4B,0x41,0x00,0x12,0x0E,0x04,0x0C, /* 000008B8 "NKA....." */ + 0xFF,0xFF,0x0E,0x00,0x0A,0x03,0x4C,0x4E, /* 000008C0 "......LN" */ + 0x4B,0x42,0x00,0x12,0x0D,0x04,0x0C,0xFF, /* 000008C8 "KB......" */ + 0xFF,0x0F,0x00,0x00,0x4C,0x4E,0x4B,0x44, /* 000008D0 "....LNKD" */ + 0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x0F, /* 000008D8 "........" */ + 0x00,0x01,0x4C,0x4E,0x4B,0x41,0x00,0x12, /* 000008E0 "..LNKA.." */ + 0x0E,0x04,0x0C,0xFF,0xFF,0x0F,0x00,0x0A, /* 000008E8 "........" */ + 0x02,0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0E, /* 000008F0 ".LNKB..." */ + 0x04,0x0C,0xFF,0xFF,0x0F,0x00,0x0A,0x03, /* 000008F8 "........" */ + 0x4C,0x4E,0x4B,0x43,0x00,0x08,0x50,0x52, /* 00000900 "LNKC..PR" */ + 0x54,0x41,0x12,0x41,0x2F,0x3C,0x12,0x0B, /* 00000908 "TA.A/<.." */ + 0x04,0x0C,0xFF,0xFF,0x01,0x00,0x00,0x00, /* 00000910 "........" */ + 0x0A,0x14,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000918 "........" */ + 0x01,0x00,0x01,0x00,0x0A,0x15,0x12,0x0C, /* 00000920 "........" */ + 0x04,0x0C,0xFF,0xFF,0x01,0x00,0x0A,0x02, /* 00000928 "........" */ + 0x00,0x0A,0x16,0x12,0x0C,0x04,0x0C,0xFF, /* 00000930 "........" */ + 0xFF,0x01,0x00,0x0A,0x03,0x00,0x0A,0x17, /* 00000938 "........" */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x02,0x00, /* 00000940 "........" */ + 0x00,0x00,0x0A,0x18,0x12,0x0B,0x04,0x0C, /* 00000948 "........" */ + 0xFF,0xFF,0x02,0x00,0x01,0x00,0x0A,0x19, /* 00000950 "........" */ + 0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x02,0x00, /* 00000958 "........" */ + 0x0A,0x02,0x00,0x0A,0x1A,0x12,0x0C,0x04, /* 00000960 "........" */ + 0x0C,0xFF,0xFF,0x02,0x00,0x0A,0x03,0x00, /* 00000968 "........" */ + 0x0A,0x1B,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000970 "........" */ + 0x03,0x00,0x00,0x00,0x0A,0x1C,0x12,0x0B, /* 00000978 "........" */ + 0x04,0x0C,0xFF,0xFF,0x03,0x00,0x01,0x00, /* 00000980 "........" */ + 0x0A,0x1D,0x12,0x0C,0x04,0x0C,0xFF,0xFF, /* 00000988 "........" */ + 0x03,0x00,0x0A,0x02,0x00,0x0A,0x1E,0x12, /* 00000990 "........" */ + 0x0C,0x04,0x0C,0xFF,0xFF,0x03,0x00,0x0A, /* 00000998 "........" */ + 0x03,0x00,0x0A,0x1F,0x12,0x0B,0x04,0x0C, /* 000009A0 "........" */ + 0xFF,0xFF,0x04,0x00,0x00,0x00,0x0A,0x20, /* 000009A8 "....... " */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x04,0x00, /* 000009B0 "........" */ + 0x01,0x00,0x0A,0x21,0x12,0x0C,0x04,0x0C, /* 000009B8 "...!...." */ + 0xFF,0xFF,0x04,0x00,0x0A,0x02,0x00,0x0A, /* 000009C0 "........" */ + 0x22,0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x04, /* 000009C8 ""......." */ + 0x00,0x0A,0x03,0x00,0x0A,0x23,0x12,0x0B, /* 000009D0 ".....#.." */ + 0x04,0x0C,0xFF,0xFF,0x05,0x00,0x00,0x00, /* 000009D8 "........" */ + 0x0A,0x24,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 000009E0 ".$......" */ + 0x05,0x00,0x01,0x00,0x0A,0x25,0x12,0x0C, /* 000009E8 ".....%.." */ + 0x04,0x0C,0xFF,0xFF,0x05,0x00,0x0A,0x02, /* 000009F0 "........" */ + 0x00,0x0A,0x26,0x12,0x0C,0x04,0x0C,0xFF, /* 000009F8 "..&....." */ + 0xFF,0x05,0x00,0x0A,0x03,0x00,0x0A,0x27, /* 00000A00 ".......'" */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x06,0x00, /* 00000A08 "........" */ + 0x00,0x00,0x0A,0x28,0x12,0x0B,0x04,0x0C, /* 00000A10 "...(...." */ + 0xFF,0xFF,0x06,0x00,0x01,0x00,0x0A,0x29, /* 00000A18 ".......)" */ + 0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x06,0x00, /* 00000A20 "........" */ + 0x0A,0x02,0x00,0x0A,0x2A,0x12,0x0C,0x04, /* 00000A28 "....*..." */ + 0x0C,0xFF,0xFF,0x06,0x00,0x0A,0x03,0x00, /* 00000A30 "........" */ + 0x0A,0x2B,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000A38 ".+......" */ + 0x07,0x00,0x00,0x00,0x0A,0x2C,0x12,0x0B, /* 00000A40 ".....,.." */ + 0x04,0x0C,0xFF,0xFF,0x07,0x00,0x01,0x00, /* 00000A48 "........" */ + 0x0A,0x2D,0x12,0x0C,0x04,0x0C,0xFF,0xFF, /* 00000A50 ".-......" */ + 0x07,0x00,0x0A,0x02,0x00,0x0A,0x2E,0x12, /* 00000A58 "........" */ + 0x0C,0x04,0x0C,0xFF,0xFF,0x07,0x00,0x0A, /* 00000A60 "........" */ + 0x03,0x00,0x0A,0x2F,0x12,0x0B,0x04,0x0C, /* 00000A68 ".../...." */ + 0xFF,0xFF,0x08,0x00,0x00,0x00,0x0A,0x11, /* 00000A70 "........" */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x08,0x00, /* 00000A78 "........" */ + 0x01,0x00,0x0A,0x12,0x12,0x0C,0x04,0x0C, /* 00000A80 "........" */ + 0xFF,0xFF,0x08,0x00,0x0A,0x02,0x00,0x0A, /* 00000A88 "........" */ + 0x13,0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x08, /* 00000A90 "........" */ + 0x00,0x0A,0x03,0x00,0x0A,0x14,0x12,0x0B, /* 00000A98 "........" */ + 0x04,0x0C,0xFF,0xFF,0x09,0x00,0x00,0x00, /* 00000AA0 "........" */ + 0x0A,0x15,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000AA8 "........" */ + 0x09,0x00,0x01,0x00,0x0A,0x16,0x12,0x0C, /* 00000AB0 "........" */ + 0x04,0x0C,0xFF,0xFF,0x09,0x00,0x0A,0x02, /* 00000AB8 "........" */ + 0x00,0x0A,0x17,0x12,0x0C,0x04,0x0C,0xFF, /* 00000AC0 "........" */ + 0xFF,0x09,0x00,0x0A,0x03,0x00,0x0A,0x18, /* 00000AC8 "........" */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x0A,0x00, /* 00000AD0 "........" */ + 0x00,0x00,0x0A,0x19,0x12,0x0B,0x04,0x0C, /* 00000AD8 "........" */ + 0xFF,0xFF,0x0A,0x00,0x01,0x00,0x0A,0x1A, /* 00000AE0 "........" */ + 0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x0A,0x00, /* 00000AE8 "........" */ + 0x0A,0x02,0x00,0x0A,0x1B,0x12,0x0C,0x04, /* 00000AF0 "........" */ + 0x0C,0xFF,0xFF,0x0A,0x00,0x0A,0x03,0x00, /* 00000AF8 "........" */ + 0x0A,0x1C,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000B00 "........" */ + 0x0B,0x00,0x00,0x00,0x0A,0x1D,0x12,0x0B, /* 00000B08 "........" */ + 0x04,0x0C,0xFF,0xFF,0x0B,0x00,0x01,0x00, /* 00000B10 "........" */ + 0x0A,0x1E,0x12,0x0C,0x04,0x0C,0xFF,0xFF, /* 00000B18 "........" */ + 0x0B,0x00,0x0A,0x02,0x00,0x0A,0x1F,0x12, /* 00000B20 "........" */ + 0x0C,0x04,0x0C,0xFF,0xFF,0x0B,0x00,0x0A, /* 00000B28 "........" */ + 0x03,0x00,0x0A,0x20,0x12,0x0B,0x04,0x0C, /* 00000B30 "... ...." */ + 0xFF,0xFF,0x0C,0x00,0x00,0x00,0x0A,0x21, /* 00000B38 ".......!" */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x0C,0x00, /* 00000B40 "........" */ + 0x01,0x00,0x0A,0x22,0x12,0x0C,0x04,0x0C, /* 00000B48 "..."...." */ + 0xFF,0xFF,0x0C,0x00,0x0A,0x02,0x00,0x0A, /* 00000B50 "........" */ + 0x23,0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x0C, /* 00000B58 "#......." */ + 0x00,0x0A,0x03,0x00,0x0A,0x24,0x12,0x0B, /* 00000B60 ".....$.." */ + 0x04,0x0C,0xFF,0xFF,0x0D,0x00,0x00,0x00, /* 00000B68 "........" */ + 0x0A,0x25,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000B70 ".%......" */ + 0x0D,0x00,0x01,0x00,0x0A,0x26,0x12,0x0C, /* 00000B78 ".....&.." */ + 0x04,0x0C,0xFF,0xFF,0x0D,0x00,0x0A,0x02, /* 00000B80 "........" */ + 0x00,0x0A,0x27,0x12,0x0C,0x04,0x0C,0xFF, /* 00000B88 "..'....." */ + 0xFF,0x0D,0x00,0x0A,0x03,0x00,0x0A,0x28, /* 00000B90 ".......(" */ + 0x12,0x0B,0x04,0x0C,0xFF,0xFF,0x0E,0x00, /* 00000B98 "........" */ + 0x00,0x00,0x0A,0x29,0x12,0x0B,0x04,0x0C, /* 00000BA0 "...)...." */ + 0xFF,0xFF,0x0E,0x00,0x01,0x00,0x0A,0x2A, /* 00000BA8 ".......*" */ + 0x12,0x0C,0x04,0x0C,0xFF,0xFF,0x0E,0x00, /* 00000BB0 "........" */ + 0x0A,0x02,0x00,0x0A,0x2B,0x12,0x0C,0x04, /* 00000BB8 "....+..." */ + 0x0C,0xFF,0xFF,0x0E,0x00,0x0A,0x03,0x00, /* 00000BC0 "........" */ + 0x0A,0x2C,0x12,0x0B,0x04,0x0C,0xFF,0xFF, /* 00000BC8 ".,......" */ + 0x0F,0x00,0x00,0x00,0x0A,0x2D,0x12,0x0B, /* 00000BD0 ".....-.." */ + 0x04,0x0C,0xFF,0xFF,0x0F,0x00,0x01,0x00, /* 00000BD8 "........" */ + 0x0A,0x2E,0x12,0x0C,0x04,0x0C,0xFF,0xFF, /* 00000BE0 "........" */ + 0x0F,0x00,0x0A,0x02,0x00,0x0A,0x2F,0x12, /* 00000BE8 "....../." */ + 0x0C,0x04,0x0C,0xFF,0xFF,0x0F,0x00,0x0A, /* 00000BF0 "........" */ + 0x03,0x00,0x0A,0x10,0x5B,0x82,0x46,0x37, /* 00000BF8 "....[.F7" */ + 0x49,0x53,0x41,0x5F,0x08,0x5F,0x41,0x44, /* 00000C00 "ISA_._AD" */ + 0x52,0x0C,0x00,0x00,0x01,0x00,0x5B,0x80, /* 00000C08 "R.....[." */ + 0x50,0x49,0x52,0x51,0x02,0x0A,0x60,0x0A, /* 00000C10 "PIRQ..`." */ + 0x04,0x10,0x2E,0x5C,0x00,0x5B,0x81,0x29, /* 00000C18 "...\.[.)" */ + 0x5C,0x2F,0x04,0x5F,0x53,0x42,0x5F,0x50, /* 00000C20 "\/._SB_P" */ + 0x43,0x49,0x30,0x49,0x53,0x41,0x5F,0x50, /* 00000C28 "CI0ISA_P" */ + 0x49,0x52,0x51,0x01,0x50,0x49,0x52,0x41, /* 00000C30 "IRQ.PIRA" */ + 0x08,0x50,0x49,0x52,0x42,0x08,0x50,0x49, /* 00000C38 ".PIRB.PI" */ + 0x52,0x43,0x08,0x50,0x49,0x52,0x44,0x08, /* 00000C40 "RC.PIRD." */ + 0x5B,0x82,0x46,0x0B,0x53,0x59,0x53,0x52, /* 00000C48 "[.F.SYSR" */ + 0x08,0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0, /* 00000C50 "._HID.A." */ + 0x0C,0x02,0x08,0x5F,0x55,0x49,0x44,0x01, /* 00000C58 "..._UID." */ + 0x08,0x43,0x52,0x53,0x5F,0x11,0x4E,0x08, /* 00000C60 ".CRS_.N." */ + 0x0A,0x8A,0x47,0x01,0x10,0x00,0x10,0x00, /* 00000C68 "..G....." */ + 0x00,0x10,0x47,0x01,0x22,0x00,0x22,0x00, /* 00000C70 "..G."."." */ + 0x00,0x0C,0x47,0x01,0x30,0x00,0x30,0x00, /* 00000C78 "..G.0.0." */ + 0x00,0x10,0x47,0x01,0x44,0x00,0x44,0x00, /* 00000C80 "..G.D.D." */ + 0x00,0x1C,0x47,0x01,0x62,0x00,0x62,0x00, /* 00000C88 "..G.b.b." */ + 0x00,0x02,0x47,0x01,0x65,0x00,0x65,0x00, /* 00000C90 "..G.e.e." */ + 0x00,0x0B,0x47,0x01,0x72,0x00,0x72,0x00, /* 00000C98 "..G.r.r." */ + 0x00,0x0E,0x47,0x01,0x80,0x00,0x80,0x00, /* 00000CA0 "..G....." */ + 0x00,0x01,0x47,0x01,0x84,0x00,0x84,0x00, /* 00000CA8 "..G....." */ + 0x00,0x03,0x47,0x01,0x88,0x00,0x88,0x00, /* 00000CB0 "..G....." */ + 0x00,0x01,0x47,0x01,0x8C,0x00,0x8C,0x00, /* 00000CB8 "..G....." */ + 0x00,0x03,0x47,0x01,0x90,0x00,0x90,0x00, /* 00000CC0 "..G....." */ + 0x00,0x10,0x47,0x01,0xA2,0x00,0xA2,0x00, /* 00000CC8 "..G....." */ + 0x00,0x1C,0x47,0x01,0xE0,0x00,0xE0,0x00, /* 00000CD0 "..G....." */ + 0x00,0x10,0x47,0x01,0xA0,0x08,0xA0,0x08, /* 00000CD8 "..G....." */ + 0x00,0x04,0x47,0x01,0xC0,0x0C,0xC0,0x0C, /* 00000CE0 "..G....." */ + 0x00,0x10,0x47,0x01,0xD0,0x04,0xD0,0x04, /* 00000CE8 "..G....." */ + 0x00,0x02,0x79,0x00,0x14,0x0B,0x5F,0x43, /* 00000CF0 "..y..._C" */ + 0x52,0x53,0x00,0xA4,0x43,0x52,0x53,0x5F, /* 00000CF8 "RS..CRS_" */ + 0x5B,0x82,0x2B,0x50,0x49,0x43,0x5F,0x08, /* 00000D00 "[.+PIC_." */ + 0x5F,0x48,0x49,0x44,0x0B,0x41,0xD0,0x08, /* 00000D08 "_HID.A.." */ + 0x5F,0x43,0x52,0x53,0x11,0x18,0x0A,0x15, /* 00000D10 "_CRS...." */ + 0x47,0x01,0x20,0x00,0x20,0x00,0x01,0x02, /* 00000D18 "G. . ..." */ + 0x47,0x01,0xA0,0x00,0xA0,0x00,0x01,0x02, /* 00000D20 "G......." */ + 0x22,0x04,0x00,0x79,0x00,0x5B,0x82,0x47, /* 00000D28 ""..y.[.G" */ + 0x05,0x44,0x4D,0x41,0x30,0x08,0x5F,0x48, /* 00000D30 ".DMA0._H" */ + 0x49,0x44,0x0C,0x41,0xD0,0x02,0x00,0x08, /* 00000D38 "ID.A...." */ + 0x5F,0x43,0x52,0x53,0x11,0x41,0x04,0x0A, /* 00000D40 "_CRS.A.." */ + 0x3D,0x2A,0x10,0x04,0x47,0x01,0x00,0x00, /* 00000D48 "=*..G..." */ + 0x00,0x00,0x00,0x10,0x47,0x01,0x81,0x00, /* 00000D50 "....G..." */ + 0x81,0x00,0x00,0x03,0x47,0x01,0x87,0x00, /* 00000D58 "....G..." */ + 0x87,0x00,0x00,0x01,0x47,0x01,0x89,0x00, /* 00000D60 "....G..." */ + 0x89,0x00,0x00,0x03,0x47,0x01,0x8F,0x00, /* 00000D68 "....G..." */ + 0x8F,0x00,0x00,0x01,0x47,0x01,0xC0,0x00, /* 00000D70 "....G..." */ + 0xC0,0x00,0x00,0x20,0x47,0x01,0x80,0x04, /* 00000D78 "... G..." */ + 0x80,0x04,0x00,0x10,0x79,0x00,0x5B,0x82, /* 00000D80 "....y.[." */ + 0x25,0x54,0x4D,0x52,0x5F,0x08,0x5F,0x48, /* 00000D88 "%TMR_._H" */ + 0x49,0x44,0x0C,0x41,0xD0,0x01,0x00,0x08, /* 00000D90 "ID.A...." */ + 0x5F,0x43,0x52,0x53,0x11,0x10,0x0A,0x0D, /* 00000D98 "_CRS...." */ + 0x47,0x01,0x40,0x00,0x40,0x00,0x00,0x04, /* 00000DA0 "G.@.@..." */ + 0x22,0x01,0x00,0x79,0x00,0x5B,0x82,0x25, /* 00000DA8 ""..y.[.%" */ + 0x52,0x54,0x43,0x5F,0x08,0x5F,0x48,0x49, /* 00000DB0 "RTC_._HI" */ + 0x44,0x0C,0x41,0xD0,0x0B,0x00,0x08,0x5F, /* 00000DB8 "D.A...._" */ + 0x43,0x52,0x53,0x11,0x10,0x0A,0x0D,0x47, /* 00000DC0 "CRS....G" */ + 0x01,0x70,0x00,0x70,0x00,0x00,0x02,0x22, /* 00000DC8 ".p.p..."" */ + 0x00,0x01,0x79,0x00,0x5B,0x82,0x22,0x53, /* 00000DD0 "..y.[."S" */ + 0x50,0x4B,0x52,0x08,0x5F,0x48,0x49,0x44, /* 00000DD8 "PKR._HID" */ + 0x0C,0x41,0xD0,0x08,0x00,0x08,0x5F,0x43, /* 00000DE0 ".A...._C" */ + 0x52,0x53,0x11,0x0D,0x0A,0x0A,0x47,0x01, /* 00000DE8 "RS....G." */ + 0x61,0x00,0x61,0x00,0x00,0x01,0x79,0x00, /* 00000DF0 "a.a...y." */ + 0x5B,0x82,0x31,0x50,0x53,0x32,0x4D,0x08, /* 00000DF8 "[.1PS2M." */ + 0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0,0x0F, /* 00000E00 "_HID.A.." */ + 0x13,0x08,0x5F,0x43,0x49,0x44,0x0C,0x41, /* 00000E08 ".._CID.A" */ + 0xD0,0x0F,0x13,0x14,0x09,0x5F,0x53,0x54, /* 00000E10 "....._ST" */ + 0x41,0x00,0xA4,0x0A,0x0F,0x08,0x5F,0x43, /* 00000E18 "A....._C" */ + 0x52,0x53,0x11,0x08,0x0A,0x05,0x22,0x00, /* 00000E20 "RS...."." */ + 0x10,0x79,0x00,0x5B,0x82,0x42,0x04,0x50, /* 00000E28 ".y.[.B.P" */ + 0x53,0x32,0x4B,0x08,0x5F,0x48,0x49,0x44, /* 00000E30 "S2K._HID" */ + 0x0C,0x41,0xD0,0x03,0x03,0x08,0x5F,0x43, /* 00000E38 ".A...._C" */ + 0x49,0x44,0x0C,0x41,0xD0,0x03,0x0B,0x14, /* 00000E40 "ID.A...." */ + 0x09,0x5F,0x53,0x54,0x41,0x00,0xA4,0x0A, /* 00000E48 "._STA..." */ + 0x0F,0x08,0x5F,0x43,0x52,0x53,0x11,0x18, /* 00000E50 ".._CRS.." */ + 0x0A,0x15,0x47,0x01,0x60,0x00,0x60,0x00, /* 00000E58 "..G.`.`." */ + 0x00,0x01,0x47,0x01,0x64,0x00,0x64,0x00, /* 00000E60 "..G.d.d." */ + 0x00,0x01,0x22,0x02,0x00,0x79,0x00,0x5B, /* 00000E68 ".."..y.[" */ + 0x82,0x3A,0x46,0x44,0x43,0x30,0x08,0x5F, /* 00000E70 ".:FDC0._" */ + 0x48,0x49,0x44,0x0C,0x41,0xD0,0x07,0x00, /* 00000E78 "HID.A..." */ + 0x14,0x09,0x5F,0x53,0x54,0x41,0x00,0xA4, /* 00000E80 ".._STA.." */ + 0x0A,0x0F,0x08,0x5F,0x43,0x52,0x53,0x11, /* 00000E88 "..._CRS." */ + 0x1B,0x0A,0x18,0x47,0x01,0xF0,0x03,0xF0, /* 00000E90 "...G...." */ + 0x03,0x01,0x06,0x47,0x01,0xF7,0x03,0xF7, /* 00000E98 "...G...." */ + 0x03,0x01,0x01,0x22,0x40,0x00,0x2A,0x04, /* 00000EA0 "..."@.*." */ + 0x00,0x79,0x00,0x5B,0x82,0x46,0x04,0x55, /* 00000EA8 ".y.[.F.U" */ + 0x41,0x52,0x31,0x08,0x5F,0x48,0x49,0x44, /* 00000EB0 "AR1._HID" */ + 0x0C,0x41,0xD0,0x05,0x01,0x08,0x5F,0x55, /* 00000EB8 ".A...._U" */ + 0x49,0x44,0x01,0x14,0x19,0x5F,0x53,0x54, /* 00000EC0 "ID..._ST" */ + 0x41,0x00,0xA0,0x0D,0x93,0x5E,0x5E,0x5E, /* 00000EC8 "A....^^^" */ + 0x5E,0x55,0x41,0x52,0x31,0x00,0xA4,0x00, /* 00000ED0 "^UAR1..." */ + 0xA1,0x04,0xA4,0x0A,0x0F,0x08,0x5F,0x43, /* 00000ED8 "......_C" */ + 0x52,0x53,0x11,0x10,0x0A,0x0D,0x47,0x01, /* 00000EE0 "RS....G." */ + 0xF8,0x03,0xF8,0x03,0x08,0x08,0x22,0x10, /* 00000EE8 "......"." */ + 0x00,0x79,0x00,0x5B,0x82,0x47,0x04,0x55, /* 00000EF0 ".y.[.G.U" */ + 0x41,0x52,0x32,0x08,0x5F,0x48,0x49,0x44, /* 00000EF8 "AR2._HID" */ + 0x0C,0x41,0xD0,0x05,0x01,0x08,0x5F,0x55, /* 00000F00 ".A...._U" */ + 0x49,0x44,0x0A,0x02,0x14,0x19,0x5F,0x53, /* 00000F08 "ID...._S" */ + 0x54,0x41,0x00,0xA0,0x0D,0x93,0x5E,0x5E, /* 00000F10 "TA....^^" */ + 0x5E,0x5E,0x55,0x41,0x52,0x32,0x00,0xA4, /* 00000F18 "^^UAR2.." */ + 0x00,0xA1,0x04,0xA4,0x0A,0x0F,0x08,0x5F, /* 00000F20 "......._" */ + 0x43,0x52,0x53,0x11,0x10,0x0A,0x0D,0x47, /* 00000F28 "CRS....G" */ + 0x01,0xF8,0x02,0xF8,0x02,0x08,0x08,0x22, /* 00000F30 "......."" */ + 0x08,0x00,0x79,0x00,0x5B,0x82,0x36,0x4C, /* 00000F38 "..y.[.6L" */ + 0x54,0x50,0x31,0x08,0x5F,0x48,0x49,0x44, /* 00000F40 "TP1._HID" */ + 0x0C,0x41,0xD0,0x04,0x00,0x08,0x5F,0x55, /* 00000F48 ".A...._U" */ + 0x49,0x44,0x0A,0x02,0x14,0x09,0x5F,0x53, /* 00000F50 "ID...._S" */ + 0x54,0x41,0x00,0xA4,0x0A,0x0F,0x08,0x5F, /* 00000F58 "TA....._" */ + 0x43,0x52,0x53,0x11,0x10,0x0A,0x0D,0x47, /* 00000F60 "CRS....G" */ + 0x01,0x78,0x03,0x78,0x03,0x08,0x08,0x22, /* 00000F68 ".x.x..."" */ + 0x80,0x00,0x79,0x00,0x5B,0x82,0x4D,0x07, /* 00000F70 "..y.[.M." */ + 0x53,0x31,0x46,0x30,0x08,0x5F,0x41,0x44, /* 00000F78 "S1F0._AD" */ + 0x52,0x0C,0x00,0x00,0x06,0x00,0x08,0x5F, /* 00000F80 "R......_" */ + 0x53,0x55,0x4E,0x01,0x14,0x13,0x5F,0x50, /* 00000F88 "SUN..._P" */ + 0x53,0x30,0x00,0x70,0x0A,0x80,0x5C,0x2E, /* 00000F90 "S0.p..\." */ + 0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000F98 "_GPEDPT2" */ + 0x14,0x13,0x5F,0x50,0x53,0x33,0x00,0x70, /* 00000FA0 ".._PS3.p" */ + 0x0A,0x83,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00000FA8 "..\._GPE" */ + 0x44,0x50,0x54,0x32,0x14,0x1F,0x5F,0x45, /* 00000FB0 "DPT2.._E" */ + 0x4A,0x30,0x01,0x70,0x0A,0x88,0x5C,0x2E, /* 00000FB8 "J0.p..\." */ + 0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000FC0 "_GPEDPT2" */ + 0x70,0x01,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00000FC8 "p.\._GPE" */ + 0x50,0x48,0x50,0x31,0x14,0x1E,0x5F,0x53, /* 00000FD0 "PHP1.._S" */ + 0x54,0x41,0x00,0x70,0x0A,0x89,0x5C,0x2E, /* 00000FD8 "TA.p..\." */ + 0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000FE0 "_GPEDPT2" */ + 0xA4,0x5C,0x2E,0x5F,0x47,0x50,0x45,0x50, /* 00000FE8 ".\._GPEP" */ + 0x48,0x50,0x31,0x5B,0x82,0x4E,0x07,0x53, /* 00000FF0 "HP1[.N.S" */ + 0x32,0x46,0x30,0x08,0x5F,0x41,0x44,0x52, /* 00000FF8 "2F0._ADR" */ + 0x0C,0x00,0x00,0x07,0x00,0x08,0x5F,0x53, /* 00001000 "......_S" */ + 0x55,0x4E,0x0A,0x02,0x14,0x13,0x5F,0x50, /* 00001008 "UN...._P" */ + 0x53,0x30,0x00,0x70,0x0A,0x90,0x5C,0x2E, /* 00001010 "S0.p..\." */ + 0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00001018 "_GPEDPT2" */ + 0x14,0x13,0x5F,0x50,0x53,0x33,0x00,0x70, /* 00001020 ".._PS3.p" */ + 0x0A,0x93,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00001028 "..\._GPE" */ + 0x44,0x50,0x54,0x32,0x14,0x1F,0x5F,0x45, /* 00001030 "DPT2.._E" */ + 0x4A,0x30,0x01,0x70,0x0A,0x98,0x5C,0x2E, /* 00001038 "J0.p..\." */ + 0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00001040 "_GPEDPT2" */ + 0x70,0x01,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00001048 "p.\._GPE" */ + 0x50,0x48,0x50,0x32,0x14,0x1E,0x5F,0x53, /* 00001050 "PHP2.._S" */ + 0x54,0x41,0x00,0x70,0x0A,0x99,0x5C,0x2E, /* 00001058 "TA.p..\." */ + 0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00001060 "_GPEDPT2" */ + 0xA4,0x5C,0x2E,0x5F,0x47,0x50,0x45,0x50, /* 00001068 ".\._GPEP" */ + 0x48,0x50,0x32,0x10,0x4E,0x0B,0x5F,0x47, /* 00001070 "HP2.N._G" */ + 0x50,0x45,0x5B,0x80,0x50,0x48,0x50,0x5F, /* 00001078 "PE[.PHP_" */ + 0x01,0x0B,0xC0,0x10,0x0A,0x03,0x5B,0x81, /* 00001080 "......[." */ + 0x15,0x50,0x48,0x50,0x5F,0x01,0x50,0x53, /* 00001088 ".PHP_.PS" */ + 0x54,0x41,0x08,0x50,0x48,0x50,0x31,0x08, /* 00001090 "TA.PHP1." */ + 0x50,0x48,0x50,0x32,0x08,0x5B,0x80,0x44, /* 00001098 "PHP2.[.D" */ + 0x47,0x31,0x5F,0x01,0x0B,0x44,0xB0,0x0A, /* 000010A0 "G1_..D.." */ + 0x04,0x5B,0x81,0x10,0x44,0x47,0x31,0x5F, /* 000010A8 ".[..DG1_" */ + 0x01,0x44,0x50,0x54,0x31,0x08,0x44,0x50, /* 000010B0 ".DPT1.DP" */ + 0x54,0x32,0x08,0x14,0x46,0x07,0x5F,0x4C, /* 000010B8 "T2..F._L" */ + 0x30,0x33,0x00,0x08,0x53,0x4C,0x54,0x5F, /* 000010C0 "03..SLT_" */ + 0x00,0x08,0x45,0x56,0x54,0x5F,0x00,0x70, /* 000010C8 "..EVT_.p" */ + 0x50,0x53,0x54,0x41,0x61,0x7A,0x61,0x0A, /* 000010D0 "PSTAaza." */ + 0x04,0x53,0x4C,0x54,0x5F,0x7B,0x61,0x0A, /* 000010D8 ".SLT_{a." */ + 0x0F,0x45,0x56,0x54,0x5F,0x70,0x53,0x4C, /* 000010E0 ".EVT_pSL" */ + 0x54,0x5F,0x44,0x50,0x54,0x31,0x70,0x45, /* 000010E8 "T_DPT1pE" */ + 0x56,0x54,0x5F,0x44,0x50,0x54,0x32,0xA0, /* 000010F0 "VT_DPT2." */ + 0x1B,0x93,0x53,0x4C,0x54,0x5F,0x01,0x86, /* 000010F8 "..SLT_.." */ + 0x5C,0x2F,0x03,0x5F,0x53,0x42,0x5F,0x50, /* 00001100 "\/._SB_P" */ + 0x43,0x49,0x30,0x53,0x31,0x46,0x30,0x45, /* 00001108 "CI0S1F0E" */ + 0x56,0x54,0x5F,0xA1,0x1E,0xA0,0x1C,0x93, /* 00001110 "VT_....." */ + 0x53,0x4C,0x54,0x5F,0x0A,0x02,0x86,0x5C, /* 00001118 "SLT_...\" */ + 0x2F,0x03,0x5F,0x53,0x42,0x5F,0x50,0x43, /* 00001120 "/._SB_PC" */ + 0x49,0x30,0x53,0x32,0x46,0x30,0x45,0x56, /* 00001128 "I0S2F0EV" */ + 0x54,0x5F, +}; +int DsdtLen=sizeof(AmlCode); diff --git a/tools/firmware/hvmloader/acpi/ssdt_pm.asl b/tools/firmware/hvmloader/acpi/ssdt_pm.asl new file mode 100644 index 0000000..afb78b6 --- /dev/null +++ b/tools/firmware/hvmloader/acpi/ssdt_pm.asl @@ -0,0 +1,423 @@ +/* + * 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 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * 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) + } + } + + Method (HLPA, 0, NotSerialized) + { + Store (HLP6 (), Local0) + Name (TMP, Buffer (Local0) {}) + HLP9 (TMP, Local0) + Return (TMP) + } + + 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 () + Name (BST0, Package (0x04) {}) + Store (HLP7 (), Index (BST0, 0x00)) + Store (HLP7 (), Index (BST0, 0x01)) + Store (HLP7 (), Index (BST0, 0x02)) + Store (HLP7 (), Index (BST0, 0x03)) + REL () + Store (2, \_SB.DBG1) + Return (BST0) + } + } + + /* 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 () + Name (BST1, Package (0x04) {}) + Store (HLP7 (), Index (BST1, 0x00)) + Store (HLP7 (), Index (BST1, 0x01)) + Store (HLP7 (), Index (BST1, 0x02)) + Store (HLP7 (), Index (BST1, 0x03)) + REL () + Return (BST1) + } + } + } +} + diff --git a/tools/firmware/hvmloader/acpi/ssdt_pm.h b/tools/firmware/hvmloader/acpi/ssdt_pm.h new file mode 100644 index 0000000..020af0b --- /dev/null +++ b/tools/firmware/hvmloader/acpi/ssdt_pm.h @@ -0,0 +1,202 @@ +/* + * + * Intel ACPI Component Architecture + * ASL Optimizing Compiler version 20061109 [May 18 2007] + * Copyright (C) 2000 - 2006 Intel Corporation + * Supports ACPI Specification Revision 3.0a + * + * Compilation of "ssdt_pm.asl" - Sun Oct 12 23:57:51 2008 + * + * C source code output + * + */ +unsigned char AmlCode_PM[] = +{ + 0x53,0x53,0x44,0x54,0xD6,0x05,0x00,0x00, /* 00000000 "SSDT...." */ + 0x02,0xD9,0x58,0x65,0x6E,0x00,0x00,0x00, /* 00000008 "..Xen..." */ + 0x48,0x56,0x4D,0x00,0x00,0x00,0x00,0x00, /* 00000010 "HVM....." */ + 0x00,0x00,0x00,0x00,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */ + 0x09,0x11,0x06,0x20,0x10,0x41,0x5B,0x5C, /* 00000020 "... .A[\" */ + 0x5F,0x53,0x42,0x5F,0x5B,0x80,0x44,0x42, /* 00000028 "_SB_[.DB" */ + 0x47,0x41,0x01,0x0B,0x40,0xB0,0x01,0x5B, /* 00000030 "GA..@..[" */ + 0x81,0x0B,0x44,0x42,0x47,0x41,0x01,0x44, /* 00000038 "..DBGA.D" */ + 0x42,0x47,0x31,0x08,0x5B,0x80,0x44,0x42, /* 00000040 "BG1.[.DB" */ + 0x47,0x42,0x01,0x0B,0x44,0xB0,0x01,0x5B, /* 00000048 "GB..D..[" */ + 0x81,0x0B,0x44,0x42,0x47,0x42,0x01,0x44, /* 00000050 "..DBGB.D" */ + 0x42,0x47,0x32,0x08,0x5B,0x80,0x44,0x42, /* 00000058 "BG2.[.DB" */ + 0x47,0x43,0x01,0x0B,0x46,0xB0,0x01,0x5B, /* 00000060 "GC..F..[" */ + 0x81,0x0B,0x44,0x42,0x47,0x43,0x01,0x44, /* 00000068 "..DBGC.D" */ + 0x42,0x47,0x33,0x08,0x5B,0x80,0x44,0x42, /* 00000070 "BG3.[.DB" */ + 0x47,0x44,0x01,0x0B,0x48,0xB0,0x01,0x5B, /* 00000078 "GD..H..[" */ + 0x81,0x0B,0x44,0x42,0x47,0x44,0x01,0x44, /* 00000080 "..DBGD.D" */ + 0x42,0x47,0x34,0x08,0x5B,0x80,0x50,0x52, /* 00000088 "BG4.[.PR" */ + 0x54,0x31,0x01,0x0A,0xB2,0x0A,0x02,0x5B, /* 00000090 "T1.....[" */ + 0x81,0x10,0x50,0x52,0x54,0x31,0x01,0x50, /* 00000098 "..PRT1.P" */ + 0x42,0x32,0x5F,0x08,0x50,0x42,0x32,0x41, /* 000000A0 "B2_.PB2A" */ + 0x08,0x5B,0x80,0x50,0x52,0x54,0x32,0x01, /* 000000A8 ".[.PRT2." */ + 0x0A,0x86,0x01,0x5B,0x81,0x0B,0x50,0x52, /* 000000B0 "...[..PR" */ + 0x54,0x32,0x01,0x50,0x38,0x36,0x5F,0x08, /* 000000B8 "T2.P86_." */ + 0x5B,0x80,0x50,0x52,0x54,0x33,0x01,0x0A, /* 000000C0 "[.PRT3.." */ + 0x88,0x01,0x5B,0x81,0x0B,0x50,0x52,0x54, /* 000000C8 "..[..PRT" */ + 0x33,0x01,0x50,0x38,0x38,0x5F,0x08,0x5B, /* 000000D0 "3.P88_.[" */ + 0x01,0x53,0x59,0x4E,0x43,0x01,0x08,0x42, /* 000000D8 ".SYNC..B" */ + 0x55,0x46,0x30,0x11,0x04,0x0B,0x00,0x01, /* 000000E0 "UF0....." */ + 0x08,0x42,0x55,0x46,0x31,0x11,0x03,0x0A, /* 000000E8 ".BUF1..." */ + 0x08,0x8B,0x42,0x55,0x46,0x31,0x00,0x42, /* 000000F0 "..BUF1.B" */ + 0x55,0x46,0x41,0x8B,0x42,0x55,0x46,0x31, /* 000000F8 "UFA.BUF1" */ + 0x0A,0x04,0x42,0x55,0x46,0x42,0x14,0x14, /* 00000100 "..BUFB.." */ + 0x41,0x43,0x51,0x52,0x00,0x5B,0x23,0x53, /* 00000108 "ACQR.[#S" */ + 0x59,0x4E,0x43,0xFF,0xFF,0x70,0x00,0x42, /* 00000110 "YNC..p.B" */ + 0x55,0x46,0x41,0x14,0x31,0x49,0x4E,0x49, /* 00000118 "UFA.1INI" */ + 0x54,0x01,0x70,0x42,0x55,0x46,0x41,0x60, /* 00000120 "T.pBUFA`" */ + 0x75,0x60,0xA0,0x22,0x92,0x94,0x60,0x87, /* 00000128 "u`."..`." */ + 0x42,0x55,0x46,0x30,0x8C,0x42,0x55,0x46, /* 00000130 "BUF0.BUF" */ + 0x30,0x42,0x55,0x46,0x41,0x54,0x4D,0x50, /* 00000138 "0BUFATMP" */ + 0x31,0x70,0x68,0x54,0x4D,0x50,0x31,0x70, /* 00000140 "1phTMP1p" */ + 0x60,0x42,0x55,0x46,0x41,0x14,0x48,0x07, /* 00000148 "`BUFA.H." */ + 0x57,0x50,0x52,0x54,0x02,0x70,0x69,0x5C, /* 00000150 "WPRT.pi\" */ + 0x2E,0x5F,0x53,0x42,0x5F,0x50,0x38,0x36, /* 00000158 "._SB_P86" */ + 0x5F,0x70,0x68,0x5C,0x2E,0x5F,0x53,0x42, /* 00000160 "_ph\._SB" */ + 0x5F,0x50,0x42,0x32,0x5F,0x70,0x68,0x5C, /* 00000168 "_PB2_ph\" */ + 0x2E,0x5F,0x53,0x42,0x5F,0x44,0x42,0x47, /* 00000170 "._SB_DBG" */ + 0x32,0x70,0x69,0x5C,0x2E,0x5F,0x53,0x42, /* 00000178 "2pi\._SB" */ + 0x5F,0x44,0x42,0x47,0x34,0x70,0x5C,0x2E, /* 00000180 "_DBG4p\." */ + 0x5F,0x53,0x42,0x5F,0x50,0x42,0x32,0x5F, /* 00000188 "_SB_PB2_" */ + 0x60,0xA2,0x11,0x92,0x93,0x60,0x00,0x70, /* 00000190 "`....`.p" */ + 0x5C,0x2E,0x5F,0x53,0x42,0x5F,0x50,0x42, /* 00000198 "\._SB_PB" */ + 0x32,0x5F,0x60,0x70,0x5C,0x2E,0x5F,0x53, /* 000001A0 "2_`p\._S" */ + 0x42,0x5F,0x50,0x38,0x36,0x5F,0x61,0x70, /* 000001A8 "B_P86_ap" */ + 0x61,0x5C,0x2E,0x5F,0x53,0x42,0x5F,0x44, /* 000001B0 "a\._SB_D" */ + 0x42,0x47,0x33,0xA4,0x5C,0x2E,0x5F,0x53, /* 000001B8 "BG3.\._S" */ + 0x42,0x5F,0x50,0x38,0x36,0x5F,0x14,0x1D, /* 000001C0 "B_P86_.." */ + 0x48,0x4C,0x50,0x31,0x02,0xA0,0x16,0x95, /* 000001C8 "HLP1...." */ + 0x69,0x87,0x68,0x8C,0x68,0x69,0x54,0x4D, /* 000001D0 "i.h.hiTM" */ + 0x50,0x31,0x57,0x50,0x52,0x54,0x0A,0x7C, /* 000001D8 "P1WPRT.|" */ + 0x54,0x4D,0x50,0x31,0x14,0x23,0x48,0x4C, /* 000001E0 "TMP1.#HL" */ + 0x50,0x32,0x00,0x57,0x50,0x52,0x54,0x0A, /* 000001E8 "P2.WPRT." */ + 0x7B,0x00,0x70,0x00,0x60,0xA2,0x12,0x95, /* 000001F0 "{.p.`..." */ + 0x60,0x42,0x55,0x46,0x41,0x48,0x4C,0x50, /* 000001F8 "`BUFAHLP" */ + 0x31,0x42,0x55,0x46,0x30,0x60,0x75,0x60, /* 00000200 "1BUF0`u`" */ + 0x14,0x1F,0x48,0x4C,0x50,0x33,0x02,0xA0, /* 00000208 "..HLP3.." */ + 0x18,0x95,0x69,0x87,0x68,0x8C,0x68,0x69, /* 00000210 "..i.h.hi" */ + 0x54,0x4D,0x50,0x31,0x70,0x57,0x50,0x52, /* 00000218 "TMP1pWPR" */ + 0x54,0x0A,0x7D,0x00,0x54,0x4D,0x50,0x31, /* 00000220 "T.}.TMP1" */ + 0x14,0x23,0x48,0x4C,0x50,0x34,0x00,0x70, /* 00000228 ".#HLP4.p" */ + 0x00,0x60,0xA2,0x19,0x95,0x60,0x42,0x55, /* 00000230 ".`...`BU" */ + 0x46,0x42,0x72,0x42,0x55,0x46,0x41,0x60, /* 00000238 "FBrBUFA`" */ + 0x61,0x48,0x4C,0x50,0x33,0x42,0x55,0x46, /* 00000240 "aHLP3BUF" */ + 0x30,0x61,0x75,0x60,0x14,0x42,0x04,0x48, /* 00000248 "0au`.B.H" */ + 0x4C,0x50,0x35,0x00,0x48,0x4C,0x50,0x32, /* 00000250 "LP5.HLP2" */ + 0x70,0x57,0x50,0x52,0x54,0x0A,0x79,0x00, /* 00000258 "pWPRT.y." */ + 0x42,0x55,0x46,0x42,0x72,0x42,0x55,0x46, /* 00000260 "BUFBrBUF" */ + 0x41,0x42,0x55,0x46,0x42,0x60,0xA0,0x1C, /* 00000268 "ABUFB`.." */ + 0x95,0x87,0x42,0x55,0x46,0x30,0x60,0x70, /* 00000270 "..BUF0`p" */ + 0x87,0x42,0x55,0x46,0x30,0x60,0x74,0x60, /* 00000278 ".BUF0`t`" */ + 0x42,0x55,0x46,0x41,0x60,0x70,0x60,0x42, /* 00000280 "BUFA`p`B" */ + 0x55,0x46,0x42,0x48,0x4C,0x50,0x34,0x14, /* 00000288 "UFBHLP4." */ + 0x32,0x48,0x4C,0x50,0x36,0x00,0x70,0x42, /* 00000290 "2HLP6.pB" */ + 0x55,0x46,0x41,0x60,0x75,0x60,0xA0,0x21, /* 00000298 "UFA`u`.!" */ + 0x92,0x94,0x60,0x87,0x42,0x55,0x46,0x30, /* 000002A0 "..`.BUF0" */ + 0x8C,0x42,0x55,0x46,0x30,0x42,0x55,0x46, /* 000002A8 ".BUF0BUF" */ + 0x41,0x54,0x4D,0x50,0x31,0x70,0x60,0x42, /* 000002B0 "ATMP1p`B" */ + 0x55,0x46,0x41,0xA4,0x54,0x4D,0x50,0x31, /* 000002B8 "UFA.TMP1" */ + 0xA4,0x00,0x14,0x35,0x48,0x4C,0x50,0x37, /* 000002C0 "...5HLP7" */ + 0x00,0x70,0x42,0x55,0x46,0x41,0x60,0x72, /* 000002C8 ".pBUFA`r" */ + 0x60,0x0A,0x04,0x60,0xA0,0x21,0x92,0x94, /* 000002D0 "`..`.!.." */ + 0x60,0x87,0x42,0x55,0x46,0x30,0x8A,0x42, /* 000002D8 "`.BUF0.B" */ + 0x55,0x46,0x30,0x42,0x55,0x46,0x41,0x53, /* 000002E0 "UF0BUFAS" */ + 0x58,0x32,0x32,0x70,0x60,0x42,0x55,0x46, /* 000002E8 "X22p`BUF" */ + 0x41,0xA4,0x53,0x58,0x32,0x32,0xA4,0x00, /* 000002F0 "A.SX22.." */ + 0x14,0x1C,0x48,0x4C,0x50,0x38,0x02,0xA0, /* 000002F8 "..HLP8.." */ + 0x15,0x95,0x69,0x87,0x68,0x8C,0x68,0x69, /* 00000300 "..i.h.hi" */ + 0x54,0x4D,0x50,0x31,0x70,0x48,0x4C,0x50, /* 00000308 "TMP1pHLP" */ + 0x36,0x54,0x4D,0x50,0x31,0x14,0x16,0x48, /* 00000310 "6TMP1..H" */ + 0x4C,0x50,0x39,0x02,0x70,0x00,0x60,0xA2, /* 00000318 "LP9.p.`." */ + 0x0C,0x95,0x60,0x69,0x48,0x4C,0x50,0x38, /* 00000320 "..`iHLP8" */ + 0x68,0x60,0x75,0x60,0x14,0x22,0x48,0x4C, /* 00000328 "h`u`."HL" */ + 0x50,0x41,0x00,0x70,0x48,0x4C,0x50,0x36, /* 00000330 "PA.pHLP6" */ + 0x60,0x08,0x54,0x4D,0x50,0x5F,0x11,0x02, /* 00000338 "`.TMP_.." */ + 0x60,0x48,0x4C,0x50,0x39,0x54,0x4D,0x50, /* 00000340 "`HLP9TMP" */ + 0x5F,0x60,0xA4,0x54,0x4D,0x50,0x5F,0x14, /* 00000348 "_`.TMP_." */ + 0x0C,0x52,0x45,0x4C,0x5F,0x00,0x5B,0x27, /* 00000350 ".REL_.['" */ + 0x53,0x59,0x4E,0x43,0x5B,0x82,0x3C,0x41, /* 00000358 "SYNC[. + +/* + * 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). + */ + +#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_20_fadt Fadt = { + .header = { + .signature = ACPI_2_0_FADT_SIGNATURE, + .length = sizeof(struct acpi_20_fadt), + .revision = ACPI_2_0_FADT_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 + }, + + .sci_int = 9, + + .pm1a_evt_blk = ACPI_PM1A_EVT_BLK_ADDRESS, + .pm1a_cnt_blk = ACPI_PM1A_CNT_BLK_ADDRESS, + .pm_tmr_blk = ACPI_PM_TMR_BLK_ADDRESS, + .gpe0_blk = ACPI_GPE0_BLK_ADDRESS, + .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, + + .p_lvl2_lat = 0x0fff, /* >100, means we do not support C2 state */ + .p_lvl3_lat = 0x0fff, /* >1000, means we do not support C3 state */ + .iapc_boot_arch = ACPI_LEGACY_DEVICES | ACPI_8042, + .flags = (ACPI_PROC_C1 | ACPI_SLP_BUTTON | + ACPI_WBINVD | ACPI_PWR_BUTTON | + ACPI_FIX_RTC | ACPI_TMR_VAL_EXT), + + .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, + }, + + .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, + }, + + .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, + } +}; + +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) +}; + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/firmware/hvmloader/apic_regs.h b/tools/firmware/hvmloader/apic_regs.h new file mode 100644 index 0000000..4c313ab --- /dev/null +++ b/tools/firmware/hvmloader/apic_regs.h @@ -0,0 +1,108 @@ +#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 diff --git a/tools/firmware/hvmloader/cacheattr.c b/tools/firmware/hvmloader/cacheattr.c new file mode 100644 index 0000000..5dc2c7f --- /dev/null +++ b/tools/firmware/hvmloader/cacheattr.c @@ -0,0 +1,99 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#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 + +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 = 36; + + /* Does the CPU support architectural MTRRs? */ + cpuid(0x00000001, &eax, &ebx, &ecx, &edx); + if ( !(edx & (1u << 12)) ) + return; + + /* 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; + } + + 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 ) + { + /* A single UC range covering PCI space. */ + wrmsr(MSR_MTRRphysBase(0), PCI_MEMBASE); + wrmsr(MSR_MTRRphysMask(0), + ((uint64_t)(int32_t)PCI_MEMBASE & addr_mask) | (1u << 11)); + printf("var MTRRs ... "); + } + + wrmsr(MSR_MTRRdefType, mtrr_def); +} diff --git a/tools/firmware/hvmloader/config.h b/tools/firmware/hvmloader/config.h new file mode 100644 index 0000000..32011cd --- /dev/null +++ b/tools/firmware/hvmloader/config.h @@ -0,0 +1,44 @@ +#ifndef __HVMLOADER_CONFIG_H__ +#define __HVMLOADER_CONFIG_H__ + +#define IOAPIC_BASE_ADDRESS 0xfec00000 +#define IOAPIC_ID 0x01 +#define IOAPIC_VERSION 0x11 + +#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 */ + +#define PCI_MEMBASE 0xf0000000 +#define PCI_MEMSIZE 0x0c000000 + +#define ROMBIOS_SEG 0xF000 +#define ROMBIOS_BEGIN 0x000F0000 +#define ROMBIOS_SIZE 0x00010000 +#define ROMBIOS_MAXOFFSET 0x0000FFFF +#define ROMBIOS_END (ROMBIOS_BEGIN + ROMBIOS_SIZE) + +/* Memory map. */ +#define HYPERCALL_PHYSICAL_ADDRESS 0x00080000 +#define VGABIOS_PHYSICAL_ADDRESS 0x000C0000 +#define ETHERBOOT_PHYSICAL_ADDRESS 0x000D0000 +#define SMBIOS_PHYSICAL_ADDRESS 0x000E9000 +#define SMBIOS_MAXIMUM_SIZE 0x00001000 +#define ACPI_PHYSICAL_ADDRESS 0x000EA000 +#define ROMBIOS_PHYSICAL_ADDRESS 0x000F0000 +#define SCRATCH_PHYSICAL_ADDRESS 0x00010000 + +/* Xen Platform Device */ +#define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */ + +struct bios_info { + uint8_t com1_present:1; + uint8_t com2_present:1; + uint8_t hpet_present:1; + uint32_t pci_min, pci_len; + uint16_t xen_pfiob; +}; + +#endif /* __HVMLOADER_CONFIG_H__ */ diff --git a/tools/firmware/hvmloader/e820.h b/tools/firmware/hvmloader/e820.h new file mode 100644 index 0000000..f59f70c --- /dev/null +++ b/tools/firmware/hvmloader/e820.h @@ -0,0 +1,23 @@ +#ifndef __HVMLOADER_E820_H__ +#define __HVMLOADER_E820_H__ + +#include + +/* + * 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 HVM_E820_NR ((unsigned char *)HVM_E820_PAGE + HVM_E820_NR_OFFSET) +#define HVM_E820 ((struct e820entry *)(HVM_E820_PAGE + HVM_E820_OFFSET)) + +#endif /* __HVMLOADER_E820_H__ */ diff --git a/tools/firmware/hvmloader/hvmloader.c b/tools/firmware/hvmloader/hvmloader.c new file mode 100644 index 0000000..9dff7cc --- /dev/null +++ b/tools/firmware/hvmloader/hvmloader.c @@ -0,0 +1,563 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "roms.h" +#include "acpi/acpi2_0.h" +#include "hypercall.h" +#include "util.h" +#include "config.h" +#include "apic_regs.h" +#include "pci_regs.h" +#include "e820.h" +#include "option_rom.h" +#include +#include + +asm ( + " .text \n" + " .globl _start \n" + "_start: \n" + /* C runtime kickoff. */ + " cld \n" + " cli \n" + " movl $stack_top,%esp \n" + " movl %esp,%ebp \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). */ + " lgdt gdt_desr \n" + " mov $0x0010,%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 $0x8,$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 0x00009a000000ffff \n" /* Ring 0 code, base 0 limit 0xffff */ + " .quad 0x000092000000ffff \n" /* Ring 0 data, base 0 limit 0xffff */ + "gdt_end: \n" + " \n" + " .bss \n" + " .align 8 \n" + "stack: \n" + " .skip 0x4000 \n" + "stack_top: \n" + " .text \n" + ); + +static enum { VGA_none, VGA_std, VGA_cirrus } virtual_vga = VGA_none; + +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 < 0x40001000; 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); +} + +static void apic_setup(void) +{ + /* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */ + ioapic_write(0x00, IOAPIC_ID); + + /* Set up Virtual Wire mode. */ + lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF); + lapic_write(APIC_LVT0, APIC_MODE_EXTINT << 8); + lapic_write(APIC_LVT1, APIC_MODE_NMI << 8); +} + +static void pci_setup(void) +{ + uint32_t base, devfn, bar_reg, bar_data, bar_sz, cmd; + uint16_t class, vendor_id, device_id; + unsigned int bar, pin, link, isa_irq; + + /* Resources assignable to PCI devices via BARs. */ + struct resource { + uint32_t base, max; + } *resource; + struct resource mem_resource = { PCI_MEMBASE, PCI_MEMBASE + PCI_MEMSIZE }; + struct resource io_resource = { 0xc000, 0x10000 }; + + /* Create a list of device BARs in descending order of size. */ + struct bars { + uint32_t devfn, bar_reg, bar_sz; + } *bars = (struct bars *)SCRATCH_PHYSICAL_ADDRESS; + unsigned int i, nr_bars = 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 < 128; 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 ( (vendor_id == 0x1234) && (device_id == 0x1111) ) + virtual_vga = VGA_std; + if ( (vendor_id == 0x1013) && (device_id == 0xb8) ) + virtual_vga = VGA_cirrus; + 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, 0x22, 0x0000); + pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */ + pci_writew(devfn, 0x3d, 0x0001); + 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_reg = PCI_BASE_ADDRESS_0 + 4*bar; + if ( bar == 6 ) + bar_reg = PCI_ROM_ADDRESS; + + bar_data = pci_readl(devfn, bar_reg); + pci_writel(devfn, bar_reg, ~0); + bar_sz = pci_readl(devfn, bar_reg); + pci_writel(devfn, bar_reg, bar_data); + if ( bar_sz == 0 ) + continue; + + bar_sz &= (((bar_data & PCI_BASE_ADDRESS_SPACE) == + PCI_BASE_ADDRESS_SPACE_MEMORY) ? + PCI_BASE_ADDRESS_MEM_MASK : + (PCI_BASE_ADDRESS_IO_MASK & 0xffff)); + bar_sz &= ~(bar_sz - 1); + + 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].devfn = devfn; + bars[i].bar_reg = bar_reg; + bars[i].bar_sz = bar_sz; + + nr_bars++; + + /* Skip the upper-half of the address for a 64-bit BAR. */ + if ( (bar_data & (PCI_BASE_ADDRESS_SPACE | + PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == + (PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64) ) + 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); + } + } + + /* 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; + + bar_data = pci_readl(devfn, bar_reg); + + if ( (bar_data & PCI_BASE_ADDRESS_SPACE) == + PCI_BASE_ADDRESS_SPACE_MEMORY ) + { + resource = &mem_resource; + bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK; + } + else + { + resource = &io_resource; + bar_data &= ~PCI_BASE_ADDRESS_IO_MASK; + } + + base = (resource->base + bar_sz - 1) & ~(bar_sz - 1); + bar_data |= base; + base += bar_sz; + + if ( (base < resource->base) || (base > resource->max) ) + { + printf("pci dev %02x:%x bar %02x size %08x: no space for " + "resource!\n", devfn>>3, devfn&7, bar_reg, bar_sz); + continue; + } + + resource->base = base; + + pci_writel(devfn, bar_reg, bar_data); + printf("pci dev %02x:%x bar %02x size %08x: %08x\n", + devfn>>3, devfn&7, bar_reg, bar_sz, 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); + } +} + +/* + * 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. + */ +static int scan_etherboot_nic(void *copy_rom_dest) +{ + struct option_rom_header *rom; + struct option_rom_pnp_header *pnph; + struct option_rom_pci_header *pcih; + uint32_t devfn; + uint16_t class, vendor_id, device_id; + uint8_t csum; + int i; + + for ( devfn = 0; devfn < 128; 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; + + /* We're only interested in NICs. */ + if ( class != 0x0200 ) + continue; + + rom = (struct option_rom_header *)etherboot; + 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 PXE 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); + memcpy(copy_rom_dest, rom, rom->rom_size * 512); + return rom->rom_size * 512; +} + +/* Replace possibly erroneous memory-size CMOS fields with correct values. */ +static void cmos_write_memory_size(void) +{ + struct e820entry *map = HVM_E820; + int i, nr = *HVM_E820_NR; + uint32_t base_mem = 640, ext_mem = 0, alt_mem = 0; + + for ( i = 0; i < nr; i++ ) + if ( (map[i].addr >= 0x100000) && (map[i].type == E820_RAM) ) + break; + + if ( i != nr ) + { + alt_mem = ext_mem = map[i].addr + map[i].size; + 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)); +} + +static uint16_t init_xen_platform_io_base(void) +{ + struct bios_info *bios_info = (struct bios_info *)ACPI_PHYSICAL_ADDRESS; + uint32_t devfn, bar_data; + uint16_t vendor_id, device_id; + + bios_info->xen_pfiob = 0; + + for ( devfn = 0; devfn < 128; devfn++ ) + { + vendor_id = pci_readw(devfn, PCI_VENDOR_ID); + device_id = pci_readw(devfn, PCI_DEVICE_ID); + if ( (vendor_id != 0x5853) || (device_id != 0x0001) ) + continue; + bar_data = pci_readl(devfn, PCI_BASE_ADDRESS_0); + bios_info->xen_pfiob = bar_data & PCI_BASE_ADDRESS_IO_MASK; + } + + return bios_info->xen_pfiob; +} + +int main(void) +{ + int vgabios_sz = 0, etherboot_sz = 0, rombios_sz, smbios_sz; + uint32_t vga_ram = 0; + uint16_t xen_pfiob; + + printf("HVM Loader\n"); + + init_hypercalls(); + + printf("CPU speed is %u MHz\n", get_cpu_mhz()); + + smp_initialise(); + + perform_tests(); + + printf("Writing SMBIOS tables ...\n"); + smbios_sz = hvm_write_smbios_tables(); + + printf("Loading ROMBIOS ...\n"); + rombios_sz = sizeof(rombios); + if ( rombios_sz > 0x10000 ) + rombios_sz = 0x10000; + memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, rombios_sz); + highbios_setup(); + + apic_setup(); + pci_setup(); + + if ( (get_vcpu_nr() > 1) || get_apic_mode() ) + create_mp_tables(); + + switch ( virtual_vga ) + { + case VGA_cirrus: + printf("Loading Cirrus VGABIOS ...\n"); + memcpy((void *)VGABIOS_PHYSICAL_ADDRESS, + vgabios_cirrusvga, sizeof(vgabios_cirrusvga)); + vgabios_sz = sizeof(vgabios_cirrusvga); + break; + case VGA_std: + printf("Loading Standard VGABIOS ...\n"); + memcpy((void *)VGABIOS_PHYSICAL_ADDRESS, + vgabios_stdvga, sizeof(vgabios_stdvga)); + vgabios_sz = sizeof(vgabios_stdvga); + break; + default: + printf("No emulated VGA adaptor ...\n"); + break; + } + + if ( virtual_vga != VGA_none ) + { + vga_ram = e820_malloc(8 << 20, 4096); + printf("VGA RAM at %08x\n", vga_ram); + } + + etherboot_sz = scan_etherboot_nic((void*)ETHERBOOT_PHYSICAL_ADDRESS); + + if ( get_acpi_enabled() ) + { + printf("Loading ACPI ...\n"); + acpi_build_tables(); + } + + cmos_write_memory_size(); + + printf("BIOS map:\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_PHYSICAL_ADDRESS, + ETHERBOOT_PHYSICAL_ADDRESS + etherboot_sz - 1); + if ( smbios_sz ) + printf(" %05x-%05x: SMBIOS tables\n", + SMBIOS_PHYSICAL_ADDRESS, + SMBIOS_PHYSICAL_ADDRESS + smbios_sz - 1); + if ( rombios_sz ) + printf(" %05x-%05x: Main BIOS\n", + ROMBIOS_PHYSICAL_ADDRESS, + ROMBIOS_PHYSICAL_ADDRESS + rombios_sz - 1); + + xen_pfiob = init_xen_platform_io_base(); + if ( xen_pfiob && vga_ram ) + outl(xen_pfiob + 4, vga_ram); + + printf("Invoking ROMBIOS ...\n"); + return 0; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/firmware/hvmloader/hypercall.h b/tools/firmware/hvmloader/hypercall.h new file mode 100644 index 0000000..4bd2707 --- /dev/null +++ b/tools/firmware/hvmloader/hypercall.h @@ -0,0 +1,183 @@ +/****************************************************************************** + * 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 __STR(...) #__VA_ARGS__ +#define STR(...) __STR(__VA_ARGS__) + +/* + * NB. Hypercall address needs to be relative to a linkage symbol for + * some version of ld to relocate the relative calls properly. + */ +#define hypercall_pa "_start - " STR(HYPERCALL_PHYSICAL_ADDRESS) + +#define _hypercall0(type, name) \ +({ \ + long __res; \ + asm volatile ( \ + "call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32) \ + : "=a" (__res) \ + : \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + long __res, __ign1; \ + asm volatile ( \ + "call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32) \ + : "=a" (__res), "=b" (__ign1) \ + : "1" ((long)(a1)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32) \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ + : "1" ((long)(a1)), "2" ((long)(a2)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32) \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3) \ + : "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 "hypercall_pa" + " STR(__HYPERVISOR_##name * 32) \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4) \ + : "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 "hypercall_pa" + " STR(__HYPERVISOR_##name * 32) \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ + : "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__ */ diff --git a/tools/firmware/hvmloader/mkhex b/tools/firmware/hvmloader/mkhex new file mode 100755 index 0000000..4517e36 --- /dev/null +++ b/tools/firmware/hvmloader/mkhex @@ -0,0 +1,26 @@ +#!/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, write to the Free Software Foundation, Inc., 59 Temple +# Place - Suite 330, Boston, MA 02111-1307 USA. +# + +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 "};" + diff --git a/tools/firmware/hvmloader/mp_tables.c b/tools/firmware/hvmloader/mp_tables.c new file mode 100644 index 0000000..2c42f6e --- /dev/null +++ b/tools/firmware/hvmloader/mp_tables.c @@ -0,0 +1,365 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#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 = get_vcpu_nr(); + + /* 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; +} + + +/* fills in an IO interrupt entry for IOAPIC 'ioapic_id' */ +static void fill_mp_io_intr_entry( + struct mp_io_intr_entry *mpiie, + int src_bus_id, int src_bus_irq, int ioapic_id, int dst_ioapic_intin) +{ + mpiie->type = ENTRY_TYPE_IO_INTR; + mpiie->intr_type = INTR_TYPE_INT; + mpiie->io_intr_flags = (PCI_ISA_IRQ_MASK & (1U<src_bus_id = src_bus_id; + mpiie->src_bus_irq = src_bus_irq; + mpiie->dst_ioapic_id = ioapic_id; + mpiie->dst_ioapic_intin = dst_ioapic_intin; +} + + +/* 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; +} + + +/* + * 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; +} + +/* create_mp_tables - creates MP tables for the guest based upon config data */ +void create_mp_tables(void) +{ + void *mp_table_base; + char *p; + int vcpu_nr, i, length; + + vcpu_nr = get_vcpu_nr(); + + printf("Creating MP tables ...\n"); + + /* Find the 'safe' place in ROMBIOS for the MP tables. */ + mp_table_base = get_mp_table_start(); + if ( mp_table_base == NULL ) + { + printf("Couldn't find start point for MP tables\n"); + return; + } + + p = mp_table_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); + + for ( i = 0; i < 16; i++ ) + { + if ( i == 2 ) continue; /* skip the slave PIC connection */ + fill_mp_io_intr_entry((struct mp_io_intr_entry *)p, + BUS_ID_ISA, i, IOAPIC_ID, (i == 0) ? 2 : i); + p += sizeof(struct mp_io_intr_entry); + } + + length = p - (char *)mp_table_base; + + /* find the next 16-byte boundary to place the mp floating pointer */ + while ( (unsigned long)p & 0xF ) + p++; + + fill_mpfps((struct mp_floating_pointer_struct *)p, + (uint32_t)mp_table_base); + + fill_mp_config_table((struct mp_config_table *)mp_table_base, length); + reset_bios_checksum(); +} diff --git a/tools/firmware/hvmloader/option_rom.h b/tools/firmware/hvmloader/option_rom.h new file mode 100644 index 0000000..a56030b --- /dev/null +++ b/tools/firmware/hvmloader/option_rom.h @@ -0,0 +1,50 @@ +#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)); + +#endif /* __HVMLOADER_OPTION_ROM_H__ */ diff --git a/tools/firmware/hvmloader/pci_regs.h b/tools/firmware/hvmloader/pci_regs.h new file mode 100644 index 0000000..4309345 --- /dev/null +++ b/tools/firmware/hvmloader/pci_regs.h @@ -0,0 +1,108 @@ +/* + * 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 */ + +#endif /* __HVMLOADER_PCI_REGS_H__ */ diff --git a/tools/firmware/hvmloader/smbios.c b/tools/firmware/hvmloader/smbios.c new file mode 100644 index 0000000..e146422 --- /dev/null +++ b/tools/firmware/hvmloader/smbios.c @@ -0,0 +1,604 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2006 + * + * Authors: Andrew D. Ball + */ + +#include +#include +#include +#include "smbios_types.h" +#include "util.h" +#include "hypercall.h" +#include "e820.h" + +static int +write_smbios_tables(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); + +static void +get_cpu_manufacturer(char *buf, int len); +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_3_init(void *start); +static void * +smbios_type_4_init(void *start, unsigned int cpu_number, + char *cpu_manufacturer); +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_32_init(void *start); +static void * +smbios_type_127_init(void *start); + +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 *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 cpu_num, nr_structs = 0, max_struct_size = 0; + char *p, *q; + char cpu_manufacturer[15]; + int i, nr_mem_devs; + + get_cpu_manufacturer(cpu_manufacturer, 15); + + p = (char *)start + sizeof(struct smbios_entry_point); + +#define do_struct(fn) do { \ + q = (fn); \ + 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_3_init(p)); + for ( cpu_num = 1; cpu_num <= vcpus; cpu_num++ ) + do_struct(smbios_type_4_init(p, cpu_num, cpu_manufacturer)); + + /* 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 = ((i == (nr_mem_devs - 1)) + ? (memsize & 0x3fff) : 0x4000); + 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_32_init(p)); + do_struct(smbios_type_127_init(p)); + +#undef do_struct + + smbios_entry_point_init( + start, max_struct_size, + (p - (char *)start) - sizeof(struct smbios_entry_point), + SMBIOS_PHYSICAL_ADDRESS + sizeof(struct smbios_entry_point), + nr_structs); + + return ((char *)p - (char *)start); +} + +/* Calculate how much pseudo-physical memory (in MB) is allocated to us. */ +static uint64_t +get_memsize(void) +{ + struct e820entry *map = HVM_E820; + uint8_t num_entries = *HVM_E820_NR; + uint64_t memsize = 0; + int i; + + /* + * Walk through e820map, ignoring any entries that aren't marked + * as usable or reserved. + */ + for ( i = 0; i < num_entries; i++ ) + { + if ( (map->type == E820_RAM) || (map->type == E820_RESERVED) ) + memsize += map->size; + map++; + } + + /* + * 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 (memsize + (1 << 20) - 1) >> 20; +} + +int +hvm_write_smbios_tables(void) +{ + 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 */ + + 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_PHYSICAL_ADDRESS is a safe large memory area for scratch. */ + len = write_smbios_tables((void *)SCRATCH_PHYSICAL_ADDRESS, + get_vcpu_nr(), get_memsize(), + uuid, xen_version_str, + xen_major_version, xen_minor_version); + if ( len > SMBIOS_MAXIMUM_SIZE ) + goto error_out; + /* Okay, not too large: copy out of scratch to final location. */ + memcpy((void *)SMBIOS_PHYSICAL_ADDRESS, + (void *)SCRATCH_PHYSICAL_ADDRESS, len); + + return len; + + error_out: + printf("Could not write SMBIOS tables, error in hvmloader.c:" + "hvm_write_smbios_tables()\n"); + return 0; +} + + +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 = (struct smbios_entry_point *)start; + + memset(ep, 0, sizeof(*ep)); + + strncpy(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; + strncpy(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__; + + memset(p, 0, sizeof(*p)); + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + 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); + strcpy((char *)start, "Xen"); + start += strlen("Xen") + 1; + strcpy((char *)start, xen_version); + start += strlen(xen_version) + 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; + + memset(p, 0, sizeof(*p)); + + p->header.type = 1; + p->header.length = sizeof(struct smbios_type_1); + p->header.handle = 0x100; + + 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); + + strcpy((char *)start, "Xen"); + start += strlen("Xen") + 1; + strcpy((char *)start, "HVM domU"); + start += strlen("HVM domU") + 1; + strcpy((char *)start, xen_version); + start += strlen(xen_version) + 1; + uuid_to_string(uuid_str, uuid); + strcpy((char *)start, uuid_str); + start += strlen(uuid_str) + 1; + *((uint8_t *)start) = 0; + + return start+1; +} + +/* Type 3 -- System Enclosure */ +static void * +smbios_type_3_init(void *start) +{ + struct smbios_type_3 *p = (struct smbios_type_3 *)start; + + memset(p, 0, sizeof(*p)); + + p->header.type = 3; + p->header.length = sizeof(struct smbios_type_3); + p->header.handle = 0x300; + + 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); + + strcpy((char *)start, "Xen"); + start += strlen("Xen") + 1; + *((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 = 0x400 + 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 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 = 0x1000; + p->header.length = sizeof(struct smbios_type_16); + + p->location = 0x01; /* other */ + p->use = 0x03; /* system memory */ + p->error_correction = 0x01; /* other */ + 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 = 0x1100 + 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 = 0x1300 + 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 = 0x1400 + 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 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 = 0x2000; + 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 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 = 0x7f00; + + start += sizeof(struct smbios_type_127); + *((uint16_t *)start) = 0; + return start + 2; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/firmware/hvmloader/smbios_types.h b/tools/firmware/hvmloader/smbios_types.h new file mode 100644 index 0000000..273db4a --- /dev/null +++ b/tools/firmware/hvmloader/smbios_types.h @@ -0,0 +1,182 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * 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 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 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 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 127 -- End-of-table */ +struct smbios_type_127 { + struct smbios_structure_header header; +} __attribute__ ((packed)); + +#endif /* SMBIOS_TYPES_H */ diff --git a/tools/firmware/hvmloader/smp.c b/tools/firmware/hvmloader/smp.c new file mode 100644 index 0000000..f64f73e --- /dev/null +++ b/tools/firmware/hvmloader/smp.c @@ -0,0 +1,132 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#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 = get_vcpu_nr(); + + 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); +} diff --git a/tools/firmware/hvmloader/tests.c b/tools/firmware/hvmloader/tests.c new file mode 100644 index 0000000..d3ee9cf --- /dev/null +++ b/tools/firmware/hvmloader/tests.c @@ -0,0 +1,164 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "util.h" + +/* + * Memory layout during tests: + * 4MB to 8MB is cleared. + * Page directory resides at 8MB. + * 4 page table pages reside at 8MB+4kB to 8MB+20kB. + * Pagetables identity-map 0-16MB, except 4kB at va 6MB maps to pa 5MB. + */ +#define PD_START (8ul << 20) +#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-16MB. */ + for ( i = 0; i < 4; i++ ) + pd[i] = (unsigned long)pt + (i<<12) + 3; + for ( i = 0; i < (4*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 = 1; + + 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 *)0x400000ul; p < (uint32_t *)0x700000ul; 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 = 0; + } + } + + return okay; +} + +void perform_tests(void) +{ + int i, passed; + + static struct { + int (* const test)(void); + const char *description; + } tests[] = { + { rep_io_test, "REP INSB across page boundaries" }, + { NULL, NULL } + }; + + printf("Testing HVM environment:\n"); + + passed = 0; + for ( i = 0; tests[i].test; i++ ) + { + printf(" - %s ... ", tests[i].description); + memset((char *)(4ul << 20), 0, 4ul << 20); + setup_paging(); + if ( (*tests[i].test)() ) + { + printf("passed\n"); + passed++; + } + else + { + printf("failed\n"); + } + } + + printf("Passed %d/%d tests\n", passed, i); + BUG_ON(passed != i); +} diff --git a/tools/firmware/hvmloader/util.c b/tools/firmware/hvmloader/util.c new file mode 100644 index 0000000..fe33b0f --- /dev/null +++ b/tools/firmware/hvmloader/util.c @@ -0,0 +1,658 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "util.h" +#include "config.h" +#include "e820.h" +#include "hypercall.h" +#include +#include +#include +#include + +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; +} + +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 void e820_collapse(void) +{ + int i = 0; + struct e820entry *ent = (struct e820entry *)HVM_E820; + + while ( i < (*HVM_E820_NR-1) ) + { + if ( (ent[i].type == ent[i+1].type) && + ((ent[i].addr + ent[i].size) == ent[i+1].addr) ) + { + ent[i].size += ent[i+1].size; + memcpy(&ent[i+1], &ent[i+2], (*HVM_E820_NR-i-2) * sizeof(*ent)); + (*HVM_E820_NR)--; + } + else + { + i++; + } + } +} + +uint32_t e820_malloc(uint32_t size, uint32_t align) +{ + uint32_t addr; + int i; + struct e820entry *ent = (struct e820entry *)HVM_E820; + + /* Align to at leats one kilobyte. */ + if ( align < 1024 ) + align = 1024; + + for ( i = *HVM_E820_NR - 1; i >= 0; i-- ) + { + addr = (ent[i].addr + ent[i].size - size) & ~(align-1); + if ( (ent[i].type != E820_RAM) || /* not ram? */ + (addr < ent[i].addr) || /* too small or starts above 4gb? */ + ((addr + size) < addr) ) /* ends above 4gb? */ + continue; + + if ( addr != ent[i].addr ) + { + memmove(&ent[i+1], &ent[i], (*HVM_E820_NR-i) * sizeof(*ent)); + (*HVM_E820_NR)++; + ent[i].size = addr - ent[i].addr; + ent[i+1].addr = addr; + ent[i+1].size -= ent[i].size; + i++; + } + + ent[i].type = E820_RESERVED; + + e820_collapse(); + + return addr; + } + + return 0; +} + +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, 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; +} + +int vprintf(const char *fmt, va_list ap) +{ + _doprint(putchar, fmt, ap); + return 0; +} + +void __assert_failed(char *assertion, char *file, int line) +{ + printf("HVMLoader assertion '%s' failed at %s:%d\n", + assertion, file, line); + for ( ; ; ) + asm volatile ( "ud2" ); +} + +void __bug(char *file, int line) +{ + printf("HVMLoader bug at %s:%d\n", file, line); + for ( ; ; ) + asm volatile ( "ud2" ); +} + +static int validate_hvm_info(struct hvm_info_table *t) +{ + char signature[] = "HVM INFO"; + uint8_t *ptr = (uint8_t *)t; + uint8_t sum = 0; + int i; + + /* strncmp(t->signature, "HVM INFO", 8) */ + for ( i = 0; i < 8; i++ ) + { + if ( signature[i] != t->signature[i] ) + { + printf("Bad hvm info signature\n"); + return 0; + } + } + + for ( i = 0; i < t->length; i++ ) + sum += ptr[i]; + + return (sum == 0); +} + +static 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; + + if ( !validate_hvm_info(t) ) + { + printf("Bad hvm info table\n"); + return NULL; + } + + table = t; + + return table; +} + +int get_vcpu_nr(void) +{ + struct hvm_info_table *t = get_hvm_info_table(); + return (t ? t->nr_vcpus : 1); +} + +int get_acpi_enabled(void) +{ + struct hvm_info_table *t = get_hvm_info_table(); + return (t ? t->acpi_enabled : 1); +} + +int get_apic_mode(void) +{ + struct hvm_info_table *t = get_hvm_info_table(); + return (t ? t->apic_mode : 1); +} + +uint16_t get_cpu_mhz(void) +{ + struct xen_add_to_physmap xatp; + struct shared_info *shared_info = (struct shared_info *)0xfffff000; + 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; + + /* Map shared-info page. */ + xatp.domid = DOMID_SELF; + xatp.space = XENMAPSPACE_shared_info; + xatp.idx = 0; + xatp.gpfn = (unsigned long)shared_info >> 12; + if ( hypercall_memory_op(XENMEM_add_to_physmap, &xatp) != 0 ) + BUG(); + + /* 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; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/firmware/hvmloader/util.h b/tools/firmware/hvmloader/util.h new file mode 100644 index 0000000..81f0e4f --- /dev/null +++ b/tools/firmware/hvmloader/util.h @@ -0,0 +1,156 @@ +#ifndef __HVMLOADER_UTIL_H__ +#define __HVMLOADER_UTIL_H__ + +#include +#include + +#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)])) + +/* 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 CPU speed in MHz. */ +uint16_t get_cpu_mhz(void); + +/* 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. */ +int get_vcpu_nr(void); +int get_acpi_enabled(void); +int get_apic_mode(void); + +/* 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); +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 */ +int printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +int vprintf(const char *fmt, va_list ap); + +/* Reserve a RAM region in the e820 table. */ +uint32_t e820_malloc(uint32_t size, uint32_t align); + +/* Prepare the 32bit BIOS */ +void highbios_setup(void); + +/* Miscellaneous. */ +void cacheattr_init(void); +void create_mp_tables(void); +int hvm_write_smbios_tables(void); +void smp_initialise(void); + +#ifndef NDEBUG +void perform_tests(void); +#else +#define perform_tests() ((void)0) +#endif + +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +extern char _start[], _end[]; + +#endif /* __HVMLOADER_UTIL_H__ */ diff --git a/tools/firmware/rombios/32bit/32bitbios.c b/tools/firmware/rombios/32bit/32bitbios.c new file mode 100644 index 0000000..551a9ff --- /dev/null +++ b/tools/firmware/rombios/32bit/32bitbios.c @@ -0,0 +1,53 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ +#include "rombios_compat.h" +#include "32bitprotos.h" + +/* + the jumptable that will be copied into the rombios in the 0xf000 segment + for every function that is to be called from the lower BIOS, make an entry + here. + */ +#define TABLE_ENTRY(idx, func) [idx] = (uint32_t)func +uint32_t jumptable[IDX_LAST+1] __attribute__((section (".biosjumptable"))) = +{ + TABLE_ENTRY(IDX_TCPA_ACPI_INIT, tcpa_acpi_init), + TABLE_ENTRY(IDX_TCPA_EXTEND_ACPI_LOG, tcpa_extend_acpi_log), + + TABLE_ENTRY(IDX_TCGINTERRUPTHANDLER, TCGInterruptHandler), + + TABLE_ENTRY(IDX_TCPA_CALLING_INT19H, tcpa_calling_int19h), + TABLE_ENTRY(IDX_TCPA_RETURNED_INT19H, tcpa_returned_int19h), + TABLE_ENTRY(IDX_TCPA_ADD_EVENT_SEPARATORS, tcpa_add_event_separators), + TABLE_ENTRY(IDX_TCPA_WAKE_EVENT, tcpa_wake_event), + TABLE_ENTRY(IDX_TCPA_ADD_BOOTDEVICE, tcpa_add_bootdevice), + TABLE_ENTRY(IDX_TCPA_START_OPTION_ROM_SCAN, tcpa_start_option_rom_scan), + TABLE_ENTRY(IDX_TCPA_OPTION_ROM, tcpa_option_rom), + TABLE_ENTRY(IDX_TCPA_IPL, tcpa_ipl), + TABLE_ENTRY(IDX_TCPA_MEASURE_POST, tcpa_measure_post), + + TABLE_ENTRY(IDX_TCPA_INITIALIZE_TPM, tcpa_initialize_tpm), + + TABLE_ENTRY(IDX_GET_S3_WAKING_VECTOR, get_s3_waking_vector), + + TABLE_ENTRY(IDX_LAST , 0) /* keep last */ +}; diff --git a/tools/firmware/rombios/32bit/Makefile b/tools/firmware/rombios/32bit/Makefile new file mode 100644 index 0000000..cdad756 --- /dev/null +++ b/tools/firmware/rombios/32bit/Makefile @@ -0,0 +1,29 @@ +XEN_ROOT = ../../../.. +include $(XEN_ROOT)/tools/firmware/Rules.mk + +SOURCES = util.c +TARGET = 32bitbios_flat.h + +CFLAGS += $(CFLAGS_include) -I.. -DGCC_PROTOS + +SUBDIRS = tcgbios + +MODULES = tcgbios/tcgbiosext.o + +.PHONY: all +all: subdirs-all + $(MAKE) $(TARGET) + +.PHONY: clean +clean: subdirs-clean + rm -rf *.o $(TARGET) + +$(TARGET): 32bitbios.o $(MODULES) util.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; \ + } || : + sh mkhex highbios_array 32bitbios_all.o > $@ diff --git a/tools/firmware/rombios/32bit/mkhex b/tools/firmware/rombios/32bit/mkhex new file mode 100644 index 0000000..4517e36 --- /dev/null +++ b/tools/firmware/rombios/32bit/mkhex @@ -0,0 +1,26 @@ +#!/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, write to the Free Software Foundation, Inc., 59 Temple +# Place - Suite 330, Boston, MA 02111-1307 USA. +# + +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 "};" + diff --git a/tools/firmware/rombios/32bit/rombios_compat.h b/tools/firmware/rombios/32bit/rombios_compat.h new file mode 100644 index 0000000..2198645 --- /dev/null +++ b/tools/firmware/rombios/32bit/rombios_compat.h @@ -0,0 +1,92 @@ +#ifndef ROMBIOS_COMPAT +#define ROMBIOS_COMPAT + +/* + * Compatibility functions and structures for transitioning between + * 16 bit Bochs BIOS and 32 bit BIOS code. + */ +#include + +#define ADDR_FROM_SEG_OFF(seg, off) (void *)((((uint32_t)(seg)) << 4) + (off)) + +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; +} + +#endif diff --git a/tools/firmware/rombios/32bit/tcgbios/Makefile b/tools/firmware/rombios/32bit/tcgbios/Makefile new file mode 100644 index 0000000..1b3cf2b --- /dev/null +++ b/tools/firmware/rombios/32bit/tcgbios/Makefile @@ -0,0 +1,18 @@ +XEN_ROOT = ../../../../.. +include $(XEN_ROOT)/tools/firmware/Rules.mk + +TARGET = tcgbiosext.o +FILES = tcgbios tpm_drivers +OBJECTS = $(foreach f,$(FILES),$(f).o) + +CFLAGS += $(CFLAGS_include) -I.. -I../.. -DGCC_PROTOS + +.PHONY: all clean + +all: $(TARGET) + +clean: + rm -rf *.o $(TARGET) + +$(TARGET): $(OBJECTS) + $(LD) $(LDFLAGS_DIRECT) -r $^ -o $@ diff --git a/tools/firmware/rombios/32bit/tcgbios/tcgbios.c b/tools/firmware/rombios/32bit/tcgbios/tcgbios.c new file mode 100644 index 0000000..b06af22 --- /dev/null +++ b/tools/firmware/rombios/32bit/tcgbios/tcgbios.c @@ -0,0 +1,1514 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ +#include "rombios_compat.h" +#include "tpm_drivers.h" + +#include "util.h" +#include "tcgbios.h" +#include "32bitprotos.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; +} + + +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()); +} + + +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; + 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 uint16_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 the boot device to the measurement log + */ +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; + 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; + 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; +} diff --git a/tools/firmware/rombios/32bit/tcgbios/tcgbios.h b/tools/firmware/rombios/32bit/tcgbios/tcgbios.h new file mode 100644 index 0000000..f16b586 --- /dev/null +++ b/tools/firmware/rombios/32bit/tcgbios/tcgbios.h @@ -0,0 +1,249 @@ +#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 diff --git a/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.c b/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.c new file mode 100644 index 0000000..d45f9b0 --- /dev/null +++ b/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.c @@ -0,0 +1,196 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 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, + }, +}; diff --git a/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.h b/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.h new file mode 100644 index 0000000..7ce5493 --- /dev/null +++ b/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.h @@ -0,0 +1,18 @@ +#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 diff --git a/tools/firmware/rombios/32bit/util.c b/tools/firmware/rombios/32bit/util.c new file mode 100644 index 0000000..a47bb71 --- /dev/null +++ b/tools/firmware/rombios/32bit/util.c @@ -0,0 +1,466 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ +#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_20_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_20_fadt *)(long)xsdt->entry[0]; + if (!fadt || (fadt->header.signature != ACPI_2_0_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; +} diff --git a/tools/firmware/rombios/32bit/util.h b/tools/firmware/rombios/32bit/util.h new file mode 100644 index 0000000..e245be6 --- /dev/null +++ b/tools/firmware/rombios/32bit/util.h @@ -0,0 +1,46 @@ +#ifndef UTIL_H +#define UTIL_H + +#include "../hvmloader/acpi/acpi2_0.h" + +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 diff --git a/tools/firmware/rombios/32bitgateway.c b/tools/firmware/rombios/32bitgateway.c new file mode 100644 index 0000000..9592dfb --- /dev/null +++ b/tools/firmware/rombios/32bitgateway.c @@ -0,0 +1,422 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ + +/* + * 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. + */ + +#define SEGMENT_OFFSET 0xf0000 +#define REAL_MODE_CODE_SEGMENT 0xf000 + +#define START_PM_CODE USE32 +#define END_PM_CODE USE16 + +/* definition of used code/data segment descriptors */ +#define PM_NORMAL_CS (gdt_entry_pm_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) + + ASM_START + + ; Switch into protected mode to allow access to 32 bit addresses. + ; This function allows switching into protected mode. + ; (the specs says big real mode, but that will not work) + ; + ; preserves all registers and prepares cs, ds, es, ss for usage + ; in protected mode; while in prot.mode interrupts remain disabled +switch_to_protmode: + cli + + ; have to fix the stack for proper return address in 32 bit mode + push WORD #(REAL_MODE_CODE_SEGMENT>>12) ;extended return address + push bp ;pop@A1 + mov bp, sp + push eax ;pop@A2 + mov eax, 2[bp] ; fix return address + rol eax, #16 + mov 2[bp], eax + + mov eax, esp + ror eax, #16 ; hi(esp) + + push bx ; preserve before function call + push cx + push dx + + push ax ; prepare stack for + push es ; call + push ds + push cs + push ss + call _store_segment_registers + add sp, #10 ; pop ax,es-ss + + pop dx ; restore after function call + pop cx + pop bx + + ; calculate protected-mode esp from ss:sp + and esp, #0xffff + xor eax, eax + mov ax, ss + rol eax, #4 + add eax, esp + mov esp, eax + + seg cs + lgdt my_gdtdesc ; switch to own table + + mov eax, cr0 + or al, #0x1 ; protected mode 'on' + mov cr0, eax + + jmpf DWORD (SEGMENT_OFFSET | switch_to_protmode_goon_1), #PM_NORMAL_CS + + START_PM_CODE + +switch_to_protmode_goon_1: + mov ax, #PM_32BIT_DS ; 32 bit segment that allows + mov ds, ax ; to reach all 32 bit + mov es, ax ; addresses + mov ss, ax + + pop eax ;@A2 + pop bp ;@A1 + ret + + END_PM_CODE + + + + .align 16 +gdt_base: + ; see Intel SW Dev. Manuals section 3.4.5, Volume 3 for meaning of bits + .word 0,0 + .byte 0,0,0,0 + +gdt_entry_pm_cs: + ; 32 bit code segment for protected mode + .word 0xffff, 0x0000 + .byte 0x00, 0x9a, 0xcf, 0x00 + +gdt_entry_pm_16bit_cs: + ; temp. 16 bit code segment used while in protected mode + .word 0xffff, 0x0000 + .byte SEGMENT_OFFSET >> 16, 0x9a, 0x0, 0x0 + +gdt_entry_pm_32bit_ds: + ; (32 bit) data segment (r/w) reaching all possible areas in 32bit memory + ; 4kb granularity + .word 0xffff, 0x0000 + .byte 0x0, 0x92, 0xcf, 0x0 +gdt_entry_end: + +my_gdtdesc: + .word (gdt_entry_end - gdt_base) - 1 + .long gdt_base | SEGMENT_OFFSET + + +realmode_gdtdesc: ;to be used in real mode + .word 0xffff + .long 0x0 + + + +switch_to_realmode: + ; Implementation of switching from protected mode to real mode + ; prepares cs, es, ds, ss to be used in real mode + ; spills eax + START_PM_CODE + + ; need to fix up the stack to return in 16 bit mode + ; currently the 32 bit return address is on the stack + pop eax + push ax + + push bx ;pop@1 + push si ;pop@2 + + call _ebda_ss_offset32 ; get the offset of the ss + mov bx, ax ; entry within the ebda. + + jmpf switch_to_realmode_goon_1, #PM_16BIT_CS + + END_PM_CODE + +switch_to_realmode_goon_1: + mov eax, cr0 + and al, #0xfe ; protected mode 'off' + mov cr0, eax + + jmpf switch_to_realmode_goon_2, #REAL_MODE_CODE_SEGMENT + +switch_to_realmode_goon_2: + + ; get orig. 'ss' without using the stack (no 'call'!) + xor eax, eax ; clear upper 16 bits (and lower) + mov ax, #0x40 ; where is the ebda located? + mov ds, ax + mov si, #0xe + seg ds + mov ax, [si] ; ax = segment of ebda + + mov ds, ax ; segment of ebda + seg ds + mov ax, [bx] ; stack segment - bx has been set above + mov ss, ax + + ; from esp and ss calculate real-mode sp + rol eax, #4 + sub esp, eax + + push dx ;preserve before call(s) + push cx + push bx + + call _get_register_ds ; get orig. 'ds' + mov ds, ax + call _get_register_es ; get orig. 'es' + mov es, ax + call _get_register_esp_hi ; fix the upper 16 bits of esp + ror esp, #16 + mov sp, ax + rol esp, #16 + + pop bx + pop cx + pop dx + + seg cs + lgdt realmode_gdtdesc + + sti ; allow interrupts + + pop si ;@2 + pop bx ;@1 + + ret + + ASM_END + +/* + * Helper function to get the offset of the reg_ss within the ebda struct + * Only 'C' can tell the offset. + */ +Bit16u +ebda_ss_offset32() +{ + ASM_START + START_PM_CODE // need to have this + ASM_END // compiled for protected mode + return &EbdaData->upcall.reg_ss; // 'C' knows the offset! + ASM_START + END_PM_CODE + ASM_END +} + +/* + * Two often-used functions + */ +Bit16u +read_word_from_ebda(offset) + Bit16u offset; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + return read_word(ebda_seg, offset); +} + +Bit32u +read_dword_from_ebda(offset) + Bit16u offset; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + return read_dword(ebda_seg, offset); +} + +/* + * Store registers in the EBDA; used to keep the registers' + * content in a well-defined place during protected mode execution + */ + void +store_segment_registers(ss, cs, ds, es, esp_hi) + Bit16u ss, cs, ds, es, esp_hi; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + write_word(ebda_seg, &EbdaData->upcall.reg_ss, ss); + write_word(ebda_seg, &EbdaData->upcall.reg_cs, cs); + write_word(ebda_seg, &EbdaData->upcall.reg_ds, ds); + write_word(ebda_seg, &EbdaData->upcall.reg_es, es); + write_word(ebda_seg, &EbdaData->upcall.esp_hi, esp_hi); +} + + + void +store_returnaddress(retaddr) + Bit16u retaddr; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + write_word(ebda_seg, &EbdaData->upcall.retaddr, retaddr); +} + +Bit16u +get_returnaddress() +{ + return read_word_from_ebda(&EbdaData->upcall.retaddr); +} + +/* + * get the segment register 'cs' value from the EBDA + */ +Bit16u +get_register_cs() +{ + return read_word_from_ebda(&EbdaData->upcall.reg_cs); +} + +/* + * get the segment register 'ds' value from the EBDA + */ +Bit16u +get_register_ds() +{ + return read_word_from_ebda(&EbdaData->upcall.reg_ds); +} + +/* + * get the segment register 'es' value from the EBDA + */ +Bit16u +get_register_es() +{ + return read_word_from_ebda(&EbdaData->upcall.reg_es); +} + +/* + * get the upper 16 bits of the esp from the EBDA + */ +Bit16u +get_register_esp_hi() +{ + return read_word_from_ebda(&EbdaData->upcall.esp_hi); +} + + + +/********************************************************/ + + +ASM_START + +Upcall: + ; do the upcall into 32 bit space + ; clear the stack frame so that 32 bit space sees all the parameters + ; on the stack as if they were prepared for it + ; ---> take the 16 bit return address off the stack and remember it + ; + ; Input: + ; bx: index of function to call + ; Ouput: + ; dx, ax: 32 bit result of call (even if 'void' is expected) + + push bp ;pop @1 + mov bp, sp + push si ;pop @2 + + mov ax, 2[bp] ; 16 bit return address + push ax + call _store_returnaddress ; store away + pop ax + + ; XXX GDT munging requires ROM to be writable! + call _enable_rom_write_access + + rol bx, #2 + mov si, #jmptable + seg cs + mov eax, dword ptr [si+bx] ; address to call from table + + pop si ;@2 + pop bp ;@1 + + add sp, #2 ; remove 16bit return address from stack + + call switch_to_protmode + START_PM_CODE + + call eax ; call 32bit function + push eax ; preserve result + + call switch_to_realmode ; back to realmode + END_PM_CODE + + pop eax ; get result + + push word 0x0000 ; placeholder for 16 bit return address + push bp + mov bp,sp + push eax ; preserve work register + + call _disable_rom_write_access + + call _get_returnaddress + mov 2[bp], ax ; 16bit return address onto stack + + pop eax + pop bp + + ror eax, #16 ; result into dx/ax + mov dx, ax ; hi(res) -> dx + ror eax, #16 + + ret + + +/* macro for functions to declare their call into 32bit space */ +MACRO DoUpcall + mov bx, #?1 + jmp Upcall +MEND + + +ASM_END + +#include "32bitprotos.h" +#include "32bitgateway.h" + +#include "tcgbios.c" + +Bit32u get_s3_waking_vector() +{ + ASM_START + DoUpcall(IDX_GET_S3_WAKING_VECTOR) + ASM_END +} diff --git a/tools/firmware/rombios/32bitgateway.h b/tools/firmware/rombios/32bitgateway.h new file mode 100644 index 0000000..fe638ef --- /dev/null +++ b/tools/firmware/rombios/32bitgateway.h @@ -0,0 +1,18 @@ +#ifndef GATEWAY +#define GATEWAY + +#include "32bitprotos.h" + +void test_gateway(); + +/* extension for the EBDA */ +typedef struct { + Bit16u reg_ss; + Bit16u reg_cs; + Bit16u reg_ds; + Bit16u reg_es; + Bit16u esp_hi; + Bit16u retaddr; +} upcall_t; + +#endif diff --git a/tools/firmware/rombios/32bitprotos.h b/tools/firmware/rombios/32bitprotos.h new file mode 100644 index 0000000..f0c4014 --- /dev/null +++ b/tools/firmware/rombios/32bitprotos.h @@ -0,0 +1,47 @@ +#ifndef PROTOS_HIGHBIOS +#define PROTOS_HIGHBIOS + +/* shared include file for bcc and gcc */ + +/* bcc does not like 'enum' */ +#define IDX_TCGINTERRUPTHANDLER 0 +#define IDX_TCPA_ACPI_INIT 1 +#define IDX_TCPA_EXTEND_ACPI_LOG 2 +#define IDX_TCPA_CALLING_INT19H 3 +#define IDX_TCPA_RETURNED_INT19H 4 +#define IDX_TCPA_ADD_EVENT_SEPARATORS 5 +#define IDX_TCPA_WAKE_EVENT 6 +#define IDX_TCPA_ADD_BOOTDEVICE 7 +#define IDX_TCPA_START_OPTION_ROM_SCAN 8 +#define IDX_TCPA_OPTION_ROM 9 +#define IDX_TCPA_IPL 10 +#define IDX_TCPA_INITIALIZE_TPM 11 +#define IDX_TCPA_MEASURE_POST 12 +#define IDX_GET_S3_WAKING_VECTOR 13 +#define IDX_LAST 14 /* keep last! */ + +#ifdef GCC_PROTOS + #define PARMS(x...) x +#else + /* bcc doesn't want any parameter types in prototypes */ + #define PARMS(x...) +#endif + +Bit32u TCGInterruptHandler( PARMS(pushad_regs_t *regs, Bit32u esds, Bit32u flags_ptr)); + +void tcpa_acpi_init( PARMS(void) ); +Bit32u tcpa_extend_acpi_log( PARMS(Bit32u entry_ptr) ); +void tcpa_calling_int19h( PARMS(void) ); +void tcpa_returned_int19h( PARMS(void) ); +void tcpa_add_event_separators( PARMS(void) ); +void tcpa_wake_event( PARMS(void) ); +void tcpa_add_bootdevice( PARMS(Bit32u bootcd, Bit32u bootdrv) ); +void tcpa_start_option_rom_scan( PARMS(void) ); +void tcpa_option_rom( PARMS(Bit32u seg) ); +void tcpa_ipl( PARMS(Bit32u bootcd,Bit32u seg,Bit32u off,Bit32u count) ); +void tcpa_measure_post( PARMS(Bit32u from, Bit32u to) ); +Bit32u tcpa_initialize_tpm( PARMS(Bit32u physpres) ); + +Bit32u get_s3_waking_vector( PARMS(void) ); + +#endif diff --git a/tools/firmware/rombios/Makefile b/tools/firmware/rombios/Makefile new file mode 100644 index 0000000..8ea8cb7 --- /dev/null +++ b/tools/firmware/rombios/Makefile @@ -0,0 +1,29 @@ +XEN_ROOT = ../../.. +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-* + +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 + diff --git a/tools/firmware/rombios/apmbios.S b/tools/firmware/rombios/apmbios.S new file mode 100644 index 0000000..a010949 --- /dev/null +++ b/tools/firmware/rombios/apmbios.S @@ -0,0 +1,367 @@ +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#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 diff --git a/tools/firmware/rombios/biossums.c b/tools/firmware/rombios/biossums.c new file mode 100644 index 0000000..be12e49 --- /dev/null +++ b/tools/firmware/rombios/biossums.c @@ -0,0 +1,478 @@ +/* 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; +} + diff --git a/tools/firmware/rombios/makesym.perl b/tools/firmware/rombios/makesym.perl new file mode 100755 index 0000000..df604e2 --- /dev/null +++ b/tools/firmware/rombios/makesym.perl @@ -0,0 +1,31 @@ +#!/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"; + } + } +} diff --git a/tools/firmware/rombios/rombios.c b/tools/firmware/rombios/rombios.c new file mode 100644 index 0000000..547d5cf --- /dev/null +++ b/tools/firmware/rombios/rombios.c @@ -0,0 +1,11241 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +// ROM BIOS for use with Bochs/Plex x86 emulation environment + +#define uint8_t unsigned char +#define uint16_t unsigned short +#define uint32_t unsigned long +#include "../hvmloader/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) + +#define DEBUG_ROMBIOS 0 + +#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_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 + + // 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) + + // Define the application NAME +#ifdef HVMASSIST +# define BX_APPNAME "HVMAssist" +#elif PLEX86 +# define BX_APPNAME "Plex86" +#else +# define BX_APPNAME "Bochs" +#endif + + // 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 + +#ifndef BX_SMP_PROCESSORS +#define BX_SMP_PROCESSORS 1 +# warning BX_SMP_PROCESSORS not defined, defaulting to 1 +#endif + +#define PANIC_PORT 0x400 +#define PANIC_PORT2 0x401 +#define INFO_PORT 0x402 +#define DEBUG_PORT 0x403 + +// #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 + 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 + } + + // 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 + 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 + } + +#if 0 + // 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 + cmp cx, #0x0000 + 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 + } +#endif + + // 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] + inc bx + inc bx + 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 + inc bx + inc bx + 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 + add 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 + add 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 + add eax, ebx + shr ebx, #16 + test eax, eax + ret + + ;; sr function + lsrul: + mov cx,di + jcxz lsr_exit + and eax, #0x0000FFFF + shl ebx, #16 + add 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 + add 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 + add 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 lba_capable; // LBA capable flag - always yes for bochs devices + Bit8u mode; // transfert 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; // Total sectors count + } 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 + +#include "32bitgateway.h" + + // for access to EBDA area + // The EBDA structure should conform to + // http://www.cybertrails.com/~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 0x9fc00, and the boot + // device tables are at 0x9ff00 -- 0x9ffff + 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 + + upcall_t upcall; + } 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; + + + +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 void copy_e820_table(); + +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_DS(); +//static void set_DS(); +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 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.138 $"; +static char bios_date_string[] = "$Date: 2005/05/07 15:55:26 $"; + +static char CVSID[] = "$Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $"; + +/* Offset to skip the CVS $Id: prefix */ +#define bios_version_string (CVSID + 4) + +#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) +#else +# define BX_DEBUG(format, p...) +#endif +#define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p) +#define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p) + +#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 }, /* ??? */ + { none, none, 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_DS() +//{ +//ASM_START +// mov ax, ds +//ASM_END +//} +// +// void +//set_DS(ds_selector) +// Bit16u ds_selector; +//{ +//ASM_START +// push bp +// mov bp, sp +// +// push ax +// mov ax, 4[bp] ; ds_selector +// mov ds, ax +// pop ax +// +// pop bp +//ASM_END +//} + + Bit16u +get_SS() +{ +ASM_START + mov ax, ss +ASM_END +} + +#ifdef HVMASSIST +void +copy_e820_table() +{ + Bit8u nr_entries = read_byte(0x9000, 0x1e8); + Bit32u base_mem; + if (nr_entries > 32) + nr_entries = 32; + write_word(0xe000, 0x8, nr_entries); + memcpyb(0xe000, 0x10, 0x9000, 0x2d0, nr_entries * 0x14); + /* 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. */ + base_mem = read_dword(0x9000, 0x2d0 + 8); + write_word(0x40, 0x13, base_mem >> 10); +} + +void +set_rom_write_access(action) + Bit16u action; +{ + Bit16u off = (Bit16u)&((struct bios_info *)0)->xen_pfiob; +ASM_START + mov si,.set_rom_write_access.off[bp] + push ds + mov ax,#(ACPI_PHYSICAL_ADDRESS >> 4) + mov ds,ax + mov dx,[si] + pop ds + mov ax,.set_rom_write_access.action[bp] + out dx,al +ASM_END +} + +void enable_rom_write_access() +{ + set_rom_write_access(0); +} + +void disable_rom_write_access() +{ + set_rom_write_access(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'); +} + +//-------------------------------------------------------------------------- +// bios_printf() +// A compact variable argument printf function which prints its output via +// an I/O port so that it can be logged by Bochs/Plex. +// Currently, only %x is supported (or %02x, %04x, etc). +// +// Supports %[format_width][format] +// where format can be d,x,c,s +//-------------------------------------------------------------------------- + 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, shift_count, format_width; + + 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') { + if (format_width == 0) + format_width = 4; + for (i=format_width-1; i>=0; i--) { + nibble = (arg >> (4 * i)) & 0x000f; + send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A')); + } + } + else if (c == 'u') { + put_uint(action, arg, format_width, 0); + } + 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') { + bios_printf(action & (~BIOS_PRINTF_HALT), 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=0x2000; + while (--max > 0) { + outb(0x80, 0x00); + if (inb(0x64) & 0x01) { + inb(0x60); + max = 0x2000; + } + } + + // 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); +} + +//-------------------------------------------------------------------------- +// print_bios_banner +// displays a the bios version +//-------------------------------------------------------------------------- +void +print_bios_banner() +{ + printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":""); + printf("%s %s\n", bios_cvs_version_string, bios_date_string); +#if BX_TCGBIOS + printf("TCG-enabled BIOS.\n"); +#endif + printf("\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 +//-------------------------------------------------------------------------- + +/* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */ +#define IPL_SEG 0x9ff0 +#define IPL_TABLE_OFFSET 0x0000 +#define IPL_TABLE_ENTRIES 8 +#define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */ +#define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */ + +struct ipl_entry { + Bit16u type; + Bit16u flags; + Bit32u vector; + Bit32u description; + Bit32u reserved; +}; + +static void +init_boot_vectors() +{ + struct ipl_entry e; + Bit16u count = 0; + Bit16u ss = get_SS(); + + /* Clear out the IPL table. */ + memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff); + + /* Floppy drive */ + e.type = 1; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; + memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); + count++; + + /* First HDD */ + e.type = 2; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; + memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); + count++; + +#if BX_ELTORITO_BOOT + /* CDROM */ + e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; + memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); + count++; +#endif + + /* Remember how many devices we have */ + write_word(IPL_SEG, IPL_COUNT_OFFSET, count); + /* Not tried booting anything yet */ + write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff); +} + +static Bit8u +get_boot_vector(i, e) +Bit16u i; struct ipl_entry *e; +{ + Bit16u count; + Bit16u ss = get_SS(); + /* Get the count of boot devices, and refuse to overrun the array */ + count = read_word(IPL_SEG, IPL_COUNT_OFFSET); + if (i >= count) return 0; + /* OK to read this device */ + memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e)); + return 1; +} + + +//-------------------------------------------------------------------------- +// print_boot_device +// displays the boot device +//-------------------------------------------------------------------------- + +static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"}; + +void +print_boot_device(type) + Bit16u type; +{ + /* NIC appears as type 0x80 */ + if (type == 0x80 ) type = 0x4; + if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n"); + printf("Booting from %s...\n", drivetypes[type]); +} + +//-------------------------------------------------------------------------- +// 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"); +} + +//-------------------------------------------------------------------------- +// 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; +} + +#define WAIT_HZ 18 +/** + * Check for keystroke. + * @returns True if keystroke available, False if not. + */ +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 +} + +/** + * Get keystroke. + * @returns BIOS scan code. + */ +Bit8u get_keystroke() +{ +ASM_START + mov ax, #0x0 + int #0x16 + xchg ah, al +ASM_END +} + +/** + * Waits (sleeps) for the given number of ticks. + * Checks for keystroke. + * + * @returns BIOS scan code if available, 0 if not. + * @param ticks Number of ticks to sleep. + * @param stop_on_key Whether to stop immediately upon keypress. + */ +Bit8u wait(ticks, stop_on_key) + Bit16u ticks; + Bit8u stop_on_key; +{ + long ticks_to_wait, delta; + Bit32u prev_ticks, t; + Bit8u scan_code = 0; + + /* + * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock. + * We also have to be careful about interrupt storms. + */ + ticks_to_wait = ticks; + prev_ticks = read_dword(0x0, 0x46c); + do + { + 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; + + if (check_for_keystroke()) + { + scan_code = get_keystroke(); + bios_printf(BIOS_PRINTF_DEBUG, "Key pressed: %x\n", scan_code); + if (stop_on_key) + return scan_code; + } + } while (ticks_to_wait > 0); + return scan_code; +} + +static void clearscreen() { + /* Hide cursor, clear screen and move cursor to starting position */ +ASM_START + push bx + push cx + push dx + + mov ax, #0x100 + mov cx, #0x1000 + int #0x10 + + mov ax, #0x700 + mov bh, #7 + xor cx, cx + mov dx, #0x184f + int #0x10 + + mov ax, #0x200 + xor bx, bx + xor dx, dx + int #0x10 + + pop dx + pop cx + pop bx +ASM_END +} + +int bootmenu(selected) + int selected; +{ + Bit8u scode; + int max; + + /* get the number of boot devices */ + max = read_word(IPL_SEG, IPL_COUNT_OFFSET); + + for(;;) { + if (selected > max || selected < 1) selected = 1; + clearscreen(); + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "\n\n\n\n\n\n\n"); + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " Select boot device\n\n"); + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 1. Floppy\n"); + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 2. Hard drive\n"); + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 3. CD-ROM\n"); + if (max == 4) + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, " 4. Network\n"); + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "\n\n Currently selected: %d\n", selected); + + do { + scode = wait(WAIT_HZ, 1); + } while (scode == 0); + switch(scode) { + case 0x02: + case 0x03: + case 0x04: + selected = scode - 1; + break; + case 0x05: + if (max == 4) + selected = scode -1 ; + else + scode = 0; + break; + case 0x48: + selected -= 1; + if (selected < 1) + selected = 1; + scode = 0; + break; + case 0x50: + selected += 1; + if (selected > max) + selected = max; + scode = 0; + break; + case 0x1c: + break; + default: + scode = 0; + break; + } + if (scode != 0) + break; + } + + switch (selected) { + case 1: + return 0x3D; + case 2: + return 0x3E; + case 3: + return 0x3F; + case 4: + return 0x58; + default: + return 0; + } +} + +void interactive_bootkey() +{ + Bit16u i; + Bit8u scan = 0; + + bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, + "\n\nPress F10 to select boot device.\n"); + + scan = wait(1, 0); + if (scan == 0x44) + scan = bootmenu(inb_cmos(0x3d) & 0x0f); + + /* set the default based on the keypress or menu */ + switch(scan) { + case 0x3D: + outb_cmos(0x3d, 0x01); + break; + case 0x3E: + outb_cmos(0x3d, 0x02); + break; + case 0x3F: + outb_cmos(0x3d, 0x03); + break; + case 0x58: + outb_cmos(0x3d, 0x04); + break; + default: + break; + } +} + + +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_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); +} + +void +s3_resume() +{ + Bit32u s3_wakeup_vector; + Bit16u s3_wakeup_ip, s3_wakeup_cs; + Bit8u cmos_shutdown_status; + +ASM_START + push ds + push ax + mov ax, #EBDA_SEG + mov ds, ax + mov al, [EBDA_CMOS_SHUTDOWN_STATUS_OFFSET] + mov .s3_resume.cmos_shutdown_status[bp], al + pop ax + pop ds +ASM_END + + if (cmos_shutdown_status != CMOS_SHUTDOWN_S3) + return; + + s3_wakeup_vector = get_s3_waking_vector(); + if (!s3_wakeup_vector) + return; + + s3_wakeup_ip = s3_wakeup_vector & 0xF; + s3_wakeup_cs = s3_wakeup_vector >> 4; + +ASM_START + push .s3_resume.s3_wakeup_cs[bp] + push .s3_resume.s3_wakeup_ip[bp] + retf +ASM_END +} + +#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 + +// 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_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,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); +} + +// --------------------------------------------------------------------------- +// 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); + } + } + } + + 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; + 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, 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 + + sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61 + + 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, sectors); + 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 /= 63; + heads = sectors / 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 / 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, 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); + sizeinmb >>= 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; + } + 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 (%04u MBytes)\n",version,(Bit16u)sizeinmb); + else + printf(" ATA-%d Hard-Disk (%04u 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; + 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 for BSY + max=0xff; + while(--max>0) { + Bit8u status = inb(iobase1+ATA_CB_STAT); + if ((status & ATA_CB_STAT_BSY) != 0) break; + } + +// 8.2.1 (f) -- clear SRST + outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); + + if (read_byte(ebda_seg,&EbdaData->ata.devices[device].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) ) { + +// 8.2.1 (h) -- wait for not BSY + max=0xff; + while(--max>0) { + Bit8u status = inb(iobase1+ATA_CB_STAT); + if ((status & ATA_CB_STAT_BSY) == 0) break; + } + } + } + +// 8.2.1 (i) -- wait for DRDY + max=0xfff; + while(--max>0) { + Bit8u status = inb(iobase1+ATA_CB_STAT); + if ((status & ATA_CB_STAT_RDY) != 0) break; + } + + // 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, segment, offset) +Bit16u device, command, count, cylinder, head, sector, segment, offset; +Bit32u lba; +{ + 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 + count >= 1UL << 28)) { + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); + outb(iobase1 + ATA_CB_SN, lba >> 24); + outb(iobase1 + ATA_CB_CL, 0); + outb(iobase1 + ATA_CB_CH, 0); + command |= 0x04; + count &= (1UL << 8) - 1; + lba &= (1UL << 24) - 1; + } + sector = (Bit16u) (lba & 0x000000ffL); + lba >>= 8; + cylinder = (Bit16u) (lba & 0x0000ffffL); + lba >>= 16; + head = ((Bit16u) (lba & 0x0000000fL)) | 0x40; + } + + 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); + + while (1) { + status = inb(iobase1 + ATA_CB_STAT); + if ( !(status & ATA_CB_STAT_BSY) ) break; + } + + 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 = inb(iobase1 + ATA_CB_STAT); + 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, segment, offset) +Bit16u device, command, count, cylinder, head, sector, segment, offset; +Bit32u lba; +{ + 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 + count >= 1UL << 28)) { + outb(iobase1 + ATA_CB_FR, 0x00); + outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); + outb(iobase1 + ATA_CB_SN, lba >> 24); + outb(iobase1 + ATA_CB_CL, 0); + outb(iobase1 + ATA_CB_CH, 0); + command |= 0x04; + count &= (1UL << 8) - 1; + lba &= (1UL << 24) - 1; + } + sector = (Bit16u) (lba & 0x000000ffL); + lba >>= 8; + cylinder = (Bit16u) (lba & 0x0000ffffL); + lba >>= 16; + head = ((Bit16u) (lba & 0x0000000fL)) | 0x40; + } + + 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); + + while (1) { + status = inb(iobase1 + ATA_CB_STAT); + if ( !(status & ATA_CB_STAT_BSY) ) break; + } + + 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 + while (1) { + status = inb(iobase1 + ATA_CB_STAT); + if ( !(status & ATA_CB_STAT_BSY) ) break; + } + + 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 = inb(iobase1 + ATA_CB_STAT); + } + else { + while (1) { + + status = inb(iobase1 + ATA_CB_STAT); + + // Check if command completed + if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break; + + if (status & ATA_CB_STAT_ERR) { + BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status); + return 3; + } + + // Device must be ready to send data + 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_packet : not ready (status %02x)\n", status); + return 4; + } + + // 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) + Bit16u device; +{ + Bit8u atacmd[12]; + Bit8u buffer[16]; + Bit8u i; + + memsetb(get_SS(),atacmd,0,12); + + // Request SENSE + atacmd[0]=0x03; + atacmd[4]=0x20; + if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0) + return 0x0002; + + if ((buffer[0] & 0x7e) == 0x70) { + return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12]; + } + + return 0; +} + + Bit16u +atapi_is_ready(device) + Bit16u device; +{ + Bit8u atacmd[12]; + Bit8u buffer[]; + + memsetb(get_SS(),atacmd,0,12); + + // Test Unit Ready + if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0) + return 0x000f; + + if (atapi_get_sense(device) !=0 ) { + memsetb(get_SS(),atacmd,0,12); + + // try to send Test Unit Ready again + if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0) + return 0x000f; + + return atapi_get_sense(device); + } + 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; + + // 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(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5; + } + for(i=0;i<23;i++) + if(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); + + // limit to 15M + if(regs.u.r16.ax > 0x3c00) + regs.u.r16.ax = 0x3c00; + + 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"); + // BX: + // 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 + CLEAR_CF(); + regs.u.r8.ah = 0; + 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 + + 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; + +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 + ;; Get the count in eax + mov ax, .int15_function32.CX [bp] + shl eax, #16 + mov ax, .int15_function32.DX [bp] + + ;; 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) + { + case 0x20: { + Bit16u e820_table_size = read_word(0xe000, 0x8) * 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, + 0xe000, 0x10 + 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) { + Bit32u base, type; + Bit16u off; + for (off = 0; off < e820_table_size; off += 0x14) { + base = read_dword(0xe000, 0x10 + off); + type = read_dword(0xe000, 0x20 + off); + if ((base >= 0x100000) && (type == 1)) + break; + } + if (off == e820_table_size) { + SET_CF(); + break; + } + memcpyb(ES, regs.u.r16.di, 0xe000, 0x10 + 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: { + Bit16u off, e820_table_size = read_word(0xe000, 0x8) * 0x14; + Bit32u base, type, size; + + // 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(0xe000, 0x10 + off); + type = read_dword(0xe000, 0x20 + 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(0xe000, 0x18 + 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; + } +} + + 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, count; + Bit16u kbd_code, max; + + BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX); + + 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); + 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, led_flags; + + // + // 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); + led_flags = read_byte(0x0040, 0x97); + 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); + led_flags ^= 0x04; + write_byte(0x0040, 0x97, led_flags); + break; + case 0xba: /* Caps Lock release */ + mf2_flags &= ~0x40; + write_byte(0x0040, 0x18, mf2_flags); + break; + + case 0x2a: /* L Shift press */ + /*shift_flags &= ~0x40;*/ + shift_flags |= 0x02; + write_byte(0x0040, 0x17, shift_flags); + led_flags &= ~0x04; + write_byte(0x0040, 0x97, led_flags); + break; + case 0xaa: /* L Shift release */ + shift_flags &= ~0x02; + write_byte(0x0040, 0x17, shift_flags); + break; + + case 0x36: /* R Shift press */ + /*shift_flags &= ~0x40;*/ + shift_flags |= 0x01; + write_byte(0x0040, 0x17, shift_flags); + led_flags &= ~0x04; + write_byte(0x0040, 0x97, led_flags); + break; + case 0xb6: /* R Shift release */ + shift_flags &= ~0x01; + write_byte(0x0040, 0x17, shift_flags); + break; + + case 0x1d: /* Ctrl press */ + shift_flags |= 0x04; + write_byte(0x0040, 0x17, shift_flags); + if (mf2_state & 0x01) { + mf2_flags |= 0x04; + } else { + mf2_flags |= 0x01; + } + write_byte(0x0040, 0x18, mf2_flags); + break; + case 0x9d: /* Ctrl release */ + shift_flags &= ~0x04; + write_byte(0x0040, 0x17, shift_flags); + if (mf2_state & 0x01) { + mf2_flags &= ~0x04; + } 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 & 0x01) { + mf2_flags |= 0x08; + } 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 & 0x01) { + mf2_flags &= ~0x08; + } else { + mf2_flags &= ~0x02; + } + write_byte(0x0040, 0x18, mf2_flags); + break; + + case 0x45: /* Num Lock press */ + if ((mf2_state & 0x01) == 0) { + mf2_flags |= 0x20; + write_byte(0x0040, 0x18, mf2_flags); + shift_flags ^= 0x20; + led_flags ^= 0x02; + write_byte(0x0040, 0x17, shift_flags); + write_byte(0x0040, 0x97, led_flags); + } + break; + case 0xc5: /* Num Lock release */ + if ((mf2_state & 0x01) == 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; + led_flags ^= 0x01; + write_byte(0x0040, 0x17, shift_flags); + write_byte(0x0040, 0x97, led_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) return; /* toss key releases ... */ + if (scancode > MAX_SCAN_CODE) { + BX_INFO("KBD: int09h_handler(): unknown scancode (%x) read!\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 (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; + } + mf2_state &= ~0x01; +} + + unsigned int +enqueue_key(scan_code, ascii_code) + Bit8u scan_code, ascii_code; +{ + Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail; + + //BX_INFO("KBD: enqueue_key() called scan:%02x, ascii:%02x\n", + // scan_code, ascii_code); + +#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 ) { + // BX_PANIC("int74_function:\n"); + 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(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; +{ + Bit32u lba; + 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) ) { + BX_INFO("int13_harddisk: function %02x, count 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 = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1; + 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, segment, offset); + else + status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, 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 physical geometry from table + 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); + + // Compute sector count seen by int13 + lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt; + CX = lba >> 16; + DX = lba & 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); + + // Can't use 64 bits lba + lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); + if (lba != 0L) { + BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH()); + goto int13_fail; + } + + // Get 32 bits lba and check + lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); + if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) { + 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, segment, offset); + else + status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, 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 = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors); + 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, 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); // FIXME should be Bit64 + write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L); + 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); + 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, (&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("int13h_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); + // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI); + + 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 15 + 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); + 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, (&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); + //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI); + + /* 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(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, 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); // HD0: 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 + + +////////////////////// +// FLOPPY functions // +////////////////////// + + 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, dor; + Bit16u curr_cyl_offset; + + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, val8); + + // turn on motor of selected drive, DMA & int enabled, normal operation + 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); + + // check port 3f4 for drive readiness + val8 = inb(0x3f4); + if ( (val8 & 0xf0) != 0x80 ) + BX_PANIC("floppy recal:f07: ctrl not ready\n"); + + // 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 + val8 = (read_byte(0x0000, 0x043e) & 0x80); + while ( val8 == 0 ) { + val8 = (read_byte(0x0000, 0x043e) & 0x80); + } + + 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(0x0000, 0x043e); + 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); +} + +#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 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); + // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI); + + 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) || + (num_sectors == 0) || (num_sectors > 72) ) { +BX_INFO("floppy: drive>1 || head>1 ...\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 + //-------------------------------------- + + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, val8); + + // turn on motor of selected drive, DMA & int enabled, normal operation + 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); + + // check port 3f4 for drive readiness + val8 = inb(0x3f4); + if ( (val8 & 0xf0) != 0x80 ) + BX_PANIC("int13_diskette:f02: ctrl not ready\n"); + + // 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, 0); // last sector number possible 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 + val8 = (read_byte(0x0000, 0x043e) & 0x80); + while ( val8 == 0 ) { + val8 = (read_byte(0x0000, 0x043e) & 0x80); + } + + 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(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, 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 + //-------------------------------------- + + // set 40:3e bit 7 to 0 + val8 = read_byte(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, val8); + + // turn on motor of selected drive, DMA & int enabled, normal operation + 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); + + // check port 3f4 for drive readiness + val8 = inb(0x3f4); + if ( (val8 & 0xf0) != 0x80 ) + BX_PANIC("int13_diskette:f03: ctrl not ready\n"); + + // send read-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, 0); // last sector number possible 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 + val8 = (read_byte(0x0000, 0x043e) & 0x80); + while ( val8 == 0 ) { + val8 = (read_byte(0x0000, 0x043e) & 0x80); + } + + 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(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, 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; + } + + + 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 + val8 = read_byte(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, val8); + // turn on motor of selected drive, DMA & int enabled, normal operation + 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); + + // check port 3f4 for drive readiness + val8 = inb(0x3f4); + if ( (val8 & 0xf0) != 0x80 ) + BX_PANIC("int13_diskette:f05: ctrl not ready\n"); + + // send read-normal-data 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 + val8 = (read_byte(0x0000, 0x043e) & 0x80); + while ( val8 == 0 ) { + val8 = (read_byte(0x0000, 0x043e) & 0x80); + } + 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(0x0000, 0x043e); + val8 &= 0x7f; + write_byte(0x0000, 0x043e, 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; + + struct ipl_entry 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; + if (bootdev == 0) BX_PANIC("No bootable device.\n"); + + /* 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); + + switch(e.type) { + case 0x01: /* FDD */ + case 0x02: /* HDD */ + + bootdrv = (e.type == 0x02) ? 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 + mov bx, #0x0000 ;; 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 != 0x00 || !((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 0x03: /* 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 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */ + bootseg = e.vector >> 16; + bootip = e.vector & 0xffff; + break; + + default: return; + } + + + /* Jump to the boot vector */ +ASM_START + mov bp, sp + ;; 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) { + BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx); + } + 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; + + offset = read_word( 0x40, 0x98 ); + segment = 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, 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: + call _int13_harddisk + +int13_out: + pop ds + pop es + popa + iret + +;---------- +;- INT18h - +;---------- +int18_handler: ;; Boot Failure recovery: try the next device. + + ;; Reset SP and SS + mov ax, #0xfffe + mov sp, ax + xor ax, ax + mov ss, ax + + ;; Get the boot sequence number out of the IPL memory + ;; The first time we do this it will have been set to -1 so + ;; we will start from device 0. + mov bx, #IPL_SEG + 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, #0xfffe + mov sp, ax + xor ax, ax + mov ss, ax + call _machine_reset + +;---------- +;- INT1Ch - +;---------- +int1c_handler: ;; User Timer Tick + iret + + +;---------------------- +;- POST: Floppy Drive - +;---------------------- +floppy_drive_post: + mov ax, #0x0000 + 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 + + mov ax, #0x0000 + 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, #EBDA_SEG, #0x003D) + SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D) + + ;; 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 + + mov ax, #EBDA_SEG + 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. + mov ax, #EBDA_SEG + 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: + call eoi_both_pics + + xor ax, ax + mov ds, ax + + jmp far ptr [0x467] + + +;-------------------- +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 + +ASM_END +#include "32bitgateway.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: + pushf + cmp eax, #0x49435024 + jne unknown_service + mov eax, #0x80000000 + mov dx, #0x0cf8 + out dx, eax + mov dx, #0x0cfc + in eax, dx + cmp eax, #0x12378086 + jne unknown_service + mov ebx, #0x000f0000 + mov ecx, #0 + mov edx, #pcibios_protected + xor al, al + jmp bios32_end +unknown_service: + mov al, #0x80 +bios32_end: + popf + retf + +.align 16 +pcibios_protected: + pushf + cli + push esi + push edi + cmp al, #0x01 ;; installation check + jne pci_pro_f02 + mov bx, #0x0210 + mov cx, #0 + mov edx, #0x20494350 + mov al, #0x01 + jmp pci_pro_ok +pci_pro_f02: ;; find pci device + cmp al, #0x02 + jne pci_pro_f08 + shl ecx, #16 + mov cx, dx + mov bx, #0x0000 + 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_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 + sti + popf + stc + retf +pci_pro_ok: + xor ah, ah + pop edi + pop esi + sti + popf + 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 + cmp eax, #0x12378086 + je pci_present + 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 + 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_f08 + shl ecx, #16 + mov cx, dx + mov bx, #0x0000 + 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 ah, #0x86 + 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_unknown + call pci_real_select_reg + push dx + mov dx, #0x0cfc + mov eax, ecx + out dx, eax + pop dx + jmp pci_real_ok +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 0x7000 ;; 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 0x07 ;; checksum + ;; 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 +#endif // BX_PCIBIOS + +; 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: + push ax + push bx + push cx + xor ax, ax + xor bx, bx + xor cx, cx + mov ch, [2] + shl cx, #1 +checksum_loop: + add al, [bx] + inc bx + loop checksum_loop + and al, #0xff + pop cx + pop bx + pop ax + 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..0xDFFFF in 2k increments + ;; System ROM: only 0xE0000 + ;; + ;; Header: + ;; Offset Value + ;; 0 0x55 + ;; 1 0xAA + ;; 2 ROM length in 512-byte blocks + ;; 3 ROM initialization entry point (FAR CALL) + +#if BX_TCGBIOS + call _tcpa_start_option_rom_scan /* specs: 3.2.3.3 + 10.4.3 */ +#endif + mov cx, #0xc000 +rom_scan_loop: + 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 + xor bx, bx ;; Restore DS back to 0000: + mov ds, bx + push ax ;; Save AX + push di ;; Save DI + ;; Push addr of ROM entry point + push cx ;; Push seg + push #0x0003 ;; Push offset + + ;; 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 ax, #0xf000 + mov es, ax + lea di, pnp_string + + 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, 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. + mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives + mov ds, bx + 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 0[bx], #0x80 ;; This entry is a BEV device + mov 6[bx], cx ;; Build a far pointer from the segment... + mov 4[bx], ax ;; and the offset + 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 + cmp cx, #0xe000 + 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 + + +;; for 'C' strings and other data, insert them here with +;; a the following hack: +;; DATA_SEG_DEFS_HERE + + +;-------- +;- 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 + mov dx, #EBDA_SEG + mov ds, dx + mov [EBDA_CMOS_SHUTDOWN_STATUS_OFFSET], AL + + cli + mov ax, #0xfffe + mov sp, ax + mov ax, #0x0000 + mov ds, ax + mov ss, ax + + ;; 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 + mov bx, #0x0000 ;; offset index + mov cx, #0x0100 ;; counter (256 interrupts) + mov ax, #dummy_iret_handler + mov dx, #0xF000 + +post_default_ints: + mov [bx], ax + inc bx + inc bx + mov [bx], dx + inc bx + inc bx + 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 + 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 + +#ifdef HVMASSIST + call _enable_rom_write_access + call _clobber_entry_point + call _copy_e820_table + call smbios_init + call _disable_rom_write_access +#endif + + call _init_boot_vectors + + call rom_scan + + call _print_bios_banner + + ;; + ;; Floppy setup + ;; + call floppy_drive_post + +#if BX_USE_ATADRV + + ;; + ;; Hard Drive setup + ;; + call hard_drive_post + + ;; + ;; ATA/ATAPI driver setup + ;; + call _ata_init + call _ata_detect + ;; +#else // BX_USE_ATADRV + + ;; + ;; Hard Drive setup + ;; + call hard_drive_post + +#endif // BX_USE_ATADRV + +#if BX_ELTORITO_BOOT + ;; + ;; eltorito floppy/harddisk emulation from cd + ;; + call _cdemu_init + ;; +#endif // BX_ELTORITO_BOOT + + call _s3_resume + call _interactive_bootkey + +#if BX_TCGBIOS + call tcpa_post_part2 +#endif + + ;; 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 + mov ax, #0x0000 + 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 + nop +#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 + //test al, #0x80 ;;look for key release + //jnz int09_process_key ;; dont pass releases to intercept? + + ;; check for extended key + cmp al, #0xe0 + jne int09_call_int15_4f + + push ds + xor ax, ax + mov ds, ax + mov al, BYTE [0x496] ;; mf2_state |= 0x01 + or al, #0x01 + mov BYTE [0x496], al + pop ds + + in al, #0x60 ;;read another key from keyboard controller + + sti + +int09_call_int15_4f: + push ds + pusha +#ifdef BX_CALL_INT15_4F + mov ah, #0x4f ;; allow for keyboard intercept + stc + int #0x15 + jnc int09_done +#endif + + +//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 + mov ax, #0x0000 ;; 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 + mov ax, #0x0000 + 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 + pusha + xor ax, ax + mov ds, ax + call _int70_function + popa + 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 "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team." + +;------------------------------------------------ +;- 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, +}; + +#ifdef HVMASSIST +ASM_START + +// space for addresses in 32bit BIOS area; currently 256/4 entries +// are allocated +.org 0xcb00 +jmptable: +db 0x5F, 0x5F, 0x5F, 0x4A, 0x4D, 0x50, 0x54 ;; ___JMPT +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 64 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 128 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 192 bytes + +// +// MP Tables +// just carve out some blank space for HVMLOADER to write the MP tables to +// +// NOTE: There should be enough space for a 32 processor entry MP table +// +.org 0xcc00 +db 0x5F, 0x5F, 0x5F, 0x48, 0x56, 0x4D, 0x4D, 0x50 ;; ___HVMMP +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 64 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 128 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 192 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 256 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 320 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 384 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 448 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 512 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 576 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 640 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 704 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 768 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 832 bytes +dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;; 896 bytes + +.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 +ASM_END + +#else // !HVMASSIST + +ASM_START +.org 0xcc00 +// bcc-generated data will be placed here + +// For documentation of this config structure, look on developer.intel.com and +// search for multiprocessor specification. Note that when you change anything +// you must update the checksum (a pain!). It would be better to construct this +// with C structures, or at least fill in the checksum automatically. +// +// Maybe this structs could be moved elsewhere than d000 + +#if (BX_SMP_PROCESSORS==1) + // no structure necessary. +#elif (BX_SMP_PROCESSORS==2) +// define the Intel MP Configuration Structure for 2 processors at +// APIC ID 0,1. I/O APIC at ID=2. +.align 16 +mp_config_table: + db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature + dw (mp_config_end-mp_config_table) ;; table length + db 4 ;; spec rev + db 0x65 ;; checksum + .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU" + db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 " + db 0x20, 0x20, 0x20, 0x20 + db 0x20, 0x20, 0x20, 0x20 + dw 0,0 ;; oem table ptr + dw 0 ;; oem table size + dw 20 ;; entry count + dw 0x0000, 0xfee0 ;; memory mapped address of local APIC + dw 0 ;; extended table length + db 0 ;; extended table checksum + db 0 ;; reserved +mp_config_proc0: + db 0 ;; entry type=processor + db 0 ;; local APIC id + db 0x11 ;; local APIC version number + db 3 ;; cpu flags: enabled, bootstrap processor + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc1: + db 0 ;; entry type=processor + db 1 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_isa_bus: + db 1 ;; entry type=bus + db 0 ;; bus ID + db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA " +mp_config_ioapic: + db 2 ;; entry type=I/O APIC + db 2 ;; apic id=2. linux will set. + db 0x11 ;; I/O APIC version number + db 1 ;; flags=1=enabled + dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC +mp_config_irqs: + db 3 ;; entry type=I/O interrupt + db 0 ;; interrupt type=vectored interrupt + db 0,0 ;; flags po=0, el=0 (linux uses as default) + db 0 ;; source bus ID is ISA + db 0 ;; source bus IRQ + db 2 ;; destination I/O APIC ID + db 0 ;; destination I/O APIC interrrupt in + ;; repeat pattern for interrupts 0-15 + db 3,0,0,0,0,1,2,1 + db 3,0,0,0,0,2,2,2 + db 3,0,0,0,0,3,2,3 + db 3,0,0,0,0,4,2,4 + db 3,0,0,0,0,5,2,5 + db 3,0,0,0,0,6,2,6 + db 3,0,0,0,0,7,2,7 + db 3,0,0,0,0,8,2,8 + db 3,0,0,0,0,9,2,9 + db 3,0,0,0,0,10,2,10 + db 3,0,0,0,0,11,2,11 + db 3,0,0,0,0,12,2,12 + db 3,0,0,0,0,13,2,13 + db 3,0,0,0,0,14,2,14 + db 3,0,0,0,0,15,2,15 +#elif (BX_SMP_PROCESSORS==4) +// define the Intel MP Configuration Structure for 4 processors at +// APIC ID 0,1,2,3. I/O APIC at ID=4. +.align 16 +mp_config_table: + db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature + dw (mp_config_end-mp_config_table) ;; table length + db 4 ;; spec rev + db 0xdd ;; checksum + .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU" + db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 " + db 0x20, 0x20, 0x20, 0x20 + db 0x20, 0x20, 0x20, 0x20 + dw 0,0 ;; oem table ptr + dw 0 ;; oem table size + dw 22 ;; entry count + dw 0x0000, 0xfee0 ;; memory mapped address of local APIC + dw 0 ;; extended table length + db 0 ;; extended table checksum + db 0 ;; reserved +mp_config_proc0: + db 0 ;; entry type=processor + db 0 ;; local APIC id + db 0x11 ;; local APIC version number + db 3 ;; cpu flags: enabled, bootstrap processor + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc1: + db 0 ;; entry type=processor + db 1 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc2: + db 0 ;; entry type=processor + db 2 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc3: + db 0 ;; entry type=processor + db 3 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_isa_bus: + db 1 ;; entry type=bus + db 0 ;; bus ID + db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA " +mp_config_ioapic: + db 2 ;; entry type=I/O APIC + db 4 ;; apic id=4. linux will set. + db 0x11 ;; I/O APIC version number + db 1 ;; flags=1=enabled + dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC +mp_config_irqs: + db 3 ;; entry type=I/O interrupt + db 0 ;; interrupt type=vectored interrupt + db 0,0 ;; flags po=0, el=0 (linux uses as default) + db 0 ;; source bus ID is ISA + db 0 ;; source bus IRQ + db 4 ;; destination I/O APIC ID + db 0 ;; destination I/O APIC interrrupt in + ;; repeat pattern for interrupts 0-15 + db 3,0,0,0,0,1,4,1 + db 3,0,0,0,0,2,4,2 + db 3,0,0,0,0,3,4,3 + db 3,0,0,0,0,4,4,4 + db 3,0,0,0,0,5,4,5 + db 3,0,0,0,0,6,4,6 + db 3,0,0,0,0,7,4,7 + db 3,0,0,0,0,8,4,8 + db 3,0,0,0,0,9,4,9 + db 3,0,0,0,0,10,4,10 + db 3,0,0,0,0,11,4,11 + db 3,0,0,0,0,12,4,12 + db 3,0,0,0,0,13,4,13 + db 3,0,0,0,0,14,4,14 + db 3,0,0,0,0,15,4,15 +#elif (BX_SMP_PROCESSORS==8) +// define the Intel MP Configuration Structure for 8 processors at +// APIC ID 0,1,2,3,4,5,6,7. I/O APIC at ID=8. +.align 16 +mp_config_table: + db 0x50, 0x43, 0x4d, 0x50 ;; "PCMP" signature + dw (mp_config_end-mp_config_table) ;; table length + db 4 ;; spec rev + db 0xc3 ;; checksum + .ascii "BOCHSCPU" ;; OEM id = "BOCHSCPU" + db 0x30, 0x2e, 0x31, 0x20 ;; vendor id = "0.1 " + db 0x20, 0x20, 0x20, 0x20 + db 0x20, 0x20, 0x20, 0x20 + dw 0,0 ;; oem table ptr + dw 0 ;; oem table size + dw 26 ;; entry count + dw 0x0000, 0xfee0 ;; memory mapped address of local APIC + dw 0 ;; extended table length + db 0 ;; extended table checksum + db 0 ;; reserved +mp_config_proc0: + db 0 ;; entry type=processor + db 0 ;; local APIC id + db 0x11 ;; local APIC version number + db 3 ;; cpu flags: enabled, bootstrap processor + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc1: + db 0 ;; entry type=processor + db 1 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc2: + db 0 ;; entry type=processor + db 2 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc3: + db 0 ;; entry type=processor + db 3 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc4: + db 0 ;; entry type=processor + db 4 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc5: + db 0 ;; entry type=processor + db 5 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc6: + db 0 ;; entry type=processor + db 6 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_proc7: + db 0 ;; entry type=processor + db 7 ;; local APIC id + db 0x11 ;; local APIC version number + db 1 ;; cpu flags: enabled + db 0,6,0,0 ;; cpu signature + dw 0x201,0 ;; feature flags + dw 0,0 ;; reserved + dw 0,0 ;; reserved +mp_config_isa_bus: + db 1 ;; entry type=bus + db 0 ;; bus ID + db 0x49, 0x53, 0x41, 0x20, 0x20, 0x20 ;; bus type="ISA " +mp_config_ioapic: + db 2 ;; entry type=I/O APIC + db 8 ;; apic id=8 + db 0x11 ;; I/O APIC version number + db 1 ;; flags=1=enabled + dw 0x0000, 0xfec0 ;; memory mapped address of I/O APIC +mp_config_irqs: + db 3 ;; entry type=I/O interrupt + db 0 ;; interrupt type=vectored interrupt + db 0,0 ;; flags po=0, el=0 (linux uses as default) + db 0 ;; source bus ID is ISA + db 0 ;; source bus IRQ + db 8 ;; destination I/O APIC ID + db 0 ;; destination I/O APIC interrrupt in + ;; repeat pattern for interrupts 0-15 + db 3,0,0,0,0,1,8,1 + db 3,0,0,0,0,2,8,2 + db 3,0,0,0,0,3,8,3 + db 3,0,0,0,0,4,8,4 + db 3,0,0,0,0,5,8,5 + db 3,0,0,0,0,6,8,6 + db 3,0,0,0,0,7,8,7 + db 3,0,0,0,0,8,8,8 + db 3,0,0,0,0,9,8,9 + db 3,0,0,0,0,10,8,10 + db 3,0,0,0,0,11,8,11 + db 3,0,0,0,0,12,8,12 + db 3,0,0,0,0,13,8,13 + db 3,0,0,0,0,14,8,14 + db 3,0,0,0,0,15,8,15 +#else +# error Sorry, rombios only has configurations for 1, 2, 4 or 8 processors. +#endif // if (BX_SMP_PROCESSORS==...) + +mp_config_end: // this label used to find length of mp structure + db 0 + +#if (BX_SMP_PROCESSORS>1) +.align 16 +mp_floating_pointer_structure: +db 0x5f, 0x4d, 0x50, 0x5f ; "_MP_" signature +dw mp_config_table, 0xf ;; pointer to MP configuration table +db 1 ;; length of this struct in 16-bit byte chunks +db 4 ;; MP spec revision +db 0xc1 ;; checksum +db 0 ;; MP feature byte 1. value 0 means look at the config table +db 0,0,0,0 ;; MP feature bytes 2-5. +#endif + +ASM_END + +#endif // HVMASSIST diff --git a/tools/firmware/rombios/tcgbios.c b/tools/firmware/rombios/tcgbios.c new file mode 100644 index 0000000..9adba40 --- /dev/null +++ b/tools/firmware/rombios/tcgbios.c @@ -0,0 +1,270 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ + +/******************************************************************* + Support for TCPA ACPI logging + ******************************************************************/ + +/* + * 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. + */ +Bit32u tcpa_extend_acpi_log(entry_ptr) + Bit32u entry_ptr; +{ + ASM_START + DoUpcall(IDX_TCPA_EXTEND_ACPI_LOG) + ASM_END +} + + +/* + initialize the TCPA ACPI subsystem; find the ACPI tables and determine + where the TCPA table is. + */ + void +tcpa_acpi_init() +{ + ASM_START + DoUpcall(IDX_TCPA_ACPI_INIT) + ASM_END +} + + +/* + * Add measurement to log about call of int 19h + */ + void +tcpa_calling_int19h() +{ + ASM_START + DoUpcall(IDX_TCPA_CALLING_INT19H) + ASM_END +} + +/* + * Add measurement to log about retuning from int 19h + */ + void +tcpa_returned_int19h() +{ + ASM_START + DoUpcall(IDX_TCPA_RETURNED_INT19H) + ASM_END +} + +/* + * Add event separators for PCRs 0 to 7; specs 8.2.3 + */ + void +tcpa_add_event_separators() +{ + ASM_START + DoUpcall(IDX_TCPA_ADD_EVENT_SEPARATORS) + ASM_END +} + + +/* + * Add a wake event to the log + */ + void +tcpa_wake_event() +{ + ASM_START + DoUpcall(IDX_TCPA_WAKE_EVENT) + ASM_END +} + + +/* + * Add measurement to the log about option rom scan + * 10.4.3 : action 14 + */ + void +tcpa_start_option_rom_scan() +{ + ASM_START + DoUpcall(IDX_TCPA_START_OPTION_ROM_SCAN) + ASM_END +} + + +/* + * Add measurement to the log about an option rom + */ + void +tcpa_option_rom(seg) + Bit32u seg; +{ + ASM_START + DoUpcall(IDX_TCPA_OPTION_ROM) + ASM_END +} + +/* + * Add a measurement regarding the boot device (CDRom, Floppy, HDD) to + * the list of measurements. + */ +void + tcpa_add_bootdevice(bootcd, bootdrv) + Bit32u bootcd; + Bit32u bootdrv; +{ + ASM_START + DoUpcall(IDX_TCPA_ADD_BOOTDEVICE) + ASM_END +} + +/* + * Add a measurement to the log in support of 8.2.5.3 + * Creates two log entries + * + * Input parameter: + * seg : segment where the IPL data are located + */ + void + tcpa_ipl(bootcd,seg,off,count) + Bit32u bootcd; + Bit32u seg; + Bit32u off; + Bit32u count; +{ + ASM_START + DoUpcall(IDX_TCPA_IPL) + ASM_END +} + + +Bit32u +tcpa_initialize_tpm(physpres) + Bit32u physpres; +{ + ASM_START + DoUpcall(IDX_TCPA_INITIALIZE_TPM) + ASM_END +} + +void +tcpa_measure_post(from, to) + Bit32u from; + Bit32u to; +{ + ASM_START + DoUpcall(IDX_TCPA_MEASURE_POST) + ASM_END +} + +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 +} + +Bit32u +TCGInterruptHandler(regs_ptr, es, ds, flags_ptr) + Bit32u regs_ptr; + Bit16u es; + Bit16u ds; + Bit32u flags_ptr; +{ + ASM_START + DoUpcall(IDX_TCGINTERRUPTHANDLER) + 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); +} diff --git a/tools/firmware/vgabios/BUGS b/tools/firmware/vgabios/BUGS new file mode 100644 index 0000000..2bf3b06 --- /dev/null +++ b/tools/firmware/vgabios/BUGS @@ -0,0 +1,3 @@ +Not all the functions have been implemented yet. + +Please report any bugs to diff --git a/tools/firmware/vgabios/COPYING b/tools/firmware/vgabios/COPYING new file mode 100644 index 0000000..223ede7 --- /dev/null +++ b/tools/firmware/vgabios/COPYING @@ -0,0 +1,504 @@ + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +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! + + diff --git a/tools/firmware/vgabios/ChangeLog b/tools/firmware/vgabios/ChangeLog new file mode 100644 index 0000000..75be5bd --- /dev/null +++ b/tools/firmware/vgabios/ChangeLog @@ -0,0 +1,1264 @@ +2008-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 + diff --git a/tools/firmware/vgabios/Makefile b/tools/firmware/vgabios/Makefile new file mode 100644 index 0000000..26bb871 --- /dev/null +++ b/tools/firmware/vgabios/Makefile @@ -0,0 +1,86 @@ +CC = gcc + +GCC = gcc +BCC = bcc +AS86 = as86 + +RELEASE = `pwd | sed "s-.*/--"` +RELDATE = `date '+%d %b %Y'` +RELVERS = `pwd | sed "s-.*/--" | sed "s/vgabios//" | sed "s/-//"` + +VGABIOS_DATE = "-DVGABIOS_DATE=\"$(RELDATE)\"" + +.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: 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 > $@ diff --git a/tools/firmware/vgabios/Notes b/tools/firmware/vgabios/Notes new file mode 100644 index 0000000..d5b708d --- /dev/null +++ b/tools/firmware/vgabios/Notes @@ -0,0 +1,11 @@ +Development 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 diff --git a/tools/firmware/vgabios/README b/tools/firmware/vgabios/README new file mode 100644 index 0000000..a21fef1 --- /dev/null +++ b/tools/firmware/vgabios/README @@ -0,0 +1,219 @@ +Plex86/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 diff --git a/tools/firmware/vgabios/TODO b/tools/firmware/vgabios/TODO new file mode 100644 index 0000000..6fa1df8 --- /dev/null +++ b/tools/firmware/vgabios/TODO @@ -0,0 +1,26 @@ +Short 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 + diff --git a/tools/firmware/vgabios/biossums.c b/tools/firmware/vgabios/biossums.c new file mode 100644 index 0000000..d5816f4 --- /dev/null +++ b/tools/firmware/vgabios/biossums.c @@ -0,0 +1,282 @@ +/* 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#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 %d 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 ); +} diff --git a/tools/firmware/vgabios/clext.c b/tools/firmware/vgabios/clext.c new file mode 100644 index 0000000..c501227 --- /dev/null +++ b/tools/firmware/vgabios/clext.c @@ -0,0 +1,1717 @@ +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +//#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 diff --git a/tools/firmware/vgabios/dataseghack b/tools/firmware/vgabios/dataseghack new file mode 100755 index 0000000..02a2d4c --- /dev/null +++ b/tools/firmware/vgabios/dataseghack @@ -0,0 +1,23 @@ +#!/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 diff --git a/tools/firmware/vgabios/vbe.c b/tools/firmware/vgabios/vbe.c new file mode 100644 index 0000000..87f7414 --- /dev/null +++ b/tools/firmware/vgabios/vbe.c @@ -0,0 +1,1432 @@ +// ============================================================================================ +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ============================================================================================ +// +// 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" + +#define VBE_TOTAL_VIDEO_MEMORY_DIV_64K (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB*1024/64) + +// 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 + +/** 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) + vbe_info_block.TotalMemory = VBE_TOTAL_VIDEO_MEMORY_DIV_64K; + + 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 + { + if ((cur_info->info.XResolution <= dispi_get_max_xres()) && + (cur_info->info.BitsPerPixel <= dispi_get_max_bpp())) { +#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 by display\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; +{ + Bit16u result=0x0100; + Bit16u ss=get_SS(); + ModeInfoBlock info; + ModeInfoListItem *cur_info; + Boolean using_lfb; + +#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) + { +#ifdef DEBUG + printf("VBE found mode %x\n",CX); +#endif + memsetb(ss, &info, 0, sizeof(ModeInfoBlock)); + memcpyb(ss, &info, 0xc000, &(cur_info->info), sizeof(ModeInfoBlockCompact)); + if (using_lfb) { + info.NumberOfBanks = 1; + } + if (info.WinAAttributes & VBE_WINDOW_ATTRIBUTE_RELOCATABLE) { + info.WinFuncPtr = 0xC0000000UL; + *(Bit16u *)&(info.WinFuncPtr) = (Bit16u)(dispi_set_bank_farcall); + } + + result = 0x4f; + } + else + { +#ifdef DEBUG + printf("VBE *NOT* found mode %x\n",CX); +#endif + result = 0x100; + } + + if (result == 0x4f) + { + // copy updates in mode_info_block back + memcpyb(ES, DI, ss, &info, sizeof(info)); + } + + 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 diff --git a/tools/firmware/vgabios/vbe.h b/tools/firmware/vgabios/vbe.h new file mode 100644 index 0000000..60434ac --- /dev/null +++ b/tools/firmware/vgabios/vbe.h @@ -0,0 +1,313 @@ +#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 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_GETCAPS 0x02 + #define VBE_DISPI_8BIT_DAC 0x20 + #define VBE_DISPI_LFB_ENABLED 0x40 + #define VBE_DISPI_NOCLEARMEM 0x80 + + #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + +#endif diff --git a/tools/firmware/vgabios/vbe_display_api.txt b/tools/firmware/vgabios/vbe_display_api.txt new file mode 100644 index 0000000..fddb78b --- /dev/null +++ b/tools/firmware/vgabios/vbe_display_api.txt @@ -0,0 +1,237 @@ +VBE 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. diff --git a/tools/firmware/vgabios/vbetables-gen.c b/tools/firmware/vgabios/vbetables-gen.c new file mode 100644 index 0000000..7014a16 --- /dev/null +++ b/tools/firmware/vgabios/vbetables-gen.c @@ -0,0 +1,240 @@ +/* Generate the VGABIOS VBE Tables */ +#include +#include + +#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 8 + +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}, +{ 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; +} diff --git a/tools/firmware/vgabios/vgabios.c b/tools/firmware/vgabios/vgabios.c new file mode 100644 index 0000000..3fd9f2f --- /dev/null +++ b/tools/firmware/vgabios/vgabios.c @@ -0,0 +1,3853 @@ +// ============================================================================================ +/* + * 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ============================================================================================ +// +// 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(0x0500, nibble + '0'); + else + outb(0x0500, (nibble - 10) + 'A'); + digit--; + } + in_format = 0; + } + //else if (c == 'd') { + // in_format = 0; + // } + } + else { + outb(0x0500, 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 diff --git a/tools/firmware/vgabios/vgabios.h b/tools/firmware/vgabios/vgabios.h new file mode 100644 index 0000000..3ad4bae --- /dev/null +++ b/tools/firmware/vgabios/vgabios.h @@ -0,0 +1,47 @@ +#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 diff --git a/tools/firmware/vgabios/vgafonts.h b/tools/firmware/vgabios/vgafonts.h new file mode 100644 index 0000000..0c213e6 --- /dev/null +++ b/tools/firmware/vgabios/vgafonts.h @@ -0,0 +1,784 @@ +/* + * 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}; diff --git a/tools/firmware/vgabios/vgatables.h b/tools/firmware/vgabios/vgatables.h new file mode 100644 index 0000000..8809100 --- /dev/null +++ b/tools/firmware/vgabios/vgatables.h @@ -0,0 +1,623 @@ +/* + * + * 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 +}; diff --git a/tools/flask/Makefile b/tools/flask/Makefile new file mode 100644 index 0000000..e78f578 --- /dev/null +++ b/tools/flask/Makefile @@ -0,0 +1,10 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +SUBDIRS := +SUBDIRS += libflask +SUBDIRS += loadpolicy + +.PHONY: all clean install +all clean install: %: subdirs-% + diff --git a/tools/flask/libflask/Makefile b/tools/flask/libflask/Makefile new file mode 100644 index 0000000..8c085b9 --- /dev/null +++ b/tools/flask/libflask/Makefile @@ -0,0 +1,65 @@ +MAJOR = 1.0 +MINOR = 0 + +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +XEN_LIBXC = $(XEN_ROOT)/tools/libxc + +SRCS := +SRCS += flask_op.c + +CFLAGS += -Werror +CFLAGS += -fno-strict-aliasing +CFLAGS += $(INCLUDES) -I./include -I$(XEN_LIBXC) -I$(XEN_INCLUDE) + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +LDFLAGS += -L. +DEPS = .*.d + +LIB_OBJS := $(patsubst %.c,%.o,$(SRCS)) +PIC_OBJS := $(patsubst %.c,%.opic,$(SRCS)) + +LIB := libflask.a +LIB += libflask.so libflask.so.$(MAJOR) libflask.so.$(MAJOR).$(MINOR) + +.PHONY: all +all: build + +.PHONY: build +build: + $(MAKE) $(LIB) + +.PHONY: install +install: build + $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR) + $(INSTALL_PROG) libflask.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DATA) libflask.a $(DESTDIR)$(LIBDIR) + ln -sf libflask.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libflask.so.$(MAJOR) + ln -sf libflask.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libflask.so + $(INSTALL_DATA) include/flask.h $(DESTDIR)$(INCLUDEDIR) + +.PHONY: TAGS +TAGS: + etags -t *.c *.h + +.PHONY: clean +clean: + rm -rf *.a *.so* *.o *.opic *.rpm $(LIB) *~ $(DEPS) xen + +# libflask + +libflask.a: $(LIB_OBJS) + $(AR) rc $@ $^ + +libflask.so: libflask.so.$(MAJOR) + ln -sf $< $@ +libflask.so.$(MAJOR): libflask.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libflask.so.$(MAJOR).$(MINOR): $(PIC_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libflask.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ + +-include $(DEPS) diff --git a/tools/flask/libflask/flask_op.c b/tools/flask/libflask/flask_op.c new file mode 100644 index 0000000..396c081 --- /dev/null +++ b/tools/flask/libflask/flask_op.c @@ -0,0 +1,72 @@ +/* + * + * 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 +#include +#include +#include +#include + +int flask_load(int xc_handle, char *buf, uint32_t size) +{ + int err; + flask_op_t op; + + op.cmd = FLASK_LOAD; + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + return err; + + return 0; +} + +int flask_context_to_sid(int xc_handle, char *buf, uint32_t size, uint32_t *sid) +{ + int err; + flask_op_t op; + + op.cmd = FLASK_CONTEXT_TO_SID; + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + return err; + + sscanf(buf, "%u", sid); + + return 0; +} + +int flask_sid_to_context(int xc_handle, int sid, char *buf, uint32_t size) +{ + int err; + flask_op_t op; + + op.cmd = FLASK_SID_TO_CONTEXT; + op.buf = buf; + op.size = size; + + snprintf(buf, size, "%u", sid); + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + return err; + + return 0; +} diff --git a/tools/flask/libflask/include/flask.h b/tools/flask/libflask/include/flask.h new file mode 100644 index 0000000..5241f7a --- /dev/null +++ b/tools/flask/libflask/include/flask.h @@ -0,0 +1,22 @@ +/* + * + * 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. + */ + +#ifndef __FLASK_H__ +#define __FLASK_H__ + +#include +#include +#include + +int flask_load(int xc_handle, char *buf, uint32_t size); +int flask_context_to_sid(int xc_handle, char *buf, uint32_t size, uint32_t *sid); +int flask_sid_to_context(int xc_handle, int sid, char *buf, uint32_t size); + +#endif /* __FLASK_H__ */ diff --git a/tools/flask/loadpolicy/Makefile b/tools/flask/loadpolicy/Makefile new file mode 100644 index 0000000..7c38525 --- /dev/null +++ b/tools/flask/loadpolicy/Makefile @@ -0,0 +1,60 @@ +XEN_ROOT=../../.. +include $(XEN_ROOT)/tools/Rules.mk +XEN_LIBXC = $(XEN_ROOT)/tools/libxc + +LIBXC_ROOT = $(XEN_ROOT)/tools/libxc +LIBFLASK_ROOT = $(XEN_ROOT)/tools/flask/libflask + +PROFILE=#-pg +BASECFLAGS=-Wall -g -Werror +# Make gcc generate dependencies. +BASECFLAGS += -Wp,-MD,.$(@F).d +PROG_DEP = .*.d +BASECFLAGS+= $(PROFILE) +#BASECFLAGS+= -I$(XEN_ROOT)/tools +BASECFLAGS+= $(CFLAGS_libxenctrl) +BASECFLAGS+= -I$(LIBFLASK_ROOT)/include +BASECFLAGS+= -I. + +CFLAGS += $(BASECFLAGS) +LDFLAGS += $(PROFILE) -L$(XEN_LIBXC) -L$(LIBFLASK_ROOT) +TESTDIR = testsuite/tmp +TESTFLAGS= -DTESTING +TESTENV = XENSTORED_ROOTDIR=$(TESTDIR) XENSTORED_RUNDIR=$(TESTDIR) + +CLIENTS := flask-loadpolicy +CLIENTS_SRCS := $(patsubst flask-%,%.c,$(CLIENTS)) +CLIENTS_OBJS := $(patsubst flask-%,%.o,$(CLIENTS)) + +.PHONY: all +all: $(CLIENTS) + +$(CLIENTS): flask-%: %.o + $(CC) $(CFLAGS) $(LDFLAGS) $< $(LOADLIBES) $(LDLIBS) -L. -lflask $(LDFLAGS_libxenctrl) -o $@ + +$(CLIENTS_OBJS): $(CLIENTS_SRCS) + $(COMPILE.c) -o $@ $< + +.PHONY: clean +clean: + rm -f *.o *.opic *.so + rm -f $(CLIENTS) + $(RM) $(PROG_DEP) + +.PHONY: print-dir +print-dir: + @echo -n tools/flask/loadpolicy: + +.PHONY: print-end +print-end: + @echo + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(SBINDIR) + $(INSTALL_PROG) $(CLIENTS) $(DESTDIR)$(SBINDIR) + +-include $(PROG_DEP) + +# never delete any intermediate files. +.SECONDARY: diff --git a/tools/flask/loadpolicy/loadpolicy.c b/tools/flask/loadpolicy/loadpolicy.c new file mode 100644 index 0000000..bb6eeb8 --- /dev/null +++ b/tools/flask/loadpolicy/loadpolicy.c @@ -0,0 +1,129 @@ +/* + * + * 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 +#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; + int 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 ) + { + fprintf(stderr, "Error occurred mapping policy file in memory: %s\n", + strerror(errno)); + ret = -1; + goto cleanup; + } + + xch = xc_interface_open(); + if ( xch < 0 ) + { + 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 = 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: + if ( polMemCp ) + 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 ) + close(polFd); + if ( xch ) + xc_interface_close(xch); + + return ret; + +cleanup: + goto done; +} diff --git a/tools/flask/policy/Makefile b/tools/flask/policy/Makefile new file mode 100644 index 0000000..069b232 --- /dev/null +++ b/tools/flask/policy/Makefile @@ -0,0 +1,234 @@ +# +# Makefile for the security policy. +# +# Targets: +# +# install - compile and install the policy configuration, and context files. +# load - compile, install, and load the policy configuration. +# reload - compile, install, and load/reload the policy configuration. +# policy - compile the policy configuration locally for testing/development. +# +# The default target is 'policy'. +# + +######################################## +# +# Configurable portions of the Makefile +# + +# Policy version +# By default, checkpolicy will create the highest +# version policy it supports. Setting this will +# override the version. +OUTPUT_POLICY = 20 + +# Policy Type +# strict, targeted, +# strict-mls, targeted-mls, +# strict-mcs, targeted-mcs +TYPE = strict + +# Policy Name +# If set, this will be used as the policy +# name. Otherwise the policy type will be +# used for the name. +NAME = xenrefpolicy + +# Distribution +# Some distributions have portions of policy +# for programs or configurations specific to the +# distribution. Setting this will enable options +# for the distribution. +# redhat, gentoo, debian, and suse are current options. +# Fedora users should enable redhat. +#DISTRO = + +# Build monolithic policy. Putting n here +# will build a loadable module policy. +MONOLITHIC=y + +# Uncomment this to disable command echoing +#QUIET:=@ + +######################################## +# +# NO OPTIONS BELOW HERE +# + +# executable paths +PREFIX := /usr +BINDIR := $(PREFIX)/bin +SBINDIR := $(PREFIX)/sbin +CHECKPOLICY := $(BINDIR)/checkpolicy +CHECKMODULE := $(BINDIR)/checkmodule +SEMOD_PKG := $(BINDIR)/semodule_package +LOADPOLICY := $(SBINDIR)/flask-loadpolicy + +CFLAGS := -Wall + +# policy source layout +POLDIR := policy +MODDIR := $(POLDIR)/modules +FLASKDIR := $(POLDIR)/flask +SECCLASS := $(FLASKDIR)/security_classes +ISIDS := $(FLASKDIR)/initial_sids +AVS := $(FLASKDIR)/access_vectors + +#policy building support tools +SUPPORT := support +FCSORT := tmp/fc_sort + +# config file paths +GLOBALTUN := $(POLDIR)/global_tunables +GLOBALBOOL := $(POLDIR)/global_booleans +MOD_CONF := $(POLDIR)/modules.conf +TUNABLES := $(POLDIR)/tunables.conf +BOOLEANS := $(POLDIR)/booleans.conf + +# install paths +TOPDIR = $(DESTDIR)/etc/xen/ +INSTALLDIR = $(TOPDIR)/$(NAME) +SRCPATH = $(INSTALLDIR)/src +USERPATH = $(INSTALLDIR)/users +CONTEXTPATH = $(INSTALLDIR)/contexts + +# enable MLS if requested. +ifneq ($(findstring -mls,$(TYPE)),) + override M4PARAM += -D enable_mls + CHECKPOLICY += -M + CHECKMODULE += -M +endif + +# enable MLS if MCS requested. +ifneq ($(findstring -mcs,$(TYPE)),) + override M4PARAM += -D enable_mcs + CHECKPOLICY += -M + CHECKMODULE += -M +endif + +# compile targeted policy if requested. +ifneq ($(findstring targeted,$(TYPE)),) + override M4PARAM += -D targeted_policy +endif + +# enable distribution-specific policy +ifneq ($(DISTRO),) + override M4PARAM += -D distro_$(DISTRO) +endif + +ifneq ($(OUTPUT_POLICY),) + CHECKPOLICY += -c $(OUTPUT_POLICY) +endif + +ifeq ($(NAME),) + NAME := $(TYPE) +endif + +# determine the policy version and current kernel version if possible +PV := $(shell $(CHECKPOLICY) -V |cut -f 1 -d ' ') +KV := $(shell cat /selinux/policyvers) + +# dont print version warnings if we are unable to determine +# the currently running kernel's policy version +ifeq ($(KV),) + KV := $(PV) +endif + +FC := file_contexts +POLVER := policy.$(PV) + +M4SUPPORT = $(wildcard $(POLDIR)/support/*.spt) + +APPCONF := config/appconfig-$(TYPE) +APPDIR := $(CONTEXTPATH) +APPFILES := $(INSTALLDIR)/booleans +CONTEXTFILES += $(wildcard $(APPCONF)/*_context*) $(APPCONF)/media +USER_FILES := $(POLDIR)/systemuser $(POLDIR)/users + +ALL_LAYERS := $(filter-out $(MODDIR)/CVS,$(shell find $(wildcard $(MODDIR)/*) -maxdepth 0 -type d)) + +GENERATED_TE := $(basename $(foreach dir,$(ALL_LAYERS),$(wildcard $(dir)/*.te.in))) +GENERATED_IF := $(basename $(foreach dir,$(ALL_LAYERS),$(wildcard $(dir)/*.if.in))) +GENERATED_FC := $(basename $(foreach dir,$(ALL_LAYERS),$(wildcard $(dir)/*.fc.in))) + +# sort here since it removes duplicates, which can happen +# when a generated file is already generated +DETECTED_MODS := $(sort $(foreach dir,$(ALL_LAYERS),$(wildcard $(dir)/*.te)) $(GENERATED_TE)) + +# modules.conf setting for base module +MODBASE := base + +# modules.conf setting for module +MODMOD := module + +# extract settings from modules.conf +BASE_MODS := $(foreach mod,$(shell awk '/^[[:blank:]]*[[:alpha:]]/{ if ($$3 == "$(MODBASE)") print $$1 }' $(MOD_CONF) 2> /dev/null),$(subst ./,,$(shell find -iname $(mod).te))) +MOD_MODS := $(foreach mod,$(shell awk '/^[[:blank:]]*[[:alpha:]]/{ if ($$3 == "$(MODMOD)") print $$1 }' $(MOD_CONF) 2> /dev/null),$(subst ./,,$(shell find -iname $(mod).te))) + +HOMEDIR_TEMPLATE = tmp/homedir_template + +######################################## +# +# Load appropriate rules +# + +ifeq ($(MONOLITHIC),y) + include Rules.monolithic +else + include Rules.modular +endif + +######################################## +# +# Create config files +# +conf: $(MOD_CONF) $(BOOLEANS) $(GENERATED_TE) $(GENERATED_IF) $(GENERATED_FC) + +$(MOD_CONF) $(BOOLEANS): $(POLXML) + @echo "Updating $(MOD_CONF) and $(BOOLEANS)" + $(QUIET) cd $(DOCS) && ../$(GENDOC) -t ../$(BOOLEANS) -m ../$(MOD_CONF) -x ../$(POLXML) + +######################################## +# +# Appconfig files +# +install-appconfig: $(APPFILES) + +$(INSTALLDIR)/booleans: $(BOOLEANS) + @mkdir -p $(INSTALLDIR) + $(QUIET) egrep '^[[:blank:]]*[[:alpha:]]' $(BOOLEANS) \ + | sed -e 's/false/0/g' -e 's/true/1/g' > tmp/booleans + $(QUIET) install -m 644 tmp/booleans $@ + +######################################## +# +# Install policy sources +# +install-src: + rm -rf $(SRCPATH)/policy.old + -mv $(SRCPATH)/policy $(SRCPATH)/policy.old + mkdir -p $(SRCPATH)/policy + cp -R . $(SRCPATH)/policy + +######################################## +# +# Clean everything +# +bare: clean + rm -f $(POLXML) + rm -f $(SUPPORT)/*.pyc + rm -f $(FCSORT) + rm -f $(MOD_CONF) + rm -f $(BOOLEANS) + rm -fR $(HTMLDIR) +ifneq ($(GENERATED_TE),) + rm -f $(GENERATED_TE) +endif +ifneq ($(GENERATED_IF),) + rm -f $(GENERATED_IF) +endif +ifneq ($(GENERATED_FC),) + rm -f $(GENERATED_FC) +endif + +.PHONY: install-src install-appconfig conf html bare diff --git a/tools/flask/policy/Rules.modular b/tools/flask/policy/Rules.modular new file mode 100644 index 0000000..798f989 --- /dev/null +++ b/tools/flask/policy/Rules.modular @@ -0,0 +1,166 @@ +######################################## +# +# Rules and Targets for building modular policies +# + +ALL_MODULES := $(filter $(BASE_MODS) $(MOD_MODS),$(DETECTED_MODS)) +ALL_INTERFACES := $(ALL_MODULES:.te=.if) + +BASE_PKG := base.pp +BASE_FC := base.fc + +BASE_SECTIONS := tmp/pre_te_files.conf tmp/generated_definitions.conf tmp/all_interfaces.conf tmp/all_attrs_types.conf $(GLOBALBOOL) $(GLOBALTUN) tmp/only_te_rules.conf tmp/all_post.conf + +BASE_PRE_TE_FILES := $(SECCLASS) $(ISIDS) $(AVS) $(M4SUPPORT) $(POLDIR)/mls $(POLDIR)/mcs +BASE_TE_FILES := $(BASE_MODS) +BASE_POST_TE_FILES := $(POLDIR)/systemuser $(POLDIR)/constraints +BASE_FC_FILES := $(BASE_MODS:.te=.fc) + +MOD_MODULES := $(MOD_MODS:.te=.mod) +MOD_PKGS := $(notdir $(MOD_MODS:.te=.pp)) + +# search layer dirs for source files +vpath %.te $(ALL_LAYERS) +vpath %.if $(ALL_LAYERS) +vpath %.fc $(ALL_LAYERS) + +######################################## +# +# default action: create all module packages +# +default: base + +base: $(BASE_PKG) + +modules: $(MOD_PKGS) + +#policy: $(POLVER) +#install: $(LOADPATH) $(FCPATH) $(APPFILES) $(USERPATH)/local.users +#load: tmp/load + +######################################## +# +# Create a base module package +# +$(BASE_PKG): tmp/base.mod $(BASE_FC) + @echo "Creating $(NAME) base module package" + $(QUIET) $(SEMOD_PKG) $@ $^ + +######################################## +# +# Compile a base module +# +tmp/base.mod: base.conf + @echo "Compiling $(NAME) base module" + $(QUIET) $(CHECKMODULE) $^ -o $@ + +######################################## +# +# Construct a base module policy.conf +# +base.conf: $(BASE_SECTIONS) + @echo "Creating $(NAME) base module policy.conf" +# checkpolicy can use the #line directives provided by -s for error reporting: + $(QUIET) m4 -D self_contained_policy $(M4PARAM) -s $^ > tmp/$@.tmp + $(QUIET) sed -e /^portcon/d -e /^nodecon/d -e /^netifcon/d < tmp/$@.tmp > $@ +# the ordering of these ocontexts matters: + $(QUIET) grep ^portcon tmp/$@.tmp >> $@ || true + $(QUIET) grep ^netifcon tmp/$@.tmp >> $@ || true + $(QUIET) grep ^nodecon tmp/$@.tmp >> $@ || true + +tmp/pre_te_files.conf: $(BASE_PRE_TE_FILES) + @test -d tmp || mkdir -p tmp + $(QUIET) cat $^ > $@ + +tmp/generated_definitions.conf: $(ALL_LAYERS) $(BASE_TE_FILES) + @test -d tmp || mkdir -p tmp +# define all available object classes + $(QUIET) $(GENPERM) $(AVS) $(SECCLASS) > $@ +# per-userdomain templates + $(QUIET) echo "define(\`per_userdomain_templates',\`" >> $@ + $(QUIET) for i in $(patsubst %.te,%,$(notdir $(ALL_MODULES))); do \ + echo "ifdef(\`""$$i""_per_userdomain_template',\`""$$i""_per_userdomain_template("'$$*'")')" \ + >> $@ ;\ + done + $(QUIET) echo "')" >> $@ +# define foo.te + $(QUIET) for i in $(notdir $(BASE_TE_FILES)); do \ + echo "define(\`$$i')" >> $@ ;\ + done + $(QUIET) $(SETTUN) $(BOOLEANS) >> $@ + +tmp/all_interfaces.conf: $(M4SUPPORT) $(ALL_INTERFACES) +ifeq ($(ALL_INTERFACES),) + $(error No enabled modules! $(notdir $(MOD_CONF)) may need to be generated by using "make conf") +endif + @test -d tmp || mkdir -p tmp + $(QUIET) m4 $^ | sed -e s/dollarsstar/\$$\*/g > $@ + +tmp/all_te_files.conf: $(BASE_TE_FILES) +ifeq ($(BASE_TE_FILES),) + $(error No enabled modules! $(notdir $(MOD_CONF)) may need to be generated by using "make conf") +endif + @test -d tmp || mkdir -p tmp + $(QUIET) cat $^ > $@ + +tmp/post_te_files.conf: $(BASE_POST_TE_FILES) + @test -d tmp || mkdir -p tmp + $(QUIET) cat $^ > $@ + +# extract attributes and put them first. extract post te stuff +# like genfscon and put last. portcon, nodecon, and netifcon +# is delayed since they are generated by m4 +tmp/all_attrs_types.conf tmp/only_te_rules.conf tmp/all_post.conf: tmp/all_te_files.conf tmp/post_te_files.conf + $(QUIET) grep ^attribute tmp/all_te_files.conf > tmp/all_attrs_types.conf || true + $(QUIET) grep '^type ' tmp/all_te_files.conf >> tmp/all_attrs_types.conf + $(QUIET) cat tmp/post_te_files.conf > tmp/all_post.conf + $(QUIET) grep '^sid ' tmp/all_te_files.conf >> tmp/all_post.conf || true + $(QUIET) egrep '^fs_use_(xattr|task|trans)' tmp/all_te_files.conf >> tmp/all_post.conf || true + $(QUIET) grep ^genfscon tmp/all_te_files.conf >> tmp/all_post.conf || true + $(QUIET) sed -r -e /^attribute/d -e '/^type /d' -e /^genfscon/d \ + -e '/^sid /d' -e '/^fs_use_(xattr|task|trans)/d' \ + < tmp/all_te_files.conf > tmp/only_te_rules.conf + +######################################## +# +# Construct base module file contexts +# +$(BASE_FC): $(M4SUPPORT) tmp/generated_definitions.conf $(BASE_FC_FILES) $(FCSORT) +ifeq ($(BASE_FC_FILES),) + $(error No enabled modules! $(notdir $(MOD_CONF)) may need to be generated by using "make conf") +endif + @echo "Creating $(NAME) base module file contexts." + @test -d tmp || mkdir -p tmp + $(QUIET) m4 $(M4PARAM) $(M4SUPPORT) tmp/generated_definitions.conf $(BASE_FC_FILES) > tmp/$@.tmp + $(QUIET) grep -e HOME -e ROLE tmp/$@.tmp > $(HOMEDIR_TEMPLATE) + $(QUIET) sed -i -e /HOME/d -e /ROLE/d tmp/$@.tmp + $(QUIET) $(FCSORT) tmp/$@.tmp $@ + +######################################## +# +# Build module packages +# +tmp/%.mod: $(M4SUPPORT) tmp/generated_definitions.conf tmp/all_interfaces.conf %.te + @if test -z "$(filter $^,$(MOD_MODS))"; then \ + echo "The $(notdir $(basename $@)) module is not configured to be compiled as a lodable module." ;\ + false ;\ + fi + @echo "Compliling $(NAME) $(@F) module" + $(QUIET) m4 $(M4PARAM) -s $^ > $(@:.mod=.tmp) + $(QUIET) $(CHECKMODULE) -m $(@:.mod=.tmp) -o $@ + +%.pp: tmp/%.mod %.fc + @echo "Creating $(NAME) $(@F) policy package" + $(QUIET) $(SEMOD_PKG) $@ $^ + +######################################## +# +# Clean the sources +# +clean: + rm -fR tmp + rm -f base.conf + rm -f *.pp + rm -f $(BASE_FC) + +.PHONY: default base modules clean diff --git a/tools/flask/policy/Rules.monolithic b/tools/flask/policy/Rules.monolithic new file mode 100644 index 0000000..03147a1 --- /dev/null +++ b/tools/flask/policy/Rules.monolithic @@ -0,0 +1,196 @@ +######################################## +# +# Rules and Targets for building monolithic policies +# + +# install paths +POLICYPATH = $(INSTALLDIR)/policy +LOADPATH = $(POLICYPATH)/$(POLVER) +FCPATH = $(CONTEXTPATH)/files/file_contexts +HOMEDIRPATH = $(CONTEXTPATH)/files/homedir_template + +# for monolithic policy use all base and module to create policy +ENABLEMOD := $(BASE_MODS) $(MOD_MODS) + +ALL_MODULES := $(filter $(ENABLEMOD),$(DETECTED_MODS)) + +ALL_INTERFACES := $(ALL_MODULES:.te=.if) +ALL_TE_FILES := $(ALL_MODULES) +ALL_FC_FILES := $(ALL_MODULES:.te=.fc) + +PRE_TE_FILES := $(SECCLASS) $(ISIDS) $(AVS) $(M4SUPPORT) $(POLDIR)/mls $(POLDIR)/mcs +POST_TE_FILES := $(POLDIR)/systemuser $(POLDIR)/users $(POLDIR)/constraints + +POLICY_SECTIONS := tmp/pre_te_files.conf tmp/generated_definitions.conf tmp/all_interfaces.conf tmp/all_attrs_types.conf $(GLOBALBOOL) $(GLOBALTUN) tmp/only_te_rules.conf tmp/all_post.conf + +######################################## +# +# default action: build policy locally +# +default: policy + +policy: $(POLVER) + +install: $(LOADPATH) $(FCPATH) $(APPFILES) $(USERPATH)/local.users + +load: tmp/load + +######################################## +# +# Build a binary policy locally +# +$(POLVER): policy.conf + @echo "Compiling $(NAME) $(POLVER)" +ifneq ($(PV),$(KV)) + @echo + @echo "WARNING: Policy version mismatch! Is your OUTPUT_POLICY set correctly?" + @echo +endif + $(QUIET) $(CHECKPOLICY) $^ -o $@ + +######################################## +# +# Install a binary policy +# +$(LOADPATH): policy.conf + @mkdir -p $(POLICYPATH) + @echo "Compiling and installing $(NAME) $(LOADPATH)" +ifneq ($(PV),$(KV)) + @echo + @echo "WARNING: Policy version mismatch! Is your OUTPUT_POLICY set correctly?" + @echo +endif + $(QUIET) $(CHECKPOLICY) $^ -o $@ + +######################################## +# +# Load the binary policy +# +reload tmp/load: $(LOADPATH) $(FCPATH) + @echo "Loading $(NAME) $(LOADPATH)" + $(QUIET) $(LOADPOLICY) -q $(LOADPATH) + @touch tmp/load + +######################################## +# +# Construct a monolithic policy.conf +# +policy.conf: $(POLICY_SECTIONS) + @echo "Creating $(NAME) policy.conf" +# checkpolicy can use the #line directives provided by -s for error reporting: + $(QUIET) m4 -D self_contained_policy $(M4PARAM) -s $^ > tmp/$@.tmp + $(QUIET) sed -e /^portcon/d -e /^nodecon/d -e /^netifcon/d < tmp/$@.tmp > $@ + +tmp/pre_te_files.conf: $(PRE_TE_FILES) + @test -d tmp || mkdir -p tmp + $(QUIET) cat $^ > $@ + +tmp/generated_definitions.conf: $(ALL_LAYERS) $(ALL_TE_FILES) +# per-userdomain templates: + @test -d tmp || mkdir -p tmp + $(QUIET) echo "define(\`per_userdomain_templates',\`" > $@ + $(QUIET) for i in $(patsubst %.te,%,$(notdir $(ALL_MODULES))); do \ + echo "ifdef(\`""$$i""_per_userdomain_template',\`""$$i""_per_userdomain_template("'$$*'")')" \ + >> $@ ;\ + done + $(QUIET) echo "')" >> $@ +# define foo.te + $(QUIET) for i in $(notdir $(ALL_MODULES)); do \ + echo "define(\`$$i')" >> $@ ;\ + done +# $(QUIET) $(SETTUN) $(BOOLEANS) >> $@ + +tmp/all_interfaces.conf: $(M4SUPPORT) $(ALL_INTERFACES) +ifeq ($(ALL_INTERFACES),) + $(error No enabled modules! $(notdir $(MOD_CONF)) may need to be generated by using "make conf") +endif + @test -d tmp || mkdir -p tmp + $(QUIET) m4 $^ | sed -e s/dollarsstar/\$$\*/g > $@ + +tmp/all_te_files.conf: $(ALL_TE_FILES) +ifeq ($(ALL_TE_FILES),) + $(error No enabled modules! $(notdir $(MOD_CONF)) may need to be generated by using "make conf") +endif + @test -d tmp || mkdir -p tmp + $(QUIET) cat $^ > $@ + +tmp/post_te_files.conf: $(POST_TE_FILES) + @test -d tmp || mkdir -p tmp + $(QUIET) cat $^ > $@ + +# extract attributes and put them first. extract post te stuff +# like genfscon and put last. portcon, nodecon, and netifcon +# is delayed since they are generated by m4 +tmp/all_attrs_types.conf tmp/only_te_rules.conf tmp/all_post.conf: tmp/all_te_files.conf tmp/post_te_files.conf + $(QUIET) grep ^attribute tmp/all_te_files.conf > tmp/all_attrs_types.conf || true + $(QUIET) grep '^type ' tmp/all_te_files.conf >> tmp/all_attrs_types.conf + $(QUIET) cat tmp/post_te_files.conf > tmp/all_post.conf + $(QUIET) grep '^sid ' tmp/all_te_files.conf >> tmp/all_post.conf || true + $(QUIET) egrep '^fs_use_(xattr|task|trans)' tmp/all_te_files.conf >> tmp/all_post.conf || true + $(QUIET) grep ^genfscon tmp/all_te_files.conf >> tmp/all_post.conf || true + $(QUIET) sed -r -e /^attribute/d -e '/^type /d' -e /^genfscon/d \ + -e '/^sid /d' -e '/^fs_use_(xattr|task|trans)/d' \ + < tmp/all_te_files.conf > tmp/only_te_rules.conf + +######################################## +# +# Remove the dontaudit rules from the policy.conf +# +enableaudit: policy.conf + @test -d tmp || mkdir -p tmp + @echo "Removing dontaudit rules from policy.conf" + $(QUIET) grep -v dontaudit policy.conf > tmp/policy.audit + $(QUIET) mv tmp/policy.audit policy.conf + +######################################## +# +# Construct file_contexts +# +$(FC): $(M4SUPPORT) tmp/generated_definitions.conf $(ALL_FC_FILES) +ifeq ($(ALL_FC_FILES),) + $(error No enabled modules! $(notdir $(MOD_CONF)) may need to be generated by using "make conf") +endif + @echo "Creating $(NAME) file_contexts." + @test -d tmp || mkdir -p tmp + $(QUIET) m4 $(M4PARAM) $(M4SUPPORT) tmp/generated_definitions.conf $(ALL_FC_FILES) > tmp/$@.tmp +# $(QUIET) grep -e HOME -e ROLE tmp/$@.tmp > $(HOMEDIR_TEMPLATE) +# $(QUIET) sed -i -e /HOME/d -e /ROLE/d tmp/$@.tmp +# $(QUIET) $(FCSORT) tmp/$@.tmp $@ + $(QUIET) touch $(HOMEDIR_TEMPLATE) + $(QUIET) touch $@ + +######################################## +# +# Install file_contexts +# +$(FCPATH): $(FC) $(LOADPATH) $(USERPATH)/system.users + @echo "Validating $(NAME) file_contexts." +# $(QUIET) $(SETFILES) -q -c $(LOADPATH) $(FC) + @echo "Installing file_contexts." + @mkdir -p $(CONTEXTPATH)/files + $(QUIET) install -m 644 $(FC) $(FCPATH) + $(QUIET) install -m 644 $(HOMEDIR_TEMPLATE) $(HOMEDIRPATH) +# $(QUIET) $(GENHOMEDIRCON) -d $(TOPDIR) -t $(NAME) $(USEPWD) + +######################################## +# +# Run policy source checks +# +check: policy.conf $(FC) + $(SECHECK) -s --profile=development --policy=policy.conf --fcfile=$(FC) > $@.res + +longcheck: policy.conf $(FC) + $(SECHECK) -s --profile=all --policy=policy.conf --fcfile=$(FC) > $@.res + +######################################## +# +# Clean the sources +# +clean: + rm -fR tmp + rm -f policy.conf + rm -f policy.$(PV) + rm -f $(FC) + rm -f *.res + +.PHONY: default policy install load reload enableaudit checklabels restorelabels relabel check longcheck clean diff --git a/tools/flask/policy/policy/constraints b/tools/flask/policy/policy/constraints new file mode 100644 index 0000000..beb949c --- /dev/null +++ b/tools/flask/policy/policy/constraints @@ -0,0 +1,27 @@ + +# +# Define the constraints +# +# 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 +# + diff --git a/tools/flask/policy/policy/flask/Makefile b/tools/flask/policy/policy/flask/Makefile new file mode 100644 index 0000000..970b9fe --- /dev/null +++ b/tools/flask/policy/policy/flask/Makefile @@ -0,0 +1,41 @@ +# flask needs to know where to export the libselinux headers. +LIBSEL ?= ../../libselinux + +# flask needs to know where to export the kernel headers. +LINUXDIR ?= ../../../linux-2.6 + +AWK = awk + +CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ + else if [ -x /bin/bash ]; then echo /bin/bash; \ + else echo sh; fi ; fi) + +FLASK_H_DEPEND = security_classes initial_sids +AV_H_DEPEND = access_vectors + +FLASK_H_FILES = class_to_string.h flask.h initial_sid_to_string.h +AV_H_FILES = av_inherit.h common_perm_to_string.h av_perm_to_string.h av_permissions.h +ALL_H_FILES = $(FLASK_H_FILES) $(AV_H_FILES) + +all: $(ALL_H_FILES) + +$(FLASK_H_FILES): $(FLASK_H_DEPEND) + $(CONFIG_SHELL) mkflask.sh $(AWK) $(FLASK_H_DEPEND) + +$(AV_H_FILES): $(AV_H_DEPEND) + $(CONFIG_SHELL) mkaccess_vector.sh $(AWK) $(AV_H_DEPEND) + +tolib: all + install -m 644 flask.h av_permissions.h $(LIBSEL)/include/selinux + install -m 644 class_to_string.h av_inherit.h common_perm_to_string.h av_perm_to_string.h $(LIBSEL)/src + +tokern: all + install -m 644 $(ALL_H_FILES) $(LINUXDIR)/security/selinux/include + +install: all + +relabel: + +clean: + rm -f $(FLASK_H_FILES) + rm -f $(AV_H_FILES) diff --git a/tools/flask/policy/policy/flask/access_vectors b/tools/flask/policy/policy/flask/access_vectors new file mode 100644 index 0000000..0df71d0 --- /dev/null +++ b/tools/flask/policy/policy/flask/access_vectors @@ -0,0 +1,166 @@ +# +# Define common prefixes for access vectors +# +# common common_name { permission_name ... } + +# +# Define a common prefix for file access vectors. +# + + +# +# Define the access vectors. +# +# class class_name [ inherits common_name ] { permission_name ... } + + +# +# Define the access vector interpretation for file-related objects. +# + +class xen +{ + scheduler + 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 +} + +class domain +{ + setvcpucontext + pause + unpause + resume + create + transition + max_vcpus + destroy + setvcpuaffinity + getvcpuaffinity + scheduler + getdomaininfo + getvcpuinfo + getvcpucontext + setdomainmaxmem + setdomainhandle + setdebugging + hypercall + settime + set_target + shutdown + setaddrsize + getaddrsize + trigger + getextvcpucontext + setextvcpucontext +} + +class hvm +{ + sethvmc + gethvmc + setparam + getparam + pcilevel + irqlevel + pciroute + bind_irq + cacheattr +} + +class event +{ + bind + send + status + notify + create + vector + reset +} + +class grant +{ + map_read + map_write + unmap + transfer + setup + copy + query +} + +class mmu +{ + map_read + map_write + pageinfo + pagelist + adjust + stat + translategp + updatemp + physmap + pinpage + mfnlist + memorymap +} + +class shadow +{ + disable + enable + logdirty +} + +class resource +{ + add + remove + use + add_irq + remove_irq + add_ioport + remove_ioport + add_iomem + remove_iomem + stat_device + add_device + remove_device +} + +class security +{ + compute_av + compute_create + compute_member + check_context + load_policy + compute_relabel + compute_user + setenforce + setbool + setsecparam +} diff --git a/tools/flask/policy/policy/flask/initial_sids b/tools/flask/policy/policy/flask/initial_sids new file mode 100644 index 0000000..9b78fba --- /dev/null +++ b/tools/flask/policy/policy/flask/initial_sids @@ -0,0 +1,17 @@ +# FLASK + +# +# Define initial security identifiers +# +sid xen +sid dom0 +sid domU +sid domio +sid domxen +sid unlabeled +sid security +sid ioport +sid iomem +sid pirq +sid device +# FLASK diff --git a/tools/flask/policy/policy/flask/mkaccess_vector.sh b/tools/flask/policy/policy/flask/mkaccess_vector.sh new file mode 100644 index 0000000..b5da734 --- /dev/null +++ b/tools/flask/policy/policy/flask/mkaccess_vector.sh @@ -0,0 +1,227 @@ +#!/bin/sh - +# + +# FLASK + +set -e + +awk=$1 +shift + +# output files +av_permissions="av_permissions.h" +av_inherit="av_inherit.h" +common_perm_to_string="common_perm_to_string.h" +av_perm_to_string="av_perm_to_string.h" + +cat $* | $awk " +BEGIN { + outfile = \"$av_permissions\" + inheritfile = \"$av_inherit\" + cpermfile = \"$common_perm_to_string\" + avpermfile = \"$av_perm_to_string\" + "' + nextstate = "COMMON_OR_AV"; + printf("/* This file is automatically generated. Do not edit. */\n") > outfile; + printf("/* This file is automatically generated. Do not edit. */\n") > inheritfile; + printf("/* This file is automatically generated. Do not edit. */\n") > cpermfile; + printf("/* This file is automatically generated. Do not edit. */\n") > avpermfile; +; + } +/^[ \t]*#/ { + next; + } +$1 == "common" { + if (nextstate != "COMMON_OR_AV") + { + printf("Parse error: Unexpected COMMON definition on line %d\n", NR); + next; + } + + if ($2 in common_defined) + { + printf("Duplicate COMMON definition for %s on line %d.\n", $2, NR); + next; + } + common_defined[$2] = 1; + + tclass = $2; + common_name = $2; + permission = 1; + + printf("TB_(common_%s_perm_to_string)\n", $2) > cpermfile; + + nextstate = "COMMON-OPENBRACKET"; + next; + } +$1 == "class" { + if (nextstate != "COMMON_OR_AV" && + nextstate != "CLASS_OR_CLASS-OPENBRACKET") + { + printf("Parse error: Unexpected class definition on line %d\n", NR); + next; + } + + tclass = $2; + + if (tclass in av_defined) + { + printf("Duplicate access vector definition for %s on line %d\n", tclass, NR); + next; + } + av_defined[tclass] = 1; + + inherits = ""; + permission = 1; + + nextstate = "INHERITS_OR_CLASS-OPENBRACKET"; + next; + } +$1 == "inherits" { + if (nextstate != "INHERITS_OR_CLASS-OPENBRACKET") + { + printf("Parse error: Unexpected INHERITS definition on line %d\n", NR); + next; + } + + if (!($2 in common_defined)) + { + printf("COMMON %s is not defined (line %d).\n", $2, NR); + next; + } + + inherits = $2; + permission = common_base[$2]; + + for (combined in common_perms) + { + split(combined,separate, SUBSEP); + if (separate[1] == inherits) + { + inherited_perms[common_perms[combined]] = separate[2]; + } + } + + j = 1; + for (i in inherited_perms) { + ind[j] = i + 0; + j++; + } + n = asort(ind); + for (i = 1; i <= n; i++) { + perm = inherited_perms[ind[i]]; + printf("#define %s__%s", toupper(tclass), toupper(perm)) > outfile; + spaces = 40 - (length(perm) + length(tclass)); + if (spaces < 1) + spaces = 1; + for (j = 0; j < spaces; j++) + printf(" ") > outfile; + printf("0x%08xUL\n", ind[i]) > outfile; + } + printf("\n") > outfile; + for (i in ind) delete ind[i]; + for (i in inherited_perms) delete inherited_perms[i]; + + printf(" S_(SECCLASS_%s, %s, 0x%08xUL)\n", toupper(tclass), inherits, permission) > inheritfile; + + nextstate = "CLASS_OR_CLASS-OPENBRACKET"; + next; + } +$1 == "{" { + if (nextstate != "INHERITS_OR_CLASS-OPENBRACKET" && + nextstate != "CLASS_OR_CLASS-OPENBRACKET" && + nextstate != "COMMON-OPENBRACKET") + { + printf("Parse error: Unexpected { on line %d\n", NR); + next; + } + + if (nextstate == "INHERITS_OR_CLASS-OPENBRACKET") + nextstate = "CLASS-CLOSEBRACKET"; + + if (nextstate == "CLASS_OR_CLASS-OPENBRACKET") + nextstate = "CLASS-CLOSEBRACKET"; + + if (nextstate == "COMMON-OPENBRACKET") + nextstate = "COMMON-CLOSEBRACKET"; + } +/[a-z][a-z_]*/ { + if (nextstate != "COMMON-CLOSEBRACKET" && + nextstate != "CLASS-CLOSEBRACKET") + { + printf("Parse error: Unexpected symbol %s on line %d\n", $1, NR); + next; + } + + if (nextstate == "COMMON-CLOSEBRACKET") + { + if ((common_name,$1) in common_perms) + { + printf("Duplicate permission %s for common %s on line %d.\n", $1, common_name, NR); + next; + } + + common_perms[common_name,$1] = permission; + + printf("#define COMMON_%s__%s", toupper(common_name), toupper($1)) > outfile; + + printf(" S_(\"%s\")\n", $1) > cpermfile; + } + else + { + if ((tclass,$1) in av_perms) + { + printf("Duplicate permission %s for %s on line %d.\n", $1, tclass, NR); + next; + } + + av_perms[tclass,$1] = permission; + + if (inherits != "") + { + if ((inherits,$1) in common_perms) + { + printf("Permission %s in %s on line %d conflicts with common permission.\n", $1, tclass, inherits, NR); + next; + } + } + + printf("#define %s__%s", toupper(tclass), toupper($1)) > outfile; + + printf(" S_(SECCLASS_%s, %s__%s, \"%s\")\n", toupper(tclass), toupper(tclass), toupper($1), $1) > avpermfile; + } + + spaces = 40 - (length($1) + length(tclass)); + if (spaces < 1) + spaces = 1; + + for (i = 0; i < spaces; i++) + printf(" ") > outfile; + printf("0x%08xUL\n", permission) > outfile; + permission = permission * 2; + } +$1 == "}" { + if (nextstate != "CLASS-CLOSEBRACKET" && + nextstate != "COMMON-CLOSEBRACKET") + { + printf("Parse error: Unexpected } on line %d\n", NR); + next; + } + + if (nextstate == "COMMON-CLOSEBRACKET") + { + common_base[common_name] = permission; + printf("TE_(common_%s_perm_to_string)\n\n", common_name) > cpermfile; + } + + printf("\n") > outfile; + + nextstate = "COMMON_OR_AV"; + } +END { + if (nextstate != "COMMON_OR_AV" && nextstate != "CLASS_OR_CLASS-OPENBRACKET") + printf("Parse error: Unexpected end of file\n"); + + }' + +# FLASK diff --git a/tools/flask/policy/policy/flask/mkflask.sh b/tools/flask/policy/policy/flask/mkflask.sh new file mode 100644 index 0000000..9c84754 --- /dev/null +++ b/tools/flask/policy/policy/flask/mkflask.sh @@ -0,0 +1,95 @@ +#!/bin/sh - +# + +# FLASK + +set -e + +awk=$1 +shift 1 + +# output file +output_file="flask.h" +debug_file="class_to_string.h" +debug_file2="initial_sid_to_string.h" + +cat $* | $awk " +BEGIN { + outfile = \"$output_file\" + debugfile = \"$debug_file\" + debugfile2 = \"$debug_file2\" + "' + nextstate = "CLASS"; + + printf("/* This file is automatically generated. Do not edit. */\n") > outfile; + + printf("#ifndef _SELINUX_FLASK_H_\n") > outfile; + printf("#define _SELINUX_FLASK_H_\n") > outfile; + printf("\n/*\n * Security object class definitions\n */\n") > outfile; + printf("/* This file is automatically generated. Do not edit. */\n") > debugfile; + printf("/*\n * Security object class definitions\n */\n") > debugfile; + printf(" S_(\"null\")\n") > debugfile; + printf("/* This file is automatically generated. Do not edit. */\n") > debugfile2; + printf("static char *initial_sid_to_string[] =\n{\n") > debugfile2; + printf(" \"null\",\n") > debugfile2; + } +/^[ \t]*#/ { + next; + } +$1 == "class" { + if (nextstate != "CLASS") + { + printf("Parse error: Unexpected class definition on line %d\n", NR); + next; + } + + if ($2 in class_found) + { + printf("Duplicate class definition for %s on line %d.\n", $2, NR); + next; + } + class_found[$2] = 1; + + class_value++; + + printf("#define SECCLASS_%s", toupper($2)) > outfile; + for (i = 0; i < 40 - length($2); i++) + printf(" ") > outfile; + printf("%d\n", class_value) > outfile; + + printf(" S_(\"%s\")\n", $2) > debugfile; + } +$1 == "sid" { + if (nextstate == "CLASS") + { + nextstate = "SID"; + printf("\n/*\n * Security identifier indices for initial entities\n */\n") > outfile; + } + + if ($2 in sid_found) + { + printf("Duplicate SID definition for %s on line %d.\n", $2, NR); + next; + } + sid_found[$2] = 1; + sid_value++; + + printf("#define SECINITSID_%s", toupper($2)) > outfile; + for (i = 0; i < 37 - length($2); i++) + printf(" ") > outfile; + printf("%d\n", sid_value) > outfile; + printf(" \"%s\",\n", $2) > debugfile2; + } +END { + if (nextstate != "SID") + printf("Parse error: Unexpected end of file\n"); + + printf("\n#define SECINITSID_NUM") > outfile; + for (i = 0; i < 34; i++) + printf(" ") > outfile; + printf("%d\n", sid_value) > outfile; + printf("\n#endif\n") > outfile; + printf("};\n\n") > debugfile2; + }' + +# FLASK diff --git a/tools/flask/policy/policy/flask/security_classes b/tools/flask/policy/policy/flask/security_classes new file mode 100644 index 0000000..2ca35d2 --- /dev/null +++ b/tools/flask/policy/policy/flask/security_classes @@ -0,0 +1,20 @@ +# FLASK + +# +# Define the security object classes +# + +# Classes marked as userspace are classes +# for userspace object managers + +class xen +class domain +class hvm +class mmu +class resource +class shadow +class event +class grant +class security + +# FLASK diff --git a/tools/flask/policy/policy/global_booleans b/tools/flask/policy/policy/global_booleans new file mode 100644 index 0000000..4c13cfb --- /dev/null +++ b/tools/flask/policy/policy/global_booleans @@ -0,0 +1,5 @@ +# +# This file is for the declaration of global booleans. +# To change the default value at build time, the booleans.conf +# file should be used. +# diff --git a/tools/flask/policy/policy/global_tunables b/tools/flask/policy/policy/global_tunables new file mode 100644 index 0000000..801b27e --- /dev/null +++ b/tools/flask/policy/policy/global_tunables @@ -0,0 +1,6 @@ +# +# This file is for the declaration of global tunables. +# To change the default value at build time, the booleans.conf +# file should be used. +# + diff --git a/tools/flask/policy/policy/mcs b/tools/flask/policy/policy/mcs new file mode 100644 index 0000000..a3cef61 --- /dev/null +++ b/tools/flask/policy/policy/mcs @@ -0,0 +1,324 @@ +ifdef(`enable_mcs',` +# +# Define sensitivities +# +# Each sensitivity has a name and zero or more aliases. +# +# MCS is single-sensitivity. +# +sensitivity s0; + +# +# Define the ordering of the sensitivity levels (least to greatest) +# +dominance { s0 } + + +# +# Define the categories +# +# Each category has a name and zero or more aliases. +# +category c0; +category c1; +category c2; +category c3; +category c4; +category c5; +category c6; +category c7; +category c8; +category c9; +category c10; +category c11; +category c12; +category c13; +category c14; +category c15; +category c16; +category c17; +category c18; +category c19; +category c20; +category c21; +category c22; +category c23; +category c24; +category c25; +category c26; +category c27; +category c28; +category c29; +category c30; +category c31; +category c32; +category c33; +category c34; +category c35; +category c36; +category c37; +category c38; +category c39; +category c40; +category c41; +category c42; +category c43; +category c44; +category c45; +category c46; +category c47; +category c48; +category c49; +category c50; +category c51; +category c52; +category c53; +category c54; +category c55; +category c56; +category c57; +category c58; +category c59; +category c60; +category c61; +category c62; +category c63; +category c64; +category c65; +category c66; +category c67; +category c68; +category c69; +category c70; +category c71; +category c72; +category c73; +category c74; +category c75; +category c76; +category c77; +category c78; +category c79; +category c80; +category c81; +category c82; +category c83; +category c84; +category c85; +category c86; +category c87; +category c88; +category c89; +category c90; +category c91; +category c92; +category c93; +category c94; +category c95; +category c96; +category c97; +category c98; +category c99; +category c100; +category c101; +category c102; +category c103; +category c104; +category c105; +category c106; +category c107; +category c108; +category c109; +category c110; +category c111; +category c112; +category c113; +category c114; +category c115; +category c116; +category c117; +category c118; +category c119; +category c120; +category c121; +category c122; +category c123; +category c124; +category c125; +category c126; +category c127; +category c128; +category c129; +category c130; +category c131; +category c132; +category c133; +category c134; +category c135; +category c136; +category c137; +category c138; +category c139; +category c140; +category c141; +category c142; +category c143; +category c144; +category c145; +category c146; +category c147; +category c148; +category c149; +category c150; +category c151; +category c152; +category c153; +category c154; +category c155; +category c156; +category c157; +category c158; +category c159; +category c160; +category c161; +category c162; +category c163; +category c164; +category c165; +category c166; +category c167; +category c168; +category c169; +category c170; +category c171; +category c172; +category c173; +category c174; +category c175; +category c176; +category c177; +category c178; +category c179; +category c180; +category c181; +category c182; +category c183; +category c184; +category c185; +category c186; +category c187; +category c188; +category c189; +category c190; +category c191; +category c192; +category c193; +category c194; +category c195; +category c196; +category c197; +category c198; +category c199; +category c200; +category c201; +category c202; +category c203; +category c204; +category c205; +category c206; +category c207; +category c208; +category c209; +category c210; +category c211; +category c212; +category c213; +category c214; +category c215; +category c216; +category c217; +category c218; +category c219; +category c220; +category c221; +category c222; +category c223; +category c224; +category c225; +category c226; +category c227; +category c228; +category c229; +category c230; +category c231; +category c232; +category c233; +category c234; +category c235; +category c236; +category c237; +category c238; +category c239; +category c240; +category c241; +category c242; +category c243; +category c244; +category c245; +category c246; +category c247; +category c248; +category c249; +category c250; +category c251; +category c252; +category c253; +category c254; +category c255; + + +# +# Each MCS level specifies a sensitivity and zero or more categories which may +# be associated with that sensitivity. +# +level s0:c0.c255; + +# +# Define the MCS 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 +# + + +') dnl end enable_mcs diff --git a/tools/flask/policy/policy/mls b/tools/flask/policy/policy/mls new file mode 100644 index 0000000..a598ebe --- /dev/null +++ b/tools/flask/policy/policy/mls @@ -0,0 +1,354 @@ + +ifdef(`enable_mls',` +# +# Define sensitivities +# +# Each sensitivity has a name and zero or more aliases. +# +sensitivity s0; +sensitivity s1; +sensitivity s2; +sensitivity s3; +sensitivity s4; +sensitivity s5; +sensitivity s6; +sensitivity s7; +sensitivity s8; +sensitivity s9; +sensitivity s10; +sensitivity s11; +sensitivity s12; +sensitivity s13; +sensitivity s14; +sensitivity s15; + +# +# Define the ordering of the sensitivity levels (least to greatest) +# +dominance { s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 } + + +# +# Define the categories +# +# Each category has a name and zero or more aliases. +# +category c0; +category c1; +category c2; +category c3; +category c4; +category c5; +category c6; +category c7; +category c8; +category c9; +category c10; +category c11; +category c12; +category c13; +category c14; +category c15; +category c16; +category c17; +category c18; +category c19; +category c20; +category c21; +category c22; +category c23; +category c24; +category c25; +category c26; +category c27; +category c28; +category c29; +category c30; +category c31; +category c32; +category c33; +category c34; +category c35; +category c36; +category c37; +category c38; +category c39; +category c40; +category c41; +category c42; +category c43; +category c44; +category c45; +category c46; +category c47; +category c48; +category c49; +category c50; +category c51; +category c52; +category c53; +category c54; +category c55; +category c56; +category c57; +category c58; +category c59; +category c60; +category c61; +category c62; +category c63; +category c64; +category c65; +category c66; +category c67; +category c68; +category c69; +category c70; +category c71; +category c72; +category c73; +category c74; +category c75; +category c76; +category c77; +category c78; +category c79; +category c80; +category c81; +category c82; +category c83; +category c84; +category c85; +category c86; +category c87; +category c88; +category c89; +category c90; +category c91; +category c92; +category c93; +category c94; +category c95; +category c96; +category c97; +category c98; +category c99; +category c100; +category c101; +category c102; +category c103; +category c104; +category c105; +category c106; +category c107; +category c108; +category c109; +category c110; +category c111; +category c112; +category c113; +category c114; +category c115; +category c116; +category c117; +category c118; +category c119; +category c120; +category c121; +category c122; +category c123; +category c124; +category c125; +category c126; +category c127; +category c128; +category c129; +category c130; +category c131; +category c132; +category c133; +category c134; +category c135; +category c136; +category c137; +category c138; +category c139; +category c140; +category c141; +category c142; +category c143; +category c144; +category c145; +category c146; +category c147; +category c148; +category c149; +category c150; +category c151; +category c152; +category c153; +category c154; +category c155; +category c156; +category c157; +category c158; +category c159; +category c160; +category c161; +category c162; +category c163; +category c164; +category c165; +category c166; +category c167; +category c168; +category c169; +category c170; +category c171; +category c172; +category c173; +category c174; +category c175; +category c176; +category c177; +category c178; +category c179; +category c180; +category c181; +category c182; +category c183; +category c184; +category c185; +category c186; +category c187; +category c188; +category c189; +category c190; +category c191; +category c192; +category c193; +category c194; +category c195; +category c196; +category c197; +category c198; +category c199; +category c200; +category c201; +category c202; +category c203; +category c204; +category c205; +category c206; +category c207; +category c208; +category c209; +category c210; +category c211; +category c212; +category c213; +category c214; +category c215; +category c216; +category c217; +category c218; +category c219; +category c220; +category c221; +category c222; +category c223; +category c224; +category c225; +category c226; +category c227; +category c228; +category c229; +category c230; +category c231; +category c232; +category c233; +category c234; +category c235; +category c236; +category c237; +category c238; +category c239; +category c240; +category c241; +category c242; +category c243; +category c244; +category c245; +category c246; +category c247; +category c248; +category c249; +category c250; +category c251; +category c252; +category c253; +category c254; +category c255; + + +# +# Each MLS level specifies a sensitivity and zero or more categories which may +# be associated with that sensitivity. +# +level s0:c0.c255; +level s1:c0.c255; +level s2:c0.c255; +level s3:c0.c255; +level s4:c0.c255; +level s5:c0.c255; +level s6:c0.c255; +level s7:c0.c255; +level s8:c0.c255; +level s9:c0.c255; +level s10:c0.c255; +level s11:c0.c255; +level s12:c0.c255; +level s13:c0.c255; +level s14:c0.c255; +level s15:c0.c255; + + +# +# 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 +# + + +') dnl end enable_mls diff --git a/tools/flask/policy/policy/modules.conf b/tools/flask/policy/policy/modules.conf new file mode 100644 index 0000000..1031c59 --- /dev/null +++ b/tools/flask/policy/policy/modules.conf @@ -0,0 +1,21 @@ +# +# This file contains a listing of available modules. +# To prevent a module from being used in policy +# creation, set the module name to "off". +# +# For monolithic policies, modules set to "base" and "module" +# will be built into the policy. +# +# For modular policies, modules set to "base" will be +# included in the base module. "module" will be compiled +# as individual loadable modules. +# + +# Layer: xen +# Module: xen +# Required in base +# +# Policy for xen. +# +xen = base + diff --git a/tools/flask/policy/policy/modules/xen/xen.if b/tools/flask/policy/policy/modules/xen/xen.if new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tools/flask/policy/policy/modules/xen/xen.if @@ -0,0 +1 @@ +# diff --git a/tools/flask/policy/policy/modules/xen/xen.te b/tools/flask/policy/policy/modules/xen/xen.te new file mode 100644 index 0000000..62920fc --- /dev/null +++ b/tools/flask/policy/policy/modules/xen/xen.te @@ -0,0 +1,138 @@ +attribute xen_type; +attribute domain_type; +attribute resource_type; +attribute event_type; + +type xen_t, xen_type, domain_type; + +type dom0_t, domain_type; + +type domio_t, domain_type; + +type domxen_t, domain_type; + +type unlabeled_t, domain_type; + +type security_t, domain_type; + +type pirq_t, resource_type; +type ioport_t, resource_type; +type iomem_t, resource_type; +type device_t, resource_type; + +################################################################################ +# +# create_domain(priv_dom, domain, channel) +# +################################################################################ +define(`create_domain', ` + type $2, domain_type; + allow $1 $2:domain {create max_vcpus setdomainmaxmem + setaddrsize getdomaininfo hypercall + setvcpucontext scheduler unpause + getvcpuinfo getaddrsize getvcpuaffinity}; + allow $1 $2:shadow {enable}; + allow $1 $2:mmu {map_read map_write memorymap adjust pinpage}; + allow $2 $2:mmu {map_read map_write adjust pinpage}; + allow $2 domio_t:mmu {map_read}; + allow $2 $2:grant {query setup}; + allow $1 $2:grant {map_read unmap}; + allow $1 $3:event {create}; +') + +################################################################################ +# +# manage_domain(priv_dom, domain) +# +################################################################################ +define(`manage_domain', ` + allow $1 $2:domain {pause destroy}; +') + +################################################################################ +# +# create_channel(caller, peer, channel) +# +################################################################################ +define(`create_channel', ` + type $3, event_type; + type_transition $1 $2:event $3; + allow $1 $3:event {create}; + allow $3 $2:event {bind}; +') + +################################################################################ +# +# Boot the hypervisor and dom0 +# +################################################################################ +allow dom0_t xen_t:xen {kexec readapic writeapic mtrr_read mtrr_add mtrr_del +scheduler physinfo heap quirk readconsole writeconsole settime microcode}; + +allow dom0_t domio_t:mmu {map_read map_write}; +allow dom0_t iomem_t:mmu {map_read map_write}; +allow dom0_t pirq_t:event {vector}; +allow dom0_t xen_t:mmu {memorymap}; + +allow dom0_t dom0_t:mmu {pinpage map_read map_write adjust}; +allow dom0_t dom0_t:grant {query setup}; +allow dom0_t dom0_t:domain {scheduler getdomaininfo getvcpuinfo getvcpuaffinity}; + +allow xen_t dom0_t:domain {create}; +allow xen_t dom0_t:resource {add remove}; +allow xen_t ioport_t:resource {add_ioport remove_ioport}; +allow dom0_t ioport_t:resource {use}; +allow xen_t iomem_t:resource {add_iomem remove_iomem}; +allow dom0_t iomem_t:resource {use}; +allow xen_t pirq_t:resource {add_irq remove_irq}; +allow dom0_t pirq_t:resource {use}; + +allow dom0_t security_t:security {compute_av compute_create compute_member +check_context load_policy compute_relabel compute_user setenforce setbool +setsecparam}; + +create_channel(dom0_t, dom0_t, evchn0-0_t) +allow dom0_t evchn0-0_t:event {send}; + +################################################################################ +# +# Create and manage a domU w/ dom0 IO +# +################################################################################ +create_domain(dom0_t, domU_t, evchnU-0_t) + +create_channel(domU_t, domU_t, evchnU-U_t) +allow domU_t evchnU-U_t:event {send}; + +create_channel(dom0_t, domU_t, evchn0-U_t) +allow dom0_t evchn0-U_t:event {send}; + +create_channel(domU_t, dom0_t, evchnU-0_t) +allow domU_t evchnU-0_t:event {send}; + +allow dom0_t dom0_t:event {send}; +allow dom0_t domU_t:grant {copy}; + +manage_domain(dom0_t, domU_t) + +################################################################################ +# +# +# +################################################################################ +sid xen gen_context(system_u:system_r:xen_t,s0) +sid dom0 gen_context(system_u:system_r:dom0_t,s0) +sid domU gen_context(system_u:system_r:domU_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 pirq gen_context(system_u:object_r:pirq_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) + +role system_r types { xen_type domain_type }; +role user_r types { xen_type domain_type }; +role sysadm_r types { xen_type domain_type }; +role staff_r types { xen_type domain_type }; diff --git a/tools/flask/policy/policy/support/loadable_module.spt b/tools/flask/policy/policy/support/loadable_module.spt new file mode 100644 index 0000000..de48b3b --- /dev/null +++ b/tools/flask/policy/policy/support/loadable_module.spt @@ -0,0 +1,166 @@ +######################################## +# +# Macros for switching between source policy +# and loadable policy module support +# + +############################## +# +# For adding the module statement +# +define(`policy_module',` + ifdef(`self_contained_policy',`',` + module $1 $2; + + require { + role system_r; + all_kernel_class_perms + } + ') +') + +############################## +# +# For use in interfaces, to optionally insert a require block +# +define(`gen_require',` + ifdef(`self_contained_policy',`',` + define(`in_gen_require_block') + require { + $1 + } + undefine(`in_gen_require_block') + ') +') + +############################## +# +# In the future interfaces should be in loadable modules +# +# template(name,rules) +# +define(`template',` + `define(`$1',` +##### begin $1(dollarsstar) + $2 +##### end $1(dollarsstar) + '') +') + +# helper function, since m4 wont expand macros +# if a line is a comment (#): +define(`policy_m4_comment',`dnl +##### $2 depth: $1 +')dnl + +############################## +# +# In the future interfaces should be in loadable modules +# +# interface(name,rules) +# +define(`interface',` + `define(`$1',` + + define(`policy_temp',incr(policy_call_depth)) + pushdef(`policy_call_depth',policy_temp) + undefine(`policy_temp') + + policy_m4_comment(policy_call_depth,begin `$1'(dollarsstar)) + + $2 + + define(`policy_temp',decr(policy_call_depth)) + pushdef(`policy_call_depth',policy_temp) + undefine(`policy_temp') + + policy_m4_comment(policy_call_depth,end `$1'(dollarsstar)) + + '') +') + +define(`policy_call_depth',0) + +############################## +# +# Optional policy handling +# +define(`optional_policy',` + ifdef(`self_contained_policy',` + ifdef(`$1',`$2',`$3') + ',` + optional { + $2 + ifelse(`$3',`',`',` + } else { + $3 + ') + } + ') +') + +############################## +# +# Determine if we should use the default +# tunable value as specified by the policy +# or if the override value should be used +# +define(`dflt_or_overr',`ifdef(`$1',$1,$2)') + +############################## +# +# Extract booleans out of an expression. +# This needs to be reworked so expressions +# with parentheses can work. + +define(`delcare_required_symbols',` +ifelse(regexp($1, `\w'), -1, `', `dnl +bool regexp($1, `\(\w+\)', `\1'); +delcare_required_symbols(regexp($1, `\w+\(.*\)', `\1'))dnl +') dnl +') + +############################## +# +# Tunable declaration +# +define(`gen_tunable',` + ifdef(`self_contained_policy',` + bool $1 dflt_or_overr(`$1'_conf,$2); + ',` + # loadable module tunable + # declaration will go here + # instead of bool when + # loadable modules support + # tunables + bool $1 dflt_or_overr(`$1'_conf,$2); + ') +') + +############################## +# +# Tunable policy handling +# +define(`tunable_policy',` + ifdef(`self_contained_policy',` + if (`$1') { + $2 + } else { + $3 + } + ',` + # structure for tunables + # will go here instead of a + # conditional when loadable + # modules support tunables + gen_require(` + delcare_required_symbols(`$1') + ') + + if (`$1') { + $2 + } else { + $3 + } + ') +') diff --git a/tools/flask/policy/policy/support/misc_macros.spt b/tools/flask/policy/policy/support/misc_macros.spt new file mode 100644 index 0000000..ce94e03 --- /dev/null +++ b/tools/flask/policy/policy/support/misc_macros.spt @@ -0,0 +1,32 @@ + +######################################## +# +# Helper macros +# + +# +# shiftn(num,list...) +# +# shift the list num times +# +define(`shiftn',`ifelse($1,0,`shift($*)',`shiftn(decr($1),shift(shift($*)))')') + +######################################## +# +# gen_user(username, role_set, mls_defaultlevel, mls_range, [mcs_categories]) +# +define(`gen_user',`user $1 roles { $2 }`'ifdef(`enable_mls', ` level $3 range $4')`'ifdef(`enable_mcs',` level s0 range s0`'ifelse(`$5',,,` - s0:$5')');') + +######################################## +# +# gen_context(context,mls_sensitivity,[mcs_categories]) +# +define(`gen_context',`$1`'ifdef(`enable_mls',`:$2')`'ifdef(`enable_mcs',`:s0`'ifelse(`$3',,,`:$3')')') dnl + +######################################## +# +# gen_bool(name,default_value) +# +define(`gen_bool',` + bool $1 dflt_or_overr(`$1'_conf,$2); +') diff --git a/tools/flask/policy/policy/systemuser b/tools/flask/policy/policy/systemuser new file mode 100644 index 0000000..35499f8 --- /dev/null +++ b/tools/flask/policy/policy/systemuser @@ -0,0 +1,19 @@ +################################## +# +# System User configuration. +# + +# +# gen_user(username, role_set, mls_defaultlevel, mls_range, [mcs_categories]) +# + +# +# system_u is the user identity for system processes and objects. +# There should be no corresponding Unix user identity for system, +# and a user process should never be assigned the system user +# identity. +# +gen_user(system_u, system_r, s0, s0 - s9:c0.c127, c0.c127) + +# Normal users should not be added to this file, +# but instead added to the users file. diff --git a/tools/flask/policy/policy/users b/tools/flask/policy/policy/users new file mode 100644 index 0000000..88a516e --- /dev/null +++ b/tools/flask/policy/policy/users @@ -0,0 +1,39 @@ + +################################## +# +# Core User configuration. +# + +# +# gen_user(username, role_set, mls_defaultlevel, mls_range, [mcs_catetories]) +# + +# +# user_u is a generic user identity for Linux users who have no +# SELinux user identity defined. The modified daemons will use +# this user identity in the security context if there is no matching +# SELinux user identity for a Linux user. If you do not want to +# permit any access to such users, then remove this entry. +# +ifdef(`targeted_policy',` +gen_user(user_u, user_r sysadm_r system_r, s0, s0 - s9:c0.c127) +',` +gen_user(user_u, user_r, s0, s0 - s9:c0.c127) +') + +# +# The following users correspond to Unix identities. +# These identities are typically assigned as the user attribute +# when login starts the user shell. Users with access to the sysadm_r +# role should use the staff_r role instead of the user_r role when +# not in the sysadm_r. +# +ifdef(`targeted_policy',` + gen_user(root, user_r sysadm_r system_r, s0, s0 - s9:c0.c127, c0.c127) +',` + ifdef(`direct_sysadm_daemon',` + gen_user(root, sysadm_r staff_r system_r, s0, s0 - s9:c0.c127, c0.c127) + ',` + gen_user(root, sysadm_r staff_r, s0, s0 - s9:c0.c127, c0.c127) + ') +') diff --git a/tools/fs-back/Makefile b/tools/fs-back/Makefile new file mode 100644 index 0000000..dc5fc44 --- /dev/null +++ b/tools/fs-back/Makefile @@ -0,0 +1,40 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +INCLUDES += -I.. -I../lib + +IBIN = fs-backend + +CFLAGS += -Werror +CFLAGS += -Wno-unused +CFLAGS += -fno-strict-aliasing +CFLAGS += $(CFLAGS_libxenctrl) +CFLAGS += $(CFLAGS_libxenstore) +CFLAGS += $(INCLUDES) -I. +CFLAGS += -D_GNU_SOURCE + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +LIBS := -L. -L.. -L../lib +LIBS += $(LDFLAGS_libxenctrl) +LIBS += $(LDFLAGS_libxenstore) +LIBS += -lpthread -lrt + +OBJS := fs-xenbus.o fs-ops.o + +all: $(IBIN) + +fs-backend: $(OBJS) fs-backend.c + $(CC) $(CFLAGS) -o fs-backend $(OBJS) $(LIBS) fs-backend.c + +install: all + $(INSTALL_PROG) $(IBIN) $(DESTDIR)$(SBINDIR) + +clean: + rm -rf *.o *~ $(DEPS) xen $(IBIN) $(LIB) + +.PHONY: clean install + +-include $(DEPS) diff --git a/tools/fs-back/fs-backend.c b/tools/fs-back/fs-backend.c new file mode 100644 index 0000000..f0d2758 --- /dev/null +++ b/tools/fs-back/fs-backend.c @@ -0,0 +1,355 @@ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs-backend.h" + +struct xs_handle *xsh = NULL; +static struct fs_export *fs_exports = NULL; +static int export_id = 0; +static int mount_id = 0; + +static void dispatch_response(struct fs_mount *mount, int priv_req_id) +{ + int i; + struct fs_op *op; + struct fs_request *req = &mount->requests[priv_req_id]; + + for(i=0;;i++) + { + op = fsops[i]; + /* We should dispatch a response before reaching the end of the array */ + assert(op != NULL); + if(op->type == req->req_shadow.type) + { + printf("Found op for type=%d\n", op->type); + /* There needs to be a response handler */ + assert(op->response_handler != NULL); + op->response_handler(mount, req); + break; + } + } + + req->active = 0; + add_id_to_freelist(priv_req_id, mount->freelist); +} + +static void handle_aio_events(struct fs_mount *mount) +{ + int fd, ret, count, i, notify; + evtchn_port_t port; + /* AIO control block for the evtchn file destriptor */ + struct aiocb evtchn_cb; + const struct aiocb * cb_list[mount->nr_entries]; + int request_ids[mount->nr_entries]; + + /* Prepare the AIO control block for evtchn */ + fd = xc_evtchn_fd(mount->evth); + bzero(&evtchn_cb, sizeof(struct aiocb)); + evtchn_cb.aio_fildes = fd; + evtchn_cb.aio_nbytes = sizeof(port); + evtchn_cb.aio_buf = &port; + assert(aio_read(&evtchn_cb) == 0); + +wait_again: + /* Create list of active AIO requests */ + count = 0; + for(i=0; inr_entries; i++) + if(mount->requests[i].active) + { + cb_list[count] = &mount->requests[i].aiocb; + request_ids[count] = i; + count++; + } + /* Add the event channel at the end of the list. Event channel needs to be + * handled last as it exits this function. */ + cb_list[count] = &evtchn_cb; + request_ids[count] = -1; + count++; + + /* Block till an AIO requset finishes, or we get an event */ + while(1) { + int ret = aio_suspend(cb_list, count, NULL); + if (!ret) + break; + assert(errno == EINTR); + } + for(i=0; i= 0) + dispatch_response(mount, request_ids[i]); + else + goto read_event_channel; + } + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&mount->ring, notify); + printf("Pushed responces and notify=%d\n", notify); + if(notify) + xc_evtchn_notify(mount->evth, mount->local_evtchn); + + goto wait_again; + +read_event_channel: + assert(aio_return(&evtchn_cb) == sizeof(evtchn_port_t)); + assert(xc_evtchn_unmask(mount->evth, mount->local_evtchn) >= 0); +} + + +static void allocate_request_array(struct fs_mount *mount) +{ + int i, nr_entries = mount->nr_entries; + struct fs_request *requests; + unsigned short *freelist; + + requests = malloc(sizeof(struct fs_request) *nr_entries); + freelist = malloc(sizeof(unsigned short) * (nr_entries + 1)); + memset(requests, 0, sizeof(struct fs_request) * nr_entries); + memset(freelist, 0, sizeof(unsigned short) * (nr_entries + 1)); + for(i=0; i< nr_entries; i++) + { + requests[i].active = 0; + add_id_to_freelist(i, freelist); + } + mount->requests = requests; + mount->freelist = freelist; +} + + +static void *handle_mount(void *data) +{ + int more, notify; + struct fs_mount *mount = (struct fs_mount *)data; + + printf("Starting a thread for mount: %d\n", mount->mount_id); + allocate_request_array(mount); + + for(;;) + { + int nr_consumed=0; + RING_IDX cons, rp; + struct fsif_request *req; + + handle_aio_events(mount); +moretodo: + rp = mount->ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + + while ((cons = mount->ring.req_cons) != rp) + { + int i; + struct fs_op *op; + + printf("Got a request at %d (of %d)\n", + cons, RING_SIZE(&mount->ring)); + req = RING_GET_REQUEST(&mount->ring, cons); + printf("Request type=%d\n", req->type); + for(i=0;;i++) + { + op = fsops[i]; + if(op == NULL) + { + /* We've reached the end of the array, no appropirate + * handler found. Warn, ignore and continue. */ + printf("WARN: Unknown request type: %d\n", req->type); + mount->ring.req_cons++; + break; + } + if(op->type == req->type) + { + /* There needs to be a dispatch handler */ + assert(op->dispatch_handler != NULL); + op->dispatch_handler(mount, req); + break; + } + } + + nr_consumed++; + } + printf("Backend consumed: %d requests\n", nr_consumed); + RING_FINAL_CHECK_FOR_REQUESTS(&mount->ring, more); + if(more) goto moretodo; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&mount->ring, notify); + printf("Pushed responces and notify=%d\n", notify); + if(notify) + xc_evtchn_notify(mount->evth, mount->local_evtchn); + } + + printf("Destroying thread for mount: %d\n", mount->mount_id); + xc_gnttab_munmap(mount->gnth, mount->ring.sring, 1); + xc_gnttab_close(mount->gnth); + xc_evtchn_unbind(mount->evth, mount->local_evtchn); + xc_evtchn_close(mount->evth); + free(mount->frontend); + pthread_exit(NULL); +} + +static void handle_connection(int frontend_dom_id, int export_id, char *frontend) +{ + struct fs_mount *mount; + struct fs_export *export; + int evt_port; + pthread_t handling_thread; + struct fsif_sring *sring; + uint32_t dom_ids[MAX_RING_SIZE]; + int i; + + printf("Handling connection from dom=%d, for export=%d\n", + frontend_dom_id, export_id); + /* Try to find the export on the list */ + export = fs_exports; + while(export) + { + if(export->export_id == export_id) + break; + export = export->next; + } + if(!export) + { + printf("Could not find the export (the id is unknown).\n"); + return; + } + + mount = (struct fs_mount*)malloc(sizeof(struct fs_mount)); + mount->dom_id = frontend_dom_id; + mount->export = export; + mount->mount_id = mount_id++; + xenbus_read_mount_request(mount, frontend); + printf("Frontend found at: %s (gref=%d, evtchn=%d)\n", + mount->frontend, mount->grefs[0], mount->remote_evtchn); + xenbus_write_backend_node(mount); + mount->evth = -1; + mount->evth = xc_evtchn_open(); + assert(mount->evth != -1); + mount->local_evtchn = -1; + mount->local_evtchn = xc_evtchn_bind_interdomain(mount->evth, + mount->dom_id, + mount->remote_evtchn); + assert(mount->local_evtchn != -1); + mount->gnth = -1; + mount->gnth = xc_gnttab_open(); + assert(mount->gnth != -1); + for(i=0; ishared_ring_size; i++) + dom_ids[i] = mount->dom_id; + sring = xc_gnttab_map_grant_refs(mount->gnth, + mount->shared_ring_size, + dom_ids, + mount->grefs, + PROT_READ | PROT_WRITE); + + BACK_RING_INIT(&mount->ring, sring, mount->shared_ring_size * XC_PAGE_SIZE); + mount->nr_entries = mount->ring.nr_ents; + for (i = 0; i < MAX_FDS; i++) + mount->fds[i] = -1; + xenbus_write_backend_ready(mount); + + pthread_create(&handling_thread, NULL, &handle_mount, mount); +} + +static void await_connections(void) +{ + int fd, ret, dom_id, export_id; + fd_set fds; + char **watch_paths; + unsigned int len; + char d; + + assert(xsh != NULL); + fd = xenbus_get_watch_fd(); + /* Infinite watch loop */ + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + ret = select(fd+1, &fds, NULL, NULL, NULL); + assert(ret == 1); + watch_paths = xs_read_watch(xsh, &len); + assert(len == 2); + assert(strcmp(watch_paths[1], "conn-watch") == 0); + dom_id = -1; + export_id = -1; + d = 0; + printf("Path changed %s\n", watch_paths[0]); + sscanf(watch_paths[0], WATCH_NODE"/%d/%d/fronten%c", + &dom_id, &export_id, &d); + if((dom_id >= 0) && (export_id >= 0) && d == 'd') { + char *frontend = xs_read(xsh, XBT_NULL, watch_paths[0], NULL); + if (frontend) { + handle_connection(dom_id, export_id, frontend); + xs_rm(xsh, XBT_NULL, watch_paths[0]); + } + } +next_select: + printf("Awaiting next connection.\n"); + /* TODO - we need to figure out what to free */ + free(watch_paths); + } while (1); +} + +static struct fs_export* create_export(char *name, char *export_path) +{ + struct fs_export *curr_export, **last_export; + + /* Create export structure */ + curr_export = (struct fs_export *)malloc(sizeof(struct fs_export)); + curr_export->name = name; + curr_export->export_path = export_path; + curr_export->export_id = export_id++; + /* Thread it onto the list */ + curr_export->next = NULL; + last_export = &fs_exports; + while(*last_export) + last_export = &((*last_export)->next); + *last_export = curr_export; + + return curr_export; +} + + +int main(void) +{ + struct fs_export *export; + + /* Open the connection to XenStore first */ + xsh = xs_domain_open(); + assert(xsh != NULL); + xs_rm(xsh, XBT_NULL, ROOT_NODE); + /* Create watch node */ + xenbus_create_request_node(); + + /* Create & register the default export */ + export = create_export("default", "/exports"); + xenbus_register_export(export); + + await_connections(); + /* Close the connection to XenStore when we are finished with everything */ + xs_daemon_close(xsh); +#if 0 + int xc_handle; + char *shared_page; + int prot = PROT_READ | PROT_WRITE; + + xc_handle = xc_gnttab_open(); + printf("Main fn.\n"); + + shared_page = xc_gnttab_map_grant_ref(xc_handle, + 7, + 2047, + prot); + + shared_page[20] = '\0'; + printf("Current content of the page = %s\n", shared_page); + sprintf(shared_page, "%s", "Haha dirty page now! Very bad page."); + xc_gnttab_munmap(xc_handle, shared_page, 1); + xc_gnttab_close(xc_handle); + unrelated next line, saved for later convinience + xc_evtchn_notify(mount->evth, mount->local_evtchn); +#endif +} diff --git a/tools/fs-back/fs-backend.h b/tools/fs-back/fs-backend.h new file mode 100644 index 0000000..b2a6be6 --- /dev/null +++ b/tools/fs-back/fs-backend.h @@ -0,0 +1,91 @@ +#ifndef __LIB_FS_BACKEND__ +#define __LIB_FS_BACKEND__ + +#include +#include +#include +#include +#include +#include + +#define ROOT_NODE "backend/vfs" +#define EXPORTS_SUBNODE "exports" +#define EXPORTS_NODE ROOT_NODE"/"EXPORTS_SUBNODE +#define WATCH_NODE EXPORTS_NODE"/requests" +#define MAX_FDS 16 +#define MAX_RING_SIZE 16 + +struct fs_export +{ + int export_id; + char *export_path; + char *name; + struct fs_export *next; +}; + +struct fs_request +{ + int active; + void *page; /* Pointer to mapped grant */ + int count; + struct fsif_request req_shadow; + struct aiocb aiocb; +}; + + +struct fs_mount +{ + struct fs_export *export; + int dom_id; + char *frontend; + int mount_id; /* = backend id */ + grant_ref_t grefs[MAX_RING_SIZE]; + evtchn_port_t remote_evtchn; + int evth; /* Handle to the event channel */ + evtchn_port_t local_evtchn; + int gnth; + int shared_ring_size; /* in pages */ + struct fsif_back_ring ring; + int nr_entries; + struct fs_request *requests; + unsigned short *freelist; + int fds[MAX_FDS]; +}; + + +/* Handle to XenStore driver */ +extern struct xs_handle *xsh; + +bool xenbus_create_request_node(void); +int xenbus_register_export(struct fs_export *export); +int xenbus_get_watch_fd(void); +void xenbus_read_mount_request(struct fs_mount *mount, char *frontend); +void xenbus_write_backend_node(struct fs_mount *mount); +void xenbus_write_backend_ready(struct fs_mount *mount); + +/* File operations, implemented in fs-ops.c */ +struct fs_op +{ + int type; /* Type of request (from fsif.h) this handlers + are responsible for */ + void (*dispatch_handler)(struct fs_mount *mount, struct fsif_request *req); + void (*response_handler)(struct fs_mount *mount, struct fs_request *req); +}; + +/* This NULL terminated array of all file requests handlers */ +extern struct fs_op *fsops[]; + +static inline void add_id_to_freelist(unsigned int id,unsigned short* freelist) +{ + freelist[id + 1] = freelist[0]; + freelist[0] = id; +} + +static inline unsigned short get_id_from_freelist(unsigned short* freelist) +{ + unsigned int id = freelist[0]; + freelist[0] = freelist[id + 1]; + return id; +} + +#endif /* __LIB_FS_BACKEND__ */ diff --git a/tools/fs-back/fs-ops.c b/tools/fs-back/fs-ops.c new file mode 100644 index 0000000..424b054 --- /dev/null +++ b/tools/fs-back/fs-ops.c @@ -0,0 +1,732 @@ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs-backend.h" + +/* For debugging only */ +#include +#include + + +#define BUFFER_SIZE 1024 + + +static unsigned short get_request(struct fs_mount *mount, struct fsif_request *req) +{ + unsigned short id = get_id_from_freelist(mount->freelist); + + printf("Private Request id: %d\n", id); + memcpy(&mount->requests[id].req_shadow, req, sizeof(struct fsif_request)); + mount->requests[id].active = 1; + + return id; +} + +static int get_fd(struct fs_mount *mount) +{ + int i; + + for (i = 0; i < MAX_FDS; i++) + if (mount->fds[i] == -1) + return i; + return -1; +} + + +static void dispatch_file_open(struct fs_mount *mount, struct fsif_request *req) +{ + char *file_name, full_path[BUFFER_SIZE]; + int fd; + struct timeval tv1, tv2; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + printf("Dispatching file open operation (gref=%d).\n", req->u.fopen.gref); + /* Read the request, and open file */ + file_name = xc_gnttab_map_grant_ref(mount->gnth, + mount->dom_id, + req->u.fopen.gref, + PROT_READ); + + req_id = req->id; + printf("File open issued for %s\n", file_name); + assert(BUFFER_SIZE > + strlen(file_name) + strlen(mount->export->export_path) + 1); + snprintf(full_path, sizeof(full_path), "%s/%s", + mount->export->export_path, file_name); + assert(xc_gnttab_munmap(mount->gnth, file_name, 1) == 0); + printf("Issuing open for %s\n", full_path); + fd = get_fd(mount); + if (fd >= 0) { + int real_fd = open(full_path, O_RDWR); + if (real_fd < 0) + fd = -1; + else + { + mount->fds[fd] = real_fd; + printf("Got FD: %d for real %d\n", fd, real_fd); + } + } + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)fd; +} + +static void dispatch_file_close(struct fs_mount *mount, struct fsif_request *req) +{ + int ret; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + printf("Dispatching file close operation (fd=%d).\n", req->u.fclose.fd); + + req_id = req->id; + if (req->u.fclose.fd < MAX_FDS) { + int fd = mount->fds[req->u.fclose.fd]; + ret = close(fd); + mount->fds[req->u.fclose.fd] = -1; + } else + ret = -1; + printf("Got ret: %d\n", ret); + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + +#define MAX_GNTS 16 +static void dispatch_file_read(struct fs_mount *mount, struct fsif_request *req) +{ + void *buf; + int fd, i, count; + uint16_t req_id; + unsigned short priv_id; + struct fs_request *priv_req; + + /* Read the request */ + assert(req->u.fread.len > 0); + count = (req->u.fread.len - 1) / XC_PAGE_SIZE + 1; + assert(count <= FSIF_NR_READ_GNTS); + buf = xc_gnttab_map_domain_grant_refs(mount->gnth, + count, + mount->dom_id, + req->u.fread.grefs, + PROT_WRITE); + + req_id = req->id; + printf("File read issued for FD=%d (len=%"PRIu64", offest=%"PRIu64")\n", + req->u.fread.fd, req->u.fread.len, req->u.fread.offset); + + if (req->u.fread.fd < MAX_FDS) + fd = mount->fds[req->u.fread.fd]; + else + fd = -1; + + priv_id = get_request(mount, req); + printf("Private id is: %d\n", priv_id); + priv_req = &mount->requests[priv_id]; + priv_req->page = buf; + priv_req->count = count; + + /* Dispatch AIO read request */ + bzero(&priv_req->aiocb, sizeof(struct aiocb)); + priv_req->aiocb.aio_fildes = fd; + priv_req->aiocb.aio_nbytes = req->u.fread.len; + priv_req->aiocb.aio_offset = req->u.fread.offset; + priv_req->aiocb.aio_buf = buf; + assert(aio_read(&priv_req->aiocb) >= 0); + +out: + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; +} + +static void end_file_read(struct fs_mount *mount, struct fs_request *priv_req) +{ + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + /* Release the grant */ + assert(xc_gnttab_munmap(mount->gnth, + priv_req->page, + priv_req->count) == 0); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + req_id = priv_req->req_shadow.id; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)aio_return(&priv_req->aiocb); +} + +static void dispatch_file_write(struct fs_mount *mount, struct fsif_request *req) +{ + void *buf; + int fd, count, i; + uint16_t req_id; + unsigned short priv_id; + struct fs_request *priv_req; + + /* Read the request */ + assert(req->u.fwrite.len > 0); + count = (req->u.fwrite.len - 1) / XC_PAGE_SIZE + 1; + assert(count <= FSIF_NR_WRITE_GNTS); + buf = xc_gnttab_map_domain_grant_refs(mount->gnth, + count, + mount->dom_id, + req->u.fwrite.grefs, + PROT_READ); + + req_id = req->id; + printf("File write issued for FD=%d (len=%"PRIu64", offest=%"PRIu64")\n", + req->u.fwrite.fd, req->u.fwrite.len, req->u.fwrite.offset); + + if (req->u.fwrite.fd < MAX_FDS) + fd = mount->fds[req->u.fwrite.fd]; + else + fd = -1; + + priv_id = get_request(mount, req); + printf("Private id is: %d\n", priv_id); + priv_req = &mount->requests[priv_id]; + priv_req->page = buf; + priv_req->count = count; + + /* Dispatch AIO write request */ + bzero(&priv_req->aiocb, sizeof(struct aiocb)); + priv_req->aiocb.aio_fildes = fd; + priv_req->aiocb.aio_nbytes = req->u.fwrite.len; + priv_req->aiocb.aio_offset = req->u.fwrite.offset; + priv_req->aiocb.aio_buf = buf; + assert(aio_write(&priv_req->aiocb) >= 0); + + + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; +} + +static void end_file_write(struct fs_mount *mount, struct fs_request *priv_req) +{ + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + /* Release the grant */ + assert(xc_gnttab_munmap(mount->gnth, + priv_req->page, + priv_req->count) == 0); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + req_id = priv_req->req_shadow.id; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)aio_return(&priv_req->aiocb); +} + +static void dispatch_stat(struct fs_mount *mount, struct fsif_request *req) +{ + struct fsif_stat_response *buf; + struct stat stat; + int fd, ret; + uint16_t req_id; + RING_IDX rsp_idx; + fsif_response_t *rsp; + + req_id = req->id; + if (req->u.fstat.fd < MAX_FDS) + fd = mount->fds[req->u.fstat.fd]; + else + fd = -1; + + printf("File stat issued for FD=%d\n", req->u.fstat.fd); + + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + /* Stat, and create the response */ + ret = fstat(fd, &stat); + printf("Mode=%o, uid=%d, a_time=%ld\n", + stat.st_mode, stat.st_uid, (long)stat.st_atime); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->fstat.stat_ret = (uint32_t)ret; + rsp->fstat.stat_mode = stat.st_mode; + rsp->fstat.stat_uid = stat.st_uid; + rsp->fstat.stat_gid = stat.st_gid; +#ifdef BLKGETSIZE + if (S_ISBLK(stat.st_mode)) { + unsigned long sectors; + if (ioctl(fd, BLKGETSIZE, §ors)) { + perror("getting device size\n"); + rsp->fstat.stat_size = 0; + } else + rsp->fstat.stat_size = sectors << 9; + } else +#endif + rsp->fstat.stat_size = stat.st_size; + rsp->fstat.stat_atime = stat.st_atime; + rsp->fstat.stat_mtime = stat.st_mtime; + rsp->fstat.stat_ctime = stat.st_ctime; +} + + +static void dispatch_truncate(struct fs_mount *mount, struct fsif_request *req) +{ + int fd, ret; + uint16_t req_id; + RING_IDX rsp_idx; + fsif_response_t *rsp; + int64_t length; + + req_id = req->id; + length = req->u.ftruncate.length; + printf("File truncate issued for FD=%d, length=%"PRId64"\n", req->u.ftruncate.fd, length); + + if (req->u.ftruncate.fd < MAX_FDS) + fd = mount->fds[req->u.ftruncate.fd]; + else + fd = -1; + + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + /* Stat, and create the response */ + ret = ftruncate(fd, length); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + +static void dispatch_remove(struct fs_mount *mount, struct fsif_request *req) +{ + char *file_name, full_path[BUFFER_SIZE]; + int ret; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + printf("Dispatching remove operation (gref=%d).\n", req->u.fremove.gref); + /* Read the request, and open file */ + file_name = xc_gnttab_map_grant_ref(mount->gnth, + mount->dom_id, + req->u.fremove.gref, + PROT_READ); + + req_id = req->id; + printf("File remove issued for %s\n", file_name); + assert(BUFFER_SIZE > + strlen(file_name) + strlen(mount->export->export_path) + 1); + snprintf(full_path, sizeof(full_path), "%s/%s", + mount->export->export_path, file_name); + assert(xc_gnttab_munmap(mount->gnth, file_name, 1) == 0); + printf("Issuing remove for %s\n", full_path); + ret = remove(full_path); + printf("Got ret: %d\n", ret); + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + + +static void dispatch_rename(struct fs_mount *mount, struct fsif_request *req) +{ + char *buf, *old_file_name, *new_file_name; + char old_full_path[BUFFER_SIZE], new_full_path[BUFFER_SIZE]; + int ret; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + printf("Dispatching rename operation (gref=%d).\n", req->u.fremove.gref); + /* Read the request, and open file */ + buf = xc_gnttab_map_grant_ref(mount->gnth, + mount->dom_id, + req->u.frename.gref, + PROT_READ); + + req_id = req->id; + old_file_name = buf + req->u.frename.old_name_offset; + new_file_name = buf + req->u.frename.new_name_offset; + printf("File rename issued for %s -> %s (buf=%s)\n", + old_file_name, new_file_name, buf); + assert(BUFFER_SIZE > + strlen(old_file_name) + strlen(mount->export->export_path) + 1); + assert(BUFFER_SIZE > + strlen(new_file_name) + strlen(mount->export->export_path) + 1); + snprintf(old_full_path, sizeof(old_full_path), "%s/%s", + mount->export->export_path, old_file_name); + snprintf(new_full_path, sizeof(new_full_path), "%s/%s", + mount->export->export_path, new_file_name); + assert(xc_gnttab_munmap(mount->gnth, buf, 1) == 0); + printf("Issuing rename for %s -> %s\n", old_full_path, new_full_path); + ret = rename(old_full_path, new_full_path); + printf("Got ret: %d\n", ret); + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + + +static void dispatch_create(struct fs_mount *mount, struct fsif_request *req) +{ + char *file_name, full_path[BUFFER_SIZE]; + int ret; + int8_t directory; + int32_t mode; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + printf("Dispatching file create operation (gref=%d).\n", req->u.fcreate.gref); + /* Read the request, and create file/directory */ + mode = req->u.fcreate.mode; + directory = req->u.fcreate.directory; + file_name = xc_gnttab_map_grant_ref(mount->gnth, + mount->dom_id, + req->u.fcreate.gref, + PROT_READ); + + req_id = req->id; + printf("File create issued for %s\n", file_name); + assert(BUFFER_SIZE > + strlen(file_name) + strlen(mount->export->export_path) + 1); + snprintf(full_path, sizeof(full_path), "%s/%s", + mount->export->export_path, file_name); + assert(xc_gnttab_munmap(mount->gnth, file_name, 1) == 0); + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + if(directory) + { + printf("Issuing create for directory: %s\n", full_path); + ret = mkdir(full_path, mode); + } + else + { + printf("Issuing create for file: %s\n", full_path); + ret = get_fd(mount); + if (ret >= 0) { + int real_fd = creat(full_path, mode); + if (real_fd < 0) + ret = -1; + else + { + mount->fds[ret] = real_fd; + printf("Got FD: %d for real %d\n", ret, real_fd); + } + } + } + printf("Got ret %d (errno=%d)\n", ret, errno); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + +static void dispatch_list(struct fs_mount *mount, struct fsif_request *req) +{ + char *file_name, *buf, full_path[BUFFER_SIZE]; + uint32_t offset, nr_files, error_code; + uint64_t ret_val; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + DIR *dir; + struct dirent *dirent = NULL; + + printf("Dispatching list operation (gref=%d).\n", req->u.flist.gref); + /* Read the request, and list directory */ + offset = req->u.flist.offset; + buf = file_name = xc_gnttab_map_grant_ref(mount->gnth, + mount->dom_id, + req->u.flist.gref, + PROT_READ | PROT_WRITE); + + req_id = req->id; + printf("Dir list issued for %s\n", file_name); + assert(BUFFER_SIZE > + strlen(file_name) + strlen(mount->export->export_path) + 1); + snprintf(full_path, sizeof(full_path), "%s/%s", + mount->export->export_path, file_name); + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + ret_val = 0; + nr_files = 0; + dir = opendir(full_path); + if(dir == NULL) + { + error_code = errno; + goto error_out; + } + /* Skip offset dirs */ + dirent = readdir(dir); + while(offset-- > 0 && dirent != NULL) + dirent = readdir(dir); + /* If there was any error with reading the directory, errno will be set */ + error_code = errno; + /* Copy file names of the remaining non-NULL dirents into buf */ + assert(NAME_MAX < XC_PAGE_SIZE >> 1); + while(dirent != NULL && + (XC_PAGE_SIZE - ((unsigned long)buf & XC_PAGE_MASK) > NAME_MAX)) + { + int curr_length = strlen(dirent->d_name) + 1; + + memcpy(buf, dirent->d_name, curr_length); + buf += curr_length; + dirent = readdir(dir); + error_code = errno; + nr_files++; + } +error_out: + ret_val = ((nr_files << NR_FILES_SHIFT) & NR_FILES_MASK) | + ((error_code << ERROR_SHIFT) & ERROR_MASK) | + (dirent != NULL ? HAS_MORE_FLAG : 0); + assert(xc_gnttab_munmap(mount->gnth, file_name, 1) == 0); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = ret_val; +} + +static void dispatch_chmod(struct fs_mount *mount, struct fsif_request *req) +{ + int fd, ret; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + int32_t mode; + + printf("Dispatching file chmod operation (fd=%d, mode=%o).\n", + req->u.fchmod.fd, req->u.fchmod.mode); + req_id = req->id; + if (req->u.fchmod.fd < MAX_FDS) + fd = mount->fds[req->u.fchmod.fd]; + else + fd = -1; + + mode = req->u.fchmod.mode; + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + ret = fchmod(fd, mode); + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + +static void dispatch_fs_space(struct fs_mount *mount, struct fsif_request *req) +{ + char *file_name, full_path[BUFFER_SIZE]; + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + struct statvfs stat; + int64_t ret; + + printf("Dispatching fs space operation (gref=%d).\n", req->u.fspace.gref); + /* Read the request, and open file */ + file_name = xc_gnttab_map_grant_ref(mount->gnth, + mount->dom_id, + req->u.fspace.gref, + PROT_READ); + + req_id = req->id; + printf("Fs space issued for %s\n", file_name); + assert(BUFFER_SIZE > + strlen(file_name) + strlen(mount->export->export_path) + 1); + snprintf(full_path, sizeof(full_path), "%s/%s", + mount->export->export_path, file_name); + assert(xc_gnttab_munmap(mount->gnth, file_name, 1) == 0); + printf("Issuing fs space for %s\n", full_path); + ret = statvfs(full_path, &stat); + if(ret >= 0) + ret = stat.f_bsize * stat.f_bfree; + + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; + + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)ret; +} + +static void dispatch_file_sync(struct fs_mount *mount, struct fsif_request *req) +{ + int fd; + uint16_t req_id; + unsigned short priv_id; + struct fs_request *priv_req; + + req_id = req->id; + if (req->u.fsync.fd < MAX_FDS) + fd = mount->fds[req->u.fsync.fd]; + else + fd = -1; + + printf("File sync issued for FD=%d\n", req->u.fsync.fd); + + priv_id = get_request(mount, req); + printf("Private id is: %d\n", priv_id); + priv_req = &mount->requests[priv_id]; + + /* Dispatch AIO read request */ + bzero(&priv_req->aiocb, sizeof(struct aiocb)); + priv_req->aiocb.aio_fildes = fd; + assert(aio_fsync(O_SYNC, &priv_req->aiocb) >= 0); + + + /* We can advance the request consumer index, from here on, the request + * should not be used (it may be overrinden by a response) */ + mount->ring.req_cons++; +} + +static void end_file_sync(struct fs_mount *mount, struct fs_request *priv_req) +{ + RING_IDX rsp_idx; + fsif_response_t *rsp; + uint16_t req_id; + + /* Get a response from the ring */ + rsp_idx = mount->ring.rsp_prod_pvt++; + req_id = priv_req->req_shadow.id; + printf("Writing response at: idx=%d, id=%d\n", rsp_idx, req_id); + rsp = RING_GET_RESPONSE(&mount->ring, rsp_idx); + rsp->id = req_id; + rsp->ret_val = (uint64_t)aio_return(&priv_req->aiocb); +} + +struct fs_op fopen_op = {.type = REQ_FILE_OPEN, + .dispatch_handler = dispatch_file_open, + .response_handler = NULL}; +struct fs_op fclose_op = {.type = REQ_FILE_CLOSE, + .dispatch_handler = dispatch_file_close, + .response_handler = NULL}; +struct fs_op fread_op = {.type = REQ_FILE_READ, + .dispatch_handler = dispatch_file_read, + .response_handler = end_file_read}; +struct fs_op fwrite_op = {.type = REQ_FILE_WRITE, + .dispatch_handler = dispatch_file_write, + .response_handler = end_file_write}; +struct fs_op fstat_op = {.type = REQ_STAT, + .dispatch_handler = dispatch_stat, + .response_handler = NULL}; +struct fs_op ftruncate_op = {.type = REQ_FILE_TRUNCATE, + .dispatch_handler = dispatch_truncate, + .response_handler = NULL}; +struct fs_op fremove_op = {.type = REQ_REMOVE, + .dispatch_handler = dispatch_remove, + .response_handler = NULL}; +struct fs_op frename_op = {.type = REQ_RENAME, + .dispatch_handler = dispatch_rename, + .response_handler = NULL}; +struct fs_op fcreate_op = {.type = REQ_CREATE, + .dispatch_handler = dispatch_create, + .response_handler = NULL}; +struct fs_op flist_op = {.type = REQ_DIR_LIST, + .dispatch_handler = dispatch_list, + .response_handler = NULL}; +struct fs_op fchmod_op = {.type = REQ_CHMOD, + .dispatch_handler = dispatch_chmod, + .response_handler = NULL}; +struct fs_op fspace_op = {.type = REQ_FS_SPACE, + .dispatch_handler = dispatch_fs_space, + .response_handler = NULL}; +struct fs_op fsync_op = {.type = REQ_FILE_SYNC, + .dispatch_handler = dispatch_file_sync, + .response_handler = end_file_sync}; + + +struct fs_op *fsops[] = {&fopen_op, + &fclose_op, + &fread_op, + &fwrite_op, + &fstat_op, + &ftruncate_op, + &fremove_op, + &frename_op, + &fcreate_op, + &flist_op, + &fchmod_op, + &fspace_op, + &fsync_op, + NULL}; diff --git a/tools/fs-back/fs-xenbus.c b/tools/fs-back/fs-xenbus.c new file mode 100644 index 0000000..6a86e24 --- /dev/null +++ b/tools/fs-back/fs-xenbus.c @@ -0,0 +1,189 @@ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs-backend.h" + + +static bool xenbus_printf(struct xs_handle *xsh, + xs_transaction_t xbt, + char* node, + char* path, + char* fmt, + ...) +{ + char fullpath[1024]; + char val[1024]; + va_list args; + + va_start(args, fmt); + snprintf(fullpath, sizeof(fullpath), "%s/%s", node, path); + vsnprintf(val, sizeof(val), fmt, args); + va_end(args); + printf("xenbus_printf (%s) <= %s.\n", fullpath, val); + + return xs_write(xsh, xbt, fullpath, val, strlen(val)); +} + +bool xenbus_create_request_node(void) +{ + bool ret; + struct xs_permissions perms; + + assert(xsh != NULL); + xs_rm(xsh, XBT_NULL, WATCH_NODE); + ret = xs_mkdir(xsh, XBT_NULL, WATCH_NODE); + if (!ret) + return false; + + perms.id = 0; + perms.perms = XS_PERM_WRITE; + ret = xs_set_permissions(xsh, XBT_NULL, WATCH_NODE, &perms, 1); + + return ret; +} + +int xenbus_register_export(struct fs_export *export) +{ + xs_transaction_t xst = 0; + char node[1024]; + struct xs_permissions perms; + + assert(xsh != NULL); + if(xsh == NULL) + { + printf("Could not open connection to xenbus deamon.\n"); + goto error_exit; + } + printf("Connection to the xenbus deamon opened successfully.\n"); + + /* Start transaction */ + xst = xs_transaction_start(xsh); + if(xst == 0) + { + printf("Could not start a transaction.\n"); + goto error_exit; + } + printf("XS transaction is %d\n", xst); + + /* Create node string */ + snprintf(node, sizeof(node), "%s/%d", EXPORTS_NODE, export->export_id); + /* Remove old export (if exists) */ + xs_rm(xsh, xst, node); + + if(!xenbus_printf(xsh, xst, node, "name", "%s", export->name)) + { + printf("Could not write the export node.\n"); + goto error_exit; + } + + /* People need to be able to read our export */ + perms.id = 0; + perms.perms = XS_PERM_READ; + if(!xs_set_permissions(xsh, xst, EXPORTS_NODE, &perms, 1)) + { + printf("Could not set permissions on the export node.\n"); + goto error_exit; + } + + xs_transaction_end(xsh, xst, 0); + return 0; + +error_exit: + if(xst != 0) + xs_transaction_end(xsh, xst, 1); + return -1; +} + +int xenbus_get_watch_fd(void) +{ + int res; + assert(xsh != NULL); + res = xs_watch(xsh, WATCH_NODE, "conn-watch"); + assert(res); + return xs_fileno(xsh); +} + +void xenbus_read_mount_request(struct fs_mount *mount, char *frontend) +{ + char node[1024]; + char *s; + int i; + + assert(xsh != NULL); +#if 0 + snprintf(node, sizeof(node), WATCH_NODE"/%d/%d/frontend", + mount->dom_id, mount->export->export_id); + frontend = xs_read(xsh, XBT_NULL, node, NULL); +#endif + mount->frontend = frontend; + snprintf(node, sizeof(node), "%s/state", frontend); + s = xs_read(xsh, XBT_NULL, node, NULL); + assert(strcmp(s, STATE_READY) == 0); + free(s); + snprintf(node, sizeof(node), "%s/ring-size", frontend); + s = xs_read(xsh, XBT_NULL, node, NULL); + mount->shared_ring_size = atoi(s); + assert(mount->shared_ring_size <= MAX_RING_SIZE); + free(s); + for(i=0; ishared_ring_size; i++) + { + snprintf(node, sizeof(node), "%s/ring-ref-%d", frontend, i); + s = xs_read(xsh, XBT_NULL, node, NULL); + mount->grefs[i] = atoi(s); + free(s); + } + snprintf(node, sizeof(node), "%s/event-channel", frontend); + s = xs_read(xsh, XBT_NULL, node, NULL); + mount->remote_evtchn = atoi(s); + free(s); +} + +/* Small utility function to figure out our domain id */ +static int get_self_id(void) +{ + char *dom_id; + int ret; + + assert(xsh != NULL); + dom_id = xs_read(xsh, XBT_NULL, "domid", NULL); + sscanf(dom_id, "%d", &ret); + free(dom_id); + + return ret; +} + + +void xenbus_write_backend_node(struct fs_mount *mount) +{ + char node[1024], backend_node[1024]; + int self_id; + + assert(xsh != NULL); + self_id = get_self_id(); + printf("Our own dom_id=%d\n", self_id); + snprintf(node, sizeof(node), "%s/backend", mount->frontend); + snprintf(backend_node, sizeof(backend_node), "/local/domain/%d/"ROOT_NODE"/%d", + self_id, mount->mount_id); + xs_write(xsh, XBT_NULL, node, backend_node, strlen(backend_node)); + + snprintf(node, sizeof(node), ROOT_NODE"/%d/state", mount->mount_id); + xs_write(xsh, XBT_NULL, node, STATE_INITIALISED, strlen(STATE_INITIALISED)); +} + +void xenbus_write_backend_ready(struct fs_mount *mount) +{ + char node[1024]; + int self_id; + + assert(xsh != NULL); + self_id = get_self_id(); + snprintf(node, sizeof(node), ROOT_NODE"/%d/state", mount->mount_id); + xs_write(xsh, XBT_NULL, node, STATE_READY, strlen(STATE_READY)); +} + diff --git a/tools/include/Makefile b/tools/include/Makefile new file mode 100644 index 0000000..db0b760 --- /dev/null +++ b/tools/include/Makefile @@ -0,0 +1,48 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +.PHONY: all +all: xen-foreign xen/.dir + +.PHONY: xen-foreign +xen-foreign: + $(MAKE) -C xen-foreign + +xen/.dir: + @rm -rf xen + mkdir xen + ln -sf ../$(XEN_ROOT)/xen/include/public/COPYING xen + ln -sf $(addprefix ../,$(wildcard $(XEN_ROOT)/xen/include/public/*.h)) xen + ln -sf $(addprefix ../$(XEN_ROOT)/xen/include/public/,arch-ia64 arch-x86 hvm io xsm) xen + ln -sf ../xen-sys/$(XEN_OS) xen/sys + ln -s ../xen-foreign xen/foreign + touch $@ + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/arch-ia64 + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/arch-ia64/hvm + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/arch-x86 + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/arch-x86/hvm + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/foreign + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/hvm + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/io + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/sys + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/xsm + $(INSTALL_DATA) xen/COPYING $(DESTDIR)$(INCLUDEDIR)/xen + $(INSTALL_DATA) xen/*.h $(DESTDIR)$(INCLUDEDIR)/xen + $(INSTALL_DATA) xen/arch-ia64/*.h $(DESTDIR)$(INCLUDEDIR)/xen/arch-ia64 + $(INSTALL_DATA) xen/arch-ia64/hvm/*.h $(DESTDIR)$(INCLUDEDIR)/xen/arch-ia64/hvm + $(INSTALL_DATA) xen/arch-x86/*.h $(DESTDIR)$(INCLUDEDIR)/xen/arch-x86 + $(INSTALL_DATA) xen/arch-x86/hvm/*.h $(DESTDIR)$(INCLUDEDIR)/xen/arch-x86/hvm + $(INSTALL_DATA) xen/foreign/*.h $(DESTDIR)$(INCLUDEDIR)/xen/foreign + $(INSTALL_DATA) xen/hvm/*.h $(DESTDIR)$(INCLUDEDIR)/xen/hvm + $(INSTALL_DATA) xen/io/*.h $(DESTDIR)$(INCLUDEDIR)/xen/io + $(INSTALL_DATA) xen/sys/*.h $(DESTDIR)$(INCLUDEDIR)/xen/sys + $(INSTALL_DATA) xen/xsm/*.h $(DESTDIR)$(INCLUDEDIR)/xen/xsm + +.PHONY: clean +clean: + rm -rf xen + $(MAKE) -C xen-foreign clean + diff --git a/tools/include/xen-foreign/Makefile b/tools/include/xen-foreign/Makefile new file mode 100644 index 0000000..0b8ed92 --- /dev/null +++ b/tools/include/xen-foreign/Makefile @@ -0,0 +1,35 @@ +XEN_ROOT=../../.. +include $(XEN_ROOT)/Config.mk + +ROOT = $(XEN_ROOT)/xen/include/public + +architectures := x86_32 x86_64 ia64 +headers := $(patsubst %, %.h, $(architectures)) + +.PHONY: all clean check-headers +all: $(headers) check-headers + +clean: + rm -f $(headers) + rm -f checker checker.c + rm -f *.pyc *.o *~ + +checker: checker.c $(headers) + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< + +check-headers: checker + ./checker > tmp.size + diff -u reference.size tmp.size + rm tmp.size + +x86_32.h: mkheader.py $(ROOT)/arch-x86/xen-x86_32.h $(ROOT)/arch-x86/xen.h $(ROOT)/xen.h + python $< $* $@ $(filter %.h,$^) + +x86_64.h: mkheader.py $(ROOT)/arch-x86/xen-x86_64.h $(ROOT)/arch-x86/xen.h $(ROOT)/xen.h + python $< $* $@ $(filter %.h,$^) + +ia64.h: mkheader.py $(ROOT)/arch-ia64.h $(ROOT)/xen.h + python $< $* $@ $(filter %.h,$^) + +checker.c: mkchecker.py + python $< $@ $(architectures) diff --git a/tools/include/xen-foreign/mkchecker.py b/tools/include/xen-foreign/mkchecker.py new file mode 100644 index 0000000..66c17b1 --- /dev/null +++ b/tools/include/xen-foreign/mkchecker.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + +import sys; +from structs import structs; + +# command line arguments +outfile = sys.argv[1]; +archs = sys.argv[2:]; + +f = open(outfile, "w"); +f.write(''' +/* + * sanity checks for generated foreign headers: + * - verify struct sizes + * + * generated by %s -- DO NOT EDIT + */ +#include +#include +#include +#include +'''); + +for a in archs: + f.write('#include "%s.h"\n' % a); + +f.write('int main(int argc, char *argv[])\n{\n'); + +f.write('\tprintf("\\n");'); +f.write('printf("%-25s |", "structs");\n'); +for a in archs: + f.write('\tprintf("%%8s", "%s");\n' % a); +f.write('\tprintf("\\n");'); + +f.write('\tprintf("\\n");'); +for struct in structs: + f.write('\tprintf("%%-25s |", "%s");\n' % struct); + for a in archs: + s = struct + "_" + a; + f.write('#ifdef %s_has_no_%s\n' % (a, struct)); + f.write('\tprintf("%8s", "-");\n'); + f.write("#else\n"); + f.write('\tprintf("%%8zd", sizeof(struct %s));\n' % s); + f.write("#endif\n"); + + f.write('\tprintf("\\n");\n\n'); + +f.write('\tprintf("\\n");\n'); +f.write('\texit(0);\n'); +f.write('}\n'); + +f.close(); + diff --git a/tools/include/xen-foreign/mkheader.py b/tools/include/xen-foreign/mkheader.py new file mode 100644 index 0000000..8b249e3 --- /dev/null +++ b/tools/include/xen-foreign/mkheader.py @@ -0,0 +1,167 @@ +#!/usr/bin/python + +import sys, re; +from structs import unions, structs, defines; + +# command line arguments +arch = sys.argv[1]; +outfile = sys.argv[2]; +infiles = sys.argv[3:]; + + +########################################################################### +# configuration #2: architecture information + +inttypes = {}; +header = {}; +footer = {}; + +# x86_32 +inttypes["x86_32"] = { + "unsigned long" : "uint32_t", + "long" : "uint32_t", + "xen_pfn_t" : "uint32_t", +}; +header["x86_32"] = """ +#define __i386___X86_32 1 +#pragma pack(4) +"""; +footer["x86_32"] = """ +#pragma pack() +"""; + +# x86_64 +inttypes["x86_64"] = { + "unsigned long" : "__align8__ uint64_t", + "long" : "__align8__ uint64_t", + "xen_pfn_t" : "__align8__ uint64_t", +}; +header["x86_64"] = """ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +# define __DECL_REG(name) union { uint64_t r ## name, e ## name; } +# define __align8__ __attribute__((aligned (8))) +#else +# define __DECL_REG(name) uint64_t r ## name +# define __align8__ FIXME +#endif +#define __x86_64___X86_64 1 +"""; + +# ia64 +inttypes["ia64"] = { + "unsigned long" : "__align8__ uint64_t", + "long" : "__align8__ uint64_t", + "xen_pfn_t" : "__align8__ uint64_t", + "long double" : "__align16__ ldouble_t", +}; +header["ia64"] = """ +#define __align8__ __attribute__((aligned (8))) +#define __align16__ __attribute__((aligned (16))) +typedef unsigned char ldouble_t[16]; +"""; + + +########################################################################### +# main + +input = ""; +output = ""; +fileid = re.sub("[-.]", "_", "__FOREIGN_%s__" % outfile.upper()); + +# read input header files +for name in infiles: + f = open(name, "r"); + input += f.read(); + f.close(); + +# add header +output += """ +/* + * public xen defines and struct for %s + * generated by %s -- DO NOT EDIT + */ + +#ifndef %s +#define %s 1 + +""" % (arch, sys.argv[0], fileid, fileid) + +if arch in header: + output += header[arch]; + output += "\n"; + +# add defines to output +for line in re.findall("#define[^\n]+", input): + for define in defines: + regex = "#define\s+%s\\b" % define; + match = re.search(regex, line); + if None == match: + continue; + if define.upper()[0] == define[0]: + replace = define + "_" + arch.upper(); + else: + replace = define + "_" + arch; + regex = "\\b%s\\b" % define; + output += re.sub(regex, replace, line) + "\n"; +output += "\n"; + +# delete defines, comments, empty lines +input = re.sub("#define[^\n]+\n", "", input); +input = re.compile("/\*(.*?)\*/", re.S).sub("", input) +input = re.compile("\n\s*\n", re.S).sub("\n", input); + +# add unions to output +for union in unions: + regex = "union\s+%s\s*\{(.*?)\n\};" % union; + match = re.search(regex, input, re.S) + if None == match: + output += "#define %s_has_no_%s 1\n" % (arch, union); + else: + output += "union %s_%s {%s\n};\n" % (union, arch, match.group(1)); + output += "\n"; + +# add structs to output +for struct in structs: + regex = "struct\s+%s\s*\{(.*?)\n\};" % struct; + match = re.search(regex, input, re.S) + if None == match: + output += "#define %s_has_no_%s 1\n" % (arch, struct); + else: + output += "struct %s_%s {%s\n};\n" % (struct, arch, match.group(1)); + output += "typedef struct %s_%s %s_%s_t;\n" % (struct, arch, struct, arch); + output += "\n"; + +# add footer +if arch in footer: + output += footer[arch]; + output += "\n"; +output += "#endif /* %s */\n" % fileid; + +# replace: defines +for define in defines: + if define.upper()[0] == define[0]: + replace = define + "_" + arch.upper(); + else: + replace = define + "_" + arch; + output = re.sub("\\b%s\\b" % define, replace, output); + +# replace: unions +for union in unions: + output = re.sub("\\b(union\s+%s)\\b" % union, "\\1_%s" % arch, output); + +# replace: structs + struct typedefs +for struct in structs: + output = re.sub("\\b(struct\s+%s)\\b" % struct, "\\1_%s" % arch, output); + output = re.sub("\\b(%s)_t\\b" % struct, "\\1_%s_t" % arch, output); + +# replace: integer types +integers = inttypes[arch].keys(); +integers.sort(lambda a, b: cmp(len(b),len(a))); +for type in integers: + output = re.sub("\\b%s\\b" % type, inttypes[arch][type], output); + +# print results +f = open(outfile, "w"); +f.write(output); +f.close; + diff --git a/tools/include/xen-foreign/reference.size b/tools/include/xen-foreign/reference.size new file mode 100644 index 0000000..ead6cd1 --- /dev/null +++ b/tools/include/xen-foreign/reference.size @@ -0,0 +1,18 @@ + +structs | x86_32 x86_64 ia64 + +start_info | 1104 1152 1152 +trap_info | 8 16 - +pt_fpreg | - - 16 +cpu_user_regs | 68 200 - +xen_ia64_boot_param | - - 96 +ia64_tr_entry | - - 32 +vcpu_tr_regs | - - 768 +vcpu_guest_context_regs | - - 22176 +vcpu_guest_context | 2800 5168 22208 +arch_vcpu_info | 24 16 0 +vcpu_time_info | 32 32 32 +vcpu_info | 64 64 48 +arch_shared_info | 268 280 272 +shared_info | 2584 3368 4384 + diff --git a/tools/include/xen-foreign/structs.py b/tools/include/xen-foreign/structs.py new file mode 100644 index 0000000..7d51ca8 --- /dev/null +++ b/tools/include/xen-foreign/structs.py @@ -0,0 +1,58 @@ +# configuration: what needs translation + +unions = [ "vcpu_cr_regs", + "vcpu_ar_regs" ]; + +structs = [ "start_info", + "trap_info", + "pt_fpreg", + "cpu_user_regs", + "xen_ia64_boot_param", + "ia64_tr_entry", + "vcpu_tr_regs", + "vcpu_guest_context_regs", + "vcpu_guest_context", + "arch_vcpu_info", + "vcpu_time_info", + "vcpu_info", + "arch_shared_info", + "shared_info" ]; + +defines = [ "__i386__", + "__x86_64__", + + "FLAT_RING1_CS", + "FLAT_RING1_DS", + "FLAT_RING1_SS", + + "FLAT_RING3_CS64", + "FLAT_RING3_DS64", + "FLAT_RING3_SS64", + "FLAT_KERNEL_CS64", + "FLAT_KERNEL_DS64", + "FLAT_KERNEL_SS64", + + "FLAT_KERNEL_CS", + "FLAT_KERNEL_DS", + "FLAT_KERNEL_SS", + + # x86_{32,64} + "_VGCF_i387_valid", + "VGCF_i387_valid", + "_VGCF_in_kernel", + "VGCF_in_kernel", + "_VGCF_failsafe_disables_events", + "VGCF_failsafe_disables_events", + "_VGCF_syscall_disables_events", + "VGCF_syscall_disables_events", + "_VGCF_online", + "VGCF_online", + + # ia64 + "VGCF_EXTRA_REGS", + + # all archs + "xen_pfn_to_cr3", + "MAX_VIRT_CPUS", + "MAX_GUEST_CMDLINE" ]; + diff --git a/tools/include/xen-sys/Linux/evtchn.h b/tools/include/xen-sys/Linux/evtchn.h new file mode 100644 index 0000000..938d4da --- /dev/null +++ b/tools/include/xen-sys/Linux/evtchn.h @@ -0,0 +1,88 @@ +/****************************************************************************** + * evtchn.h + * + * Interface to /dev/xen/evtchn. + * + * Copyright (c) 2003-2005, 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 __LINUX_PUBLIC_EVTCHN_H__ +#define __LINUX_PUBLIC_EVTCHN_H__ + +/* + * Bind a fresh port to VIRQ @virq. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_VIRQ \ + _IOC(_IOC_NONE, 'E', 0, sizeof(struct ioctl_evtchn_bind_virq)) +struct ioctl_evtchn_bind_virq { + unsigned int virq; +}; + +/* + * Bind a fresh port to remote <@remote_domain, @remote_port>. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_INTERDOMAIN \ + _IOC(_IOC_NONE, 'E', 1, sizeof(struct ioctl_evtchn_bind_interdomain)) +struct ioctl_evtchn_bind_interdomain { + unsigned int remote_domain, remote_port; +}; + +/* + * Allocate a fresh port for binding to @remote_domain. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_UNBOUND_PORT \ + _IOC(_IOC_NONE, 'E', 2, sizeof(struct ioctl_evtchn_bind_unbound_port)) +struct ioctl_evtchn_bind_unbound_port { + unsigned int remote_domain; +}; + +/* + * Unbind previously allocated @port. + */ +#define IOCTL_EVTCHN_UNBIND \ + _IOC(_IOC_NONE, 'E', 3, sizeof(struct ioctl_evtchn_unbind)) +struct ioctl_evtchn_unbind { + unsigned int port; +}; + +/* + * Unbind previously allocated @port. + */ +#define IOCTL_EVTCHN_NOTIFY \ + _IOC(_IOC_NONE, 'E', 4, sizeof(struct ioctl_evtchn_notify)) +struct ioctl_evtchn_notify { + unsigned int port; +}; + +/* Clear and reinitialise the event buffer. Clear error condition. */ +#define IOCTL_EVTCHN_RESET \ + _IOC(_IOC_NONE, 'E', 5, 0) + +#endif /* __LINUX_PUBLIC_EVTCHN_H__ */ diff --git a/tools/include/xen-sys/Linux/gntdev.h b/tools/include/xen-sys/Linux/gntdev.h new file mode 100644 index 0000000..8bd1467 --- /dev/null +++ b/tools/include/xen-sys/Linux/gntdev.h @@ -0,0 +1,119 @@ +/****************************************************************************** + * gntdev.h + * + * Interface to /dev/xen/gntdev. + * + * Copyright (c) 2007, D G Murray + * + * 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 __LINUX_PUBLIC_GNTDEV_H__ +#define __LINUX_PUBLIC_GNTDEV_H__ + +struct ioctl_gntdev_grant_ref { + /* The domain ID of the grant to be mapped. */ + uint32_t domid; + /* The grant reference of the grant to be mapped. */ + uint32_t ref; +}; + +/* + * Inserts the grant references into the mapping table of an instance + * of gntdev. N.B. This does not perform the mapping, which is deferred + * until mmap() is called with @index as the offset. + */ +#define IOCTL_GNTDEV_MAP_GRANT_REF \ +_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_gntdev_map_grant_ref)) +struct ioctl_gntdev_map_grant_ref { + /* IN parameters */ + /* The number of grants to be mapped. */ + uint32_t count; + uint32_t pad; + /* OUT parameters */ + /* The offset to be used on a subsequent call to mmap(). */ + uint64_t index; + /* Variable IN parameter. */ + /* Array of grant references, of size @count. */ + struct ioctl_gntdev_grant_ref refs[1]; +}; + +/* + * Removes the grant references from the mapping table of an instance of + * of gntdev. N.B. munmap() must be called on the relevant virtual address(es) + * before this ioctl is called, or an error will result. + */ +#define IOCTL_GNTDEV_UNMAP_GRANT_REF \ +_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_gntdev_unmap_grant_ref)) +struct ioctl_gntdev_unmap_grant_ref { + /* IN parameters */ + /* The offset was returned by the corresponding map operation. */ + uint64_t index; + /* The number of pages to be unmapped. */ + uint32_t count; + uint32_t pad; +}; + +/* + * Returns the offset in the driver's address space that corresponds + * to @vaddr. This can be used to perform a munmap(), followed by an + * UNMAP_GRANT_REF ioctl, where no state about the offset is retained by + * the caller. The number of pages that were allocated at the same time as + * @vaddr is returned in @count. + * + * N.B. Where more than one page has been mapped into a contiguous range, the + * supplied @vaddr must correspond to the start of the range; otherwise + * an error will result. It is only possible to munmap() the entire + * contiguously-allocated range at once, and not any subrange thereof. + */ +#define IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR \ +_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_gntdev_get_offset_for_vaddr)) +struct ioctl_gntdev_get_offset_for_vaddr { + /* IN parameters */ + /* The virtual address of the first mapped page in a range. */ + uint64_t vaddr; + /* OUT parameters */ + /* The offset that was used in the initial mmap() operation. */ + uint64_t offset; + /* The number of pages mapped in the VM area that begins at @vaddr. */ + uint32_t count; + uint32_t pad; +}; + +/* + * Sets the maximum number of grants that may mapped at once by this gntdev + * instance. + * + * N.B. This must be called before any other ioctl is performed on the device. + */ +#define IOCTL_GNTDEV_SET_MAX_GRANTS \ +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_gntdev_set_max_grants)) +struct ioctl_gntdev_set_max_grants { + /* IN parameter */ + /* The maximum number of grants that may be mapped at once. */ + uint32_t count; +}; + +#endif /* __LINUX_PUBLIC_GNTDEV_H__ */ diff --git a/tools/include/xen-sys/Linux/privcmd.h b/tools/include/xen-sys/Linux/privcmd.h new file mode 100644 index 0000000..d1162ee --- /dev/null +++ b/tools/include/xen-sys/Linux/privcmd.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * privcmd.h + * + * Interface to /proc/xen/privcmd. + * + * Copyright (c) 2003-2005, 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 __LINUX_PUBLIC_PRIVCMD_H__ +#define __LINUX_PUBLIC_PRIVCMD_H__ + +#include + +#ifndef __user +#define __user +#endif + +typedef struct privcmd_hypercall +{ + __u64 op; + __u64 arg[5]; +} privcmd_hypercall_t; + +typedef struct privcmd_mmap_entry { + __u64 va; + __u64 mfn; + __u64 npages; +} privcmd_mmap_entry_t; + +typedef struct privcmd_mmap { + int num; + domid_t dom; /* target domain */ + privcmd_mmap_entry_t __user *entry; +} privcmd_mmap_t; + +typedef struct privcmd_mmapbatch { + int num; /* number of pages to populate */ + domid_t dom; /* target domain */ + __u64 addr; /* virtual address */ + xen_pfn_t __user *arr; /* array of mfns - top nibble set on err */ +} privcmd_mmapbatch_t; + +/* + * @cmd: IOCTL_PRIVCMD_HYPERCALL + * @arg: &privcmd_hypercall_t + * Return: Value returned from execution of the specified hypercall. + */ +#define IOCTL_PRIVCMD_HYPERCALL \ + _IOC(_IOC_NONE, 'P', 0, sizeof(privcmd_hypercall_t)) +#define IOCTL_PRIVCMD_MMAP \ + _IOC(_IOC_NONE, 'P', 2, sizeof(privcmd_mmap_t)) +#define IOCTL_PRIVCMD_MMAPBATCH \ + _IOC(_IOC_NONE, 'P', 3, sizeof(privcmd_mmapbatch_t)) + +#endif /* __LINUX_PUBLIC_PRIVCMD_H__ */ diff --git a/tools/include/xen-sys/MiniOS/privcmd.h b/tools/include/xen-sys/MiniOS/privcmd.h new file mode 100644 index 0000000..db0f00e --- /dev/null +++ b/tools/include/xen-sys/MiniOS/privcmd.h @@ -0,0 +1,16 @@ +#ifndef __MINIOS_PUBLIC_PRIVCMD_H__ +#define __MINIOS_PUBLIC_PRIVCMD_H__ + +#include + +typedef struct privcmd_hypercall +{ + u64 op; + u64 arg[5]; +} privcmd_hypercall_t; + +typedef struct privcmd_mmap_entry { + u64 mfn; +} privcmd_mmap_entry_t; + +#endif /* __MINIOS_PUBLIC_PRIVCMD_H__ */ diff --git a/tools/include/xen-sys/NetBSD/evtchn.h b/tools/include/xen-sys/NetBSD/evtchn.h new file mode 100644 index 0000000..dc30e81 --- /dev/null +++ b/tools/include/xen-sys/NetBSD/evtchn.h @@ -0,0 +1,89 @@ +/* $NetBSD: evtchn.h,v 1.1.1.1 2007/06/14 19:39:45 bouyer Exp $ */ +/****************************************************************************** + * evtchn.h + * + * Interface to /dev/xen/evtchn. + * + * Copyright (c) 2003-2005, K A Fraser + * + * This file may be 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 __NetBSD_EVTCHN_H__ +#define __NetBSD_EVTCHN_H__ + +/* + * Bind a fresh port to VIRQ @virq. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_VIRQ \ + _IOWR('E', 4, struct ioctl_evtchn_bind_virq) +struct ioctl_evtchn_bind_virq { + unsigned int virq; + unsigned int port; +}; + +/* + * Bind a fresh port to remote <@remote_domain, @remote_port>. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_INTERDOMAIN \ + _IOWR('E', 5, struct ioctl_evtchn_bind_interdomain) +struct ioctl_evtchn_bind_interdomain { + unsigned int remote_domain, remote_port; + unsigned int port; +}; + +/* + * Allocate a fresh port for binding to @remote_domain. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_UNBOUND_PORT \ + _IOWR('E', 6, struct ioctl_evtchn_bind_unbound_port) +struct ioctl_evtchn_bind_unbound_port { + unsigned int remote_domain; + unsigned int port; +}; + +/* + * Unbind previously allocated @port. + */ +#define IOCTL_EVTCHN_UNBIND \ + _IOW('E', 7, struct ioctl_evtchn_unbind) +struct ioctl_evtchn_unbind { + unsigned int port; +}; + +/* + * Send event to previously allocated @port. + */ +#define IOCTL_EVTCHN_NOTIFY \ + _IOW('E', 8, struct ioctl_evtchn_notify) +struct ioctl_evtchn_notify { + unsigned int port; +}; + +/* Clear and reinitialise the event buffer. Clear error condition. */ +#define IOCTL_EVTCHN_RESET \ + _IO('E', 9) + +#endif /* __NetBSD_EVTCHN_H__ */ diff --git a/tools/include/xen-sys/NetBSD/privcmd.h b/tools/include/xen-sys/NetBSD/privcmd.h new file mode 100644 index 0000000..1296b30 --- /dev/null +++ b/tools/include/xen-sys/NetBSD/privcmd.h @@ -0,0 +1,106 @@ +/* NetBSD: xenio.h,v 1.3 2005/05/24 12:07:12 yamt Exp $ */ + +/****************************************************************************** + * privcmd.h + * + * Copyright (c) 2003-2004, K A Fraser + * + * This file may be 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 __NetBSD_PRIVCMD_H__ +#define __NetBSD_PRIVCMD_H__ + +/* Interface to /proc/xen/privcmd */ + +typedef struct privcmd_hypercall +{ + unsigned long op; + unsigned long arg[5]; + long retval; +} privcmd_hypercall_t; + +typedef struct privcmd_mmap_entry { + unsigned long va; + unsigned long mfn; + unsigned long npages; +} privcmd_mmap_entry_t; + +typedef struct privcmd_mmap { + int num; + domid_t dom; /* target domain */ + privcmd_mmap_entry_t *entry; +} privcmd_mmap_t; + +typedef struct privcmd_mmapbatch { + int num; /* number of pages to populate */ + domid_t dom; /* target domain */ + unsigned long addr; /* virtual address */ + unsigned long *arr; /* array of mfns - top nibble set on err */ +} privcmd_mmapbatch_t; + +typedef struct privcmd_blkmsg +{ + unsigned long op; + void *buf; + int buf_size; +} privcmd_blkmsg_t; + +/* + * @cmd: IOCTL_PRIVCMD_HYPERCALL + * @arg: &privcmd_hypercall_t + * Return: Value returned from execution of the specified hypercall. + */ +#define IOCTL_PRIVCMD_HYPERCALL \ + _IOWR('P', 0, privcmd_hypercall_t) + +#if defined(_KERNEL) +/* compat */ +#define IOCTL_PRIVCMD_INITDOMAIN_EVTCHN_OLD \ + _IO('P', 1) +#endif /* defined(_KERNEL) */ + +#define IOCTL_PRIVCMD_MMAP \ + _IOW('P', 2, privcmd_mmap_t) +#define IOCTL_PRIVCMD_MMAPBATCH \ + _IOW('P', 3, privcmd_mmapbatch_t) +#define IOCTL_PRIVCMD_GET_MACH2PHYS_START_MFN \ + _IOR('P', 4, unsigned long) + +/* + * @cmd: IOCTL_PRIVCMD_INITDOMAIN_EVTCHN + * @arg: n/a + * Return: Port associated with domain-controller end of control event channel + * for the initial domain. + */ +#define IOCTL_PRIVCMD_INITDOMAIN_EVTCHN \ + _IOR('P', 5, int) + +/* Interface to /dev/xenevt */ +/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */ +#define EVTCHN_RESET _IO('E', 1) +/* EVTCHN_BIND: Bind to the specified event-channel port. */ +#define EVTCHN_BIND _IOW('E', 2, unsigned long) +/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */ +#define EVTCHN_UNBIND _IOW('E', 3, unsigned long) + +#endif /* __NetBSD_PRIVCMD_H__ */ diff --git a/tools/include/xen-sys/SunOS/evtchn.h b/tools/include/xen-sys/SunOS/evtchn.h new file mode 100644 index 0000000..abc8c60 --- /dev/null +++ b/tools/include/xen-sys/SunOS/evtchn.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * evtchn.h + * + * Interface to /dev/xen/evtchn. + * + * Copyright (c) 2003-2005, K A Fraser + * + * This file may be 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 _XEN_SYS_EVTCHN_H +#define _XEN_SYS_EVTCHN_H + +#define _IOC_NONE 0 +#define _IOC(flag, letter, inum, size) ((letter) << 8 | (inum)) + +/* + * Bind a fresh port to VIRQ @virq. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_VIRQ \ + _IOC(_IOC_NONE, 'E', 0, sizeof(struct ioctl_evtchn_bind_virq)) +struct ioctl_evtchn_bind_virq { + unsigned int virq; +}; + +/* + * Bind a fresh port to remote <@remote_domain, @remote_port>. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_INTERDOMAIN \ + _IOC(_IOC_NONE, 'E', 1, sizeof(struct ioctl_evtchn_bind_interdomain)) +struct ioctl_evtchn_bind_interdomain { + unsigned int remote_domain, remote_port; +}; + +/* + * Allocate a fresh port for binding to @remote_domain. + * Return allocated port. + */ +#define IOCTL_EVTCHN_BIND_UNBOUND_PORT \ + _IOC(_IOC_NONE, 'E', 2, sizeof(struct ioctl_evtchn_bind_unbound_port)) +struct ioctl_evtchn_bind_unbound_port { + unsigned int remote_domain; +}; + +/* + * Unbind previously allocated @port. + */ +#define IOCTL_EVTCHN_UNBIND \ + _IOC(_IOC_NONE, 'E', 3, sizeof(struct ioctl_evtchn_unbind)) +struct ioctl_evtchn_unbind { + unsigned int port; +}; + +/* + * Notify the given @port. + */ +#define IOCTL_EVTCHN_NOTIFY \ + _IOC(_IOC_NONE, 'E', 4, sizeof(struct ioctl_evtchn_notify)) +struct ioctl_evtchn_notify { + unsigned int port; +}; + +#endif /* _XEN_SYS_EVTCHN_H */ + +/* + * Local variables: + * c-file-style: "solaris" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/tools/include/xen-sys/SunOS/privcmd.h b/tools/include/xen-sys/SunOS/privcmd.h new file mode 100644 index 0000000..31ea6a5 --- /dev/null +++ b/tools/include/xen-sys/SunOS/privcmd.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2003-2005, K A Fraser + * + * This file may be 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. + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _XEN_SYS_PRIVCMD_H +#define _XEN_SYS_PRIVCMD_H + +/* + * WARNING: + * These numbers and structure are built into the ON privcmd + * driver, as well as the low-level tools and libraries in + * the Xen consolidation. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ioctl numbers and corresponding data structures + */ + +#define __PRIVCMD_IOC (('p'<<24)|('r'<<16)|('v'<<8)) + +#define IOCTL_PRIVCMD_HYPERCALL (__PRIVCMD_IOC|0) +#define IOCTL_PRIVCMD_MMAP (__PRIVCMD_IOC|1) +#define IOCTL_PRIVCMD_MMAPBATCH (__PRIVCMD_IOC|2) + +typedef struct __privcmd_hypercall { + unsigned long op; + unsigned long arg[5]; +} privcmd_hypercall_t; + +typedef struct __privcmd_mmap_entry { + unsigned long va; + unsigned long mfn; + unsigned long npages; +} privcmd_mmap_entry_t; + +typedef struct __privcmd_mmap { + int num; + domid_t dom; /* target domain */ + privcmd_mmap_entry_t *entry; +} privcmd_mmap_t; + +typedef struct __privcmd_mmapbatch { + int num; /* number of pages to populate */ + domid_t dom; /* target domain */ + unsigned long addr; /* virtual address */ + unsigned long *arr; /* array of mfns - top nibble set on err */ +} privcmd_mmapbatch_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _XEN_SYS_PRIVCMD_H */ diff --git a/tools/include/xen-sys/SunOS/xenbus.h b/tools/include/xen-sys/SunOS/xenbus.h new file mode 100644 index 0000000..fc1035e --- /dev/null +++ b/tools/include/xen-sys/SunOS/xenbus.h @@ -0,0 +1,42 @@ +/* + * This file may be 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. + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _XEN_SYS_XENBUS_H +#define _XEN_SYS_XENBUS_H + +/* + * Return the xenstore event channel. + */ +#define IOCTL_XENBUS_XENSTORE_EVTCHN ('X' << 8) + +/* + * Notify the kernel that the xenstore is up and running + */ +#define IOCTL_XENBUS_NOTIFY_UP ('U' << 8) + +#endif /* _XEN_SYS_XENBUS_H */ diff --git a/tools/libaio/COPYING b/tools/libaio/COPYING new file mode 100644 index 0000000..c4792dd --- /dev/null +++ b/tools/libaio/COPYING @@ -0,0 +1,515 @@ + + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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 +^L + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +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! + + diff --git a/tools/libaio/ChangeLog b/tools/libaio/ChangeLog new file mode 100644 index 0000000..ddcf6e3 --- /dev/null +++ b/tools/libaio/ChangeLog @@ -0,0 +1,43 @@ +0.4.0 + - remove libredhat-kernel + - add rough outline for man pages + - make the compiled io_getevents() add the extra parameter and + pass the timeout for updating as per 2.5 + - fixes for ia64, now works + - fixes for x86-64 + - powerpc support from Gianni Tedesco + - disable the NULL check in harness/cases/4.t on ia64: ia64 + maps the 0 page and causes this check to fail. + +0.3.15 + - use real syscall interface, but don't break source compatibility + yet (that will happen with 0.4.0) + +0.3.13 + - add test cases + +0.3.11 + - use library versioning of libredhat-kernel to always provide a + fallback + +0.3.9 + - add io_queue_release function + +0.3.8 + - make clean deletes libredhat-kernel.so.1 + - const struct timespec * + - add make srpm target + +0.3.7 + - fix assembly function .types + - export io_getevents + - fix io_submit function prototype to match the kernel + - provide /usr/lib/libredhat-kernel.so link for compilation + (do NOT link against libredhat-kernel.so directly) + - fix soname to libaio.so.1 + - fix dummy libredhat-kernel's soname + - work around nfs bug + - provide and install libredhat-kernel.so.1 stub + - Makefile improvements + - make sure dummy libredhat-kernel.so only returns -ENOSYS + diff --git a/tools/libaio/INSTALL b/tools/libaio/INSTALL new file mode 100644 index 0000000..29b9077 --- /dev/null +++ b/tools/libaio/INSTALL @@ -0,0 +1,18 @@ +To install the library, execute the command: + + make prefix=`pwd`/usr install + +which will install the binaries and header files into the directory +usr. Set prefix=/usr to get them installed into the main system. + +Please note: Do not attempt to install on the system the +"libredhat-kernel.so" file. It is a dummy shared library +provided only for the purpose of being able to bootstrap +this facility while running on systems without the correct +libredhat-kernel.so built. The contents of the included +libredhat-kernel.so are only stubs; this library is NOT +functional for anything except the internal purpose of +linking libaio.so against the provided stubs. At runtime, +libaio.so requires a real libredhat-kernel.so library; this +is provided by the Red Hat kernel RPM packages with async +I/O functionality. diff --git a/tools/libaio/Makefile b/tools/libaio/Makefile new file mode 100644 index 0000000..7f5c344 --- /dev/null +++ b/tools/libaio/Makefile @@ -0,0 +1,40 @@ +NAME=libaio +SPECFILE=$(NAME).spec +VERSION=$(shell awk '/Version:/ { print $$2 }' $(SPECFILE)) +RELEASE=$(shell awk '/Release:/ { print $$2 }' $(SPECFILE)) +CVSTAG = $(NAME)_$(subst .,-,$(VERSION))_$(subst .,-,$(RELEASE)) +RPMBUILD=$(shell `which rpmbuild >&/dev/null` && echo "rpmbuild" || echo "rpm") + +prefix=/usr +includedir=$(prefix)/include +libdir=$(prefix)/lib + +default: all + +all: + @$(MAKE) -C src + +install: all + +clean: + @$(MAKE) -C src clean + @$(MAKE) -C harness clean + +tag-archive: + @cvs -Q tag -F $(CVSTAG) + +create-archive: tag-archive + @rm -rf /tmp/$(NAME) + @cd /tmp && cvs -Q -d $(CVSROOT) export -r$(CVSTAG) $(NAME) || echo GRRRrrrrr -- ignore [export aborted] + @mv /tmp/$(NAME) /tmp/$(NAME)-$(VERSION) + @cd /tmp && tar czSpf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION) + @rm -rf /tmp/$(NAME)-$(VERSION) + @cp /tmp/$(NAME)-$(VERSION).tar.gz . + @rm -f /tmp/$(NAME)-$(VERSION).tar.gz + @echo " " + @echo "The final archive is ./$(NAME)-$(VERSION).tar.gz." + +archive: clean tag-archive create-archive + +srpm: create-archive + $(RPMBUILD) --define "_sourcedir `pwd`" --define "_srcrpmdir `pwd`" --nodeps -bs $(SPECFILE) diff --git a/tools/libaio/TODO b/tools/libaio/TODO new file mode 100644 index 0000000..0a9ac15 --- /dev/null +++ b/tools/libaio/TODO @@ -0,0 +1,4 @@ +- Write man pages. +- Make -static links against libaio work. +- Fallback on userspace if the kernel calls return -ENOSYS. + diff --git a/tools/libaio/harness/Makefile b/tools/libaio/harness/Makefile new file mode 100644 index 0000000..d2483fd --- /dev/null +++ b/tools/libaio/harness/Makefile @@ -0,0 +1,37 @@ +# foo. +TEST_SRCS:=$(shell find cases/ -name \*.t | sort -n -t/ -k2) +PROGS:=$(patsubst %.t,%.p,$(TEST_SRCS)) +HARNESS_SRCS:=main.c +# io_queue.c + +CFLAGS=-Wall -Werror -g -O -laio +#-lpthread -lrt + +all: $(PROGS) + +$(PROGS): %.p: %.t $(HARNESS_SRCS) + $(CC) $(CFLAGS) -DTEST_NAME=\"$<\" -o $@ main.c + +clean: + rm -f $(PROGS) *.o runtests.out rofile wofile rwfile + +.PHONY: + +testdir/rofile: .PHONY + rm -f $@ + echo "test" >$@ + chmod 400 $@ + +testdir/wofile: .PHONY + rm -f $@ + echo "test" >$@ + chmod 200 $@ + +testdir/rwfile: .PHONY + rm -f $@ + echo "test" >$@ + chmod 600 $@ + +check: $(PROGS) testdir/rofile testdir/rwfile testdir/wofile + ./runtests.sh $(PROGS) + diff --git a/tools/libaio/harness/README b/tools/libaio/harness/README new file mode 100644 index 0000000..5557370 --- /dev/null +++ b/tools/libaio/harness/README @@ -0,0 +1,19 @@ +Notes on running this test suite: + +To run the test suite, run "make check". All test cases should pass +and there should be 0 fails. + +Several of the test cases require a directory on the filesystem under +test for the creation of test files, as well as the generation of +error conditions. The test cases assume the directories (or symlinks +to directories) are as follows: + + testdir/ + - used for general read/write test cases. Must have at + least as much free space as the machine has RAM (up + to 768MB). + testdir.enospc/ + - a filesystem that has space for writing 8KB out, but + fails with -ENOSPC beyond 8KB. + testdir.ext2/ + - must be an ext2 filesystem. diff --git a/tools/libaio/harness/attic/0.t b/tools/libaio/harness/attic/0.t new file mode 100644 index 0000000..033e62c --- /dev/null +++ b/tools/libaio/harness/attic/0.t @@ -0,0 +1,9 @@ +/* 0.t + Test harness check: okay. +*/ +int test_main(void) +{ + printf("test_main: okay\n"); + return 0; +} + diff --git a/tools/libaio/harness/attic/1.t b/tools/libaio/harness/attic/1.t new file mode 100644 index 0000000..799ffd1 --- /dev/null +++ b/tools/libaio/harness/attic/1.t @@ -0,0 +1,9 @@ +/* 1.t + Test harness check: fail. +*/ +int test_main(void) +{ + printf("test_main: fail\n"); + return 1; +} + diff --git a/tools/libaio/harness/cases/10.t b/tools/libaio/harness/cases/10.t new file mode 100644 index 0000000..9d3beb2 --- /dev/null +++ b/tools/libaio/harness/cases/10.t @@ -0,0 +1,53 @@ +/* 10.t - uses testdir.enospc/rwfile +- Check results on out-of-space and out-of-quota. (10.t) + - write that fills filesystem but does not go over should succeed + - write that fills filesystem and goes over should be partial + - write to full filesystem should return -ENOSPC + - read beyond end of file after ENOSPC should return 0 +*/ +#include "aio_setup.h" + +#include +#include +#include + +int test_main(void) +{ +/* Note: changing either of these requires updating the ext2-enospc.img + * filesystem image. Also, if SIZE is less than PAGE_SIZE, problems + * crop up due to ext2's preallocation. + */ +#define LIMIT 65536 +#define SIZE 65536 + char *buf; + int rwfd; + int status = 0, res; + + rwfd = open("testdir.enospc/rwfile", O_RDWR|O_CREAT|O_TRUNC, 0600); + assert(rwfd != -1); + res = ftruncate(rwfd, 0); assert(res == 0); + buf = malloc(SIZE); assert(buf != NULL); + memset(buf, 0, SIZE); + + + status |= attempt_rw(rwfd, buf, SIZE, LIMIT-SIZE, WRITE, SIZE); + status |= attempt_rw(rwfd, buf, SIZE, LIMIT-SIZE, READ, SIZE); + + status |= attempt_rw(rwfd, buf, SIZE, LIMIT, WRITE, -ENOSPC); + status |= attempt_rw(rwfd, buf, SIZE, LIMIT, READ, 0); + + res = ftruncate(rwfd, 0); assert(res == 0); + + status |= attempt_rw(rwfd, buf, SIZE, 1+LIMIT-SIZE, WRITE, SIZE-1); + status |= attempt_rw(rwfd, buf, SIZE, 1+LIMIT-SIZE, READ, SIZE-1); + status |= attempt_rw(rwfd, buf, SIZE, LIMIT, READ, 0); + + status |= attempt_rw(rwfd, buf, SIZE, LIMIT, WRITE, -ENOSPC); + status |= attempt_rw(rwfd, buf, SIZE, LIMIT, READ, 0); + status |= attempt_rw(rwfd, buf, 0, LIMIT, WRITE, 0); + + res = close(rwfd); assert(res == 0); + res = unlink("testdir.enospc/rwfile"); assert(res == 0); + return status; +} + diff --git a/tools/libaio/harness/cases/11.t b/tools/libaio/harness/cases/11.t new file mode 100644 index 0000000..efcf6d4 --- /dev/null +++ b/tools/libaio/harness/cases/11.t @@ -0,0 +1,39 @@ +/* 11.t - uses testdir/rwfile +- repeated read / write of same page (to check accounting) (11.t) +*/ +#include "aio_setup.h" + +#include +#include +#include + +int test_main(void) +{ +#define COUNT 1000000 +#define SIZE 256 + char *buf; + int rwfd; + int status = 0; + int i; + + rwfd = open("testdir/rwfile", O_RDWR|O_CREAT|O_TRUNC, 0600); + assert(rwfd != -1); + buf = malloc(SIZE); assert(buf != NULL); + memset(buf, 0, SIZE); + + for (i=0; i +#include +#include +#include + +#include "aio_setup.h" + +void test_child(void) +{ + int res; + res = attempt_io_submit(io_ctx, 0, NULL, -EINVAL); + fflush(stdout); + _exit(res); +} + +int test_main(void) +{ + int res, status; + pid_t pid; + + if (attempt_io_submit(io_ctx, 0, NULL, 0)) + return 1; + + sigblock(sigmask(SIGCHLD) | siggetmask()); + fflush(NULL); + pid = fork(); assert(pid != -1); + + if (pid == 0) + test_child(); + + res = waitpid(pid, &status, 0); + + if (WIFEXITED(status)) { + int failed = (WEXITSTATUS(status) != 0); + printf("child exited with status %d%s\n", WEXITSTATUS(status), + failed ? " -- FAILED" : ""); + return failed; + } + + /* anything else: failed */ + if (WIFSIGNALED(status)) + printf("child killed by signal %d -- FAILED.\n", + WTERMSIG(status)); + + return 1; +} diff --git a/tools/libaio/harness/cases/13.t b/tools/libaio/harness/cases/13.t new file mode 100644 index 0000000..5f18005 --- /dev/null +++ b/tools/libaio/harness/cases/13.t @@ -0,0 +1,66 @@ +/* 13.t - uses testdir/rwfile +- Submit multiple writes larger than aio-max-size (deadlocks on older + aio code) +*/ +#include "aio_setup.h" + +#include +#include +#include + +int test_main(void) +{ +#define SIZE (1024 * 1024) +#define IOS 8 + struct iocb iocbs[IOS]; + struct iocb *iocb_list[IOS]; + char *bufs[IOS]; + int rwfd; + int status = 0, res; + int i; + + rwfd = open("testdir/rwfile", O_RDWR|O_CREAT|O_TRUNC, 0600); + assert(rwfd != -1); + res = ftruncate(rwfd, 0); assert(res == 0); + + for (i=0; i +#include +#include +#include + +#include "aio_setup.h" +#include + +#define SIZE 768*1024*1024 + +//just submit an I/O + +int test_child(void) +{ + char *buf; + int rwfd; + int res; + long size; + struct iocb iocb; + struct iocb *iocbs[] = { &iocb }; + int loop = 10; + int i; + + aio_setup(1024); + + size = SIZE; + + printf("size = %ld\n", size); + + rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd != +-1); + res = ftruncate(rwfd, 0); assert(res == 0); + buf = malloc(size); assert(buf != +NULL); + + for(i=0;i + +int test_main(void) +{ + int page_size = getpagesize(); +#define SIZE 512 + char *buf; + int rwfd; + int status = 0, res; + + rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd != -1); + res = ftruncate(rwfd, 512); assert(res == 0); + + buf = mmap(0, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, rwfd, 0); + assert(buf != (char *)-1); + + status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, SIZE); + status |= attempt_rw(rwfd, buf, SIZE, 0, READ, SIZE); + + res = munmap(buf, page_size); assert(res == 0); + buf = mmap(0, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, rwfd, 0); + assert(buf != (char *)-1); + + status |= attempt_rw(rwfd, buf, SIZE, 0, READ, SIZE); + status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, SIZE); + + res = munmap(buf, page_size); assert(res == 0); + buf = mmap(0, page_size, PROT_READ, MAP_SHARED, rwfd, 0); + assert(buf != (char *)-1); + + status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, SIZE); + status |= attempt_rw(rwfd, buf, SIZE, 0, READ, -EFAULT); + + res = munmap(buf, page_size); assert(res == 0); + buf = mmap(0, page_size, PROT_WRITE, MAP_SHARED, rwfd, 0); + assert(buf != (char *)-1); + + status |= attempt_rw(rwfd, buf, SIZE, 0, READ, SIZE); + status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, -EFAULT); + + return status; +} + diff --git a/tools/libaio/harness/cases/6.t b/tools/libaio/harness/cases/6.t new file mode 100644 index 0000000..cea4b01 --- /dev/null +++ b/tools/libaio/harness/cases/6.t @@ -0,0 +1,57 @@ +/* 6.t +- huge reads (pinned pages) (6.t) +- huge writes (6.t) +*/ +#include "aio_setup.h" +#include + +long getmemsize(void) +{ + FILE *f = fopen("/proc/meminfo", "r"); + long size; + int gotit = 0; + char str[256]; + + assert(f != NULL); + while (NULL != fgets(str, 255, f)) { + str[255] = 0; + if (0 == memcmp(str, "MemTotal:", 9)) { + if (1 == sscanf(str + 9, "%ld", &size)) { + gotit = 1; + break; + } + } + } + fclose(f); + + assert(gotit != 0); + return size; +} + +int test_main(void) +{ + char *buf; + int rwfd; + int status = 0, res; + long size; + + size = getmemsize(); + printf("size = %ld\n", size); + assert(size >= (16 * 1024)); + if (size > (768 * 1024)) + size = 768 * 1024; + size *= 1024; + + rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd != -1); + res = ftruncate(rwfd, 0); assert(res == 0); + buf = malloc(size); assert(buf != NULL); + + //memset(buf, 0, size); + status |= attempt_rw(rwfd, buf, size, 0, WRITE, size); + status |= attempt_rw(rwfd, buf, size, 0, READ, size); + + //res = ftruncate(rwfd, 0); assert(res == 0); + + return status; +} + diff --git a/tools/libaio/harness/cases/7.t b/tools/libaio/harness/cases/7.t new file mode 100644 index 0000000..d2d6cbc --- /dev/null +++ b/tools/libaio/harness/cases/7.t @@ -0,0 +1,27 @@ +/* 7.t +- Write overlapping the file size rlimit boundary: should be a short + write. (7.t) +- Write at the file size rlimit boundary: should give EFBIG. (I think + the spec requires that you do NOT deliver SIGXFSZ in this case, where + you would do so for sync IO.) (7.t) +- Special case: a write of zero bytes at or beyond the file size rlimit + boundary must return success. (7.t) +*/ + +#include + +void SET_RLIMIT(long long limit) +{ + struct rlimit rlim; + int res; + + rlim.rlim_cur = limit; assert(rlim.rlim_cur == limit); + rlim.rlim_max = limit; assert(rlim.rlim_max == limit); + + res = setrlimit(RLIMIT_FSIZE, &rlim); assert(res == 0); +} + +#define LIMIT 8192 +#define FILENAME "testdir/rwfile" + +#include "common-7-8.h" diff --git a/tools/libaio/harness/cases/8.t b/tools/libaio/harness/cases/8.t new file mode 100644 index 0000000..8a3d83e --- /dev/null +++ b/tools/libaio/harness/cases/8.t @@ -0,0 +1,49 @@ +/* 8.t +- Ditto for the above three tests at the offset maximum (largest + possible ext2/3 file size.) (8.t) + */ +#include + +#define EXT2_OLD_SUPER_MAGIC 0xEF51 +#define EXT2_SUPER_MAGIC 0xEF53 + +long long get_fs_limit(int fd) +{ + struct statfs s; + int res; + long long lim = 0; + + res = fstatfs(fd, &s); assert(res == 0); + + switch(s.f_type) { + case EXT2_OLD_SUPER_MAGIC: + case EXT2_SUPER_MAGIC: +#if 0 + { + long long tmp; + tmp = s.f_bsize / 4; + /* 12 direct + indirect block + dind + tind */ + lim = 12 + tmp + tmp * tmp + tmp * tmp * tmp; + lim *= s.f_bsize; + printf("limit(%ld) = %Ld\n", (long)s.f_bsize, lim); + } +#endif + switch(s.f_bsize) { + case 4096: lim = 2199023251456; break; + default: + printf("unknown ext2 blocksize %ld\n", (long)s.f_bsize); + exit(3); + } + break; + default: + printf("unknown filesystem 0x%08lx\n", (long)s.f_type); + exit(3); + } + return lim; +} + +#define SET_RLIMIT(x) do ; while (0) +#define LIMIT get_fs_limit(rwfd) +#define FILENAME "testdir.ext2/rwfile" + +#include "common-7-8.h" diff --git a/tools/libaio/harness/cases/aio_setup.h b/tools/libaio/harness/cases/aio_setup.h new file mode 100644 index 0000000..37c9618 --- /dev/null +++ b/tools/libaio/harness/cases/aio_setup.h @@ -0,0 +1,98 @@ +io_context_t io_ctx; +#define BAD_CTX ((io_context_t)-1) + +void aio_setup(int n) +{ + int res = io_queue_init(n, &io_ctx); + if (res != 0) { + printf("io_queue_setup(%d) returned %d (%s)\n", + n, res, strerror(-res)); + exit(3); + } +} + +int attempt_io_submit(io_context_t ctx, long nr, struct iocb *ios[], int expect) +{ + int res; + + printf("expect %3d: io_submit(%10p, %3ld, %10p) = ", expect, ctx, nr, ios); + fflush(stdout); + res = io_submit(ctx, nr, ios); + printf("%3d [%s]%s\n", res, (res <= 0) ? strerror(-res) : "", + (res != expect) ? " -- FAILED" : ""); + if (res != expect) + return 1; + + return 0; +} + +int sync_submit(struct iocb *iocb) +{ + struct io_event event; + struct iocb *iocbs[] = { iocb }; + int res; + + /* 30 second timeout should be enough */ + struct timespec ts; + ts.tv_sec = 30; + ts.tv_nsec = 0; + + res = io_submit(io_ctx, 1, iocbs); + if (res != 1) { + printf("sync_submit: io_submit res=%d [%s]\n", res, strerror(-res)); + return res; + } + + res = io_getevents(io_ctx, 0, 1, &event, &ts); + if (res != 1) { + printf("sync_submit: io_getevents res=%d [%s]\n", res, strerror(-res)); + return res; + } + return event.res; +} + +#define SETUP aio_setup(1024) + + +#define READ 'r' +#define WRITE 'w' +#define READ_SILENT 'R' +#define WRITE_SILENT 'W' +int attempt_rw(int fd, void *buf, int count, long long pos, int rw, int expect) +{ + struct iocb iocb; + int res; + int silent = 0; + + switch(rw) { + case READ_SILENT: + silent = 1; + case READ: + io_prep_pread (&iocb, fd, buf, count, pos); + break; + case WRITE_SILENT: + silent = 1; + case WRITE: + io_prep_pwrite(&iocb, fd, buf, count, pos); + break; + } + + if (!silent) { + printf("expect %5d: (%c), res = ", expect, rw); + fflush(stdout); + } + res = sync_submit(&iocb); + if (!silent || res != expect) { + if (silent) + printf("expect %5d: (%c), res = ", expect, rw); + printf("%5d [%s]%s\n", res, + (res <= 0) ? strerror(-res) : "Success", + (res != expect) ? " -- FAILED" : ""); + } + + if (res != expect) + return 1; + + return 0; +} + diff --git a/tools/libaio/harness/cases/common-7-8.h b/tools/libaio/harness/cases/common-7-8.h new file mode 100644 index 0000000..3ec2bb4 --- /dev/null +++ b/tools/libaio/harness/cases/common-7-8.h @@ -0,0 +1,37 @@ +/* common-7-8.h +*/ +#include "aio_setup.h" + +#include + +#define SIZE 512 + +int test_main(void) +{ + char *buf; + int rwfd; + int status = 0, res; + long long limit; + + rwfd = open(FILENAME, O_RDWR); assert(rwfd != -1); + res = ftruncate(rwfd, 0); assert(res == 0); + buf = malloc(SIZE); assert(buf != NULL); + memset(buf, 0, SIZE); + + limit = LIMIT; + + SET_RLIMIT(limit); + + status |= attempt_rw(rwfd, buf, SIZE, limit-SIZE, WRITE, SIZE); + status |= attempt_rw(rwfd, buf, SIZE, limit-SIZE, READ, SIZE); + + status |= attempt_rw(rwfd, buf, SIZE, 1+limit-SIZE, WRITE, SIZE-1); + status |= attempt_rw(rwfd, buf, SIZE, 1+limit-SIZE, READ, SIZE-1); + + status |= attempt_rw(rwfd, buf, SIZE, limit, WRITE, -EFBIG); + status |= attempt_rw(rwfd, buf, SIZE, limit, READ, 0); + status |= attempt_rw(rwfd, buf, 0, limit, WRITE, 0); + + return status; +} + diff --git a/tools/libaio/harness/main.c b/tools/libaio/harness/main.c new file mode 100644 index 0000000..74b2764 --- /dev/null +++ b/tools/libaio/harness/main.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if defined(__i386__) +#define KERNEL_RW_POINTER ((void *)0xc0010000) +#else +//#warning Not really sure where kernel memory is. Guessing. +#define KERNEL_RW_POINTER ((void *)0xffffffffc0010000) +#endif + + +char test_name[] = TEST_NAME; + +#include TEST_NAME + +int main(void) +{ + int res; + +#if defined(SETUP) + SETUP; +#endif + + res = test_main(); + printf("test %s completed %s.\n", test_name, + res ? "FAILED" : "PASSED" + ); + fflush(stdout); + return res ? 1 : 0; +} diff --git a/tools/libaio/harness/runtests.sh b/tools/libaio/harness/runtests.sh new file mode 100644 index 0000000..d763d88 --- /dev/null +++ b/tools/libaio/harness/runtests.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +passes=0 +fails=0 + +echo "Test run starting at" `date` + +while [ $# -ge 1 ] ; do + this_test=$1 + shift + echo "Starting $this_test" + $this_test 2>&1 + res=$? + if [ $res -eq 0 ] ; then str="" ; passes=$[passes + 1] ; else str=" -- FAILED" ; fails=$[fails + 1] ; fi + echo "Completed $this_test with $res$str". +done + +echo "Pass: $passes Fail: $fails" +echo "Test run complete at" `date` diff --git a/tools/libaio/libaio.spec b/tools/libaio/libaio.spec new file mode 100644 index 0000000..bdcc5b2 --- /dev/null +++ b/tools/libaio/libaio.spec @@ -0,0 +1,187 @@ +Name: libaio +Version: 0.3.106 +Release: 1 +Summary: Linux-native asynchronous I/O access library +Copyright: LGPL +Group: System Environment/Libraries +Source: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-root +# Fix ExclusiveArch as we implement this functionality on more architectures +ExclusiveArch: i386 x86_64 ia64 s390 s390x ppc ppc64 ppc64pseries ppc64iseries alpha alphaev6 + +%description +The Linux-native asynchronous I/O facility ("async I/O", or "aio") has a +richer API and capability set than the simple POSIX async I/O facility. +This library, libaio, provides the Linux-native API for async I/O. +The POSIX async I/O facility requires this library in order to provide +kernel-accelerated async I/O capabilities, as do applications which +require the Linux-native async I/O API. + +%package devel +Summary: Development files for Linux-native asynchronous I/O access +Group: Development/System +Requires: libaio +Provides: libaio.so.1 + +%description devel +This package provides header files to include and libraries to link with +for the Linux-native asynchronous I/O facility ("async I/O", or "aio"). + +%prep +%setup + +%build +make + +%install +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +make install prefix=$RPM_BUILD_ROOT/usr \ + libdir=$RPM_BUILD_ROOT/%{_libdir} \ + root=$RPM_BUILD_ROOT + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(-,root,root) +%attr(0755,root,root) %{_libdir}/libaio.so.* +%doc COPYING TODO + +%files devel +%defattr(-,root,root) +%attr(0644,root,root) %{_includedir}/* +%attr(0755,root,root) %{_libdir}/libaio.so +%attr(0644,root,root) %{_libdir}/libaio.a + +%changelog +* Tue Jan 3 2006 Jeff Moyer - 0.3.106-1 +- Add a .proc directive for the ia64_aio_raw_syscall macro. This sounds a lot + like the previous entry, but that one fixed the __ia64_raw_syscall macro, + located in syscall-ia64.h. This macro is in raw_syscall.c, which pretty much + only exists for ia64. This bug prevented the package from building with + newer version of gcc. + +* Mon Aug 1 2005 Jeff Moyer - 0.3.105-1 +- Add a .proc directive for the ia64 raw syscall macro. + +* Fri Apr 1 2005 Jeff Moyer - 0.3.104-1 +- Add Alpha architecture support. (Sergey Tikhonov ) + +* Tue Jan 25 2005 Jeff Moyer - 0.3.103-1 +- Fix SONAME breakage. In changing file names around, I also changed the + SONAME, which is a no no. + +* Thu Oct 14 2004 Jeff Moyer - 0.3.102-1 +- S390 asm had a bug; I forgot to update the clobber list. Lucky for me, + newer compilers complain about such things. +- Also update the s390 asm to look more like the new kernel variants. + +* Wed Oct 13 2004 Jeff Moyer - 0.3.101-1 +- Revert syscall return values to be -ERRNO. This was an inadvertant bug + introduced when clobber lists changed. +- add ppc64pseries and ppc64iseries to exclusivearch + +* Tue Sep 14 2004 Jeff Moyer - 0.3.100-1 +- Switch around the tests for _PPC_ and _powerpc64_ so that the ppc64 + platforms get the right padding. + +* Wed Jul 14 2004 Jeff Moyer - 0.3.99-4 +- Ok, there was a race in moving the cvs module. Someone rebuild from + the old cvs into fc3. *sigh* bumping rev. + +* Wed Jul 14 2004 Jeff Moyer - 0.3.99-3 +- Actually provide libaio.so.1. + +* Tue Mar 30 2004 Jeff Moyer - 0.3.99-2 +- Apparently the 0.3.93 patch was not meant for 0.3.96. Backed it out. + +* Tue Mar 30 2004 Jeff Moyer - 0.3.99-1 +- Fix compat calls. +- make library .so.1.0.0 and make symlinks properly. +- Fix header file for inclusion in c++ code. + +* Thu Feb 26 2004 Jeff Moyer 0.3.98-2 +- bah. fix version nr in changelog. + +* Thu Feb 26 2004 Jeff Moyer 0.3.98-1 +- fix compiler warnings. + +* Thu Feb 26 2004 Jeff Moyer 0.3.97-2 +- make srpm was using rpm to do a build. changed that to use rpmbuild if + it exists, and fallback to rpm if it doesn't. + +* Tue Feb 24 2004 Jeff Moyer 0.3.97-1 +- Use libc syscall(2) instead of rolling our own calling mechanism. This + change is inspired due to a failure to build with newer gcc, since clobber + lists were wrong. +- Add -fpic to the CFLAGS for all architectures. Should address bz #109457. +- change a #include from to . Fixes a build + issue on s390. + +* Wed Jul 7 2003 Bill Nottingham 0.3.96-3 +- fix paths on lib64 arches + +* Wed Jun 18 2003 Michael K. Johnson 0.3.96-2 +- optimization in io_getevents from Arjan van de Ven in 0.3.96-1 +- deal with ia64 in 0.3.96-2 + +* Wed May 28 2003 Michael K. Johnson 0.3.95-1 +- ppc bugfix from Julie DeWandel + +* Tue May 20 2003 Michael K. Johnson 0.3.94-1 +- symbol versioning fix from Ulrich Drepper + +* Mon Jan 27 2003 Benjamin LaHaise +- bump to 0.3.93-3 for rebuild. + +* Mon Dec 16 2002 Benjamin LaHaise +- libaio 0.3.93 test release +- add powerpc support from Gianni Tedesco +- add s/390 support from Arnd Bergmann + +* Fri Sep 12 2002 Benjamin LaHaise +- libaio 0.3.92 test release +- build on x86-64 + +* Thu Sep 12 2002 Benjamin LaHaise +- libaio 0.3.91 test release +- build on ia64 +- remove libredhat-kernel from the .spec file + +* Thu Sep 5 2002 Benjamin LaHaise +- libaio 0.3.90 test release + +* Mon Apr 29 2002 Benjamin LaHaise +- add requires initscripts >= 6.47-1 to get boot time libredhat-kernel + linkage correct. +- typo fix + +* Thu Apr 25 2002 Benjamin LaHaise +- make /usr/lib/libredhat-kernel.so point to /lib/libredhat-kernel.so.1.0.0 + +* Mon Apr 15 2002 Tim Powers +- make the post scriptlet not use /bin/sh + +* Sat Apr 12 2002 Benjamin LaHaise +- add /lib/libredhat-kernel* to %files. + +* Fri Apr 12 2002 Benjamin LaHaise +- make the dummy install as /lib/libredhat-kernel.so.1.0.0 so + that ldconfig will link against it if no other is installed. + +* Tue Jan 22 2002 Benjamin LaHaise +- add io_getevents + +* Tue Jan 22 2002 Michael K. Johnson +- Make linker happy with /usr/lib symlink for libredhat-kernel.so + +* Mon Jan 21 2002 Michael K. Johnson +- Added stub library + +* Sun Jan 20 2002 Michael K. Johnson +- Initial packaging diff --git a/tools/libaio/man/aio.3 b/tools/libaio/man/aio.3 new file mode 100644 index 0000000..6dc3c63 --- /dev/null +++ b/tools/libaio/man/aio.3 @@ -0,0 +1,315 @@ +.TH aio 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio \- Asynchronous IO +.SH SYNOPSIS +.nf +.B #include +.sp +.br +.B #include +.sp +.fi +.SH DESCRIPTION +The POSIX.1b standard defines a new set of I/O operations which can +significantly reduce the time an application spends waiting at I/O. The +new functions allow a program to initiate one or more I/O operations and +then immediately resume normal work while the I/O operations are +executed in parallel. This functionality is available if the +.IR "unistd.h" +file defines the symbol +.B "_POSIX_ASYNCHRONOUS_IO" +. + +These functions are part of the library with realtime functions named +.IR "librt" +. They are not actually part of the +.IR "libc" +binary. +The implementation of these functions can be done using support in the +kernel (if available) or using an implementation based on threads at +userlevel. In the latter case it might be necessary to link applications +with the thread library +.IR "libpthread" +in addition to +.IR "librt" +and +.IR "libaio" +. + +All AIO operations operate on files which were opened previously. There +might be arbitrarily many operations running for one file. The +asynchronous I/O operations are controlled using a data structure named +.IR "struct aiocb" +It is defined in +.IR "aio.h" + as follows. + +.nf +struct aiocb +{ + int aio_fildes; /* File desriptor. */ + int aio_lio_opcode; /* Operation to be performed. */ + int aio_reqprio; /* Request priority offset. */ + volatile void *aio_buf; /* Location of buffer. */ + size_t aio_nbytes; /* Length of transfer. */ + struct sigevent aio_sigevent; /* Signal number and value. */ + + /* Internal members. */ + struct aiocb *__next_prio; + int __abs_prio; + int __policy; + int __error_code; + __ssize_t __return_value; + +#ifndef __USE_FILE_OFFSET64 + __off_t aio_offset; /* File offset. */ + char __pad[sizeof (__off64_t) - sizeof (__off_t)]; +#else + __off64_t aio_offset; /* File offset. */ +#endif + char __unused[32]; +}; + +.fi +The POSIX.1b standard mandates that the +.IR "struct aiocb" +structure +contains at least the members described in the following table. There +might be more elements which are used by the implementation, but +depending upon these elements is not portable and is highly deprecated. + +.TP +.IR "int aio_fildes" +This element specifies the file descriptor to be used for the +operation. It must be a legal descriptor, otherwise the operation will +fail. + +The device on which the file is opened must allow the seek operation. +I.e., it is not possible to use any of the AIO operations on devices +like terminals where an +.IR "lseek" + call would lead to an error. +.TP +.IR "off_t aio_offset" +This element specifies the offset in the file at which the operation (input +or output) is performed. Since the operations are carried out in arbitrary +order and more than one operation for one file descriptor can be +started, one cannot expect a current read/write position of the file +descriptor. +.TP +.IR "volatile void *aio_buf" +This is a pointer to the buffer with the data to be written or the place +where the read data is stored. +.TP +.IR "size_t aio_nbytes" +This element specifies the length of the buffer pointed to by +.IR "aio_buf" +. +.TP +.IR "int aio_reqprio" +If the platform has defined +.B "_POSIX_PRIORITIZED_IO" +and +.B "_POSIX_PRIORITY_SCHEDULING" +, the AIO requests are +processed based on the current scheduling priority. The +.IR "aio_reqprio" +element can then be used to lower the priority of the +AIO operation. +.TP +.IR "struct sigevent aio_sigevent" +This element specifies how the calling process is notified once the +operation terminates. If the +.IR "sigev_notify" +element is +.B "SIGEV_NONE" +, no notification is sent. If it is +.B "SIGEV_SIGNAL" +, +the signal determined by +.IR "sigev_signo" +is sent. Otherwise, +.IR "sigev_notify" +must be +.B "SIGEV_THREAD" +. In this case, a thread +is created which starts executing the function pointed to by +.IR "sigev_notify_function" +. +.TP +.IR "int aio_lio_opcode" +This element is only used by the +.IR "lio_listio" + and +.IR "lio_listio64" + functions. Since these functions allow an +arbitrary number of operations to start at once, and each operation can be +input or output (or nothing), the information must be stored in the +control block. The possible values are: +.TP +.B "LIO_READ" +Start a read operation. Read from the file at position +.IR "aio_offset" + and store the next +.IR "aio_nbytes" + bytes in the +buffer pointed to by +.IR "aio_buf" +. +.TP +.B "LIO_WRITE" +Start a write operation. Write +.IR "aio_nbytes" +bytes starting at +.IR "aio_buf" +into the file starting at position +.IR "aio_offset" +. +.TP +.B "LIO_NOP" +Do nothing for this control block. This value is useful sometimes when +an array of +.IR "struct aiocb" +values contains holes, i.e., some of the +values must not be handled although the whole array is presented to the +.IR "lio_listio" +function. + +When the sources are compiled using +.B "_FILE_OFFSET_BITS == 64" +on a +32 bit machine, this type is in fact +.IR "struct aiocb64" +, since the LFS +interface transparently replaces the +.IR "struct aiocb" +definition. +.PP +For use with the AIO functions defined in the LFS, there is a similar type +defined which replaces the types of the appropriate members with larger +types but otherwise is equivalent to +.IR "struct aiocb" +. Particularly, +all member names are the same. + +.nf +/* The same for the 64bit offsets. Please note that the members aio_fildes + to __return_value have to be the same in aiocb and aiocb64. */ +#ifdef __USE_LARGEFILE64 +struct aiocb64 +{ + int aio_fildes; /* File desriptor. */ + int aio_lio_opcode; /* Operation to be performed. */ + int aio_reqprio; /* Request priority offset. */ + volatile void *aio_buf; /* Location of buffer. */ + size_t aio_nbytes; /* Length of transfer. */ + struct sigevent aio_sigevent; /* Signal number and value. */ + + /* Internal members. */ + struct aiocb *__next_prio; + int __abs_prio; + int __policy; + int __error_code; + __ssize_t __return_value; + + __off64_t aio_offset; /* File offset. */ + char __unused[32]; +}; + +.fi +.TP +.IR "int aio_fildes" +This element specifies the file descriptor which is used for the +operation. It must be a legal descriptor since otherwise the operation +fails for obvious reasons. +The device on which the file is opened must allow the seek operation. +I.e., it is not possible to use any of the AIO operations on devices +like terminals where an +.IR "lseek" + call would lead to an error. +.TP +.IR "off64_t aio_offset" +This element specifies at which offset in the file the operation (input +or output) is performed. Since the operation are carried in arbitrary +order and more than one operation for one file descriptor can be +started, one cannot expect a current read/write position of the file +descriptor. +.TP +.IR "volatile void *aio_buf" +This is a pointer to the buffer with the data to be written or the place +where the read data is stored. +.TP +.IR "size_t aio_nbytes" +This element specifies the length of the buffer pointed to by +.IR "aio_buf" +. +.TP +.IR "int aio_reqprio" +If for the platform +.B "_POSIX_PRIORITIZED_IO" +and +.B "_POSIX_PRIORITY_SCHEDULING" +are defined the AIO requests are +processed based on the current scheduling priority. The +.IR "aio_reqprio" +element can then be used to lower the priority of the +AIO operation. +.TP +.IR "struct sigevent aio_sigevent" +This element specifies how the calling process is notified once the +operation terminates. If the +.IR "sigev_notify" +, element is +.B "SIGEV_NONE" +no notification is sent. If it is +.B "SIGEV_SIGNAL" +, +the signal determined by +.IR "sigev_signo" +is sent. Otherwise, +.IR "sigev_notify" + must be +.B "SIGEV_THREAD" +in which case a thread +which starts executing the function pointed to by +.IR "sigev_notify_function" +. +.TP +.IR "int aio_lio_opcode" +This element is only used by the +.IR "lio_listio" +and +.IR "lio_listio64" +functions. Since these functions allow an +arbitrary number of operations to start at once, and since each operation can be +input or output (or nothing), the information must be stored in the +control block. See the description of +.IR "struct aiocb" +for a description +of the possible values. +.PP +When the sources are compiled using +.B "_FILE_OFFSET_BITS == 64" +on a +32 bit machine, this type is available under the name +.IR "struct aiocb64" +, since the LFS transparently replaces the old interface. +.SH "RETURN VALUES" +.SH ERRORS +.SH "SEE ALSO" +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_cancel.3 b/tools/libaio/man/aio_cancel.3 new file mode 100644 index 0000000..502c83c --- /dev/null +++ b/tools/libaio/man/aio_cancel.3 @@ -0,0 +1,137 @@ +.TH aio_cancel 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_cancel - Cancel asynchronous I/O requests +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_cancel (int fildes " , struct aiocb *aiocbp " )" +.fi +.SH DESCRIPTION +When one or more requests are asynchronously processed, it might be +useful in some situations to cancel a selected operation, e.g., if it +becomes obvious that the written data is no longer accurate and would +have to be overwritten soon. As an example, assume an application, which +writes data in files in a situation where new incoming data would have +to be written in a file which will be updated by an enqueued request. +The POSIX AIO implementation provides such a function, but this function +is not capable of forcing the cancellation of the request. It is up to the +implementation to decide whether it is possible to cancel the operation +or not. Therefore using this function is merely a hint. +.B "The libaio implementation does not implement the cancel operation in the" +.B "POSIX libraries". +.PP +The +.IR aio_cancel +function can be used to cancel one or more +outstanding requests. If the +.IR aiocbp +parameter is +.IR NULL +, the +function tries to cancel all of the outstanding requests which would process +the file descriptor +.IR fildes +(i.e., whose +.IR aio_fildes +member +is +.IR fildes +). If +.IR aiocbp is not +.IR NULL +, +.IR aio_cancel +attempts to cancel the specific request pointed to by +.IR aiocbp. + +For requests which were successfully canceled, the normal notification +about the termination of the request should take place. I.e., depending +on the +.IR "struct sigevent" +object which controls this, nothing +happens, a signal is sent or a thread is started. If the request cannot +be canceled, it terminates the usual way after performing the operation. +After a request is successfully canceled, a call to +.IR aio_error +with +a reference to this request as the parameter will return +.B ECANCELED +and a call to +.IR aio_return +will return +.IR -1. +If the request wasn't canceled and is still running the error status is +still +.B EINPROGRESS. +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +, this +function is in fact +.IR aio_cancel64 +since the LFS interface +transparently replaces the normal implementation. + +.SH "RETURN VALUES" +.TP +.B AIO_CANCELED +If there were +requests which haven't terminated and which were successfully canceled. +.TP +.B AIO_NOTCANCELED +If there is one or more requests left which couldn't be canceled, +. In this case +.IR aio_error +must be used to find out which of the, perhaps multiple, requests (in +.IR aiocbp +is +.IR NULL +) weren't successfully canceled. +.TP +.B AIO_ALLDONE +If all +requests already terminated at the time +.IR aio_cancel +is called the +return value is +. +.SH ERRORS +If an error occurred during the execution of +.IR aio_cancel +the +function returns +.IR -1 +and sets +.IR errno +to one of the following +values. +.TP +.B EBADF +The file descriptor +.IR fildes +is not valid. +.TP +.B ENOSYS +.IR aio_cancel +is not implemented. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_cancel64.3 b/tools/libaio/man/aio_cancel64.3 new file mode 100644 index 0000000..ede775b --- /dev/null +++ b/tools/libaio/man/aio_cancel64.3 @@ -0,0 +1,50 @@ +.TH aio_cancel64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_cancel64 \- Cancel asynchronous I/O requests +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_cancel64 (int fildes, struct aiocb64 *aiocbp)" +.fi +.SH DESCRIPTION +This function is similar to +.IR aio_cancel +with the only difference +that the argument is a reference to a variable of type +.IR struct aiocb64 +. + +When the sources are compiled with +.IR _FILE_OFFSET_BITS == 64 +, this +function is available under the name +.IR aio_cancel +and so +transparently replaces the interface for small files on 32 bit +machines. +.SH "RETURN VALUES" +See aio_cancel(3). +.SH ERRORS +See aio_cancel(3). +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_error.3 b/tools/libaio/man/aio_error.3 new file mode 100644 index 0000000..12b82cf --- /dev/null +++ b/tools/libaio/man/aio_error.3 @@ -0,0 +1,81 @@ +.TH aio_error 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_error \- Getting the Status of AIO Operations +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_error (const struct aiocb *aiocbp)" +.fi +.SH DESCRIPTION +The function +.IR aio_error +determines the error state of the request described by the +.IR "struct aiocb" +variable pointed to by +.I aiocbp +. + +When the operation is performed truly asynchronously (as with +.IR "aio_read" +and +.IR "aio_write" +and with +.IR "lio_listio" +when the mode is +.IR "LIO_NOWAIT" +), one sometimes needs to know whether a +specific request already terminated and if so, what the result was. +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this function is in fact +.IR "aio_error64" +since the LFS interface transparently replaces the normal implementation. +.SH "RETURN VALUES" +If the request has not yet terminated the value returned is always +.IR "EINPROGRESS" +. Once the request has terminated the value +.IR "aio_error" +returns is either +.I 0 +if the request completed successfully or it returns the value which would be stored in the +.IR "errno" +variable if the request would have been done using +.IR "read" +, +.IR "write" +, or +.IR "fsync" +. +.SH ERRORS +.TP +.IR "ENOSYS" +if it is not implemented. It +could also return +.TP +.IR "EINVAL" +if the +.I aiocbp +parameter does not +refer to an asynchronous operation whose return status is not yet known. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_error64.3 b/tools/libaio/man/aio_error64.3 new file mode 100644 index 0000000..3333161 --- /dev/null +++ b/tools/libaio/man/aio_error64.3 @@ -0,0 +1,64 @@ +.TH aio_error64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_error64 \- Return errors +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_error64 (const struct aiocb64 *aiocbp)" +.fi +.SH DESCRIPTION +This function is similar to +.IR aio_error +with the only difference +that the argument is a reference to a variable of type +.IR "struct aiocb64". +.PP +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this +function is available under the name +.IR aio_error +and so +transparently replaces the interface for small files on 32 bit +machines. +.SH "RETURN VALUES" +If the request has not yet terminated the value returned is always +.IR "EINPROGRESS" +. Once the request has terminated the value +.IR "aio_error" +returns is either +.I 0 +if the request completed successfully or it returns the value which would be stored in the +.IR "errno" +variable if the request would have been done using +.IR "read" +, +.IR "write" +, or +.IR "fsync" +. +.SH ERRORS +See +.IR aio_error(3). +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_fsync.3 b/tools/libaio/man/aio_fsync.3 new file mode 100644 index 0000000..637f0f6 --- /dev/null +++ b/tools/libaio/man/aio_fsync.3 @@ -0,0 +1,139 @@ +.TH aio_fsync 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_fsync \- Synchronize a file's complete in-core state with that on disk +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_fsync (int op, struct aiocb aiocbp)" +.fi +.SH DESCRIPTION +.PP +When dealing with asynchronous operations it is sometimes necessary to +get into a consistent state. This would mean for AIO that one wants to +know whether a certain request or a group of request were processed. +This could be done by waiting for the notification sent by the system +after the operation terminated, but this sometimes would mean wasting +resources (mainly computation time). Instead POSIX.1b defines two +functions which will help with most kinds of consistency. +.PP +The +.IR aio_fsync +and +.IR "aio_fsync64" +functions are only available +if the symbol +.IR "_POSIX_SYNCHRONIZED_IO" +is defined in +.I unistd.h +. + +Calling this function forces all I/O operations operating queued at the +time of the function call operating on the file descriptor +.IR "aiocbp->aio_fildes" +into the synchronized I/O completion state . The +.IR "aio_fsync" +function returns +immediately but the notification through the method described in +.IR "aiocbp->aio_sigevent" +will happen only after all requests for this +file descriptor have terminated and the file is synchronized. This also +means that requests for this very same file descriptor which are queued +after the synchronization request are not affected. + +If +.IR "op" +is +.IR "O_DSYNC" +the synchronization happens as with a call +to +.IR "fdatasync" +. Otherwise +.IR "op" +should be +.IR "O_SYNC" +and +the synchronization happens as with +.IR "fsync" +. + +As long as the synchronization has not happened, a call to +.IR "aio_error" +with the reference to the object pointed to by +.IR "aiocbp" +returns +.IR "EINPROGRESS" +. Once the synchronization is +done +.IR "aio_error" +return +.IR 0 +if the synchronization was not +successful. Otherwise the value returned is the value to which the +.IR "fsync" +or +.IR "fdatasync" +function would have set the +.IR "errno" +variable. In this case nothing can be assumed about the +consistency for the data written to this file descriptor. + +.SH "RETURN VALUES" +The return value of this function is +.IR 0 +if the request was +successfully enqueued. Otherwise the return value is +.IR -1 +and +.IR "errno". +.SH ERRORS +.TP +.B EAGAIN +The request could not be enqueued due to temporary lack of resources. +.TP +.B EBADF +The file descriptor +.IR "aiocbp->aio_fildes" +is not valid or not open +for writing. +.TP +.B EINVAL +The implementation does not support I/O synchronization or the +.IR "op" +parameter is other than +.IR "O_DSYNC" +and +.IR "O_SYNC" +. +.TP +.B ENOSYS +This function is not implemented. +.PP +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" + this +function is in fact +.IR "aio_return64" +since the LFS interface +transparently replaces the normal implementation. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_fsync64.3 b/tools/libaio/man/aio_fsync64.3 new file mode 100644 index 0000000..5dce22d --- /dev/null +++ b/tools/libaio/man/aio_fsync64.3 @@ -0,0 +1,51 @@ +.TH aio_fsync64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_fsync64 \- Synchronize a file's complete in-core state with that on disk +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_fsync64 (int op, struct aiocb64 *aiocbp)" +.fi +.SH DESCRIPTION +This function is similar to +.IR aio_fsync +with the only difference +that the argument is a reference to a variable of type +.IR "struct aiocb64". + +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this +function is available under the name +.IR aio_fsync +and so +transparently replaces the interface for small files on 32 bit +machines. +.SH "RETURN VALUES" +See +.IR aio_fsync. +.SH ERRORS +See +.IR aio_fsync. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_init.3 b/tools/libaio/man/aio_init.3 new file mode 100644 index 0000000..3b0ec95 --- /dev/null +++ b/tools/libaio/man/aio_init.3 @@ -0,0 +1,96 @@ +.TH aio_init 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_init \- How to optimize the AIO implementation +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "void aio_init (const struct aioinit *init)" +.fi +.SH DESCRIPTION + +The POSIX standard does not specify how the AIO functions are +implemented. They could be system calls, but it is also possible to +emulate them at userlevel. + +At the point of this writing, the available implementation is a userlevel +implementation which uses threads for handling the enqueued requests. +While this implementation requires making some decisions about +limitations, hard limitations are something which is best avoided +in the GNU C library. Therefore, the GNU C library provides a means +for tuning the AIO implementation according to the individual use. + +.BI "struct aioinit" +.PP +This data type is used to pass the configuration or tunable parameters +to the implementation. The program has to initialize the members of +this struct and pass it to the implementation using the +.IR aio_init +function. +.TP +.B "int aio_threads" +This member specifies the maximal number of threads which may be used +at any one time. +.TP +.B "int aio_num" +This number provides an estimate on the maximal number of simultaneously +enqueued requests. +.TP +.B "int aio_locks" +Unused. +.TP +.B "int aio_usedba" +Unused. +.TP +.B "int aio_debug" +Unused. +.TP +.B "int aio_numusers" +Unused. +.TP +.B "int aio_reserved[2]" +Unused. +.PP +This function must be called before any other AIO function. Calling it +is completely voluntary, as it is only meant to help the AIO +implementation perform better. + +Before calling the +.IR aio_init +, function the members of a variable of +type +.IR "struct aioinit" +must be initialized. Then a reference to +this variable is passed as the parameter to +.IR aio_init +which itself +may or may not pay attention to the hints. + +It is a extension which follows a proposal from the SGI implementation in +.IR Irix 6 +. It is not covered by POSIX.1b or Unix98. +.SH "RETURN VALUES" +The function has no return value. +.SH ERRORS +The function has no error cases defined. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_read.3 b/tools/libaio/man/aio_read.3 new file mode 100644 index 0000000..5bcb6c8 --- /dev/null +++ b/tools/libaio/man/aio_read.3 @@ -0,0 +1,146 @@ +.TH aio_read 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_read \- Initiate an asynchronous read operation +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_read (struct aiocb *aiocbp)" +.fi +.SH DESCRIPTION +This function initiates an asynchronous read operation. It +immediately returns after the operation was enqueued or when an +error was encountered. + +The first +.IR "aiocbp->aio_nbytes" +bytes of the file for which +.IR "aiocbp->aio_fildes" +is a descriptor are written to the buffer +starting at +.IR "aiocbp->aio_buf" +. Reading starts at the absolute +position +.IR "aiocbp->aio_offset" +in the file. + +If prioritized I/O is supported by the platform the +.IR "aiocbp->aio_reqprio" +value is used to adjust the priority before +the request is actually enqueued. + +The calling process is notified about the termination of the read +request according to the +.IR "aiocbp->aio_sigevent" +value. + +.SH "RETURN VALUES" +When +.IR "aio_read" +returns, the return value is zero if no error +occurred that can be found before the process is enqueued. If such an +early error is found, the function returns +.IR -1 +and sets +.IR "errno". + +.PP +If +.IR "aio_read" +returns zero, the current status of the request +can be queried using +.IR "aio_error" +and +.IR "aio_return" +functions. +As long as the value returned by +.IR "aio_error" +is +.IR "EINPROGRESS" +the operation has not yet completed. If +.IR "aio_error" +returns zero, +the operation successfully terminated, otherwise the value is to be +interpreted as an error code. If the function terminated, the result of +the operation can be obtained using a call to +.IR "aio_return" +. The +returned value is the same as an equivalent call to +.IR "read" +would +have returned. +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this +function is in fact +.IR "aio_read64" +since the LFS interface transparently +replaces the normal implementation. + +.SH ERRORS +In the case of an early error: +.TP +.B EAGAIN +The request was not enqueued due to (temporarily) exceeded resource +limitations. +.TP +.B ENOSYS +The +.IR "aio_read" +function is not implemented. +.TP +.B EBADF +The +.IR "aiocbp->aio_fildes" +descriptor is not valid. This condition +need not be recognized before enqueueing the request and so this error +might also be signaled asynchronously. +.TP +.B EINVAL +The +.IR "aiocbp->aio_offset" +or +.IR "aiocbp->aio_reqpiro" +value is +invalid. This condition need not be recognized before enqueueing the +request and so this error might also be signaled asynchronously. + +.PP +In the case of a normal return, possible error codes returned by +.IR "aio_error" +are: +.TP +.B EBADF +The +.IR "aiocbp->aio_fildes" +descriptor is not valid. +.TP +.B ECANCELED +The operation was canceled before the operation was finished +.TP +.B EINVAL +The +.IR "aiocbp->aio_offset" +value is invalid. +.PP +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_read64.3 b/tools/libaio/man/aio_read64.3 new file mode 100644 index 0000000..8e407a5 --- /dev/null +++ b/tools/libaio/man/aio_read64.3 @@ -0,0 +1,60 @@ +.TH aio_read64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_read64 \- Initiate an asynchronous read operation +.SH SYNOPSYS +.nf +.B #include +.br +.B #include +.sp +.br +.BI "int aio_read64 (struct aiocb *aiocbp)" +.fi +.SH DESCRIPTION +This function is similar to the +.IR "aio_read" +function. The only +difference is that on +.IR "32 bit" +machines, the file descriptor should +be opened in the large file mode. Internally, +.IR "aio_read64" +uses +functionality equivalent to +.IR "lseek64" +to position the file descriptor correctly for the reading, +as opposed to +.IR "lseek" +functionality used in +.IR "aio_read". + +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +, this +function is available under the name +.IR "aio_read" +and so transparently +replaces the interface for small files on 32 bit machines. +.SH "RETURN VALUES" +See +.IR aio_read. +.SH ERRORS +See +.IR aio_read. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_return.3 b/tools/libaio/man/aio_return.3 new file mode 100644 index 0000000..1e3335f --- /dev/null +++ b/tools/libaio/man/aio_return.3 @@ -0,0 +1,71 @@ +.TH aio_return 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_return \- Retrieve status of asynchronous I/O operation +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "ssize_t aio_return (const struct aiocb *aiocbp)" +.fi +.SH DESCRIPTION +This function can be used to retrieve the return status of the operation +carried out by the request described in the variable pointed to by +.IR aiocbp +. As long as the error status of this request as returned +by +.IR aio_error +is +.IR EINPROGRESS +the return of this function is +undefined. + +Once the request is finished this function can be used exactly once to +retrieve the return value. Following calls might lead to undefined +behavior. +When the sources are compiled with +.B "_FILE_OFFSET_BITS == 64" +this function is in fact +.IR aio_return64 +since the LFS interface +transparently replaces the normal implementation. +.SH "RETURN VALUES" +The return value itself is the value which would have been +returned by the +.IR read +, +.IR write +, or +.IR fsync +call. +.SH ERRORS +The function can return +.TP +.B ENOSYS +if it is not implemented. +.TP +.B EINVAL +if the +.IR aiocbp +parameter does not +refer to an asynchronous operation whose return status is not yet known. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_return64.3 b/tools/libaio/man/aio_return64.3 new file mode 100644 index 0000000..7e78362 --- /dev/null +++ b/tools/libaio/man/aio_return64.3 @@ -0,0 +1,51 @@ +.TH aio_read64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_read64 \- Retrieve status of asynchronous I/O operation +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_return64 (const struct aiocb64 *aiocbp)" +.fi +.SH DESCRIPTION +This function is similar to +.IR "aio_return" +with the only difference +that the argument is a reference to a variable of type +.IR "struct aiocb64". + +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this +function is available under the name +.IR "aio_return" +and so +transparently replaces the interface for small files on 32 bit +machines. +.SH "RETURN VALUES" +See +.IR aio_return. +.SH ERRORS +See +.IR aio_return. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_suspend.3 b/tools/libaio/man/aio_suspend.3 new file mode 100644 index 0000000..cae1b65 --- /dev/null +++ b/tools/libaio/man/aio_suspend.3 @@ -0,0 +1,123 @@ +.TH aio_suspend 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_suspend \- Wait until one or more requests of a specific set terminates. +.SH SYNOPSYS +.nf +.B "#include " +.sp +.br +.B "#include " +.sp +.br +.BI "int aio_suspend (const struct aiocb *const list[], int nent, const struct timespec *timeout)" +.fi +.SH DESCRIPTION +Another method of synchronization is to wait until one or more requests of a +specific set terminated. This could be achieved by the +.IR "aio_*" +functions to notify the initiating process about the termination but in +some situations this is not the ideal solution. In a program which +constantly updates clients somehow connected to the server it is not +always the best solution to go round robin since some connections might +be slow. On the other hand letting the +.IR "aio_*" +function notify the +caller might also be not the best solution since whenever the process +works on preparing data for on client it makes no sense to be +interrupted by a notification since the new client will not be handled +before the current client is served. For situations like this +.IR "aio_suspend" +should be used. +.PP +When calling this function, the calling thread is suspended until at +least one of the requests pointed to by the +.IR "nent" +elements of the +array +.IR "list" +has completed. If any of the requests has already +completed at the time +.IR "aio_suspend" +is called, the function returns +immediately. Whether a request has terminated or not is determined by +comparing the error status of the request with +.IR "EINPROGRESS" +. If +an element of +.IR "list" +is +.IR "NULL" +, the entry is simply ignored. + +If no request has finished, the calling process is suspended. If +.IR "timeout" +is +.IR "NULL" +, the process is not woken until a request +has finished. If +.IR "timeout" +is not +.IR "NULL" +, the process remains +suspended at least as long as specified in +.IR "timeout" +. In this case, +.IR "aio_suspend" +returns with an error. +.PP +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this +function is in fact +.IR "aio_suspend64" +since the LFS interface +transparently replaces the normal implementation. +.SH "RETURN VALUES" +The return value of the function is +.IR 0 +if one or more requests +from the +.IR "list" +have terminated. Otherwise the function returns +.IR -1 +and +.IR "errno" +is set. +.SH ERRORS +.TP +.B EAGAIN +None of the requests from the +.IR "list" +completed in the time specified +by +.IR "timeout" +. +.TP +.B EINTR +A signal interrupted the +.IR "aio_suspend" +function. This signal might +also be sent by the AIO implementation while signalling the termination +of one of the requests. +.TP +.B ENOSYS +The +.IR "aio_suspend" +function is not implemented. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_suspend64.3 b/tools/libaio/man/aio_suspend64.3 new file mode 100644 index 0000000..2f289ec --- /dev/null +++ b/tools/libaio/man/aio_suspend64.3 @@ -0,0 +1,51 @@ +.TH aio_suspend64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_suspend64 \- Wait until one or more requests of a specific set terminates +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_suspend64 (const struct aiocb64 *const list[], int nent, const struct timespec *timeout)" +.fi +.SH DESCRIPTION +This function is similar to +.IR "aio_suspend" +with the only difference +that the argument is a reference to a variable of type +.IR "struct aiocb64". + +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +this +function is available under the name +.IR "aio_suspend" +and so +transparently replaces the interface for small files on 32 bit +machines. +.SH "RETURN VALUES" +See +.IR aio_suspend. +.SH ERRORS +See +.IR aio_suspend. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_write(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_write.3 b/tools/libaio/man/aio_write.3 new file mode 100644 index 0000000..7c0cfd0 --- /dev/null +++ b/tools/libaio/man/aio_write.3 @@ -0,0 +1,176 @@ +.TH aio_write 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_write \- Initiate an asynchronous write operation +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_write (struct aiocb * aiocbp);" +.fi +.SH DESCRIPTION +This function initiates an asynchronous write operation. The function +call immediately returns after the operation was enqueued or if before +this happens an error was encountered. + +The first +.IR "aiocbp->aio_nbytes" +bytes from the buffer starting at +.IR "aiocbp->aio_buf" +are written to the file for which +.IR "aiocbp->aio_fildes" +is an descriptor, starting at the absolute +position +.IR "aiocbp->aio_offset" +in the file. + +If prioritized I/O is supported by the platform, the +.IR "aiocbp->aio_reqprio " +value is used to adjust the priority before +the request is actually enqueued. + +The calling process is notified about the termination of the read +request according to the +.IR "aiocbp->aio_sigevent" +value. + +When +.IR "aio_write" +returns, the return value is zero if no error +occurred that can be found before the process is enqueued. If such an +early error is found the function returns +.IR -1 +and sets +.IR "errno" +to one of the following values. + +.TP +.B EAGAIN +The request was not enqueued due to (temporarily) exceeded resource +limitations. +.TP +.B ENOSYS +The +.IR "aio_write" +function is not implemented. +.TP +.B EBADF +The +.IR "aiocbp->aio_fildes" +descriptor is not valid. This condition +may not be recognized before enqueueing the request, and so this error +might also be signaled asynchronously. +.TP +.B EINVAL +The +.IR "aiocbp->aio_offset" +or +.IR "aiocbp->aio_reqprio" +value is +invalid. This condition may not be recognized before enqueueing the +request and so this error might also be signaled asynchronously. +.PP + +In the case +.IR "aio_write" +returns zero, the current status of the +request can be queried using +.IR "aio_error" +and +.IR "aio_return" +functions. As long as the value returned by +.IR "aio_error" +is +.IR "EINPROGRESS" +the operation has not yet completed. If +.IR "aio_error" +returns zero, the operation successfully terminated, +otherwise the value is to be interpreted as an error code. If the +function terminated, the result of the operation can be get using a call +to +.IR "aio_return" +. The returned value is the same as an equivalent +call to +.IR "read" +would have returned. Possible error codes returned +by +.IR "aio_error" +are: + +.TP +.B EBADF +The +.IR "aiocbp->aio_fildes" +descriptor is not valid. +.TP +.B ECANCELED +The operation was canceled before the operation was finished. +.TP +.B EINVAL +The +.IR "aiocbp->aio_offset" +value is invalid. +.PP +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +, this +function is in fact +.IR "aio_write64" +since the LFS interface transparently +replaces the normal implementation. +.SH "RETURN VALUES" +When +.IR "aio_write" +returns, the return value is zero if no error +occurred that can be found before the process is enqueued. If such an +early error is found the function returns +.IR -1 +and sets +.IR "errno" +to one of the following values. +.SH ERRORS +.TP +.B EAGAIN +The request was not enqueued due to (temporarily) exceeded resource +limitations. +.TP +.B ENOSYS +The +.IR "aio_write" +function is not implemented. +.TP +.B EBADF +The +.IR "aiocbp->aio_fildes" +descriptor is not valid. This condition +may not be recognized before enqueueing the request, and so this error +might also be signaled asynchronously. +.TP +.B EINVAL +The +.IR "aiocbp->aio_offset" +or +.IR "aiocbp->aio_reqprio" +value is +invalid. This condition may not be recognized before enqueueing the +request and so this error might also be signaled asynchronously. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write64(3), +.BR errno(3), diff --git a/tools/libaio/man/aio_write64.3 b/tools/libaio/man/aio_write64.3 new file mode 100644 index 0000000..1080903 --- /dev/null +++ b/tools/libaio/man/aio_write64.3 @@ -0,0 +1,61 @@ +.TH aio_write64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +aio_write64 \- Initiate an asynchronous write operation +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int aio_write64 (struct aiocb *aiocbp)" +.fi +.SH DESCRIPTION +This function is similar to the +.IR "aio_write" +function. The only +difference is that on +.IR "32 bit" +machines the file descriptor should +be opened in the large file mode. Internally +.IR "aio_write64" +uses +functionality equivalent to +.IR "lseek64" +to position the file descriptor correctly for the writing, +as opposed to +.IR "lseek" +functionality used in +.IR "aio_write". + +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +, this +function is available under the name +.IR "aio_write" +and so transparently +replaces the interface for small files on 32 bit machines. +.SH "RETURN VALUES" +See +.IR aio_write. +.SH ERRORS +See +.IR aio_write. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR errno(3), diff --git a/tools/libaio/man/io.3 b/tools/libaio/man/io.3 new file mode 100644 index 0000000..d910a68 --- /dev/null +++ b/tools/libaio/man/io.3 @@ -0,0 +1,351 @@ +.TH io 3 2002-09-12 "Linux 2.4" Linux IO" +.SH NAME +io \- Asynchronous IO +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.fi +.SH DESCRIPTION +The libaio library defines a new set of I/O operations which can +significantly reduce the time an application spends waiting at I/O. The +new functions allow a program to initiate one or more I/O operations and +then immediately resume normal work while the I/O operations are +executed in parallel. + +These functions are part of the library with realtime functions named +.IR "libaio" +. They are not actually part of the +.IR "libc" +binary. +The implementation of these functions can be done using support in the +kernel. + +All IO operations operate on files which were opened previously. There +might be arbitrarily many operations running for one file. The +asynchronous I/O operations are controlled using a data structure named +.IR "struct iocb" +It is defined in +.IR "libio.h" +as follows. + +.nf + +typedef struct io_context *io_context_t; + +typedef enum io_iocb_cmd { + IO_CMD_PREAD = 0, + IO_CMD_PWRITE = 1, + + IO_CMD_FSYNC = 2, + IO_CMD_FDSYNC = 3, + + IO_CMD_POLL = 5, + IO_CMD_NOOP = 6, +} io_iocb_cmd_t; + +struct io_iocb_common { + void *buf; + unsigned __pad1; + long nbytes; + unsigned __pad2; + long long offset; + long long __pad3, __pad4; +}; /* result code is the amount read or -'ve errno */ + + +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; + union { + struct io_iocb_common c; + struct io_iocb_vector v; + struct io_iocb_poll poll; + struct io_iocb_sockaddr saddr; + } u; +}; + + +.fi +.TP +.IR "int aio_fildes" +This element specifies the file descriptor to be used for the +operation. It must be a legal descriptor, otherwise the operation will +fail. + +The device on which the file is opened must allow the seek operation. +I.e., it is not possible to use any of the IO operations on devices +like terminals where an +.IR "lseek" +call would lead to an error. +.TP +.IR "long u.c.offset" +This element specifies the offset in the file at which the operation (input +or output) is performed. Since the operations are carried out in arbitrary +order and more than one operation for one file descriptor can be +started, one cannot expect a current read/write position of the file +descriptor. +.TP +.IR "void *buf" +This is a pointer to the buffer with the data to be written or the place +where the read data is stored. +.TP +.IR "long u.c.nbytes" +This element specifies the length of the buffer pointed to by +.IR "io_buf" +. +.TP +.IR "int aio_reqprio" +Is not currently used. +.TP +.B "IO_CMD_PREAD" +Start a read operation. Read from the file at position +.IR "u.c.offset" +and store the next +.IR "u.c.nbytes" +bytes in the +buffer pointed to by +.IR "buf" +. +.TP +.B "IO_CMD_PWRITE" +Start a write operation. Write +.IR "u.c.nbytes" +bytes starting at +.IR "buf" +into the file starting at position +.IR "u.c.offset" +. +.TP +.B "IO_CMD_NOP" +Do nothing for this control block. This value is useful sometimes when +an array of +.IR "struct iocb" +values contains holes, i.e., some of the +values must not be handled although the whole array is presented to the +.IR "io_submit" +function. +.TP +.B "IO_CMD_FSYNC" +.TP +.B "IO_CMD_POLL" +This is experimental. +.SH EXAMPLE +.nf +/* + * Simplistic version of copy command using async i/o + * + * From: Stephen Hemminger + * Copy file by using a async I/O state machine. + * 1. Start read request + * 2. When read completes turn it into a write request + * 3. When write completes decrement counter and free resources + * + * + * Usage: aiocp file(s) desination + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AIO_BLKSIZE (64*1024) +#define AIO_MAXIO 32 + +static int busy = 0; // # of I/O's in flight +static int tocopy = 0; // # of blocks left to copy +static int dstfd = -1; // destination file descriptor +static const char *dstname = NULL; +static const char *srcname = NULL; + + +/* Fatal error handler */ +static void io_error(const char *func, int rc) +{ + if (rc == -ENOSYS) + fprintf(stderr, "AIO not in this kernel\n"); + else if (rc < 0 && -rc < sys_nerr) + fprintf(stderr, "%s: %s\n", func, sys_errlist[-rc]); + else + fprintf(stderr, "%s: error %d\n", func, rc); + + if (dstfd > 0) + close(dstfd); + if (dstname) + unlink(dstname); + exit(1); +} + +/* + * Write complete callback. + * Adjust counts and free resources + */ +static void wr_done(io_context_t ctx, struct iocb *iocb, long res, long res2) +{ + if (res2 != 0) { + io_error("aio write", res2); + } + if (res != iocb->u.c.nbytes) { + fprintf(stderr, "write missed bytes expect %d got %d\n", iocb->u.c.nbytes, res2); + exit(1); + } + --tocopy; + --busy; + free(iocb->u.c.buf); + + memset(iocb, 0xff, sizeof(iocb)); // paranoia + free(iocb); + write(2, "w", 1); +} + +/* + * Read complete callback. + * Change read iocb into a write iocb and start it. + */ +static void rd_done(io_context_t ctx, struct iocb *iocb, long res, long res2) +{ + /* library needs accessors to look at iocb? */ + int iosize = iocb->u.c.nbytes; + char *buf = iocb->u.c.buf; + off_t offset = iocb->u.c.offset; + + if (res2 != 0) + io_error("aio read", res2); + if (res != iosize) { + fprintf(stderr, "read missing bytes expect %d got %d\n", iocb->u.c.nbytes, res); + exit(1); + } + + + /* turn read into write */ + io_prep_pwrite(iocb, dstfd, buf, iosize, offset); + io_set_callback(iocb, wr_done); + if (1 != (res = io_submit(ctx, 1, &iocb))) + io_error("io_submit write", res); + write(2, "r", 1); +} + + +int main(int argc, char *const *argv) +{ + int srcfd; + struct stat st; + off_t length = 0, offset = 0; + io_context_t myctx; + + if (argc != 3 || argv[1][0] == '-') { + fprintf(stderr, "Usage: aiocp SOURCE DEST"); + exit(1); + } + if ((srcfd = open(srcname = argv[1], O_RDONLY)) < 0) { + perror(srcname); + exit(1); + } + if (fstat(srcfd, &st) < 0) { + perror("fstat"); + exit(1); + } + length = st.st_size; + + if ((dstfd = open(dstname = argv[2], O_WRONLY | O_CREAT, 0666)) < 0) { + close(srcfd); + perror(dstname); + exit(1); + } + + /* initialize state machine */ + memset(&myctx, 0, sizeof(myctx)); + io_queue_init(AIO_MAXIO, &myctx); + tocopy = howmany(length, AIO_BLKSIZE); + + while (tocopy > 0) { + int i, rc; + /* Submit as many reads as once as possible upto AIO_MAXIO */ + int n = MIN(MIN(AIO_MAXIO - busy, AIO_MAXIO / 2), + howmany(length - offset, AIO_BLKSIZE)); + if (n > 0) { + struct iocb *ioq[n]; + + for (i = 0; i < n; i++) { + struct iocb *io = (struct iocb *) malloc(sizeof(struct iocb)); + int iosize = MIN(length - offset, AIO_BLKSIZE); + char *buf = (char *) malloc(iosize); + + if (NULL == buf || NULL == io) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + io_prep_pread(io, srcfd, buf, iosize, offset); + io_set_callback(io, rd_done); + ioq[i] = io; + offset += iosize; + } + + rc = io_submit(myctx, n, ioq); + if (rc < 0) + io_error("io_submit", rc); + + busy += n; + } + + // Handle IO's that have completed + rc = io_queue_run(myctx); + if (rc < 0) + io_error("io_queue_run", rc); + + // if we have maximum number of i/o's in flight + // then wait for one to complete + if (busy == AIO_MAXIO) { + rc = io_queue_wait(myctx, NULL); + if (rc < 0) + io_error("io_queue_wait", rc); + } + + } + + close(srcfd); + close(dstfd); + exit(0); +} + +/* + * Results look like: + * [alanm@toolbox ~/MOT3]$ ../taio kernel-source-2.4.8-0.4g.ppc.rpm abc + * rrrrrrrrrrrrrrrwwwrwrrwwrrwrwwrrwrwrwwrrwrwrrrrwwrwwwrrwrrrwwwwwwwwwwwwwwwww + * rrrrrrrrrrrrrrwwwrrwrwrwrwrrwwwwwwwwwwwwwwrrrrrrrrrrrrrrrrrrwwwwrwrwwrwrwrwr + * wrrrrrrrwwwwwwwwwwwwwrrrwrrrwrrwrwwwwwwwwwwrrrrwwrwrrrrrrrrrrrwwwwwwwwwwwrww + * wwwrrrrrrrrwwrrrwwrwrwrwwwrrrrrrrwwwrrwwwrrwrwwwwwwwwrrrrrrrwwwrrrrrrrwwwwww + * wwwwwwwrwrrrrrrrrwrrwrrwrrwrwrrrwrrrwrrrwrwwwwwwwwwwwwwwwwwwrrrwwwrrrrrrrrrr + * rrwrrrrrrwrrwwwwwwwwwwwwwwwwrwwwrrwrwwrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwww + * rrrrrwrrwrwrwrrwrrrwwwwwwwwrrrrwrrrwrwwrwrrrwrrwrrrrwwwwwwwrwrwwwwrwwrrrwrrr + * rrrwwwwwwwrrrrwwrrrrrrrrrrrrwrwrrrrwwwwwwwwwwwwwwrwrrrrwwwwrwrrrrwrwwwrrrwww + * rwwrrrrrrrwrrrrrrrrrrrrwwwwrrrwwwrwrrwwwwwwwwwwwwwwwwwwwwwrrrrrrrwwwwwwwrw + */ +.fi +.SH "SEE ALSO" +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_cancel.1 b/tools/libaio/man/io_cancel.1 new file mode 100644 index 0000000..16e898a --- /dev/null +++ b/tools/libaio/man/io_cancel.1 @@ -0,0 +1,21 @@ +.\"/* sys_io_cancel: +.\" * Attempts to cancel an iocb previously passed to io_submit. If +.\" * the operation is successfully cancelled, the resulting event is +.\" * copied into the memory pointed to by result without being placed +.\" * into the completion queue and 0 is returned. May fail with +.\" * -EFAULT if any of the data structures pointed to are invalid. +.\" * May fail with -EINVAL if aio_context specified by ctx_id is +.\" * invalid. May fail with -EAGAIN if the iocb specified was not +.\" * cancelled. Will fail with -ENOSYS if not implemented. +.\" */ +.\" +.TH io_cancel 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_cancel \- cancel io requests +.SH SYNOPSIS +.B #include +.br +.B #include +.LP +.BI "int io_submit(io_context_t " ctx ", struct iocb *" iocb ", struct io_event *" result ");" + diff --git a/tools/libaio/man/io_cancel.3 b/tools/libaio/man/io_cancel.3 new file mode 100644 index 0000000..9a16084 --- /dev/null +++ b/tools/libaio/man/io_cancel.3 @@ -0,0 +1,65 @@ +.TH io_cancel 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_cancel \- Cancel io requests +.SH SYNOPSIS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int io_cancel(io_context_t ctx, struct iocb *iocb)" +.br +.sp +struct iocb { + void *data; /* Return in the io completion event */ + unsigned key; /* For use in identifying io requests */ + short aio_lio_opcode; + short aio_reqprio; /* Not used */ + int aio_fildes; +}; +.fi +.SH DESCRIPTION +Attempts to cancel an iocb previously passed to io_submit. If +the operation is successfully cancelled, the resulting event is +copied into the memory pointed to by result without being placed +into the completion queue. +.PP +When one or more requests are asynchronously processed, it might be +useful in some situations to cancel a selected operation, e.g., if it +becomes obvious that the written data is no longer accurate and would +have to be overwritten soon. As an example, assume an application, which +writes data in files in a situation where new incoming data would have +to be written in a file which will be updated by an enqueued request. +.SH "RETURN VALUES" +0 is returned on success , otherwise returns Errno. +.SH ERRORS +.TP +.B EFAULT +If any of the data structures pointed to are invalid. +.TP +.B EINVAL +If aio_context specified by ctx_id is +invalid. +.TP +.B EAGAIN +If the iocb specified was not +cancelled. +.TP +.B ENOSYS +if not implemented. +.SH "SEE ALSO" +.BR io(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_destroy.1 b/tools/libaio/man/io_destroy.1 new file mode 100644 index 0000000..177683b --- /dev/null +++ b/tools/libaio/man/io_destroy.1 @@ -0,0 +1,17 @@ +.\"/* sys_io_destroy: +.\" * Destroy the aio_context specified. May cancel any outstanding +.\" * AIOs and block on completion. Will fail with -ENOSYS if not +.\" * implemented. May fail with -EFAULT if the context pointed to +.\" * is invalid. +.\" */ +.\" libaio provides this as io_queue_release. +.TH io_destroy 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_destroy \- destroy an io context +.SH SYNOPSIS +.B #include +.br +.B #include +.LP +.BI "int io_destroy(io_context_t " ctx ");" + diff --git a/tools/libaio/man/io_fsync.3 b/tools/libaio/man/io_fsync.3 new file mode 100644 index 0000000..53eb63d --- /dev/null +++ b/tools/libaio/man/io_fsync.3 @@ -0,0 +1,82 @@ +./" static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) +./" { +./" io_prep_fsync(iocb, fd); +./" io_set_callback(iocb, cb); +./" return io_submit(ctx, 1, &iocb); +./" } +.TH io_fsync 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +io_fsync \- Synchronize a file's complete in-core state with that on disk +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.sp +.br +.BI "int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)" +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.sp +typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2); +.sp +.fi +.SH DESCRIPTION +When dealing with asynchronous operations it is sometimes necessary to +get into a consistent state. This would mean for AIO that one wants to +know whether a certain request or a group of request were processed. +This could be done by waiting for the notification sent by the system +after the operation terminated, but this sometimes would mean wasting +resources (mainly computation time). +.PP +Calling this function forces all I/O operations operating queued at the +time of the function call operating on the file descriptor +.IR "iocb->io_fildes" +into the synchronized I/O completion state . The +.IR "io_fsync" +function returns +immediately but the notification through the method described in +.IR "io_callback" +will happen only after all requests for this +file descriptor have terminated and the file is synchronized. This also +means that requests for this very same file descriptor which are queued +after the synchronization request are not affected. +.SH "RETURN VALUES" +Returns 0, otherwise returns errno. +.SH ERRORS +.TP +.B EFAULT +.I iocbs +referenced data outside of the program's accessible address space. +.TP +.B EINVAL +.I ctx +refers to an unitialized aio context, the iocb pointed to by +.I iocbs +contains an improperly initialized iocb, +.TP +.B EBADF +The iocb contains a file descriptor that does not exist. +.TP +.B EINVAL +The file specified in the iocb does not support the given io operation. +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_getevents(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_getevents.1 b/tools/libaio/man/io_getevents.1 new file mode 100644 index 0000000..27730b9 --- /dev/null +++ b/tools/libaio/man/io_getevents.1 @@ -0,0 +1,29 @@ +./"/* io_getevents: +./" * Attempts to read at least min_nr events and up to nr events from +./" * the completion queue for the aio_context specified by ctx_id. May +./" * fail with -EINVAL if ctx_id is invalid, if min_nr is out of range, +./" * if nr is out of range, if when is out of range. May fail with +./" * -EFAULT if any of the memory specified to is invalid. May return +./" * 0 or < min_nr if no events are available and the timeout specified +./" * by when has elapsed, where when == NULL specifies an infinite +./" * timeout. Note that the timeout pointed to by when is relative and +./" * will be updated if not NULL and the operation blocks. Will fail +./" * with -ENOSYS if not implemented. +./" */ +./"asmlinkage long sys_io_getevents(io_context_t ctx_id, +./" long min_nr, +./" long nr, +./" struct io_event *events, +./" struct timespec *timeout) +./" +.TH io_getevents 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_getevents \- read resulting events from io requests +.SH SYNOPSIS +.B #include +.br +.B #include +.sp +.BI "int io_getevents(io_context_t " ctx ", long " min_nr ", long " nr ", struct io_events *" events "[], struct timespec *" timeout ");" + + diff --git a/tools/libaio/man/io_getevents.3 b/tools/libaio/man/io_getevents.3 new file mode 100644 index 0000000..8e9ddc8 --- /dev/null +++ b/tools/libaio/man/io_getevents.3 @@ -0,0 +1,79 @@ +./"/* io_getevents: +./" * Attempts to read at least min_nr events and up to nr events from +./" * the completion queue for the aio_context specified by ctx_id. May +./" * fail with -EINVAL if ctx_id is invalid, if min_nr is out of range, +./" * if nr is out of range, if when is out of range. May fail with +./" * -EFAULT if any of the memory specified to is invalid. May return +./" * 0 or < min_nr if no events are available and the timeout specified +./" * by when has elapsed, where when == NULL specifies an infinite +./" * timeout. Note that the timeout pointed to by when is relative and +./" * will be updated if not NULL and the operation blocks. Will fail +./" * with -ENOSYS if not implemented. +./" */ +./"asmlinkage long sys_io_getevents(io_context_t ctx_id, +./" long min_nr, +./" long nr, +./" struct io_event *events, +./" struct timespec *timeout) +./" +.TH io_getevents 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_getevents \- Read resulting events from io requests +.SH SYNOPSIS +.nf +.B #include +.sp +.br +.B #include +.br +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.sp +struct io_event { + unsigned PADDED(data, __pad1); + unsigned PADDED(obj, __pad2); + unsigned PADDED(res, __pad3); + unsigned PADDED(res2, __pad4); +}; +.sp +.BI "int io_getevents(io_context_t " ctx ", long " nr ", struct io_event *" events "[], struct timespec *" timeout ");" + +.fi +.SH DESCRIPTION +Attempts to read up to nr events from +the completion queue for the aio_context specified by ctx. +.SH "RETURN VALUES" +May return +0 if no events are available and the timeout specified +by when has elapsed, where when == NULL specifies an infinite +timeout. Note that the timeout pointed to by when is relative and +will be updated if not NULL and the operation blocks. Will fail +with ENOSYS if not implemented. +.SH ERRORS +.TP +.B EINVAL +if ctx_id is invalid, if min_nr is out of range, +if nr is out of range, if when is out of range. +.TP +.B EFAULT +if any of the memory specified to is invalid. +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_prep_fsync.3 b/tools/libaio/man/io_prep_fsync.3 new file mode 100644 index 0000000..4cf935a --- /dev/null +++ b/tools/libaio/man/io_prep_fsync.3 @@ -0,0 +1,89 @@ +./" static inline void io_prep_fsync(struct iocb *iocb, int fd) +./" { +./" memset(iocb, 0, sizeof(*iocb)); +./" iocb->aio_fildes = fd; +./" iocb->aio_lio_opcode = IO_CMD_FSYNC; +./" iocb->aio_reqprio = 0; +./" } +.TH io_prep_fsync 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +io_prep_fsync \- Synchronize a file's complete in-core state with that on disk +.SH SYNOPSYS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "static inline void io_prep_fsync(struct iocb *iocb, int fd)" +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.sp +.fi +.SH DESCRIPTION +This is an inline convenience function for setting up an iocbv for a FSYNC request. +.br +The file for which +.TP +.IR "iocb->aio_fildes = fd" +is a descriptor is set up with +the command +.TP +.IR "iocb->aio_lio_opcode = IO_CMD_FSYNC: +. +.PP +The io_prep_fsync() function shall set up an IO_CMD_FSYNC operation +to asynchronously force all I/O +operations associated with the file indicated by the file +descriptor aio_fildes member of the iocb structure referenced by +the iocb argument and queued at the time of the call to +io_submit() to the synchronized I/O completion state. The function +call shall return when the synchronization request has been +initiated or queued to the file or device (even when the data +cannot be synchronized immediately). + +All currently queued I/O operations shall be completed as if by a call +to fsync(); that is, as defined for synchronized I/O file +integrity completion. If the +operation queued by io_prep_fsync() fails, then, as for fsync(), +outstanding I/O operations are not guaranteed to have +been completed. + +If io_prep_fsync() succeeds, then it is only the I/O that was queued +at the time of the call to io_submit() that is guaranteed to be +forced to the relevant completion state. The completion of +subsequent I/O on the file descriptor is not guaranteed to be +completed in a synchronized fashion. +.PP +This function returns immediately . To schedule the operation, the +function +.IR io_submit +must be called. +.PP +Simultaneous asynchronous operations using the same iocb produce +undefined results. +.SH "RETURN VALUES" +None +.SH ERRORS +None +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_prep_pread.3 b/tools/libaio/man/io_prep_pread.3 new file mode 100644 index 0000000..5938aec --- /dev/null +++ b/tools/libaio/man/io_prep_pread.3 @@ -0,0 +1,79 @@ +./" static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +./" { +./" memset(iocb, 0, sizeof(*iocb)); +./" iocb->aio_fildes = fd; +./" iocb->aio_lio_opcode = IO_CMD_PREAD; +./" iocb->aio_reqprio = 0; +./" iocb->u.c.buf = buf; +./" iocb->u.c.nbytes = count; +./" iocb->u.c.offset = offset; +./" } +.TH io_prep_pread 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +io_prep_pread \- Set up asynchronous read +.SH SYNOPSYS +.nf +.B #include +.sp +.br +.B #include +.br +.sp +.BI "inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +" +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.fi +.SH DESCRIPTION +.IR io_prep_pread +is an inline convenience function designed to facilitate the initialization of +the iocb for an asynchronous read operation. + +The first +.TP +.IR "iocb->u.c.nbytes = count" +bytes of the file for which +.TP +.IR "iocb->aio_fildes = fd" +is a descriptor are written to the buffer +starting at +.TP +.IR "iocb->u.c.buf = buf" +. +.br +Reading starts at the absolute position +.TP +.IR "ioc->u.c.offset = offset" +in the file. +.PP +This function returns immediately . To schedule the operation, the +function +.IR io_submit +must be called. +.PP +Simultaneous asynchronous operations using the same iocb produce +undefined results. +.SH "RETURN VALUES" +None +.SH ERRORS +None +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_prep_pwrite.3 b/tools/libaio/man/io_prep_pwrite.3 new file mode 100644 index 0000000..68b3500 --- /dev/null +++ b/tools/libaio/man/io_prep_pwrite.3 @@ -0,0 +1,77 @@ +./" static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +./" { +./" memset(iocb, 0, sizeof(*iocb)); +./" iocb->aio_fildes = fd; +./" iocb->aio_lio_opcode = IO_CMD_PWRITE; +./" iocb->aio_reqprio = 0; +./" iocb->u.c.buf = buf; +./" iocb->u.c.nbytes = count; +./" iocb->u.c.offset = offset; +./" } +.TH io_prep_pwrite 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +io_prep_pwrite \- Set up iocb for asynchronous writes +.SH SYNOPSYS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +" +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.fi +.SH DESCRIPTION +io_prep_write is a convenicence function for setting up parallel writes. + +The first +.TP +.IR "iocb->u.c.nbytes = count" +bytes of the file for which +.TP +.IR "iocb->aio_fildes = fd" +is a descriptor are written from the buffer +starting at +.TP +.IR "iocb->u.c.buf = buf" +. +.br +Writing starts at the absolute position +.TP +.IR "ioc->u.c.offset = offset" +in the file. +.PP +This function returns immediately . To schedule the operation, the +function +.IR io_submit +must be called. +.PP +Simultaneous asynchronous operations using the same iocb produce +undefined results. +.SH "RETURN VALUES" +None +.SH ERRORS +None +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_queue_init.3 b/tools/libaio/man/io_queue_init.3 new file mode 100644 index 0000000..317f631 --- /dev/null +++ b/tools/libaio/man/io_queue_init.3 @@ -0,0 +1,63 @@ +.TH io_queue_init 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_queue_init \- Initialize asynchronous io state machine + +.SH SYNOPSIS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "int io_queue_init(int maxevents, io_context_t *ctx );" +.sp +.fi +.SH DESCRIPTION +.B io_queue_init +Attempts to create an aio context capable of receiving at least +.IR maxevents +events. +.IR ctx +must point to an aio context that already exists and must be initialized +to +.IR 0 +before the call. +If the operation is successful, *cxtp is filled with the resulting handle. +.SH "RETURN VALUES" +On success, +.B io_queue_init +returns 0. Otherwise, -error is return, where +error is one of the Exxx values defined in the Errors section. +.SH ERRORS +.TP +.B EFAULT +.I iocbs +referenced data outside of the program's accessible address space. +.TP +.B EINVAL +.I maxevents +is <= 0 or +.IR ctx +is an invalid memory locattion. +.TP +.B ENOSYS +Not implemented +.TP +.B EAGAIN +.IR "maxevents > max_aio_reqs" +where max_aio_reqs is a tunable value. +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_queue_release.3 b/tools/libaio/man/io_queue_release.3 new file mode 100644 index 0000000..06b9ec0 --- /dev/null +++ b/tools/libaio/man/io_queue_release.3 @@ -0,0 +1,48 @@ +.TH io_queue_release 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_queue_release \- Release the context associated with the userspace handle +.SH SYNOPSIS +.nf +.B #include +.br +.B #include +.br +.sp +.BI "int io_queue_release(io_context_t ctx)" +.sp +.SH DESCRIPTION +.B io_queue_release +destroys the context associated with the userspace handle. May cancel any outstanding +AIOs and block on completion. + +.B cts. +.SH "RETURN VALUES" +On success, +.B io_queue_release +returns 0. Otherwise, -error is return, where +error is one of the Exxx values defined in the Errors section. +.SH ERRORS +.TP +.B EINVAL +.I ctx +refers to an unitialized aio context, the iocb pointed to by +.I iocbs +contains an improperly initialized iocb, +.TP +.B ENOSYS +Not implemented +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) + diff --git a/tools/libaio/man/io_queue_run.3 b/tools/libaio/man/io_queue_run.3 new file mode 100644 index 0000000..57dd417 --- /dev/null +++ b/tools/libaio/man/io_queue_run.3 @@ -0,0 +1,50 @@ +.TH io_queue_run 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_queue_run \- Handle completed io requests +.SH SYNOPSIS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "int io_queue_run(io_context_t ctx );" +.sp +.fi +.SH DESCRIPTION +.B io_queue_run +Attempts to read all the events events from +the completion queue for the aio_context specified by ctx_id. +.SH "RETURN VALUES" +May return +0 if no events are available. +Will fail with -ENOSYS if not implemented. +.SH ERRORS +.TP +.B EFAULT +.I iocbs +referenced data outside of the program's accessible address space. +.TP +.B EINVAL +.I ctx +refers to an unitialized aio context, the iocb pointed to by +.I iocbs +contains an improperly initialized iocb, +.TP +.B ENOSYS +Not implemented +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_queue_wait.3 b/tools/libaio/man/io_queue_wait.3 new file mode 100644 index 0000000..2306663 --- /dev/null +++ b/tools/libaio/man/io_queue_wait.3 @@ -0,0 +1,56 @@ +.TH io_queue_wait 2 2002-09-03 "Linux 2.4" "Linux AIO" +.SH NAME +io_queue_wait \- Wait for io requests to complete +.SH SYNOPSIS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "int io_queue_wait(io_context_t ctx, const struct timespec *timeout);" +.fi +.SH DESCRIPTION +Attempts to read an event from +the completion queue for the aio_context specified by ctx_id. +.SH "RETURN VALUES" +May return +0 if no events are available and the timeout specified +by when has elapsed, where when == NULL specifies an infinite +timeout. Note that the timeout pointed to by when is relative and +will be updated if not NULL and the operation blocks. Will fail +with -ENOSYS if not implemented. +.SH "RETURN VALUES" +On success, +.B io_queue_wait +returns 0. Otherwise, -error is return, where +error is one of the Exxx values defined in the Errors section. +.SH ERRORS +.TP +.B EFAULT +.I iocbs +referenced data outside of the program's accessible address space. +.TP +.B EINVAL +.I ctx +refers to an unitialized aio context, the iocb pointed to by +.I iocbs +contains an improperly initialized iocb, +.TP +.B ENOSYS +Not implemented +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_set_callback(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_set_callback.3 b/tools/libaio/man/io_set_callback.3 new file mode 100644 index 0000000..a8ca789 --- /dev/null +++ b/tools/libaio/man/io_set_callback.3 @@ -0,0 +1,44 @@ +./"static inline void io_set_callback(struct iocb *iocb, io_callback_t cb) +.TH io_set_callback 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +io_set_callback \- Set up io completion callback function +.SH SYNOPSYS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "static inline void io_set_callback(struct iocb *iocb, io_callback_t cb)" +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.sp +typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2); +.sp +.fi +.SH DESCRIPTION +The callback is not done if the caller uses raw events from +io_getevents, only with the library helpers +.SH "RETURN VALUES" +.SH ERRORS +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_submit(3), +.BR errno(3) diff --git a/tools/libaio/man/io_setup.1 b/tools/libaio/man/io_setup.1 new file mode 100644 index 0000000..68690e1 --- /dev/null +++ b/tools/libaio/man/io_setup.1 @@ -0,0 +1,15 @@ +./"/* sys_io_setup: +./" * Create an aio_context capable of receiving at least nr_events. +./" * ctxp must not point to an aio_context that already exists, and +./" * must be initialized to 0 prior to the call. On successful +./" * creation of the aio_context, *ctxp is filled in with the resulting +./" * handle. May fail with -EINVAL if *ctxp is not initialized, +./" * if the specified nr_events exceeds internal limits. May fail +./" * with -EAGAIN if the specified nr_events exceeds the user's limit +./" * of available events. May fail with -ENOMEM if insufficient kernel +./" * resources are available. May fail with -EFAULT if an invalid +./" * pointer is passed for ctxp. Will fail with -ENOSYS if not +./" * implemented. +./" */ +./" -- note: libaio is actually providing io_queue_init and io_queue_grow +./" as separate functions. For now io_setup is the same as io_queue_grow. diff --git a/tools/libaio/man/io_submit.1 b/tools/libaio/man/io_submit.1 new file mode 100644 index 0000000..f66e80f --- /dev/null +++ b/tools/libaio/man/io_submit.1 @@ -0,0 +1,109 @@ +.TH io_submit 2 2002-09-02 "Linux 2.4" "Linux AIO" +.SH NAME +io_submit \- submit io requests +.SH SYNOPSIS +.B #include +.br +.B #include +.LP +.BI "int io_submit(io_context_t " ctx ", long " nr ", struct iocb *" iocbs "[]);" +.SH DESCRIPTION +.B io_submit +submits to the io_context +.I ctx +up to +.I nr +I/O requests pointed to by the vector +.IR iocbs . + +The +.B iocb +structure is defined as something like +.sp +.RS +.nf +struct iocb { + void *data; +.\" unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.fi +.RE +.sp +.I data +is a an opaque pointer which will upon completion be returned in the +.B io_event +structure by +.BR io_getevents (2). +.\" and io_wait(2) +Callers will typically use this to point directly or indirectly to a +callback function. +.sp +.I aio_lio_opcode +is the I/O operation requested. Callers will typically set this and the +arguments to the I/O operation calling the +.BR io_prep_ (3) +function corresponding to the operation. +.sp +.I aio_reqprio +is the priority of the request. Higher values have more priority; the +normal priority is 0. +.sp +.I aio_fildes +is the file descriptor for the I/O operation. +Callers will typically set this and the +arguments to the I/O operation calling the +.BR io_prep_ *(3) +function corresponding to the operation. +.sp +The caller may not modify the contents or resubmit a submitted +.B iocb +structure until after the operation completes or is canceled. +The implementation of +.BR io_submit (2) +is permitted to modify reserved fields of the +.B iocb +structure. +.SH "RETURN VALUES" +If able to submit at least one iocb, +.B io_submit +returns the number of iocbs submitted successfully. Otherwise, +.RI - error +is returned, where +.I error +is one of the Exxx values defined in the Errors section. +.SH ERRORS +.TP +.B EFAULT +.I iocbs +referenced data outside of the program's accessible address space. +.TP +.B EINVAL +.I nr +is negative, +.I ctx +refers to an uninitialized aio context, the iocb pointed to by +.IR iocbs [0] +is improperly initialized or specifies an unsupported operation. +.TP +.B EBADF +The iocb pointed to by +.IR iocbs [0] +contains a file descriptor that does not exist. +.TP +.B EAGAIN +Insufficient resources were available to queue any operations. +.SH "SEE ALSO" +.BR io_setup (2), +.BR io_destroy (2), +.BR io_getevents (2), +.\".BR io_wait (2), +.BR io_prep_pread (3), +.BR io_prep_pwrite (3), +.BR io_prep_fsync (3), +.BR io_prep_fdsync (3), +.BR io_prep_noop (3), +.BR io_cancel (2), +.BR errno (3) diff --git a/tools/libaio/man/io_submit.3 b/tools/libaio/man/io_submit.3 new file mode 100644 index 0000000..b6966ef --- /dev/null +++ b/tools/libaio/man/io_submit.3 @@ -0,0 +1,135 @@ +./"/* sys_io_submit: +./" * Queue the nr iocbs pointed to by iocbpp for processing. Returns +./" * the number of iocbs queued. May return -EINVAL if the aio_context +./" * specified by ctx_id is invalid, if nr is < 0, if the iocb at +./" * *iocbpp[0] is not properly initialized, if the operation specified +./" * is invalid for the file descriptor in the iocb. May fail with +./" * -EFAULT if any of the data structures point to invalid data. May +./" * fail with -EBADF if the file descriptor specified in the first +./" * iocb is invalid. May fail with -EAGAIN if insufficient resources +./" * are available to queue any iocbs. Will return 0 if nr is 0. Will +./" * fail with -ENOSYS if not implemented. +./" */ +.TH io_submit 2 2002-09-02 "Linux 2.4" "Linux AIO" +.SH NAME +io_submit \- Submit io requests +.SH SYNOPSIS +.nf +.B #include +.br +.sp +.B #include +.br +.sp +.BI "int io_submit(io_context_t " ctx ", long " nr ", struct iocb *" iocbs "[]);" +.sp +struct iocb { + void *data; + unsigned key; + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; +}; +.fi +.SH DESCRIPTION +.B io_submit +submits +.I nr +iocbs for processing for a given io context ctx. + +The +.IR "io_submit" +function can be used to enqueue an arbitrary +number of read and write requests at one time. The requests can all be +meant for the same file, all for different files or every solution in +between. + +.IR "io_submit" +gets the +.IR "nr" +requests from the array pointed to +by +.IR "iocbs" +. The operation to be performed is determined by the +.IR "aio_lio_opcode" +member in each element of +.IR "iocbs" +. If this +field is +.B "IO_CMD_PREAD" +a read operation is enqueued, similar to a call +of +.IR "io_prep_pread" +for this element of the array (except that the way +the termination is signalled is different, as we will see below). If +the +.IR "aio_lio_opcode" +member is +.B "IO_CMD_PWRITE" +a write operation +is enqueued. Otherwise the +.IR "aio_lio_opcode" +must be +.B "IO_CMD_NOP" +in which case this element of +.IR "iocbs" +is simply ignored. This +``operation'' is useful in situations where one has a fixed array of +.IR "struct iocb" +elements from which only a few need to be handled at +a time. Another situation is where the +.IR "io_submit" +call was +canceled before all requests are processed and the remaining requests have to be reissued. + +The other members of each element of the array pointed to by +.IR "iocbs" +must have values suitable for the operation as described in +the documentation for +.IR "io_prep_pread" +and +.IR "io_prep_pwrite" +above. + +The function returns immediately after +having enqueued all the requests. +On success, +.B io_submit +returns the number of iocbs submitted successfully. Otherwise, -error is return, where +error is one of the Exxx values defined in the Errors section. +.PP +If an error is detected, then the behavior is undefined. +.PP +Simultaneous asynchronous operations using the same iocb produce +undefined results. +.SH ERRORS +.TP +.B EFAULT +.I iocbs +referenced data outside of the program's accessible address space. +.TP +.B EINVAL +.I ctx +refers to an unitialized aio context, the iocb pointed to by +.I iocbs +contains an improperly initialized iocb, +.TP +.B EBADF +The iocb contains a file descriptor that does not exist. +.TP +.B EINVAL +The file specified in the iocb does not support the given io operation. +.SH "SEE ALSO" +.BR io(3), +.BR io_cancel(3), +.BR io_fsync(3), +.BR io_getevents(3), +.BR io_prep_fsync(3), +.BR io_prep_pread(3), +.BR io_prep_pwrite(3), +.BR io_queue_init(3), +.BR io_queue_release(3), +.BR io_queue_run(3), +.BR io_queue_wait(3), +.BR io_set_callback(3), +.BR errno(3) diff --git a/tools/libaio/man/lio_listio.3 b/tools/libaio/man/lio_listio.3 new file mode 100644 index 0000000..9b5b5e4 --- /dev/null +++ b/tools/libaio/man/lio_listio.3 @@ -0,0 +1,229 @@ +.TH lio_listio 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +lio_listio - List directed I/O +.SH SYNOPSYS +.B #include +.br +.B #include +.LP +.BI "int lio_listio (int mode, struct aiocb *const list[], int nent, struct sigevent *sig)" +.nf +.SH DESCRIPTION + +Besides these functions with the more or less traditional interface, +POSIX.1b also defines a function which can initiate more than one +operation at a time, and which can handle freely mixed read and write +operations. It is therefore similar to a combination of +.IR readv +and +.IR "writev" +. + +The +.IR "lio_listio" +function can be used to enqueue an arbitrary +number of read and write requests at one time. The requests can all be +meant for the same file, all for different files or every solution in +between. + +.IR "lio_listio" +gets the +.IR "nent" +requests from the array pointed to +by +.IR "list" +. The operation to be performed is determined by the +.IR "aio_lio_opcode" +member in each element of +.IR "list" +. If this +field is +.B "LIO_READ" +a read operation is enqueued, similar to a call +of +.IR "aio_read" +for this element of the array (except that the way +the termination is signalled is different, as we will see below). If +the +.IR "aio_lio_opcode" +member is +.B "LIO_WRITE" +a write operation +is enqueued. Otherwise the +.IR "aio_lio_opcode" +must be +.B "LIO_NOP" +in which case this element of +.IR "list" +is simply ignored. This +``operation'' is useful in situations where one has a fixed array of +.IR "struct aiocb" +elements from which only a few need to be handled at +a time. Another situation is where the +.IR "lio_listio" +call was +canceled before all requests are processed and the remaining requests have to be reissued. + +The other members of each element of the array pointed to by +.IR "list" +must have values suitable for the operation as described in +the documentation for +.IR "aio_read" +and +.IR "aio_write" +above. + +The +.IR "mode" +argument determines how +.IR "lio_listio" +behaves after +having enqueued all the requests. If +.IR "mode" +is +.B "LIO_WAIT" +it +waits until all requests terminated. Otherwise +.IR "mode" +must be +.B "LIO_NOWAIT" +and in this case the function returns immediately after +having enqueued all the requests. In this case the caller gets a +notification of the termination of all requests according to the +.IR "sig" +parameter. If +.IR "sig" +is +.B "NULL" +no notification is +send. Otherwise a signal is sent or a thread is started, just as +described in the description for +.IR "aio_read" +or +.IR "aio_write" +. + +When the sources are compiled with +.B "_FILE_OFFSET_BITS == 64" +, this +function is in fact +.IR "lio_listio64" +since the LFS interface +transparently replaces the normal implementation. +.SH "RETURN VALUES" +If +.IR "mode" +is +.B "LIO_WAIT" +, the return value of +.IR "lio_listio" +is +.IR 0 +when all requests completed successfully. Otherwise the +function return +.IR 1 +and +.IR "errno" +is set accordingly. To find +out which request or requests failed one has to use the +.IR "aio_error" +function on all the elements of the array +.IR "list" +. + +In case +.IR "mode" +is +.B "LIO_NOWAIT" +, the function returns +.IR 0 +if +all requests were enqueued correctly. The current state of the requests +can be found using +.IR "aio_error" +and +.IR "aio_return" +as described +above. If +.IR "lio_listio" +returns +.IR -1 +in this mode, the +global variable +.IR "errno" +is set accordingly. If a request did not +yet terminate, a call to +.IR "aio_error" +returns +.B "EINPROGRESS" +. If +the value is different, the request is finished and the error value (or + +.IR 0 +) is returned and the result of the operation can be retrieved +using +.IR "aio_return" +. +.SH ERRORS +Possible values for +.IR "errno" +are: + +.TP +.B EAGAIN +The resources necessary to queue all the requests are not available at +the moment. The error status for each element of +.IR "list" +must be +checked to determine which request failed. + +Another reason could be that the system wide limit of AIO requests is +exceeded. This cannot be the case for the implementation on GNU systems +since no arbitrary limits exist. +.TP +.B EINVAL +The +.IR "mode" +parameter is invalid or +.IR "nent" +is larger than +.B "AIO_LISTIO_MAX" +. +.TP +.B EIO +One or more of the request's I/O operations failed. The error status of +each request should be checked to determine which one failed. +.TP +.B ENOSYS +The +.IR "lio_listio" +function is not supported. +.PP + +If the +.IR "mode" +parameter is +.B "LIO_NOWAIT" +and the caller cancels +a request, the error status for this request returned by +.IR "aio_error" +is +.B "ECANCELED" +. +.SH "SEE ALSO" +.BR aio(3), +.BR aio_cancel(3), +.BR aio_cancel64(3), +.BR aio_error(3), +.BR aio_error64(3), +.BR aio_fsync(3), +.BR aio_fsync64(3), +.BR aio_init(3), +.BR aio_read(3), +.BR aio_read64(3), +.BR aio_return(3), +.BR aio_return64(3), +.BR aio_suspend(3), +.BR aio_suspend64(3), +.BR aio_write(3), +.BR aio_write64(3) diff --git a/tools/libaio/man/lio_listio64.3 b/tools/libaio/man/lio_listio64.3 new file mode 100644 index 0000000..97f6955 --- /dev/null +++ b/tools/libaio/man/lio_listio64.3 @@ -0,0 +1,39 @@ +.TH lio_listio64 3 2002-09-12 "Linux 2.4" Linux AIO" +.SH NAME +lio_listio64 \- List directed I/O +.SH SYNOPSYS +.B #include +.br +.B #include +.LP +.BI "int lio_listio64 (int mode, struct aiocb *const list[], int nent, struct sigevent *sig)" +.nf +.SH DESCRIPTION +This function is similar to the +.IR "code{lio_listio" +function. The only +difference is that on +.IR "32 bit" +machines, the file descriptor should +be opened in the large file mode. Internally, +.IR "lio_listio64" +uses +functionality equivalent to +.IR lseek64" +to position the file descriptor correctly for the reading or +writing, as opposed to +.IR "lseek" +functionality used in +.IR "lio_listio". + +When the sources are compiled with +.IR "_FILE_OFFSET_BITS == 64" +, this +function is available under the name +.IR "lio_listio" +and so +transparently replaces the interface for small files on 32 bit +machines. +.SH "RETURN VALUES" +.SH ERRORS +.SH "SEE ALSO" diff --git a/tools/libaio/src/Makefile b/tools/libaio/src/Makefile new file mode 100644 index 0000000..da981b7 --- /dev/null +++ b/tools/libaio/src/Makefile @@ -0,0 +1,67 @@ +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +prefix=$(PREFIX) +includedir=$(prefix)/include +libdir=$(prefix)/lib + +ARCH := $(shell uname -m | sed -e s/i.86/i386/) +CFLAGS := -nostdlib -nostartfiles -Wall -I. -g -fomit-frame-pointer -O2 -fPIC +SO_CFLAGS=-shared $(CFLAGS) +L_CFLAGS=$(CFLAGS) +LINK_FLAGS= + +soname=libaio.so.1 +minor=0 +micro=1 +libname=$(soname).$(minor).$(micro) +all_targets += libaio.a $(libname) + +all: $(all_targets) + +# libaio provided functions +libaio_srcs := io_queue_init.c io_queue_release.c +libaio_srcs += io_queue_wait.c io_queue_run.c + +# real syscalls +libaio_srcs += io_getevents.c io_submit.c io_cancel.c +libaio_srcs += io_setup.c io_destroy.c + +# internal functions +libaio_srcs += raw_syscall.c + +# old symbols +libaio_srcs += compat-0_1.c + +libaio_objs := $(patsubst %.c,%.ol,$(libaio_srcs)) +libaio_sobjs := $(patsubst %.c,%.os,$(libaio_srcs)) + +$(libaio_objs) $(libaio_sobjs): libaio.h vsys_def.h + +%.os: %.c + $(CC) $(SO_CFLAGS) -c -o $@ $< + +%.ol: %.c + $(CC) $(L_CFLAGS) -c -o $@ $< + + +libaio.a: $(libaio_objs) + rm -f libaio.a + $(AR) r libaio.a $^ + $(RANLIB) libaio.a + +$(libname): $(libaio_sobjs) libaio.map + $(CC) $(SO_CFLAGS) -Wl,--version-script=libaio.map -Wl,-soname=$(soname) -o $@ $(libaio_sobjs) $(LINK_FLAGS) + +install: $(all_targets) + install -D -m 644 libaio.h $(includedir)/libaio.h + install -D -m 644 libaio.a $(libdir)/libaio.a + install -D -m 755 $(libname) $(libdir)/$(libname) + ln -sf $(libname) $(libdir)/$(soname) + ln -sf $(libname) $(libdir)/libaio.so + +$(libaio_objs): libaio.h + +clean: + rm -f $(all_targets) $(libaio_objs) $(libaio_sobjs) $(soname).new + rm -f *.so* *.a *.o diff --git a/tools/libaio/src/compat-0_1.c b/tools/libaio/src/compat-0_1.c new file mode 100644 index 0000000..53d520b --- /dev/null +++ b/tools/libaio/src/compat-0_1.c @@ -0,0 +1,62 @@ +/* libaio Linux async I/O interface + + compat-0_1.c : compatibility symbols for libaio 0.1.x-0.3.x + + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include + +#include "libaio.h" +#include "vsys_def.h" + +#include "syscall.h" + + +/* ABI change. Provide partial compatibility on this one for now. */ +SYMVER(compat0_1_io_cancel, io_cancel, 0.1); +int compat0_1_io_cancel(io_context_t ctx, struct iocb *iocb) +{ + struct io_event event; + + /* FIXME: the old ABI would return the event on the completion queue */ + return io_cancel(ctx, iocb, &event); +} + +SYMVER(compat0_1_io_queue_wait, io_queue_wait, 0.1); +int compat0_1_io_queue_wait(io_context_t ctx, struct timespec *when) +{ + struct timespec timeout; + if (when) + timeout = *when; + return io_getevents(ctx, 0, 0, NULL, when ? &timeout : NULL); +} + + +/* ABI change. Provide backwards compatibility for this one. */ +SYMVER(compat0_1_io_getevents, io_getevents, 0.1); +int compat0_1_io_getevents(io_context_t ctx_id, long nr, + struct io_event *events, + const struct timespec *const_timeout) +{ + struct timespec timeout; + if (const_timeout) + timeout = *const_timeout; + return io_getevents(ctx_id, 1, nr, events, + const_timeout ? &timeout : NULL); +} + diff --git a/tools/libaio/src/io_cancel.c b/tools/libaio/src/io_cancel.c new file mode 100644 index 0000000..2f0f5f4 --- /dev/null +++ b/tools/libaio/src/io_cancel.c @@ -0,0 +1,23 @@ +/* io_cancel.c + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include "syscall.h" + +io_syscall3(int, io_cancel_0_4, io_cancel, io_context_t, ctx, struct iocb *, iocb, struct io_event *, event) +DEFSYMVER(io_cancel_0_4, io_cancel, 0.4) diff --git a/tools/libaio/src/io_destroy.c b/tools/libaio/src/io_destroy.c new file mode 100644 index 0000000..0ab6bd1 --- /dev/null +++ b/tools/libaio/src/io_destroy.c @@ -0,0 +1,23 @@ +/* io_destroy + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include "syscall.h" + +io_syscall1(int, io_destroy, io_destroy, io_context_t, ctx) diff --git a/tools/libaio/src/io_getevents.c b/tools/libaio/src/io_getevents.c new file mode 100644 index 0000000..5a05174 --- /dev/null +++ b/tools/libaio/src/io_getevents.c @@ -0,0 +1,57 @@ +/* io_getevents.c + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include "syscall.h" + +io_syscall5(int, __io_getevents_0_4, io_getevents, io_context_t, ctx, long, min_nr, long, nr, struct io_event *, events, struct timespec *, timeout) + +#define AIO_RING_MAGIC 0xa10a10a1 + +/* Ben will hate me for this */ +struct aio_ring { + unsigned id; /* kernel internal index number */ + unsigned nr; /* number of io_events */ + unsigned head; + unsigned tail; + + unsigned magic; + unsigned compat_features; + unsigned incompat_features; + unsigned header_length; /* size of aio_ring */ +}; + +int io_getevents_0_4(io_context_t ctx, long min_nr, long nr, struct io_event * events, struct timespec * timeout) +{ + struct aio_ring *ring; + ring = (struct aio_ring*)ctx; + if (ring==NULL || ring->magic != AIO_RING_MAGIC) + goto do_syscall; + if (timeout!=NULL && timeout->tv_sec == 0 && timeout->tv_nsec == 0) { + if (ring->head == ring->tail) + return 0; + } + +do_syscall: + return __io_getevents_0_4(ctx, min_nr, nr, events, timeout); +} + +DEFSYMVER(io_getevents_0_4, io_getevents, 0.4) diff --git a/tools/libaio/src/io_queue_init.c b/tools/libaio/src/io_queue_init.c new file mode 100644 index 0000000..563d137 --- /dev/null +++ b/tools/libaio/src/io_queue_init.c @@ -0,0 +1,33 @@ +/* io_queue_init.c + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +#include "syscall.h" + +int io_queue_init(int maxevents, io_context_t *ctxp) +{ + if (maxevents > 0) { + *ctxp = NULL; + return io_setup(maxevents, ctxp); + } + return -EINVAL; +} diff --git a/tools/libaio/src/io_queue_release.c b/tools/libaio/src/io_queue_release.c new file mode 100644 index 0000000..94bbb86 --- /dev/null +++ b/tools/libaio/src/io_queue_release.c @@ -0,0 +1,27 @@ +/* io_queue_release.c + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +int io_queue_release(io_context_t ctx) +{ + return io_destroy(ctx); +} diff --git a/tools/libaio/src/io_queue_run.c b/tools/libaio/src/io_queue_run.c new file mode 100644 index 0000000..e0132f4 --- /dev/null +++ b/tools/libaio/src/io_queue_run.c @@ -0,0 +1,39 @@ +/* io_submit + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +int io_queue_run(io_context_t ctx) +{ + static struct timespec timeout = { 0, 0 }; + struct io_event event; + int ret; + + /* FIXME: batch requests? */ + while (1 == (ret = io_getevents(ctx, 0, 1, &event, &timeout))) { + io_callback_t cb = (io_callback_t)event.data; + struct iocb *iocb = event.obj; + + cb(ctx, iocb, event.res, event.res2); + } + + return ret; +} diff --git a/tools/libaio/src/io_queue_wait.c b/tools/libaio/src/io_queue_wait.c new file mode 100644 index 0000000..538d2f3 --- /dev/null +++ b/tools/libaio/src/io_queue_wait.c @@ -0,0 +1,31 @@ +/* io_submit + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define NO_SYSCALL_ERRNO +#include +#include +#include +#include "syscall.h" + +struct timespec; + +int io_queue_wait_0_4(io_context_t ctx, struct timespec *timeout) +{ + return io_getevents(ctx, 0, 0, NULL, timeout); +} +DEFSYMVER(io_queue_wait_0_4, io_queue_wait, 0.4) diff --git a/tools/libaio/src/io_setup.c b/tools/libaio/src/io_setup.c new file mode 100644 index 0000000..4ba1afc --- /dev/null +++ b/tools/libaio/src/io_setup.c @@ -0,0 +1,23 @@ +/* io_setup + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include "syscall.h" + +io_syscall2(int, io_setup, io_setup, int, maxevents, io_context_t *, ctxp) diff --git a/tools/libaio/src/io_submit.c b/tools/libaio/src/io_submit.c new file mode 100644 index 0000000..e22ba54 --- /dev/null +++ b/tools/libaio/src/io_submit.c @@ -0,0 +1,23 @@ +/* io_submit + libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include "syscall.h" + +io_syscall3(int, io_submit, io_submit, io_context_t, ctx, long, nr, struct iocb **, iocbs) diff --git a/tools/libaio/src/libaio.h b/tools/libaio/src/libaio.h new file mode 100644 index 0000000..6574601 --- /dev/null +++ b/tools/libaio/src/libaio.h @@ -0,0 +1,222 @@ +/* /usr/include/libaio.h + * + * Copyright 2000,2001,2002 Red Hat, Inc. + * + * Written by Benjamin LaHaise + * + * libaio Linux async I/O interface + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __LIBAIO_H +#define __LIBAIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +struct timespec; +struct sockaddr; +struct iovec; +struct iocb; + +typedef struct io_context *io_context_t; + +typedef enum io_iocb_cmd { + IO_CMD_PREAD = 0, + IO_CMD_PWRITE = 1, + + IO_CMD_FSYNC = 2, + IO_CMD_FDSYNC = 3, + + IO_CMD_POLL = 5, + IO_CMD_NOOP = 6, +} io_iocb_cmd_t; + +#if defined(__i386__) /* little endian, 32 bits */ +#define PADDED(x, y) x; unsigned y +#define PADDEDptr(x, y) x; unsigned y +#define PADDEDul(x, y) unsigned long x; unsigned y +#elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__) +#define PADDED(x, y) x, y +#define PADDEDptr(x, y) x +#define PADDEDul(x, y) unsigned long x +#elif defined(__powerpc64__) /* big endian, 64 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x,y) x +#define PADDEDul(x, y) unsigned long x +#elif defined(__PPC__) /* big endian, 32 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x, y) unsigned y; x +#define PADDEDul(x, y) unsigned y; unsigned long x +#elif defined(__s390x__) /* big endian, 64 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x,y) x +#define PADDEDul(x, y) unsigned long x +#elif defined(__s390__) /* big endian, 32 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x, y) unsigned y; x +#define PADDEDul(x, y) unsigned y; unsigned long x +#else +#error endian? +#endif + +struct io_iocb_poll { + PADDED(int events, __pad1); +}; /* result code is the set of result flags or -'ve errno */ + +struct io_iocb_sockaddr { + struct sockaddr *addr; + int len; +}; /* result code is the length of the sockaddr, or -'ve errno */ + +struct io_iocb_common { + PADDEDptr(void *buf, __pad1); + PADDEDul(nbytes, __pad2); + long long offset; + long long __pad3, __pad4; +}; /* result code is the amount read or -'ve errno */ + +struct io_iocb_vector { + const struct iovec *vec; + int nr; + long long offset; +}; /* result code is the amount read or -'ve errno */ + +struct iocb { + PADDEDptr(void *data, __pad1); /* Return in the io completion event */ + PADDED(unsigned key, __pad2); /* For use in identifying io requests */ + + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; + + union { + struct io_iocb_common c; + struct io_iocb_vector v; + struct io_iocb_poll poll; + struct io_iocb_sockaddr saddr; + } u; +}; + +struct io_event { + PADDEDptr(void *data, __pad1); + PADDEDptr(struct iocb *obj, __pad2); + PADDEDul(res, __pad3); + PADDEDul(res2, __pad4); +}; + +#undef PADDED +#undef PADDEDptr +#undef PADDEDul + +typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2); + +/* library wrappers */ +extern int io_queue_init(int maxevents, io_context_t *ctxp); +/*extern int io_queue_grow(io_context_t ctx, int new_maxevents);*/ +extern int io_queue_release(io_context_t ctx); +/*extern int io_queue_wait(io_context_t ctx, struct timespec *timeout);*/ +extern int io_queue_run(io_context_t ctx); + +/* Actual syscalls */ +extern int io_setup(int maxevents, io_context_t *ctxp); +extern int io_destroy(io_context_t ctx); +extern int io_submit(io_context_t ctx, long nr, struct iocb *ios[]); +extern int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt); +extern int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout); + + +static inline void io_set_callback(struct iocb *iocb, io_callback_t cb) +{ + iocb->data = (void *)cb; +} + +static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_PREAD; + iocb->aio_reqprio = 0; + iocb->u.c.buf = buf; + iocb->u.c.nbytes = count; + iocb->u.c.offset = offset; +} + +static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_PWRITE; + iocb->aio_reqprio = 0; + iocb->u.c.buf = buf; + iocb->u.c.nbytes = count; + iocb->u.c.offset = offset; +} + +static inline void io_prep_poll(struct iocb *iocb, int fd, int events) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_POLL; + iocb->aio_reqprio = 0; + iocb->u.poll.events = events; +} + +static inline int io_poll(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd, int events) +{ + io_prep_poll(iocb, fd, events); + io_set_callback(iocb, cb); + return io_submit(ctx, 1, &iocb); +} + +static inline void io_prep_fsync(struct iocb *iocb, int fd) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_FSYNC; + iocb->aio_reqprio = 0; +} + +static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) +{ + io_prep_fsync(iocb, fd); + io_set_callback(iocb, cb); + return io_submit(ctx, 1, &iocb); +} + +static inline void io_prep_fdsync(struct iocb *iocb, int fd) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_FDSYNC; + iocb->aio_reqprio = 0; +} + +static inline int io_fdsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) +{ + io_prep_fdsync(iocb, fd); + io_set_callback(iocb, cb); + return io_submit(ctx, 1, &iocb); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBAIO_H */ diff --git a/tools/libaio/src/libaio.map b/tools/libaio/src/libaio.map new file mode 100644 index 0000000..dc37725 --- /dev/null +++ b/tools/libaio/src/libaio.map @@ -0,0 +1,22 @@ +LIBAIO_0.1 { + global: + io_queue_init; + io_queue_run; + io_queue_wait; + io_queue_release; + io_cancel; + io_submit; + io_getevents; + local: + *; + +}; + +LIBAIO_0.4 { + global: + io_setup; + io_destroy; + io_cancel; + io_getevents; + io_queue_wait; +} LIBAIO_0.1; diff --git a/tools/libaio/src/raw_syscall.c b/tools/libaio/src/raw_syscall.c new file mode 100644 index 0000000..c3fe4b8 --- /dev/null +++ b/tools/libaio/src/raw_syscall.c @@ -0,0 +1,19 @@ +#include "syscall.h" + +#if defined(__ia64__) +/* based on code from glibc by Jes Sorensen */ +__asm__(".text\n" + ".globl __ia64_aio_raw_syscall\n" + ".proc __ia64_aio_raw_syscall\n" + "__ia64_aio_raw_syscall:\n" + "alloc r2=ar.pfs,1,0,8,0\n" + "mov r15=r32\n" + "break 0x100000\n" + ";;" + "br.ret.sptk.few b0\n" + ".size __ia64_aio_raw_syscall, . - __ia64_aio_raw_syscall\n" + ".endp __ia64_aio_raw_syscall" +); +#endif + +; diff --git a/tools/libaio/src/syscall-alpha.h b/tools/libaio/src/syscall-alpha.h new file mode 100644 index 0000000..467b74f --- /dev/null +++ b/tools/libaio/src/syscall-alpha.h @@ -0,0 +1,209 @@ +#define __NR_io_setup 398 +#define __NR_io_destroy 399 +#define __NR_io_getevents 400 +#define __NR_io_submit 401 +#define __NR_io_cancel 402 + +#define inline_syscall_r0_asm +#define inline_syscall_r0_out_constraint "=v" + +#define inline_syscall_clobbers \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", \ + "$22", "$23", "$24", "$25", "$27", "$28", "memory" + +#define inline_syscall0(name, args...) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_19 __asm__("$19"); \ + \ + _sc_0 = name; \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19) \ + : "0"(_sc_0) \ + : inline_syscall_clobbers, \ + "$16", "$17", "$18", "$20", "$21"); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define inline_syscall1(name,arg1) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_16 __asm__("$16"); \ + register long _sc_19 __asm__("$19"); \ + \ + _sc_0 = name; \ + _sc_16 = (long) (arg1); \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2 %3" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19), "=r"(_sc_16) \ + : "0"(_sc_0), "2"(_sc_16) \ + : inline_syscall_clobbers, \ + "$17", "$18", "$20", "$21"); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define inline_syscall2(name,arg1,arg2) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_16 __asm__("$16"); \ + register long _sc_17 __asm__("$17"); \ + register long _sc_19 __asm__("$19"); \ + \ + _sc_0 = name; \ + _sc_16 = (long) (arg1); \ + _sc_17 = (long) (arg2); \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2 %3 %4" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19), "=r"(_sc_16), "=r"(_sc_17) \ + : "0"(_sc_0), "2"(_sc_16), "3"(_sc_17) \ + : inline_syscall_clobbers, \ + "$18", "$20", "$21"); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define inline_syscall3(name,arg1,arg2,arg3) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_16 __asm__("$16"); \ + register long _sc_17 __asm__("$17"); \ + register long _sc_18 __asm__("$18"); \ + register long _sc_19 __asm__("$19"); \ + \ + _sc_0 = name; \ + _sc_16 = (long) (arg1); \ + _sc_17 = (long) (arg2); \ + _sc_18 = (long) (arg3); \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2 %3 %4 %5" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19), "=r"(_sc_16), "=r"(_sc_17), \ + "=r"(_sc_18) \ + : "0"(_sc_0), "2"(_sc_16), "3"(_sc_17), \ + "4"(_sc_18) \ + : inline_syscall_clobbers, "$20", "$21"); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define inline_syscall4(name,arg1,arg2,arg3,arg4) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_16 __asm__("$16"); \ + register long _sc_17 __asm__("$17"); \ + register long _sc_18 __asm__("$18"); \ + register long _sc_19 __asm__("$19"); \ + \ + _sc_0 = name; \ + _sc_16 = (long) (arg1); \ + _sc_17 = (long) (arg2); \ + _sc_18 = (long) (arg3); \ + _sc_19 = (long) (arg4); \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2 %3 %4 %5 %6" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19), "=r"(_sc_16), "=r"(_sc_17), \ + "=r"(_sc_18) \ + : "0"(_sc_0), "2"(_sc_16), "3"(_sc_17), \ + "4"(_sc_18), "1"(_sc_19) \ + : inline_syscall_clobbers, "$20", "$21"); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define inline_syscall5(name,arg1,arg2,arg3,arg4,arg5) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_16 __asm__("$16"); \ + register long _sc_17 __asm__("$17"); \ + register long _sc_18 __asm__("$18"); \ + register long _sc_19 __asm__("$19"); \ + register long _sc_20 __asm__("$20"); \ + \ + _sc_0 = name; \ + _sc_16 = (long) (arg1); \ + _sc_17 = (long) (arg2); \ + _sc_18 = (long) (arg3); \ + _sc_19 = (long) (arg4); \ + _sc_20 = (long) (arg5); \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2 %3 %4 %5 %6 %7" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19), "=r"(_sc_16), "=r"(_sc_17), \ + "=r"(_sc_18), "=r"(_sc_20) \ + : "0"(_sc_0), "2"(_sc_16), "3"(_sc_17), \ + "4"(_sc_18), "1"(_sc_19), "5"(_sc_20) \ + : inline_syscall_clobbers, "$21"); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define inline_syscall6(name,arg1,arg2,arg3,arg4,arg5,arg6) \ +{ \ + register long _sc_0 inline_syscall_r0_asm; \ + register long _sc_16 __asm__("$16"); \ + register long _sc_17 __asm__("$17"); \ + register long _sc_18 __asm__("$18"); \ + register long _sc_19 __asm__("$19"); \ + register long _sc_20 __asm__("$20"); \ + register long _sc_21 __asm__("$21"); \ + \ + _sc_0 = name; \ + _sc_16 = (long) (arg1); \ + _sc_17 = (long) (arg2); \ + _sc_18 = (long) (arg3); \ + _sc_19 = (long) (arg4); \ + _sc_20 = (long) (arg5); \ + _sc_21 = (long) (arg6); \ + __asm__ __volatile__ \ + ("callsys # %0 %1 <= %2 %3 %4 %5 %6 %7 %8" \ + : inline_syscall_r0_out_constraint (_sc_0), \ + "=r"(_sc_19), "=r"(_sc_16), "=r"(_sc_17), \ + "=r"(_sc_18), "=r"(_sc_20), "=r"(_sc_21) \ + : "0"(_sc_0), "2"(_sc_16), "3"(_sc_17), "4"(_sc_18), \ + "1"(_sc_19), "5"(_sc_20), "6"(_sc_21) \ + : inline_syscall_clobbers); \ + _sc_ret = _sc_0, _sc_err = _sc_19; \ +} + +#define INLINE_SYSCALL1(name, nr, args...) \ +({ \ + long _sc_ret, _sc_err; \ + inline_syscall##nr(__NR_##name, args); \ + if (_sc_err != 0) \ + { \ + _sc_ret = -(_sc_ret); \ + } \ + _sc_ret; \ +}) + +#define io_syscall1(type,fname,sname,type1,arg1) \ +type fname(type1 arg1) \ +{ \ + return (type)INLINE_SYSCALL1(sname, 1, arg1); \ +} + +#define io_syscall2(type,fname,sname,type1,arg1,type2,arg2) \ +type fname(type1 arg1,type2 arg2) \ +{ \ + return (type)INLINE_SYSCALL1(sname, 2, arg1, arg2); \ +} + +#define io_syscall3(type,fname,sname,type1,arg1,type2,arg2,type3,arg3) \ +type fname(type1 arg1,type2 arg2,type3 arg3) \ +{ \ + return (type)INLINE_SYSCALL1(sname, 3, arg1, arg2, arg3); \ +} + +#define io_syscall4(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type fname (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + return (type)INLINE_SYSCALL1(sname, 4, arg1, arg2, arg3, arg4); \ +} + +#define io_syscall5(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ +type fname (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ +{ \ + return (type)INLINE_SYSCALL1(sname, 5, arg1, arg2, arg3, arg4, arg5);\ +} diff --git a/tools/libaio/src/syscall-i386.h b/tools/libaio/src/syscall-i386.h new file mode 100644 index 0000000..9576975 --- /dev/null +++ b/tools/libaio/src/syscall-i386.h @@ -0,0 +1,72 @@ +#define __NR_io_setup 245 +#define __NR_io_destroy 246 +#define __NR_io_getevents 247 +#define __NR_io_submit 248 +#define __NR_io_cancel 249 + +#define io_syscall1(type,fname,sname,type1,arg1) \ +type fname(type1 arg1) \ +{ \ +long __res; \ +__asm__ volatile ("xchgl %%edi,%%ebx\n" \ + "int $0x80\n" \ + "xchgl %%edi,%%ebx" \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1))); \ +return __res; \ +} + +#define io_syscall2(type,fname,sname,type1,arg1,type2,arg2) \ +type fname(type1 arg1,type2 arg2) \ +{ \ +long __res; \ +__asm__ volatile ("xchgl %%edi,%%ebx\n" \ + "int $0x80\n" \ + "xchgl %%edi,%%ebx" \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"c" ((long)(arg2))); \ +return __res; \ +} + +#define io_syscall3(type,fname,sname,type1,arg1,type2,arg2,type3,arg3) \ +type fname(type1 arg1,type2 arg2,type3 arg3) \ +{ \ +long __res; \ +__asm__ volatile ("xchgl %%edi,%%ebx\n" \ + "int $0x80\n" \ + "xchgl %%edi,%%ebx" \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ +return __res; \ +} + +#define io_syscall4(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type fname (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ +long __res; \ +__asm__ volatile ("xchgl %%edi,%%ebx\n" \ + "int $0x80\n" \ + "xchgl %%edi,%%ebx" \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ +return __res; \ +} + +#define io_syscall5(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ +type fname (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ +{ \ +long __res; \ +long tmp; \ +__asm__ volatile ("movl %%ebx,%7\n" \ + "movl %2,%%ebx\n" \ + "int $0x80\n" \ + "movl %7,%%ebx" \ + : "=a" (__res) \ + : "0" (__NR_##sname),"rm" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \ + "m" (tmp)); \ +return __res; \ +} diff --git a/tools/libaio/src/syscall-ia64.h b/tools/libaio/src/syscall-ia64.h new file mode 100644 index 0000000..52ce9dd --- /dev/null +++ b/tools/libaio/src/syscall-ia64.h @@ -0,0 +1,45 @@ +#define __NR_io_setup 1238 +#define __NR_io_destroy 1239 +#define __NR_io_getevents 1240 +#define __NR_io_submit 1241 +#define __NR_io_cancel 1242 + +#define __ia64_raw_syscall(fname, sname) \ + __asm__ (".text\n" \ + ".globl " SYMSTR(fname) "\n" \ + ".proc " SYMSTR(fname) "\n" \ + SYMSTR(fname) ":\n" \ + " mov r15=" SYMSTR( __NR_ ## sname ) "\n" \ + " break 0x100000\n" \ + " ;;\n" \ + " cmp.eq p6,p0=-1,r10\n" \ + " ;;\n" \ + " (p6) sub r8=0,r8\n" \ + " br.ret.sptk.few b0\n" \ + ".size " SYMSTR(fname) ", . - " SYMSTR(fname) "\n" \ + ".endp " SYMSTR(fname) "\n" \ + ); + +#define io_syscall0(type, name) \ + extern type name(void); \ + __ia64_raw_syscall(name); + +#define io_syscall1(type, fname, sname, type1, arg1) \ + extern type fname(type1 arg1); \ + __ia64_raw_syscall(fname, sname); + +#define io_syscall2(type, fname, sname, type1, arg1, type2, arg2) \ + extern type fname(type1 arg1, type2 arg2); \ + __ia64_raw_syscall(fname, sname); + +#define io_syscall3(type, fname, sname, type1, arg1, type2, arg2, type3, arg3) \ + extern type fname(type1 arg1, type2 arg2, type3 arg3); \ + __ia64_raw_syscall(fname, sname); + +#define io_syscall4(type, fname, sname, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + extern type fname(type1 arg1, type2 arg2, type3 arg3, type4 arg4); \ + __ia64_raw_syscall(fname, sname); + +#define io_syscall5(type, fname, sname, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ + extern type fname(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5); \ + __ia64_raw_syscall(fname, sname); diff --git a/tools/libaio/src/syscall-ppc.h b/tools/libaio/src/syscall-ppc.h new file mode 100644 index 0000000..435513e --- /dev/null +++ b/tools/libaio/src/syscall-ppc.h @@ -0,0 +1,98 @@ +#include +#include + +#define __NR_io_setup 227 +#define __NR_io_destroy 228 +#define __NR_io_getevents 229 +#define __NR_io_submit 230 +#define __NR_io_cancel 231 + +/* On powerpc a system call basically clobbers the same registers like a + * function call, with the exception of LR (which is needed for the + * "sc; bnslr" sequence) and CR (where only CR0.SO is clobbered to signal + * an error return status). + */ +#ifndef __syscall_nr +#define __syscall_nr(nr, type, name, args...) \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + \ + __sc_loadargs_##nr(name, args); \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %0 " \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : __sc_asm_input_##nr \ + : "cr0", "ctr", "memory", \ + "r9", "r10","r11", "r12"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + if (__sc_err & 0x10000000) return -((int)__sc_ret); \ + return (type) __sc_ret +#endif + +#define __sc_loadargs_0(name, dummy...) \ + __sc_0 = __NR_##name +#define __sc_loadargs_1(name, arg1) \ + __sc_loadargs_0(name); \ + __sc_3 = (unsigned long) (arg1) +#define __sc_loadargs_2(name, arg1, arg2) \ + __sc_loadargs_1(name, arg1); \ + __sc_4 = (unsigned long) (arg2) +#define __sc_loadargs_3(name, arg1, arg2, arg3) \ + __sc_loadargs_2(name, arg1, arg2); \ + __sc_5 = (unsigned long) (arg3) +#define __sc_loadargs_4(name, arg1, arg2, arg3, arg4) \ + __sc_loadargs_3(name, arg1, arg2, arg3); \ + __sc_6 = (unsigned long) (arg4) +#define __sc_loadargs_5(name, arg1, arg2, arg3, arg4, arg5) \ + __sc_loadargs_4(name, arg1, arg2, arg3, arg4); \ + __sc_7 = (unsigned long) (arg5) + +#define __sc_asm_input_0 "0" (__sc_0) +#define __sc_asm_input_1 __sc_asm_input_0, "1" (__sc_3) +#define __sc_asm_input_2 __sc_asm_input_1, "2" (__sc_4) +#define __sc_asm_input_3 __sc_asm_input_2, "3" (__sc_5) +#define __sc_asm_input_4 __sc_asm_input_3, "4" (__sc_6) +#define __sc_asm_input_5 __sc_asm_input_4, "5" (__sc_7) + +#define io_syscall1(type,fname,sname,type1,arg1) \ +type fname(type1 arg1) \ +{ \ + __syscall_nr(1, type, sname, arg1); \ +} + +#define io_syscall2(type,fname,sname,type1,arg1,type2,arg2) \ +type fname(type1 arg1, type2 arg2) \ +{ \ + __syscall_nr(2, type, sname, arg1, arg2); \ +} + +#define io_syscall3(type,fname,sname,type1,arg1,type2,arg2,type3,arg3) \ +type fname(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + __syscall_nr(3, type, sname, arg1, arg2, arg3); \ +} + +#define io_syscall4(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type fname(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + __syscall_nr(4, type, sname, arg1, arg2, arg3, arg4); \ +} + +#define io_syscall5(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \ +type fname(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ +{ \ + __syscall_nr(5, type, sname, arg1, arg2, arg3, arg4, arg5); \ +} diff --git a/tools/libaio/src/syscall-s390.h b/tools/libaio/src/syscall-s390.h new file mode 100644 index 0000000..3ec5ee3 --- /dev/null +++ b/tools/libaio/src/syscall-s390.h @@ -0,0 +1,131 @@ +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 + +#define io_svc_clobber "1", "cc", "memory" + +#define io_syscall1(type,fname,sname,type1,arg1) \ +type fname(type1 arg1) { \ + register type1 __arg1 asm("2") = arg1; \ + register long __svcres asm("2"); \ + long __res; \ + __asm__ __volatile__ ( \ + " .if %1 < 256\n" \ + " svc %b1\n" \ + " .else\n" \ + " la %%r1,%1\n" \ + " .svc 0\n" \ + " .endif" \ + : "=d" (__svcres) \ + : "i" (__NR_##sname), \ + "0" (__arg1) \ + : io_svc_clobber ); \ + __res = __svcres; \ + return (type) __res; \ +} + +#define io_syscall2(type,fname,sname,type1,arg1,type2,arg2) \ +type fname(type1 arg1, type2 arg2) { \ + register type1 __arg1 asm("2") = arg1; \ + register type2 __arg2 asm("3") = arg2; \ + register long __svcres asm("2"); \ + long __res; \ + __asm__ __volatile__ ( \ + " .if %1 < 256\n" \ + " svc %b1\n" \ + " .else\n" \ + " la %%r1,%1\n" \ + " svc 0\n" \ + " .endif" \ + : "=d" (__svcres) \ + : "i" (__NR_##sname), \ + "0" (__arg1), \ + "d" (__arg2) \ + : io_svc_clobber ); \ + __res = __svcres; \ + return (type) __res; \ +} + +#define io_syscall3(type,fname,sname,type1,arg1,type2,arg2, \ + type3,arg3) \ +type fname(type1 arg1, type2 arg2, type3 arg3) { \ + register type1 __arg1 asm("2") = arg1; \ + register type2 __arg2 asm("3") = arg2; \ + register type3 __arg3 asm("4") = arg3; \ + register long __svcres asm("2"); \ + long __res; \ + __asm__ __volatile__ ( \ + " .if %1 < 256\n" \ + " svc %b1\n" \ + " .else\n" \ + " la %%r1,%1\n" \ + " svc 0\n" \ + " .endif" \ + : "=d" (__svcres) \ + : "i" (__NR_##sname), \ + "0" (__arg1), \ + "d" (__arg2), \ + "d" (__arg3) \ + : io_svc_clobber ); \ + __res = __svcres; \ + return (type) __res; \ +} + +#define io_syscall4(type,fname,sname,type1,arg1,type2,arg2, \ + type3,arg3,type4,arg4) \ +type fname(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + register type1 __arg1 asm("2") = arg1; \ + register type2 __arg2 asm("3") = arg2; \ + register type3 __arg3 asm("4") = arg3; \ + register type4 __arg4 asm("5") = arg4; \ + register long __svcres asm("2"); \ + long __res; \ + __asm__ __volatile__ ( \ + " .if %1 < 256\n" \ + " svc %b1\n" \ + " .else\n" \ + " la %%r1,%1\n" \ + " svc 0\n" \ + " .endif" \ + : "=d" (__svcres) \ + : "i" (__NR_##sname), \ + "0" (__arg1), \ + "d" (__arg2), \ + "d" (__arg3), \ + "d" (__arg4) \ + : io_svc_clobber ); \ + __res = __svcres; \ + return (type) __res; \ +} + +#define io_syscall5(type,fname,sname,type1,arg1,type2,arg2, \ + type3,arg3,type4,arg4,type5,arg5) \ +type fname(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + register type1 __arg1 asm("2") = arg1; \ + register type2 __arg2 asm("3") = arg2; \ + register type3 __arg3 asm("4") = arg3; \ + register type4 __arg4 asm("5") = arg4; \ + register type5 __arg5 asm("6") = arg5; \ + register long __svcres asm("2"); \ + long __res; \ + __asm__ __volatile__ ( \ + " .if %1 < 256\n" \ + " svc %b1\n" \ + " .else\n" \ + " la %%r1,%1\n" \ + " svc 0\n" \ + " .endif" \ + : "=d" (__svcres) \ + : "i" (__NR_##sname), \ + "0" (__arg1), \ + "d" (__arg2), \ + "d" (__arg3), \ + "d" (__arg4), \ + "d" (__arg5) \ + : io_svc_clobber ); \ + __res = __svcres; \ + return (type) __res; \ +} diff --git a/tools/libaio/src/syscall-x86_64.h b/tools/libaio/src/syscall-x86_64.h new file mode 100644 index 0000000..9361856 --- /dev/null +++ b/tools/libaio/src/syscall-x86_64.h @@ -0,0 +1,63 @@ +#define __NR_io_setup 206 +#define __NR_io_destroy 207 +#define __NR_io_getevents 208 +#define __NR_io_submit 209 +#define __NR_io_cancel 210 + +#define __syscall_clobber "r11","rcx","memory" +#define __syscall "syscall" + +#define io_syscall1(type,fname,sname,type1,arg1) \ +type fname(type1 arg1) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)) : __syscall_clobber ); \ +return __res; \ +} + +#define io_syscall2(type,fname,sname,type1,arg1,type2,arg2) \ +type fname(type1 arg1,type2 arg2) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"S" ((long)(arg2)) : __syscall_clobber ); \ +return __res; \ +} + +#define io_syscall3(type,fname,sname,type1,arg1,type2,arg2,type3,arg3) \ +type fname(type1 arg1,type2 arg2,type3 arg3) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"S" ((long)(arg2)), \ + "d" ((long)(arg3)) : __syscall_clobber); \ +return __res; \ +} + +#define io_syscall4(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type fname (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ +long __res; \ +__asm__ volatile ("movq %5,%%r10 ;" __syscall \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"S" ((long)(arg2)), \ + "d" ((long)(arg3)),"g" ((long)(arg4)) : __syscall_clobber,"r10" ); \ +return __res; \ +} + +#define io_syscall5(type,fname,sname,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ +type fname (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ +{ \ +long __res; \ +__asm__ volatile ("movq %5,%%r10 ; movq %6,%%r8 ; " __syscall \ + : "=a" (__res) \ + : "0" (__NR_##sname),"D" ((long)(arg1)),"S" ((long)(arg2)), \ + "d" ((long)(arg3)),"g" ((long)(arg4)),"g" ((long)(arg5)) : \ + __syscall_clobber,"r8","r10" ); \ +return __res; \ +} diff --git a/tools/libaio/src/syscall.h b/tools/libaio/src/syscall.h new file mode 100644 index 0000000..0283825 --- /dev/null +++ b/tools/libaio/src/syscall.h @@ -0,0 +1,27 @@ +#include +#include + +#define _SYMSTR(str) #str +#define SYMSTR(str) _SYMSTR(str) + +#define SYMVER(compat_sym, orig_sym, ver_sym) \ + __asm__(".symver " SYMSTR(compat_sym) "," SYMSTR(orig_sym) "@LIBAIO_" SYMSTR(ver_sym)); + +#define DEFSYMVER(compat_sym, orig_sym, ver_sym) \ + __asm__(".symver " SYMSTR(compat_sym) "," SYMSTR(orig_sym) "@@LIBAIO_" SYMSTR(ver_sym)); + +#if defined(__i386__) +#include "syscall-i386.h" +#elif defined(__x86_64__) +#include "syscall-x86_64.h" +#elif defined(__ia64__) +#include "syscall-ia64.h" +#elif defined(__PPC__) +#include "syscall-ppc.h" +#elif defined(__s390__) +#include "syscall-s390.h" +#elif defined(__alpha__) +#include "syscall-alpha.h" +#else +#error "add syscall-arch.h" +#endif diff --git a/tools/libaio/src/vsys_def.h b/tools/libaio/src/vsys_def.h new file mode 100644 index 0000000..13d032e --- /dev/null +++ b/tools/libaio/src/vsys_def.h @@ -0,0 +1,24 @@ +/* libaio Linux async I/O interface + Copyright 2002 Red Hat, 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 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +extern int vsys_io_setup(unsigned nr_reqs, io_context_t *ctxp); +extern int vsys_io_destroy(io_context_t ctx); +extern int vsys_io_submit(io_context_t ctx, long nr, struct iocb *iocbs[]); +extern int vsys_io_cancel(io_context_t ctx, struct iocb *iocb); +extern int vsys_io_wait(io_context_t ctx, struct iocb *iocb, const struct timespec *when); +extern int vsys_io_getevents(io_context_t ctx_id, long nr, struct io_event *events, const struct timespec *timeout); + diff --git a/tools/libfsimage/Makefile b/tools/libfsimage/Makefile new file mode 100644 index 0000000..d5194fe --- /dev/null +++ b/tools/libfsimage/Makefile @@ -0,0 +1,11 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +SUBDIRS-y = common ufs reiserfs iso9660 fat zfs +SUBDIRS-y += $(shell env CC="$(CC)" ./check-libext2fs) + +.PHONY: all clean install +all clean install: %: subdirs-% + +.PHONY: distclean +distclean: clean diff --git a/tools/libfsimage/Rules.mk b/tools/libfsimage/Rules.mk new file mode 100644 index 0000000..afc08bd --- /dev/null +++ b/tools/libfsimage/Rules.mk @@ -0,0 +1,33 @@ +include $(XEN_ROOT)/tools/Rules.mk + +DEPS = .*.d + +CFLAGS += -I$(XEN_ROOT)/tools/libfsimage/common/ -Werror -Wp,-MD,.$(@F).d +LDFLAGS += -L../common/ + +PIC_OBJS := $(patsubst %.c,%.opic,$(LIB_SRCS-y)) + +FSDIR-$(CONFIG_Linux) = $(LIBDIR)/fs/$(FS) +FSDIR-$(CONFIG_SunOS)-x86_64 = $(PREFIX)/lib/fs/$(FS)/64 +FSDIR-$(CONFIG_SunOS)-x86_32 = $(PREFIX)/lib/fs/$(FS)/ +FSDIR-$(CONFIG_SunOS) = $(FSDIR-$(CONFIG_SunOS)-$(XEN_TARGET_ARCH)) +FSDIR-$(CONFIG_NetBSD) = $(LIBDIR)/fs/$(FS) +FSDIR = $(FSDIR-y) + +FSLIB = fsimage.so + +.PHONY: fs-all +fs-all: $(FSLIB) + +.PHONY: fs-install +fs-install: fs-all + $(INSTALL_DIR) $(DESTDIR)$(FSDIR) + $(INSTALL_PROG) $(FSLIB) $(DESTDIR)$(FSDIR) + +$(FSLIB): $(PIC_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(SHLIB_CFLAGS) -o $@ $^ -lfsimage $(FS_LIBDEPS) + +clean distclean: + rm -f $(PIC_OBJS) $(FSLIB) $(DEPS) + +-include $(DEPS) diff --git a/tools/libfsimage/check-libext2fs b/tools/libfsimage/check-libext2fs new file mode 100755 index 0000000..a861806 --- /dev/null +++ b/tools/libfsimage/check-libext2fs @@ -0,0 +1,21 @@ +#!/bin/sh + +cat >ext2-test.c < + +int main() +{ + ext2fs_open2; +} +EOF + +${CC-gcc} -o ext2-test ext2-test.c -lext2fs >/dev/null 2>&1 +if [ $? = 0 ]; then + echo ext2fs-lib +else + echo ext2fs +fi + +rm -f ext2-test ext2-test.c + +exit 0 diff --git a/tools/libfsimage/common/Makefile b/tools/libfsimage/common/Makefile new file mode 100644 index 0000000..641bca5 --- /dev/null +++ b/tools/libfsimage/common/Makefile @@ -0,0 +1,46 @@ +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 1.0 +MINOR = 0 + +CFLAGS += -Werror -Wp,-MD,.$(@F).d +DEPS = .*.d + +LDFLAGS-$(CONFIG_SunOS) = -Wl,-M -Wl,mapfile-SunOS +LDFLAGS-$(CONFIG_Linux) = -Wl,mapfile-GNU +LDFLAGS = $(LDFLAGS-y) + +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 $(PIC_OBJS) $(LIB) $(DEPS) + +libfsimage.so: libfsimage.so.$(MAJOR) + ln -sf $< $@ +libfsimage.so.$(MAJOR): libfsimage.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libfsimage.so.$(MAJOR).$(MINOR): $(PIC_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libfsimage.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ -lpthread + +-include $(DEPS) + diff --git a/tools/libfsimage/common/fsimage.c b/tools/libfsimage/common/fsimage.c new file mode 100644 index 0000000..21d6c38 --- /dev/null +++ b/tools/libfsimage/common/fsimage.c @@ -0,0 +1,169 @@ +/* + * 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); +} diff --git a/tools/libfsimage/common/fsimage.h b/tools/libfsimage/common/fsimage.h new file mode 100644 index 0000000..201abd5 --- /dev/null +++ b/tools/libfsimage/common/fsimage.h @@ -0,0 +1,56 @@ +/* + * 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 */ diff --git a/tools/libfsimage/common/fsimage_grub.c b/tools/libfsimage/common/fsimage_grub.c new file mode 100644 index 0000000..c58790d --- /dev/null +++ b/tools/libfsimage/common/fsimage_grub.c @@ -0,0 +1,386 @@ +/* + * 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; +} + +#elif defined(__ia64__) + +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define ia64_popcnt(x) __builtin_popcountl(x) +#else +# define ia64_popcnt(x) \ + ({ \ + uint64_t ia64_intri_res; \ + asm ("popcnt %0=%1" : "=r" (ia64_intri_res) : "r" (x)); \ + ia64_intri_res; \ + }) +#endif + +unsigned long +fsig_log2 (unsigned long word) +{ + unsigned long result; + + result = ia64_popcnt((word - 1) & ~word); + return result; +} + +#elif defined(__powerpc__) + +#ifdef __powerpc64__ +#define PPC_CNTLZL "cntlzd" +#else +#define PPC_CNTLZL "cntlzw" +#endif +#define BITS_PER_LONG (sizeof(long) * 8) + +static int +__ilog2(unsigned long x) +{ + int lz; + + asm (PPC_CNTLZL " %0,%1" : "=r" (lz) : "r" (x)); + return BITS_PER_LONG - 1 - lz; +} + +unsigned long +fsig_log2 (unsigned long word) +{ + return __ilog2(word & -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); +} diff --git a/tools/libfsimage/common/fsimage_grub.h b/tools/libfsimage/common/fsimage_grub.h new file mode 100644 index 0000000..11684d3 --- /dev/null +++ b/tools/libfsimage/common/fsimage_grub.h @@ -0,0 +1,101 @@ +/* + * 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 + +#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 + +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 */ diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c new file mode 100644 index 0000000..5ee9d74 --- /dev/null +++ b/tools/libfsimage/common/fsimage_plugin.c @@ -0,0 +1,212 @@ +/* + * 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"); + const char *isadir = ""; + struct dirent *dp = NULL; + struct dirent *dpp; + DIR *dir = NULL; + char *tmp = NULL; + size_t name_max; + int err; + int ret = -1; + +#if defined(FSIMAGE_FSDIR) + if (fsdir == NULL) + fsdir = FSIMAGE_FSDIR; +#elif defined(__sun__) + if (fsdir == NULL) + fsdir = "/usr/lib/fs"; + + if (sizeof(void *) == 8) + isadir = "64/"; +#elif defined(__ia64__) + if (fsdir == NULL) + fsdir = "/usr/lib/fs"; +#else + if (fsdir == NULL) { + if (sizeof(void *) == 8) + fsdir = "/usr/lib64/fs"; + else + fsdir = "/usr/lib/fs"; + } +#endif + + if ((name_max = pathconf(fsdir, _PC_NAME_MAX)) == -1) + goto fail; + + if ((tmp = malloc(name_max + 1)) == NULL) + goto fail; + + if ((dp = malloc(sizeof (struct dirent) + name_max + 1)) == NULL) + goto fail; + + if ((dir = opendir(fsdir)) == NULL) + goto fail; + + bzero(dp, sizeof (struct dirent) + name_max + 1); + + while (readdir_r(dir, dp, &dpp) == 0 && dpp != NULL) { + if (strcmp(dpp->d_name, ".") == 0) + continue; + if (strcmp(dpp->d_name, "..") == 0) + continue; + + (void) snprintf(tmp, name_max, "%s/%s/%sfsimage.so", fsdir, + dpp->d_name, isadir); + + 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); +} diff --git a/tools/libfsimage/common/fsimage_plugin.h b/tools/libfsimage/common/fsimage_plugin.h new file mode 100644 index 0000000..a682719 --- /dev/null +++ b/tools/libfsimage/common/fsimage_plugin.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef _FSIMAGE_PLUGIN_H +#define _FSIMAGE_PLUGIN_H + +#ifdef __cplusplus +extern C { +#endif + +#include + +#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 */ diff --git a/tools/libfsimage/common/fsimage_priv.h b/tools/libfsimage/common/fsimage_priv.h new file mode 100644 index 0000000..57a0826 --- /dev/null +++ b/tools/libfsimage/common/fsimage_priv.h @@ -0,0 +1,63 @@ +/* + * 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 */ diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU new file mode 100644 index 0000000..26d4d7a --- /dev/null +++ b/tools/libfsimage/common/mapfile-GNU @@ -0,0 +1,40 @@ +VERSION { + 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: + *; + }; +} diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS new file mode 100644 index 0000000..e99b90b --- /dev/null +++ b/tools/libfsimage/common/mapfile-SunOS @@ -0,0 +1,38 @@ +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_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: + *; +}; diff --git a/tools/libfsimage/ext2fs-lib/Makefile b/tools/libfsimage/ext2fs-lib/Makefile new file mode 100644 index 0000000..a60b3a5 --- /dev/null +++ b/tools/libfsimage/ext2fs-lib/Makefile @@ -0,0 +1,15 @@ +XEN_ROOT = ../../.. + +LIB_SRCS-y = ext2fs-lib.c + +FS = ext2fs-lib + +FS_LIBDEPS = -lext2fs + +.PHONY: all +all: fs-all + +.PHONY: install +install: fs-install + +include $(XEN_ROOT)/tools/libfsimage/Rules.mk diff --git a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c new file mode 100644 index 0000000..36a27dc --- /dev/null +++ b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c @@ -0,0 +1,174 @@ +/* + * 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 + +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); +} diff --git a/tools/libfsimage/ext2fs/Makefile b/tools/libfsimage/ext2fs/Makefile new file mode 100644 index 0000000..43a4501 --- /dev/null +++ b/tools/libfsimage/ext2fs/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../../.. + +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 diff --git a/tools/libfsimage/ext2fs/fsys_ext2fs.c b/tools/libfsimage/ext2fs/fsys_ext2fs.c new file mode 100644 index 0000000..12fc8ca --- /dev/null +++ b/tools/libfsimage/ext2fs/fsys_ext2fs.c @@ -0,0 +1,832 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#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) + +/* 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) + + +/* 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); +} + +/* 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 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)]; +} + +/* 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) */ + 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; + ino_blk = gdp[desc].bg_inode_table + + (((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)) + >> log2 (EXT2_INODES_PER_BLOCK (SUPERBLOCK))); +#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)); +} diff --git a/tools/libfsimage/fat/Makefile b/tools/libfsimage/fat/Makefile new file mode 100644 index 0000000..bfc55e4 --- /dev/null +++ b/tools/libfsimage/fat/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../../.. + +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 diff --git a/tools/libfsimage/fat/fat.h b/tools/libfsimage/fat/fat.h new file mode 100644 index 0000000..f72b72a --- /dev/null +++ b/tools/libfsimage/fat/fat.h @@ -0,0 +1,100 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * 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))) diff --git a/tools/libfsimage/fat/fsys_fat.c b/tools/libfsimage/fat/fsys_fat.c new file mode 100644 index 0000000..d22d243 --- /dev/null +++ b/tools/libfsimage/fat/fsys_fat.c @@ -0,0 +1,485 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#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)); +} diff --git a/tools/libfsimage/iso9660/Makefile b/tools/libfsimage/iso9660/Makefile new file mode 100644 index 0000000..6e71694 --- /dev/null +++ b/tools/libfsimage/iso9660/Makefile @@ -0,0 +1,15 @@ +XEN_ROOT = ../../.. + +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 diff --git a/tools/libfsimage/iso9660/fsys_iso9660.c b/tools/libfsimage/iso9660/fsys_iso9660.c new file mode 100644 index 0000000..b991cfc --- /dev/null +++ b/tools/libfsimage/iso9660/fsys_iso9660.c @@ -0,0 +1,463 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * 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)); +} diff --git a/tools/libfsimage/iso9660/iso9660.h b/tools/libfsimage/iso9660/iso9660.h new file mode 100644 index 0000000..83d0019 --- /dev/null +++ b/tools/libfsimage/iso9660/iso9660.h @@ -0,0 +1,219 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * 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< + +#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 +{ + unsigned long dc_block_number; /* Disk child's block number. */ + unsigned short 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)); +} diff --git a/tools/libfsimage/ufs/Makefile b/tools/libfsimage/ufs/Makefile new file mode 100644 index 0000000..b7218c2 --- /dev/null +++ b/tools/libfsimage/ufs/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../../.. + +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 diff --git a/tools/libfsimage/ufs/fsys_ufs.c b/tools/libfsimage/ufs/fsys_ufs.c new file mode 100644 index 0000000..be51411 --- /dev/null +++ b/tools/libfsimage/ufs/fsys_ufs.c @@ -0,0 +1,278 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * 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)); +} diff --git a/tools/libfsimage/ufs/ufs.h b/tools/libfsimage/ufs/ufs.h new file mode 100644 index 0000000..4e7c736 --- /dev/null +++ b/tools/libfsimage/ufs/ufs.h @@ -0,0 +1,228 @@ +/* + * 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 */ diff --git a/tools/libfsimage/zfs/Makefile b/tools/libfsimage/zfs/Makefile new file mode 100644 index 0000000..f77daef --- /dev/null +++ b/tools/libfsimage/zfs/Makefile @@ -0,0 +1,37 @@ +# +# 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, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +XEN_ROOT = ../../.. + +LIB_SRCS-y = fsys_zfs.c zfs_lzjb.c zfs_sha256.c zfs_fletcher.c + +FS = zfs + +.PHONY: all +all: fs-all + +.PHONY: install +install: fs-install + +include $(XEN_ROOT)/tools/libfsimage/Rules.mk diff --git a/tools/libfsimage/zfs/fsys_zfs.c b/tools/libfsimage/zfs/fsys_zfs.c new file mode 100644 index 0000000..1409e3c --- /dev/null +++ b/tools/libfsimage/zfs/fsys_zfs.c @@ -0,0 +1,1457 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * All files in the zfs directory are derived from the OpenSolaris + * zfs grub files. All files in the zfs-include directory were + * included without changes. + */ + +/* + * 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 | + * +--------------------------------------------+ + */ + +#include +#include + +/* 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 MIN(x, y) ((x) < (y) ? (x) : (y)) +/* End from shared.h */ + +#include "fsys_zfs.h" + +/* cache for a file block of the currently zfs_open()-ed file */ +#define file_buf zfs_ba->zfs_file_buf +#define file_start zfs_ba->zfs_file_start +#define file_end zfs_ba->zfs_file_end + +/* cache for a dnode block */ +#define dnode_buf zfs_ba->zfs_dnode_buf +#define dnode_mdn zfs_ba->zfs_dnode_mdn +#define dnode_start zfs_ba->zfs_dnode_start +#define dnode_end zfs_ba->zfs_dnode_end + +#define stackbase zfs_ba->zfs_stackbase + +decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = +{ + {"noop", 0}, + {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ + {"off", 0}, + {"lzjb", lzjb_decompress} /* ZIO_COMPRESS_LZJB */ +}; + +/* From disk_io.c */ +/* ZFS root filesystem for booting */ +#define current_bootpath zfs_ba->zfs_current_bootpath +#define current_rootpool zfs_ba->zfs_current_rootpool +#define current_bootfs zfs_ba->zfs_current_bootfs +#define current_bootfs_obj zfs_ba->zfs_current_bootfs_obj +#define is_zfs_mount (*fsig_int1(ffi)) +/* End from disk_io.c */ + +#define is_zfs_open zfs_ba->zfs_open + +/* + * Our own version of bcmp(). + */ +static int +zfs_bcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *ps1 = s1; + const unsigned char *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"}, + {{fletcher_2_native, fletcher_2_byteswap}, 0, 1, "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"} +}; + +/* + * 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_IS_GANG(bp) ? ZIO_CHECKSUM_GANG_HEADER : + BP_GET_CHECKSUM(bp); + int byteswap = BP_SHOULD_BYTESWAP(bp); + zio_block_tail_t *zbt = (zio_block_tail_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_zbt) { + if (checksum == ZIO_CHECKSUM_GANG_HEADER) { + /* + * 'gang blocks' is not supported. + */ + return (-1); + } + + if (zbt->zbt_magic == BSWAP_64(ZBT_MAGIC)) { + /* byte swapping is not supported */ + return (-1); + } else { + expected_cksum = zbt->zbt_cksum; + zbt->zbt_cksum = zc; + ci->ci_func[0](data, size, &actual_cksum); + zbt->zbt_cksum = expected_cksum; + } + zc = expected_cksum; + + } else { + if (BP_IS_GANG(bp)) + return (-1); + 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_offset takes "offset" (the offset within a vdev_label) and + * returns its physical disk offset (starting from the beginning of the vdev). + * + * Input: + * psize : Physical size of this vdev + * l : Label Number (0-3) + * offset : The offset with a vdev_label in which we want the physical + * address + * Return: + * Success : physical disk offset + * Failure : errnum = ERR_BAD_ARGUMENT, return value is meaningless + */ +static uint64_t +vdev_label_offset(fsi_file_t *ffi, uint64_t psize, int l, uint64_t offset) +{ + /* XXX Need to add back label support! */ + if (l >= VDEV_LABELS/2 || offset > sizeof (vdev_label_t)) { + errnum = ERR_BAD_ARGUMENT; + return (0); + } + + return (offset + 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, int 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 >= SPA_VERSION_1 && + 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(fsi_file_t *ffi, uberblock_phys_t *ub_array, int label) +{ + uberblock_phys_t *ubbest = NULL; + int i, offset; + + for (i = 0; i < (VDEV_UBERBLOCK_RING >> VDEV_UBERBLOCK_SHIFT); i++) { + offset = vdev_label_offset(ffi, 0, label, + VDEV_UBERBLOCK_OFFSET(i)); + if (errnum == ERR_BAD_ARGUMENT) + return (NULL); + 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 in a block and put its uncompressed data in buf. + * + * Return: + * 0 - success + * errnum - failure + */ +static int +zio_read(fsi_file_t *ffi, blkptr_t *bp, void *buf, char *stack) +{ + uint64_t offset, sector; + int psize, lsize; + int i, comp, cksum; + + psize = BP_GET_PSIZE(bp); + lsize = BP_GET_LSIZE(bp); + comp = BP_GET_COMPRESS(bp); + cksum = BP_GET_CHECKSUM(bp); + + if ((unsigned int)comp >= ZIO_COMPRESS_FUNCTIONS || + (comp != ZIO_COMPRESS_OFF && + decomp_table[comp].decomp_func == NULL)) + return (ERR_FSYS_CORRUPT); + + /* pick a good dva from the block pointer */ + for (i = 0; i < SPA_DVAS_PER_BP; i++) { + + if (bp->blk_dva[i].dva_word[0] == 0 && + bp->blk_dva[i].dva_word[1] == 0) + continue; + + /* read in a block */ + offset = DVA_GET_OFFSET(&bp->blk_dva[i]); + sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); + + if (comp != ZIO_COMPRESS_OFF) { + + if (devread(ffi, sector, 0, psize, stack) == 0) + continue; + if (zio_checksum_verify(bp, stack, psize) != 0) + continue; + decomp_table[comp].decomp_func(stack, buf, psize, + lsize); + } else { + if (devread(ffi, sector, 0, psize, buf) == 0) + continue; + if (zio_checksum_verify(bp, buf, psize) != 0) + continue; + } + return (0); + } + + return (ERR_FSYS_CORRUPT); +} + +/* + * Get the block from a block id. + * push the block onto the stack. + * + * Return: + * 0 - success + * errnum - failure + */ +static int +dmu_read(fsi_file_t *ffi, 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(ffi, 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 (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(fsi_file_t *ffi, 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 - ZAP_HASHBITS)) - 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(fsi_file_t *ffi, 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) + return (ERR_FSYS_CORRUPT); + + hash = zap_hash(ffi, 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(ffi, 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(ffi, 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(fsi_file_t *ffi, 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; + zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; + + 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(ffi, dn, ZFS_ROOT_OBJ, &objnum, stack))) + return (errnum); + + if ((errnum = dnode_get(ffi, 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(ffi, dn, cname, &objnum, stack))) + return (errnum); + + objnum = ZFS_DIRENT_OBJ(objnum); + if ((errnum = dnode_get(ffi, 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(fsi_file_t *ffi, 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(ffi, 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(ffi, dn, DMU_POOL_PROPS, &objnum, stack)) + return (ERR_FILESYSTEM_NOT_FOUND); + + if ((errnum = dnode_get(ffi, mosmdn, objnum, DMU_OT_POOL_PROPS, dn, + stack))) + return (errnum); + + if (zap_lookup(ffi, 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(fsi_file_t *ffi, 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; + + if (fsname == NULL && obj) { + headobj = *obj; + goto skip; + } + + if ((errnum = dnode_get(ffi, mosmdn, DMU_POOL_DIRECTORY_OBJECT, + DMU_OT_OBJECT_DIRECTORY, mdn, stack))) + return (errnum); + + if ((errnum = zap_lookup(ffi, mdn, DMU_POOL_ROOT_DATASET, &objnum, + stack))) + return (errnum); + + if ((errnum = dnode_get(ffi, 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; + + childobj = + ((dsl_dir_phys_t *)DN_BONUS(mdn))->dd_child_dir_zapobj; + if ((errnum = dnode_get(ffi, mosmdn, childobj, + DMU_OT_DSL_DIR_CHILD_MAP, mdn, stack))) + return (errnum); + + if (zap_lookup(ffi, mdn, cname, &objnum, stack)) + return (ERR_FILESYSTEM_NOT_FOUND); + + if ((errnum = dnode_get(ffi, mosmdn, objnum, DMU_OT_DSL_DIR, + mdn, stack))) + return (errnum); + + *fsname = ch; + } + headobj = ((dsl_dir_phys_t *)DN_BONUS(mdn))->dd_head_dataset_obj; + if (obj) + *obj = headobj; + +skip: + if ((errnum = dnode_get(ffi, mosmdn, headobj, DMU_OT_DSL_DATASET, mdn, + stack))) + return (errnum); + + /* TODO: Add snapshot support here - for fsname=snapshot-name */ + + bp = &((dsl_dataset_phys_t *)DN_BONUS(mdn))->ds_bp; + osp = (objset_phys_t *)stack; + stack += sizeof (objset_phys_t); + if ((errnum = zio_read(ffi, 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 (((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_DEGRADED, &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 list of valid vdev pathname from the boot device. + * The caller should already allocate MAXNAMELEN memory for bootpath. + */ +static int +vdev_get_bootpath(char *nv, char *bootpath) +{ + char type[16]; + + bootpath[0] = '\0'; + if (nvlist_lookup_value(nv, ZPOOL_CONFIG_TYPE, &type, DATA_TYPE_STRING, + NULL)) + return (ERR_FSYS_CORRUPT); + + if (strcmp(type, VDEV_TYPE_DISK) == 0) { + if (vdev_validate(nv) != 0 || + nvlist_lookup_value(nv, ZPOOL_CONFIG_PHYS_PATH, bootpath, + DATA_TYPE_STRING, NULL) != 0) + return (ERR_NO_BOOTPATH); + + } else if (strcmp(type, VDEV_TYPE_MIRROR) == 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 tmp_path[MAXNAMELEN]; + char *child_i; + + child_i = nvlist_array(child, i); + if (vdev_validate(child_i) != 0) + continue; + + if (nvlist_lookup_value(child_i, ZPOOL_CONFIG_PHYS_PATH, + tmp_path, DATA_TYPE_STRING, NULL) != 0) + return (ERR_NO_BOOTPATH); + + if ((strlen(bootpath) + strlen(tmp_path)) > MAXNAMELEN) + return (ERR_WONT_FIT); + + if (strlen(bootpath) == 0) + sprintf(bootpath, "%s", tmp_path); + else + sprintf(bootpath, "%s %s", bootpath, tmp_path); + } + } + + return (strlen(bootpath) > 0 ? 0 : ERR_NO_BOOTPATH); +} + +/* + * Check the disk label information and retrieve needed vdev name-value pairs. + * + * Return: + * 0 - success + * ERR_* - failure + */ +static int +check_pool_label(fsi_file_t *ffi, int label, char *stack) +{ + vdev_phys_t *vdev; + uint64_t sector, pool_state, txg = 0; + char *nvlist, *nv; + zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; + + sector = (label * sizeof (vdev_label_t) + VDEV_SKIP_SIZE + + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT; + + /* Read in the vdev name-value pair list (112K). */ + if (devread(ffi, sector, 0, VDEV_PHYS_SIZE, stack) == 0) + return (ERR_READ); + + vdev = (vdev_phys_t *)stack; + + 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_VDEV_TREE, &nv, + DATA_TYPE_NVLIST, NULL)) + return (ERR_FSYS_CORRUPT); + + if (vdev_get_bootpath(nv, current_bootpath)) + return (ERR_NO_BOOTPATH); + + 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 + */ +static int +zfs_mount(fsi_file_t *ffi, const char *options) +{ + char *stack; + int label = 0; + uberblock_phys_t *ub_array, *ubbest = NULL; + objset_phys_t *osp; + zfs_bootarea_t *zfs_ba; + + /* if zfs is already mounted, don't do it again */ + if (is_zfs_mount == 1) + return (1); + + /* get much bigger data block for zfs */ + if (((zfs_ba = malloc(sizeof (zfs_bootarea_t))) == NULL)) { + return (1); + } + bzero(zfs_ba, sizeof (zfs_bootarea_t)); + + /* replace small data area in fsi with big one */ + free(ffi->ff_fsi->f_data); + ffi->ff_fsi->f_data = (void *)zfs_ba; + + /* If an boot filesystem is passed in, set it to current_bootfs */ + if (options != NULL) { + if (strlen(options) < MAXNAMELEN) { + strcpy(current_bootfs, options); + } + } + + 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); + + /* XXX add back labels support? */ + for (label = 0; ubbest == NULL && label < (VDEV_LABELS/2); label++) { + uint64_t sector = (label * sizeof (vdev_label_t) + + VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE + + VDEV_PHYS_SIZE) >> SPA_MINBLOCKSHIFT; + + + /* Read in the uberblock ring (128K). */ + if (devread(ffi, sector, 0, VDEV_UBERBLOCK_RING, + (char *)ub_array) == 0) + continue; + + if ((ubbest = find_bestub(ffi, ub_array, label)) != NULL && + zio_read(ffi, &ubbest->ubp_uberblock.ub_rootbp, osp, stack) + == 0) { + + VERIFY_OS_TYPE(osp, DMU_OST_META); + + /* Got the MOS. Save it at the memory addr MOS. */ + grub_memmove(MOS, &osp->os_meta_dnode, DNODE_SIZE); + + if (check_pool_label(ffi, label, stack)) + return (0); + + /* + * Copy fsi->f_data to ffi->ff_data since + * fsig_mount copies from ff_data to f_data + * overwriting fsi->f_data. + */ + bcopy(zfs_ba, fsig_file_buf(ffi), FSYS_BUFLEN); + + is_zfs_mount = 1; + return (1); + } + } + + 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 + */ +static int +zfs_open(fsi_file_t *ffi, char *filename) +{ + char *stack; + dnode_phys_t *mdn; + char *bootstring; + zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; + + 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<ff_fsi, + alloc_size); + if (bootstring != NULL) { + strcpy(bootstring, zfs_bootstr); + strcat(bootstring, current_rootpool); + strcat(bootstring, "/"); + strcat(bootstring, temp); + strcat(bootstring, zfs_bootpath); + strcat(bootstring, current_bootpath); + strcat(bootstring, "'"); + is_zfs_open = 1; + } + } + } + + if (dnode_get_path(ffi, mdn, filename, DNODE, stack)) { + errnum = ERR_FILE_NOT_FOUND; + return (0); + } + + /* get the file size and set the file position to 0 */ + 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 + */ +static int +zfs_read(fsi_file_t *ffi, char *buf, int len) +{ + char *stack; + int blksz, length, movesize; + zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; + + 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(ffi, 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); +} + +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 = zfs_mount, + .fpo_dir = zfs_open, + .fpo_read = zfs_read + }; + + *name = "zfs"; + return (fsig_init(fp, &ops)); +} diff --git a/tools/libfsimage/zfs/fsys_zfs.h b/tools/libfsimage/zfs/fsys_zfs.h new file mode 100644 index 0000000..1ea56f5 --- /dev/null +++ b/tools/libfsimage/zfs/fsys_zfs.h @@ -0,0 +1,203 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#ifndef _FSYS_ZFS_H +#define _FSYS_ZFS_H + +#include +#include + +#include "zfs-include/zfs.h" +#include "zfs-include/dmu.h" +#include "zfs-include/spa.h" +#include "zfs-include/zio.h" +#include "zfs-include/zio_checksum.h" +#include "zfs-include/vdev_impl.h" +#include "zfs-include/zap_impl.h" +#include "zfs-include/zap_leaf.h" +#include "zfs-include/uberblock_impl.h" +#include "zfs-include/dnode.h" +#include "zfs-include/dsl_dir.h" +#include "zfs-include/zfs_acl.h" +#include "zfs-include/zfs_znode.h" +#include "zfs-include/dsl_dataset.h" +#include "zfs-include/zil.h" +#include "zfs-include/dmu_objset.h" + +/* + * Global Memory addresses to store MOS and DNODE data + */ +#define MOS ((dnode_phys_t *)(((zfs_bootarea_t *) \ + (ffi->ff_fsi->f_data))->zfs_data)) +#define DNODE (MOS+1) /* move sizeof(dnode_phys_t) bytes */ +#define ZFS_SCRATCH ((char *)(DNODE+1)) + +#define MAXNAMELEN 256 + +typedef struct zfs_bootarea { + char zfs_current_bootpath[MAXNAMELEN]; + char zfs_current_rootpool[MAXNAMELEN]; + char zfs_current_bootfs[MAXNAMELEN]; + uint64_t zfs_current_bootfs_obj; + int zfs_open; + + /* cache for a file block of the currently zfs_open()-ed file */ + void *zfs_file_buf; + uint64_t zfs_file_start; + uint64_t zfs_file_end; + + /* cache for a dnode block */ + dnode_phys_t *zfs_dnode_buf; + dnode_phys_t *zfs_dnode_mdn; + uint64_t zfs_dnode_start; + uint64_t zfs_dnode_end; + + char *zfs_stackbase; + char zfs_data[0x400000]; +} zfs_bootarea_t; + +/* + * 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) +#undef offsetof +#define offsetof(t, m) (size_t)(&(((t *)0)->m)) +#define VDEV_UBERBLOCK_SHIFT UBERBLOCK_SHIFT +#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_block_tail_t)]; + zio_block_tail_t ubp_zbt; +} 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) + +/* + * 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_H */ diff --git a/tools/libfsimage/zfs/mb_info.h b/tools/libfsimage/zfs/mb_info.h new file mode 100644 index 0000000..1e1e63b --- /dev/null +++ b/tools/libfsimage/zfs/mb_info.h @@ -0,0 +1,217 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * 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 diff --git a/tools/libfsimage/zfs/zfs-include/dmu.h b/tools/libfsimage/zfs/zfs-include/dmu.h new file mode 100644 index 0000000..ba51e63 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/dmu.h @@ -0,0 +1,105 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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_ACL, /* 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_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 */ diff --git a/tools/libfsimage/zfs/zfs-include/dmu_objset.h b/tools/libfsimage/zfs/zfs-include/dmu_objset.h new file mode 100644 index 0000000..9ddc481 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/dmu_objset.h @@ -0,0 +1,35 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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; + char os_pad[1024 - sizeof (dnode_phys_t) - sizeof (zil_header_t) - + sizeof (uint64_t)]; +} objset_phys_t; + +#endif /* _SYS_DMU_OBJSET_H */ diff --git a/tools/libfsimage/zfs/zfs-include/dnode.h b/tools/libfsimage/zfs/zfs-include/dnode.h new file mode 100644 index 0000000..e61e2ae --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/dnode.h @@ -0,0 +1,76 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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 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]; +} dnode_phys_t; + +#endif /* _SYS_DNODE_H */ diff --git a/tools/libfsimage/zfs/zfs-include/dsl_dataset.h b/tools/libfsimage/zfs/zfs-include/dsl_dataset.h new file mode 100644 index 0000000..ab0ee22 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/dsl_dataset.h @@ -0,0 +1,53 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DSL_DATASET_H +#define _SYS_DSL_DATASET_H + +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 */ diff --git a/tools/libfsimage/zfs/zfs-include/dsl_dir.h b/tools/libfsimage/zfs/zfs-include/dsl_dir.h new file mode 100644 index 0000000..54d6663 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/dsl_dir.h @@ -0,0 +1,49 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DSL_DIR_H +#define _SYS_DSL_DIR_H + +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 */ diff --git a/tools/libfsimage/zfs/zfs-include/spa.h b/tools/libfsimage/zfs/zfs-include/spa.h new file mode 100644 index 0000000..f6f2f15 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/spa.h @@ -0,0 +1,283 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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) + +/* + * 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 |E| lvl | type | cksum | comp | PSIZE | LSIZE | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * 7 | padding | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * 8 | padding | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * 9 | padding | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * a | 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 + * E endianness + * type DMU object type + * lvl level of indirection + * birth txg transaction group in which the block was born + * fill count number of non-zero blocks under this bp + * checksum[4] 256-bit checksum of the data this bp describes + */ +typedef struct blkptr { + dva_t blk_dva[3]; /* 128-bit Data Virtual Address */ + uint64_t blk_prop; /* size, compression, type, etc */ + uint64_t blk_pad[3]; /* Extra space for the future */ + uint64_t blk_birth; /* transaction group at birth */ + uint64_t blk_fill; /* fill count */ + zio_cksum_t blk_cksum; /* 256-bit checksum */ +} blkptr_t; + +#define SPA_BLKPTRSHIFT 7 /* blkptr_t is 128 bytes */ +#define SPA_DVAS_PER_BP 3 /* Number of DVAs in a bp */ + +/* + * 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) \ + (BP_IS_HOLE(bp) ? 0 : \ + 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_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_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 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) +#define BP_IS_OLDER(bp, txg) (!BP_IS_HOLE(bp) && (bp)->blk_birth < (txg)) + +#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_pad[2] = 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 */ diff --git a/tools/libfsimage/zfs/zfs-include/uberblock_impl.h b/tools/libfsimage/zfs/zfs-include/uberblock_impl.h new file mode 100644 index 0000000..faab545 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/uberblock_impl.h @@ -0,0 +1,49 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_UBERBLOCK_IMPL_H +#define _SYS_UBERBLOCK_IMPL_H + +/* + * 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 */ diff --git a/tools/libfsimage/zfs/zfs-include/vdev_impl.h b/tools/libfsimage/zfs/zfs-include/vdev_impl.h new file mode 100644 index 0000000..4328a1b --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/vdev_impl.h @@ -0,0 +1,70 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_VDEV_IMPL_H +#define _SYS_VDEV_IMPL_H + +#define VDEV_SKIP_SIZE (8 << 10) +#define VDEV_BOOT_HEADER_SIZE (8 << 10) +#define VDEV_PHYS_SIZE (112 << 10) +#define VDEV_UBERBLOCK_RING (128 << 10) + +/* ZFS boot block */ +#define VDEV_BOOT_MAGIC 0x2f5b007b10cULL +#define VDEV_BOOT_VERSION 1 /* version number */ + +typedef struct vdev_boot_header { + uint64_t vb_magic; /* VDEV_BOOT_MAGIC */ + uint64_t vb_version; /* VDEV_BOOT_VERSION */ + uint64_t vb_offset; /* start offset (bytes) */ + uint64_t vb_size; /* size (bytes) */ + char vb_pad[VDEV_BOOT_HEADER_SIZE - 4 * sizeof (uint64_t)]; +} vdev_boot_header_t; + +typedef struct vdev_phys { + char vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_block_tail_t)]; + zio_block_tail_t vp_zbt; +} vdev_phys_t; + +typedef struct vdev_label { + char vl_pad[VDEV_SKIP_SIZE]; /* 8K */ + vdev_boot_header_t vl_boot_header; /* 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 */ diff --git a/tools/libfsimage/zfs/zfs-include/zap_impl.h b/tools/libfsimage/zfs/zfs-include/zap_impl.h new file mode 100644 index 0000000..89bf2fa --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/zap_impl.h @@ -0,0 +1,110 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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 ZAP_HASHBITS 28 +#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<| + * |<-- 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 */ diff --git a/tools/libfsimage/zfs/zfs-include/zil.h b/tools/libfsimage/zfs/zfs-include/zil.h new file mode 100644 index 0000000..5ea719a --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/zil.h @@ -0,0 +1,51 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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_pad[5]; +} zil_header_t; + +#endif /* _SYS_ZIL_H */ diff --git a/tools/libfsimage/zfs/zfs-include/zio.h b/tools/libfsimage/zfs/zfs-include/zio.h new file mode 100644 index 0000000..2f7aff4 --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/zio.h @@ -0,0 +1,81 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ZIO_H +#define _ZIO_H + +#define ZBT_MAGIC 0x210da7ab10c7a11ULL /* zio data bloc tail */ + +typedef struct zio_block_tail { + uint64_t zbt_magic; /* for validation, endianness */ + zio_cksum_t zbt_cksum; /* 256-bit checksum */ +} zio_block_tail_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_block_tail_t)) / sizeof (blkptr_t)) +#define SPA_GBH_FILLER ((SPA_GANGBLOCKSIZE - \ + sizeof (zio_block_tail_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_block_tail_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_FUNCTIONS +}; + +#define ZIO_CHECKSUM_ON_VALUE ZIO_CHECKSUM_FLETCHER_2 +#define ZIO_CHECKSUM_DEFAULT ZIO_CHECKSUM_ON + +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 */ diff --git a/tools/libfsimage/zfs/zfs-include/zio_checksum.h b/tools/libfsimage/zfs/zfs-include/zio_checksum.h new file mode 100644 index 0000000..30a472f --- /dev/null +++ b/tools/libfsimage/zfs/zfs-include/zio_checksum.h @@ -0,0 +1,42 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 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_zbt; /* uses zio block tail? */ + char *ci_name; /* descriptive name */ +} zio_checksum_info_t; + +#endif /* _SYS_ZIO_CHECKSUM_H */ diff --git a/tools/libfsimage/zfs/zfs_fletcher.c b/tools/libfsimage/zfs/zfs_fletcher.c new file mode 100644 index 0000000..a62342a --- /dev/null +++ b/tools/libfsimage/zfs/zfs_fletcher.c @@ -0,0 +1,93 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#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); +} diff --git a/tools/libfsimage/zfs/zfs_lzjb.c b/tools/libfsimage/zfs/zfs_lzjb.c new file mode 100644 index 0000000..fc48a94 --- /dev/null +++ b/tools/libfsimage/zfs/zfs_lzjb.c @@ -0,0 +1,60 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#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) +{ + unsigned char *src = s_start; + unsigned char *dst = d_start; + unsigned char *d_end = (unsigned char *)d_start + d_len; + unsigned char *cpy; + unsigned char copymap = '\0'; + int copymask = 1 << (NBBY - 1); + + while (dst < d_end) { + if ((copymask <<= 1) == (1 << NBBY)) { + copymask = 1; + copymap = *src++; + } + if (copymap & (unsigned char)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) < (unsigned char *)d_start) + return (-1); + while (--mlen >= 0 && dst < d_end) + *dst++ = *cpy++; + } else { + *dst++ = *src++; + } + } + return (0); +} diff --git a/tools/libfsimage/zfs/zfs_sha256.c b/tools/libfsimage/zfs/zfs_sha256.c new file mode 100644 index 0000000..5e73580 --- /dev/null +++ b/tools/libfsimage/zfs/zfs_sha256.c @@ -0,0 +1,124 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#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]); +} diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile new file mode 100644 index 0000000..4ec156c --- /dev/null +++ b/tools/libxc/Makefile @@ -0,0 +1,159 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 3.2 +MINOR = 0 + +CTRL_SRCS-y := +CTRL_SRCS-y += xc_core.c +CTRL_SRCS-$(CONFIG_X86) += xc_core_x86.c +CTRL_SRCS-$(CONFIG_IA64) += xc_core_ia64.c +CTRL_SRCS-y += xc_domain.c +CTRL_SRCS-y += xc_evtchn.c +CTRL_SRCS-y += xc_misc.c +CTRL_SRCS-y += xc_acm.c +CTRL_SRCS-y += xc_flask.c +CTRL_SRCS-y += xc_physdev.c +CTRL_SRCS-y += xc_private.c +CTRL_SRCS-y += xc_sedf.c +CTRL_SRCS-y += xc_csched.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-$(CONFIG_X86) += xc_pagetab.c +CTRL_SRCS-$(CONFIG_Linux) += xc_linux.c +CTRL_SRCS-$(CONFIG_SunOS) += xc_solaris.c +CTRL_SRCS-$(CONFIG_X86_Linux) += xc_ptrace.c xc_ptrace_core.c +CTRL_SRCS-$(CONFIG_NetBSD) += xc_netbsd.c +CTRL_SRCS-$(CONFIG_MiniOS) += xc_minios.c + +GUEST_SRCS-y := +GUEST_SRCS-y += xg_private.c +GUEST_SRCS-$(CONFIG_MIGRATE) += xc_domain_restore.c xc_domain_save.c +GUEST_SRCS-$(CONFIG_HVM) += xc_hvm_build.c + +vpath %.c ../../xen/common/libelf +CFLAGS += -I../../xen/common/libelf + +GUEST_SRCS-y += libelf-tools.c libelf-loader.c +GUEST_SRCS-y += libelf-dominfo.c libelf-relocate.c + +# 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-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_IA64) += xc_dom_ia64.c + +-include $(XEN_TARGET_ARCH)/Makefile + +CFLAGS += -Werror -Wmissing-prototypes +CFLAGS += $(INCLUDES) -I. -I../xenstore -I../include + +# Needed for posix_fadvise64() in xc_linux.c +CFLAGS-$(CONFIG_Linux) += -D_GNU_SOURCE + +# Define this to make it possible to run valgrind on code linked with these +# libraries. +#CFLAGS += -DVALGRIND -O0 -ggdb3 + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +LDFLAGS += -L. +DEPS = .*.d + +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)) + +LIB := libxenctrl.a +ifneq ($(stubdom),y) +LIB += libxenctrl.so libxenctrl.so.$(MAJOR) libxenctrl.so.$(MAJOR).$(MINOR) +endif + +LIB += libxenguest.a +ifneq ($(stubdom),y) +LIB += libxenguest.so libxenguest.so.$(MAJOR) libxenguest.so.$(MAJOR).$(MINOR) +endif + +.PHONY: all +all: build + +.PHONY: build +build: + $(MAKE) libs + +.PHONY: libs +libs: $(LIB) + +.PHONY: install +install: build + $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR) + $(INSTALL_PROG) libxenctrl.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DATA) libxenctrl.a $(DESTDIR)$(LIBDIR) + ln -sf libxenctrl.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libxenctrl.so.$(MAJOR) + ln -sf libxenctrl.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libxenctrl.so + $(INSTALL_DATA) xenctrl.h $(DESTDIR)$(INCLUDEDIR) + + $(INSTALL_PROG) libxenguest.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DATA) libxenguest.a $(DESTDIR)$(LIBDIR) + ln -sf libxenguest.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libxenguest.so.$(MAJOR) + ln -sf libxenguest.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libxenguest.so + $(INSTALL_DATA) xenguest.h $(DESTDIR)$(INCLUDEDIR) + +.PHONY: TAGS +TAGS: + etags -t *.c *.h + +.PHONY: clean +clean: + rm -rf *.rpm $(LIB) *~ $(DEPS) \ + $(CTRL_LIB_OBJS) $(CTRL_PIC_OBJS) \ + $(GUEST_LIB_OBJS) $(GUEST_PIC_OBJS) + +.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) + ln -sf $< $@ +libxenctrl.so.$(MAJOR): libxenctrl.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libxenctrl.so.$(MAJOR).$(MINOR): $(CTRL_PIC_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenctrl.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ $(PTHREAD_LIBS) + +# libxenguest + +libxenguest.a: $(GUEST_LIB_OBJS) + $(AR) rc $@ $^ + +libxenguest.so: libxenguest.so.$(MAJOR) + ln -sf $< $@ +libxenguest.so.$(MAJOR): libxenguest.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libxenguest.so.$(MAJOR).$(MINOR): $(GUEST_PIC_OBJS) libxenctrl.so + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenguest.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $(GUEST_PIC_OBJS) -lz -lxenctrl $(PTHREAD_LIBS) + +-include $(DEPS) + diff --git a/tools/libxc/ia64/Makefile b/tools/libxc/ia64/Makefile new file mode 100644 index 0000000..cc15633 --- /dev/null +++ b/tools/libxc/ia64/Makefile @@ -0,0 +1,56 @@ +ifneq ($(stubdom),y) +CTRL_SRCS-y += ia64/xc_ia64_stubs.c + +GUEST_SRCS-y += ia64/xc_ia64_hvm_build.c +GUEST_SRCS-y += ia64/xc_ia64_linux_save.c +GUEST_SRCS-y += ia64/xc_ia64_linux_restore.c + +GUEST_SRCS-y += ia64/xc_dom_ia64_util.c +GUEST_SRCS-y += ia64/dom_fw_acpi.c + +GUEST_SRCS-y += ia64/xc_ia64_dom_fwloader.c + +DOMFW_SRCS_BASE := dom_fw_common.c dom_fw_domu.c dom_fw_asm.S +endif +DOMFW_SRCS := $(addprefix ia64/, $(DOMFW_SRCS_BASE)) +$(DOMFW_SRCS): + ln -sf ../$(XEN_ROOT)/xen/arch/ia64/xen/$(@F) $@ + +# XXX kludge: libxc/Makefile doesn't understand .S. +GUEST_SRCS-y += $(patsubst %.S, %.c, $(DOMFW_SRCS)) +%.o: %.S + $(CC) $(CFLAGS) -c $< -o $@ +%.opic: %.S + $(CC) $(CPPFLAGS) -DPIC $(CFLAGS) -fPIC -c -o $@ $< + + +CFLAGS += -Iia64 + +DOMFW_ASM_HDRS_BASE := bundle.h dom_fw.h dom_fw_common.h dom_fw_domu.h +DOMFW_ASM_HDRS := $(addprefix ia64/asm/, $(DOMFW_ASM_HDRS_BASE)) +$(DOMFW_ASM_HDRS): ia64/asm + ln -sf ../../$(XEN_ROOT)/xen/include/asm-ia64/$(@F) $@ +build: $(DOMFW_ASM_HDR) + +.PHONY: mk-symlinks-acpi mk-symlinks-misc ia64-clean + +IA64_HDR_DIRS := ia64/asm ia64/xen ia64/acpi ia64/acpi/platform +$(IA64_HDR_DIRS): + mkdir -p $@ + +IA64_EMPTY_FILES := ia64/asm/acpi.h ia64/xen/list.h +$(IA64_EMPTY_FILES): $(IA64_HDR_DIRS) + echo "/* automatically created dummy empty header file. */" > $@ + +mk-symlinks-acpi: $(IA64_HDR_DIRS) $(IA64_EMPTY_FILES) $(DOMFW_ASM_HDRS) + ( cd ia64/acpi && ln -sf ../../$(XEN_ROOT)/xen/include/acpi/*.h .) + ( cd ia64/acpi/platform && ln -sf ../../../$(XEN_ROOT)/xen/include/acpi/platform/*.h .) + ( cd ia64/acpi/platform/ && ln -sf ../../aclinux.h .) + ( cd ia64/xen && ln -sf ../../$(XEN_ROOT)/xen/include/xen/acpi.h .) +mk-symlinks-misc: $(IA64_HDR_DIRS) + ( cd ia64/asm && ln -sf ../../$(XEN_ROOT)/xen/include/asm-ia64/linux-xen/asm/kregs.h .) +build: mk-symlinks-acpi mk-symlinks-misc + +clean: ia64-clean +ia64-clean: + rm -rf $(DOMFW_SRCS) $(DOMFW_ASM_HDRS) $(IA64_EMPTY_FILES) $(IA64_HDR_DIRS) diff --git a/tools/libxc/ia64/aclinux.h b/tools/libxc/ia64/aclinux.h new file mode 100644 index 0000000..4019c72 --- /dev/null +++ b/tools/libxc/ia64/aclinux.h @@ -0,0 +1,111 @@ +/****************************************************************************** + * + * Name: aclinux.h - OS specific defines, etc. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * All rights reserved. + * + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * 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 MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACLINUX_H__ +#define __ACLINUX_H__ + +#define ACPI_USE_SYSTEM_CLIBRARY +#define ACPI_USE_DO_WHILE_0 + +#if 0 /* def __KERNEL__ */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define strtoul simple_strtoul + +#define ACPI_MACHINE_WIDTH BITS_PER_LONG + +#else /* !__KERNEL__ */ + +#include +#include +//#include +#include +#include + +#if defined(__ia64__) || defined(__x86_64__) +#define ACPI_MACHINE_WIDTH 64 +#define COMPILER_DEPENDENT_INT64 long +#define COMPILER_DEPENDENT_UINT64 unsigned long +#else +#define ACPI_MACHINE_WIDTH 32 +#define COMPILER_DEPENDENT_INT64 long long +#define COMPILER_DEPENDENT_UINT64 unsigned long long +#define ACPI_USE_NATIVE_DIVIDE +#endif + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; +#define __iomem +#ifdef __ia64__ +#ifdef __cplusplus +#define CPP_ASMLINKAGE extern "C" +#else +#define CPP_ASMLINKAGE +#endif +#define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage)) +#endif +#define CONFIG_ACPI_BOOT 1 + +#define __cdecl +#define ACPI_FLUSH_CPU_CACHE() +#endif /* __KERNEL__ */ + +/* Linux uses GCC */ + +#include "acgcc.h" + +#endif /* __ACLINUX_H__ */ diff --git a/tools/libxc/ia64/dom_fw_acpi.c b/tools/libxc/ia64/dom_fw_acpi.c new file mode 100644 index 0000000..493a5ad --- /dev/null +++ b/tools/libxc/ia64/dom_fw_acpi.c @@ -0,0 +1,31 @@ +#include +#include "xc_dom_ia64_util.h" +#include +#include + +/* stolen from xen/drivers/acpi/tables/tbutils.c */ + +/******************************************************************************* + * + * FUNCTION: acpi_tb_checksum + * + * PARAMETERS: Buffer - Pointer to memory region to be checked + * Length - Length of this memory region + * + * RETURN: Checksum (u8) + * + * DESCRIPTION: Calculates circular checksum of memory region. + * + ******************************************************************************/ + +u8 acpi_tb_checksum(u8 * buffer, acpi_native_uint length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) { + sum = (u8) (sum + *(buffer++)); + } + + return sum; +} diff --git a/tools/libxc/ia64/sal.h b/tools/libxc/ia64/sal.h new file mode 100644 index 0000000..741ef6f --- /dev/null +++ b/tools/libxc/ia64/sal.h @@ -0,0 +1,71 @@ +#ifndef XC_IA64_SAL_H +#define XC_IA64_SAL_H + +/* + * definitions from + * xen/include/asm-ia64/linux/asm/sal.h + */ + +/* + * The SAL system table is followed by a variable number of variable + * length descriptors. The structure of these descriptors follows + * below. + * The defininition follows SAL specs from July 2000 + */ +struct ia64_sal_systab { + uint8_t signature[4]; /* should be "SST_" */ + uint32_t size; /* size of this table in bytes */ + uint8_t sal_rev_minor; + uint8_t sal_rev_major; + uint16_t entry_count; /* # of entries in variable portion */ + uint8_t checksum; + uint8_t reserved1[7]; + uint8_t sal_a_rev_minor; + uint8_t sal_a_rev_major; + uint8_t sal_b_rev_minor; + uint8_t sal_b_rev_major; + /* oem_id & product_id: terminating NUL is missing if string is exactly 32 bytes long. */ + uint8_t oem_id[32]; + uint8_t product_id[32]; /* ASCII product id */ + uint8_t reserved2[8]; +}; + +enum sal_systab_entry_type { + SAL_DESC_ENTRY_POINT = 0, + SAL_DESC_MEMORY = 1, + SAL_DESC_PLATFORM_FEATURE = 2, + SAL_DESC_TR = 3, + SAL_DESC_PTC = 4, + SAL_DESC_AP_WAKEUP = 5 +}; + +typedef struct ia64_sal_desc_entry_point { + uint8_t type; + uint8_t reserved1[7]; + uint64_t pal_proc; + uint64_t sal_proc; + uint64_t gp; + uint8_t reserved2[16]; +}ia64_sal_desc_entry_point_t; + +#define IA64_SAL_AP_EXTERNAL_INT 0 + +typedef struct ia64_sal_desc_ap_wakeup { + uint8_t type; + uint8_t mechanism; /* 0 == external interrupt */ + uint8_t reserved1[6]; + uint64_t vector; /* interrupt vector in range 0x10-0xff */ +} ia64_sal_desc_ap_wakeup_t ; + +//XXX should move xen_sal_data to arch-ia64.h? +/* These are data in domain memory for SAL emulator. */ +struct xen_sal_data { + /* OS boot rendez vous. */ + unsigned long boot_rdv_ip; + unsigned long boot_rdv_r1; + + /* There are these for EFI_SET_VIRTUAL_ADDRESS_MAP emulation. */ + int efi_virt_mode; /* phys : 0 , virt : 1 */ +}; + +#endif /* XC_IA64_SAL_H */ diff --git a/tools/libxc/ia64/xc_dom_ia64_util.c b/tools/libxc/ia64/xc_dom_ia64_util.c new file mode 100644 index 0000000..37a4120 --- /dev/null +++ b/tools/libxc/ia64/xc_dom_ia64_util.c @@ -0,0 +1,192 @@ +/* + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#include +#include "xg_private.h" +#include "xc_dom.h" +#include "asm/dom_fw.h" +#include "asm/dom_fw_common.h" +#include "ia64/xc_dom_ia64_util.h" + +uint32_t +xen_ia64_version(struct xc_dom_image *dom) +{ + return xc_version(dom->guest_xc, XENVER_version, NULL); +} + +int +xen_ia64_fpswa_revision(struct xc_dom_image *dom, unsigned int *revision) +{ + int ret; + DECLARE_HYPERCALL; + hypercall.op = __HYPERVISOR_ia64_dom0vp_op; + hypercall.arg[0] = IA64_DOM0VP_fpswa_revision; + hypercall.arg[1] = (unsigned long)revision; + + if (lock_pages(revision, sizeof(*revision)) != 0) { + PERROR("Could not lock memory for xen fpswa hypercall"); + return -1; + } + + ret = do_xen_hypercall(dom->guest_xc, &hypercall); + + unlock_pages(revision, sizeof(*revision)); + + return ret; +} + +int xen_ia64_is_running_on_sim(struct xc_dom_image *dom) +{ + /* + * This is only used by dom_fw_init() as + * "!xen_ia64_is_dom0() || xen_ia64_is_running_on_sim()". + * So this doesn't affect the result. + */ + return 0; +} + +int +xen_ia64_is_dom0(struct xc_dom_image *unused) +{ + /* libxc is for non-dom0 domain builder */ + return 0; +} + +void* +xen_ia64_dom_fw_map(struct xc_dom_image *dom, unsigned long mpaddr) +{ + unsigned long page_size = XC_DOM_PAGE_SIZE(dom); + void* ret; + + ret = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, + page_size, PROT_READ | PROT_WRITE, + mpaddr / page_size); + if (ret != NULL) + ret = (void*)((unsigned long)ret | (mpaddr & (page_size - 1))); + + return ret; +} + +void +xen_ia64_dom_fw_unmap(struct xc_dom_image *dom, void *vaddr) +{ + unsigned long page_size = XC_DOM_PAGE_SIZE(dom); + munmap((void*)((unsigned long)vaddr & ~(page_size - 1)), page_size); +} + +int +xen_ia64_is_vcpu_allocated(struct xc_dom_image *dom, uint32_t vcpu) +{ + // return d->vcpu[vcpu] != NULL; + + int rc; + xc_vcpuinfo_t info; + + rc = xc_vcpu_getinfo(dom->guest_xc, dom->guest_domid, + vcpu, &info); + if (rc == 0) + return 1; + + if (errno != ESRCH) + PERROR("Could not get vcpu info"); + return 0; +} + +int +xen_ia64_dom_fw_setup(struct xc_dom_image *d, uint64_t brkimm, + unsigned long bp_mpa, unsigned long maxmem) +{ + int rc = 0; + void *imva_hypercall_base = NULL; + struct fw_tables *fw_tables = NULL; + struct fake_acpi_tables *imva = NULL; + struct xen_ia64_boot_param *bp = NULL; + + BUILD_BUG_ON(sizeof(struct fw_tables) > + (FW_TABLES_END_PADDR_MIN - FW_TABLES_BASE_PADDR)); + + /* Create page for hypercalls. */ + imva_hypercall_base = xen_ia64_dom_fw_map(d, FW_HYPERCALL_BASE_PADDR); + if (imva_hypercall_base == NULL) { + rc = -errno; + goto out; + } + + /* Create page for FW tables. */ + fw_tables = (struct fw_tables*)xen_ia64_dom_fw_map(d, FW_TABLES_BASE_PADDR); + if (fw_tables == NULL) { + rc = -errno; + goto out; + } + memset(fw_tables, 0, FW_TABLES_END_PADDR_MIN - FW_TABLES_BASE_PADDR); + BUILD_BUG_ON(FW_END_PADDR_MIN != FW_TABLES_END_PADDR_MIN); + fw_tables->fw_tables_size = FW_TABLES_END_PADDR_MIN - FW_TABLES_BASE_PADDR; + fw_tables->fw_end_paddr = FW_END_PADDR_MIN; + fw_tables->fw_tables_end_paddr = FW_TABLES_END_PADDR_MIN; + fw_tables->num_mds = 0; + + /* Create page for acpi tables. */ + imva = (struct fake_acpi_tables *) + xen_ia64_dom_fw_map(d, FW_ACPI_BASE_PADDR); + if (imva == NULL) { + rc = -errno; + goto out; + } + dom_fw_fake_acpi(d, imva); + + /* Create page for boot_param. */ + bp = xen_ia64_dom_fw_map(d, bp_mpa); + if (bp == NULL) { + rc = -errno; + goto out; + } + rc = dom_fw_init(d, brkimm, bp, fw_tables, + (unsigned long)imva_hypercall_base, maxmem); + BUG_ON(fw_tables->fw_tables_size < sizeof(*fw_tables) + + sizeof(fw_tables->efi_memmap[0]) * fw_tables->num_mds); + + /* clear domain builder internal use member */ + fw_tables->fw_tables_size = 0; + fw_tables->fw_end_paddr = 0; + fw_tables->fw_tables_end_paddr = 0; + fw_tables->num_mds = 0; + + out: + if (imva_hypercall_base != NULL) + xen_ia64_dom_fw_unmap(d, imva_hypercall_base); + if (fw_tables != NULL) + xen_ia64_dom_fw_unmap(d, fw_tables); + if (imva != NULL) + xen_ia64_dom_fw_unmap(d, imva); + if (bp != NULL) + xen_ia64_dom_fw_unmap(d, bp); + + return rc; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_dom_ia64_util.h b/tools/libxc/ia64/xc_dom_ia64_util.h new file mode 100644 index 0000000..f103d19 --- /dev/null +++ b/tools/libxc/ia64/xc_dom_ia64_util.h @@ -0,0 +1,30 @@ +#ifndef XC_IA64_DOM_IA64_UTIL_H +#define XC_IA64_DOM_IA64_UTIL_H + +struct xc_dom_image; +uint32_t xen_ia64_version(struct xc_dom_image *dom); +void* xen_ia64_dom_fw_map(struct xc_dom_image *dom, unsigned long mpaddr); +void xen_ia64_dom_fw_unmap(struct xc_dom_image *dom, void *addr); +int xen_ia64_fpswa_revision(struct xc_dom_image *dom, unsigned int *revision); +int xen_ia64_is_vcpu_allocated(struct xc_dom_image *dom, uint32_t vcpu); +int xen_ia64_is_running_on_sim(struct xc_dom_image *dom); +int xen_ia64_is_dom0(struct xc_dom_image *dom); + +int +xen_ia64_dom_fw_setup(struct xc_dom_image *d, uint64_t brkimm, + unsigned long bp_mpa, unsigned long maxmem); +#define efi_systable_init_dom0(tables) assert(0) +#define complete_dom0_memmap(d, tables) ({assert(0);0;}) + +/* Defined in xc_dom_ia64.c */ +extern int start_info_ia64(struct xc_dom_image *dom); +extern int shared_info_ia64(struct xc_dom_image *dom, void *ptr); + +#define FW_MEM_BASE 0xff000000UL +#define FW_MEM_SIZE 0x01000000UL + +#ifdef __XEN_TOOLS__ +/* Necessary for including the acpi header chain when not in kernel context */ +typedef struct { } spinlock_t; +#endif +#endif /* XC_IA64_DOM_IA64_UTIL_H */ diff --git a/tools/libxc/ia64/xc_ia64.h b/tools/libxc/ia64/xc_ia64.h new file mode 100644 index 0000000..3c1d27e --- /dev/null +++ b/tools/libxc/ia64/xc_ia64.h @@ -0,0 +1,58 @@ +/* + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef _XC_IA64_H_ +#define _XC_IA64_H_ + +int xc_ia64_copy_memmap(int xc_handle, uint32_t domid, + shared_info_t *live_shinfo, + xen_ia64_memmap_info_t **memmap_info_p, + unsigned long *memmap_info_num_pages_p); + +struct xen_ia64_p2m_table { + unsigned long size; + unsigned long *p2m; +}; + +void xc_ia64_p2m_init(struct xen_ia64_p2m_table *p2m_table); +int xc_ia64_p2m_map(struct xen_ia64_p2m_table *p2m_table, int xc_handle, + uint32_t domid, struct xen_ia64_memmap_info *memmap_info, + unsigned long flag); +void xc_ia64_p2m_unmap(struct xen_ia64_p2m_table *p2m_table); +int xc_ia64_p2m_present(struct xen_ia64_p2m_table *p2m_table, + unsigned long gpfn); +int xc_ia64_p2m_allocated(struct xen_ia64_p2m_table *p2m_table, + unsigned long gpfn); + +unsigned long xc_ia64_p2m_mfn(struct xen_ia64_p2m_table *p2m_table, + unsigned long gpfn); + + +#endif /* _XC_IA64_H_ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_ia64_dom_fwloader.c b/tools/libxc/ia64/xc_ia64_dom_fwloader.c new file mode 100644 index 0000000..e7e1525 --- /dev/null +++ b/tools/libxc/ia64/xc_ia64_dom_fwloader.c @@ -0,0 +1,126 @@ +#include +#include +#include + +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" + +#include "ia64/xc_dom_ia64_util.h" + +static const char fw_magic[16] = {'X', 'e', 'n', '-', + 'i', 'a', '6', '4', + '-', 'f', 'w', 0, + 0, 0, 0, 0}; +#define FW_LOAD 0xff800000UL +#define FW_SIZE (8 * 1024 * 1024) + +static int xc_dom_probe_fw_kernel(struct xc_dom_image *dom) +{ + if (dom->kernel_size != FW_SIZE) + return -EINVAL; + if (memcmp (dom->kernel_blob, fw_magic, sizeof (fw_magic))) + return -EINVAL; + return 0; +} + +static int xc_dom_parse_fw_kernel(struct xc_dom_image *dom) +{ + dom->kernel_seg.vstart = FW_LOAD; + dom->kernel_seg.vend = FW_LOAD + FW_SIZE; + dom->parms.virt_base = FW_MEM_BASE; + dom->parms.virt_entry = FW_LOAD + sizeof (fw_magic); + dom->ramdisk_blob = NULL; /* No ramdisk yet. */ + dom->guest_type = "hvm-3.0-ia64-sioemu"; + return 0; +} + +static int xc_dom_load_fw_kernel(struct xc_dom_image *dom) +{ + char *dest; + unsigned long i; + + dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart); + memcpy(dest, dom->kernel_blob, FW_SIZE); + + /* Synchronize cache. */ + for (i = 0; i < FW_SIZE; i += 32) + asm volatile ("fc.i %0" :: "r"(dest + i) : "memory"); + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int alloc_magic_pages(struct xc_dom_image *dom) +{ + /* allocate special pages */ + /* Note: do not use 0 for console or xenstore otherwise clear_page won't + clear the page. */ + dom->start_info_pfn = 0; + dom->console_pfn = 1; + dom->xenstore_pfn = 2; + return 0; +} + +extern unsigned long xc_ia64_fpsr_default(void); + +static int vcpu_ia64(struct xc_dom_image *dom, void *ptr) +{ + vcpu_guest_context_ia64_t *ctxt = ptr; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* clear everything */ + memset(ctxt, 0, sizeof(*ctxt)); + + ctxt->flags = 0; + ctxt->regs.ip = dom->parms.virt_entry; +#ifdef __ia64__ /* FIXME */ + ctxt->regs.ar.fpsr = xc_ia64_fpsr_default(); +#endif + ctxt->regs.cr.isr = 1UL << 63; + ctxt->regs.psr = IA64_PSR_AC | IA64_PSR_BN; + ctxt->regs.cr.dcr = 0; + ctxt->regs.cr.pta = 15 << 2; + + return 0; +} + +static struct xc_dom_arch xc_dom_arch_ia64_fw = { + .guest_type = "hvm-3.0-ia64-sioemu", + .native_protocol = XEN_IO_PROTO_ABI_IA64, + .page_shift = PAGE_SHIFT_IA64, + .alloc_magic_pages = alloc_magic_pages, + .start_info = start_info_ia64, + .shared_info = shared_info_ia64, + .vcpu = vcpu_ia64, +}; + +/* ------------------------------------------------------------------------ */ + +static struct xc_dom_loader fw_loader = { + .name = "xen-ia64-fw", + .probe = xc_dom_probe_fw_kernel, + .parser = xc_dom_parse_fw_kernel, + .loader = xc_dom_load_fw_kernel, +}; + +static void __init register_fwloader(void) +{ + xc_dom_register_arch_hooks(&xc_dom_arch_ia64_fw); + xc_dom_register_loader(&fw_loader); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_ia64_hvm_build.c b/tools/libxc/ia64/xc_ia64_hvm_build.c new file mode 100644 index 0000000..89f1143 --- /dev/null +++ b/tools/libxc/ia64/xc_ia64_hvm_build.c @@ -0,0 +1,1182 @@ +#include +#include "xg_private.h" +#include "xenguest.h" +#include "xc_private.h" +#include "xc_elf.h" +#include "xc_efi.h" +#include +#include +#include +#include +#include "xen/arch-ia64.h" +#include +#include + +static int +xc_ia64_copy_to_domain_pages(int xc_handle, uint32_t domid, void* src_page, + unsigned long dst_pfn, int nr_pages) +{ + // N.B. gva should be page aligned + int i; + + for (i = 0; i < nr_pages; i++) { + if (xc_copy_to_domain_page(xc_handle, domid, dst_pfn + i, + src_page + (i << PAGE_SHIFT))) + return -1; + } + + return 0; +} + +#define HOB_SIGNATURE 0x3436474953424f48 // "HOBSIG64" +#define GFW_HOB_START ((4UL<<30)-(14UL<<20)) // 4G - 14M +#define GFW_HOB_SIZE (1UL<<20) // 1M + +typedef struct { + unsigned long signature; + unsigned int type; + unsigned int length; +} HOB_GENERIC_HEADER; + +/* + * INFO HOB is the first data data in one HOB list + * it contains the control information of the HOB list + */ +typedef struct { + HOB_GENERIC_HEADER header; + unsigned long length; // current length of hob + unsigned long cur_pos; // current poisiton of hob + unsigned long buf_size; // size of hob buffer +} HOB_INFO; + +typedef struct{ + unsigned long start; + unsigned long size; +} hob_mem_t; + +typedef enum { + HOB_TYPE_INFO=0, + HOB_TYPE_TERMINAL, + HOB_TYPE_MEM, + HOB_TYPE_PAL_BUS_GET_FEATURES_DATA, + HOB_TYPE_PAL_CACHE_SUMMARY, + HOB_TYPE_PAL_MEM_ATTRIB, + HOB_TYPE_PAL_CACHE_INFO, + HOB_TYPE_PAL_CACHE_PROT_INFO, + HOB_TYPE_PAL_DEBUG_INFO, + HOB_TYPE_PAL_FIXED_ADDR, + HOB_TYPE_PAL_FREQ_BASE, + HOB_TYPE_PAL_FREQ_RATIOS, + HOB_TYPE_PAL_HALT_INFO, + HOB_TYPE_PAL_PERF_MON_INFO, + HOB_TYPE_PAL_PROC_GET_FEATURES, + HOB_TYPE_PAL_PTCE_INFO, + HOB_TYPE_PAL_REGISTER_INFO, + HOB_TYPE_PAL_RSE_INFO, + HOB_TYPE_PAL_TEST_INFO, + HOB_TYPE_PAL_VM_SUMMARY, + HOB_TYPE_PAL_VM_INFO, + HOB_TYPE_PAL_VM_PAGE_SIZE, + HOB_TYPE_NR_VCPU, + HOB_TYPE_NVRAM, + HOB_TYPE_MAX +} hob_type_t; + +static int hob_init(void *buffer ,unsigned long buf_size); +static int add_pal_hob(void* hob_buf); +static int add_mem_hob(void* hob_buf, unsigned long dom_mem_size); +static int add_vcpus_hob(void* hob_buf, unsigned long nr_vcpu); +static int add_nvram_hob(void* hob_buf, unsigned long nvram_addr); +static int build_hob(void* hob_buf, unsigned long hob_buf_size, + unsigned long dom_mem_size, unsigned long vcpus, + unsigned long nvram_addr); +static int load_hob(int xc_handle,uint32_t dom, void *hob_buf); + +static int +xc_ia64_build_hob(int xc_handle, uint32_t dom, + unsigned long memsize, unsigned long vcpus, + unsigned long nvram_addr) +{ + char *hob_buf; + + hob_buf = malloc(GFW_HOB_SIZE); + if (hob_buf == NULL) { + PERROR("Could not allocate hob"); + return -1; + } + + if (build_hob(hob_buf, GFW_HOB_SIZE, memsize, vcpus, nvram_addr) < 0) { + free(hob_buf); + PERROR("Could not build hob"); + return -1; + } + + if (load_hob(xc_handle, dom, hob_buf) < 0) { + free(hob_buf); + PERROR("Could not load hob"); + return -1; + } + free(hob_buf); + return 0; + +} + +static int +hob_init(void *buffer, unsigned long buf_size) +{ + HOB_INFO *phit; + HOB_GENERIC_HEADER *terminal; + + if (sizeof(HOB_INFO) + sizeof(HOB_GENERIC_HEADER) > buf_size) { + // buffer too small + return -1; + } + + phit = (HOB_INFO*)buffer; + phit->header.signature = HOB_SIGNATURE; + phit->header.type = HOB_TYPE_INFO; + phit->header.length = sizeof(HOB_INFO); + phit->length = sizeof(HOB_INFO) + sizeof(HOB_GENERIC_HEADER); + phit->cur_pos = 0; + phit->buf_size = buf_size; + + terminal = (HOB_GENERIC_HEADER*)(buffer + sizeof(HOB_INFO)); + terminal->signature = HOB_SIGNATURE; + terminal->type = HOB_TYPE_TERMINAL; + terminal->length = sizeof(HOB_GENERIC_HEADER); + + return 0; +} + +/* + * Add a new HOB to the HOB List. + * + * hob_start - start address of hob buffer + * type - type of the hob to be added + * data - data of the hob to be added + * data_size - size of the data + */ +static int +hob_add(void* hob_start, int type, void* data, int data_size) +{ + HOB_INFO *phit; + HOB_GENERIC_HEADER *newhob, *tail; + + phit = (HOB_INFO*)hob_start; + + if (phit->length + data_size > phit->buf_size) { + // no space for new hob + return -1; + } + + //append new HOB + newhob = (HOB_GENERIC_HEADER*)(hob_start + phit->length - + sizeof(HOB_GENERIC_HEADER)); + newhob->signature = HOB_SIGNATURE; + newhob->type = type; + newhob->length = data_size + sizeof(HOB_GENERIC_HEADER); + memcpy((void*)newhob + sizeof(HOB_GENERIC_HEADER), data, data_size); + + // append terminal HOB + tail = (HOB_GENERIC_HEADER*)(hob_start + phit->length + data_size); + tail->signature = HOB_SIGNATURE; + tail->type = HOB_TYPE_TERMINAL; + tail->length = sizeof(HOB_GENERIC_HEADER); + + // adjust HOB list length + phit->length += sizeof(HOB_GENERIC_HEADER) + data_size; + + return 0; +} + +static int +get_hob_size(void* hob_buf) +{ + HOB_INFO *phit = (HOB_INFO*)hob_buf; + + if (phit->header.signature != HOB_SIGNATURE) { + PERROR("xc_get_hob_size:Incorrect signature"); + return -1; + } + return phit->length; +} + +static int +build_hob(void* hob_buf, unsigned long hob_buf_size, + unsigned long dom_mem_size, unsigned long vcpus, + unsigned long nvram_addr) +{ + //Init HOB List + if (hob_init(hob_buf, hob_buf_size) < 0) { + PERROR("buffer too small"); + goto err_out; + } + + if (add_mem_hob(hob_buf,dom_mem_size) < 0) { + PERROR("Add memory hob failed, buffer too small"); + goto err_out; + } + + if (add_vcpus_hob(hob_buf, vcpus) < 0) { + PERROR("Add NR_VCPU hob failed, buffer too small"); + goto err_out; + } + + if (add_pal_hob( hob_buf ) < 0) { + PERROR("Add PAL hob failed, buffer too small"); + goto err_out; + } + + if (add_nvram_hob( hob_buf, nvram_addr ) < 0) { + PERROR("Add nvram hob failed, buffer too small"); + goto err_out; + } + + return 0; + +err_out: + return -1; +} + +static int +load_hob(int xc_handle, uint32_t dom, void *hob_buf) +{ + // hob_buf should be page aligned + int hob_size; + int nr_pages; + + hob_size = get_hob_size(hob_buf); + if (hob_size < 0) { + PERROR("Invalid hob data"); + return -1; + } + + if (hob_size > GFW_HOB_SIZE) { + PERROR("No enough memory for hob data"); + return -1; + } + + nr_pages = (hob_size + PAGE_SIZE -1) >> PAGE_SHIFT; + + return xc_ia64_copy_to_domain_pages(xc_handle, dom, hob_buf, + GFW_HOB_START >> PAGE_SHIFT, nr_pages); +} + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +static int +add_mem_hob(void* hob_buf, unsigned long dom_mem_size) +{ + hob_mem_t memhob; + + // less than 3G accounting VGA RAM hole + memhob.start = 0; + if (dom_mem_size < VGA_IO_START) + memhob.size = dom_mem_size; + else + memhob.size = MIN(dom_mem_size + VGA_IO_SIZE, 0xC0000000); + + if (hob_add(hob_buf, HOB_TYPE_MEM, &memhob, sizeof(memhob)) < 0) + return -1; + + if (dom_mem_size > 0xC0000000) { + // 4G ~ 4G+remain + memhob.start = 0x100000000; //4G + memhob.size = dom_mem_size + VGA_IO_SIZE - 0xC0000000; + if (hob_add(hob_buf, HOB_TYPE_MEM, &memhob, sizeof(memhob)) < 0) + return -1; + } + return 0; +} + +static int +add_vcpus_hob(void* hob_buf, unsigned long vcpus) +{ + return hob_add(hob_buf, HOB_TYPE_NR_VCPU, &vcpus, sizeof(vcpus)); +} + +static int +add_nvram_hob(void *hob_buf, unsigned long nvram_addr) +{ + return hob_add(hob_buf, HOB_TYPE_NVRAM, &nvram_addr, sizeof(nvram_addr)); +} + +static const unsigned char config_pal_bus_get_features_data[24] = { + 0, 0, 0, 32, 0, 0, 240, 189, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_cache_summary[16] = { + 3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_mem_attrib[8] = { + 241, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_cache_info[152] = { + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 4, 6, 7, 255, 1, 0, 1, 0, 64, 0, 0, 12, 12, + 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 7, 0, 1, + 0, 1, 0, 64, 0, 0, 12, 12, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 7, 7, 255, 7, 0, 11, 0, 0, 16, 0, + 12, 17, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 7, + 7, 7, 5, 9, 11, 0, 0, 4, 0, 12, 15, 49, 0, 254, 255, + 255, 255, 255, 255, 255, 255, 2, 8, 7, 7, 7, 5, 9, + 11, 0, 0, 4, 0, 12, 15, 49, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 12, 7, 7, 7, 14, 1, 3, 0, 0, 192, 0, 12, 20, 49, 0 +}; + +static const unsigned char config_pal_cache_prot_info[200] = { + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 45, 0, 16, 8, 0, 76, 12, 64, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 16, 4, 0, 76, 44, 68, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 0, 16, 8, 0, 81, 44, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, + 112, 12, 0, 79, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255, + 32, 0, 112, 12, 0, 79, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 160, + 12, 0, 84, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const unsigned char config_pal_debug_info[16] = { + 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_fixed_addr[8] = { + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_freq_base[8] = { + 109, 219, 182, 13, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_freq_ratios[24] = { + 11, 1, 0, 0, 77, 7, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 4, + 0, 0, 0, 7, 0, 0, 0 +}; + +static const unsigned char config_pal_halt_info[64] = { + 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_perf_mon_info[136] = { + 12, 47, 18, 8, 0, 0, 0, 0, 241, 255, 0, 0, 255, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 0, 0, 223, 0, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_proc_get_features[104] = { + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 64, 6, 64, 49, 0, 0, 0, 0, 64, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 231, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, + 63, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_ptce_info[24] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_register_info[64] = { + 255, 0, 47, 127, 17, 17, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 208, 128, 238, 238, 0, 0, 248, 255, 255, 255, 255, 255, 0, 0, 7, 3, + 251, 3, 0, 0, 0, 0, 255, 7, 3, 0, 0, 0, 0, 0, 248, 252, 4, + 252, 255, 255, 255, 255, 2, 248, 252, 255, 255, 255, 255, 255 +}; + +static const unsigned char config_pal_rse_info[16] = { + 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_test_info[48] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_vm_summary[16] = { + 101, 18, 15, 2, 7, 7, 4, 2, 59, 18, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_vm_info[104] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 32, 32, 0, 0, 0, 0, 0, 0, 112, 85, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 32, 32, 0, 0, 0, 0, 0, 0, 112, 85, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 128, 128, 0, + 4, 0, 0, 0, 0, 112, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 128, 128, 0, 4, 0, 0, 0, 0, 112, 85, 0, 0, 0, 0, 0 +}; + +static const unsigned char config_pal_vm_page_size[16] = { + 0, 112, 85, 21, 0, 0, 0, 0, 0, 112, 85, 21, 0, 0, 0, 0 +}; + +typedef struct{ + hob_type_t type; + void* data; + unsigned long size; +} hob_batch_t; + +static const hob_batch_t hob_batch[]={ + { HOB_TYPE_PAL_BUS_GET_FEATURES_DATA, + &config_pal_bus_get_features_data, + sizeof(config_pal_bus_get_features_data) + }, + { HOB_TYPE_PAL_CACHE_SUMMARY, + &config_pal_cache_summary, + sizeof(config_pal_cache_summary) + }, + { HOB_TYPE_PAL_MEM_ATTRIB, + &config_pal_mem_attrib, + sizeof(config_pal_mem_attrib) + }, + { HOB_TYPE_PAL_CACHE_INFO, + &config_pal_cache_info, + sizeof(config_pal_cache_info) + }, + { HOB_TYPE_PAL_CACHE_PROT_INFO, + &config_pal_cache_prot_info, + sizeof(config_pal_cache_prot_info) + }, + { HOB_TYPE_PAL_DEBUG_INFO, + &config_pal_debug_info, + sizeof(config_pal_debug_info) + }, + { HOB_TYPE_PAL_FIXED_ADDR, + &config_pal_fixed_addr, + sizeof(config_pal_fixed_addr) + }, + { HOB_TYPE_PAL_FREQ_BASE, + &config_pal_freq_base, + sizeof(config_pal_freq_base) + }, + { HOB_TYPE_PAL_FREQ_RATIOS, + &config_pal_freq_ratios, + sizeof(config_pal_freq_ratios) + }, + { HOB_TYPE_PAL_HALT_INFO, + &config_pal_halt_info, + sizeof(config_pal_halt_info) + }, + { HOB_TYPE_PAL_PERF_MON_INFO, + &config_pal_perf_mon_info, + sizeof(config_pal_perf_mon_info) + }, + { HOB_TYPE_PAL_PROC_GET_FEATURES, + &config_pal_proc_get_features, + sizeof(config_pal_proc_get_features) + }, + { HOB_TYPE_PAL_PTCE_INFO, + &config_pal_ptce_info, + sizeof(config_pal_ptce_info) + }, + { HOB_TYPE_PAL_REGISTER_INFO, + &config_pal_register_info, + sizeof(config_pal_register_info) + }, + { HOB_TYPE_PAL_RSE_INFO, + &config_pal_rse_info, + sizeof(config_pal_rse_info) + }, + { HOB_TYPE_PAL_TEST_INFO, + &config_pal_test_info, + sizeof(config_pal_test_info) + }, + { HOB_TYPE_PAL_VM_SUMMARY, + &config_pal_vm_summary, + sizeof(config_pal_vm_summary) + }, + { HOB_TYPE_PAL_VM_INFO, + &config_pal_vm_info, + sizeof(config_pal_vm_info) + }, + { HOB_TYPE_PAL_VM_PAGE_SIZE, + &config_pal_vm_page_size, + sizeof(config_pal_vm_page_size) + }, +}; + +static int +add_pal_hob(void* hob_buf) +{ + int i; + for (i = 0; i < sizeof(hob_batch)/sizeof(hob_batch_t); i++) { + if (hob_add(hob_buf, hob_batch[i].type, hob_batch[i].data, + hob_batch[i].size) < 0) + return -1; + } + return 0; +} + +// The most significant bit of nvram file descriptor: +// 1: valid; 0: invalid +#define VALIDATE_NVRAM_FD(x) ((1UL<<(sizeof(x)*8 - 1)) | x) +#define IS_VALID_NVRAM_FD(x) ((uint64_t)x >> (sizeof(x)*8 - 1)) +static uint64_t +nvram_init(const char *nvram_path) +{ + uint64_t fd = 0; + fd = open(nvram_path, O_CREAT|O_RDWR, 0644); + + if ( fd < 0 ) + { + PERROR("Nvram open failed at %s. Guest will boot without" + " nvram support!\n", nvram_path); + return -1; + } + + return VALIDATE_NVRAM_FD(fd); +} + +static int +copy_from_nvram_to_GFW(int xc_handle, uint32_t dom, int nvram_fd) +{ + unsigned int nr_pages = NVRAM_SIZE >> PAGE_SHIFT; + struct stat file_stat; + char buf[NVRAM_SIZE] = {0}; + + if ( fstat(nvram_fd, &file_stat) < 0 ) + { + PERROR("Cannot get Nvram file info! Guest will boot without " + "nvram support!\n"); + return -1; + } + + if ( 0 == file_stat.st_size ) + { + DPRINTF("Nvram file create successful!\n"); + return 0; + } + + if ( read(nvram_fd, buf, NVRAM_SIZE) != NVRAM_SIZE ) + { + PERROR("Load nvram fail. guest will boot without" + " nvram support!\n"); + return -1; + } + + return xc_ia64_copy_to_domain_pages(xc_handle, dom, buf, + NVRAM_START >> PAGE_SHIFT, nr_pages); +} + + +/* + *Check is the address where NVRAM data located valid + */ +static int is_valid_address(void *addr) +{ + struct nvram_save_addr *p = (struct nvram_save_addr *)addr; + + if ( p->signature == NVRAM_VALID_SIG ) + return 1; + else { + PERROR("Invalid nvram signature. Nvram save failed!\n"); + return 0; + } +} + +/* + * GFW use 4k page. when doing foreign map, we should 16k align + * the address and map one more page to guarantee all 64k nvram data + * can be got. + */ +static int +copy_from_GFW_to_nvram(int xc_handle, uint32_t dom, int nvram_fd) +{ + xen_pfn_t *pfn_list = NULL; + char *tmp_ptr = NULL; + unsigned int nr_pages = 0; + uint64_t addr_from_GFW_4k_align = 0; + uint32_t offset = 0; + uint64_t nvram_base_addr = 0; + char buf[NVRAM_SIZE] = {0}; + int i; + + // map one more page + nr_pages = (NVRAM_SIZE + PAGE_SIZE) >> PAGE_SHIFT; + pfn_list = (xen_pfn_t *)malloc(sizeof(xen_pfn_t) * nr_pages); + if ( NULL == pfn_list ) + { + PERROR("Cannot allocate memory for nvram save!\n"); + close(nvram_fd); + return -1; + } + + /* + * GFW allocate memory dynamicly to save nvram data + * and save address of the dynamic memory at NVRAM_START. + * To save nvram data to file, we must get the dynamic + * memory address first. + */ + pfn_list[0] = NVRAM_START >> PAGE_SHIFT; + tmp_ptr = (char *)xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ | PROT_WRITE, pfn_list[0]); + + if ( NULL == tmp_ptr ) + { + PERROR("Cannot get nvram data from GFW!\n"); + free(pfn_list); + close(nvram_fd); + return -1; + } + + /* Check is NVRAM data vaild */ + if ( !is_valid_address(tmp_ptr) ) + { + free(pfn_list); + munmap(tmp_ptr, PAGE_SIZE); + close(nvram_fd); + return -1; + } + + addr_from_GFW_4k_align = ((struct nvram_save_addr *)tmp_ptr)->addr; + munmap(tmp_ptr, PAGE_SIZE); + + // align address to 16k + offset = addr_from_GFW_4k_align % ( 16 * MEM_K ); + addr_from_GFW_4k_align = addr_from_GFW_4k_align - offset; + for ( i=0; i> PAGE_SHIFT) + i; + + tmp_ptr = (char *)xc_map_foreign_pages(xc_handle, dom, + PROT_READ | PROT_WRITE, + pfn_list, nr_pages); + if ( NULL == tmp_ptr ) + { + PERROR("Cannot get nvram data from GFW!\n"); + free(pfn_list); + close(nvram_fd); + return -1; + } + + // calculate nvram data base addrees + nvram_base_addr = (uint64_t)(tmp_ptr + offset); + + memcpy(buf, (void *)nvram_base_addr, NVRAM_SIZE); + free(pfn_list); + munmap(tmp_ptr, NVRAM_SIZE + PAGE_SIZE); + + lseek(nvram_fd, 0, SEEK_SET); + if ( write(nvram_fd, buf, NVRAM_SIZE) != NVRAM_SIZE ) + { + PERROR("Save to nvram fail!\n"); + return -1; + } + + close(nvram_fd); + + DPRINTF("Nvram save successful!\n"); + + return 0; +} + +int xc_ia64_save_to_nvram(int xc_handle, uint32_t dom) +{ + xc_dominfo_t info; + uint64_t nvram_fd = 0; + + if ( xc_domain_getinfo(xc_handle, dom, 1, &info) != 1 ) + { + PERROR("Could not get info for domain"); + return -1; + } + + if ( !info.hvm ) + return 0; + + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_NVRAM_FD, &nvram_fd); + + if ( !IS_VALID_NVRAM_FD(nvram_fd) ) + PERROR("Nvram not initialized. Nvram save failed!\n"); + else + copy_from_GFW_to_nvram(xc_handle, dom, (int)nvram_fd); + + // although save to nvram maybe fail, we don't return any error number + // to Xend. This is quite logical because damage of NVRAM on native would + // not block OS's executive path. Return error number will cause an + // exception of Xend and block XenU when it destroy. + return 0; +} + +#define NVRAM_DIR "/var/lib/xen/nvram/" +#define NVRAM_FILE_PREFIX "nvram_" + +int xc_ia64_nvram_init(int xc_handle, char *dom_name, uint32_t dom) +{ + uint64_t nvram_fd; + char nvram_path[PATH_MAX] = NVRAM_DIR; + + if ( access(nvram_path, R_OK|W_OK|X_OK) == -1 ) { + if ( errno != ENOENT ) + { + PERROR("Error stat'ing NVRAM dir %s.", nvram_path); + return -1; + } + if ( mkdir(nvram_path, 0755) == -1 ) + { + PERROR("Unable to create NVRAM store directory %s.", nvram_path); + return -1; + } + } + + if ( access(nvram_path, R_OK|W_OK|X_OK) == -1 ) { + errno = EACCES; + PERROR("No RWX permission to NVRAM store directory %s.", nvram_path); + return -1; + } + + if ( strlen(nvram_path) + strlen(NVRAM_FILE_PREFIX) + + strlen(dom_name) + 1 > sizeof(nvram_path) ) + { + PERROR("Nvram file path is too long!\n"); + return -1; + } + strcat(nvram_path, NVRAM_FILE_PREFIX); + strcat(nvram_path, dom_name); + + nvram_fd = nvram_init(nvram_path); + if ( nvram_fd == (uint64_t)(-1) ) + { + xc_set_hvm_param(xc_handle, dom, HVM_PARAM_NVRAM_FD, 0); + return -1; + } + + xc_set_hvm_param(xc_handle, dom, HVM_PARAM_NVRAM_FD, nvram_fd); + return 0; +} + +#define GFW_PAGES (GFW_SIZE >> PAGE_SHIFT) +#define VGA_START_PAGE (VGA_IO_START >> PAGE_SHIFT) +#define VGA_END_PAGE ((VGA_IO_START + VGA_IO_SIZE) >> PAGE_SHIFT) + +static void +xc_ia64_setup_md(efi_memory_desc_t *md, + unsigned long start, unsigned long end) +{ + md->type = EFI_CONVENTIONAL_MEMORY; + md->pad = 0; + md->phys_addr = start; + md->virt_addr = 0; + md->num_pages = (end - start) >> EFI_PAGE_SHIFT; + md->attribute = EFI_MEMORY_WB; +} + +static inline unsigned long +min(unsigned long lhs, unsigned long rhs) +{ + return (lhs < rhs)? lhs: rhs; +} + +static int +xc_ia64_setup_memmap_info(int xc_handle, uint32_t dom, + unsigned long dom_memsize, /* in bytes */ + unsigned long *pfns_special_pages, + unsigned long nr_special_pages, + unsigned long memmap_info_pfn, + unsigned long memmap_info_num_pages) +{ + xen_ia64_memmap_info_t* memmap_info; + efi_memory_desc_t *md; + uint64_t nr_mds; + + memmap_info = xc_map_foreign_range(xc_handle, dom, + PAGE_SIZE * memmap_info_num_pages, + PROT_READ | PROT_WRITE, + memmap_info_pfn); + if (memmap_info == NULL) { + PERROR("Could not map memmmap_info page.\n"); + return -1; + } + memset(memmap_info, 0, PAGE_SIZE * memmap_info_num_pages); + + /* + * [0, VGA_IO_START = 0xA0000) + * [VGA_IO_START + VGA_IO_SIZE = 0xC0000, MMIO_START = 3GB) + * [IO_PAGE_START (> 3GB), IO_PAGE_START + IO_PAGE_SIZE) + * [STORE_PAGE_START, STORE_PAGE_START + STORE_PAGE_SIZE) + * [BUFFER_IO_PAGE_START, BUFFER_IO_PAGE_START + BUFFER_IO_PAGE_SIZE) + * [BUFFER_PIO_PAGE_START, BUFFER_PIO_PAGE_START + BUFFER_PIO_PAGE_SIZE) + * [memmap_info_pfn << PAGE_SHIFT, + * (memmap_info_pfn << PAGE_SHIFT) + PAGE_SIZE) + * [GFW_START=4GB - GFW_SIZE, GFW_START + GFW_SIZE = 4GB) + * [4GB, ...) + */ + md = (efi_memory_desc_t*)&memmap_info->memdesc; + xc_ia64_setup_md(md, 0, min(VGA_IO_START, dom_memsize)); + md++; + + if (dom_memsize > VGA_IO_START) { + xc_ia64_setup_md(md, VGA_IO_START + VGA_IO_SIZE, + min(MMIO_START, dom_memsize + VGA_IO_SIZE)); + md++; + } + xc_ia64_setup_md(md, IO_PAGE_START, IO_PAGE_START + IO_PAGE_SIZE); + md++; + xc_ia64_setup_md(md, STORE_PAGE_START, STORE_PAGE_START + STORE_PAGE_SIZE); + md++; + xc_ia64_setup_md(md, BUFFER_IO_PAGE_START, + BUFFER_IO_PAGE_START + BUFFER_IO_PAGE_SIZE); + md++; + xc_ia64_setup_md(md, BUFFER_PIO_PAGE_START, + BUFFER_PIO_PAGE_START + BUFFER_PIO_PAGE_SIZE); + md++; + xc_ia64_setup_md(md, memmap_info_pfn << PAGE_SHIFT, + (memmap_info_pfn << PAGE_SHIFT) + + PAGE_SIZE * memmap_info_num_pages); + md++; + xc_ia64_setup_md(md, GFW_START, GFW_START + GFW_SIZE); + md++; + if (dom_memsize + VGA_IO_SIZE > MMIO_START) { + xc_ia64_setup_md(md, 4 * MEM_G, dom_memsize + VGA_IO_SIZE + (1 * MEM_G)); + md++; + } + nr_mds = md - (efi_memory_desc_t*)&memmap_info->memdesc; + + assert(nr_mds <= + (PAGE_SIZE * memmap_info_num_pages - + offsetof(typeof(*memmap_info), memdesc))/sizeof(*md)); + memmap_info->efi_memmap_size = nr_mds * sizeof(*md); + memmap_info->efi_memdesc_size = sizeof(*md); + memmap_info->efi_memdesc_version = EFI_MEMORY_DESCRIPTOR_VERSION; + + munmap(memmap_info, PAGE_SIZE * memmap_info_num_pages); + return 0; +} + +/* setup shared_info page */ +static int +xc_ia64_setup_shared_info(int xc_handle, uint32_t dom, + unsigned long shared_info_pfn, + unsigned long memmap_info_pfn, + unsigned long memmap_info_num_pages) +{ + shared_info_t *shared_info; + + shared_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ | PROT_WRITE, + shared_info_pfn); + if (shared_info == NULL) { + PERROR("Could not map shared_info"); + return -1; + } + memset(shared_info, 0, sizeof(*shared_info)); + shared_info->arch.memmap_info_num_pages = memmap_info_num_pages; + shared_info->arch.memmap_info_pfn = memmap_info_pfn; + munmap(shared_info, PAGE_SIZE); + return 0; +} + +/* + * In this function, we will allocate memory and build P2M/M2P table for VTI + * guest. Frist, a pfn list will be initialized discontiguous, normal memory + * begins with 0, GFW memory and other five pages at their place defined in + * xen/include/public/arch-ia64.h xc_domain_memory_populate_physmap() called + * five times, to set parameter 'extent_order' to different value, this is + * convenient to allocate discontiguous memory with different size. + */ +static int +setup_guest(int xc_handle, uint32_t dom, unsigned long memsize, + char *image, unsigned long image_size) +{ + xen_pfn_t *pfn_list; + unsigned long dom_memsize = memsize << 20; + unsigned long nr_pages = memsize << (20 - PAGE_SHIFT); + unsigned long vcpus; + unsigned long nr_special_pages; + unsigned long memmap_info_pfn; + unsigned long memmap_info_num_pages; + unsigned long nvram_start = NVRAM_START, nvram_fd = 0; + int rc; + unsigned long i; + unsigned long pfn; + const struct hvm_special_page { + int param; + xen_pfn_t pfn; + } special_pages[] = { + // pfn-sorted array + { HVM_PARAM_IOREQ_PFN, IO_PAGE_START >> PAGE_SHIFT}, + { HVM_PARAM_STORE_PFN, STORE_PAGE_START >> PAGE_SHIFT}, + { HVM_PARAM_BUFIOREQ_PFN, BUFFER_IO_PAGE_START >> PAGE_SHIFT}, + { HVM_PARAM_BUFPIOREQ_PFN, BUFFER_PIO_PAGE_START >> PAGE_SHIFT}, + }; + DECLARE_DOMCTL; + + + if ((image_size > 12 * MEM_M) || (image_size & (PAGE_SIZE - 1))) { + PERROR("Guest firmware size is incorrect [%ld]?", image_size); + return -1; + } + + pfn_list = malloc(nr_pages * sizeof(xen_pfn_t)); + if (pfn_list == NULL) { + PERROR("Could not allocate memory.\n"); + return -1; + } + + // + // Populate + // [0, VGA_IO_START) (VGA_IO_SIZE hole) + // [VGA_IO_START + VGA_IO_SIZE, MMIO_START) (1GB hole) + // [4GB, end) + // + i = 0; + for (pfn = 0; + pfn < MIN((dom_memsize >> PAGE_SHIFT), VGA_START_PAGE); + pfn++) + pfn_list[i++] = pfn; + for (pfn = VGA_END_PAGE; + pfn < (MIN(dom_memsize + VGA_IO_SIZE, MMIO_START) >> PAGE_SHIFT); + pfn++) + pfn_list[i++] = pfn; + for (pfn = ((4 * MEM_G) >> PAGE_SHIFT); + pfn < ((dom_memsize + VGA_IO_SIZE + 1 * MEM_G) >> PAGE_SHIFT); + pfn++) + pfn_list[i++] = pfn; + + rc = xc_domain_memory_populate_physmap(xc_handle, dom, nr_pages, 0, 0, + &pfn_list[0]); + if (rc != 0) { + PERROR("Could not allocate normal memory for Vti guest.\n"); + goto error_out; + } + + // We allocate additional pfn for GFW and other five pages, so + // the pfn_list is not contiguous. Due to this we must support + // old interface xc_ia64_get_pfn_list(). + for (i = 0; i < GFW_PAGES; i++) + pfn_list[i] = (GFW_START >> PAGE_SHIFT) + i; + + rc = xc_domain_memory_populate_physmap(xc_handle, dom, GFW_PAGES, + 0, 0, &pfn_list[0]); + if (rc != 0) { + PERROR("Could not allocate GFW memory for Vti guest.\n"); + goto error_out; + } + + for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) + pfn_list[i] = special_pages[i].pfn; + + nr_special_pages = i; + memmap_info_pfn = pfn_list[nr_special_pages - 1] + 1; + memmap_info_num_pages = 1; + pfn_list[nr_special_pages] = memmap_info_pfn; + nr_special_pages++; + + rc = xc_domain_memory_populate_physmap(xc_handle, dom, nr_special_pages, + 0, 0, &pfn_list[0]); + if (rc != 0) { + PERROR("Could not allocate IO page or store page or buffer io page.\n"); + goto error_out; + } + + domctl.u.arch_setup.flags = 0; + domctl.u.arch_setup.bp = 0; + domctl.u.arch_setup.maxmem = GFW_START + GFW_SIZE; + if (dom_memsize + VGA_IO_SIZE > MMIO_START) + domctl.u.arch_setup.maxmem = dom_memsize + VGA_IO_SIZE + 1 * MEM_G; + domctl.cmd = XEN_DOMCTL_arch_setup; + domctl.domain = (domid_t)dom; + if (xc_domctl(xc_handle, &domctl)) + goto error_out; + + // Load guest firmware + if (xc_ia64_copy_to_domain_pages(xc_handle, dom, image, + (GFW_START + GFW_SIZE - image_size) >> PAGE_SHIFT, + image_size >> PAGE_SHIFT)) { + PERROR("Could not load guest firmware into domain"); + goto error_out; + } + + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)dom; + if (xc_domctl(xc_handle, &domctl) < 0) { + PERROR("Could not get info on domain"); + goto error_out; + } + + if (xc_ia64_setup_memmap_info(xc_handle, dom, dom_memsize, + pfn_list, nr_special_pages, + memmap_info_pfn, memmap_info_num_pages)) { + PERROR("Could not build memmap info\n"); + goto error_out; + } + if (xc_ia64_setup_shared_info(xc_handle, dom, + domctl.u.getdomaininfo.shared_info_frame, + memmap_info_pfn, memmap_info_num_pages)) { + PERROR("Could not setup shared_info\n"); + goto error_out; + } + + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_NVRAM_FD, &nvram_fd); + if ( !IS_VALID_NVRAM_FD(nvram_fd) ) + nvram_start = 0; + else if ( copy_from_nvram_to_GFW(xc_handle, dom, (int)nvram_fd ) == -1 ) { + nvram_start = 0; + close(nvram_fd); + } + + vcpus = domctl.u.getdomaininfo.max_vcpu_id + 1; + + // Hand-off state passed to guest firmware + if (xc_ia64_build_hob(xc_handle, dom, dom_memsize, vcpus, nvram_start) < 0) { + PERROR("Could not build hob\n"); + goto error_out; + } + + // zero clear all special pages + for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { + xc_set_hvm_param(xc_handle, dom, + special_pages[i].param, special_pages[i].pfn); + if (xc_clear_domain_page(xc_handle, dom, special_pages[i].pfn)) + goto error_out; + } + + free(pfn_list); + return 0; + +error_out: + return -1; +} + +int +xc_hvm_build(int xc_handle, uint32_t domid, int memsize, const char *image_name) +{ + vcpu_guest_context_any_t st_ctxt_any; + vcpu_guest_context_t *ctxt = &st_ctxt_any.c; + char *image = NULL; + unsigned long image_size; + unsigned long nr_pages; + + nr_pages = xc_get_max_pages(xc_handle, domid); + if (nr_pages < 0) { + PERROR("Could not find total pages for domain"); + goto error_out; + } + + image = xc_read_image(image_name, &image_size); + if (image == NULL) { + PERROR("Could not read guest firmware image %s", image_name); + goto error_out; + } + + image_size = (image_size + PAGE_SIZE - 1) & PAGE_MASK; + + if (setup_guest(xc_handle, domid, (unsigned long)memsize, image, + image_size) < 0) { + ERROR("Error constructing guest OS"); + goto error_out; + } + + free(image); + + memset(&st_ctxt_any, 0, sizeof(st_ctxt_any)); + ctxt->regs.ip = 0x80000000ffffffb0UL; + ctxt->regs.ar.fpsr = xc_ia64_fpsr_default(); + ctxt->regs.cr.itir = 14 << 2; + ctxt->regs.psr = IA64_PSR_AC | IA64_PSR_BN; + ctxt->regs.cr.dcr = 0; + ctxt->regs.cr.pta = 15 << 2; + return xc_vcpu_setcontext(xc_handle, domid, 0, &st_ctxt_any); + +error_out: + free(image); + return -1; +} + +/* + * From asm/pgtable.h + */ +#define _PAGE_P_BIT 0 +#define _PAGE_A_BIT 5 +#define _PAGE_D_BIT 6 + +#define _PAGE_P (1 << _PAGE_P_BIT) /* page present bit */ +#define _PAGE_A (1 << _PAGE_A_BIT) /* page accessed bit */ +#define _PAGE_D (1 << _PAGE_D_BIT) /* page dirty bit */ + +#define _PAGE_MA_WB (0x0 << 2) /* write back memory attribute */ +#define _PAGE_MA_UC (0x4 << 2) /* uncacheable memory attribute */ +#define _PAGE_AR_RW (2 << 9) /* read & write */ + +int +xc_ia64_set_os_type(int xc_handle, char *guest_os_type, uint32_t dom) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_set_opt_feature; + domctl.domain = (domid_t)dom; + + if (!guest_os_type || !strlen(guest_os_type) || + !strcmp("default", guest_os_type)) { + + /* Nothing */ + return 0; + + } else if (!strcmp("windows", guest_os_type)) { + DPRINTF("Enabling Windows guest OS optimizations\n"); + + /* Windows identity maps regions 4 & 5 */ + domctl.u.set_opt_feature.optf.cmd = XEN_IA64_OPTF_IDENT_MAP_REG4; + domctl.u.set_opt_feature.optf.on = XEN_IA64_OPTF_ON; + domctl.u.set_opt_feature.optf.pgprot = (_PAGE_P | _PAGE_A | _PAGE_D | + _PAGE_MA_WB | _PAGE_AR_RW); + domctl.u.set_opt_feature.optf.key = 0; + if (xc_domctl(xc_handle, &domctl)) + PERROR("Failed to set region 4 identity mapping for Windows " + "guest OS type.\n"); + + domctl.u.set_opt_feature.optf.cmd = XEN_IA64_OPTF_IDENT_MAP_REG5; + domctl.u.set_opt_feature.optf.on = XEN_IA64_OPTF_ON; + domctl.u.set_opt_feature.optf.pgprot = (_PAGE_P | _PAGE_A | _PAGE_D | + _PAGE_MA_UC | _PAGE_AR_RW); + domctl.u.set_opt_feature.optf.key = 0; + if (xc_domctl(xc_handle, &domctl)) + PERROR("Failed to set region 5 identity mapping for Windows " + "guest OS type.\n"); + return 0; + + } else if (!strcmp("linux", guest_os_type)) { + DPRINTF("Enabling Linux guest OS optimizations\n"); + + /* Linux identity maps regions 7 */ + domctl.u.set_opt_feature.optf.cmd = XEN_IA64_OPTF_IDENT_MAP_REG7; + domctl.u.set_opt_feature.optf.on = XEN_IA64_OPTF_ON; + domctl.u.set_opt_feature.optf.pgprot = (_PAGE_P | _PAGE_A | _PAGE_D | + _PAGE_MA_WB | _PAGE_AR_RW); + domctl.u.set_opt_feature.optf.key = 0; + if (xc_domctl(xc_handle, &domctl)) + PERROR("Failed to set region 7 identity mapping for Linux " + "guest OS type.\n"); + return 0; + } + + DPRINTF("Unknown guest_os_type (%s), using defaults\n", guest_os_type); + + return 0; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_ia64_linux_restore.c b/tools/libxc/ia64/xc_ia64_linux_restore.c new file mode 100644 index 0000000..290c8e7 --- /dev/null +++ b/tools/libxc/ia64/xc_ia64_linux_restore.c @@ -0,0 +1,730 @@ +/****************************************************************************** + * xc_ia64_linux_restore.c + * + * Restore the state of a Linux session. + * + * Copyright (c) 2003, K A Fraser. + * Rewritten for ia64 by Tristan Gingold + * + * Copyright (c) 2007 Isaku Yamahata + * Use foreign p2m exposure. + * VTi domain support + */ + +#include +#include + +#include "xg_private.h" +#include "xc_ia64_save_restore.h" +#include "xc_ia64.h" +#include "xc_efi.h" +#include "xen/hvm/params.h" + +#define PFN_TO_KB(_pfn) ((_pfn) << (PAGE_SHIFT - 10)) + +/* number of pfns this guest has (i.e. number of entries in the P2M) */ +static unsigned long p2m_size; + +/* number of 'in use' pfns in the guest (i.e. #P2M entries with a valid mfn) */ +static unsigned long nr_pfns; + +static int +populate_page_if_necessary(int xc_handle, uint32_t dom, unsigned long gmfn, + struct xen_ia64_p2m_table *p2m_table) +{ + if (xc_ia64_p2m_present(p2m_table, gmfn)) + return 0; + + return xc_domain_memory_populate_physmap(xc_handle, dom, 1, 0, 0, &gmfn); +} + +static int +read_page(int xc_handle, int io_fd, uint32_t dom, unsigned long pfn) +{ + void *mem; + + mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ|PROT_WRITE, pfn); + if (mem == NULL) { + ERROR("cannot map page"); + return -1; + } + if (read_exact(io_fd, mem, PAGE_SIZE)) { + ERROR("Error when reading from state file (5)"); + munmap(mem, PAGE_SIZE); + return -1; + } + munmap(mem, PAGE_SIZE); + return 0; +} + +/* + * Get the list of PFNs that are not in the psuedo-phys map. + * Although we allocate pages on demand, balloon driver may + * decreased simaltenously. So we have to free the freed + * pages here. + */ +static int +xc_ia64_recv_unallocated_list(int xc_handle, int io_fd, uint32_t dom, + struct xen_ia64_p2m_table *p2m_table) +{ + int rc = -1; + unsigned int i; + unsigned int count; + unsigned long *pfntab = NULL; + unsigned int nr_frees; + + if (read_exact(io_fd, &count, sizeof(count))) { + ERROR("Error when reading pfn count"); + goto out; + } + + pfntab = malloc(sizeof(unsigned long) * count); + if (pfntab == NULL) { + ERROR("Out of memory"); + goto out; + } + + if (read_exact(io_fd, pfntab, sizeof(unsigned long)*count)) { + ERROR("Error when reading pfntab"); + goto out; + } + + nr_frees = 0; + for (i = 0; i < count; i++) { + if (xc_ia64_p2m_allocated(p2m_table, pfntab[i])) { + pfntab[nr_frees] = pfntab[i]; + nr_frees++; + } + } + if (nr_frees > 0) { + if (xc_domain_memory_decrease_reservation(xc_handle, dom, nr_frees, + 0, pfntab) < 0) { + PERROR("Could not decrease reservation"); + goto out; + } else + DPRINTF("Decreased reservation by %d / %d pages\n", + nr_frees, count); + } + + rc = 0; + + out: + if (pfntab != NULL) + free(pfntab); + return rc; +} + +static int +xc_ia64_recv_vcpu_context(int xc_handle, int io_fd, uint32_t dom, + uint32_t vcpu, vcpu_guest_context_any_t *ctxt_any) +{ + vcpu_guest_context_t *ctxt = &ctxt_any->c; + if (read_exact(io_fd, ctxt, sizeof(*ctxt))) { + ERROR("Error when reading ctxt"); + return -1; + } + + fprintf(stderr, "ip=%016lx, b0=%016lx\n", ctxt->regs.ip, ctxt->regs.b[0]); + + /* Initialize and set registers. */ + ctxt->flags = VGCF_EXTRA_REGS | VGCF_SET_CR_IRR | VGCF_online; + if (xc_vcpu_setcontext(xc_handle, dom, vcpu, ctxt_any) != 0) { + ERROR("Couldn't set vcpu context"); + return -1; + } + + /* Just a check. */ + ctxt->flags = 0; + if (xc_vcpu_getcontext(xc_handle, dom, vcpu, ctxt_any)) { + ERROR("Could not get vcpu context"); + return -1; + } + + return 0; +} + +/* Read shared info. */ +static int +xc_ia64_recv_shared_info(int xc_handle, int io_fd, uint32_t dom, + unsigned long shared_info_frame, + unsigned long *start_info_pfn) +{ + unsigned int i; + + /* The new domain's shared-info frame. */ + shared_info_t *shared_info; + + /* Read shared info. */ + shared_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ|PROT_WRITE, + shared_info_frame); + if (shared_info == NULL) { + ERROR("cannot map page"); + return -1; + } + + if (read_exact(io_fd, shared_info, PAGE_SIZE)) { + ERROR("Error when reading shared_info page"); + munmap(shared_info, PAGE_SIZE); + return -1; + } + + /* clear any pending events and the selector */ + memset(&(shared_info->evtchn_pending[0]), 0, + sizeof (shared_info->evtchn_pending)); + for (i = 0; i < MAX_VIRT_CPUS; i++) + shared_info->vcpu_info[i].evtchn_pending_sel = 0; + + if (start_info_pfn != NULL) + *start_info_pfn = shared_info->arch.start_info_pfn; + + munmap (shared_info, PAGE_SIZE); + + return 0; +} + +static int +xc_ia64_recv_vcpumap(const xc_dominfo_t *info, int io_fd, uint64_t **vcpumapp) +{ + uint64_t max_virt_cpus; + unsigned long vcpumap_size; + uint64_t *vcpumap = NULL; + + *vcpumapp = NULL; + + if (read_exact(io_fd, &max_virt_cpus, sizeof(max_virt_cpus))) { + ERROR("error reading max_virt_cpus"); + return -1; + } + if (max_virt_cpus < info->max_vcpu_id) { + ERROR("too large max_virt_cpus %i < %i\n", + max_virt_cpus, info->max_vcpu_id); + return -1; + } + vcpumap_size = (max_virt_cpus + 1 + sizeof(vcpumap[0]) - 1) / + sizeof(vcpumap[0]); + vcpumap = malloc(vcpumap_size); + if (vcpumap == NULL) { + ERROR("memory alloc for vcpumap"); + return -1; + } + memset(vcpumap, 0, vcpumap_size); + if (read_exact(io_fd, vcpumap, vcpumap_size)) { + ERROR("read vcpumap"); + free(vcpumap); + return -1; + } + + *vcpumapp = vcpumap; + return 0; +} + +static int +xc_ia64_pv_recv_vcpu_context(int xc_handle, int io_fd, int32_t dom, + uint32_t vcpu) +{ + int rc = -1; + + /* A copy of the CPU context of the guest. */ + vcpu_guest_context_any_t ctxt_any; + vcpu_guest_context_t *ctxt = &ctxt_any.c; + + if (lock_pages(&ctxt_any, sizeof(ctxt_any))) { + /* needed for build domctl, but might as well do early */ + ERROR("Unable to lock_pages ctxt"); + return -1; + } + + if (xc_ia64_recv_vcpu_context(xc_handle, io_fd, dom, vcpu, &ctxt_any)) + goto out; + + /* Then get privreg page. */ + if (read_page(xc_handle, io_fd, dom, ctxt->privregs_pfn) < 0) { + ERROR("Could not read vcpu privregs"); + goto out; + } + + rc = 0; + + out: + unlock_pages(&ctxt, sizeof(ctxt)); + return rc; +} + +static int +xc_ia64_pv_recv_shared_info(int xc_handle, int io_fd, int32_t dom, + unsigned long shared_info_frame, + struct xen_ia64_p2m_table *p2m_table, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + unsigned long gmfn; + + /* A temporary mapping of the guest's start_info page. */ + start_info_t *start_info; + + /* Read shared info. */ + if (xc_ia64_recv_shared_info(xc_handle, io_fd, dom, + shared_info_frame, &gmfn)) + return -1; + + /* Uncanonicalise the suspend-record frame number and poke resume rec. */ + if (populate_page_if_necessary(xc_handle, dom, gmfn, p2m_table)) { + ERROR("cannot populate page 0x%lx", gmfn); + return -1; + } + start_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ | PROT_WRITE, gmfn); + if (start_info == NULL) { + ERROR("cannot map start_info page"); + return -1; + } + start_info->nr_pages = p2m_size; + start_info->shared_info = shared_info_frame << PAGE_SHIFT; + start_info->flags = 0; + *store_mfn = start_info->store_mfn; + start_info->store_evtchn = store_evtchn; + *console_mfn = start_info->console.domU.mfn; + start_info->console.domU.evtchn = console_evtchn; + munmap(start_info, PAGE_SIZE); + + return 0; +} + +static int +xc_ia64_pv_recv_context_ver_one_or_two(int xc_handle, int io_fd, uint32_t dom, + unsigned long shared_info_frame, + struct xen_ia64_p2m_table *p2m_table, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + int rc; + + /* vcpu 0 context */ + rc = xc_ia64_pv_recv_vcpu_context(xc_handle, io_fd, dom, 0); + if (rc) + return rc; + + + /* shared_info */ + rc = xc_ia64_pv_recv_shared_info(xc_handle, io_fd, dom, shared_info_frame, + p2m_table, store_evtchn, store_mfn, + console_evtchn, console_mfn); + return rc; +} + +static int +xc_ia64_pv_recv_context_ver_three(int xc_handle, int io_fd, uint32_t dom, + unsigned long shared_info_frame, + struct xen_ia64_p2m_table *p2m_table, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + int rc = -1; + xc_dominfo_t info; + unsigned int i; + + /* vcpu map */ + uint64_t *vcpumap = NULL; + + if (xc_domain_getinfo(xc_handle, dom, 1, &info) != 1) { + ERROR("Could not get domain info"); + return -1; + } + rc = xc_ia64_recv_vcpumap(&info, io_fd, &vcpumap); + if (rc != 0) + goto out; + + /* vcpu context */ + for (i = 0; i <= info.max_vcpu_id; i++) { + if (!__test_bit(i, vcpumap)) + continue; + + rc = xc_ia64_pv_recv_vcpu_context(xc_handle, io_fd, dom, i); + if (rc != 0) + goto out; + } + + /* shared_info */ + rc = xc_ia64_pv_recv_shared_info(xc_handle, io_fd, dom, shared_info_frame, + p2m_table, store_evtchn, store_mfn, + console_evtchn, console_mfn); + out: + if (vcpumap != NULL) + free(vcpumap); + return rc; +} + +static int +xc_ia64_pv_recv_context(unsigned long format_version, + int xc_handle, int io_fd, uint32_t dom, + unsigned long shared_info_frame, + struct xen_ia64_p2m_table *p2m_table, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + int rc; + switch (format_version) { + case XC_IA64_SR_FORMAT_VER_ONE: + case XC_IA64_SR_FORMAT_VER_TWO: + rc = xc_ia64_pv_recv_context_ver_one_or_two(xc_handle, io_fd, dom, + shared_info_frame, + p2m_table, store_evtchn, + store_mfn, console_evtchn, + console_mfn); + break; + case XC_IA64_SR_FORMAT_VER_THREE: + rc = xc_ia64_pv_recv_context_ver_three(xc_handle, io_fd, dom, + shared_info_frame, + p2m_table, store_evtchn, + store_mfn, console_evtchn, + console_mfn); + break; + default: + ERROR("Unsupported format version"); + rc = -1; + break; + } + return rc; +} + +static int +xc_ia64_hvm_recv_context(int xc_handle, int io_fd, uint32_t dom, + unsigned long shared_info_frame, + struct xen_ia64_p2m_table *p2m_table, + unsigned int store_evtchn, unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + int rc = -1; + xc_dominfo_t info; + unsigned int i; + + /* cpumap */ + uint64_t *vcpumap = NULL; + + /* HVM: magic frames for ioreqs and xenstore comms */ + const int hvm_params[] = { + HVM_PARAM_STORE_PFN, + HVM_PARAM_IOREQ_PFN, + HVM_PARAM_BUFIOREQ_PFN, + HVM_PARAM_BUFPIOREQ_PFN, + }; + const int NR_PARAMS = sizeof(hvm_params) / sizeof(hvm_params[0]); + /* ioreq_pfn, bufioreq_pfn, store_pfn */ + uint64_t magic_pfns[NR_PARAMS]; + + /* HVM: a buffer for holding HVM contxt */ + uint64_t rec_size = 0; + uint8_t *hvm_buf = NULL; + + /* Read shared info. */ + if (xc_ia64_recv_shared_info(xc_handle, io_fd, dom, shared_info_frame, + NULL)) + goto out; + + /* vcpu map */ + if (xc_domain_getinfo(xc_handle, dom, 1, &info) != 1) { + ERROR("Could not get domain info"); + goto out; + } + if (xc_ia64_recv_vcpumap(&info, io_fd, &vcpumap)) + goto out; + + /* vcpu context */ + for (i = 0; i <= info.max_vcpu_id; i++) { + /* A copy of the CPU context of the guest. */ + vcpu_guest_context_any_t ctxt_any; + + if (!__test_bit(i, vcpumap)) + continue; + + if (xc_ia64_recv_vcpu_context(xc_handle, io_fd, dom, i, &ctxt_any)) + goto out; + + /* system context of vcpu is recieved as hvm context. */ + } + + /* Set HVM-specific parameters */ + if (read_exact(io_fd, magic_pfns, sizeof(magic_pfns))) { + ERROR("error reading magic page addresses"); + goto out; + } + + /* These comms pages need to be zeroed at the start of day */ + for (i = 0; i < NR_PARAMS; i++) { + rc = xc_clear_domain_page(xc_handle, dom, magic_pfns[i]); + if (rc != 0) { + ERROR("error zeroing magic pages: %i", rc); + goto out; + } + rc = xc_set_hvm_param(xc_handle, dom, hvm_params[i], magic_pfns[i]); + if (rc != 0) { + ERROR("error setting HVM params: %i", rc); + goto out; + } + } + rc = xc_set_hvm_param(xc_handle, dom, + HVM_PARAM_STORE_EVTCHN, store_evtchn); + if (rc != 0) { + ERROR("error setting HVM params: %i", rc); + goto out; + } + rc = -1; + *store_mfn = magic_pfns[0]; + + /* Read HVM context */ + if (read_exact(io_fd, &rec_size, sizeof(rec_size))) { + ERROR("error read hvm context size!\n"); + goto out; + } + + hvm_buf = malloc(rec_size); + if (hvm_buf == NULL) { + ERROR("memory alloc for hvm context buffer failed"); + errno = ENOMEM; + goto out; + } + + if (read_exact(io_fd, hvm_buf, rec_size)) { + ERROR("error loading the HVM context"); + goto out; + } + + rc = xc_domain_hvm_setcontext(xc_handle, dom, hvm_buf, rec_size); + if (rc != 0) { + ERROR("error setting the HVM context"); + goto out; + } + + rc = 0; + +out: + if (vcpumap != NULL) + free(vcpumap); + if (hvm_buf != NULL) + free(hvm_buf); + return rc; +} + +/* + * hvm domain requires IO pages allocated when XEN_DOMCTL_arch_setup + */ +static int +xc_ia64_hvm_domain_setup(int xc_handle, uint32_t dom) +{ + int rc; + xen_pfn_t pfn_list[] = { + IO_PAGE_START >> PAGE_SHIFT, + BUFFER_IO_PAGE_START >> PAGE_SHIFT, + BUFFER_PIO_PAGE_START >> PAGE_SHIFT, + }; + unsigned long nr_pages = sizeof(pfn_list) / sizeof(pfn_list[0]); + + rc = xc_domain_memory_populate_physmap(xc_handle, dom, nr_pages, + 0, 0, &pfn_list[0]); + if (rc != 0) + PERROR("Could not allocate IO page or buffer io page.\n"); + return rc; +} + +int +xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, + unsigned int store_evtchn, unsigned long *store_mfn, + unsigned int console_evtchn, unsigned long *console_mfn, + unsigned int hvm, unsigned int pae) +{ + DECLARE_DOMCTL; + int rc = 1; + unsigned long ver; + + /* The new domain's shared-info frame number. */ + unsigned long shared_info_frame; + + struct xen_ia64_p2m_table p2m_table; + xc_ia64_p2m_init(&p2m_table); + + /* For info only */ + nr_pfns = 0; + + if ( read_exact(io_fd, &p2m_size, sizeof(unsigned long)) ) + { + ERROR("read: p2m_size"); + goto out; + } + DPRINTF("xc_linux_restore start: p2m_size = %lx\n", p2m_size); + + if (read_exact(io_fd, &ver, sizeof(unsigned long))) { + ERROR("Error when reading version"); + goto out; + } + if (ver != XC_IA64_SR_FORMAT_VER_ONE && + ver != XC_IA64_SR_FORMAT_VER_TWO && + ver != XC_IA64_SR_FORMAT_VER_THREE) { + ERROR("version of save doesn't match"); + goto out; + } + + if (read_exact(io_fd, &domctl.u.arch_setup, sizeof(domctl.u.arch_setup))) { + ERROR("read: domain setup"); + goto out; + } + + if (hvm && xc_ia64_hvm_domain_setup(xc_handle, dom) != 0) + goto out; + + /* Build firmware (will be overwritten). */ + domctl.domain = (domid_t)dom; + domctl.u.arch_setup.flags &= ~XEN_DOMAINSETUP_query; + domctl.u.arch_setup.bp = 0; /* indicate domain restore */ + + domctl.cmd = XEN_DOMCTL_arch_setup; + if (xc_domctl(xc_handle, &domctl)) + goto out; + + /* Get the domain's shared-info frame. */ + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)dom; + if (xc_domctl(xc_handle, &domctl) < 0) { + ERROR("Could not get information on new domain"); + goto out; + } + shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; + + if (ver == XC_IA64_SR_FORMAT_VER_THREE || + ver == XC_IA64_SR_FORMAT_VER_TWO) { + unsigned int memmap_info_num_pages; + unsigned long memmap_size; + xen_ia64_memmap_info_t *memmap_info; + + if (read_exact(io_fd, &memmap_info_num_pages, + sizeof(memmap_info_num_pages))) { + ERROR("read: memmap_info_num_pages"); + goto out; + } + memmap_size = memmap_info_num_pages * PAGE_SIZE; + memmap_info = malloc(memmap_size); + if (memmap_info == NULL) { + ERROR("Could not allocate memory for memmap_info"); + goto out; + } + if (read_exact(io_fd, memmap_info, memmap_size)) { + ERROR("read: memmap_info"); + goto out; + } + if (xc_ia64_p2m_map(&p2m_table, xc_handle, + dom, memmap_info, IA64_DOM0VP_EFP_ALLOC_PTE)) { + ERROR("p2m mapping"); + goto out; + } + free(memmap_info); + } else if (ver == XC_IA64_SR_FORMAT_VER_ONE) { + xen_ia64_memmap_info_t *memmap_info; + efi_memory_desc_t *memdesc; + uint64_t buffer[(sizeof(*memmap_info) + sizeof(*memdesc) + + sizeof(uint64_t) - 1) / sizeof(uint64_t)]; + + memset(buffer, 0, sizeof(buffer)); + memmap_info = (xen_ia64_memmap_info_t *)buffer; + memdesc = (efi_memory_desc_t*)&memmap_info->memdesc[0]; + memmap_info->efi_memmap_size = sizeof(*memdesc); + memmap_info->efi_memdesc_size = sizeof(*memdesc); + memmap_info->efi_memdesc_version = EFI_MEMORY_DESCRIPTOR_VERSION; + + memdesc->type = EFI_MEMORY_DESCRIPTOR_VERSION; + memdesc->phys_addr = 0; + memdesc->virt_addr = 0; + memdesc->num_pages = nr_pfns << (PAGE_SHIFT - EFI_PAGE_SHIFT); + memdesc->attribute = EFI_MEMORY_WB; + + if (xc_ia64_p2m_map(&p2m_table, xc_handle, + dom, memmap_info, IA64_DOM0VP_EFP_ALLOC_PTE)) { + ERROR("p2m mapping"); + goto out; + } + } else { + ERROR("unknown version"); + goto out; + } + + DPRINTF("Reloading memory pages: 0%%\n"); + + while (1) { + unsigned long gmfn; + if (read_exact(io_fd, &gmfn, sizeof(unsigned long))) { + ERROR("Error when reading batch size"); + goto out; + } + if (gmfn == INVALID_MFN) + break; + + if (populate_page_if_necessary(xc_handle, dom, gmfn, &p2m_table) < 0) { + ERROR("can not populate page 0x%lx", gmfn); + goto out; + } + if (read_page(xc_handle, io_fd, dom, gmfn) < 0) + goto out; + } + + DPRINTF("Received all pages\n"); + + if (xc_ia64_recv_unallocated_list(xc_handle, io_fd, dom, &p2m_table)) + goto out; + + if (!hvm) + rc = xc_ia64_pv_recv_context(ver, + xc_handle, io_fd, dom, shared_info_frame, + &p2m_table, store_evtchn, store_mfn, + console_evtchn, console_mfn); + else + rc = xc_ia64_hvm_recv_context(xc_handle, io_fd, dom, shared_info_frame, + &p2m_table, store_evtchn, store_mfn, + console_evtchn, console_mfn); + if (rc) + goto out; + + /* + * Safety checking of saved context: + * 1. user_regs is fine, as Xen checks that on context switch. + * 2. fpu_ctxt is fine, as it can't hurt Xen. + * 3. trap_ctxt needs the code selectors checked. + * 4. ldt base must be page-aligned, no more than 8192 ents, ... + * 5. gdt already done, and further checking is done by Xen. + * 6. check that kernel_ss is safe. + * 7. pt_base is already done. + * 8. debugregs are checked by Xen. + * 9. callback code selectors need checking. + */ + DPRINTF("Domain ready to be built.\n"); + + rc = 0; + + out: + xc_ia64_p2m_unmap(&p2m_table); + + if ((rc != 0) && (dom != 0)) + xc_domain_destroy(xc_handle, dom); + + DPRINTF("Restore exit with rc=%d\n", rc); + + return rc; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_ia64_linux_save.c b/tools/libxc/ia64/xc_ia64_linux_save.c new file mode 100644 index 0000000..3f19613 --- /dev/null +++ b/tools/libxc/ia64/xc_ia64_linux_save.c @@ -0,0 +1,790 @@ +/****************************************************************************** + * xc_ia64_linux_save.c + * + * Save the state of a running Linux session. + * + * Copyright (c) 2003, K A Fraser. + * Rewritten for ia64 by Tristan Gingold + * + * Copyright (c) 2007 Isaku Yamahata + * Use foreign p2m exposure. + * VTi domain support. + */ + +#include +#include +#include +#include +#include + +#include "xg_private.h" +#include "xc_ia64.h" +#include "xc_ia64_save_restore.h" +#include "xc_efi.h" +#include "xen/hvm/params.h" + +/* +** Default values for important tuning parameters. Can override by passing +** non-zero replacement values to xc_linux_save(). +** +** XXX SMH: should consider if want to be able to override MAX_MBIT_RATE too. +** +*/ +#define DEF_MAX_ITERS (4 - 1) /* limit us to 4 times round loop */ +#define DEF_MAX_FACTOR 3 /* never send more than 3x nr_pfns */ + +/* +** During (live) save/migrate, we maintain a number of bitmaps to track +** which pages we have to send, and to skip. +*/ +static inline int test_bit(int nr, volatile void * addr) +{ + return (BITMAP_ENTRY(nr, addr) >> BITMAP_SHIFT(nr)) & 1; +} + +static inline void clear_bit(int nr, volatile void * addr) +{ + BITMAP_ENTRY(nr, addr) &= ~(1UL << BITMAP_SHIFT(nr)); +} + +static inline void set_bit(int nr, volatile void * addr) +{ + BITMAP_ENTRY(nr, addr) |= (1UL << BITMAP_SHIFT(nr)); +} + +static int +suspend_and_state(int (*suspend)(void), int xc_handle, int io_fd, + int dom, xc_dominfo_t *info) +{ + if (!(*suspend)()) { + ERROR("Suspend request failed"); + return -1; + } + + if ( (xc_domain_getinfo(xc_handle, dom, 1, info) != 1) || + !info->shutdown || (info->shutdown_reason != SHUTDOWN_suspend) ) { + ERROR("Could not get domain info"); + return -1; + } + + return 0; +} + +static inline int +md_is_not_ram(const efi_memory_desc_t *md) +{ + return ((md->type != EFI_CONVENTIONAL_MEMORY) || + (md->attribute != EFI_MEMORY_WB) || + (md->num_pages == 0)); +} + +/* + * Send through a list of all the PFNs that were not in map at the close. + * We send pages which was allocated. However balloon driver may + * decreased after sending page. So we have to check the freed + * page after pausing the domain. + */ +static int +xc_ia64_send_unallocated_list(int xc_handle, int io_fd, + struct xen_ia64_p2m_table *p2m_table, + xen_ia64_memmap_info_t *memmap_info, + void *memmap_desc_start, void *memmap_desc_end) +{ + void *p; + efi_memory_desc_t *md; + + unsigned long N; + unsigned long pfntab[1024]; + unsigned int j; + + j = 0; + for (p = memmap_desc_start; + p < memmap_desc_end; + p += memmap_info->efi_memdesc_size) { + md = p; + + if (md_is_not_ram(md)) + continue; + + for (N = md->phys_addr >> PAGE_SHIFT; + N < (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >> + PAGE_SHIFT; + N++) { + if (!xc_ia64_p2m_allocated(p2m_table, N)) + j++; + } + } + if (write_exact(io_fd, &j, sizeof(unsigned int))) { + ERROR("Error when writing to state file (6a)"); + return -1; + } + + j = 0; + for (p = memmap_desc_start; + p < memmap_desc_end; + p += memmap_info->efi_memdesc_size) { + md = p; + + if (md_is_not_ram(md)) + continue; + + for (N = md->phys_addr >> PAGE_SHIFT; + N < (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >> + PAGE_SHIFT; + N++) { + if (!xc_ia64_p2m_allocated(p2m_table, N)) + pfntab[j++] = N; + if (j == sizeof(pfntab)/sizeof(pfntab[0])) { + if (write_exact(io_fd, &pfntab, sizeof(pfntab[0]) * j)) { + ERROR("Error when writing to state file (6b)"); + return -1; + } + j = 0; + } + } + } + if (j > 0) { + if (write_exact(io_fd, &pfntab, sizeof(pfntab[0]) * j)) { + ERROR("Error when writing to state file (6c)"); + return -1; + } + } + + return 0; +} + +static int +xc_ia64_send_vcpu_context(int xc_handle, int io_fd, uint32_t dom, + uint32_t vcpu, vcpu_guest_context_any_t *ctxt_any) +{ + vcpu_guest_context_t *ctxt = &ctxt_any->c; + if (xc_vcpu_getcontext(xc_handle, dom, vcpu, ctxt_any)) { + ERROR("Could not get vcpu context"); + return -1; + } + + if (write_exact(io_fd, ctxt, sizeof(*ctxt))) { + ERROR("Error when writing to state file (1)"); + return -1; + } + + fprintf(stderr, "ip=%016lx, b0=%016lx\n", ctxt->regs.ip, ctxt->regs.b[0]); + return 0; +} + +static int +xc_ia64_send_shared_info(int xc_handle, int io_fd, shared_info_t *live_shinfo) +{ + if (write_exact(io_fd, live_shinfo, PAGE_SIZE)) { + ERROR("Error when writing to state file (1)"); + return -1; + } + return 0; +} + +static int +xc_ia64_send_vcpumap(int xc_handle, int io_fd, uint32_t dom, + const xc_dominfo_t *info, uint64_t max_virt_cpus, + uint64_t **vcpumapp) +{ + int rc = -1; + unsigned int i; + unsigned long vcpumap_size; + uint64_t *vcpumap = NULL; + + vcpumap_size = (max_virt_cpus + 1 + sizeof(vcpumap[0]) - 1) / + sizeof(vcpumap[0]); + vcpumap = malloc(vcpumap_size); + if (vcpumap == NULL) { + ERROR("memory alloc for vcpumap"); + goto out; + } + memset(vcpumap, 0, vcpumap_size); + + for (i = 0; i <= info->max_vcpu_id; i++) { + xc_vcpuinfo_t vinfo; + if ((xc_vcpu_getinfo(xc_handle, dom, i, &vinfo) == 0) && vinfo.online) + __set_bit(i, vcpumap); + } + + if (write_exact(io_fd, &max_virt_cpus, sizeof(max_virt_cpus))) { + ERROR("write max_virt_cpus"); + goto out; + } + + if (write_exact(io_fd, vcpumap, vcpumap_size)) { + ERROR("write vcpumap"); + goto out; + } + + rc = 0; + + out: + if (rc != 0 && vcpumap != NULL) { + free(vcpumap); + vcpumap = NULL; + } + *vcpumapp = vcpumap; + return rc; +} + + +static int +xc_ia64_pv_send_context(int xc_handle, int io_fd, uint32_t dom, + const xc_dominfo_t *info, shared_info_t *live_shinfo) +{ + int rc = -1; + unsigned int i; + + /* vcpu map */ + uint64_t *vcpumap = NULL; + if (xc_ia64_send_vcpumap(xc_handle, io_fd, dom, info, MAX_VIRT_CPUS, + &vcpumap)) + goto out; + + /* vcpu context */ + for (i = 0; i <= info->max_vcpu_id; i++) { + /* A copy of the CPU context of the guest. */ + vcpu_guest_context_any_t ctxt_any; + vcpu_guest_context_t *ctxt = &ctxt_any.c; + + char *mem; + + if (!__test_bit(i, vcpumap)) + continue; + + if (xc_ia64_send_vcpu_context(xc_handle, io_fd, dom, i, &ctxt_any)) + goto out; + + mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ|PROT_WRITE, ctxt->privregs_pfn); + if (mem == NULL) { + ERROR("cannot map privreg page"); + goto out; + } + if (write_exact(io_fd, mem, PAGE_SIZE)) { + ERROR("Error when writing privreg to state file (5)"); + munmap(mem, PAGE_SIZE); + goto out; + } + munmap(mem, PAGE_SIZE); + } + + rc = xc_ia64_send_shared_info(xc_handle, io_fd, live_shinfo); + + out: + if (vcpumap != NULL) + free(vcpumap); + return rc; +} + +static int +xc_ia64_hvm_send_context(int xc_handle, int io_fd, uint32_t dom, + const xc_dominfo_t *info, shared_info_t *live_shinfo) +{ + int rc = -1; + unsigned int i; + + /* vcpu map */ + uint64_t *vcpumap = NULL; + + /* HVM: magic frames for ioreqs and xenstore comms */ + const int hvm_params[] = { + HVM_PARAM_STORE_PFN, + HVM_PARAM_IOREQ_PFN, + HVM_PARAM_BUFIOREQ_PFN, + HVM_PARAM_BUFPIOREQ_PFN, + }; + const int NR_PARAMS = sizeof(hvm_params) / sizeof(hvm_params[0]); + /* ioreq_pfn, bufioreq_pfn, store_pfn */ + uint64_t magic_pfns[NR_PARAMS]; + + /* HVM: a buffer for holding HVM contxt */ + uint64_t rec_size; + uint64_t hvm_buf_size = 0; + uint8_t *hvm_buf = NULL; + + if (xc_ia64_send_shared_info(xc_handle, io_fd, live_shinfo)) + return -1; + + /* vcpu map */ + if (xc_ia64_send_vcpumap(xc_handle, io_fd, dom, info, MAX_VIRT_CPUS, + &vcpumap)) + goto out; + + /* vcpu context */ + for (i = 0; i <= info->max_vcpu_id; i++) { + /* A copy of the CPU context of the guest. */ + vcpu_guest_context_any_t ctxt_any; + + if (!__test_bit(i, vcpumap)) + continue; + + if (xc_ia64_send_vcpu_context(xc_handle, io_fd, dom, i, &ctxt_any)) + goto out; + + /* system context of vcpu is sent as hvm context. */ + } + + /* Save magic-page locations. */ + memset(magic_pfns, 0, sizeof(magic_pfns)); + for (i = 0; i < NR_PARAMS; i++) { + if (xc_get_hvm_param(xc_handle, dom, hvm_params[i], &magic_pfns[i])) { + PERROR("Error when xc_get_hvm_param"); + goto out; + } + } + + if (write_exact(io_fd, magic_pfns, sizeof(magic_pfns))) { + ERROR("Error when writing to state file (7)"); + goto out; + } + + /* Need another buffer for HVM context */ + hvm_buf_size = xc_domain_hvm_getcontext(xc_handle, dom, 0, 0); + if (hvm_buf_size == -1) { + ERROR("Couldn't get HVM context size from Xen"); + goto out; + } + + hvm_buf = malloc(hvm_buf_size); + if (!hvm_buf) { + ERROR("Couldn't allocate memory"); + goto out; + } + + /* Get HVM context from Xen and save it too */ + rec_size = xc_domain_hvm_getcontext(xc_handle, dom, hvm_buf, hvm_buf_size); + if (rec_size == -1) { + ERROR("HVM:Could not get hvm buffer"); + goto out; + } + + if (write_exact(io_fd, &rec_size, sizeof(rec_size))) { + ERROR("error write hvm buffer size"); + goto out; + } + + if (write_exact(io_fd, hvm_buf, rec_size)) { + ERROR("write HVM info failed!\n"); + goto out; + } + + rc = 0; +out: + if (hvm_buf != NULL) + free(hvm_buf); + if (vcpumap != NULL) + free(vcpumap); + return rc; +} + +int +xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, + uint32_t max_factor, uint32_t flags, int (*suspend)(void), + int hvm, void *(*init_qemu_maps)(int, unsigned), + void (*qemu_flip_buffer)(int, int)) +{ + DECLARE_DOMCTL; + xc_dominfo_t info; + + int rc = 1; + + int debug = (flags & XCFLAGS_DEBUG); + int live = (flags & XCFLAGS_LIVE); + + /* The new domain's shared-info frame number. */ + unsigned long shared_info_frame; + + /* Live mapping of shared info structure */ + shared_info_t *live_shinfo = NULL; + + /* Iteration number. */ + int iter; + + /* Number of pages sent in the last iteration (live only). */ + unsigned int sent_last_iter; + + /* Number of pages sent (live only). */ + unsigned int total_sent; + + /* total number of pages used by the current guest */ + unsigned long p2m_size; + + /* Size of the shadow bitmap (live only). */ + unsigned int bitmap_size = 0; + + /* True if last iteration. */ + int last_iter; + + /* Bitmap of pages to be sent. */ + unsigned long *to_send = NULL; + /* Bitmap of pages not to be sent (because dirtied). */ + unsigned long *to_skip = NULL; + + char *mem; + + /* HVM: shared-memory bitmaps for getting log-dirty bits from qemu-dm */ + unsigned long *qemu_bitmaps[2]; + int qemu_active = 0; + int qemu_non_active = 1; + + /* for foreign p2m exposure */ + unsigned long memmap_info_num_pages; + /* Unsigned int was used before. To keep file format compatibility. */ + unsigned int memmap_info_num_pages_to_send; + unsigned long memmap_size = 0; + xen_ia64_memmap_info_t *memmap_info = NULL; + void *memmap_desc_start; + void *memmap_desc_end; + void *p; + efi_memory_desc_t *md; + struct xen_ia64_p2m_table p2m_table; + xc_ia64_p2m_init(&p2m_table); + + if (debug) + fprintf(stderr, "xc_linux_save (ia64): started dom=%d\n", dom); + + /* If no explicit control parameters given, use defaults */ + if (!max_iters) + max_iters = DEF_MAX_ITERS; + if (!max_factor) + max_factor = DEF_MAX_FACTOR; + + //initialize_mbit_rate(); + + if (xc_domain_getinfo(xc_handle, dom, 1, &info) != 1) { + ERROR("Could not get domain info"); + return 1; + } + + shared_info_frame = info.shared_info_frame; + +#if 0 + /* cheesy sanity check */ + if ((info.max_memkb >> (PAGE_SHIFT - 10)) > max_mfn) { + ERROR("Invalid state record -- pfn count out of range: %lu", + (info.max_memkb >> (PAGE_SHIFT - 10))); + goto out; + } +#endif + + /* Map the shared info frame */ + live_shinfo = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ, shared_info_frame); + if (!live_shinfo) { + ERROR("Couldn't map live_shinfo"); + goto out; + } + + p2m_size = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &dom) + 1; + + /* This is expected by xm restore. */ + if (write_exact(io_fd, &p2m_size, sizeof(unsigned long))) { + ERROR("write: p2m_size"); + goto out; + } + + /* xc_linux_restore starts to read here. */ + /* Write a version number. This can avoid searching for a stupid bug + if the format change. + The version is hard-coded, don't forget to change the restore code + too! */ + { + unsigned long version = XC_IA64_SR_FORMAT_VER_CURRENT; + + if (write_exact(io_fd, &version, sizeof(unsigned long))) { + ERROR("write: version"); + goto out; + } + } + + domctl.cmd = XEN_DOMCTL_arch_setup; + domctl.domain = (domid_t)dom; + domctl.u.arch_setup.flags = XEN_DOMAINSETUP_query; + if (xc_domctl(xc_handle, &domctl) < 0) { + ERROR("Could not get domain setup"); + goto out; + } + if (write_exact(io_fd, &domctl.u.arch_setup, + sizeof(domctl.u.arch_setup))) { + ERROR("write: domain setup"); + goto out; + } + + /* Domain is still running at this point */ + if (live) { + + if (xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, + NULL, 0, NULL, 0, NULL ) < 0) { + ERROR("Couldn't enable shadow mode"); + goto out; + } + + last_iter = 0; + + bitmap_size = ((p2m_size + BITS_PER_LONG-1) & ~(BITS_PER_LONG-1)) / 8; + to_send = malloc(bitmap_size); + to_skip = malloc(bitmap_size); + + if (!to_send || !to_skip) { + ERROR("Couldn't allocate bitmap array"); + goto out; + } + + /* Initially all the pages must be sent. */ + memset(to_send, 0xff, bitmap_size); + + if (lock_pages(to_send, bitmap_size)) { + ERROR("Unable to lock_pages to_send"); + goto out; + } + if (lock_pages(to_skip, bitmap_size)) { + ERROR("Unable to lock_pages to_skip"); + goto out; + } + + if (hvm) { + /* Get qemu-dm logging dirty pages too */ + void *seg = init_qemu_maps(dom, bitmap_size); + qemu_bitmaps[0] = seg; + qemu_bitmaps[1] = seg + bitmap_size; + qemu_active = 0; + qemu_non_active = 1; + } + } else { + + /* This is a non-live suspend. Issue the call back to get the + domain suspended */ + + last_iter = 1; + + if (suspend_and_state(suspend, xc_handle, io_fd, dom, &info)) { + ERROR("Domain appears not to have suspended"); + goto out; + } + + } + + /* copy before use in case someone updating them */ + if (xc_ia64_copy_memmap(xc_handle, info.domid, live_shinfo, + &memmap_info, &memmap_info_num_pages) != 0) { + PERROR("Could not copy memmap"); + goto out; + } + memmap_size = memmap_info_num_pages << PAGE_SHIFT; + + if (xc_ia64_p2m_map(&p2m_table, xc_handle, dom, memmap_info, 0) < 0) { + PERROR("xc_ia64_p2m_map"); + goto out; + } + memmap_info_num_pages_to_send = memmap_info_num_pages; + if (write_exact(io_fd, &memmap_info_num_pages_to_send, + sizeof(memmap_info_num_pages_to_send))) { + PERROR("write: arch.memmap_info_num_pages"); + goto out; + } + if (write_exact(io_fd, memmap_info, memmap_size)) { + PERROR("write: memmap_info"); + goto out; + } + + sent_last_iter = p2m_size; + total_sent = 0; + + for (iter = 1; ; iter++) { + unsigned int sent_this_iter, skip_this_iter; + unsigned long N; + + sent_this_iter = 0; + skip_this_iter = 0; + + /* Dirtied pages won't be saved. + slightly wasteful to peek the whole array evey time, + but this is fast enough for the moment. */ + if (!last_iter) { + if (xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_PEEK, + to_skip, p2m_size, + NULL, 0, NULL) != p2m_size) { + ERROR("Error peeking shadow bitmap"); + goto out; + } + } + + /* Start writing out the saved-domain record. */ + memmap_desc_start = &memmap_info->memdesc; + memmap_desc_end = memmap_desc_start + memmap_info->efi_memmap_size; + for (p = memmap_desc_start; + p < memmap_desc_end; + p += memmap_info->efi_memdesc_size) { + md = p; + if (md_is_not_ram(md)) + continue; + + for (N = md->phys_addr >> PAGE_SHIFT; + N < (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >> + PAGE_SHIFT; + N++) { + + if (!xc_ia64_p2m_allocated(&p2m_table, N)) + continue; + + if (!last_iter) { + if (test_bit(N, to_skip) && test_bit(N, to_send)) + skip_this_iter++; + if (test_bit(N, to_skip) || !test_bit(N, to_send)) + continue; + } else if (live) { + if (!test_bit(N, to_send)) + continue; + } + + if (debug) + fprintf(stderr, "xc_linux_save: page %lx (%lu/%lu)\n", + xc_ia64_p2m_mfn(&p2m_table, N), + N, p2m_size); + + mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ|PROT_WRITE, N); + if (mem == NULL) { + /* The page may have move. + It will be remarked dirty. + FIXME: to be tracked. */ + fprintf(stderr, "cannot map mfn page %lx gpfn %lx: %s\n", + xc_ia64_p2m_mfn(&p2m_table, N), + N, safe_strerror(errno)); + continue; + } + + if (write_exact(io_fd, &N, sizeof(N))) { + ERROR("write: p2m_size"); + munmap(mem, PAGE_SIZE); + goto out; + } + + if (write(io_fd, mem, PAGE_SIZE) != PAGE_SIZE) { + ERROR("Error when writing to state file (5)"); + munmap(mem, PAGE_SIZE); + goto out; + } + munmap(mem, PAGE_SIZE); + sent_this_iter++; + total_sent++; + } + } + + if (last_iter) + break; + + DPRINTF(" %d: sent %d, skipped %d\n", + iter, sent_this_iter, skip_this_iter ); + + if (live) { + if ( /* ((sent_this_iter > sent_last_iter) && RATE_IS_MAX()) || */ + (iter >= max_iters) || (sent_this_iter+skip_this_iter < 50) || + (total_sent > p2m_size*max_factor)) { + DPRINTF("Start last iteration\n"); + last_iter = 1; + + if (suspend_and_state(suspend, xc_handle, io_fd, dom, &info)) { + ERROR("Domain appears not to have suspended"); + goto out; + } + } + + /* Pages to be sent are pages which were dirty. */ + if (xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_CLEAN, + to_send, p2m_size, + NULL, 0, NULL ) != p2m_size) { + ERROR("Error flushing shadow PT"); + goto out; + } + + if (hvm) { + unsigned int j; + /* Pull in the dirty bits from qemu-dm too */ + if (!last_iter) { + qemu_active = qemu_non_active; + qemu_non_active = qemu_active ? 0 : 1; + qemu_flip_buffer(dom, qemu_active); + for (j = 0; j < bitmap_size / sizeof(unsigned long); j++) { + to_send[j] |= qemu_bitmaps[qemu_non_active][j]; + qemu_bitmaps[qemu_non_active][j] = 0; + } + } else { + for (j = 0; j < bitmap_size / sizeof(unsigned long); j++) + to_send[j] |= qemu_bitmaps[qemu_active][j]; + } + } + + sent_last_iter = sent_this_iter; + + //print_stats(xc_handle, dom, sent_this_iter, &stats, 1); + } + } + + fprintf(stderr, "All memory is saved\n"); + + /* terminate */ + { + unsigned long pfn = INVALID_MFN; + if (write_exact(io_fd, &pfn, sizeof(pfn))) { + ERROR("Error when writing to state file (6)"); + goto out; + } + } + + if (xc_ia64_send_unallocated_list(xc_handle, io_fd, &p2m_table, + memmap_info, + memmap_desc_start, memmap_desc_end)) + goto out; + + if (!hvm) + rc = xc_ia64_pv_send_context(xc_handle, io_fd, + dom, &info, live_shinfo); + else + rc = xc_ia64_hvm_send_context(xc_handle, io_fd, + dom, &info, live_shinfo); + if (rc) + goto out; + + /* Success! */ + rc = 0; + + out: + + if (live) { + if (xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_OFF, + NULL, 0, NULL, 0, NULL ) < 0) { + DPRINTF("Warning - couldn't disable shadow mode"); + } + } + + unlock_pages(to_send, bitmap_size); + free(to_send); + unlock_pages(to_skip, bitmap_size); + free(to_skip); + if (live_shinfo) + munmap(live_shinfo, PAGE_SIZE); + if (memmap_info) + free(memmap_info); + xc_ia64_p2m_unmap(&p2m_table); + + fprintf(stderr,"Save exit rc=%d\n",rc); + + return !!rc; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_ia64_save_restore.h b/tools/libxc/ia64/xc_ia64_save_restore.h new file mode 100644 index 0000000..cfca1cd --- /dev/null +++ b/tools/libxc/ia64/xc_ia64_save_restore.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * xc_ia64_save_restore.h + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef XC_IA64_SAVE_RESTORE_H +#define XC_IA64_SR_H + + /* introduced changeset 10692:306d7857928c of xen-ia64-unstable.ht */ +#define XC_IA64_SR_FORMAT_VER_ONE 1UL + /* using foreign p2m exposure version */ +#define XC_IA64_SR_FORMAT_VER_TWO 2UL + /* only pv change: send vcpumap and all vcpu context */ +#define XC_IA64_SR_FORMAT_VER_THREE 3UL +#define XC_IA64_SR_FORMAT_VER_MAX 3UL + +#define XC_IA64_SR_FORMAT_VER_CURRENT XC_IA64_SR_FORMAT_VER_THREE + +/* +** During (live) save/migrate, we maintain a number of bitmaps to track +** which pages we have to send, and to skip. +*/ +#define BITS_PER_LONG (sizeof(unsigned long) * 8) + +#define BITMAP_ENTRY(_nr,_bmap) \ + ((unsigned long *)(_bmap))[(_nr)/BITS_PER_LONG] + +#define BITMAP_SHIFT(_nr) ((_nr) % BITS_PER_LONG) + +static inline int __test_bit(int nr, void * addr) +{ + return (BITMAP_ENTRY(nr, addr) >> BITMAP_SHIFT(nr)) & 1; +} + +static inline void __set_bit(int nr, void * addr) +{ + BITMAP_ENTRY(nr, addr) |= (1UL << BITMAP_SHIFT(nr)); +} + +#endif /* XC_IA64_SAVE_RESTORE_H */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/ia64/xc_ia64_stubs.c b/tools/libxc/ia64/xc_ia64_stubs.c new file mode 100644 index 0000000..0dcc83d --- /dev/null +++ b/tools/libxc/ia64/xc_ia64_stubs.c @@ -0,0 +1,303 @@ +#include "xg_private.h" +#include "xc_efi.h" +#include "xc_ia64.h" + +/* this is a very ugly way of getting FPSR_DEFAULT. struct ia64_fpreg is + * mysteriously declared in two places: /usr/include/asm/fpu.h and + * /usr/include/bits/sigcontext.h. The former also defines FPSR_DEFAULT, + * the latter doesn't but is included (indirectly) by xg_private.h */ +#define __ASSEMBLY__ +#include +#undef __IA64_UL +#define __IA64_UL(x) ((unsigned long)(x)) +#undef __ASSEMBLY__ + +unsigned long +xc_ia64_fpsr_default(void) +{ + return FPSR_DEFAULT; +} + +static int +xc_ia64_get_pfn_list(int xc_handle, uint32_t domid, xen_pfn_t *pfn_buf, + unsigned int start_page, unsigned int nr_pages) +{ + DECLARE_DOMCTL; + int ret; + + domctl.cmd = XEN_DOMCTL_getmemlist; + domctl.domain = (domid_t)domid; + domctl.u.getmemlist.max_pfns = nr_pages; + domctl.u.getmemlist.start_pfn = start_page; + domctl.u.getmemlist.num_pfns = 0; + set_xen_guest_handle(domctl.u.getmemlist.buffer, pfn_buf); + + if (lock_pages(pfn_buf, nr_pages * sizeof(xen_pfn_t)) != 0) { + PERROR("Could not lock pfn list buffer"); + return -1; + } + ret = do_domctl(xc_handle, &domctl); + unlock_pages(pfn_buf, nr_pages * sizeof(xen_pfn_t)); + + return ret < 0 ? -1 : nr_pages; +} + +int +xc_get_pfn_list(int xc_handle, uint32_t domid, uint64_t *pfn_buf, + unsigned long max_pfns) +{ + return xc_ia64_get_pfn_list(xc_handle, domid, (xen_pfn_t *)pfn_buf, + 0, max_pfns); +} + +long +xc_get_max_pages(int xc_handle, uint32_t domid) +{ + struct xen_domctl domctl; + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)domid; + return ((do_domctl(xc_handle, &domctl) < 0) + ? -1 : domctl.u.getdomaininfo.max_pages); +} + +/* It is possible to get memmap_info and memmap by + foreign domain page mapping. But it's racy. Use hypercall to avoid race. */ +static int +xc_ia64_get_memmap(int xc_handle, + uint32_t domid, char *buf, unsigned long bufsize) +{ + privcmd_hypercall_t hypercall; + int ret; + + hypercall.op = __HYPERVISOR_ia64_dom0vp_op; + hypercall.arg[0] = IA64_DOM0VP_get_memmap; + hypercall.arg[1] = domid; + hypercall.arg[2] = (unsigned long)buf; + hypercall.arg[3] = bufsize; + hypercall.arg[4] = 0; + + if (lock_pages(buf, bufsize) != 0) + return -1; + ret = do_xen_hypercall(xc_handle, &hypercall); + unlock_pages(buf, bufsize); + return ret; +} + +int +xc_ia64_copy_memmap(int xc_handle, uint32_t domid, shared_info_t *live_shinfo, + xen_ia64_memmap_info_t **memmap_info_p, + unsigned long *memmap_info_num_pages_p) +{ + unsigned long gpfn_max_prev; + unsigned long gpfn_max_post; + + unsigned long num_pages; + unsigned long num_pages_post; + unsigned long memmap_size; + xen_ia64_memmap_info_t *memmap_info; + + int ret; + + gpfn_max_prev = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &domid); + if (gpfn_max_prev < 0) + return -1; + + again: + num_pages = live_shinfo->arch.memmap_info_num_pages; + if (num_pages == 0) { + ERROR("num_pages 0x%x", num_pages); + return -1; + } + + memmap_size = num_pages << PAGE_SHIFT; + memmap_info = malloc(memmap_size); + if (memmap_info == NULL) + return -1; + ret = xc_ia64_get_memmap(xc_handle, + domid, (char*)memmap_info, memmap_size); + if (ret != 0) { + free(memmap_info); + return -1; + } + xen_rmb(); + num_pages_post = live_shinfo->arch.memmap_info_num_pages; + if (num_pages != num_pages_post) { + free(memmap_info); + num_pages = num_pages_post; + goto again; + } + + gpfn_max_post = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &domid); + if (gpfn_max_prev < 0) { + free(memmap_info); + return -1; + } + if (gpfn_max_post > gpfn_max_prev) { + free(memmap_info); + gpfn_max_prev = gpfn_max_post; + goto again; + } + + /* reject unknown memmap */ + if (memmap_info->efi_memdesc_size != sizeof(efi_memory_desc_t) || + (memmap_info->efi_memmap_size / memmap_info->efi_memdesc_size) == 0 || + memmap_info->efi_memmap_size > + (num_pages << PAGE_SHIFT) - sizeof(memmap_info) || + memmap_info->efi_memdesc_version != EFI_MEMORY_DESCRIPTOR_VERSION) { + PERROR("unknown memmap header. defaulting to compat mode."); + free(memmap_info); + return -1; + } + + *memmap_info_p = memmap_info; + if (memmap_info_num_pages_p != NULL) + *memmap_info_num_pages_p = num_pages; + + return 0; +} + +/* + * XXX from xen/include/asm-ia64/linux-xen/asm/pgtable.h + * Should PTRS_PER_PTE be exported by arch-ia64.h? + */ +#define PTRS_PER_PTE (1UL << (PAGE_SHIFT - 3)) + +static void* +xc_ia64_map_foreign_p2m(int xc_handle, uint32_t dom, + struct xen_ia64_memmap_info *memmap_info, + unsigned long flags, unsigned long *p2m_size_p) +{ + unsigned long gpfn_max; + unsigned long p2m_size; + void *addr; + privcmd_hypercall_t hypercall; + int ret; + int saved_errno; + + gpfn_max = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &dom); + if (gpfn_max < 0) + return NULL; + p2m_size = + (((gpfn_max + 1) + PTRS_PER_PTE - 1) / PTRS_PER_PTE) << PAGE_SHIFT; + addr = mmap(NULL, p2m_size, PROT_READ, MAP_SHARED, xc_handle, 0); + if (addr == MAP_FAILED) + return NULL; + + hypercall.op = __HYPERVISOR_ia64_dom0vp_op; + hypercall.arg[0] = IA64_DOM0VP_expose_foreign_p2m; + hypercall.arg[1] = (unsigned long)addr; + hypercall.arg[2] = dom; + hypercall.arg[3] = (unsigned long)memmap_info; + hypercall.arg[4] = flags; + + if (lock_pages(memmap_info, + sizeof(*memmap_info) + memmap_info->efi_memmap_size) != 0) { + saved_errno = errno; + munmap(addr, p2m_size); + errno = saved_errno; + return NULL; + } + ret = do_xen_hypercall(xc_handle, &hypercall); + saved_errno = errno; + unlock_pages(memmap_info, + sizeof(*memmap_info) + memmap_info->efi_memmap_size); + if (ret < 0) { + munmap(addr, p2m_size); + errno = saved_errno; + return NULL; + } + + *p2m_size_p = p2m_size; + return addr; +} + +void +xc_ia64_p2m_init(struct xen_ia64_p2m_table *p2m_table) +{ + p2m_table->size = 0; + p2m_table->p2m = NULL; +} + +int +xc_ia64_p2m_map(struct xen_ia64_p2m_table *p2m_table, int xc_handle, + uint32_t domid, struct xen_ia64_memmap_info *memmap_info, + unsigned long flag) +{ + p2m_table->p2m = xc_ia64_map_foreign_p2m(xc_handle, domid, memmap_info, + flag, &p2m_table->size); + if (p2m_table->p2m == NULL) { + PERROR("Could not map foreign p2m. falling back to old method"); + return -1; + } + return 0; +} + +void +xc_ia64_p2m_unmap(struct xen_ia64_p2m_table *p2m_table) +{ + if (p2m_table->p2m == NULL) + return; + munmap(p2m_table->p2m, p2m_table->size); + //p2m_table->p2m = NULL; + //p2m_table->size = 0; +} + +/* + * XXX from xen/include/asm-ia64/linux-xen/asm/pgtable.h + * Should those be exported by arch-ia64.h? + */ +#define _PAGE_P_BIT 0 +#define _PAGE_P (1UL << _PAGE_P_BIT) /* page present bit */ +#define _PAGE_PGC_ALLOCATED_BIT 59 /* _PGC_allocated */ +#define _PAGE_PGC_ALLOCATED (1UL << _PAGE_PGC_ALLOCATED_BIT) +#define _PAGE_IO_BIT 60 +#define _PAGE_IO (1UL << _PAGE_IO_BIT) + +#define IA64_MAX_PHYS_BITS 50 /* max. number of physical address bits (architected) */ +#define _PAGE_PPN_MASK (((1UL << IA64_MAX_PHYS_BITS) - 1) & ~0xfffUL) + +int +xc_ia64_p2m_present(struct xen_ia64_p2m_table *p2m_table, unsigned long gpfn) +{ + if (sizeof(p2m_table->p2m[0]) * gpfn < p2m_table->size) { + unsigned long pte = p2m_table->p2m[gpfn]; + return !!((pte & _PAGE_P) && !(pte & _PAGE_IO)); + } + return 0; +} + +int +xc_ia64_p2m_allocated(struct xen_ia64_p2m_table *p2m_table, unsigned long gpfn) +{ + if (sizeof(p2m_table->p2m[0]) * gpfn < p2m_table->size) { + unsigned long pte = p2m_table->p2m[gpfn]; + return !!((pte & _PAGE_P) && (pte & _PAGE_PGC_ALLOCATED) && + !(pte & _PAGE_IO)); + } + return 0; +} + +unsigned long +xc_ia64_p2m_mfn(struct xen_ia64_p2m_table *p2m_table, unsigned long gpfn) +{ + unsigned long pte; + + if (sizeof(p2m_table->p2m[0]) * gpfn >= p2m_table->size) + return INVALID_MFN; + pte = p2m_table->p2m[gpfn]; + if (pte & _PAGE_IO) + return INVALID_MFN; + if (!(pte & _PAGE_P)) + return INVALID_MFN; + return (pte & _PAGE_PPN_MASK) >> PAGE_SHIFT; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/rpm.spec b/tools/libxc/rpm.spec new file mode 100644 index 0000000..1b4c5fc --- /dev/null +++ b/tools/libxc/rpm.spec @@ -0,0 +1,28 @@ +Summary: Xen control interface library +Name: xen-internal-library +Version: 1.2 +Release: 1 +License: Xen +Group: Xen +BuildRoot: %{staging} +%description +Library to make it easier to access the Xen control interfaces. + +%pre +%preun +%install +install -m 0755 -d $RPM_BUILD_ROOT/lib +install -m 0755 libxc.a $RPM_BUILD_ROOT/lib/libxc.a +install -m 0755 libxc.so $RPM_BUILD_ROOT/lib/libxc.so +install -m 0755 -d $RPM_BUILD_ROOT/include +install -m 0644 xc.h $RPM_BUILD_ROOT/include/xc.h +%clean +%post +%postun +%files +%defattr(-,root,root) +%dir /lib +/lib/libxc.a +/lib/libxc.so +%dir /include +/include/xc.h diff --git a/tools/libxc/xc_acm.c b/tools/libxc/xc_acm.c new file mode 100644 index 0000000..b4d89d0 --- /dev/null +++ b/tools/libxc/xc_acm.c @@ -0,0 +1,120 @@ +/****************************************************************************** + * xc_acm.c + * + * Copyright (C) 2005, 2006 IBM Corporation, R Sailer + * + * Copyright 2006 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 "xc_private.h" + +int xc_acm_op(int xc_handle, int cmd, void *arg, unsigned long arg_size) +{ + int ret; + DECLARE_HYPERCALL; + struct xen_acmctl acmctl; + + switch (cmd) { + case ACMOP_setpolicy: { + struct acm_setpolicy *setpolicy = (struct acm_setpolicy *)arg; + memcpy(&acmctl.u.setpolicy, + setpolicy, + sizeof(struct acm_setpolicy)); + } + break; + + case ACMOP_getpolicy: { + struct acm_getpolicy *getpolicy = (struct acm_getpolicy *)arg; + memcpy(&acmctl.u.getpolicy, + getpolicy, + sizeof(struct acm_getpolicy)); + } + break; + + case ACMOP_dumpstats: { + struct acm_dumpstats *dumpstats = (struct acm_dumpstats *)arg; + memcpy(&acmctl.u.dumpstats, + dumpstats, + sizeof(struct acm_dumpstats)); + } + break; + + case ACMOP_getssid: { + struct acm_getssid *getssid = (struct acm_getssid *)arg; + memcpy(&acmctl.u.getssid, + getssid, + sizeof(struct acm_getssid)); + } + break; + + case ACMOP_getdecision: { + struct acm_getdecision *getdecision = (struct acm_getdecision *)arg; + memcpy(&acmctl.u.getdecision, + getdecision, + sizeof(struct acm_getdecision)); + } + break; + + case ACMOP_chgpolicy: { + struct acm_change_policy *change_policy = (struct acm_change_policy *)arg; + memcpy(&acmctl.u.change_policy, + change_policy, + sizeof(struct acm_change_policy)); + } + break; + + case ACMOP_relabeldoms: { + struct acm_relabel_doms *relabel_doms = (struct acm_relabel_doms *)arg; + memcpy(&acmctl.u.relabel_doms, + relabel_doms, + sizeof(struct acm_relabel_doms)); + } + break; + } + + acmctl.cmd = cmd; + acmctl.interface_version = ACM_INTERFACE_VERSION; + + hypercall.op = __HYPERVISOR_xsm_op; + hypercall.arg[0] = (unsigned long)&acmctl; + if ( lock_pages(&acmctl, sizeof(acmctl)) != 0) + { + PERROR("Could not lock memory for Xen hypercall"); + return -EFAULT; + } + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0) + { + if ( errno == EACCES ) + DPRINTF("acmctl operation failed -- need to" + " rebuild the user-space tool set?\n"); + } + unlock_pages(&acmctl, sizeof(acmctl)); + + switch (cmd) { + case ACMOP_getdecision: { + struct acm_getdecision *getdecision = (struct acm_getdecision *)arg; + memcpy(getdecision, + &acmctl.u.getdecision, + sizeof(struct acm_getdecision)); + break; + } + } + + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_core.c b/tools/libxc/xc_core.c new file mode 100644 index 0000000..9ee3981 --- /dev/null +++ b/tools/libxc/xc_core.c @@ -0,0 +1,919 @@ +/* + * Elf format, (pfn, gmfn) table, IA64 support. + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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_ia64_mmapped_regs if ia64 | + * | .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_ia64_mmapped_regs if ia64 pv | + * | mmapped_regs_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) + +/* Don't yet support cross-address-size core dump */ +#define guest_width (sizeof (unsigned long)) + +/* string table */ +struct xc_core_strtab { + char *strings; + uint16_t length; + uint16_t max; +}; + +static struct xc_core_strtab* +xc_core_strtab_init(void) +{ + 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(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(void) +{ + 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(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(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(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; + ehdr->e_machine = ELF_ARCH_MACHINE; + 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(int xc_handle, + struct xen_dumpcore_elfnote_xen_version_desc + *xen_version) +{ + int rc; + memset(xen_version, 0, sizeof(*xen_version)); + + rc = xc_version(xc_handle, 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(xc_handle, XENVER_extraversion, + &xen_version->extra_version); + if ( rc < 0 ) + return rc; + + rc = xc_version(xc_handle, XENVER_compile_info, + &xen_version->compile_info); + if ( rc < 0 ) + return rc; + + rc = xc_version(xc_handle, + XENVER_capabilities, &xen_version->capabilities); + if ( rc < 0 ) + return rc; + + rc = xc_version(xc_handle, XENVER_changeset, &xen_version->changeset); + if ( rc < 0 ) + return rc; + + rc = xc_version(xc_handle, XENVER_platform_parameters, + &xen_version->platform_parameters); + if ( rc < 0 ) + return rc; + + rc = xc_version(xc_handle, 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(void *args, dumpcore_rtn_t dump_rtn) +{ + int sts; + struct elfnote elfnote; + struct xen_dumpcore_elfnote_none_desc none; + + elfnote_init(&elfnote); + memset(&none, 0, sizeof(none)); + + elfnote.descsz = sizeof(none); + elfnote.type = XEN_ELFNOTE_DUMPCORE_NONE; + sts = dump_rtn(args, (char*)&elfnote, sizeof(elfnote)); + if ( sts != 0 ) + return sts; + return dump_rtn(args, (char*)&none, sizeof(none)); +} + +static int +elfnote_dump_core_header( + 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(args, (char*)&elfnote, sizeof(elfnote)); + if ( sts != 0 ) + return sts; + return dump_rtn(args, (char*)&header, sizeof(header)); +} + +static int +elfnote_dump_xen_version(void *args, dumpcore_rtn_t dump_rtn, int xc_handle) +{ + 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(xc_handle, &xen_version); + sts = dump_rtn(args, (char*)&elfnote, sizeof(elfnote)); + if ( sts != 0 ) + return sts; + return dump_rtn(args, (char*)&xen_version, sizeof(xen_version)); +} + +static int +elfnote_dump_format_version(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(args, (char*)&elfnote, sizeof(elfnote)); + if ( sts != 0 ) + return sts; + return dump_rtn(args, (char*)&format_version, sizeof(format_version)); +} + +int +xc_domain_dumpcore_via_callback(int xc_handle, + uint32_t domid, + void *args, + dumpcore_rtn_t dump_rtn) +{ + xc_dominfo_t info; + shared_info_t *live_shinfo = NULL; + + int nr_vcpus = 0; + char *dump_mem, *dump_mem_start = NULL; + vcpu_guest_context_any_t ctxt[MAX_VIRT_CPUS]; + 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; + unsigned long p2m_size = 0; + 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; + + 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(xc_handle, domid, 1, &info) != 1 ) + { + PERROR("Could not get info for domain"); + goto out; + } + /* Map the shared info frame */ + live_shinfo = xc_map_foreign_range(xc_handle, 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; + } + + for ( i = 0; i <= info.max_vcpu_id; i++ ) + { + if ( xc_vcpu_getcontext(xc_handle, domid, i, &ctxt[nr_vcpus]) == 0 ) + { + if ( xc_core_arch_context_get(&arch_ctxt, &ctxt[nr_vcpus], + xc_handle, 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(xc_handle, &arch_ctxt, &info, + live_shinfo, &memory_map, + &nr_memory_map); + if ( sts != 0 ) + goto out; + + 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(xc_handle, &info, live_shinfo, + &p2m, &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(); + if ( strtab == NULL ) + { + PERROR("Could not allocate string table"); + goto out; + } + sheaders = xc_core_shdr_init(); + if ( sheaders == NULL ) + { + PERROR("Could not allocate section headers"); + goto out; + } + /* null section */ + shdr = xc_core_shdr_get(sheaders); + if ( shdr == NULL ) + { + PERROR("Could not get section header for null section"); + goto out; + } + + /* .shstrtab */ + shdr = xc_core_shdr_get(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(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(sheaders); + if ( shdr == NULL ) + { + PERROR("Could not get section header for note section"); + goto out; + } + sts = xc_core_shdr_set(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(sheaders); + if ( shdr == NULL ) + { + PERROR("Could not get section header for .xen_prstatus"); + goto out; + } + filesz = sizeof(ctxt[0].c) * nr_vcpus; + sts = xc_core_shdr_set(shdr, strtab, XEN_DUMPCORE_SEC_PRSTATUS, + SHT_PROGBITS, offset, filesz, + __alignof__(ctxt[0].c), sizeof(ctxt[0].c)); + if ( sts != 0 ) + goto out; + offset += filesz; + + /* arch context */ + sts = xc_core_arch_context_get_shdr(&arch_ctxt, sheaders, strtab, + &filesz, offset); + if ( sts != 0 ) + goto out; + offset += filesz; + + /* shared_info */ + if ( live_shinfo != NULL ) + { + shdr = xc_core_shdr_get(sheaders); + if ( shdr == NULL ) + { + PERROR("Could not get section header for .xen_shared_info"); + goto out; + } + filesz = PAGE_SIZE; + sts = xc_core_shdr_set(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(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(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(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(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(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; + sts = dump_rtn(args, (char*)&ehdr, sizeof(ehdr)); + if ( sts != 0 ) + goto out; + + /* section headers */ + sts = dump_rtn(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(args, dump_rtn); + if ( sts != 0 ) + goto out; + + /* elf note section: xen core header */ + sts = elfnote_dump_core_header(args, dump_rtn, &info, nr_vcpus, nr_pages); + if ( sts != 0 ) + goto out; + + /* elf note section: xen version */ + sts = elfnote_dump_xen_version(args, dump_rtn, xc_handle); + if ( sts != 0 ) + goto out; + + /* elf note section: format version */ + sts = elfnote_dump_format_version(args, dump_rtn); + if ( sts != 0 ) + goto out; + + /* prstatus: .xen_prstatus */ + sts = dump_rtn(args, (char *)&ctxt[0].c, sizeof(ctxt[0].c) * nr_vcpus); + if ( sts != 0 ) + goto out; + + if ( live_shinfo != NULL ) + { + /* shared_info: .xen_shared_info */ + sts = dump_rtn(args, (char*)live_shinfo, PAGE_SIZE); + if ( sts != 0 ) + goto out; + } + + /* arch specific context */ + sts = xc_core_arch_context_dump(&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(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 ) + { + gmfn = p2m[i]; + if ( gmfn == INVALID_P2M_ENTRY ) + 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( + xc_handle, 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( + 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(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. + */ + IPRINTF("j (%ld) != nr_pages (%ld)", j , nr_pages); + memset(dump_mem_start, 0, PAGE_SIZE); + for (; j < nr_pages; j++) { + sts = dump_rtn(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( + args, (char *)p2m_array, sizeof(p2m_array[0]) * nr_pages); + else + sts = dump_rtn( + args, (char *)pfn_array, sizeof(pfn_array[0]) * nr_pages); + if ( sts != 0 ) + goto out; + + /* elf section header string table: .shstrtab */ + sts = dump_rtn(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 ( 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(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(da->fd, 0 /* no flush */); + } + + return 0; +} + +int +xc_domain_dumpcore(int xc_handle, + uint32_t domid, + const char *corename) +{ + struct dump_args da; + int sts; + + if ( (da.fd = open(corename, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)) < 0 ) + { + PERROR("Could not open corefile %s", corename); + return -errno; + } + + sts = xc_domain_dumpcore_via_callback( + xc_handle, domid, &da, &local_file_dump); + + /* flush and discard any remaining portion of the file from cache */ + discard_file_cache(da.fd, 1/* flush first*/); + + close(da.fd); + + return sts; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_core.h b/tools/libxc/xc_core.h new file mode 100644 index 0000000..c5663e9 --- /dev/null +++ b/tools/libxc/xc_core.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef XC_CORE_H +#define XC_CORE_H + +#include "xen/version.h" +#include "xg_private.h" +#include "xen/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" + +#define XEN_DUMPCORE_SEC_IA64_MAPPED_REGS ".xen_ia64_mapped_regs" + +/* 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(struct xc_core_section_headers *sheaders); +int +xc_core_shdr_set(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(int xc_handle, + struct xc_core_arch_context *arch_ctxt, + xc_dominfo_t *info, shared_info_t *live_shinfo, + xc_core_memory_map_t **mapp, + unsigned int *nr_entries); +int xc_core_arch_map_p2m(int xc_handle, xc_dominfo_t *info, + shared_info_t *live_shinfo, xen_pfn_t **live_p2m, + unsigned long *pfnp); + + +#if defined (__i386__) || defined (__x86_64__) +# include "xc_core_x86.h" +#elif defined (__ia64__) +# include "xc_core_ia64.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-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_core_ia64.c b/tools/libxc/xc_core_ia64.c new file mode 100644 index 0000000..23e886e --- /dev/null +++ b/tools/libxc/xc_core_ia64.c @@ -0,0 +1,361 @@ +/* + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#include "xg_private.h" +#include "xc_core.h" +#include "xc_efi.h" +#include "xc_dom.h" +#include + +int +xc_core_arch_gpfn_may_present(struct xc_core_arch_context *arch_ctxt, + unsigned long pfn) +{ + if (arch_ctxt->p2m_table.p2m == NULL) + return 1; /* default to trying to map the page */ + + return xc_ia64_p2m_present(&arch_ctxt->p2m_table, pfn); +} + +static int +xc_memory_map_cmp(const void *lhs__, const void *rhs__) +{ + const struct xc_core_memory_map *lhs = + (const struct xc_core_memory_map *)lhs__; + const struct xc_core_memory_map *rhs = + (const struct xc_core_memory_map *)rhs__; + + if (lhs->addr < rhs->addr) + return -1; + if (lhs->addr > rhs->addr) + return 1; + + /* memory map overlap isn't allowed. complain */ + DPRINTF("duplicated addresses are detected " + "(0x%" PRIx64 ", 0x%" PRIx64 "), " + "(0x%" PRIx64 ", 0x%" PRIx64 ")\n", + lhs->addr, lhs->size, rhs->addr, rhs->size); + return 0; +} + +int +xc_core_arch_auto_translated_physmap(const xc_dominfo_t *info) +{ + /* + * on ia64, both paravirtualize domain and hvm domain are + * auto_translated_physmap mode + */ + return 1; +} + +/* see setup_guest() @ xc_linux_build.c */ +static int +memory_map_get_old_domu(int xc_handle, xc_dominfo_t *info, + shared_info_t *live_shinfo, + xc_core_memory_map_t **mapp, unsigned int *nr_entries) +{ + xc_core_memory_map_t *map = NULL; + + map = malloc(sizeof(*map)); + if ( map == NULL ) + { + PERROR("Could not allocate memory"); + goto out; + } + + map->addr = 0; + map->size = info->max_memkb * 1024; + + *mapp = map; + *nr_entries = 1; + return 0; + +out: + if ( map != NULL ) + free(map); + return -1; +} + +/* see setup_guest() @ xc_ia64_hvm_build.c */ +static int +memory_map_get_old_hvm(int xc_handle, xc_dominfo_t *info, + shared_info_t *live_shinfo, + xc_core_memory_map_t **mapp, unsigned int *nr_entries) +{ + const xc_core_memory_map_t gfw_map[] = { + {IO_PAGE_START, IO_PAGE_SIZE}, + {STORE_PAGE_START, STORE_PAGE_SIZE}, + {BUFFER_IO_PAGE_START, BUFFER_IO_PAGE_SIZE}, + {BUFFER_PIO_PAGE_START, BUFFER_PIO_PAGE_SIZE}, + {GFW_START, GFW_SIZE}, + }; + const unsigned int nr_gfw_map = sizeof(gfw_map)/sizeof(gfw_map[0]); + xc_core_memory_map_t *map = NULL; + unsigned int i; + +#define VGA_IO_END (VGA_IO_START + VGA_IO_SIZE) + /* [0, VGA_IO_START) [VGA_IO_END, 3GB), [4GB, ...) + gfw_map */ + map = malloc((3 + nr_gfw_map) * sizeof(*map)); + if ( map == NULL ) + { + PERROR("Could not allocate memory"); + goto out; + } + + for ( i = 0; i < nr_gfw_map; i++ ) + map[i] = gfw_map[i]; + map[i].addr = 0; + map[i].size = info->max_memkb * 1024; + i++; + if ( map[i - 1].size < VGA_IO_END ) + { + map[i - 1].size = VGA_IO_START; + } + else + { + map[i].addr = VGA_IO_END; + map[i].size = map[i - 1].size - VGA_IO_END; + map[i - 1].size = VGA_IO_START; + i++; + if ( map[i - 1].addr + map[i - 1].size > MMIO_START ) + { + map[i].addr = MMIO_START + 1 * MEM_G; + map[i].size = map[i - 1].addr + map[i - 1].size - MMIO_START; + map[i - 1].size = MMIO_START - map[i - 1].addr; + i++; + } + } + *mapp = map; + *nr_entries = i; + qsort(map, *nr_entries, sizeof(map[0]), &xc_memory_map_cmp); + return 0; + +out: + if ( map != NULL ) + free(map); + return -1; +} + +static int +memory_map_get_old(int xc_handle, xc_dominfo_t *info, + shared_info_t *live_shinfo, + xc_core_memory_map_t **mapp, unsigned int *nr_entries) +{ + if ( info->hvm ) + return memory_map_get_old_hvm(xc_handle, info, live_shinfo, + mapp, nr_entries); + if ( live_shinfo == NULL ) + return -1; + return memory_map_get_old_domu(xc_handle, info, live_shinfo, + mapp, nr_entries); +} + +int +xc_core_arch_memory_map_get(int xc_handle, + struct xc_core_arch_context *arch_ctxt, + xc_dominfo_t *info, shared_info_t *live_shinfo, + xc_core_memory_map_t **mapp, + unsigned int *nr_entries) +{ + int ret = -1; + + xen_ia64_memmap_info_t *memmap_info = NULL; + xc_core_memory_map_t *map; + char *start; + char *end; + char *p; + efi_memory_desc_t *md; + + if ( live_shinfo == NULL ) + { + ERROR("can't access shared info"); + goto old; + } + + /* copy before use in case someone updating them */ + if (xc_ia64_copy_memmap(xc_handle, info->domid, live_shinfo, &memmap_info, + NULL)) { + goto old; + } + + *nr_entries = memmap_info->efi_memmap_size / memmap_info->efi_memdesc_size; + map = malloc(*nr_entries * sizeof(*md)); + if ( map == NULL ) + { + PERROR("Could not allocate memory for memmap."); + free(memmap_info); + return -1; + } + *mapp = map; + + *nr_entries = 0; + start = (char*)&memmap_info->memdesc; + end = start + memmap_info->efi_memmap_size; + for ( p = start; p < end; p += memmap_info->efi_memdesc_size ) + { + md = (efi_memory_desc_t*)p; + if ( md->type != EFI_CONVENTIONAL_MEMORY || + md->attribute != EFI_MEMORY_WB || + md->num_pages == 0 ) + continue; + + map[*nr_entries].addr = md->phys_addr; + map[*nr_entries].size = md->num_pages << EFI_PAGE_SHIFT; + (*nr_entries)++; + } + ret = 0; + + xc_ia64_p2m_map(&arch_ctxt->p2m_table, xc_handle, info->domid, + memmap_info, 0); + if ( memmap_info != NULL ) + free(memmap_info); + qsort(map, *nr_entries, sizeof(map[0]), &xc_memory_map_cmp); + return ret; + +old: + DPRINTF("Falling back old method.\n"); + return memory_map_get_old(xc_handle, info, live_shinfo, mapp, nr_entries); +} + +int +xc_core_arch_map_p2m(int xc_handle, xc_dominfo_t *info, + shared_info_t *live_shinfo, xen_pfn_t **live_p2m, + unsigned long *pfnp) +{ + /* + * on ia64, both paravirtualize domain and hvm domain are + * auto_translated_physmap mode + */ + errno = ENOSYS; + return -1; +} + +void +xc_core_arch_context_init(struct xc_core_arch_context* arch_ctxt) +{ + int i; + + arch_ctxt->mapped_regs_size = + (XMAPPEDREGS_SIZE < PAGE_SIZE) ? PAGE_SIZE: XMAPPEDREGS_SIZE; + arch_ctxt->nr_vcpus = 0; + for ( i = 0; i < MAX_VIRT_CPUS; i++ ) + arch_ctxt->mapped_regs[i] = NULL; + + xc_ia64_p2m_init(&arch_ctxt->p2m_table); +} + +void +xc_core_arch_context_free(struct xc_core_arch_context* arch_ctxt) +{ + int i; + for ( i = 0; i < arch_ctxt->nr_vcpus; i++ ) + if ( arch_ctxt->mapped_regs[i] != NULL ) + munmap(arch_ctxt->mapped_regs[i], arch_ctxt->mapped_regs_size); + xc_ia64_p2m_unmap(&arch_ctxt->p2m_table); +} + +int +xc_core_arch_context_get(struct xc_core_arch_context* arch_ctxt, + vcpu_guest_context_any_t* ctxt_any, + int xc_handle, uint32_t domid) +{ + vcpu_guest_context_t *ctxt = &ctxt_any->c; + mapped_regs_t* mapped_regs; + + if ( ctxt->privregs_pfn == VGC_PRIVREGS_HVM ) + return 0; /* VTi domain case */ + + if ( ctxt->privregs_pfn == INVALID_P2M_ENTRY ) + { + PERROR("Could not get mmapped privregs gmfn"); + errno = ENOENT; + return -1; + } + mapped_regs = xc_map_foreign_range(xc_handle, domid, + arch_ctxt->mapped_regs_size, + PROT_READ, ctxt->privregs_pfn); + if ( mapped_regs == NULL ) + { + PERROR("Could not map mapped privregs"); + return -1; + } + arch_ctxt->mapped_regs[arch_ctxt->nr_vcpus] = mapped_regs; + arch_ctxt->nr_vcpus++; + return 0; +} + +int +xc_core_arch_context_get_shdr(struct xc_core_arch_context *arch_ctxt, + struct xc_core_section_headers *sheaders, + struct xc_core_strtab *strtab, + uint64_t *filesz, uint64_t offset) +{ + int sts = -1; + Elf64_Shdr *shdr; + + if ( arch_ctxt->nr_vcpus == 0 ) + { + /* VTi domain case */ + *filesz = 0; + return 0; + } + + /* mmapped priv regs */ + shdr = xc_core_shdr_get(sheaders); + if ( shdr == NULL ) + { + PERROR("Could not get section header for .xen_ia64_mapped_regs"); + return sts; + } + *filesz = arch_ctxt->mapped_regs_size * arch_ctxt->nr_vcpus; + sts = xc_core_shdr_set(shdr, strtab, XEN_DUMPCORE_SEC_IA64_MAPPED_REGS, + SHT_PROGBITS, offset, *filesz, + __alignof__(*arch_ctxt->mapped_regs[0]), + arch_ctxt->mapped_regs_size); + return sts; +} + +int +xc_core_arch_context_dump(struct xc_core_arch_context* arch_ctxt, + void* args, dumpcore_rtn_t dump_rtn) +{ + int sts = 0; + int i; + + /* ia64 mapped_regs: .xen_ia64_mapped_regs */ + for ( i = 0; i < arch_ctxt->nr_vcpus; i++ ) + { + sts = dump_rtn(args, (char*)arch_ctxt->mapped_regs[i], + arch_ctxt->mapped_regs_size); + if ( sts != 0 ) + break; + } + return sts; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_core_ia64.h b/tools/libxc/xc_core_ia64.h new file mode 100644 index 0000000..75dd40f --- /dev/null +++ b/tools/libxc/xc_core_ia64.h @@ -0,0 +1,68 @@ +/* + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef XC_CORE_IA64_H +#define XC_CORE_IA64_H + +#include "ia64/xc_ia64.h" + +#define ELF_ARCH_DATA ELFDATA2LSB +#define ELF_ARCH_MACHINE EM_IA_64 + +struct xc_core_arch_context { + size_t mapped_regs_size; + int nr_vcpus; + mapped_regs_t* mapped_regs[MAX_VIRT_CPUS]; + + struct xen_ia64_p2m_table p2m_table; +}; + +void +xc_core_arch_context_init(struct xc_core_arch_context* arch_ctxt); +void +xc_core_arch_context_free(struct xc_core_arch_context* arch_ctxt); +int +xc_core_arch_context_get(struct xc_core_arch_context* arch_ctxt, + vcpu_guest_context_any_t* ctxt, + int xc_handle, uint32_t domid); +int +xc_core_arch_context_get_shdr(struct xc_core_arch_context* arch_ctxt, + struct xc_core_section_headers *sheaders, + struct xc_core_strtab *strtab, + uint64_t *filesz, uint64_t offset); +int +xc_core_arch_context_dump(struct xc_core_arch_context* arch_ctxt, + void* args, dumpcore_rtn_t dump_rtn); + +int +xc_core_arch_gpfn_may_present(struct xc_core_arch_context *arch_ctxt, + unsigned long pfn); + +#endif /* XC_CORE_IA64_H */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_core_x86.c b/tools/libxc/xc_core_x86.c new file mode 100644 index 0000000..d9eaa49 --- /dev/null +++ b/tools/libxc/xc_core_x86.c @@ -0,0 +1,137 @@ +/* + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#include "xg_private.h" +#include "xc_core.h" + +/* Don't yet support cross-address-size core dump */ +#define guest_width (sizeof (unsigned long)) + +static int nr_gpfns(int xc_handle, domid_t domid) +{ + return xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &domid) + 1; +} + +int +xc_core_arch_auto_translated_physmap(const xc_dominfo_t *info) +{ + return info->hvm; +} + +int +xc_core_arch_memory_map_get(int xc_handle, struct xc_core_arch_context *unused, + xc_dominfo_t *info, shared_info_t *live_shinfo, + xc_core_memory_map_t **mapp, + unsigned int *nr_entries) +{ + unsigned long p2m_size = nr_gpfns(xc_handle, info->domid); + xc_core_memory_map_t *map; + + 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; +} + +int +xc_core_arch_map_p2m(int xc_handle, xc_dominfo_t *info, + shared_info_t *live_shinfo, xen_pfn_t **live_p2m, + unsigned long *pfnp) +{ + /* 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; + uint32_t dom = info->domid; + unsigned long p2m_size = nr_gpfns(xc_handle, info->domid); + int ret = -1; + int err; + + if ( p2m_size < info->nr_pages ) + { + ERROR("p2m_size < nr_pages -1 (%lx < %lx", p2m_size, info->nr_pages - 1); + goto out; + } + + live_p2m_frame_list_list = + xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, + 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; + } + + live_p2m_frame_list = + xc_map_foreign_pages(xc_handle, dom, PROT_READ, + live_p2m_frame_list_list, + P2M_FLL_ENTRIES); + + if ( !live_p2m_frame_list ) + { + PERROR("Couldn't map p2m_frame_list"); + goto out; + } + + *live_p2m = xc_map_foreign_pages(xc_handle, dom, PROT_READ, + live_p2m_frame_list, + P2M_FL_ENTRIES); + + if ( !*live_p2m ) + { + PERROR("Couldn't map p2m table"); + goto out; + } + + *pfnp = 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); + + errno = err; + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_core_x86.h b/tools/libxc/xc_core_x86.h new file mode 100644 index 0000000..6e3490b --- /dev/null +++ b/tools/libxc/xc_core_x86.h @@ -0,0 +1,65 @@ +/* + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef XC_CORE_X86_H +#define XC_CORE_X86_H + +#if defined(__i386__) || defined(__x86_64__) +#define ELF_ARCH_DATA ELFDATA2LSB +#if defined (__i386__) +# define ELF_ARCH_MACHINE EM_386 +#else +# define ELF_ARCH_MACHINE EM_X86_64 +#endif +#endif /* __i386__ or __x86_64__ */ + + +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, xc_handle, domid) \ + (0) +#define xc_core_arch_context_dump(arch_ctxt, args, dump_rtn) (0) +#define xc_core_arch_gpfn_may_present(arch_ctxt, i) (1) + +static inline int +xc_core_arch_context_get_shdr(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-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_cpu_hotplug.c b/tools/libxc/xc_cpu_hotplug.c new file mode 100644 index 0000000..4f68823 --- /dev/null +++ b/tools/libxc/xc_cpu_hotplug.c @@ -0,0 +1,53 @@ +/****************************************************************************** + * xc_cpu_hotplug.c - Libxc API for Xen Physical CPU hotplug Management + * + * Copyright (c) 2008, Shan Haitao + * + * 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 "xc_private.h" + +int xc_cpu_online(int xc_handle, 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(xc_handle, &sysctl); + + return ret; +} + +int xc_cpu_offline(int xc_handle, 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(xc_handle, &sysctl); + + return ret; +} + diff --git a/tools/libxc/xc_cpufeature.h b/tools/libxc/xc_cpufeature.h new file mode 100644 index 0000000..6cd442c --- /dev/null +++ b/tools/libxc/xc_cpufeature.h @@ -0,0 +1,115 @@ +#ifndef __LIBXC_CPUFEATURE_H +#define __LIBXC_CPUFEATURE_H + +/* Intel-defined CPU features, CPUID level 0x00000001 (edx), word 0 */ +#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */ +#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */ +#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */ +#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */ +#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */ +#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */ +#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */ +#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */ +#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */ +#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */ +#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */ +#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */ +#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */ +#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */ +#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ +#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */ +#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */ +#define X86_FEATURE_PN (0*32+18) /* Processor serial number */ +#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */ +#define X86_FEATURE_DS (0*32+21) /* Debug Store */ +#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */ +#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ +#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */ + /* of FPU context), and CR4.OSFXSR available */ +#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ +#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */ +#define X86_FEATURE_HT (0*32+28) /* Hyper-Threading */ +#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */ +#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */ +#define X86_FEATURE_PBE (0*32+31) /* Pending Break Enable */ + +/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */ +/* Don't duplicate feature flags which are redundant with Intel! */ +#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */ +#define X86_FEATURE_MP (1*32+19) /* MP Capable. */ +#define X86_FEATURE_NX (1*32+20) /* Execute Disable */ +#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ +#define X86_FEATURE_FFXSR (1*32+25) /* FFXSR instruction optimizations */ +#define X86_FEATURE_PAGE1GB (1*32+26) /* 1Gb large page support */ +#define X86_FEATURE_RDTSCP (1*32+27) /* RDTSCP */ +#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */ +#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */ +#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */ + +/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */ +#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */ +#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */ +#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */ + +/* Other features, Linux-defined mapping, word 3 */ +/* This range is used for feature bits which conflict or are synthesized */ +#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */ +#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */ +#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */ +#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */ +/* cpu types for specific tunings: */ +#define X86_FEATURE_K8 (3*32+ 4) /* Opteron, Athlon64 */ +#define X86_FEATURE_K7 (3*32+ 5) /* Athlon */ +#define X86_FEATURE_P3 (3*32+ 6) /* P3 */ +#define X86_FEATURE_P4 (3*32+ 7) /* P4 */ +#define X86_FEATURE_CONSTANT_TSC (3*32+ 8) /* TSC ticks at a constant rate */ + +/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ +#define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ +#define X86_FEATURE_DTES64 (4*32+ 2) /* 64-bit Debug Store */ +#define X86_FEATURE_MWAIT (4*32+ 3) /* Monitor/Mwait support */ +#define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */ +#define X86_FEATURE_VMXE (4*32+ 5) /* Virtual Machine Extensions */ +#define X86_FEATURE_SMXE (4*32+ 6) /* Safer Mode Extensions */ +#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ +#define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */ +#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental Streaming SIMD Extensions-3 */ +#define X86_FEATURE_CID (4*32+10) /* Context ID */ +#define X86_FEATURE_CX16 (4*32+13) /* CMPXCHG16B */ +#define X86_FEATURE_XTPR (4*32+14) /* Send Task Priority Messages */ +#define X86_FEATURE_PDCM (4*32+15) /* Perf/Debug Capability MSR */ +#define X86_FEATURE_DCA (4*32+18) /* Direct Cache Access */ +#define X86_FEATURE_SSE4_1 (4*32+19) /* Streaming SIMD Extensions 4.1 */ +#define X86_FEATURE_SSE4_2 (4*32+20) /* Streaming SIMD Extensions 4.2 */ +#define X86_FEATURE_POPCNT (4*32+23) /* POPCNT instruction */ + +/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */ +#define X86_FEATURE_XSTORE (5*32+ 2) /* on-CPU RNG present (xstore insn) */ +#define X86_FEATURE_XSTORE_EN (5*32+ 3) /* on-CPU RNG enabled */ +#define X86_FEATURE_XCRYPT (5*32+ 6) /* on-CPU crypto (xcrypt insn) */ +#define X86_FEATURE_XCRYPT_EN (5*32+ 7) /* on-CPU crypto enabled */ +#define X86_FEATURE_ACE2 (5*32+ 8) /* Advanced Cryptography Engine v2 */ +#define X86_FEATURE_ACE2_EN (5*32+ 9) /* ACE v2 enabled */ +#define X86_FEATURE_PHE (5*32+ 10) /* PadLock Hash Engine */ +#define X86_FEATURE_PHE_EN (5*32+ 11) /* PHE enabled */ +#define X86_FEATURE_PMM (5*32+ 12) /* PadLock Montgomery Multiplier */ +#define X86_FEATURE_PMM_EN (5*32+ 13) /* PMM enabled */ + +/* More extended AMD flags: CPUID level 0x80000001, ecx, word 6 */ +#define X86_FEATURE_LAHF_LM (6*32+ 0) /* LAHF/SAHF in long mode */ +#define X86_FEATURE_CMP_LEGACY (6*32+ 1) /* If yes HyperThreading not valid */ +#define X86_FEATURE_SVME (6*32+ 2) /* Secure Virtual Machine */ +#define X86_FEATURE_EXTAPICSPACE (6*32+ 3) /* Extended APIC space */ +#define X86_FEATURE_ALTMOVCR (6*32+ 4) /* LOCK MOV CR accesses CR+8 */ +#define X86_FEATURE_ABM (6*32+ 5) /* Advanced Bit Manipulation */ +#define X86_FEATURE_SSE4A (6*32+ 6) /* AMD Streaming SIMD Extensions-4a */ +#define X86_FEATURE_MISALIGNSSE (6*32+ 7) /* Misaligned SSE Access */ +#define X86_FEATURE_3DNOWPF (6*32+ 8) /* 3DNow! Prefetch */ +#define X86_FEATURE_OSVW (6*32+ 9) /* OS Visible Workaround */ +#define X86_FEATURE_IBS (6*32+ 10) /* Instruction Based Sampling */ +#define X86_FEATURE_SSE5 (6*32+ 11) /* AMD Streaming SIMD Extensions-5 */ +#define X86_FEATURE_SKINIT (6*32+ 12) /* SKINIT, STGI/CLGI, DEV */ +#define X86_FEATURE_WDT (6*32+ 13) /* Watchdog Timer */ + +#endif /* __LIBXC_CPUFEATURE_H */ diff --git a/tools/libxc/xc_cpuid_x86.c b/tools/libxc/xc_cpuid_x86.c new file mode 100644 index 0000000..6a8e759 --- /dev/null +++ b/tools/libxc/xc_cpuid_x86.c @@ -0,0 +1,575 @@ +/****************************************************************************** + * xc_cpuid_x86.c + * + * Compute cpuid of a domain. + * + * Copyright (c) 2008, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include "xc_private.h" +#include "xc_cpufeature.h" +#include + +#define bitmaskof(idx) (1u << ((idx) & 31)) +#define clear_bit(idx, dst) ((dst) &= ~(1u << ((idx) & 31))) +#define set_bit(idx, dst) ((dst) |= (1u << ((idx) & 31))) + +#define DEF_MAX_BASE 0x00000004u +#define DEF_MAX_EXT 0x80000008u + +static int hypervisor_is_64bit(int xc) +{ + xen_capabilities_info_t xen_caps = ""; + return ((xc_version(xc, XENVER_capabilities, &xen_caps) == 0) && + (strstr(xen_caps, "x86_64") != NULL)); +} + +static void cpuid(const unsigned int *input, unsigned int *regs) +{ + unsigned int count = (input[1] == XEN_CPUID_INPUT_UNUSED) ? 0 : input[1]; + asm ( +#ifdef __i386__ + "push %%ebx; cpuid; mov %%ebx,%1; pop %%ebx" +#else + "push %%rbx; cpuid; mov %%ebx,%1; pop %%rbx" +#endif + : "=a" (regs[0]), "=r" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) + : "0" (input[0]), "2" (count) ); +} + +/* Get the manufacturer brand name of the host processor. */ +static void xc_cpuid_brand_get(char *str) +{ + unsigned int input[2] = { 0, 0 }; + unsigned int regs[4]; + + cpuid(input, regs); + + *(uint32_t *)(str + 0) = regs[1]; + *(uint32_t *)(str + 4) = regs[3]; + *(uint32_t *)(str + 8) = regs[2]; + str[12] = '\0'; +} + +static void amd_xc_cpuid_policy( + int xc, domid_t domid, const unsigned int *input, unsigned int *regs, + int is_pae) +{ + switch ( input[0] ) + { + case 0x00000001: + /* Mask Intel-only features. */ + regs[2] &= ~(bitmaskof(X86_FEATURE_SSSE3) | + bitmaskof(X86_FEATURE_SSE4_1) | + bitmaskof(X86_FEATURE_SSE4_2)); + break; + + case 0x00000002: + case 0x00000004: + regs[0] = regs[1] = regs[2] = 0; + break; + + case 0x80000001: { + int is_64bit = hypervisor_is_64bit(xc) && is_pae; + + if ( !is_pae ) + clear_bit(X86_FEATURE_PAE, regs[3]); + clear_bit(X86_FEATURE_PSE36, regs[3]); + + /* Filter all other features according to a whitelist. */ + regs[2] &= ((is_64bit ? bitmaskof(X86_FEATURE_LAHF_LM) : 0) | + bitmaskof(X86_FEATURE_CMP_LEGACY) | + bitmaskof(X86_FEATURE_ALTMOVCR) | + bitmaskof(X86_FEATURE_ABM) | + bitmaskof(X86_FEATURE_SSE4A) | + bitmaskof(X86_FEATURE_MISALIGNSSE) | + bitmaskof(X86_FEATURE_3DNOWPF)); + regs[3] &= (0x0183f3ff | /* features shared with 0x00000001:EDX */ + (is_pae ? bitmaskof(X86_FEATURE_NX) : 0) | + (is_64bit ? bitmaskof(X86_FEATURE_LM) : 0) | + bitmaskof(X86_FEATURE_SYSCALL) | + bitmaskof(X86_FEATURE_MP) | + bitmaskof(X86_FEATURE_MMXEXT) | + bitmaskof(X86_FEATURE_FFXSR) | + bitmaskof(X86_FEATURE_3DNOW) | + bitmaskof(X86_FEATURE_3DNOWEXT)); + 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] & 0xf000u) + 1) | ((regs[2] & 0xffu) << 1) | 1u; + break; + } +} + +static void intel_xc_cpuid_policy( + int xc, domid_t domid, const unsigned int *input, unsigned int *regs, + int is_pae) +{ + switch ( input[0] ) + { + case 0x00000001: + /* Mask AMD-only features. */ + regs[2] &= ~(bitmaskof(X86_FEATURE_POPCNT)); + break; + + 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 0x80000001: { + int is_64bit = hypervisor_is_64bit(xc) && is_pae; + + /* Only a few features are advertised in Intel's 0x80000001. */ + regs[2] &= (is_64bit ? bitmaskof(X86_FEATURE_LAHF_LM) : 0); + regs[3] &= ((is_pae ? bitmaskof(X86_FEATURE_NX) : 0) | + (is_64bit ? bitmaskof(X86_FEATURE_LM) : 0) | + (is_64bit ? bitmaskof(X86_FEATURE_SYSCALL) : 0)); + 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( + int xc, domid_t domid, const unsigned int *input, unsigned int *regs) +{ + char brand[13]; + unsigned long pae; + int is_pae; + + xc_get_hvm_param(xc, domid, HVM_PARAM_PAE_ENABLED, &pae); + is_pae = !!pae; + + 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] &= (bitmaskof(X86_FEATURE_XMM3) | + bitmaskof(X86_FEATURE_SSSE3) | + bitmaskof(X86_FEATURE_CX16) | + bitmaskof(X86_FEATURE_SSE4_1) | + bitmaskof(X86_FEATURE_SSE4_2) | + bitmaskof(X86_FEATURE_POPCNT)); + + regs[3] &= (bitmaskof(X86_FEATURE_FPU) | + bitmaskof(X86_FEATURE_VME) | + bitmaskof(X86_FEATURE_DE) | + bitmaskof(X86_FEATURE_PSE) | + bitmaskof(X86_FEATURE_TSC) | + bitmaskof(X86_FEATURE_MSR) | + bitmaskof(X86_FEATURE_PAE) | + bitmaskof(X86_FEATURE_MCE) | + bitmaskof(X86_FEATURE_CX8) | + bitmaskof(X86_FEATURE_APIC) | + bitmaskof(X86_FEATURE_SEP) | + bitmaskof(X86_FEATURE_MTRR) | + bitmaskof(X86_FEATURE_PGE) | + bitmaskof(X86_FEATURE_MCA) | + bitmaskof(X86_FEATURE_CMOV) | + bitmaskof(X86_FEATURE_PAT) | + bitmaskof(X86_FEATURE_CLFLSH) | + bitmaskof(X86_FEATURE_MMX) | + bitmaskof(X86_FEATURE_FXSR) | + bitmaskof(X86_FEATURE_XMM) | + bitmaskof(X86_FEATURE_XMM2) | + bitmaskof(X86_FEATURE_HT)); + + /* We always support MTRR MSRs. */ + regs[3] |= bitmaskof(X86_FEATURE_MTRR); + + if ( !is_pae ) + clear_bit(X86_FEATURE_PAE, regs[3]); + break; + + case 0x80000000: + if ( regs[0] > DEF_MAX_EXT ) + regs[0] = DEF_MAX_EXT; + break; + + case 0x80000001: + if ( !is_pae ) + clear_bit(X86_FEATURE_NX, regs[3]); + break; + + + case 0x80000008: + regs[0] &= 0x0000ffffu; + regs[1] = regs[3] = 0; + break; + + case 0x00000002: /* Intel cache info (dumped by AMD policy) */ + case 0x00000004: /* Intel cache info (dumped by AMD policy) */ + 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 */ + break; + + default: + regs[0] = regs[1] = regs[2] = regs[3] = 0; + break; + } + + xc_cpuid_brand_get(brand); + if ( strstr(brand, "AMD") ) + amd_xc_cpuid_policy(xc, domid, input, regs, is_pae); + else + intel_xc_cpuid_policy(xc, domid, input, regs, is_pae); + +} + +static void xc_cpuid_pv_policy( + int xc, domid_t domid, const unsigned int *input, unsigned int *regs) +{ + DECLARE_DOMCTL; + int guest_64bit, xen_64bit = hypervisor_is_64bit(xc); + char brand[13]; + + xc_cpuid_brand_get(brand); + + memset(&domctl, 0, sizeof(domctl)); + domctl.domain = domid; + domctl.cmd = XEN_DOMCTL_get_address_size; + do_domctl(xc, &domctl); + guest_64bit = (domctl.u.address_size.size == 64); + + if ( (input[0] & 0x7fffffff) == 1 ) + { + clear_bit(X86_FEATURE_VME, regs[3]); + clear_bit(X86_FEATURE_PSE, regs[3]); + clear_bit(X86_FEATURE_PGE, regs[3]); + clear_bit(X86_FEATURE_MCE, regs[3]); + clear_bit(X86_FEATURE_MCA, regs[3]); + clear_bit(X86_FEATURE_MTRR, regs[3]); + clear_bit(X86_FEATURE_PSE36, regs[3]); + } + + switch ( input[0] ) + { + case 1: + if ( !xen_64bit || strstr(brand, "AMD") ) + clear_bit(X86_FEATURE_SEP, regs[3]); + clear_bit(X86_FEATURE_DS, regs[3]); + clear_bit(X86_FEATURE_ACC, regs[3]); + clear_bit(X86_FEATURE_PBE, regs[3]); + + clear_bit(X86_FEATURE_DTES64, regs[2]); + clear_bit(X86_FEATURE_MWAIT, regs[2]); + clear_bit(X86_FEATURE_DSCPL, regs[2]); + clear_bit(X86_FEATURE_VMXE, regs[2]); + clear_bit(X86_FEATURE_SMXE, regs[2]); + clear_bit(X86_FEATURE_EST, regs[2]); + clear_bit(X86_FEATURE_TM2, regs[2]); + if ( !guest_64bit ) + clear_bit(X86_FEATURE_CX16, regs[2]); + clear_bit(X86_FEATURE_XTPR, regs[2]); + clear_bit(X86_FEATURE_PDCM, regs[2]); + clear_bit(X86_FEATURE_DCA, regs[2]); + break; + case 0x80000001: + if ( !guest_64bit ) + { + clear_bit(X86_FEATURE_LM, regs[3]); + clear_bit(X86_FEATURE_LAHF_LM, regs[2]); + if ( !strstr(brand, "AMD") ) + clear_bit(X86_FEATURE_SYSCALL, regs[3]); + } + else + { + set_bit(X86_FEATURE_SYSCALL, regs[3]); + } + clear_bit(X86_FEATURE_PAGE1GB, regs[3]); + clear_bit(X86_FEATURE_RDTSCP, regs[3]); + + clear_bit(X86_FEATURE_SVME, regs[2]); + clear_bit(X86_FEATURE_OSVW, regs[2]); + clear_bit(X86_FEATURE_IBS, regs[2]); + clear_bit(X86_FEATURE_SKINIT, regs[2]); + clear_bit(X86_FEATURE_WDT, regs[2]); + break; + case 5: /* MONITOR/MWAIT */ + case 0xa: /* Architectural Performance Monitor Features */ + case 0x8000000a: /* SVM revision and features */ + case 0x8000001b: /* Instruction Based Sampling */ + regs[0] = regs[1] = regs[2] = regs[3] = 0; + break; + } +} + +static int xc_cpuid_policy( + int xc, domid_t domid, const unsigned int *input, unsigned int *regs) +{ + xc_dominfo_t info; + + if ( xc_domain_getinfo(xc, domid, 1, &info) == 0 ) + return -EINVAL; + + if ( info.hvm ) + xc_cpuid_hvm_policy(xc, domid, input, regs); + else + xc_cpuid_pv_policy(xc, domid, input, regs); + + return 0; +} + +static int xc_cpuid_do_domctl( + int xc, 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(xc, &domctl); +} + +static char *alloc_str(void) +{ + char *s = malloc(33); + 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(); + for ( j = 0; j < 32; j++ ) + strs[i][j] = !!((regs[i] & (1U << (31 - j)))) ? '1' : '0'; + } +} + +int xc_cpuid_apply_policy(int xc, domid_t domid) +{ + unsigned int input[2] = { 0, 0 }, regs[4]; + unsigned int base_max, ext_max; + int rc; + + cpuid(input, regs); + base_max = (regs[0] <= DEF_MAX_BASE) ? regs[0] : DEF_MAX_BASE; + input[0] = 0x80000000; + cpuid(input, regs); + ext_max = (regs[0] <= DEF_MAX_EXT) ? regs[0] : DEF_MAX_EXT; + + input[0] = 0; + input[1] = XEN_CPUID_INPUT_UNUSED; + for ( ; ; ) + { + cpuid(input, regs); + xc_cpuid_policy(xc, domid, input, regs); + + if ( regs[0] || regs[1] || regs[2] || regs[3] ) + { + rc = xc_cpuid_do_domctl(xc, domid, input, regs); + if ( rc ) + return rc; + + /* 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]++; + input[1] = (input[0] == 4) ? 0 : XEN_CPUID_INPUT_UNUSED; + if ( !(input[0] & 0x80000000u) && (input[0] > base_max ) ) + input[0] = 0x80000000u; + + if ( (input[0] & 0x80000000u) && (input[0] > ext_max) ) + break; + } + + return 0; +} + +/* + * 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( + int xc, const unsigned int *input, + const char **config, + char **config_transformed) +{ + int i, j; + 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(); + 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: + for ( i = 0; i < 4; i++ ) + { + free(config_transformed[i]); + config_transformed[i] = NULL; + } + return -EPERM; +} + +/* + * 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( + int xc, domid_t domid, const unsigned int *input, + const char **config, char **config_transformed) +{ + int rc; + unsigned int i, j, regs[4], polregs[4]; + + memset(config_transformed, 0, 4 * sizeof(*config_transformed)); + + cpuid(input, regs); + + memcpy(polregs, regs, sizeof(regs)); + xc_cpuid_policy(xc, domid, input, polregs); + + for ( i = 0; i < 4; i++ ) + { + if ( config[i] == NULL ) + { + regs[i] = polregs[i]; + continue; + } + + config_transformed[i] = alloc_str(); + + 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_bit(31 - j, regs[i]); + else + clear_bit(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(xc, domid, input, regs); + if ( rc == 0 ) + return 0; + + fail: + for ( i = 0; i < 4; i++ ) + { + free(config_transformed[i]); + config_transformed[i] = NULL; + } + return rc; +} diff --git a/tools/libxc/xc_csched.c b/tools/libxc/xc_csched.c new file mode 100644 index 0000000..4ea986f --- /dev/null +++ b/tools/libxc/xc_csched.c @@ -0,0 +1,50 @@ +/**************************************************************************** + * (C) 2006 - Emmanuel Ackaouy - XenSource Inc. + **************************************************************************** + * + * File: xc_csched.c + * Author: Emmanuel Ackaouy + * + * Description: XC Interface to the credit scheduler + * + */ +#include "xc_private.h" + + +int +xc_sched_credit_domain_set( + int xc_handle, + 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; + + return do_domctl(xc_handle, &domctl); +} + +int +xc_sched_credit_domain_get( + int xc_handle, + uint32_t domid, + struct xen_domctl_sched_credit *sdom) +{ + DECLARE_DOMCTL; + int err; + + 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; + + err = do_domctl(xc_handle, &domctl); + if ( err == 0 ) + *sdom = domctl.u.scheduler_op.u.credit; + + return err; +} diff --git a/tools/libxc/xc_dom.h b/tools/libxc/xc_dom.h new file mode 100644 index 0000000..770e00b --- /dev/null +++ b/tools/libxc/xc_dom.h @@ -0,0 +1,275 @@ +#include + +#define INVALID_P2M_ENTRY ((xen_pfn_t)-1) + +/* --- 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; +}; + +struct xc_dom_mem { + struct xc_dom_mem *next; + void *mmap_ptr; + size_t mmap_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; + + /* arguments and parameters */ + char *cmdline; + 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; + struct xc_dom_seg ramdisk_seg; + struct xc_dom_seg p2m_seg; + struct xc_dom_seg pgtables_seg; + struct xc_dom_seg devicetree_seg; + 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_vaddr_t virt_alloc_end; + xen_vaddr_t bsd_symtab_start; + + /* initial page tables */ + unsigned int pgtables; + unsigned int pg_l4; + unsigned int pg_l3; + unsigned int pg_l2; + unsigned int pg_l1; + unsigned int alloc_bootstack; + unsigned int extra_pages; + xen_vaddr_t virt_pgtab_end; + + /* other state info */ + uint32_t f_active[XENFEAT_NR_SUBMAPS]; + xen_pfn_t *p2m_host; + void *p2m_guest; + + /* physical memory */ + xen_pfn_t total_pages; + struct xc_dom_phys *phys_pages; + int realmodearea_log; + + /* 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; + xen_pfn_t shared_info_mfn; + + int guest_xc; + domid_t guest_domid; + int8_t vhpt_size_log2; /* for IA64 */ + int shadow_enabled; + + int xen_version; + xen_capabilities_info_t xen_caps; + + /* kernel loader, arch hooks */ + struct xc_dom_loader *kernel_loader; + void *private_loader; + + /* kernel loader */ + struct xc_dom_arch *arch_hooks; + /* allocate up to virt_alloc_end */ + int (*allocate) (struct xc_dom_image * dom, xen_vaddr_t up_to); +}; + +/* --- pluggable kernel loader ------------------------------------- */ + +struct xc_dom_loader { + char *name; + int (*probe) (struct xc_dom_image * dom); + int (*parser) (struct xc_dom_image * dom); + int (*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 (*count_pgtables) (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, void *vcpu_ctxt); + + char *guest_type; + char *native_protocol; + int page_shift; + int sizeof_pfn; + + 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) (1 << (dom)->arch_hooks->page_shift) + +/* --- main functions ---------------------------------------------- */ + +struct xc_dom_image *xc_dom_allocate(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_mem_init(struct xc_dom_image *dom, unsigned int mem_mb); + +size_t xc_dom_check_gzip(void *blob, size_t ziplen); +int xc_dom_do_gunzip(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_parse_image(struct xc_dom_image *dom); +struct xc_dom_arch *xc_dom_find_arch_hooks(char *guest_type); +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, int xc, 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); + +/* --- debugging bits ---------------------------------------------- */ + +extern FILE *xc_dom_logfile; + +void xc_dom_loginit(void); +int xc_dom_printf(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); +int xc_dom_panic_func(const char *file, int line, xc_error_code err, + const char *fmt, ...) + __attribute__ ((format(printf, 4, 5))); +#define xc_dom_panic(err, fmt, args...) \ + xc_dom_panic_func(__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); +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); +char *xc_dom_strdup(struct xc_dom_image *dom, const char *str); + +/* --- alloc memory pool ------------------------------------------- */ + +int 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_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(struct xc_dom_image *dom, + struct xc_dom_seg *seg) +{ + xen_vaddr_t segsize = seg->vend - seg->vstart; + unsigned int page_size = XC_DOM_PAGE_SIZE(dom); + xen_pfn_t pages = (segsize + page_size - 1) / page_size; + + return xc_dom_pfn_to_ptr(dom, seg->pfn, pages); +} + +static inline void *xc_dom_vaddr_to_ptr(struct xc_dom_image *dom, + xen_vaddr_t vaddr) +{ + 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; + void *ptr = xc_dom_pfn_to_ptr(dom, page, 0); + return (ptr ? (ptr + offset) : NULL); +} + +static inline int xc_dom_feature_translated(struct xc_dom_image *dom) +{ + return elf_xen_feature_get(XENFEAT_auto_translated_physmap, dom->f_active); +} + +static inline xen_pfn_t xc_dom_p2m_host(struct xc_dom_image *dom, xen_pfn_t pfn) +{ + if (dom->shadow_enabled) + return pfn; + return dom->p2m_host[pfn]; +} + +static inline xen_pfn_t xc_dom_p2m_guest(struct xc_dom_image *dom, + xen_pfn_t pfn) +{ + if (xc_dom_feature_translated(dom)) + return pfn; + return dom->p2m_host[pfn]; +} + +/* --- arch bits --------------------------------------------------- */ + +int arch_setup_meminit(struct xc_dom_image *dom); +int arch_setup_bootearly(struct xc_dom_image *dom); +int arch_setup_bootlate(struct xc_dom_image *dom); + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_binloader.c b/tools/libxc/xc_dom_binloader.c new file mode 100644 index 0000000..e887620 --- /dev/null +++ b/tools/libxc/xc_dom_binloader.c @@ -0,0 +1,287 @@ +/****************************************************************************** + * + * 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. + * + * 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. + */ + +#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; + + probe_ptr = dom->kernel_blob; + probe_end = dom->kernel_blob + dom->kernel_size - sizeof(*table); + if ( (void*)probe_end > (dom->kernel_blob + 8192) ) + probe_end = dom->kernel_blob + 8192; + + 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; + + xc_dom_printf("%s: multiboot header fields\n", __FUNCTION__); + xc_dom_printf(" flags: 0x%" PRIx32 "\n", image_info->flags); + xc_dom_printf(" header_addr: 0x%" PRIx32 "\n", image_info->header_addr); + xc_dom_printf(" load_addr: 0x%" PRIx32 "\n", image_info->load_addr); + xc_dom_printf(" load_end_addr: 0x%" PRIx32 "\n", image_info->load_end_addr); + xc_dom_printf(" bss_end_addr: 0x%" PRIx32 "\n", image_info->bss_end_addr); + xc_dom_printf(" entry_addr: 0x%" PRIx32 "\n", image_info->entry_addr); + + /* Check the flags */ + if ( (image_info->flags & FLAGS_MASK) != FLAGS_REQUIRED ) + { + xc_dom_panic(XC_INVALID_KERNEL, + "%s: xen_bin_image_table flags required " + "0x%08" PRIx32 " found 0x%08" PRIx32 "\n", + __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(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; + + xc_dom_printf("%s: calculated addresses\n", __FUNCTION__); + xc_dom_printf(" start_addr: 0x%" PRIx32 "\n", start_addr); + xc_dom_printf(" load_end_addr: 0x%" PRIx32 "\n", load_end_addr); + xc_dom_printf(" bss_end_addr: 0x%" PRIx32 "\n", bss_end_addr); + + if ( (start_addr + image_size) < load_end_addr ) + { + xc_dom_panic(XC_INVALID_KERNEL, "%s: Invalid load_end_addr.\n", + __FUNCTION__); + return -EINVAL; + } + + if ( bss_end_addr < load_end_addr) + { + xc_dom_panic(XC_INVALID_KERNEL, "%s: Invalid bss_end_addr.\n", + __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") ) + { + xc_dom_printf("%s: PAE fixup\n", __FUNCTION__); + dom->guest_type = "xen-3.0-x86_32p"; + dom->parms.pae = 2; + } + 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; + 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; + + xc_dom_printf("%s: calculated sizes\n", __FUNCTION__); + xc_dom_printf(" skip: 0x%" PRIx32 "\n", skip); + xc_dom_printf(" text_size: 0x%" PRIx32 "\n", text_size); + xc_dom_printf(" bss_size: 0x%" PRIx32 "\n", bss_size); + + dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart); + 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-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_boot.c b/tools/libxc/xc_dom_boot.c new file mode 100644 index 0000000..e767e8c --- /dev/null +++ b/tools/libxc/xc_dom_boot.c @@ -0,0 +1,265 @@ +/* + * 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 code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * + */ +#include +#include +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" +#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); + + xc_dom_printf("%s: vaddr=0x%" PRIx64 " pfn=0x%" PRIpfn "\n", __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_guest(dom, pfn); + rc = do_domctl(dom->guest_xc, &domctl); + if ( rc != 0 ) + xc_dom_panic(XC_INTERNAL_ERROR, "%s: HYPERCALL_INIT failed (rc=%d)\n", + __FUNCTION__, rc); + return rc; +} + +static int launch_vm(int xc, domid_t domid, void *ctxt) +{ + DECLARE_DOMCTL; + int rc; + + xc_dom_printf("%s: called, ctxt=%p\n", __FUNCTION__, ctxt); + memset(&domctl, 0, sizeof(domctl)); + domctl.cmd = XEN_DOMCTL_setvcpucontext; + domctl.domain = domid; + domctl.u.vcpucontext.vcpu = 0; + set_xen_guest_handle(domctl.u.vcpucontext.ctxt, ctxt); + rc = do_domctl(xc, &domctl); + if ( rc != 0 ) + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: SETVCPUCONTEXT failed (rc=%d)\n", __FUNCTION__, rc); + 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_host(dom, pfn); + xc_dom_printf("%s: pfn 0x%" PRIpfn ", mfn 0x%" PRIpfn "\n", + __FUNCTION__, pfn, dst); + rc = xc_clear_domain_page(dom->guest_xc, dom->guest_domid, dst); + if ( rc != 0 ) + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: xc_clear_domain_page failed (pfn 0x%" PRIpfn + ", rc=%d)\n", __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); + xc_dom_printf("%s: supported guest type: %s%s\n", __FUNCTION__, + item, match ? " <= matches" : ""); + if ( match ) + found++; + } + if ( !found ) + xc_dom_panic(XC_INVALID_KERNEL, + "%s: guest type %s not supported by xen kernel, sorry\n", + __FUNCTION__, dom->guest_type); + + return found; +} + +int xc_dom_boot_xen_init(struct xc_dom_image *dom, int xc, domid_t domid) +{ + dom->guest_xc = xc; + dom->guest_domid = domid; + + dom->xen_version = xc_version(dom->guest_xc, XENVER_version, NULL); + if ( xc_version(xc, XENVER_capabilities, &dom->xen_caps) < 0 ) + { + xc_dom_panic(XC_INTERNAL_ERROR, "can't get xen capabilities"); + return -1; + } + xc_dom_printf("%s: ver %d.%d, caps %s\n", __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; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + rc = arch_setup_meminit(dom); + if ( rc != 0 ) + { + xc_dom_panic(XC_OUT_OF_MEMORY, + "%s: can't allocate low memory for domain\n", + __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(XC_INTERNAL_ERROR, + "%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn + " [malloc]\n", __FUNCTION__, pfn, count); + return NULL; + } + + for ( i = 0; i < count; i++ ) + entries[i].mfn = xc_dom_p2m_host(dom, pfn + i); + + ptr = xc_map_foreign_ranges(dom->guest_xc, dom->guest_domid, + count << page_shift, PROT_READ | PROT_WRITE, 1 << page_shift, + entries, count); + if ( ptr == NULL ) + { + err = errno; + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn + " [mmap, errno=%i (%s)]\n", __FUNCTION__, pfn, count, + err, strerror(err)); + return NULL; + } + + return ptr; +} + +int xc_dom_boot_image(struct xc_dom_image *dom) +{ + DECLARE_DOMCTL; + vcpu_guest_context_any_t ctxt; + int rc; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* misc ia64 stuff*/ + if ( (rc = arch_setup_bootearly(dom)) != 0 ) + return rc; + + /* collect some info */ + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = dom->guest_domid; + rc = do_domctl(dom->guest_xc, &domctl); + if ( rc != 0 ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: getdomaininfo failed (rc=%d)\n", __FUNCTION__, rc); + return rc; + } + if ( domctl.domain != dom->guest_domid ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: Huh? domid mismatch (%d != %d)\n", __FUNCTION__, + domctl.domain, dom->guest_domid); + return -1; + } + dom->shared_info_mfn = domctl.u.getdomaininfo.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 = arch_setup_bootlate(dom)) != 0 ) + return rc; + + /* let the vm run */ + memset(&ctxt, 0, sizeof(ctxt)); + if ( (rc = dom->arch_hooks->vcpu(dom, &ctxt)) != 0 ) + return rc; + xc_dom_unmap_all(dom); + rc = launch_vm(dom->guest_xc, dom->guest_domid, &ctxt); + + return rc; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_bzimageloader.c b/tools/libxc/xc_dom_bzimageloader.c new file mode 100644 index 0000000..628cd7d --- /dev/null +++ b/tools/libxc/xc_dom_bzimageloader.c @@ -0,0 +1,159 @@ +/* + * 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 code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * written 2007 by Jeremy Fitzhardinge + * written 2008 by Ian Campbell + * + */ +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" + +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 unsigned int payload_offset(struct setup_header *hdr) +{ + unsigned int off; + + off = (hdr->setup_sects + 1) * 512; + off += hdr->payload_offset; + return off; +} + +static int check_bzimage_kernel(struct xc_dom_image *dom, int verbose) +{ + struct setup_header *hdr; + + if ( dom->kernel_blob == NULL ) + { + if ( verbose ) + xc_dom_panic(XC_INTERNAL_ERROR, "%s: no kernel image loaded\n", + __FUNCTION__); + return -EINVAL; + } + if ( dom->kernel_size < sizeof(struct setup_header) ) + { + if ( verbose ) + xc_dom_panic(XC_INTERNAL_ERROR, "%s: kernel image too small\n", + __FUNCTION__); + return -EINVAL; + } + + hdr = dom->kernel_blob; + + if ( memcmp(&hdr->header, HDR_MAGIC, HDR_MAGIC_SZ) != 0 ) + { + if ( verbose ) + xc_dom_panic(XC_INVALID_KERNEL, "%s: kernel is not a bzImage\n", + __FUNCTION__); + return -EINVAL; + } + + if ( hdr->version < VERSION(2,8) ) + { + if ( verbose ) + xc_dom_panic(XC_INVALID_KERNEL, "%s: boot protocol too old (%04x)\n", + __FUNCTION__, hdr->version); + return -EINVAL; + } + + dom->kernel_blob = dom->kernel_blob + payload_offset(hdr); + dom->kernel_size = hdr->payload_length; + + if ( xc_dom_try_gunzip(dom, &dom->kernel_blob, &dom->kernel_size) == -1 ) + { + if ( verbose ) + xc_dom_panic(XC_INVALID_KERNEL, "%s: unable to decompress kernel\n", + __FUNCTION__); + return -EINVAL; + } + + return elf_loader.probe(dom); +} + +static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom) +{ + return check_bzimage_kernel(dom, 0); +} + +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-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_compat_linux.c b/tools/libxc/xc_dom_compat_linux.c new file mode 100644 index 0000000..92cdae4 --- /dev/null +++ b/tools/libxc/xc_dom_compat_linux.c @@ -0,0 +1,157 @@ +/* + * 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 code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * + */ +#include +#include +#include +#include +#include + +#include "xenctrl.h" +#include "xg_private.h" +#include "xc_dom.h" + +/* ------------------------------------------------------------------------ */ + +static int xc_linux_build_internal(struct xc_dom_image *dom, + int xc_handle, uint32_t domid, + unsigned int mem_mb, + unsigned long flags, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + int rc; + + dom->flags = flags; + dom->console_evtchn = console_evtchn; + dom->xenstore_evtchn = store_evtchn; + + if ( (rc = xc_dom_boot_xen_init(dom, xc_handle, 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; + + *console_mfn = xc_dom_p2m_host(dom, dom->console_pfn); + *store_mfn = xc_dom_p2m_host(dom, dom->xenstore_pfn); + + out: + return rc; +} + +int xc_linux_build_mem(int xc_handle, uint32_t domid, + unsigned int mem_mb, + const char *image_buffer, + unsigned long image_size, + const char *initrd, + unsigned long initrd_len, + 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(); + dom = xc_dom_allocate(cmdline, features); + if ( (rc = xc_dom_kernel_mem(dom, image_buffer, image_size)) != 0 ) + goto out; + if ( initrd && ((rc = xc_dom_ramdisk_mem(dom, initrd, initrd_len)) != 0) ) + goto out; + + rc = xc_linux_build_internal(dom, xc_handle, domid, + mem_mb, flags, + store_evtchn, store_mfn, + console_evtchn, console_mfn); + + out: + xc_dom_release(dom); + return rc; +} + +int xc_linux_build(int xc_handle, 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(); + dom = xc_dom_allocate(cmdline, features); + 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; + + rc = xc_linux_build_internal(dom, xc_handle, domid, + mem_mb, flags, + store_evtchn, store_mfn, + console_evtchn, console_mfn); + + out: + xc_dom_release(dom); + return rc; +} + +int xc_dom_linux_build(int xc_handle, + struct xc_dom_image *dom, + uint32_t domid, + unsigned int mem_mb, + const char *image_name, + const char *initrd_name, + unsigned long flags, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, unsigned long *console_mfn) +{ + int rc; + + if ( (rc = xc_dom_kernel_file(dom, image_name)) != 0 ) + return rc; + if ( initrd_name && strlen(initrd_name) && + ((rc = xc_dom_ramdisk_file(dom, initrd_name)) != 0) ) + return rc; + + return xc_linux_build_internal(dom, xc_handle, domid, + mem_mb, flags, + store_evtchn, store_mfn, + console_evtchn, console_mfn); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_core.c b/tools/libxc/xc_dom_core.c new file mode 100644 index 0000000..2b6f6b6 --- /dev/null +++ b/tools/libxc/xc_dom_core.c @@ -0,0 +1,790 @@ +/* + * Xen domain builder -- core bits. + * + * The core code goes here: + * - allocate and release domain structs. + * - memory management functions. + * - misc helper functions. + * + * This code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * + */ +#include +#include +#include +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" + +/* ------------------------------------------------------------------------ */ +/* debugging */ + +FILE *xc_dom_logfile = NULL; + +void xc_dom_loginit(void) +{ + if ( xc_dom_logfile ) + return; + xc_dom_logfile = fopen("/var/log/xen/domain-builder-ng.log", "a"); + setvbuf(xc_dom_logfile, NULL, _IONBF, 0); + xc_dom_printf("### ----- xc domain builder logfile opened -----\n"); +} + +int xc_dom_printf(const char *fmt, ...) +{ + va_list args; + char buf[1024]; + int rc; + + if ( !xc_dom_logfile ) + return 0; + + va_start(args, fmt); + rc = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + rc = fwrite(buf, rc, 1, xc_dom_logfile); + + return rc; +} + +int xc_dom_panic_func(const char *file, int line, xc_error_code err, + const char *fmt, ...) +{ + va_list args; + FILE *fp = stderr; + int rc = 0; + char pos[256]; + char msg[XC_MAX_ERROR_MSG_LEN]; + + if ( xc_dom_logfile ) + fp = xc_dom_logfile; + + snprintf(pos, sizeof(pos), "%s:%d: panic: ", file, line); + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + xc_set_error(err, "%s", msg); + rc = fprintf(fp, "%s%s", pos, msg); + return rc; +} + +static void print_mem(const char *name, size_t mem) +{ + if ( mem > (32 * 1024 * 1024) ) + xc_dom_printf("%-24s : %zd MB\n", name, mem / (1024 * 1024)); + else if ( mem > (32 * 1024) ) + xc_dom_printf("%-24s : %zd kB\n", name, mem / 1024); + else + xc_dom_printf("%-24s : %zd bytes\n", name, mem); +} + +void xc_dom_log_memory_footprint(struct xc_dom_image *dom) +{ + xc_dom_printf("domain builder memory footprint\n"); + xc_dom_printf(" allocated\n"); + print_mem(" malloc", dom->alloc_malloc); + print_mem(" anon mmap", dom->alloc_mem_map); + xc_dom_printf(" mapped\n"); + print_mem(" file mmap", dom->alloc_file_map); + print_mem(" 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; + + block = malloc(sizeof(*block) + size); + if ( block == NULL ) + return NULL; + memset(block, 0, sizeof(*block) + size); + block->next = dom->memblocks; + dom->memblocks = block; + dom->alloc_malloc += sizeof(*block) + size; + if ( size > (100 * 1024) ) + print_mem(__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 ) + return NULL; + memset(block, 0, sizeof(*block)); + block->mmap_len = size; + block->mmap_ptr = mmap(NULL, block->mmap_len, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, + -1, 0); + if ( block->mmap_ptr == MAP_FAILED ) + { + free(block); + return NULL; + } + block->next = dom->memblocks; + dom->memblocks = block; + dom->alloc_malloc += sizeof(*block); + dom->alloc_mem_map += block->mmap_len; + if ( size > (100 * 1024) ) + print_mem(__FUNCTION__, size); + return block->mmap_ptr; +} + +void *xc_dom_malloc_filemap(struct xc_dom_image *dom, + const char *filename, size_t * size) +{ + struct xc_dom_mem *block = NULL; + int fd = -1; + + fd = open(filename, O_RDONLY); + if ( fd == -1 ) + goto err; + + lseek(fd, 0, SEEK_SET); + *size = lseek(fd, 0, SEEK_END); + + block = malloc(sizeof(*block)); + if ( block == NULL ) + goto err; + memset(block, 0, sizeof(*block)); + block->mmap_len = *size; + block->mmap_ptr = mmap(NULL, block->mmap_len, PROT_READ, + MAP_SHARED, fd, 0); + if ( block->mmap_ptr == MAP_FAILED ) + goto err; + block->next = dom->memblocks; + dom->memblocks = block; + dom->alloc_malloc += sizeof(*block); + dom->alloc_file_map += block->mmap_len; + close(fd); + if ( *size > (100 * 1024) ) + print_mem(__FUNCTION__, *size); + return block->mmap_ptr; + + err: + if ( fd != -1 ) + close(fd); + if ( block != NULL ) + free(block); + 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; + if ( block->mmap_ptr ) + munmap(block->mmap_ptr, block->mmap_len); + 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; +} + +/* ------------------------------------------------------------------------ */ +/* read files, copy memory blocks, with transparent gunzip */ + +size_t xc_dom_check_gzip(void *blob, size_t ziplen) +{ + unsigned char *gzlen; + size_t unziplen; + + if ( strncmp(blob, "\037\213", 2) ) + /* not gzipped */ + return 0; + + gzlen = blob + ziplen - 4; + unziplen = gzlen[3] << 24 | gzlen[2] << 16 | gzlen[1] << 8 | gzlen[0]; + if ( (unziplen < 0) || (unziplen > (1024*1024*1024)) ) /* 1GB limit */ + { + xc_dom_printf + ("%s: size (zip %zd, unzip %zd) looks insane, skip gunzip\n", + __FUNCTION__, ziplen, unziplen); + return 0; + } + + return unziplen + 16; +} + +int xc_dom_do_gunzip(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(XC_INTERNAL_ERROR, + "%s: inflateInit2 failed (rc=%d)\n", __FUNCTION__, rc); + return -1; + } + rc = inflate(&zStream, Z_FINISH); + if ( rc != Z_STREAM_END ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: inflate failed (rc=%d)\n", __FUNCTION__, rc); + return -1; + } + + xc_dom_printf("%s: unzip ok, 0x%zx -> 0x%zx\n", + __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(*blob, *size); + if ( unziplen == 0 ) + return 0; + + unzip = xc_dom_malloc(dom, unziplen); + if ( unzip == NULL ) + return -1; + + if ( xc_dom_do_gunzip(*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) +{ + struct xc_dom_phys *phys; + unsigned int page_shift = XC_DOM_PAGE_SHIFT(dom); + char *mode = "unset"; + + if ( pfn > dom->total_pages ) + { + xc_dom_printf("%s: pfn out of range (0x%" PRIpfn " > 0x%" PRIpfn ")\n", + __FUNCTION__, pfn, 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)) ) + { + xc_dom_printf("%s: request overlaps allocated block" + " (req 0x%" PRIpfn "+0x%" PRIpfn "," + " blk 0x%" PRIpfn "+0x%" PRIpfn ")\n", + __FUNCTION__, pfn, count, phys->first, + phys->count); + return NULL; + } + } + else + { + /* no size given: block must be allocated already, + just hand out a pointer to it */ + if ( pfn < phys->first ) + continue; + } + return phys->ptr + ((pfn - phys->first) << page_shift); + } + + /* allocating is allowed with size specified only */ + if ( count == 0 ) + { + xc_dom_printf("%s: no block found, no size given," + " can't malloc (pfn 0x%" PRIpfn ")\n", + __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(XC_OUT_OF_MEMORY, + "%s: oom: can't allocate 0x%" PRIpfn " pages" + " [mmap, errno=%i (%s)]\n", + __FUNCTION__, count, err, strerror(err)); + return NULL; + } + dom->alloc_mem_map += phys->count << page_shift; + } + +#if 1 + xc_dom_printf("%s: %s: pfn 0x%" PRIpfn "+0x%" PRIpfn " at %p\n", + __FUNCTION__, mode, phys->first, phys->count, phys->ptr); +#endif + phys->next = dom->phys_pages; + dom->phys_pages = phys; + return phys->ptr; +} + +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 = (size + page_size - 1) / page_size; + void *ptr; + + if ( start == 0 ) + start = dom->virt_alloc_end; + + if ( start & (page_size - 1) ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: segment start isn't page aligned (0x%" PRIx64 ")\n", + __FUNCTION__, start); + return -1; + } + if ( start < dom->virt_alloc_end ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: segment start too low (0x%" PRIx64 " < 0x%" PRIx64 + ")\n", __FUNCTION__, start, dom->virt_alloc_end); + return -1; + } + + seg->vstart = start; + seg->vend = start + pages * page_size; + seg->pfn = (seg->vstart - dom->parms.virt_base) / page_size; + dom->virt_alloc_end = seg->vend; + if (dom->allocate) + dom->allocate(dom, dom->virt_alloc_end); + + xc_dom_printf("%-20s: %-12s : 0x%" PRIx64 " -> 0x%" PRIx64 + " (pfn 0x%" PRIpfn " + 0x%" PRIpfn " pages)\n", + __FUNCTION__, name, seg->vstart, seg->vend, seg->pfn, pages); + + /* map and clear pages */ + ptr = xc_dom_seg_to_ptr(dom, seg); + if ( ptr == NULL ) + return -1; + memset(ptr, 0, pages * page_size); + + return 0; +} + +int xc_dom_alloc_page(struct xc_dom_image *dom, char *name) +{ + unsigned int page_size = XC_DOM_PAGE_SIZE(dom); + xen_vaddr_t start; + xen_pfn_t pfn; + + start = dom->virt_alloc_end; + dom->virt_alloc_end += page_size; + if (dom->allocate) + dom->allocate(dom, dom->virt_alloc_end); + pfn = (start - dom->parms.virt_base) / page_size; + + xc_dom_printf("%-20s: %-12s : 0x%" PRIx64 " (pfn 0x%" PRIpfn ")\n", + __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 ) + { + xc_dom_printf("%s: Huh? no mapping with pfn 0x%" PRIpfn "\n", + __FUNCTION__, pfn); + return; + } + + munmap(phys->ptr, phys->count << page_shift); + if ( prev ) + prev->next = phys->next; + else + dom->phys_pages = phys->next; +} + +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 ) + { + xc_dom_printf("%s: trying %s loader ... ", __FUNCTION__, loader->name); + if ( loader->probe(dom) == 0 ) + { + xc_dom_printf("OK\n"); + return loader; + } + xc_dom_printf("failed\n"); + loader = loader->next; + } + xc_dom_panic(XC_INVALID_KERNEL, "%s: no loader found\n", __FUNCTION__); + return NULL; +} + +void xc_dom_register_arch_hooks(struct xc_dom_arch *hooks) +{ + hooks->next = first_hook; + first_hook = hooks; +} + +struct xc_dom_arch *xc_dom_find_arch_hooks(char *guest_type) +{ + struct xc_dom_arch *hooks = first_hook; + + while ( hooks != NULL ) + { + if ( !strcmp(hooks->guest_type, guest_type)) + return hooks; + hooks = hooks->next; + } + xc_dom_panic(XC_INVALID_KERNEL, + "%s: not found (type %s)\n", __FUNCTION__, guest_type); + return NULL; +} + +/* ------------------------------------------------------------------------ */ +/* public interface */ + +void xc_dom_release(struct xc_dom_image *dom) +{ + xc_dom_printf("%s: called\n", __FUNCTION__); + if ( dom->phys_pages ) + xc_dom_unmap_all(dom); + xc_dom_free_all(dom); + free(dom); +} + +struct xc_dom_image *xc_dom_allocate(const char *cmdline, const char *features) +{ + struct xc_dom_image *dom; + + xc_dom_printf("%s: cmdline=\"%s\", features=\"%s\"\n", + __FUNCTION__, cmdline, features); + dom = malloc(sizeof(*dom)); + if ( !dom ) + goto err; + + memset(dom, 0, sizeof(*dom)); + 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->alloc_malloc += sizeof(*dom); + return dom; + + err: + if ( dom ) + xc_dom_release(dom); + return NULL; +} + +int xc_dom_kernel_file(struct xc_dom_image *dom, const char *filename) +{ + xc_dom_printf("%s: filename=\"%s\"\n", __FUNCTION__, filename); + dom->kernel_blob = xc_dom_malloc_filemap(dom, filename, &dom->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) +{ + xc_dom_printf("%s: filename=\"%s\"\n", __FUNCTION__, filename); + dom->ramdisk_blob = + xc_dom_malloc_filemap(dom, filename, &dom->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_kernel_mem(struct xc_dom_image *dom, const void *mem, size_t memsize) +{ + xc_dom_printf("%s: called\n", __FUNCTION__); + 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) +{ + xc_dom_printf("%s: called\n", __FUNCTION__); + 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_parse_image(struct xc_dom_image *dom) +{ + int i; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* 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(XC_INTERNAL_ERROR, + "%s: guest_type not set\n", __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(XC_INVALID_PARAM, + "%s: unsupported feature requested\n", __FUNCTION__); + goto err; + } + } + return 0; + + err: + return -1; +} + +int xc_dom_mem_init(struct xc_dom_image *dom, unsigned int mem_mb) +{ + unsigned int page_shift; + xen_pfn_t nr_pages; + + dom->arch_hooks = xc_dom_find_arch_hooks(dom->guest_type); + if ( dom->arch_hooks == NULL ) + { + xc_dom_panic(XC_INTERNAL_ERROR, "%s: arch hooks not set\n", + __FUNCTION__); + return -1; + } + + page_shift = XC_DOM_PAGE_SHIFT(dom); + nr_pages = mem_mb << (20 - page_shift); + + xc_dom_printf("%s: mem %d MB, pages 0x%" PRIpfn " pages, %dk each\n", + __FUNCTION__, mem_mb, nr_pages, 1 << (page_shift-10)); + dom->total_pages = nr_pages; + + xc_dom_printf("%s: 0x%" PRIpfn " pages\n", + __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: + xc_dom_printf("%s: dst 32bit, pages 0x%" PRIpfn " \n", + __FUNCTION__, dom->total_pages); + p2m_32 = dom->p2m_guest; + for ( i = 0; i < dom->total_pages; i++ ) + if ( dom->p2m_host[i] != INVALID_P2M_ENTRY ) + p2m_32[i] = dom->p2m_host[i]; + else + p2m_32[i] = (uint32_t) - 1; + break; + case 8: + xc_dom_printf("%s: dst 64bit, pages 0x%" PRIpfn " \n", + __FUNCTION__, dom->total_pages); + p2m_64 = dom->p2m_guest; + for ( i = 0; i < dom->total_pages; i++ ) + if ( dom->p2m_host[i] != INVALID_P2M_ENTRY ) + p2m_64[i] = dom->p2m_host[i]; + else + p2m_64[i] = (uint64_t) - 1; + break; + default: + xc_dom_panic(XC_INTERNAL_ERROR, + "sizeof_pfn is invalid (is %d, can be 4 or 8)", + dom->arch_hooks->sizeof_pfn); + return -1; + } + return 0; +} + +int xc_dom_build_image(struct xc_dom_image *dom) +{ + unsigned int page_size; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* check for arch hooks */ + if ( dom->arch_hooks == NULL ) + { + xc_dom_panic(XC_INTERNAL_ERROR, "%s: arch hooks not set\n", + __FUNCTION__); + goto err; + } + page_size = XC_DOM_PAGE_SIZE(dom); + + /* 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; + + /* load ramdisk */ + if ( dom->ramdisk_blob ) + { + size_t unziplen, ramdisklen; + void *ramdiskmap; + + unziplen = xc_dom_check_gzip(dom->ramdisk_blob, dom->ramdisk_size); + ramdisklen = unziplen ? unziplen : dom->ramdisk_size; + if ( xc_dom_alloc_segment(dom, &dom->ramdisk_seg, "ramdisk", 0, + ramdisklen) != 0 ) + goto err; + ramdiskmap = xc_dom_seg_to_ptr(dom, &dom->ramdisk_seg); + if ( unziplen ) + { + if ( xc_dom_do_gunzip(dom->ramdisk_blob, dom->ramdisk_size, + ramdiskmap, ramdisklen) == -1 ) + goto err; + } + else + memcpy(ramdiskmap, dom->ramdisk_blob, dom->ramdisk_size); + } + + /* allocate other pages */ + if ( dom->arch_hooks->alloc_magic_pages(dom) != 0 ) + goto err; + if ( dom->arch_hooks->count_pgtables ) + { + dom->arch_hooks->count_pgtables(dom); + if ( (dom->pgtables > 0) && + (xc_dom_alloc_segment(dom, &dom->pgtables_seg, "page tables", 0, + dom->pgtables * page_size) != 0) ) + goto err; + } + if ( dom->alloc_bootstack ) + dom->bootstack_pfn = xc_dom_alloc_page(dom, "boot stack"); + xc_dom_printf("%-20s: virt_alloc_end : 0x%" PRIx64 "\n", + __FUNCTION__, dom->virt_alloc_end); + xc_dom_printf("%-20s: virt_pgtab_end : 0x%" PRIx64 "\n", + __FUNCTION__, dom->virt_pgtab_end); + return 0; + + err: + return -1; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_elfloader.c b/tools/libxc/xc_dom_elfloader.c new file mode 100644 index 0000000..f9bbd85 --- /dev/null +++ b/tools/libxc/xc_dom_elfloader.c @@ -0,0 +1,302 @@ +/* + * Xen domain builder -- ELF bits. + * + * Parse and load ELF kernel images. + * + * This code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * + */ +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" + +#define XEN_VER "xen-3.0" + +/* ------------------------------------------------------------------------ */ + +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); + + switch ( machine ) + { + case EM_386: + switch ( dom->parms.pae ) + { + case 3 /* PAEKERN_bimodal */: + if ( strstr(dom->xen_caps, "xen-3.0-x86_32p") ) + return "xen-3.0-x86_32p"; + return "xen-3.0-x86_32"; + case PAEKERN_extended_cr3: + case PAEKERN_yes: + return "xen-3.0-x86_32p"; + case PAEKERN_no: + default: + return "xen-3.0-x86_32"; + } + case EM_X86_64: + return "xen-3.0-x86_64"; + case EM_IA_64: + return elf_msb(elf) ? "xen-3.0-ia64be" : "xen-3.0-ia64"; + default: + return "xen-3.0-unknown"; + } +} + +/* ------------------------------------------------------------------------ */ +/* parse elf binary */ + +static int check_elf_kernel(struct xc_dom_image *dom, int verbose) +{ + if ( dom->kernel_blob == NULL ) + { + if ( verbose ) + xc_dom_panic(XC_INTERNAL_ERROR, "%s: no kernel image loaded\n", + __FUNCTION__); + return -EINVAL; + } + + if ( !elf_is_elfbinary(dom->kernel_blob) ) + { + if ( verbose ) + xc_dom_panic(XC_INVALID_KERNEL, "%s: kernel is not an ELF image\n", + __FUNCTION__); + return -EINVAL; + } + return 0; +} + +static int xc_dom_probe_elf_kernel(struct xc_dom_image *dom) +{ + return check_elf_kernel(dom, 0); +} + +static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, + struct elf_binary *elf, int load) +{ + struct elf_binary syms; + const elf_shdr *shdr, *shdr2; + xen_vaddr_t symtab, maxaddr; + char *hdr; + size_t size; + int h, count, type, i, tables = 0; + + if ( elf_swap(elf) ) + { + xc_dom_printf("%s: non-native byte order, bsd symtab not supported\n", + __FUNCTION__); + return 0; + } + + if ( load ) + { + if ( !dom->bsd_symtab_start ) + return 0; + size = dom->kernel_seg.vend - dom->bsd_symtab_start; + hdr = xc_dom_vaddr_to_ptr(dom, dom->bsd_symtab_start); + *(int *)hdr = size - sizeof(int); + } + else + { + size = sizeof(int) + elf_size(elf, elf->ehdr) + + elf_shdr_count(elf) * elf_size(elf, shdr); + hdr = xc_dom_malloc(dom, size); + if ( hdr == NULL ) + return 0; + dom->bsd_symtab_start = elf_round_up(&syms, dom->kernel_seg.vend); + } + + memcpy(hdr + sizeof(int), + elf->image, + elf_size(elf, elf->ehdr)); + memcpy(hdr + sizeof(int) + elf_size(elf, elf->ehdr), + elf->image + elf_uval(elf, elf->ehdr, e_shoff), + elf_shdr_count(elf) * elf_size(elf, shdr)); + if ( elf_64bit(elf) ) + { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)(hdr + sizeof(int)); + ehdr->e_phoff = 0; + ehdr->e_phentsize = 0; + ehdr->e_phnum = 0; + ehdr->e_shoff = elf_size(elf, elf->ehdr); + ehdr->e_shstrndx = SHN_UNDEF; + } + else + { + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(hdr + sizeof(int)); + ehdr->e_phoff = 0; + ehdr->e_phentsize = 0; + ehdr->e_phnum = 0; + ehdr->e_shoff = elf_size(elf, elf->ehdr); + ehdr->e_shstrndx = SHN_UNDEF; + } + if ( elf_init(&syms, hdr + sizeof(int), size - sizeof(int)) ) + return -1; + if ( xc_dom_logfile ) + elf_set_logfile(&syms, xc_dom_logfile, 1); + + symtab = dom->bsd_symtab_start + sizeof(int); + maxaddr = elf_round_up(&syms, symtab + elf_size(&syms, syms.ehdr) + + elf_shdr_count(&syms) * elf_size(&syms, shdr)); + + xc_dom_printf("%s/%s: bsd_symtab_start=%" PRIx64 ", kernel.end=0x%" PRIx64 + " -- symtab=0x%" PRIx64 ", maxaddr=0x%" PRIx64 "\n", + __FUNCTION__, load ? "load" : "parse", + dom->bsd_symtab_start, dom->kernel_seg.vend, + symtab, maxaddr); + + count = elf_shdr_count(&syms); + for ( h = 0; h < count; h++ ) + { + shdr = elf_shdr_by_index(&syms, h); + type = elf_uval(&syms, shdr, sh_type); + if ( type == SHT_STRTAB ) + { + /* Look for a strtab @i linked to symtab @h. */ + for ( i = 0; i < count; i++ ) + { + shdr2 = elf_shdr_by_index(&syms, i); + if ( (elf_uval(&syms, shdr2, sh_type) == SHT_SYMTAB) && + (elf_uval(&syms, shdr2, sh_link) == h) ) + break; + } + /* Skip symtab @h if we found no corresponding strtab @i. */ + if ( i == count ) + { + if ( elf_64bit(&syms) ) + *(Elf64_Off*)(&shdr->e64.sh_offset) = 0; + else + *(Elf32_Off*)(&shdr->e32.sh_offset) = 0; + continue; + } + } + + if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) ) + { + /* Mangled to be based on ELF header location. */ + if ( elf_64bit(&syms) ) + *(Elf64_Off*)(&shdr->e64.sh_offset) = maxaddr - symtab; + else + *(Elf32_Off*)(&shdr->e32.sh_offset) = maxaddr - symtab; + size = elf_uval(&syms, shdr, sh_size); + maxaddr = elf_round_up(&syms, maxaddr + size); + tables++; + xc_dom_printf("%s: h=%d %s, size=0x%zx, maxaddr=0x%" PRIx64 "\n", + __FUNCTION__, h, + type == SHT_SYMTAB ? "symtab" : "strtab", + size, maxaddr); + + if ( load ) + { + shdr2 = elf_shdr_by_index(elf, h); + memcpy((void*)elf_section_start(&syms, shdr), + elf_section_start(elf, shdr2), + size); + } + } + + /* Name is NULL. */ + if ( elf_64bit(&syms) ) + *(Elf64_Half*)(&shdr->e64.sh_name) = 0; + else + *(Elf32_Word*)(&shdr->e32.sh_name) = 0; + } + + if ( tables == 0 ) + { + xc_dom_printf("%s: no symbol table present\n", __FUNCTION__); + dom->bsd_symtab_start = 0; + return 0; + } + if ( !load ) + dom->kernel_seg.vend = maxaddr; + return 0; +} + +static int xc_dom_parse_elf_kernel(struct xc_dom_image *dom) +{ + struct elf_binary *elf; + int rc; + + rc = check_elf_kernel(dom, 1); + if ( rc != 0 ) + return rc; + + elf = xc_dom_malloc(dom, sizeof(*elf)); + dom->private_loader = elf; + rc = elf_init(elf, dom->kernel_blob, dom->kernel_size); + if ( xc_dom_logfile ) + elf_set_logfile(elf, xc_dom_logfile, 1); + if ( rc != 0 ) + { + xc_dom_panic(XC_INVALID_KERNEL, "%s: corrupted ELF image\n", + __FUNCTION__); + return rc; + } + + /* Find the section-header strings table. */ + if ( elf->sec_strtab == NULL ) + { + xc_dom_panic(XC_INVALID_KERNEL, "%s: ELF image has no shstrtab\n", + __FUNCTION__); + return -EINVAL; + } + + /* parse binary and get xen meta info */ + elf_parse_binary(elf); + if ( (rc = elf_xen_parse(elf, &dom->parms)) != 0 ) + return rc; + + /* find kernel segment */ + dom->kernel_seg.vstart = dom->parms.virt_kstart; + dom->kernel_seg.vend = dom->parms.virt_kend; + + if ( dom->parms.bsd_symtab ) + xc_dom_load_elf_symtab(dom, elf, 0); + + dom->guest_type = xc_dom_guest_type(dom, elf); + xc_dom_printf("%s: %s: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", + __FUNCTION__, dom->guest_type, + dom->kernel_seg.vstart, dom->kernel_seg.vend); + return 0; +} + +static int xc_dom_load_elf_kernel(struct xc_dom_image *dom) +{ + struct elf_binary *elf = dom->private_loader; + + elf->dest = xc_dom_seg_to_ptr(dom, &dom->kernel_seg); + elf_load_binary(elf); + if ( dom->parms.bsd_symtab ) + xc_dom_load_elf_symtab(dom, elf, 1); + 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-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_ia64.c b/tools/libxc/xc_dom_ia64.c new file mode 100644 index 0000000..7610aa1 --- /dev/null +++ b/tools/libxc/xc_dom_ia64.c @@ -0,0 +1,320 @@ +/* + * Xen domain builder -- ia64 bits. + * + * Most architecture-specific code for ia64 goes here. + * - fill architecture-specific structs. + * + * This code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" +#include "xenctrl.h" + +#include +#include "ia64/xc_dom_ia64_util.h" + +/* ------------------------------------------------------------------------ */ + +static int alloc_magic_pages(struct xc_dom_image *dom) +{ + /* allocate special pages */ + dom->console_pfn = dom->total_pages -1; + dom->xenstore_pfn = dom->total_pages -2; + dom->start_info_pfn = dom->total_pages -3; + return 0; +} + +int start_info_ia64(struct xc_dom_image *dom) +{ + start_info_ia64_t *start_info = + xc_dom_pfn_to_ptr(dom, dom->start_info_pfn, 1); + struct xen_ia64_boot_param_ia64 *bp = + (struct xen_ia64_boot_param_ia64 *)(start_info + 1); + + xc_dom_printf("%s\n", __FUNCTION__); + + memset(start_info, 0, sizeof(*start_info)); + sprintf(start_info->magic, dom->guest_type); + start_info->flags = dom->flags; + start_info->nr_pages = dom->total_pages; + start_info->store_mfn = dom->xenstore_pfn; + start_info->store_evtchn = dom->xenstore_evtchn; + start_info->console.domU.mfn = dom->console_pfn; + start_info->console.domU.evtchn = dom->console_evtchn; + + /* + * domain_start and domain_size are abused for arch_setup hypercall + * so that we need to clear them here. + */ + XEN_IA64_MEMMAP_INFO_NUM_PAGES(bp) = 0; + XEN_IA64_MEMMAP_INFO_PFN(bp) = 0; + + if ( dom->ramdisk_blob ) + { + start_info->mod_start = dom->ramdisk_seg.vstart; + start_info->mod_len = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; + bp->initrd_start = start_info->mod_start; + bp->initrd_size = start_info->mod_len; + } + bp->command_line = (dom->start_info_pfn << PAGE_SHIFT_IA64) + + offsetof(start_info_t, cmd_line); + 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; +} + +int shared_info_ia64(struct xc_dom_image *dom, void *ptr) +{ + shared_info_ia64_t *shared_info = ptr; + int i; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + memset(shared_info, 0, sizeof(*shared_info)); + for (i = 0; i < MAX_VIRT_CPUS; i++) + shared_info->vcpu_info[i].evtchn_upcall_mask = 1; + shared_info->arch.start_info_pfn = dom->start_info_pfn; + shared_info->arch.memmap_info_num_pages = 1; //XXX + shared_info->arch.memmap_info_pfn = dom->start_info_pfn - 1; + return 0; +} + +extern unsigned long xc_ia64_fpsr_default(void); + +static int vcpu_ia64(struct xc_dom_image *dom, void *ptr) +{ + vcpu_guest_context_ia64_t *ctxt = ptr; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* clear everything */ + memset(ctxt, 0, sizeof(*ctxt)); + + ctxt->flags = 0; + /* PSR is set according to SAL 3.2.4: AC, IC and BN are set. */ + ctxt->regs.psr = IA64_PSR_AC | IA64_PSR_IC | IA64_PSR_BN; + ctxt->regs.ip = dom->parms.virt_entry; + ctxt->regs.cfm = 1UL << 63; +#ifdef __ia64__ /* FIXME */ + ctxt->regs.ar.fpsr = xc_ia64_fpsr_default(); +#endif + ctxt->regs.r[28] = (dom->start_info_pfn << PAGE_SHIFT_IA64) + + sizeof(start_info_ia64_t); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct xc_dom_arch xc_dom_arch = { + .guest_type = "xen-3.0-ia64", + .native_protocol = XEN_IO_PROTO_ABI_IA64, + .page_shift = PAGE_SHIFT_IA64, + .alloc_magic_pages = alloc_magic_pages, + .start_info = start_info_ia64, + .shared_info = shared_info_ia64, + .vcpu = vcpu_ia64, +}; + +static struct xc_dom_arch xc_dom_arch_ia64be = { + .guest_type = "xen-3.0-ia64be", + .native_protocol = XEN_IO_PROTO_ABI_IA64, + .page_shift = PAGE_SHIFT_IA64, + .alloc_magic_pages = alloc_magic_pages, + .start_info = start_info_ia64, + .shared_info = shared_info_ia64, + .vcpu = vcpu_ia64, +}; + +static void __init register_arch_hooks(void) +{ + xc_dom_register_arch_hooks(&xc_dom_arch); + xc_dom_register_arch_hooks(&xc_dom_arch_ia64be); +} + +#include "xc_efi.h" + +int arch_setup_meminit(struct xc_dom_image *dom) +{ + xen_pfn_t pfn; + int rc; + unsigned long start; + unsigned long nbr; + + /* setup initial p2m */ + if (dom->guest_type && strcmp(dom->guest_type, + "hvm-3.0-ia64-sioemu") == 0) { + start = FW_MEM_BASE >> PAGE_SHIFT_IA64; + nbr = FW_MEM_SIZE >> PAGE_SHIFT_IA64; + } else { + start = 0; + nbr = dom->total_pages; + } + + /* setup initial p2m */ + dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * nbr); + for ( pfn = 0; pfn < nbr; pfn++ ) + dom->p2m_host[pfn] = start + pfn; + + /* allocate guest memory */ + rc = xc_domain_memory_populate_physmap(dom->guest_xc, dom->guest_domid, + nbr, 0, 0, + dom->p2m_host); + return rc; +} + +static int ia64_setup_memmap(struct xc_dom_image *dom) +{ + unsigned int page_size = XC_DOM_PAGE_SIZE(dom); + unsigned long memmap_info_num_pages; + unsigned long memmap_info_pfn; + xen_ia64_memmap_info_t* memmap_info; + unsigned int num_mds; + efi_memory_desc_t *md; + + char* start_info; + struct xen_ia64_boot_param* bp; + + /* setup memmap page */ + memmap_info_num_pages = 1; + memmap_info_pfn = dom->start_info_pfn - 1; + xc_dom_printf("%s: memmap: mfn 0x%" PRIpfn " pages 0x%lx\n", + __FUNCTION__, memmap_info_pfn, memmap_info_num_pages); + memmap_info = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, + page_size * memmap_info_num_pages, + PROT_READ | PROT_WRITE, + memmap_info_pfn); + if (NULL == memmap_info) + return -1; + /* [0, total_pages) */ + memmap_info->efi_memdesc_size = sizeof(md[0]); + memmap_info->efi_memdesc_version = EFI_MEMORY_DESCRIPTOR_VERSION; + num_mds = 0; + md = (efi_memory_desc_t*)&memmap_info->memdesc; + md[num_mds].type = EFI_CONVENTIONAL_MEMORY; + md[num_mds].pad = 0; + md[num_mds].phys_addr = 0; + md[num_mds].virt_addr = 0; + md[num_mds].num_pages = dom->total_pages << (PAGE_SHIFT - EFI_PAGE_SHIFT); + md[num_mds].attribute = EFI_MEMORY_WB; + num_mds++; + memmap_info->efi_memmap_size = num_mds * sizeof(md[0]); + munmap(memmap_info, page_size * memmap_info_num_pages); + assert(num_mds <= + (page_size * memmap_info_num_pages - + offsetof(typeof(*memmap_info), memdesc))/sizeof(*md)); + + /* + * kludge: we need to pass memmap_info page's pfn and other magic pages + * somehow. + * we use xen_ia64_boot_param::efi_memmap::{efi_memmap, efi_memmap_size} + * for this purpose + */ + start_info = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, + page_size, + PROT_READ | PROT_WRITE, + dom->start_info_pfn); + if (NULL == start_info) + return -1; + bp = (struct xen_ia64_boot_param*)(start_info + sizeof(start_info_t)); + memset(bp, 0, sizeof(*bp)); + XEN_IA64_MEMMAP_INFO_NUM_PAGES(bp) = memmap_info_num_pages; + XEN_IA64_MEMMAP_INFO_PFN(bp) = memmap_info_pfn; + munmap(start_info, page_size); + return 0; +} + +int arch_setup_bootearly(struct xc_dom_image *dom) +{ + DECLARE_DOMCTL; + int rc; + + xc_dom_printf("%s: setup firmware for %s\n", __FUNCTION__, dom->guest_type); + + if (dom->guest_type && strcmp(dom->guest_type, + "hvm-3.0-ia64-sioemu") == 0) { + memset(&domctl, 0, sizeof(domctl)); + domctl.u.arch_setup.flags = XEN_DOMAINSETUP_sioemu_guest; + domctl.u.arch_setup.bp = 0; + domctl.u.arch_setup.maxmem = 0; + domctl.cmd = XEN_DOMCTL_arch_setup; + domctl.domain = dom->guest_domid; + rc = xc_domctl(dom->guest_xc, &domctl); + xc_dom_printf("%s: hvm-3.0-ia64-sioemu: %d\n", __FUNCTION__, rc); + return rc; + } + + rc = ia64_setup_memmap(dom); + if (rc) + return rc; + + memset(&domctl, 0, sizeof(domctl)); + domctl.cmd = XEN_DOMCTL_arch_setup; + domctl.domain = dom->guest_domid; + domctl.u.arch_setup.flags = XEN_DOMAINSETUP_query; + rc = do_domctl(dom->guest_xc, &domctl); + if (rc) + return rc; + rc = xen_ia64_dom_fw_setup(dom, domctl.u.arch_setup.hypercall_imm, + (dom->start_info_pfn << PAGE_SHIFT) + + sizeof(start_info_t), + dom->total_pages << PAGE_SHIFT); + if (rc) + return rc; + + memset(&domctl, 0, sizeof(domctl)); + domctl.cmd = XEN_DOMCTL_arch_setup; + domctl.domain = dom->guest_domid; + domctl.u.arch_setup.flags = 0; + + domctl.u.arch_setup.bp = (dom->start_info_pfn << PAGE_SHIFT) + + sizeof(start_info_t); + domctl.u.arch_setup.maxmem = dom->total_pages << PAGE_SHIFT; + domctl.u.arch_setup.vhpt_size_log2 = dom->vhpt_size_log2; + rc = do_domctl(dom->guest_xc, &domctl); + return rc; +} + +int arch_setup_bootlate(struct xc_dom_image *dom) +{ + unsigned int page_size = XC_DOM_PAGE_SIZE(dom); + shared_info_t *shared_info; + + /* setup shared_info page */ + xc_dom_printf("%s: shared_info: mfn 0x%" PRIpfn "\n", + __FUNCTION__, dom->shared_info_mfn); + shared_info = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, + page_size, + PROT_READ | PROT_WRITE, + dom->shared_info_mfn); + if ( shared_info == NULL ) + return -1; + dom->arch_hooks->shared_info(dom, shared_info); + munmap(shared_info, page_size); + return 0; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_dom_x86.c b/tools/libxc/xc_dom_x86.c new file mode 100644 index 0000000..f96ec3f --- /dev/null +++ b/tools/libxc/xc_dom_x86.c @@ -0,0 +1,829 @@ +/* + * Xen domain builder -- i386 and x86_64 bits. + * + * Most architecture-specific code for x86 goes here. + * - prepare page tables. + * - fill architecture-specific structs. + * + * This code is licenced under the GPL. + * written 2006 by Gerd Hoffmann . + * + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "xg_private.h" +#include "xc_dom.h" +#include "xenctrl.h" + +/* ------------------------------------------------------------------------ */ + +#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)) + +static unsigned long +nr_page_tables(xen_vaddr_t start, xen_vaddr_t end, unsigned long bits) +{ + xen_vaddr_t mask = bits_to_mask(bits); + int tables; + + if ( bits == 0 ) + return 0; /* unused */ + + if ( bits == (8 * sizeof(unsigned long)) ) + { + /* must be pgd, need one */ + start = 0; + end = -1; + tables = 1; + } + else + { + start = round_down(start, mask); + end = round_up(end, mask); + tables = ((end - start) >> bits) + 1; + } + + xc_dom_printf("%s: 0x%016" PRIx64 "/%ld: 0x%016" PRIx64 + " -> 0x%016" PRIx64 ", %d table(s)\n", + __FUNCTION__, mask, bits, start, end, tables); + return tables; +} + +static int count_pgtables(struct xc_dom_image *dom, int pae, + int l4_bits, int l3_bits, int l2_bits, int l1_bits) +{ + int pages, extra_pages; + xen_vaddr_t try_virt_end; + + extra_pages = dom->alloc_bootstack ? 1 : 0; + extra_pages += dom->extra_pages; + extra_pages += 128; /* 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 */ + dom->pg_l4 = + nr_page_tables(dom->parms.virt_base, try_virt_end, l4_bits); + dom->pg_l3 = + nr_page_tables(dom->parms.virt_base, try_virt_end, l3_bits); + dom->pg_l2 = + nr_page_tables(dom->parms.virt_base, try_virt_end, l2_bits); + dom->pg_l1 = + nr_page_tables(dom->parms.virt_base, try_virt_end, l1_bits); + if (pae && try_virt_end < 0xc0000000) + { + xc_dom_printf("%s: PAE: extra l2 page table for l3#3\n", + __FUNCTION__); + dom->pg_l2++; + } + dom->pgtables = dom->pg_l4 + dom->pg_l3 + dom->pg_l2 + dom->pg_l1; + pages = dom->pgtables + extra_pages; + if ( dom->virt_alloc_end + pages * PAGE_SIZE_X86 <= try_virt_end + 1 ) + break; + } + dom->virt_pgtab_end = try_virt_end + 1; + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* i386 pagetables */ + +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) +#define L3_PROT (_PAGE_PRESENT) + +static int count_pgtables_x86_32(struct xc_dom_image *dom) +{ + return count_pgtables(dom, 0, 0, 0, 32, L2_PAGETABLE_SHIFT_I386); +} + +static int count_pgtables_x86_32_pae(struct xc_dom_image *dom) +{ + return count_pgtables(dom, 1, 0, 32, + L3_PAGETABLE_SHIFT_PAE, L2_PAGETABLE_SHIFT_PAE); +} + +#define pfn_to_paddr(pfn) ((xen_paddr_t)(pfn) << PAGE_SHIFT_X86) + +static int setup_pgtables_x86_32(struct xc_dom_image *dom) +{ + xen_pfn_t l2pfn = dom->pgtables_seg.pfn; + xen_pfn_t l1pfn = dom->pgtables_seg.pfn + dom->pg_l2; + l2_pgentry_32_t *l2tab = xc_dom_pfn_to_ptr(dom, l2pfn, 1); + l1_pgentry_32_t *l1tab = NULL; + unsigned long l2off, l1off; + xen_vaddr_t addr; + xen_pfn_t pgpfn; + + for ( addr = dom->parms.virt_base; addr < dom->virt_pgtab_end; + addr += PAGE_SIZE_X86 ) + { + if ( l1tab == NULL ) + { + /* get L1 tab, make L2 entry */ + l1tab = xc_dom_pfn_to_ptr(dom, l1pfn, 1); + l2off = l2_table_offset_i386(addr); + l2tab[l2off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, l1pfn)) | L2_PROT; + l1pfn++; + } + + /* make L1 entry */ + l1off = l1_table_offset_i386(addr); + pgpfn = (addr - dom->parms.virt_base) >> PAGE_SHIFT_X86; + l1tab[l1off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, pgpfn)) | L1_PROT; + if ( (addr >= dom->pgtables_seg.vstart) && + (addr < dom->pgtables_seg.vend) ) + l1tab[l1off] &= ~_PAGE_RW; /* page tables are r/o */ + if ( l1off == (L1_PAGETABLE_ENTRIES_I386 - 1) ) + l1tab = NULL; + } + return 0; +} + +/* + * 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; + int xc = dom->guest_xc; + + mmu = xc_alloc_mmu_updates(xc, dom->guest_domid); + if ( mmu == NULL ) + { + xc_dom_printf("%s: failed at %d\n", __FUNCTION__, __LINE__); + return l3mfn; + } + + xc_dom_unmap_one(dom, l3pfn); + + new_l3mfn = xc_make_page_below_4G(dom->guest_xc, 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(xc, mmu, + (((unsigned long long)new_l3mfn) + << XC_DOM_PAGE_SHIFT(dom)) | + MMU_MACHPHYS_UPDATE, l3pfn) ) + goto out; + + if ( xc_flush_mmu_updates(xc, 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); + memset(l3tab, 0, XC_DOM_PAGE_SIZE(dom)); + + xc_dom_printf("%s: successfully relocated L3 below 4G. " + "(L3 PFN %#"PRIpfn" MFN %#"PRIpfn"=>%#"PRIpfn")\n", + __FUNCTION__, l3pfn, l3mfn, new_l3mfn); + + l3mfn = new_l3mfn; + + out: + free(mmu); + + return l3mfn; +} + +static int setup_pgtables_x86_32_pae(struct xc_dom_image *dom) +{ + xen_pfn_t l3pfn = dom->pgtables_seg.pfn; + xen_pfn_t l2pfn = dom->pgtables_seg.pfn + dom->pg_l3; + xen_pfn_t l1pfn = dom->pgtables_seg.pfn + dom->pg_l3 + dom->pg_l2; + l3_pgentry_64_t *l3tab; + l2_pgentry_64_t *l2tab = NULL; + l1_pgentry_64_t *l1tab = NULL; + unsigned long l3off, l2off, l1off; + xen_vaddr_t addr; + xen_pfn_t pgpfn; + xen_pfn_t l3mfn = xc_dom_p2m_guest(dom, l3pfn); + + if ( dom->parms.pae == 1 ) + { + if ( l3mfn >= 0x100000 ) + l3mfn = move_l3_below_4G(dom, l3pfn, l3mfn); + + if ( l3mfn >= 0x100000 ) + { + xc_dom_panic(XC_INTERNAL_ERROR,"%s: cannot move L3 below 4G. " + "extended-cr3 not supported by guest. " + "(L3 PFN %#"PRIpfn" MFN %#"PRIpfn")\n", + __FUNCTION__, l3pfn, l3mfn); + return -EINVAL; + } + } + + l3tab = xc_dom_pfn_to_ptr(dom, l3pfn, 1); + + for ( addr = dom->parms.virt_base; addr < dom->virt_pgtab_end; + addr += PAGE_SIZE_X86 ) + { + if ( l2tab == NULL ) + { + /* get L2 tab, make L3 entry */ + l2tab = xc_dom_pfn_to_ptr(dom, l2pfn, 1); + l3off = l3_table_offset_pae(addr); + l3tab[l3off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, l2pfn)) | L3_PROT; + l2pfn++; + } + + if ( l1tab == NULL ) + { + /* get L1 tab, make L2 entry */ + l1tab = xc_dom_pfn_to_ptr(dom, l1pfn, 1); + l2off = l2_table_offset_pae(addr); + l2tab[l2off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, l1pfn)) | L2_PROT; + if ( l2off == (L2_PAGETABLE_ENTRIES_PAE - 1) ) + l2tab = NULL; + l1pfn++; + } + + /* make L1 entry */ + l1off = l1_table_offset_pae(addr); + pgpfn = (addr - dom->parms.virt_base) >> PAGE_SHIFT_X86; + l1tab[l1off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, pgpfn)) | L1_PROT; + if ( (addr >= dom->pgtables_seg.vstart) && + (addr < dom->pgtables_seg.vend) ) + l1tab[l1off] &= ~_PAGE_RW; /* page tables are r/o */ + if ( l1off == (L1_PAGETABLE_ENTRIES_PAE - 1) ) + l1tab = NULL; + } + + if ( dom->virt_pgtab_end <= 0xc0000000 ) + { + xc_dom_printf("%s: PAE: extra l2 page table for l3#3\n", __FUNCTION__); + l3tab[3] = pfn_to_paddr(xc_dom_p2m_guest(dom, l2pfn)) | L3_PROT; + } + return 0; +} + +#undef L1_PROT +#undef L2_PROT +#undef L3_PROT + +/* ------------------------------------------------------------------------ */ +/* x86_64 pagetables */ + +static int count_pgtables_x86_64(struct xc_dom_image *dom) +{ + return count_pgtables(dom, 0, + L4_PAGETABLE_SHIFT_X86_64 + 9, + L4_PAGETABLE_SHIFT_X86_64, + L3_PAGETABLE_SHIFT_X86_64, + L2_PAGETABLE_SHIFT_X86_64); +} + +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) +#define L3_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) +#define L4_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) + +static int setup_pgtables_x86_64(struct xc_dom_image *dom) +{ + xen_pfn_t l4pfn = dom->pgtables_seg.pfn; + xen_pfn_t l3pfn = dom->pgtables_seg.pfn + dom->pg_l4; + xen_pfn_t l2pfn = dom->pgtables_seg.pfn + dom->pg_l4 + dom->pg_l3; + xen_pfn_t l1pfn = + dom->pgtables_seg.pfn + dom->pg_l4 + dom->pg_l3 + dom->pg_l2; + l4_pgentry_64_t *l4tab = xc_dom_pfn_to_ptr(dom, l4pfn, 1); + l3_pgentry_64_t *l3tab = NULL; + l2_pgentry_64_t *l2tab = NULL; + l1_pgentry_64_t *l1tab = NULL; + uint64_t l4off, l3off, l2off, l1off; + uint64_t addr; + xen_pfn_t pgpfn; + + for ( addr = dom->parms.virt_base; addr < dom->virt_pgtab_end; + addr += PAGE_SIZE_X86 ) + { + if ( l3tab == NULL ) + { + /* get L3 tab, make L4 entry */ + l3tab = xc_dom_pfn_to_ptr(dom, l3pfn, 1); + l4off = l4_table_offset_x86_64(addr); + l4tab[l4off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, l3pfn)) | L4_PROT; + l3pfn++; + } + + if ( l2tab == NULL ) + { + /* get L2 tab, make L3 entry */ + l2tab = xc_dom_pfn_to_ptr(dom, l2pfn, 1); + l3off = l3_table_offset_x86_64(addr); + l3tab[l3off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, l2pfn)) | L3_PROT; + if ( l3off == (L3_PAGETABLE_ENTRIES_X86_64 - 1) ) + l3tab = NULL; + l2pfn++; + } + + if ( l1tab == NULL ) + { + /* get L1 tab, make L2 entry */ + l1tab = xc_dom_pfn_to_ptr(dom, l1pfn, 1); + l2off = l2_table_offset_x86_64(addr); + l2tab[l2off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, l1pfn)) | L2_PROT; + if ( l2off == (L2_PAGETABLE_ENTRIES_X86_64 - 1) ) + l2tab = NULL; + l1pfn++; + } + + /* make L1 entry */ + l1off = l1_table_offset_x86_64(addr); + pgpfn = (addr - dom->parms.virt_base) >> PAGE_SHIFT_X86; + l1tab[l1off] = + pfn_to_paddr(xc_dom_p2m_guest(dom, pgpfn)) | L1_PROT; + if ( (addr >= dom->pgtables_seg.vstart) && + (addr < dom->pgtables_seg.vend) ) + l1tab[l1off] &= ~_PAGE_RW; /* page tables are r/o */ + if ( l1off == (L1_PAGETABLE_ENTRIES_X86_64 - 1) ) + l1tab = NULL; + } + return 0; +} + +#undef L1_PROT +#undef L2_PROT +#undef L3_PROT +#undef L4_PROT + +/* ------------------------------------------------------------------------ */ + +static int alloc_magic_pages(struct xc_dom_image *dom) +{ + size_t p2m_size = dom->total_pages * dom->arch_hooks->sizeof_pfn; + + /* allocate phys2mach table */ + if ( xc_dom_alloc_segment(dom, &dom->p2m_seg, "phys2mach", 0, p2m_size) ) + return -1; + dom->p2m_guest = xc_dom_seg_to_ptr(dom, &dom->p2m_seg); + + /* allocate special pages */ + dom->start_info_pfn = xc_dom_alloc_page(dom, "start info"); + dom->xenstore_pfn = xc_dom_alloc_page(dom, "xenstore"); + dom->console_pfn = xc_dom_alloc_page(dom, "console"); + if ( xc_dom_feature_translated(dom) ) + dom->shared_info_pfn = xc_dom_alloc_page(dom, "shared info"); + dom->alloc_bootstack = 1; + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int start_info_x86_32(struct xc_dom_image *dom) +{ + start_info_x86_32_t *start_info = + xc_dom_pfn_to_ptr(dom, dom->start_info_pfn, 1); + xen_pfn_t shinfo = + xc_dom_feature_translated(dom) ? dom->shared_info_pfn : dom-> + shared_info_mfn; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + memset(start_info, 0, sizeof(*start_info)); + snprintf(start_info->magic, sizeof(start_info->magic), dom->guest_type); + 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 = dom->pgtables; + start_info->mfn_list = dom->p2m_seg.vstart; + + start_info->flags = dom->flags; + start_info->store_mfn = xc_dom_p2m_guest(dom, dom->xenstore_pfn); + start_info->store_evtchn = dom->xenstore_evtchn; + start_info->console.domU.mfn = xc_dom_p2m_guest(dom, dom->console_pfn); + start_info->console.domU.evtchn = dom->console_evtchn; + + if ( dom->ramdisk_blob ) + { + start_info->mod_start = dom->ramdisk_seg.vstart; + start_info->mod_len = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; + } + + 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) +{ + start_info_x86_64_t *start_info = + xc_dom_pfn_to_ptr(dom, dom->start_info_pfn, 1); + xen_pfn_t shinfo = + xc_dom_feature_translated(dom) ? dom->shared_info_pfn : dom-> + shared_info_mfn; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + memset(start_info, 0, sizeof(*start_info)); + snprintf(start_info->magic, sizeof(start_info->magic), dom->guest_type); + 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 = dom->pgtables; + start_info->mfn_list = dom->p2m_seg.vstart; + + start_info->flags = dom->flags; + start_info->store_mfn = xc_dom_p2m_guest(dom, dom->xenstore_pfn); + start_info->store_evtchn = dom->xenstore_evtchn; + start_info->console.domU.mfn = xc_dom_p2m_guest(dom, dom->console_pfn); + start_info->console.domU.evtchn = dom->console_evtchn; + + if ( dom->ramdisk_blob ) + { + start_info->mod_start = dom->ramdisk_seg.vstart; + start_info->mod_len = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; + } + + 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; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + memset(shared_info, 0, sizeof(*shared_info)); + for ( i = 0; i < MAX_VIRT_CPUS; 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; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + memset(shared_info, 0, sizeof(*shared_info)); + for ( i = 0; i < MAX_VIRT_CPUS; i++ ) + shared_info->vcpu_info[i].evtchn_upcall_mask = 1; + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int vcpu_x86_32(struct xc_dom_image *dom, void *ptr) +{ + vcpu_guest_context_x86_32_t *ctxt = ptr; + xen_pfn_t cr3_pfn; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* clear everything */ + memset(ctxt, 0, sizeof(*ctxt)); + + 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->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->kernel_ss = ctxt->user_regs.ss; + ctxt->kernel_sp = ctxt->user_regs.esp; + + ctxt->flags = VGCF_in_kernel_X86_32 | VGCF_online_X86_32; + if ( dom->parms.pae == 2 /* extended_cr3 */ || + dom->parms.pae == 3 /* bimodal */ ) + ctxt->vm_assist |= (1UL << VMASST_TYPE_pae_extended_cr3); + + cr3_pfn = xc_dom_p2m_guest(dom, dom->pgtables_seg.pfn); + ctxt->ctrlreg[3] = xen_pfn_to_cr3_x86_32(cr3_pfn); + xc_dom_printf("%s: cr3: pfn 0x%" PRIpfn " mfn 0x%" PRIpfn "\n", + __FUNCTION__, dom->pgtables_seg.pfn, cr3_pfn); + + return 0; +} + +static int vcpu_x86_64(struct xc_dom_image *dom, void *ptr) +{ + vcpu_guest_context_x86_64_t *ctxt = ptr; + xen_pfn_t cr3_pfn; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + /* clear everything */ + memset(ctxt, 0, sizeof(*ctxt)); + + 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->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->kernel_ss = ctxt->user_regs.ss; + ctxt->kernel_sp = ctxt->user_regs.esp; + + ctxt->flags = VGCF_in_kernel_X86_64 | VGCF_online_X86_64; + cr3_pfn = xc_dom_p2m_guest(dom, dom->pgtables_seg.pfn); + ctxt->ctrlreg[3] = xen_pfn_to_cr3_x86_64(cr3_pfn); + xc_dom_printf("%s: cr3: pfn 0x%" PRIpfn " mfn 0x%" PRIpfn "\n", + __FUNCTION__, dom->pgtables_seg.pfn, cr3_pfn); + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct xc_dom_arch xc_dom_32 = { + .guest_type = "xen-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, + .count_pgtables = count_pgtables_x86_32, + .setup_pgtables = setup_pgtables_x86_32, + .start_info = start_info_x86_32, + .shared_info = shared_info_x86_32, + .vcpu = vcpu_x86_32, +}; +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, + .alloc_magic_pages = alloc_magic_pages, + .count_pgtables = count_pgtables_x86_32_pae, + .setup_pgtables = setup_pgtables_x86_32_pae, + .start_info = start_info_x86_32, + .shared_info = shared_info_x86_32, + .vcpu = vcpu_x86_32, +}; + +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, + .alloc_magic_pages = alloc_magic_pages, + .count_pgtables = count_pgtables_x86_64, + .setup_pgtables = setup_pgtables_x86_64, + .start_info = start_info_x86_64, + .shared_info = shared_info_x86_64, + .vcpu = vcpu_x86_64, +}; + +static void __init register_arch_hooks(void) +{ + xc_dom_register_arch_hooks(&xc_dom_32); + xc_dom_register_arch_hooks(&xc_dom_32_pae); + xc_dom_register_arch_hooks(&xc_dom_64); +} + +static int x86_compat(int xc, 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 < sizeof(types)/sizeof(types[0]); 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("%s: guest %s, address size %" PRId32 "\n", __FUNCTION__, + guest_type, domctl.u.address_size.size); + rc = do_domctl(xc, &domctl); + if ( rc != 0 ) + xc_dom_printf("%s: warning: failed (rc=%d)\n", + __FUNCTION__, rc); + return rc; +} + + +static int x86_shadow(int xc, domid_t domid) +{ + int rc, mode; + + xc_dom_printf("%s: called\n", __FUNCTION__); + + mode = XEN_DOMCTL_SHADOW_ENABLE_REFCOUNT | + XEN_DOMCTL_SHADOW_ENABLE_TRANSLATE; + + rc = xc_shadow_control(xc, domid, + XEN_DOMCTL_SHADOW_OP_ENABLE, + NULL, 0, NULL, mode, NULL); + if ( rc != 0 ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: SHADOW_OP_ENABLE (mode=0x%x) failed (rc=%d)\n", + __FUNCTION__, mode, rc); + return rc; + } + xc_dom_printf("%s: shadow enabled (mode=0x%x)\n", __FUNCTION__, mode); + return rc; +} + +int arch_setup_meminit(struct xc_dom_image *dom) +{ + int rc; + xen_pfn_t pfn; + + rc = x86_compat(dom->guest_xc, dom->guest_domid, dom->guest_type); + if ( rc ) + return rc; + if ( xc_dom_feature_translated(dom) ) + { + dom->shadow_enabled = 1; + rc = x86_shadow(dom->guest_xc, dom->guest_domid); + if ( rc ) + return rc; + } + + /* setup initial p2m */ + dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * dom->total_pages); + for ( pfn = 0; pfn < dom->total_pages; pfn++ ) + dom->p2m_host[pfn] = pfn; + + /* allocate guest memory */ + rc = xc_domain_memory_populate_physmap(dom->guest_xc, dom->guest_domid, + dom->total_pages, 0, 0, + dom->p2m_host); + return rc; +} + +int arch_setup_bootearly(struct xc_dom_image *dom) +{ + xc_dom_printf("%s: doing nothing\n", __FUNCTION__); + return 0; +} + +int arch_setup_bootlate(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 < sizeof(types) / sizeof(types[0]); i++ ) + if ( !strcmp(types[i].guest, dom->guest_type) ) + pgd_type = types[i].pgd_type; + + if ( !xc_dom_feature_translated(dom) ) + { + /* paravirtualized guest */ + xc_dom_unmap_one(dom, dom->pgtables_seg.pfn); + rc = pin_table(dom->guest_xc, pgd_type, + xc_dom_p2m_host(dom, dom->pgtables_seg.pfn), + dom->guest_domid); + if ( rc != 0 ) + { + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: pin_table failed (pfn 0x%" PRIpfn ", rc=%d)\n", + __FUNCTION__, dom->pgtables_seg.pfn, rc); + return rc; + } + shinfo = dom->shared_info_mfn; + } + else + { + /* paravirtualized guest with auto-translation */ + struct xen_add_to_physmap xatp; + int i; + + /* Map shared info frame into guest physmap. */ + xatp.domid = dom->guest_domid; + xatp.space = XENMAPSPACE_shared_info; + xatp.idx = 0; + xatp.gpfn = dom->shared_info_pfn; + rc = xc_memory_op(dom->guest_xc, XENMEM_add_to_physmap, &xatp); + if ( rc != 0 ) + { + xc_dom_panic(XC_INTERNAL_ERROR, "%s: mapping shared_info failed " + "(pfn=0x%" PRIpfn ", rc=%d)\n", + __FUNCTION__, xatp.gpfn, rc); + return rc; + } + + /* Map grant table frames into guest physmap. */ + for ( i = 0; ; i++ ) + { + xatp.domid = dom->guest_domid; + xatp.space = XENMAPSPACE_grant_table; + xatp.idx = i; + xatp.gpfn = dom->total_pages + i; + rc = xc_memory_op(dom->guest_xc, XENMEM_add_to_physmap, &xatp); + if ( rc != 0 ) + { + if ( (i > 0) && (errno == EINVAL) ) + { + xc_dom_printf("%s: %d grant tables mapped\n", __FUNCTION__, + i); + break; + } + xc_dom_panic(XC_INTERNAL_ERROR, + "%s: mapping grant tables failed " "(pfn=0x%" + PRIpfn ", rc=%d)\n", __FUNCTION__, xatp.gpfn, rc); + return rc; + } + } + shinfo = dom->shared_info_pfn; + } + + /* setup shared_info page */ + xc_dom_printf("%s: shared_info: pfn 0x%" PRIpfn ", mfn 0x%" PRIpfn "\n", + __FUNCTION__, dom->shared_info_pfn, dom->shared_info_mfn); + shared_info = xc_map_foreign_range(dom->guest_xc, 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; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c new file mode 100644 index 0000000..e1bf86e --- /dev/null +++ b/tools/libxc/xc_domain.c @@ -0,0 +1,1072 @@ +/****************************************************************************** + * xc_domain.c + * + * API for manipulating and obtaining information on domains. + * + * Copyright (c) 2003, K A Fraser. + */ + +#include "xc_private.h" +#include "xg_save_restore.h" +#include +#include + +int xc_domain_create(int xc_handle, + uint32_t ssidref, + xen_domain_handle_t handle, + uint32_t flags, + uint32_t *pdomid) +{ + int err; + DECLARE_DOMCTL; + + 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)); + if ( (err = do_domctl(xc_handle, &domctl)) != 0 ) + return err; + + *pdomid = (uint16_t)domctl.domain; + return 0; +} + + +int xc_domain_pause(int xc_handle, + uint32_t domid) +{ + DECLARE_DOMCTL; + domctl.cmd = XEN_DOMCTL_pausedomain; + domctl.domain = (domid_t)domid; + return do_domctl(xc_handle, &domctl); +} + + +int xc_domain_unpause(int xc_handle, + uint32_t domid) +{ + DECLARE_DOMCTL; + domctl.cmd = XEN_DOMCTL_unpausedomain; + domctl.domain = (domid_t)domid; + return do_domctl(xc_handle, &domctl); +} + + +int xc_domain_destroy(int xc_handle, + uint32_t domid) +{ + int ret; + DECLARE_DOMCTL; + domctl.cmd = XEN_DOMCTL_destroydomain; + domctl.domain = (domid_t)domid; + do { + ret = do_domctl(xc_handle, &domctl); + } while ( ret && (errno == EAGAIN) ); + return ret; +} + +int xc_domain_shutdown(int xc_handle, + uint32_t domid, + int reason) +{ + int ret = -1; + sched_remote_shutdown_t arg; + DECLARE_HYPERCALL; + + hypercall.op = __HYPERVISOR_sched_op; + hypercall.arg[0] = (unsigned long)SCHEDOP_remote_shutdown; + hypercall.arg[1] = (unsigned long)&arg; + arg.domain_id = domid; + arg.reason = reason; + + if ( lock_pages(&arg, sizeof(arg)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out1; + } + + ret = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + out1: + return ret; +} + + +int xc_vcpu_setaffinity(int xc_handle, + uint32_t domid, + int vcpu, + uint64_t cpumap) +{ + DECLARE_DOMCTL; + int ret = -1; + uint8_t local[sizeof (cpumap)]; + + domctl.cmd = XEN_DOMCTL_setvcpuaffinity; + domctl.domain = (domid_t)domid; + domctl.u.vcpuaffinity.vcpu = vcpu; + + bitmap_64_to_byte(local, &cpumap, sizeof(cpumap) * 8); + + set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap.bitmap, local); + + domctl.u.vcpuaffinity.cpumap.nr_cpus = sizeof(cpumap) * 8; + + if ( lock_pages(local, sizeof(local)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out; + } + + ret = do_domctl(xc_handle, &domctl); + + unlock_pages(local, sizeof(local)); + + out: + return ret; +} + + +int xc_vcpu_getaffinity(int xc_handle, + uint32_t domid, + int vcpu, + uint64_t *cpumap) +{ + DECLARE_DOMCTL; + int ret = -1; + uint8_t local[sizeof (cpumap)]; + + domctl.cmd = XEN_DOMCTL_getvcpuaffinity; + domctl.domain = (domid_t)domid; + domctl.u.vcpuaffinity.vcpu = vcpu; + + set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap.bitmap, local); + domctl.u.vcpuaffinity.cpumap.nr_cpus = sizeof(cpumap) * 8; + + if ( lock_pages(local, sizeof(local)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out; + } + + ret = do_domctl(xc_handle, &domctl); + + unlock_pages(local, sizeof (local)); + bitmap_byte_to_64(cpumap, local, sizeof(local) * 8); + out: + return ret; +} + + +int xc_domain_getinfo(int xc_handle, + 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(xc_handle, &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->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->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; + + 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(int xc_handle, + uint32_t first_domain, + unsigned int max_domains, + xc_domaininfo_t *info) +{ + int ret = 0; + DECLARE_SYSCTL; + + if ( lock_pages(info, max_domains*sizeof(xc_domaininfo_t)) != 0 ) + 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(xc_handle, &sysctl) < 0 ) + ret = -1; + else + ret = sysctl.u.getdomaininfolist.num_domains; + + unlock_pages(info, max_domains*sizeof(xc_domaininfo_t)); + + return ret; +} + +/* get info from hvm guest for save */ +int xc_domain_hvm_getcontext(int xc_handle, + uint32_t domid, + uint8_t *ctxt_buf, + uint32_t size) +{ + int ret; + DECLARE_DOMCTL; + + 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); + + if ( ctxt_buf ) + if ( (ret = lock_pages(ctxt_buf, size)) != 0 ) + return ret; + + ret = do_domctl(xc_handle, &domctl); + + if ( ctxt_buf ) + unlock_pages(ctxt_buf, size); + + return (ret < 0 ? -1 : domctl.u.hvmcontext.size); +} + +/* set info to hvm guest for restore */ +int xc_domain_hvm_setcontext(int xc_handle, + uint32_t domid, + uint8_t *ctxt_buf, + uint32_t size) +{ + int ret; + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_sethvmcontext; + domctl.domain = domid; + domctl.u.hvmcontext.size = size; + set_xen_guest_handle(domctl.u.hvmcontext.buffer, ctxt_buf); + + if ( (ret = lock_pages(ctxt_buf, size)) != 0 ) + return ret; + + ret = do_domctl(xc_handle, &domctl); + + unlock_pages(ctxt_buf, size); + + return ret; +} + +int xc_vcpu_getcontext(int xc_handle, + uint32_t domid, + uint32_t vcpu, + vcpu_guest_context_any_t *ctxt) +{ + int rc; + DECLARE_DOMCTL; + size_t sz = sizeof(vcpu_guest_context_any_t); + + 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->c); + + + if ( (rc = lock_pages(ctxt, sz)) != 0 ) + return rc; + rc = do_domctl(xc_handle, &domctl); + unlock_pages(ctxt, sz); + + return rc; +} + + +int xc_shadow_control(int xc_handle, + uint32_t domid, + unsigned int sop, + unsigned long *dirty_bitmap, + unsigned long pages, + unsigned long *mb, + uint32_t mode, + xc_shadow_op_stats_t *stats) +{ + int rc; + DECLARE_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; + set_xen_guest_handle(domctl.u.shadow_op.dirty_bitmap, + (uint8_t *)dirty_bitmap); + + rc = do_domctl(xc_handle, &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(int xc_handle, + uint32_t domid, + unsigned int 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(xc_handle, &domctl); +} + +int xc_domain_pin_memory_cacheattr(int xc_handle, + 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(xc_handle, &domctl); +} + +#if defined(__i386__) || defined(__x86_64__) +#include "xc_e820.h" +int xc_domain_set_memmap_limit(int xc_handle, + uint32_t domid, + unsigned long map_limitkb) +{ + int rc; + + struct xen_foreign_memory_map fmap = { + .domid = domid, + .map = { .nr_entries = 1 } + }; + + struct e820entry e820 = { + .addr = 0, + .size = (uint64_t)map_limitkb << 10, + .type = E820_RAM + }; + + set_xen_guest_handle(fmap.map.buffer, &e820); + + if ( lock_pages(&fmap, sizeof(fmap)) || lock_pages(&e820, sizeof(e820)) ) + { + PERROR("Could not lock memory for Xen hypercall"); + rc = -1; + goto out; + } + + rc = xc_memory_op(xc_handle, XENMEM_set_memory_map, &fmap); + + out: + unlock_pages(&fmap, sizeof(fmap)); + unlock_pages(&e820, sizeof(e820)); + return rc; +} +#else +int xc_domain_set_memmap_limit(int xc_handle, + uint32_t domid, + unsigned long map_limitkb) +{ + PERROR("Function not implemented"); + errno = ENOSYS; + return -1; +} +#endif + +int xc_domain_set_time_offset(int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_domain_memory_increase_reservation(int xc_handle, + uint32_t domid, + unsigned long nr_extents, + unsigned int extent_order, + unsigned int mem_flags, + xen_pfn_t *extent_start) +{ + int err; + struct xen_memory_reservation reservation = { + .nr_extents = nr_extents, + .extent_order = extent_order, + .mem_flags = mem_flags, + .domid = domid + }; + + /* may be NULL */ + set_xen_guest_handle(reservation.extent_start, extent_start); + + err = xc_memory_op(xc_handle, XENMEM_increase_reservation, &reservation); + 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_memory_decrease_reservation(int xc_handle, + uint32_t domid, + unsigned long nr_extents, + unsigned int extent_order, + xen_pfn_t *extent_start) +{ + int err; + struct xen_memory_reservation reservation = { + .nr_extents = nr_extents, + .extent_order = extent_order, + .mem_flags = 0, + .domid = domid + }; + + set_xen_guest_handle(reservation.extent_start, extent_start); + + if ( extent_start == NULL ) + { + DPRINTF("decrease_reservation extent_start is NULL!\n"); + errno = EINVAL; + return -1; + } + + err = xc_memory_op(xc_handle, XENMEM_decrease_reservation, &reservation); + 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_memory_populate_physmap(int xc_handle, + uint32_t domid, + unsigned long nr_extents, + unsigned int extent_order, + unsigned int mem_flags, + xen_pfn_t *extent_start) +{ + int err; + struct xen_memory_reservation reservation = { + .nr_extents = nr_extents, + .extent_order = extent_order, + .mem_flags = mem_flags, + .domid = domid + }; + set_xen_guest_handle(reservation.extent_start, extent_start); + + err = xc_memory_op(xc_handle, XENMEM_populate_physmap, &reservation); + 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_translate_gpfn_list(int xc_handle, + uint32_t domid, + unsigned long nr_gpfns, + xen_pfn_t *gpfn_list, + xen_pfn_t *mfn_list) +{ + int err; + struct xen_translate_gpfn_list translate_gpfn_list = { + .domid = domid, + .nr_gpfns = nr_gpfns, + }; + set_xen_guest_handle(translate_gpfn_list.gpfn_list, gpfn_list); + set_xen_guest_handle(translate_gpfn_list.mfn_list, mfn_list); + + err = xc_memory_op(xc_handle, XENMEM_translate_gpfn_list, &translate_gpfn_list); + + if ( err != 0 ) + { + DPRINTF("Failed translation for dom %d (%ld PFNs)\n", + domid, nr_gpfns); + errno = -err; + err = -1; + } + + return err; +} + +int xc_domain_max_vcpus(int xc_handle, 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(xc_handle, &domctl); +} + +int xc_domain_sethandle(int xc_handle, 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(xc_handle, &domctl); +} + +int xc_vcpu_getinfo(int xc_handle, + 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(xc_handle, &domctl); + + memcpy(info, &domctl.u.getvcpuinfo, sizeof(*info)); + + return rc; +} + +int xc_domain_ioport_permission(int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_availheap(int xc_handle, + 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(xc_handle, &sysctl); + + *bytes = sysctl.u.availheap.avail_bytes; + + return rc; +} + +int xc_vcpu_setcontext(int xc_handle, + uint32_t domid, + uint32_t vcpu, + vcpu_guest_context_any_t *ctxt) +{ + DECLARE_DOMCTL; + int rc; + size_t sz = sizeof(vcpu_guest_context_any_t); + + if (ctxt == NULL) + { + errno = EINVAL; + return -1; + } + + domctl.cmd = XEN_DOMCTL_setvcpucontext; + domctl.domain = domid; + domctl.u.vcpucontext.vcpu = vcpu; + set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt->c); + + if ( (rc = lock_pages(ctxt, sz)) != 0 ) + return rc; + rc = do_domctl(xc_handle, &domctl); + + unlock_pages(ctxt, sz); + + return rc; +} + +int xc_domain_irq_permission(int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_domain_iomem_permission(int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_domain_send_trigger(int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_set_hvm_param(int handle, domid_t dom, int param, unsigned long value) +{ + DECLARE_HYPERCALL; + xen_hvm_param_t arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_param; + hypercall.arg[1] = (unsigned long)&arg; + arg.domid = dom; + arg.index = param; + arg.value = value; + if ( lock_pages(&arg, sizeof(arg)) != 0 ) + return -1; + rc = do_xen_hypercall(handle, &hypercall); + unlock_pages(&arg, sizeof(arg)); + return rc; +} + +int xc_get_hvm_param(int handle, domid_t dom, int param, unsigned long *value) +{ + DECLARE_HYPERCALL; + xen_hvm_param_t arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_get_param; + hypercall.arg[1] = (unsigned long)&arg; + arg.domid = dom; + arg.index = param; + if ( lock_pages(&arg, sizeof(arg)) != 0 ) + return -1; + rc = do_xen_hypercall(handle, &hypercall); + unlock_pages(&arg, sizeof(arg)); + *value = arg.value; + return rc; +} + +int xc_domain_setdebugging(int xc_handle, + uint32_t domid, + unsigned int enable) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_setdebugging; + domctl.domain = domid; + domctl.u.setdebugging.enable = enable; + return do_domctl(xc_handle, &domctl); +} + +int xc_assign_device( + int xc_handle, + uint32_t domid, + uint32_t machine_bdf) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_assign_device; + domctl.domain = domid; + domctl.u.assign_device.machine_bdf = machine_bdf; + + return do_domctl(xc_handle, &domctl); +} + +int xc_get_device_group( + int xc_handle, + uint32_t domid, + uint32_t machine_bdf, + uint32_t max_sdevs, + uint32_t *num_sdevs, + uint32_t *sdev_array) +{ + int rc; + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_get_device_group; + domctl.domain = (domid_t)domid; + + domctl.u.get_device_group.machine_bdf = machine_bdf; + domctl.u.get_device_group.max_sdevs = max_sdevs; + + set_xen_guest_handle(domctl.u.get_device_group.sdev_array, sdev_array); + + if ( lock_pages(sdev_array, max_sdevs * sizeof(*sdev_array)) != 0 ) + { + PERROR("Could not lock memory for xc_get_device_group\n"); + return -ENOMEM; + } + rc = do_domctl(xc_handle, &domctl); + unlock_pages(sdev_array, max_sdevs * sizeof(*sdev_array)); + + *num_sdevs = domctl.u.get_device_group.num_sdevs; + return rc; +} + +int xc_test_assign_device( + int xc_handle, + uint32_t domid, + uint32_t machine_bdf) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_test_assign_device; + domctl.domain = domid; + domctl.u.assign_device.machine_bdf = machine_bdf; + + return do_domctl(xc_handle, &domctl); +} + +int xc_deassign_device( + int xc_handle, + uint32_t domid, + uint32_t machine_bdf) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_deassign_device; + domctl.domain = domid; + domctl.u.assign_device.machine_bdf = machine_bdf; + + return do_domctl(xc_handle, &domctl); +} + +int xc_domain_update_msi_irq( + int xc_handle, + 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_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; + + rc = do_domctl(xc_handle, &domctl); + return rc; +} + +/* Pass-through: binds machine irq to guests irq */ +int xc_domain_bind_pt_irq( + int xc_handle, + 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 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; + if ( irq_type == PT_IRQ_TYPE_PCI ) + { + bind->u.pci.bus = bus; + bind->u.pci.device = device; + bind->u.pci.intx = intx; + } + else if ( irq_type == PT_IRQ_TYPE_ISA ) + bind->u.isa.isa_irq = isa_irq; + + rc = do_domctl(xc_handle, &domctl); + return rc; +} + +int xc_domain_unbind_pt_irq( + int xc_handle, + 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 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; + bind->u.pci.bus = bus; + bind->u.pci.device = device; + bind->u.pci.intx = intx; + bind->u.isa.isa_irq = isa_irq; + + rc = do_domctl(xc_handle, &domctl); + return rc; +} + +int xc_domain_bind_pt_pci_irq( + int xc_handle, + uint32_t domid, + uint8_t machine_irq, + uint8_t bus, + uint8_t device, + uint8_t intx) +{ + + return (xc_domain_bind_pt_irq(xc_handle, domid, machine_irq, + PT_IRQ_TYPE_PCI, bus, device, intx, 0)); +} + +int xc_domain_bind_pt_isa_irq( + int xc_handle, + uint32_t domid, + uint8_t machine_irq) +{ + + return (xc_domain_bind_pt_irq(xc_handle, domid, machine_irq, + PT_IRQ_TYPE_ISA, 0, 0, 0, machine_irq)); +} + +int xc_domain_memory_mapping( + int xc_handle, + uint32_t domid, + unsigned long first_gfn, + unsigned long first_mfn, + unsigned long nr_mfns, + uint32_t add_mapping) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_memory_mapping; + domctl.domain = domid; + domctl.u.memory_mapping.first_gfn = first_gfn; + domctl.u.memory_mapping.first_mfn = first_mfn; + domctl.u.memory_mapping.nr_mfns = nr_mfns; + domctl.u.memory_mapping.add_mapping = add_mapping; + + return do_domctl(xc_handle, &domctl); +} + +int xc_domain_ioport_mapping( + int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_domain_set_target( + int xc_handle, + 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(xc_handle, &domctl); +} + +int xc_domain_subscribe_for_suspend( + int xc_handle, 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(xc_handle, &domctl); +} + +int xc_domain_set_machine_address_size(int xc, + 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(xc, &domctl); +} + + +int xc_domain_get_machine_address_size(int xc, 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(xc, &domctl); + + return rc == 0 ? domctl.u.address_size.size : rc; +} + +int xc_domain_suppress_spurious_page_faults(int 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); + +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_domain_restore.c b/tools/libxc/xc_domain_restore.c new file mode 100644 index 0000000..1916728 --- /dev/null +++ b/tools/libxc/xc_domain_restore.c @@ -0,0 +1,1209 @@ +/****************************************************************************** + * xc_domain_restore.c + * + * Restore the state of a guest session. + * + * Copyright (c) 2003, K A Fraser. + * Copyright (c) 2006, Intel Corporation + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#include +#include + +#include "xg_private.h" +#include "xg_save_restore.h" +#include "xc_dom.h" + +#include +#include + +/* max mfn of the current host machine */ +static unsigned long max_mfn; + +/* virtual starting address of the hypervisor */ +static unsigned long hvirt_start; + +/* #levels of page tables used by the current guest */ +static unsigned int pt_levels; + +/* number of pfns this guest has (i.e. number of entries in the P2M) */ +static unsigned long p2m_size; + +/* number of 'in use' pfns in the guest (i.e. #P2M entries with a valid mfn) */ +static unsigned long nr_pfns; + +/* Live mapping of the table mapping each PFN to its current MFN. */ +static xen_pfn_t *live_p2m = NULL; + +/* A table mapping each PFN to its new MFN. */ +static xen_pfn_t *p2m = NULL; + +/* A table of P2M mappings in the current region */ +static xen_pfn_t *p2m_batch = NULL; + +/* Address size of the guest, in bytes */ +unsigned int guest_width; + +/* +** In the state file (or during transfer), all page-table pages are +** converted into a 'canonical' form where references to actual mfns +** are replaced with references to the corresponding pfns. +** This function inverts that operation, replacing the pfn values with +** the (now known) appropriate mfn values. +*/ +static int uncanonicalize_pagetable(int xc_handle, uint32_t dom, + unsigned long type, void *page) +{ + int i, pte_last; + unsigned long pfn; + uint64_t pte; + int nr_mfns = 0; + + pte_last = PAGE_SIZE / ((pt_levels == 2)? 4 : 8); + + /* First pass: work out how many (if any) MFNs we need to alloc */ + for ( i = 0; i < pte_last; i++ ) + { + if ( pt_levels == 2 ) + pte = ((uint32_t *)page)[i]; + else + pte = ((uint64_t *)page)[i]; + + /* XXX SMH: below needs fixing for PROT_NONE etc */ + if ( !(pte & _PAGE_PRESENT) ) + continue; + + pfn = (pte >> PAGE_SHIFT) & MFN_MASK_X86; + + if ( pfn >= p2m_size ) + { + /* This "page table page" is probably not one; bail. */ + ERROR("Frame number in type %lu page table is out of range: " + "i=%d pfn=0x%lx p2m_size=%lu", + type >> 28, i, pfn, p2m_size); + return 0; + } + + if ( p2m[pfn] == INVALID_P2M_ENTRY ) + { + /* Have a 'valid' PFN without a matching MFN - need to alloc */ + p2m_batch[nr_mfns++] = pfn; + p2m[pfn]--; + } + } + + /* Allocate the requisite number of mfns. */ + if ( nr_mfns && + (xc_domain_memory_populate_physmap(xc_handle, dom, nr_mfns, 0, 0, + p2m_batch) != 0) ) + { + ERROR("Failed to allocate memory for batch.!\n"); + errno = ENOMEM; + return 0; + } + + /* Second pass: uncanonicalize each present PTE */ + nr_mfns = 0; + for ( i = 0; i < pte_last; i++ ) + { + if ( pt_levels == 2 ) + pte = ((uint32_t *)page)[i]; + else + pte = ((uint64_t *)page)[i]; + + /* XXX SMH: below needs fixing for PROT_NONE etc */ + if ( !(pte & _PAGE_PRESENT) ) + continue; + + pfn = (pte >> PAGE_SHIFT) & MFN_MASK_X86; + + if ( p2m[pfn] == (INVALID_P2M_ENTRY-1) ) + p2m[pfn] = p2m_batch[nr_mfns++]; + + pte &= ~MADDR_MASK_X86; + pte |= (uint64_t)p2m[pfn] << PAGE_SHIFT; + + if ( pt_levels == 2 ) + ((uint32_t *)page)[i] = (uint32_t)pte; + else + ((uint64_t *)page)[i] = (uint64_t)pte; + } + + return 1; +} + + +/* Load the p2m frame list, plus potential extended info chunk */ +static xen_pfn_t *load_p2m_frame_list( + int io_fd, int *pae_extended_cr3, int *ext_vcpucontext) +{ + xen_pfn_t *p2m_frame_list; + vcpu_guest_context_any_t ctxt; + xen_pfn_t p2m_fl_zero; + + /* Read first entry of P2M list, or extended-info signature (~0UL). */ + if ( read_exact(io_fd, &p2m_fl_zero, sizeof(long)) ) + { + ERROR("read extended-info signature failed"); + return NULL; + } + + if ( p2m_fl_zero == ~0UL ) + { + uint32_t tot_bytes; + + /* Next 4 bytes: total size of following extended info. */ + if ( read_exact(io_fd, &tot_bytes, sizeof(tot_bytes)) ) + { + ERROR("read extended-info size failed"); + return NULL; + } + + while ( tot_bytes ) + { + uint32_t chunk_bytes; + char chunk_sig[4]; + + /* 4-character chunk signature + 4-byte remaining chunk size. */ + if ( read_exact(io_fd, chunk_sig, sizeof(chunk_sig)) || + read_exact(io_fd, &chunk_bytes, sizeof(chunk_bytes)) || + (tot_bytes < (chunk_bytes + 8)) ) + { + ERROR("read extended-info chunk signature failed"); + return NULL; + } + tot_bytes -= 8; + + /* VCPU context structure? */ + if ( !strncmp(chunk_sig, "vcpu", 4) ) + { + /* Pick a guest word-size and PT depth from the ctxt size */ + if ( chunk_bytes == sizeof (ctxt.x32) ) + { + guest_width = 4; + if ( pt_levels > 2 ) + pt_levels = 3; + } + else if ( chunk_bytes == sizeof (ctxt.x64) ) + { + guest_width = 8; + pt_levels = 4; + } + else + { + ERROR("bad extended-info context size %d", chunk_bytes); + return NULL; + } + + if ( read_exact(io_fd, &ctxt, chunk_bytes) ) + { + ERROR("read extended-info vcpu context failed"); + return NULL; + } + tot_bytes -= chunk_bytes; + chunk_bytes = 0; + + if ( GET_FIELD(&ctxt, vm_assist) + & (1UL << VMASST_TYPE_pae_extended_cr3) ) + *pae_extended_cr3 = 1; + } + else if ( !strncmp(chunk_sig, "extv", 4) ) + { + *ext_vcpucontext = 1; + } + + /* Any remaining bytes of this chunk: read and discard. */ + while ( chunk_bytes ) + { + unsigned long sz = MIN(chunk_bytes, sizeof(xen_pfn_t)); + if ( read_exact(io_fd, &p2m_fl_zero, sz) ) + { + ERROR("read-and-discard extended-info chunk bytes failed"); + return NULL; + } + chunk_bytes -= sz; + tot_bytes -= sz; + } + } + + /* Now read the real first entry of P2M list. */ + if ( read_exact(io_fd, &p2m_fl_zero, sizeof(xen_pfn_t)) ) + { + ERROR("read first entry of p2m_frame_list failed"); + return NULL; + } + } + + /* Now that we know the guest's word-size, can safely allocate + * the p2m frame list */ + if ( (p2m_frame_list = malloc(P2M_TOOLS_FL_SIZE)) == NULL ) + { + ERROR("Couldn't allocate p2m_frame_list array"); + return NULL; + } + + /* First entry has already been read. */ + p2m_frame_list[0] = p2m_fl_zero; + if ( read_exact(io_fd, &p2m_frame_list[1], + (P2M_FL_ENTRIES - 1) * sizeof(xen_pfn_t)) ) + { + ERROR("read p2m_frame_list failed"); + return NULL; + } + + return p2m_frame_list; +} + +int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, + unsigned int store_evtchn, unsigned long *store_mfn, + unsigned int console_evtchn, unsigned long *console_mfn, + unsigned int hvm, unsigned int pae) +{ + DECLARE_DOMCTL; + int rc = 1, frc, i, j, n, m, pae_extended_cr3 = 0, ext_vcpucontext = 0; + unsigned long mfn, pfn; + unsigned int prev_pc, this_pc; + int verify = 0; + int nraces = 0; + + /* The new domain's shared-info frame number. */ + unsigned long shared_info_frame; + unsigned char shared_info_page[PAGE_SIZE]; /* saved contents from file */ + shared_info_any_t *old_shared_info = + (shared_info_any_t *)shared_info_page; + shared_info_any_t *new_shared_info; + + /* A copy of the CPU context of the guest. */ + vcpu_guest_context_any_t ctxt; + + /* A table containing the type of each PFN (/not/ MFN!). */ + unsigned long *pfn_type = NULL; + + /* A table of MFNs to map in the current region */ + xen_pfn_t *region_mfn = NULL; + + /* Types of the pfns in the current region */ + unsigned long region_pfn_type[MAX_BATCH_SIZE]; + + /* A copy of the pfn-to-mfn table frame list. */ + xen_pfn_t *p2m_frame_list = NULL; + + /* A temporary mapping of the guest's start_info page. */ + start_info_any_t *start_info; + + /* Our mapping of the current region (batch) */ + char *region_base; + + struct xc_mmu *mmu = NULL; + + /* used by debug verify code */ + unsigned long buf[PAGE_SIZE/sizeof(unsigned long)]; + + struct mmuext_op pin[MAX_PIN_BATCH]; + unsigned int nr_pins; + + uint64_t vcpumap = 1ULL; + unsigned int max_vcpu_id = 0; + int new_ctxt_format = 0; + + /* Magic frames in HVM guests: ioreqs and xenstore comms. */ + uint64_t magic_pfns[3]; /* ioreq_pfn, bufioreq_pfn, store_pfn */ + + /* Buffer for holding HVM context */ + uint8_t *hvm_buf = NULL; + + /* For info only */ + nr_pfns = 0; + + if ( read_exact(io_fd, &p2m_size, sizeof(unsigned long)) ) + { + ERROR("read: p2m_size"); + goto out; + } + DPRINTF("xc_domain_restore start: p2m_size = %lx\n", p2m_size); + + if ( !get_platform_info(xc_handle, dom, + &max_mfn, &hvirt_start, &pt_levels, &guest_width) ) + { + ERROR("Unable to get platform info."); + return 1; + } + + /* The *current* word size of the guest isn't very interesting; for now + * assume the guest will be the same as we are. We'll fix that later + * if we discover otherwise. */ + guest_width = sizeof(unsigned long); + pt_levels = (guest_width == 8) ? 4 : (pt_levels == 2) ? 2 : 3; + + if ( !hvm ) + { + /* Load the p2m frame list, plus potential extended info chunk */ + p2m_frame_list = load_p2m_frame_list( + io_fd, &pae_extended_cr3, &ext_vcpucontext); + if ( !p2m_frame_list ) + goto out; + + /* Now that we know the word size, tell Xen about it */ + memset(&domctl, 0, sizeof(domctl)); + domctl.domain = dom; + domctl.cmd = XEN_DOMCTL_set_address_size; + domctl.u.address_size.size = guest_width * 8; + frc = do_domctl(xc_handle, &domctl); + if ( frc != 0 ) + { + ERROR("Unable to set guest address size."); + goto out; + } + } + + /* We want zeroed memory so use calloc rather than malloc. */ + p2m = calloc(p2m_size, sizeof(xen_pfn_t)); + pfn_type = calloc(p2m_size, sizeof(unsigned long)); + + region_mfn = xg_memalign(PAGE_SIZE, ROUNDUP( + MAX_BATCH_SIZE * sizeof(xen_pfn_t), PAGE_SHIFT)); + p2m_batch = xg_memalign(PAGE_SIZE, ROUNDUP( + MAX_BATCH_SIZE * sizeof(xen_pfn_t), PAGE_SHIFT)); + + if ( (p2m == NULL) || (pfn_type == NULL) || + (region_mfn == NULL) || (p2m_batch == NULL) ) + { + ERROR("memory alloc failed"); + errno = ENOMEM; + goto out; + } + + memset(region_mfn, 0, + ROUNDUP(MAX_BATCH_SIZE * sizeof(xen_pfn_t), PAGE_SHIFT)); + memset(p2m_batch, 0, + ROUNDUP(MAX_BATCH_SIZE * sizeof(xen_pfn_t), PAGE_SHIFT)); + + if ( lock_pages(region_mfn, sizeof(xen_pfn_t) * MAX_BATCH_SIZE) ) + { + ERROR("Could not lock region_mfn"); + goto out; + } + + if ( lock_pages(p2m_batch, sizeof(xen_pfn_t) * MAX_BATCH_SIZE) ) + { + ERROR("Could not lock p2m_batch"); + goto out; + } + + /* Get the domain's shared-info frame. */ + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)dom; + if ( xc_domctl(xc_handle, &domctl) < 0 ) + { + ERROR("Could not get information on new domain"); + goto out; + } + shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; + + /* Mark all PFNs as invalid; we allocate on demand */ + for ( pfn = 0; pfn < p2m_size; pfn++ ) + p2m[pfn] = INVALID_P2M_ENTRY; + + mmu = xc_alloc_mmu_updates(xc_handle, dom); + if ( mmu == NULL ) + { + ERROR("Could not initialise for MMU updates"); + goto out; + } + + DPRINTF("Reloading memory pages: 0%%\n"); + + /* + * Now simply read each saved frame into its new machine frame. + * We uncanonicalise page tables as we go. + */ + prev_pc = 0; + + n = m = 0; + for ( ; ; ) + { + int j, nr_mfns = 0; + + this_pc = (n * 100) / p2m_size; + if ( (this_pc - prev_pc) >= 5 ) + { + PPRINTF("\b\b\b\b%3d%%", this_pc); + prev_pc = this_pc; + } + + if ( read_exact(io_fd, &j, sizeof(int)) ) + { + ERROR("Error when reading batch size"); + goto out; + } + + PPRINTF("batch %d\n",j); + + if ( j == -1 ) + { + verify = 1; + DPRINTF("Entering page verify mode\n"); + continue; + } + + if ( j == -2 ) + { + new_ctxt_format = 1; + if ( read_exact(io_fd, &max_vcpu_id, sizeof(int)) || + (max_vcpu_id >= 64) || + read_exact(io_fd, &vcpumap, sizeof(uint64_t)) ) + { + ERROR("Error when reading max_vcpu_id"); + goto out; + } + continue; + } + + if ( j == -3 ) + { + uint64_t ident_pt; + + /* Skip padding 4 bytes then read the EPT identity PT location. */ + if ( read_exact(io_fd, &ident_pt, sizeof(uint32_t)) || + read_exact(io_fd, &ident_pt, sizeof(uint64_t)) ) + { + ERROR("error read the address of the EPT identity map"); + goto out; + } + + xc_set_hvm_param(xc_handle, dom, HVM_PARAM_IDENT_PT, ident_pt); + continue; + } + + if ( j == 0 ) + break; /* our work here is done */ + + if ( (j > MAX_BATCH_SIZE) || (j < 0) ) + { + ERROR("Max batch size exceeded. Giving up."); + goto out; + } + + if ( read_exact(io_fd, region_pfn_type, j*sizeof(unsigned long)) ) + { + ERROR("Error when reading region pfn types"); + goto out; + } + + /* First pass for this batch: work out how much memory to alloc */ + nr_mfns = 0; + for ( i = 0; i < j; i++ ) + { + unsigned long pfn, pagetype; + pfn = region_pfn_type[i] & ~XEN_DOMCTL_PFINFO_LTAB_MASK; + pagetype = region_pfn_type[i] & XEN_DOMCTL_PFINFO_LTAB_MASK; + + if ( (pagetype != XEN_DOMCTL_PFINFO_XTAB) && + (p2m[pfn] == INVALID_P2M_ENTRY) ) + { + /* Have a live PFN which hasn't had an MFN allocated */ + p2m_batch[nr_mfns++] = pfn; + p2m[pfn]--; + } + } + + /* Now allocate a bunch of mfns for this batch */ + if ( nr_mfns && + (xc_domain_memory_populate_physmap(xc_handle, dom, nr_mfns, 0, + 0, p2m_batch) != 0) ) + { + ERROR("Failed to allocate memory for batch.!\n"); + errno = ENOMEM; + goto out; + } + + /* Second pass for this batch: update p2m[] and region_mfn[] */ + nr_mfns = 0; + for ( i = 0; i < j; i++ ) + { + unsigned long pfn, pagetype; + pfn = region_pfn_type[i] & ~XEN_DOMCTL_PFINFO_LTAB_MASK; + pagetype = region_pfn_type[i] & XEN_DOMCTL_PFINFO_LTAB_MASK; + + if ( pagetype == XEN_DOMCTL_PFINFO_XTAB ) + region_mfn[i] = ~0UL; /* map will fail but we don't care */ + else + { + if ( p2m[pfn] == (INVALID_P2M_ENTRY-1) ) + { + /* We just allocated a new mfn above; update p2m */ + p2m[pfn] = p2m_batch[nr_mfns++]; + nr_pfns++; + } + + /* setup region_mfn[] for batch map. + * For HVM guests, this interface takes PFNs, not MFNs */ + region_mfn[i] = hvm ? pfn : p2m[pfn]; + } + } + + /* Map relevant mfns */ + region_base = xc_map_foreign_batch( + xc_handle, dom, PROT_WRITE, region_mfn, j); + + if ( region_base == NULL ) + { + ERROR("map batch failed"); + goto out; + } + + for ( i = 0; i < j; i++ ) + { + void *page; + unsigned long pagetype; + + pfn = region_pfn_type[i] & ~XEN_DOMCTL_PFINFO_LTAB_MASK; + pagetype = region_pfn_type[i] & XEN_DOMCTL_PFINFO_LTAB_MASK; + + if ( pagetype == XEN_DOMCTL_PFINFO_XTAB ) + /* a bogus/unmapped page: skip it */ + continue; + + if ( pfn > p2m_size ) + { + ERROR("pfn out of range"); + goto out; + } + + pfn_type[pfn] = pagetype; + + mfn = p2m[pfn]; + + /* In verify mode, we use a copy; otherwise we work in place */ + page = verify ? (void *)buf : (region_base + i*PAGE_SIZE); + + if ( read_exact(io_fd, page, PAGE_SIZE) ) + { + ERROR("Error when reading page (type was %lx)", pagetype); + goto out; + } + + pagetype &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK; + + if ( (pagetype >= XEN_DOMCTL_PFINFO_L1TAB) && + (pagetype <= XEN_DOMCTL_PFINFO_L4TAB) ) + { + /* + ** A page table page - need to 'uncanonicalize' it, i.e. + ** replace all the references to pfns with the corresponding + ** mfns for the new domain. + ** + ** On PAE we need to ensure that PGDs are in MFNs < 4G, and + ** so we may need to update the p2m after the main loop. + ** Hence we defer canonicalization of L1s until then. + */ + if ((pt_levels != 3) || + pae_extended_cr3 || + (pagetype != XEN_DOMCTL_PFINFO_L1TAB)) { + + if (!uncanonicalize_pagetable(xc_handle, dom, + pagetype, page)) { + /* + ** Failing to uncanonicalize a page table can be ok + ** under live migration since the pages type may have + ** changed by now (and we'll get an update later). + */ + DPRINTF("PT L%ld race on pfn=%08lx mfn=%08lx\n", + pagetype >> 28, pfn, mfn); + nraces++; + continue; + } + } + } + else if ( pagetype != XEN_DOMCTL_PFINFO_NOTAB ) + { + ERROR("Bogus page type %lx page table is out of range: " + "i=%d p2m_size=%lu", pagetype, i, p2m_size); + goto out; + + } + + if ( verify ) + { + int res = memcmp(buf, (region_base + i*PAGE_SIZE), PAGE_SIZE); + if ( res ) + { + int v; + + DPRINTF("************** pfn=%lx type=%lx gotcs=%08lx " + "actualcs=%08lx\n", pfn, pfn_type[pfn], + csum_page(region_base + i*PAGE_SIZE), + csum_page(buf)); + + for ( v = 0; v < 4; v++ ) + { + unsigned long *p = (unsigned long *) + (region_base + i*PAGE_SIZE); + if ( buf[v] != p[v] ) + DPRINTF(" %d: %08lx %08lx\n", v, buf[v], p[v]); + } + } + } + + if ( !hvm && + xc_add_mmu_update(xc_handle, mmu, + (((unsigned long long)mfn) << PAGE_SHIFT) + | MMU_MACHPHYS_UPDATE, pfn) ) + { + ERROR("failed machpys update mfn=%lx pfn=%lx", mfn, pfn); + goto out; + } + } /* end of 'batch' for loop */ + + munmap(region_base, j*PAGE_SIZE); + n+= j; /* crude stats */ + + /* + * Discard cache for portion of file read so far up to last + * page boundary every 16MB or so. + */ + m += j; + if ( m > MAX_PAGECACHE_USAGE ) + { + discard_file_cache(io_fd, 0 /* no flush */); + m = 0; + } + } + + /* + * Ensure we flush all machphys updates before potential PAE-specific + * reallocations below. + */ + if ( !hvm && xc_flush_mmu_updates(xc_handle, mmu) ) + { + ERROR("Error doing flush_mmu_updates()"); + goto out; + } + + DPRINTF("Received all pages (%d races)\n", nraces); + + if ( hvm ) + { + uint32_t rec_len; + + /* Set HVM-specific parameters */ + if ( read_exact(io_fd, magic_pfns, sizeof(magic_pfns)) ) + { + ERROR("error reading magic page addresses"); + goto out; + } + + /* These comms pages need to be zeroed at the start of day */ + if ( xc_clear_domain_page(xc_handle, dom, magic_pfns[0]) || + xc_clear_domain_page(xc_handle, dom, magic_pfns[1]) || + xc_clear_domain_page(xc_handle, dom, magic_pfns[2]) ) + { + ERROR("error zeroing magic pages"); + goto out; + } + + if ( (frc = xc_set_hvm_param(xc_handle, dom, + HVM_PARAM_IOREQ_PFN, magic_pfns[0])) + || (frc = xc_set_hvm_param(xc_handle, dom, + HVM_PARAM_BUFIOREQ_PFN, magic_pfns[1])) + || (frc = xc_set_hvm_param(xc_handle, dom, + HVM_PARAM_STORE_PFN, magic_pfns[2])) + || (frc = xc_set_hvm_param(xc_handle, dom, + HVM_PARAM_PAE_ENABLED, pae)) + || (frc = xc_set_hvm_param(xc_handle, dom, + HVM_PARAM_STORE_EVTCHN, + store_evtchn)) ) + { + ERROR("error setting HVM params: %i", frc); + goto out; + } + *store_mfn = magic_pfns[2]; + + /* Read HVM context */ + if ( read_exact(io_fd, &rec_len, sizeof(uint32_t)) ) + { + ERROR("error read hvm context size!\n"); + goto out; + } + + hvm_buf = malloc(rec_len); + if ( hvm_buf == NULL ) + { + ERROR("memory alloc for hvm context buffer failed"); + errno = ENOMEM; + goto out; + } + + if ( read_exact(io_fd, hvm_buf, rec_len) ) + { + ERROR("error loading the HVM context"); + goto out; + } + + frc = xc_domain_hvm_setcontext(xc_handle, dom, hvm_buf, rec_len); + if ( frc ) + { + ERROR("error setting the HVM context"); + goto out; + } + + /* HVM success! */ + rc = 0; + goto out; + } + + /* Non-HVM guests only from here on */ + + if ( (pt_levels == 3) && !pae_extended_cr3 ) + { + /* + ** XXX SMH on PAE we need to ensure PGDs are in MFNs < 4G. This + ** is a little awkward and involves (a) finding all such PGDs and + ** replacing them with 'lowmem' versions; (b) upating the p2m[] + ** with the new info; and (c) canonicalizing all the L1s using the + ** (potentially updated) p2m[]. + ** + ** This is relatively slow (and currently involves two passes through + ** the pfn_type[] array), but at least seems to be correct. May wish + ** to consider more complex approaches to optimize this later. + */ + + int j, k; + + /* First pass: find all L3TABs current in > 4G mfns and get new mfns */ + for ( i = 0; i < p2m_size; i++ ) + { + if ( ((pfn_type[i] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) == + XEN_DOMCTL_PFINFO_L3TAB) && + (p2m[i] > 0xfffffUL) ) + { + unsigned long new_mfn; + uint64_t l3ptes[4]; + uint64_t *l3tab; + + l3tab = (uint64_t *) + xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ, p2m[i]); + + for ( j = 0; j < 4; j++ ) + l3ptes[j] = l3tab[j]; + + munmap(l3tab, PAGE_SIZE); + + new_mfn = xc_make_page_below_4G(xc_handle, dom, p2m[i]); + if ( !new_mfn ) + { + ERROR("Couldn't get a page below 4GB :-("); + goto out; + } + + p2m[i] = new_mfn; + if ( xc_add_mmu_update(xc_handle, mmu, + (((unsigned long long)new_mfn) + << PAGE_SHIFT) | + MMU_MACHPHYS_UPDATE, i) ) + { + ERROR("Couldn't m2p on PAE root pgdir"); + goto out; + } + + l3tab = (uint64_t *) + xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ | PROT_WRITE, p2m[i]); + + for ( j = 0; j < 4; j++ ) + l3tab[j] = l3ptes[j]; + + munmap(l3tab, PAGE_SIZE); + } + } + + /* Second pass: find all L1TABs and uncanonicalize them */ + j = 0; + + for ( i = 0; i < p2m_size; i++ ) + { + if ( ((pfn_type[i] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) == + XEN_DOMCTL_PFINFO_L1TAB) ) + { + region_mfn[j] = p2m[i]; + j++; + } + + if ( (i == (p2m_size-1)) || (j == MAX_BATCH_SIZE) ) + { + region_base = xc_map_foreign_batch( + xc_handle, dom, PROT_READ | PROT_WRITE, region_mfn, j); + if ( region_base == NULL ) + { + ERROR("map batch failed"); + goto out; + } + + for ( k = 0; k < j; k++ ) + { + if ( !uncanonicalize_pagetable( + xc_handle, dom, XEN_DOMCTL_PFINFO_L1TAB, + region_base + k*PAGE_SIZE) ) + { + ERROR("failed uncanonicalize pt!"); + goto out; + } + } + + munmap(region_base, j*PAGE_SIZE); + j = 0; + } + } + + if ( xc_flush_mmu_updates(xc_handle, mmu) ) + { + ERROR("Error doing xc_flush_mmu_updates()"); + goto out; + } + } + + /* + * Pin page tables. Do this after writing to them as otherwise Xen + * will barf when doing the type-checking. + */ + nr_pins = 0; + for ( i = 0; i < p2m_size; i++ ) + { + if ( (pfn_type[i] & XEN_DOMCTL_PFINFO_LPINTAB) == 0 ) + continue; + + switch ( pfn_type[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 = p2m[i]; + nr_pins++; + + /* Batch full? Then flush. */ + if ( nr_pins == MAX_PIN_BATCH ) + { + if ( xc_mmuext_op(xc_handle, pin, nr_pins, dom) < 0 ) + { + ERROR("Failed to pin batch of %d page tables", nr_pins); + goto out; + } + nr_pins = 0; + } + } + + /* Flush final partial batch. */ + if ( (nr_pins != 0) && (xc_mmuext_op(xc_handle, pin, nr_pins, dom) < 0) ) + { + ERROR("Failed to pin batch of %d page tables", nr_pins); + goto out; + } + + DPRINTF("\b\b\b\b100%%\n"); + DPRINTF("Memory reloaded (%ld pages)\n", nr_pfns); + + /* Get the list of PFNs that are not in the psuedo-phys map */ + { + unsigned int count = 0; + unsigned long *pfntab; + int nr_frees; + + if ( read_exact(io_fd, &count, sizeof(count)) || + (count > (1U << 28)) ) /* up to 1TB of address space */ + { + ERROR("Error when reading pfn count (= %u)", count); + goto out; + } + + if ( !(pfntab = malloc(sizeof(unsigned long) * count)) ) + { + ERROR("Out of memory"); + goto out; + } + + if ( read_exact(io_fd, pfntab, sizeof(unsigned long)*count) ) + { + ERROR("Error when reading pfntab"); + goto out; + } + + nr_frees = 0; + for ( i = 0; i < count; i++ ) + { + unsigned long pfn = pfntab[i]; + + if ( p2m[pfn] != INVALID_P2M_ENTRY ) + { + /* pfn is not in physmap now, but was at some point during + the save/migration process - need to free it */ + pfntab[nr_frees++] = p2m[pfn]; + p2m[pfn] = INVALID_P2M_ENTRY; /* not in pseudo-physical map */ + } + } + + if ( nr_frees > 0 ) + { + struct xen_memory_reservation reservation = { + .nr_extents = nr_frees, + .extent_order = 0, + .domid = dom + }; + set_xen_guest_handle(reservation.extent_start, pfntab); + + if ( (frc = xc_memory_op(xc_handle, XENMEM_decrease_reservation, + &reservation)) != nr_frees ) + { + ERROR("Could not decrease reservation : %d", frc); + goto out; + } + else + DPRINTF("Decreased reservation by %d pages\n", count); + } + } + + if ( lock_pages(&ctxt, sizeof(ctxt)) ) + { + ERROR("Unable to lock ctxt"); + return 1; + } + + for ( i = 0; i <= max_vcpu_id; i++ ) + { + if ( !(vcpumap & (1ULL << i)) ) + continue; + + if ( read_exact(io_fd, &ctxt, ((guest_width == 8) + ? sizeof(ctxt.x64) + : sizeof(ctxt.x32))) ) + { + ERROR("Error when reading ctxt %d", i); + goto out; + } + + if ( !new_ctxt_format ) + SET_FIELD(&ctxt, flags, GET_FIELD(&ctxt, flags) | VGCF_online); + + if ( i == 0 ) + { + /* + * Uncanonicalise the suspend-record frame number and poke + * resume record. + */ + pfn = GET_FIELD(&ctxt, user_regs.edx); + if ( (pfn >= p2m_size) || + (pfn_type[pfn] != XEN_DOMCTL_PFINFO_NOTAB) ) + { + ERROR("Suspend record frame number is bad"); + goto out; + } + mfn = p2m[pfn]; + SET_FIELD(&ctxt, user_regs.edx, mfn); + start_info = xc_map_foreign_range( + xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); + SET_FIELD(start_info, nr_pages, p2m_size); + SET_FIELD(start_info, shared_info, shared_info_frame< 8192 ) + { + ERROR("GDT entry count out of range"); + goto out; + } + + for ( j = 0; (512*j) < GET_FIELD(&ctxt, gdt_ents); j++ ) + { + pfn = GET_FIELD(&ctxt, gdt_frames[j]); + if ( (pfn >= p2m_size) || + (pfn_type[pfn] != XEN_DOMCTL_PFINFO_NOTAB) ) + { + ERROR("GDT frame number %i (0x%lx) is bad", + j, (unsigned long)pfn); + goto out; + } + SET_FIELD(&ctxt, gdt_frames[j], p2m[pfn]); + } + /* Uncanonicalise the page table base pointer. */ + pfn = UNFOLD_CR3(GET_FIELD(&ctxt, ctrlreg[3])); + + if ( pfn >= p2m_size ) + { + ERROR("PT base is bad: pfn=%lu p2m_size=%lu type=%08lx", + pfn, p2m_size, pfn_type[pfn]); + goto out; + } + + if ( (pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) != + ((unsigned long)pt_levels<= p2m_size ) + { + ERROR("User PT base is bad: pfn=%lu p2m_size=%lu", + pfn, p2m_size); + goto out; + } + if ( (pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) != + ((unsigned long)pt_levels<= p2m_size) || (pfn_type[pfn] != XEN_DOMCTL_PFINFO_NOTAB) ) + { + ERROR("PFN-to-MFN frame number %i (%#lx) is bad", i, pfn); + goto out; + } + p2m_frame_list[i] = p2m[pfn]; + } + + /* Copy the P2M we've constructed to the 'live' P2M */ + if ( !(live_p2m = xc_map_foreign_batch(xc_handle, dom, PROT_WRITE, + p2m_frame_list, P2M_FL_ENTRIES)) ) + { + ERROR("Couldn't map p2m table"); + goto out; + } + + /* If the domain we're restoring has a different word size to ours, + * we need to adjust the live_p2m assignment appropriately */ + if ( guest_width > sizeof (xen_pfn_t) ) + for ( i = p2m_size - 1; i >= 0; i-- ) + ((uint64_t *)live_p2m)[i] = p2m[i]; + else if ( guest_width < sizeof (xen_pfn_t) ) + for ( i = 0; i < p2m_size; i++ ) + ((uint32_t *)live_p2m)[i] = p2m[i]; + else + memcpy(live_p2m, p2m, p2m_size * sizeof(xen_pfn_t)); + munmap(live_p2m, P2M_FL_ENTRIES * PAGE_SIZE); + + DPRINTF("Domain ready to be built.\n"); + rc = 0; + + out: + if ( (rc != 0) && (dom != 0) ) + xc_domain_destroy(xc_handle, dom); + free(mmu); + free(p2m); + free(pfn_type); + free(hvm_buf); + + /* discard cache for save file */ + discard_file_cache(io_fd, 1 /*flush*/); + + DPRINTF("Restore exit with rc=%d\n", rc); + + return rc; +} diff --git a/tools/libxc/xc_domain_save.c b/tools/libxc/xc_domain_save.c new file mode 100644 index 0000000..a910414 --- /dev/null +++ b/tools/libxc/xc_domain_save.c @@ -0,0 +1,1635 @@ +/****************************************************************************** + * xc_linux_save.c + * + * Save the state of a running Linux session. + * + * Copyright (c) 2003, K A Fraser. + */ + +#include +#include +#include +#include +#include + +#include "xc_private.h" +#include "xc_dom.h" +#include "xg_private.h" +#include "xg_save_restore.h" + +#include +#include "xc_e820.h" + +/* +** Default values for important tuning parameters. Can override by passing +** non-zero replacement values to xc_domain_save(). +** +** XXX SMH: should consider if want to be able to override MAX_MBIT_RATE too. +** +*/ +#define DEF_MAX_ITERS 29 /* limit us to 30 times round loop */ +#define DEF_MAX_FACTOR 3 /* never send more than 3x p2m_size */ + +/* max mfn of the whole machine */ +static unsigned long max_mfn; + +/* virtual starting address of the hypervisor */ +static unsigned long hvirt_start; + +/* #levels of page tables used by the current guest */ +static unsigned int pt_levels; + +/* HVM: shared-memory bitmaps for getting log-dirty bits from qemu-dm */ +static unsigned long *qemu_bitmaps[2]; +static int qemu_active; +static int qemu_non_active; + +/* number of pfns this guest has (i.e. number of entries in the P2M) */ +static unsigned long p2m_size; + +/* Live mapping of the table mapping each PFN to its current MFN. */ +static xen_pfn_t *live_p2m = NULL; + +/* Live mapping of system MFN to PFN table. */ +static xen_pfn_t *live_m2p = NULL; +static unsigned long m2p_mfn0; + +/* Address size of the guest */ +unsigned int guest_width; + +/* grep fodder: machine_to_phys */ + +#define mfn_to_pfn(_mfn) (live_m2p[(_mfn)]) + +#define pfn_to_mfn(_pfn) \ + ((xen_pfn_t) ((guest_width==8) \ + ? (((uint64_t *)live_p2m)[(_pfn)]) \ + : ((((uint32_t *)live_p2m)[(_pfn)]) == 0xffffffffU \ + ? (-1UL) : (((uint32_t *)live_p2m)[(_pfn)])))) + +/* + * Returns TRUE if the given machine frame number has a unique mapping + * in the guest's pseudophysical map. + */ +#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \ + (((_mfn) < (max_mfn)) && \ + ((mfn_to_pfn(_mfn) < (p2m_size)) && \ + (pfn_to_mfn(mfn_to_pfn(_mfn)) == (_mfn)))) + +/* +** During (live) save/migrate, we maintain a number of bitmaps to track +** which pages we have to send, to fixup, and to skip. +*/ + +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define BITS_TO_LONGS(bits) (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG) +#define BITMAP_SIZE (BITS_TO_LONGS(p2m_size) * sizeof(unsigned long)) + +#define BITMAP_ENTRY(_nr,_bmap) \ + ((volatile unsigned long *)(_bmap))[(_nr)/BITS_PER_LONG] + +#define BITMAP_SHIFT(_nr) ((_nr) % BITS_PER_LONG) + +static inline int test_bit (int nr, volatile void * addr) +{ + return (BITMAP_ENTRY(nr, addr) >> BITMAP_SHIFT(nr)) & 1; +} + +static inline void clear_bit (int nr, volatile void * addr) +{ + BITMAP_ENTRY(nr, addr) &= ~(1UL << BITMAP_SHIFT(nr)); +} + +static inline void set_bit ( int nr, volatile void * addr) +{ + BITMAP_ENTRY(nr, addr) |= (1UL << BITMAP_SHIFT(nr)); +} + +/* Returns the hamming weight (i.e. the number of bits set) in a N-bit word */ +static inline unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +static inline int count_bits ( int nr, volatile void *addr) +{ + int i, count = 0; + volatile unsigned long *p = (volatile unsigned long *)addr; + /* We know that the array is padded to unsigned long. */ + for ( i = 0; i < (nr / (sizeof(unsigned long)*8)); i++, p++ ) + count += hweight32(*p); + return count; +} + +static uint64_t tv_to_us(struct timeval *new) +{ + return (new->tv_sec * 1000000) + new->tv_usec; +} + +static uint64_t llgettimeofday(void) +{ + struct timeval now; + gettimeofday(&now, NULL); + return tv_to_us(&now); +} + +static uint64_t tv_delta(struct timeval *new, struct timeval *old) +{ + return (((new->tv_sec - old->tv_sec)*1000000) + + (new->tv_usec - old->tv_usec)); +} + +static int noncached_write(int fd, int live, void *buffer, int len) +{ + static int write_count = 0; + int rc = (write_exact(fd, buffer, len) == 0) ? len : -1; + + write_count += len; + if ( write_count >= (MAX_PAGECACHE_USAGE * PAGE_SIZE) ) + { + /* Time to discard cache - dont care if this fails */ + discard_file_cache(fd, 0 /* no flush */); + write_count = 0; + } + + return rc; +} + +#ifdef ADAPTIVE_SAVE + +/* +** We control the rate at which we transmit (or save) to minimize impact +** on running domains (including the target if we're doing live migrate). +*/ + +#define MAX_MBIT_RATE 500 /* maximum transmit rate for migrate */ +#define START_MBIT_RATE 100 /* initial transmit rate for migrate */ + +/* Scaling factor to convert between a rate (in Mb/s) and time (in usecs) */ +#define RATE_TO_BTU 781250 + +/* Amount in bytes we allow ourselves to send in a burst */ +#define BURST_BUDGET (100*1024) + +/* We keep track of the current and previous transmission rate */ +static int mbit_rate, ombit_rate = 0; + +/* Have we reached the maximum transmission rate? */ +#define RATE_IS_MAX() (mbit_rate == MAX_MBIT_RATE) + +static inline void initialize_mbit_rate() +{ + mbit_rate = START_MBIT_RATE; +} + +static int ratewrite(int io_fd, int live, void *buf, int n) +{ + static int budget = 0; + static int burst_time_us = -1; + static struct timeval last_put = { 0 }; + struct timeval now; + struct timespec delay; + long long delta; + + if ( START_MBIT_RATE == 0 ) + return noncached_write(io_fd, live, buf, n); + + budget -= n; + if ( budget < 0 ) + { + if ( mbit_rate != ombit_rate ) + { + burst_time_us = RATE_TO_BTU / mbit_rate; + ombit_rate = mbit_rate; + DPRINTF("rate limit: %d mbit/s burst budget %d slot time %d\n", + mbit_rate, BURST_BUDGET, burst_time_us); + } + if ( last_put.tv_sec == 0 ) + { + budget += BURST_BUDGET; + gettimeofday(&last_put, NULL); + } + else + { + while ( budget < 0 ) + { + gettimeofday(&now, NULL); + delta = tv_delta(&now, &last_put); + while ( delta > burst_time_us ) + { + budget += BURST_BUDGET; + last_put.tv_usec += burst_time_us; + if ( last_put.tv_usec > 1000000 ) + { + last_put.tv_usec -= 1000000; + last_put.tv_sec++; + } + delta -= burst_time_us; + } + if ( budget > 0 ) + break; + delay.tv_sec = 0; + delay.tv_nsec = 1000 * (burst_time_us - delta); + while ( delay.tv_nsec > 0 ) + if ( nanosleep(&delay, &delay) == 0 ) + break; + } + } + } + return noncached_write(io_fd, live, buf, n); +} + +#else /* ! ADAPTIVE SAVE */ + +#define RATE_IS_MAX() (0) +#define ratewrite(_io_fd, _live, _buf, _n) noncached_write((_io_fd), (_live), (_buf), (_n)) +#define initialize_mbit_rate() + +#endif + +static int print_stats(int xc_handle, uint32_t domid, int pages_sent, + xc_shadow_op_stats_t *stats, int print) +{ + static struct timeval wall_last; + static long long d0_cpu_last; + static long long d1_cpu_last; + + struct timeval wall_now; + long long wall_delta; + long long d0_cpu_now, d0_cpu_delta; + long long d1_cpu_now, d1_cpu_delta; + + gettimeofday(&wall_now, NULL); + + d0_cpu_now = xc_domain_get_cpu_usage(xc_handle, 0, /* FIXME */ 0)/1000; + d1_cpu_now = xc_domain_get_cpu_usage(xc_handle, domid, /* FIXME */ 0)/1000; + + if ( (d0_cpu_now == -1) || (d1_cpu_now == -1) ) + DPRINTF("ARRHHH!!\n"); + + wall_delta = tv_delta(&wall_now,&wall_last)/1000; + if ( wall_delta == 0 ) + wall_delta = 1; + + d0_cpu_delta = (d0_cpu_now - d0_cpu_last)/1000; + d1_cpu_delta = (d1_cpu_now - d1_cpu_last)/1000; + + if ( print ) + DPRINTF("delta %lldms, dom0 %d%%, target %d%%, sent %dMb/s, " + "dirtied %dMb/s %" PRId32 " pages\n", + wall_delta, + (int)((d0_cpu_delta*100)/wall_delta), + (int)((d1_cpu_delta*100)/wall_delta), + (int)((pages_sent*PAGE_SIZE)/(wall_delta*(1000/8))), + (int)((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8))), + stats->dirty_count); + +#ifdef ADAPTIVE_SAVE + if ( ((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8))) > mbit_rate ) + { + mbit_rate = (int)((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8))) + + 50; + if ( mbit_rate > MAX_MBIT_RATE ) + mbit_rate = MAX_MBIT_RATE; + } +#endif + + d0_cpu_last = d0_cpu_now; + d1_cpu_last = d1_cpu_now; + wall_last = wall_now; + + return 0; +} + + +static int analysis_phase(int xc_handle, uint32_t domid, int p2m_size, + unsigned long *arr, int runs) +{ + long long start, now; + xc_shadow_op_stats_t stats; + int j; + + start = llgettimeofday(); + + for ( j = 0; j < runs; j++ ) + { + int i; + + xc_shadow_control(xc_handle, domid, XEN_DOMCTL_SHADOW_OP_CLEAN, + arr, p2m_size, NULL, 0, NULL); + DPRINTF("#Flush\n"); + for ( i = 0; i < 40; i++ ) + { + usleep(50000); + now = llgettimeofday(); + xc_shadow_control(xc_handle, domid, XEN_DOMCTL_SHADOW_OP_PEEK, + NULL, 0, NULL, 0, &stats); + DPRINTF("now= %lld faults= %"PRId32" dirty= %"PRId32"\n", + ((now-start)+500)/1000, + stats.fault_count, stats.dirty_count); + } + } + + return -1; +} + + +static int suspend_and_state(int (*suspend)(void), int xc_handle, int io_fd, + int dom, xc_dominfo_t *info) +{ + if ( !(*suspend)() ) + { + ERROR("Suspend request failed"); + return -1; + } + + if ( (xc_domain_getinfo(xc_handle, dom, 1, info) != 1) || + !info->shutdown || (info->shutdown_reason != SHUTDOWN_suspend) ) + { + ERROR("Domain not in suspended state"); + return -1; + } + + return 0; +} + +/* +** Map the top-level page of MFNs from the guest. The guest might not have +** finished resuming from a previous restore operation, so we wait a while for +** it to update the MFN to a reasonable value. +*/ +static void *map_frame_list_list(int xc_handle, uint32_t dom, + shared_info_any_t *shinfo) +{ + int count = 100; + void *p; + uint64_t fll = GET_FIELD(shinfo, arch.pfn_to_mfn_frame_list_list); + + while ( count-- && (fll == 0) ) + { + usleep(10000); + fll = GET_FIELD(shinfo, arch.pfn_to_mfn_frame_list_list); + } + + if ( fll == 0 ) + { + ERROR("Timed out waiting for frame list updated."); + return NULL; + } + + p = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, fll); + if ( p == NULL ) + ERROR("Couldn't map p2m_frame_list_list (errno %d)", errno); + + return p; +} + +/* +** During transfer (or in the state file), all page-table pages must be +** converted into a 'canonical' form where references to actual mfns +** are replaced with references to the corresponding pfns. +** +** This function performs the appropriate conversion, taking into account +** which entries do not require canonicalization (in particular, those +** entries which map the virtual address reserved for the hypervisor). +*/ +static int canonicalize_pagetable(unsigned long type, unsigned long pfn, + const void *spage, void *dpage) +{ + + int i, pte_last, xen_start, xen_end, race = 0; + uint64_t pte; + + /* + ** We need to determine which entries in this page table hold + ** reserved hypervisor mappings. This depends on the current + ** page table type as well as the number of paging levels. + */ + xen_start = xen_end = pte_last = PAGE_SIZE / ((pt_levels == 2) ? 4 : 8); + + if ( (pt_levels == 2) && (type == XEN_DOMCTL_PFINFO_L2TAB) ) + xen_start = (hvirt_start >> L2_PAGETABLE_SHIFT); + + if ( (pt_levels == 3) && (type == XEN_DOMCTL_PFINFO_L3TAB) ) + xen_start = L3_PAGETABLE_ENTRIES_PAE; + + /* + ** In PAE only the L2 mapping the top 1GB contains Xen mappings. + ** We can spot this by looking for the guest's mappingof the m2p. + ** Guests must ensure that this check will fail for other L2s. + */ + if ( (pt_levels == 3) && (type == XEN_DOMCTL_PFINFO_L2TAB) ) + { + int hstart; + uint64_t he; + + hstart = (hvirt_start >> L2_PAGETABLE_SHIFT_PAE) & 0x1ff; + he = ((const uint64_t *) spage)[hstart]; + + if ( ((he >> PAGE_SHIFT) & MFN_MASK_X86) == m2p_mfn0 ) + { + /* hvirt starts with xen stuff... */ + xen_start = hstart; + } + else if ( hvirt_start != 0xf5800000 ) + { + /* old L2s from before hole was shrunk... */ + hstart = (0xf5800000 >> L2_PAGETABLE_SHIFT_PAE) & 0x1ff; + he = ((const uint64_t *) spage)[hstart]; + if ( ((he >> PAGE_SHIFT) & MFN_MASK_X86) == m2p_mfn0 ) + xen_start = hstart; + } + } + + if ( (pt_levels == 4) && (type == XEN_DOMCTL_PFINFO_L4TAB) ) + { + /* + ** XXX SMH: should compute these from hvirt_start (which we have) + ** and hvirt_end (which we don't) + */ + xen_start = 256; + xen_end = 272; + } + + /* Now iterate through the page table, canonicalizing each PTE */ + for (i = 0; i < pte_last; i++ ) + { + unsigned long pfn, mfn; + + if ( pt_levels == 2 ) + pte = ((const uint32_t*)spage)[i]; + else + pte = ((const uint64_t*)spage)[i]; + + if ( (i >= xen_start) && (i < xen_end) ) + pte = 0; + + if ( pte & _PAGE_PRESENT ) + { + mfn = (pte >> PAGE_SHIFT) & MFN_MASK_X86; + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + /* This will happen if the type info is stale which + is quite feasible under live migration */ + pfn = 0; /* zap it - we'll retransmit this page later */ + /* XXX: We can't spot Xen mappings in compat-mode L2es + * from 64-bit tools, but the only thing in them is the + * compat m2p, so we quietly zap them. This doesn't + * count as a race, so don't report it. */ + if ( !(type == XEN_DOMCTL_PFINFO_L2TAB + && sizeof (unsigned long) > guest_width) ) + race = 1; /* inform the caller; fatal if !live */ + } + else + pfn = mfn_to_pfn(mfn); + + pte &= ~MADDR_MASK_X86; + pte |= (uint64_t)pfn << PAGE_SHIFT; + + /* + * PAE guest L3Es can contain these flags when running on + * a 64bit hypervisor. We zap these here to avoid any + * surprise at restore time... + */ + if ( (pt_levels == 3) && + (type == XEN_DOMCTL_PFINFO_L3TAB) && + (pte & (_PAGE_USER|_PAGE_RW|_PAGE_ACCESSED)) ) + pte &= ~(_PAGE_USER|_PAGE_RW|_PAGE_ACCESSED); + } + + if ( pt_levels == 2 ) + ((uint32_t*)dpage)[i] = pte; + else + ((uint64_t*)dpage)[i] = pte; + } + + return race; +} + +static xen_pfn_t *xc_map_m2p(int xc_handle, + unsigned long max_mfn, + int prot) +{ + struct xen_machphys_mfn_list xmml; + 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); + + xmml.max_extents = m2p_chunks; + + extent_start = calloc(m2p_chunks, sizeof(xen_pfn_t)); + if ( !extent_start ) + { + ERROR("failed to allocate space for m2p mfns"); + goto err0; + } + set_xen_guest_handle(xmml.extent_start, extent_start); + + if ( xc_memory_op(xc_handle, XENMEM_machphys_mfn_list, &xmml) || + (xmml.nr_extents != m2p_chunks) ) + { + ERROR("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(xc_handle, DOMID_XEN, + m2p_size, prot, M2P_CHUNK_SIZE, + entries, m2p_chunks); + if (m2p == NULL) + { + ERROR("xc_mmap_foreign_ranges failed"); + goto err2; + } + + m2p_mfn0 = entries[0].mfn; + +err2: + free(entries); +err1: + free(extent_start); + +err0: + return m2p; +} + + +static xen_pfn_t *map_and_save_p2m_table(int xc_handle, + int io_fd, + uint32_t dom, + unsigned long p2m_size, + shared_info_any_t *live_shinfo) +{ + vcpu_guest_context_any_t ctxt; + + /* Double and single indirect references to the live P2M table */ + void *live_p2m_frame_list_list = NULL; + void *live_p2m_frame_list = NULL; + + /* Copies of the above. */ + xen_pfn_t *p2m_frame_list_list = NULL; + xen_pfn_t *p2m_frame_list = NULL; + + /* The mapping of the live p2m table itself */ + xen_pfn_t *p2m = NULL; + + int i, success = 0; + + live_p2m_frame_list_list = map_frame_list_list(xc_handle, dom, + live_shinfo); + if ( !live_p2m_frame_list_list ) + 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 ( guest_width > sizeof(unsigned long) ) + for ( i = 0; i < PAGE_SIZE/sizeof(unsigned long); i++ ) + if ( i < PAGE_SIZE/guest_width ) + p2m_frame_list_list[i] = ((uint64_t *)p2m_frame_list_list)[i]; + else + p2m_frame_list_list[i] = 0; + else if ( 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_batch(xc_handle, dom, PROT_READ, + p2m_frame_list_list, + P2M_FLL_ENTRIES); + if ( !live_p2m_frame_list ) + { + ERROR("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 ( 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 ( guest_width < sizeof(unsigned long) ) + for ( i = P2M_FL_ENTRIES - 1; i >= 0; i-- ) + p2m_frame_list[i] = ((uint32_t *)p2m_frame_list)[i]; + + + /* 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_batch(xc_handle, dom, PROT_READ, + p2m_frame_list, + P2M_FL_ENTRIES); + if ( !p2m ) + { + ERROR("Couldn't map p2m table"); + goto out; + } + live_p2m = p2m; /* So that translation macros will work */ + + /* Canonicalise the pfn-to-mfn table frame-number list. */ + for ( i = 0; i < p2m_size; i += FPP ) + { + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(p2m_frame_list[i/FPP]) ) + { + ERROR("Frame# in pfn-to-mfn frame list is not in pseudophys"); + ERROR("entry %d: p2m_frame_list[%ld] is 0x%"PRIx64", max 0x%lx", + i, i/FPP, (uint64_t)p2m_frame_list[i/FPP], max_mfn); + if ( p2m_frame_list[i/FPP] < max_mfn ) + { + ERROR("m2p[0x%"PRIx64"] = 0x%"PRIx64, + (uint64_t)p2m_frame_list[i/FPP], + (uint64_t)live_m2p[p2m_frame_list[i/FPP]]); + ERROR("p2m[0x%"PRIx64"] = 0x%"PRIx64, + (uint64_t)live_m2p[p2m_frame_list[i/FPP]], + (uint64_t)p2m[live_m2p[p2m_frame_list[i/FPP]]]); + + } + goto out; + } + p2m_frame_list[i/FPP] = mfn_to_pfn(p2m_frame_list[i/FPP]); + } + + if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt) ) + { + ERROR("Could not get vcpu context"); + goto out; + } + + /* + * Write an extended-info structure to inform the restore code that + * a PAE guest understands extended CR3 (PDPTs above 4GB). Turns off + * slow paths in the restore code. + */ + { + unsigned long signature = ~0UL; + uint32_t chunk1_sz = ((guest_width==8) + ? sizeof(ctxt.x64) + : sizeof(ctxt.x32)); + uint32_t chunk2_sz = 0; + uint32_t tot_sz = (chunk1_sz + 8) + (chunk2_sz + 8); + if ( write_exact(io_fd, &signature, sizeof(signature)) || + write_exact(io_fd, &tot_sz, sizeof(tot_sz)) || + write_exact(io_fd, "vcpu", 4) || + write_exact(io_fd, &chunk1_sz, sizeof(chunk1_sz)) || + write_exact(io_fd, &ctxt, chunk1_sz) || + write_exact(io_fd, "extv", 4) || + write_exact(io_fd, &chunk2_sz, sizeof(chunk2_sz)) ) + { + PERROR("write: extended info"); + goto out; + } + } + + if ( write_exact(io_fd, p2m_frame_list, + P2M_FL_ENTRIES * sizeof(xen_pfn_t)) ) + { + PERROR("write: p2m_frame_list"); + goto out; + } + + success = 1; + + out: + + if ( !success && p2m ) + munmap(p2m, P2M_FLL_ENTRIES * PAGE_SIZE); + + 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); + + if ( p2m_frame_list_list ) + free(p2m_frame_list_list); + + if ( p2m_frame_list ) + free(p2m_frame_list); + + return success ? p2m : NULL; +} + + + +int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, + uint32_t max_factor, uint32_t flags, int (*suspend)(void), + int hvm, void *(*init_qemu_maps)(int, unsigned), + void (*qemu_flip_buffer)(int, int)) +{ + xc_dominfo_t info; + DECLARE_DOMCTL; + + int rc = 1, frc, i, j, last_iter, iter = 0; + int live = (flags & XCFLAGS_LIVE); + int debug = (flags & XCFLAGS_DEBUG); + int race = 0, sent_last_iter, skip_this_iter; + + /* The new domain's shared-info frame number. */ + unsigned long shared_info_frame; + + /* A copy of the CPU context of the guest. */ + vcpu_guest_context_any_t ctxt; + + /* A table containing the type of each PFN (/not/ MFN!). */ + unsigned long *pfn_type = NULL; + unsigned long *pfn_batch = NULL; + + /* A copy of one frame of guest memory. */ + char page[PAGE_SIZE]; + + /* Live mapping of shared info structure */ + shared_info_any_t *live_shinfo = NULL; + + /* base of the region in which domain memory is mapped */ + unsigned char *region_base = NULL; + + /* bitmap of pages: + - that should be sent this iteration (unless later marked as skip); + - to skip this iteration because already dirty; + - to fixup by sending at the end if not already resent; */ + unsigned long *to_send = NULL, *to_skip = NULL, *to_fix = NULL; + + xc_shadow_op_stats_t stats; + + unsigned long needed_to_fix = 0; + unsigned long total_sent = 0; + + uint64_t vcpumap = 1ULL; + + /* HVM: a buffer for holding HVM context */ + uint32_t hvm_buf_size = 0; + uint8_t *hvm_buf = NULL; + + /* HVM: magic frames for ioreqs and xenstore comms. */ + uint64_t magic_pfns[3]; /* ioreq_pfn, bufioreq_pfn, store_pfn */ + + unsigned long mfn; + + /* If no explicit control parameters given, use defaults */ + max_iters = max_iters ? : DEF_MAX_ITERS; + max_factor = max_factor ? : DEF_MAX_FACTOR; + + initialize_mbit_rate(); + + if ( !get_platform_info(xc_handle, dom, + &max_mfn, &hvirt_start, &pt_levels, &guest_width) ) + { + ERROR("Unable to get platform info."); + return 1; + } + + if ( xc_domain_getinfo(xc_handle, dom, 1, &info) != 1 ) + { + ERROR("Could not get domain info"); + return 1; + } + + shared_info_frame = info.shared_info_frame; + + /* Map the shared info frame */ + if ( !hvm ) + { + live_shinfo = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ, shared_info_frame); + if ( !live_shinfo ) + { + ERROR("Couldn't map live_shinfo"); + goto out; + } + } + + /* Get the size of the P2M table */ + p2m_size = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &dom) + 1; + + /* Domain is still running at this point */ + if ( live ) + { + /* Live suspend. Enable log-dirty mode. */ + if ( xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, + NULL, 0, NULL, 0, NULL) < 0 ) + { + /* log-dirty already enabled? There's no test op, + so attempt to disable then reenable it */ + frc = xc_shadow_control(xc_handle, dom, XEN_DOMCTL_SHADOW_OP_OFF, + NULL, 0, NULL, 0, NULL); + if ( frc >= 0 ) + { + frc = xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, + NULL, 0, NULL, 0, NULL); + } + + if ( frc < 0 ) + { + ERROR("Couldn't enable shadow mode (rc %d) (errno %d)", frc, errno ); + goto out; + } + } + + if ( hvm ) + { + /* Get qemu-dm logging dirty pages too */ + void *seg = init_qemu_maps(dom, BITMAP_SIZE); + qemu_bitmaps[0] = seg; + qemu_bitmaps[1] = seg + BITMAP_SIZE; + qemu_active = 0; + qemu_non_active = 1; + } + } + else + { + /* This is a non-live suspend. Suspend the domain .*/ + if ( suspend_and_state(suspend, xc_handle, io_fd, dom, &info) ) + { + ERROR("Domain appears not to have suspended"); + goto out; + } + } + + last_iter = !live; + + /* pretend we sent all the pages last iteration */ + sent_last_iter = p2m_size; + + /* Setup to_send / to_fix and to_skip bitmaps */ + to_send = xg_memalign(PAGE_SIZE, ROUNDUP(BITMAP_SIZE, PAGE_SHIFT)); + to_fix = calloc(1, BITMAP_SIZE); + to_skip = xg_memalign(PAGE_SIZE, ROUNDUP(BITMAP_SIZE, PAGE_SHIFT)); + + if ( !to_send || !to_fix || !to_skip ) + { + ERROR("Couldn't allocate to_send array"); + goto out; + } + + memset(to_send, 0xff, BITMAP_SIZE); + + if ( lock_pages(to_send, BITMAP_SIZE) ) + { + ERROR("Unable to lock to_send"); + return 1; + } + + /* (to fix is local only) */ + if ( lock_pages(to_skip, BITMAP_SIZE) ) + { + ERROR("Unable to lock to_skip"); + return 1; + } + + if ( hvm ) + { + /* Need another buffer for HVM context */ + hvm_buf_size = xc_domain_hvm_getcontext(xc_handle, dom, 0, 0); + if ( hvm_buf_size == -1 ) + { + ERROR("Couldn't get HVM context size from Xen"); + goto out; + } + hvm_buf = malloc(hvm_buf_size); + if ( !hvm_buf ) + { + ERROR("Couldn't allocate memory"); + goto out; + } + } + + analysis_phase(xc_handle, dom, p2m_size, to_skip, 0); + + pfn_type = xg_memalign(PAGE_SIZE, ROUNDUP( + MAX_BATCH_SIZE * sizeof(*pfn_type), PAGE_SHIFT)); + pfn_batch = calloc(MAX_BATCH_SIZE, sizeof(*pfn_batch)); + if ( (pfn_type == NULL) || (pfn_batch == NULL) ) + { + ERROR("failed to alloc memory for pfn_type and/or pfn_batch arrays"); + errno = ENOMEM; + goto out; + } + memset(pfn_type, 0, + ROUNDUP(MAX_BATCH_SIZE * sizeof(*pfn_type), PAGE_SHIFT)); + + if ( lock_pages(pfn_type, MAX_BATCH_SIZE * sizeof(*pfn_type)) ) + { + ERROR("Unable to lock pfn_type array"); + goto out; + } + + /* Setup the mfn_to_pfn table mapping */ + if ( !(live_m2p = xc_map_m2p(xc_handle, max_mfn, PROT_READ)) ) + { + ERROR("Failed to map live M2P table"); + goto out; + } + + /* Start writing out the saved-domain record. */ + if ( write_exact(io_fd, &p2m_size, sizeof(unsigned long)) ) + { + PERROR("write: p2m_size"); + goto out; + } + + if ( !hvm ) + { + int err = 0; + + /* Map the P2M table, and write the list of P2M frames */ + live_p2m = map_and_save_p2m_table(xc_handle, io_fd, dom, + p2m_size, live_shinfo); + if ( live_p2m == NULL ) + { + ERROR("Failed to map/save the p2m frame list"); + goto out; + } + + /* + * Quick belt and braces sanity check. + */ + + for ( i = 0; i < p2m_size; i++ ) + { + mfn = pfn_to_mfn(i); + if( (mfn != INVALID_P2M_ENTRY) && (mfn_to_pfn(mfn) != i) ) + { + DPRINTF("i=0x%x mfn=%lx live_m2p=%lx\n", i, + mfn, mfn_to_pfn(mfn)); + err++; + } + } + DPRINTF("Had %d unexplained entries in p2m table\n", err); + } + + print_stats(xc_handle, dom, 0, &stats, 0); + + /* Now write out each data page, canonicalising page tables as we go... */ + for ( ; ; ) + { + unsigned int prev_pc, sent_this_iter, N, batch, run; + + iter++; + sent_this_iter = 0; + skip_this_iter = 0; + prev_pc = 0; + N = 0; + + DPRINTF("Saving memory pages: iter %d 0%%", iter); + + while ( N < p2m_size ) + { + unsigned int this_pc = (N * 100) / p2m_size; + + if ( (this_pc - prev_pc) >= 5 ) + { + DPRINTF("\b\b\b\b%3d%%", this_pc); + prev_pc = this_pc; + } + + if ( !last_iter ) + { + /* Slightly wasteful to peek the whole array evey time, + but this is fast enough for the moment. */ + frc = xc_shadow_control( + xc_handle, dom, XEN_DOMCTL_SHADOW_OP_PEEK, to_skip, + p2m_size, NULL, 0, NULL); + if ( frc != p2m_size ) + { + ERROR("Error peeking shadow bitmap"); + goto out; + } + } + + /* load pfn_type[] with the mfn of all the pages we're doing in + this batch. */ + for ( batch = 0; + (batch < MAX_BATCH_SIZE) && (N < p2m_size); + N++ ) + { + int n = N; + + if ( debug ) + { + DPRINTF("%d pfn= %08lx mfn= %08lx %d", + iter, (unsigned long)n, + hvm ? 0 : pfn_to_mfn(n), + test_bit(n, to_send)); + if ( !hvm && is_mapped(pfn_to_mfn(n)) ) + DPRINTF(" [mfn]= %08lx", + mfn_to_pfn(pfn_to_mfn(n)&0xFFFFF)); + DPRINTF("\n"); + } + if ( !last_iter && + test_bit(n, to_send) && + test_bit(n, to_skip) ) + skip_this_iter++; /* stats keeping */ + + if ( !((test_bit(n, to_send) && !test_bit(n, to_skip)) || + (test_bit(n, to_send) && last_iter) || + (test_bit(n, to_fix) && last_iter)) ) + continue; + + /* + ** we get here if: + ** 1. page is marked to_send & hasn't already been re-dirtied + ** 2. (ignore to_skip in last iteration) + ** 3. add in pages that still need fixup (net bufs) + */ + + pfn_batch[batch] = n; + + /* Hypercall interfaces operate in PFNs for HVM guests + * and MFNs for PV guests */ + if ( hvm ) + pfn_type[batch] = n; + else + pfn_type[batch] = pfn_to_mfn(n); + + if ( !is_mapped(pfn_type[batch]) ) + { + /* + ** not currently in psuedo-physical map -- set bit + ** in to_fix since we must send this page in last_iter + ** unless its sent sooner anyhow, or it never enters + ** pseudo-physical map (e.g. for ballooned down doms) + */ + set_bit(n, to_fix); + continue; + } + + if ( last_iter && + test_bit(n, to_fix) && + !test_bit(n, to_send) ) + { + needed_to_fix++; + DPRINTF("Fix! iter %d, pfn %x. mfn %lx\n", + iter, n, pfn_type[batch]); + } + + clear_bit(n, to_fix); + + batch++; + } + + if ( batch == 0 ) + goto skip; /* vanishingly unlikely... */ + + region_base = xc_map_foreign_batch( + xc_handle, dom, PROT_READ, pfn_type, batch); + if ( region_base == NULL ) + { + ERROR("map batch failed"); + goto out; + } + + if ( hvm ) + { + /* Look for and skip completely empty batches. */ + for ( j = 0; j < batch; j++ ) + if ( (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) != + XEN_DOMCTL_PFINFO_XTAB ) + break; + if ( j == batch ) + { + munmap(region_base, batch*PAGE_SIZE); + continue; /* bail on this batch: no valid pages */ + } + } + else + { + /* Get page types */ + for ( j = 0; j < batch; j++ ) + ((uint32_t *)pfn_type)[j] = pfn_type[j]; + if ( xc_get_pfn_type_batch(xc_handle, dom, batch, + (uint32_t *)pfn_type) ) + { + ERROR("get_pfn_type_batch failed"); + goto out; + } + for ( j = batch-1; j >= 0; j-- ) + pfn_type[j] = ((uint32_t *)pfn_type)[j]; + + for ( j = 0; j < batch; j++ ) + { + + if ( (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) == + XEN_DOMCTL_PFINFO_XTAB ) + { + DPRINTF("type fail: page %i mfn %08lx\n", + j, pfn_type[j]); + continue; + } + + if ( debug ) + DPRINTF("%d pfn= %08lx mfn= %08lx [mfn]= %08lx" + " sum= %08lx\n", + iter, + (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) | + pfn_batch[j], + pfn_type[j], + mfn_to_pfn(pfn_type[j] & + ~XEN_DOMCTL_PFINFO_LTAB_MASK), + csum_page(region_base + (PAGE_SIZE*j))); + + /* canonicalise mfn->pfn */ + pfn_type[j] = (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) | + pfn_batch[j]; + } + } + + if ( write_exact(io_fd, &batch, sizeof(unsigned int)) ) + { + PERROR("Error when writing to state file (2)"); + goto out; + } + + if ( write_exact(io_fd, pfn_type, sizeof(unsigned long)*batch) ) + { + PERROR("Error when writing to state file (3)"); + goto out; + } + + /* entering this loop, pfn_type is now in pfns (Not mfns) */ + run = 0; + for ( j = 0; j < batch; j++ ) + { + unsigned long pfn, pagetype; + void *spage = (char *)region_base + (PAGE_SIZE*j); + + pfn = pfn_type[j] & ~XEN_DOMCTL_PFINFO_LTAB_MASK; + pagetype = pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK; + + if ( pagetype != 0 ) + { + /* If the page is not a normal data page, write out any + run of pages we may have previously acumulated */ + if ( run ) + { + if ( ratewrite(io_fd, live, + (char*)region_base+(PAGE_SIZE*(j-run)), + PAGE_SIZE*run) != PAGE_SIZE*run ) + { + ERROR("Error when writing to state file (4a)" + " (errno %d)", errno); + goto out; + } + run = 0; + } + } + + /* skip pages that aren't present */ + if ( pagetype == XEN_DOMCTL_PFINFO_XTAB ) + continue; + + pagetype &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK; + + if ( (pagetype >= XEN_DOMCTL_PFINFO_L1TAB) && + (pagetype <= XEN_DOMCTL_PFINFO_L4TAB) ) + { + /* We have a pagetable page: need to rewrite it. */ + race = + canonicalize_pagetable(pagetype, pfn, spage, page); + + if ( race && !live ) + { + ERROR("Fatal PT race (pfn %lx, type %08lx)", pfn, + pagetype); + goto out; + } + + if ( ratewrite(io_fd, live, page, PAGE_SIZE) != PAGE_SIZE ) + { + ERROR("Error when writing to state file (4b)" + " (errno %d)", errno); + goto out; + } + } + else + { + /* We have a normal page: accumulate it for writing. */ + run++; + } + } /* end of the write out for this batch */ + + if ( run ) + { + /* write out the last accumulated run of pages */ + if ( ratewrite(io_fd, live, + (char*)region_base+(PAGE_SIZE*(j-run)), + PAGE_SIZE*run) != PAGE_SIZE*run ) + { + ERROR("Error when writing to state file (4c)" + " (errno %d)", errno); + goto out; + } + } + + sent_this_iter += batch; + + munmap(region_base, batch*PAGE_SIZE); + + } /* end of this while loop for this iteration */ + + skip: + + total_sent += sent_this_iter; + + DPRINTF("\r %d: sent %d, skipped %d, ", + iter, sent_this_iter, skip_this_iter ); + + if ( last_iter ) + { + print_stats( xc_handle, dom, sent_this_iter, &stats, 1); + + DPRINTF("Total pages sent= %ld (%.2fx)\n", + total_sent, ((float)total_sent)/p2m_size ); + DPRINTF("(of which %ld were fixups)\n", needed_to_fix ); + } + + if ( last_iter && debug ) + { + int minusone = -1; + memset(to_send, 0xff, BITMAP_SIZE); + debug = 0; + DPRINTF("Entering debug resend-all mode\n"); + + /* send "-1" to put receiver into debug mode */ + if ( write_exact(io_fd, &minusone, sizeof(int)) ) + { + PERROR("Error when writing to state file (6)"); + goto out; + } + + continue; + } + + if ( last_iter ) + break; + + if ( live ) + { + if ( ((sent_this_iter > sent_last_iter) && RATE_IS_MAX()) || + (iter >= max_iters) || + (sent_this_iter+skip_this_iter < 50) || + (total_sent > p2m_size*max_factor) ) + { + DPRINTF("Start last iteration\n"); + last_iter = 1; + + if ( suspend_and_state(suspend, xc_handle, io_fd, dom, &info) ) + { + ERROR("Domain appears not to have suspended"); + goto out; + } + + DPRINTF("SUSPEND shinfo %08lx\n", info.shared_info_frame); + } + + if ( xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_CLEAN, to_send, + p2m_size, NULL, 0, &stats) != p2m_size ) + { + ERROR("Error flushing shadow PT"); + goto out; + } + + if ( hvm ) + { + /* Pull in the dirty bits from qemu-dm too */ + if ( !last_iter ) + { + qemu_active = qemu_non_active; + qemu_non_active = qemu_active ? 0 : 1; + qemu_flip_buffer(dom, qemu_active); + for ( j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++ ) + { + to_send[j] |= qemu_bitmaps[qemu_non_active][j]; + qemu_bitmaps[qemu_non_active][j] = 0; + } + } + else + { + for ( j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++ ) + to_send[j] |= qemu_bitmaps[qemu_active][j]; + } + } + + sent_last_iter = sent_this_iter; + + print_stats(xc_handle, dom, sent_this_iter, &stats, 1); + + } + } /* end of infinite for loop */ + + DPRINTF("All memory is saved\n"); + + { + struct { + int minustwo; + int max_vcpu_id; + uint64_t vcpumap; + } chunk = { -2, info.max_vcpu_id }; + + if ( info.max_vcpu_id >= 64 ) + { + ERROR("Too many VCPUS in guest!"); + goto out; + } + + for ( i = 1; i <= info.max_vcpu_id; i++ ) + { + xc_vcpuinfo_t vinfo; + if ( (xc_vcpu_getinfo(xc_handle, dom, i, &vinfo) == 0) && + vinfo.online ) + vcpumap |= 1ULL << i; + } + + chunk.vcpumap = vcpumap; + if ( write_exact(io_fd, &chunk, sizeof(chunk)) ) + { + PERROR("Error when writing to state file"); + goto out; + } + } + + if ( hvm ) + { + struct { + int minusthree; + uint32_t pad; + uint64_t ident_pt; + } chunk = { -3, 0 }; + + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_IDENT_PT, + (unsigned long *)&chunk.ident_pt); + + if ( (chunk.ident_pt != 0) && + write_exact(io_fd, &chunk, sizeof(chunk)) ) + { + PERROR("Error when writing the ident_pt for EPT guest"); + goto out; + } + } + + /* Zero terminate */ + i = 0; + if ( write_exact(io_fd, &i, sizeof(int)) ) + { + PERROR("Error when writing to state file (6')"); + goto out; + } + + if ( hvm ) + { + uint32_t rec_size; + + /* Save magic-page locations. */ + memset(magic_pfns, 0, sizeof(magic_pfns)); + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_IOREQ_PFN, + (unsigned long *)&magic_pfns[0]); + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_BUFIOREQ_PFN, + (unsigned long *)&magic_pfns[1]); + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_STORE_PFN, + (unsigned long *)&magic_pfns[2]); + if ( write_exact(io_fd, magic_pfns, sizeof(magic_pfns)) ) + { + PERROR("Error when writing to state file (7)"); + goto out; + } + + /* Get HVM context from Xen and save it too */ + if ( (rec_size = xc_domain_hvm_getcontext(xc_handle, dom, hvm_buf, + hvm_buf_size)) == -1 ) + { + ERROR("HVM:Could not get hvm buffer"); + goto out; + } + + if ( write_exact(io_fd, &rec_size, sizeof(uint32_t)) ) + { + PERROR("error write hvm buffer size"); + goto out; + } + + if ( write_exact(io_fd, hvm_buf, rec_size) ) + { + PERROR("write HVM info failed!\n"); + goto out; + } + + /* HVM guests are done now */ + rc = 0; + goto out; + } + + /* PV guests only from now on */ + + /* Send through a list of all the PFNs that were not in map at the close */ + { + unsigned int i,j; + unsigned long pfntab[1024]; + + for ( i = 0, j = 0; i < p2m_size; i++ ) + { + if ( !is_mapped(pfn_to_mfn(i)) ) + j++; + } + + if ( write_exact(io_fd, &j, sizeof(unsigned int)) ) + { + PERROR("Error when writing to state file (6a)"); + goto out; + } + + for ( i = 0, j = 0; i < p2m_size; ) + { + if ( !is_mapped(pfn_to_mfn(i)) ) + pfntab[j++] = i; + + i++; + if ( (j == 1024) || (i == p2m_size) ) + { + if ( write_exact(io_fd, &pfntab, sizeof(unsigned long)*j) ) + { + PERROR("Error when writing to state file (6b)"); + goto out; + } + j = 0; + } + } + } + + if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt) ) + { + ERROR("Could not get vcpu context"); + goto out; + } + + /* Canonicalise the suspend-record frame number. */ + mfn = GET_FIELD(&ctxt, user_regs.edx); + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + ERROR("Suspend record is not in range of pseudophys map"); + goto out; + } + SET_FIELD(&ctxt, user_regs.edx, mfn_to_pfn(mfn)); + + for ( i = 0; i <= info.max_vcpu_id; i++ ) + { + if ( !(vcpumap & (1ULL << i)) ) + continue; + + if ( (i != 0) && xc_vcpu_getcontext(xc_handle, dom, i, &ctxt) ) + { + ERROR("No context for VCPU%d", i); + goto out; + } + + /* Canonicalise each GDT frame number. */ + for ( j = 0; (512*j) < GET_FIELD(&ctxt, gdt_ents); j++ ) + { + mfn = GET_FIELD(&ctxt, gdt_frames[j]); + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + ERROR("GDT frame is not in range of pseudophys map"); + goto out; + } + SET_FIELD(&ctxt, gdt_frames[j], mfn_to_pfn(mfn)); + } + + /* Canonicalise the page table base pointer. */ + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(UNFOLD_CR3( + GET_FIELD(&ctxt, ctrlreg[3]))) ) + { + ERROR("PT base is not in range of pseudophys map"); + goto out; + } + SET_FIELD(&ctxt, ctrlreg[3], + FOLD_CR3(mfn_to_pfn(UNFOLD_CR3(GET_FIELD(&ctxt, ctrlreg[3]))))); + + /* Guest pagetable (x86/64) stored in otherwise-unused CR1. */ + if ( (pt_levels == 4) && ctxt.x64.ctrlreg[1] ) + { + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(UNFOLD_CR3(ctxt.x64.ctrlreg[1])) ) + { + ERROR("PT base is not in range of pseudophys map"); + goto out; + } + /* Least-significant bit means 'valid PFN'. */ + ctxt.x64.ctrlreg[1] = 1 | + FOLD_CR3(mfn_to_pfn(UNFOLD_CR3(ctxt.x64.ctrlreg[1]))); + } + + if ( write_exact(io_fd, &ctxt, ((guest_width==8) + ? sizeof(ctxt.x64) + : sizeof(ctxt.x32))) ) + { + PERROR("Error when writing to state file (1)"); + goto out; + } + + domctl.cmd = XEN_DOMCTL_get_ext_vcpucontext; + domctl.domain = dom; + domctl.u.ext_vcpucontext.vcpu = i; + if ( xc_domctl(xc_handle, &domctl) < 0 ) + { + ERROR("No extended context for VCPU%d", i); + goto out; + } + if ( write_exact(io_fd, &domctl.u.ext_vcpucontext, 128) ) + { + PERROR("Error when writing to state file (2)"); + goto out; + } + } + + /* + * Reset the MFN to be a known-invalid value. See map_frame_list_list(). + */ + memcpy(page, live_shinfo, PAGE_SIZE); + SET_FIELD(((shared_info_any_t *)page), + arch.pfn_to_mfn_frame_list_list, 0); + if ( write_exact(io_fd, page, PAGE_SIZE) ) + { + PERROR("Error when writing to state file (1)"); + goto out; + } + + /* Success! */ + rc = 0; + + out: + + if ( live ) + { + if ( xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_OFF, + NULL, 0, NULL, 0, NULL) < 0 ) + DPRINTF("Warning - couldn't disable shadow mode"); + } + + /* Flush last write and discard cache for file. */ + discard_file_cache(io_fd, 1 /* flush */); + + if ( live_shinfo ) + munmap(live_shinfo, PAGE_SIZE); + + if ( live_p2m ) + munmap(live_p2m, P2M_FLL_ENTRIES * PAGE_SIZE); + + if ( live_m2p ) + munmap(live_m2p, M2P_SIZE(max_mfn)); + + free(pfn_type); + free(pfn_batch); + free(to_send); + free(to_fix); + free(to_skip); + + DPRINTF("Save exit rc=%d\n",rc); + + return !!rc; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_e820.h b/tools/libxc/xc_e820.h new file mode 100644 index 0000000..52bbb0f --- /dev/null +++ b/tools/libxc/xc_e820.h @@ -0,0 +1,20 @@ +#ifndef __XC_E820_H__ +#define __XC_E820_H__ + +#include + +/* + * 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 /* __XC_E820_H__ */ diff --git a/tools/libxc/xc_efi.h b/tools/libxc/xc_efi.h new file mode 100644 index 0000000..7b39d7e --- /dev/null +++ b/tools/libxc/xc_efi.h @@ -0,0 +1,145 @@ +#ifndef XC_EFI_H +#define XC_EFI_H + +/* definitions from xen/include/asm-ia64/linux-xen/linux/efi.h */ + +/* + * Extensible Firmware Interface + * Based on 'Extensible Firmware Interface Specification' version 0.9, April 30, 1999 + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond + * Copyright (C) 1999, 2002-2003 Hewlett-Packard Co. + * David Mosberger-Tang + * Stephane Eranian + */ + +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 */ diff --git a/tools/libxc/xc_elf.h b/tools/libxc/xc_elf.h new file mode 100644 index 0000000..089df2b --- /dev/null +++ b/tools/libxc/xc_elf.h @@ -0,0 +1 @@ +#include diff --git a/tools/libxc/xc_evtchn.c b/tools/libxc/xc_evtchn.c new file mode 100644 index 0000000..204698a --- /dev/null +++ b/tools/libxc/xc_evtchn.c @@ -0,0 +1,66 @@ +/****************************************************************************** + * xc_evtchn.c + * + * API for manipulating and accessing inter-domain event channels. + * + * Copyright (c) 2004, K A Fraser. + */ + +#include "xc_private.h" + + +static int do_evtchn_op(int xc_handle, int cmd, void *arg, + size_t arg_size, int silently_fail) +{ + int ret = -1; + DECLARE_HYPERCALL; + + hypercall.op = __HYPERVISOR_event_channel_op; + hypercall.arg[0] = cmd; + hypercall.arg[1] = (unsigned long)arg; + + if ( lock_pages(arg, arg_size) != 0 ) + { + PERROR("do_evtchn_op: arg lock failed"); + goto out; + } + + if ((ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 && !silently_fail) + ERROR("do_evtchn_op: HYPERVISOR_event_channel_op failed: %d", ret); + + unlock_pages(arg, arg_size); + out: + return ret; +} + + +evtchn_port_or_error_t +xc_evtchn_alloc_unbound(int xc_handle, + 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(xc_handle, EVTCHNOP_alloc_unbound, &arg, sizeof(arg), 0); + if ( rc == 0 ) + rc = arg.port; + + return rc; +} + +int xc_evtchn_reset(int xc_handle, + uint32_t dom) +{ + struct evtchn_reset arg = { .dom = (domid_t)dom }; + return do_evtchn_op(xc_handle, EVTCHNOP_reset, &arg, sizeof(arg), 0); +} + +int xc_evtchn_status(int xc_handle, xc_evtchn_status_t *status) +{ + return do_evtchn_op(xc_handle, EVTCHNOP_status, status, + sizeof(*status), 1); +} diff --git a/tools/libxc/xc_flask.c b/tools/libxc/xc_flask.c new file mode 100644 index 0000000..bb2b612 --- /dev/null +++ b/tools/libxc/xc_flask.c @@ -0,0 +1,46 @@ +/****************************************************************************** + * xc_flask.c + * + * 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 "xc_private.h" + +int xc_flask_op(int xc_handle, flask_op_t *op) +{ + int ret = -1; + DECLARE_HYPERCALL; + + hypercall.op = __HYPERVISOR_xsm_op; + hypercall.arg[0] = (unsigned long)op; + + if ( mlock(op, sizeof(*op)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out; + } + + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 ) + { + if ( errno == EACCES ) + fprintf(stderr, "XSM operation failed!\n"); + } + + safe_munlock(op, sizeof(*op)); + + out: + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_hvm_build.c b/tools/libxc/xc_hvm_build.c new file mode 100644 index 0000000..752c4e7 --- /dev/null +++ b/tools/libxc/xc_hvm_build.c @@ -0,0 +1,446 @@ +/****************************************************************************** + * xc_hvm_build.c + */ + +#include +#include +#include +#include +#include + +#include "xg_private.h" +#include "xc_private.h" + +#include +#include +#include +#include +#include "xc_e820.h" + +#include + +#define SUPERPAGE_PFN_SHIFT 9 +#define SUPERPAGE_NR_PFNS (1UL << SUPERPAGE_PFN_SHIFT) + +#define SCRATCH_PFN 0xFFFFF + +#define SPECIALPAGE_GUARD 0 +#define SPECIALPAGE_BUFIOREQ 1 +#define SPECIALPAGE_XENSTORE 2 +#define SPECIALPAGE_IOREQ 3 +#define SPECIALPAGE_IDENT_PT 4 +#define NR_SPECIAL_PAGES 5 + +static void build_e820map(void *e820_page, unsigned long long mem_size) +{ + struct e820entry *e820entry = + (struct e820entry *)(((unsigned char *)e820_page) + HVM_E820_OFFSET); + unsigned long long extra_mem_size = 0; + unsigned char nr_map = 0; + + /* + * Physical address space from HVM_BELOW_4G_RAM_END to 4G is reserved + * for PCI devices MMIO. So if HVM has more than HVM_BELOW_4G_RAM_END + * RAM, memory beyond HVM_BELOW_4G_RAM_END will go to 4G above. + */ + if ( mem_size > HVM_BELOW_4G_RAM_END ) + { + extra_mem_size = mem_size - HVM_BELOW_4G_RAM_END; + mem_size = HVM_BELOW_4G_RAM_END; + } + + /* 0x0-0x9FC00: Ordinary RAM. */ + e820entry[nr_map].addr = 0x0; + e820entry[nr_map].size = 0x9FC00; + e820entry[nr_map].type = E820_RAM; + nr_map++; + + /* 0x9FC00-0xA0000: Extended BIOS Data Area (EBDA). */ + e820entry[nr_map].addr = 0x9FC00; + e820entry[nr_map].size = 0x400; + e820entry[nr_map].type = E820_RESERVED; + nr_map++; + + /* + * 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: hvmloader should free pages which turn out to be unused. + */ + + /* + * 0xE0000-0x0F0000: PC-specific area. We place ACPI tables here. + * We *cannot* mark as E820_ACPI, for two reasons: + * 1. ACPI spec. says that E820_ACPI regions below + * 16MB must clip INT15h 0x88 and 0xe801 queries. + * Our rombios doesn't do this. + * 2. The OS is allowed to reclaim ACPI memory after + * parsing the tables. But our FACS is in this + * region and it must not be reclaimed (it contains + * the ACPI global lock!). + * 0xF0000-0x100000: System BIOS. + * TODO: hvmloader should free pages which turn out to be unused. + */ + e820entry[nr_map].addr = 0xE0000; + e820entry[nr_map].size = 0x20000; + e820entry[nr_map].type = E820_RESERVED; + nr_map++; + + /* Low RAM goes here. Reserve space for special pages. */ + e820entry[nr_map].addr = 0x100000; + e820entry[nr_map].size = (mem_size - 0x100000 - + PAGE_SIZE * NR_SPECIAL_PAGES); + e820entry[nr_map].type = E820_RAM; + nr_map++; + + /* Explicitly reserve space for special pages (excluding guard page). */ + e820entry[nr_map].addr = mem_size - PAGE_SIZE * (NR_SPECIAL_PAGES - 1); + e820entry[nr_map].size = PAGE_SIZE * (NR_SPECIAL_PAGES - 1); + e820entry[nr_map].type = E820_RESERVED; + nr_map++; + + if ( extra_mem_size ) + { + e820entry[nr_map].addr = (1ULL << 32); + e820entry[nr_map].size = extra_mem_size; + e820entry[nr_map].type = E820_RAM; + nr_map++; + } + + *(((unsigned char *)e820_page) + HVM_E820_NR_OFFSET) = nr_map; +} + +static int loadelfimage( + struct elf_binary *elf, int xch, uint32_t dom, unsigned long *parray) +{ + privcmd_mmap_entry_t *entries = NULL; + size_t pages = (elf->pend - elf->pstart + PAGE_SIZE - 1) >> PAGE_SHIFT; + int i, rc = -1; + + /* Map address space for initial elf image. */ + entries = calloc(pages, sizeof(privcmd_mmap_entry_t)); + if ( entries == NULL ) + goto err; + + for ( i = 0; i < pages; i++ ) + entries[i].mfn = parray[(elf->pstart >> PAGE_SHIFT) + i]; + + elf->dest = xc_map_foreign_ranges( + xch, dom, pages << PAGE_SHIFT, PROT_READ | PROT_WRITE, 1 << PAGE_SHIFT, + entries, pages); + if ( elf->dest == NULL ) + goto err; + + /* Load the initial elf image. */ + elf_load_binary(elf); + rc = 0; + + munmap(elf->dest, pages << PAGE_SHIFT); + elf->dest = NULL; + + err: + free(entries); + + return rc; +} + +static int setup_guest(int xc_handle, + uint32_t dom, int memsize, + char *image, unsigned long image_size) +{ + xen_pfn_t *page_array = NULL; + unsigned long i, nr_pages = (unsigned long)memsize << (20 - PAGE_SHIFT); + unsigned long special_page_nr, entry_eip, cur_pages; + struct xen_add_to_physmap xatp; + struct shared_info *shared_info; + void *e820_page; + uint32_t *ident_pt; + struct elf_binary elf; + uint64_t v_start, v_end; + int rc; + xen_capabilities_info_t caps; + + /* An HVM guest must be initialised with at least 2MB memory. */ + if ( memsize < 2 ) + goto error_out; + + if ( elf_init(&elf, image, image_size) != 0 ) + goto error_out; + elf_parse_binary(&elf); + v_start = 0; + v_end = (unsigned long long)memsize << 20; + + if ( xc_version(xc_handle, XENVER_capabilities, &caps) != 0 ) + { + PERROR("Could not get Xen capabilities\n"); + goto error_out; + } + + if ( (elf.pstart & (PAGE_SIZE - 1)) != 0 ) + { + PERROR("Guest OS must load to a page boundary.\n"); + goto error_out; + } + + IPRINTF("VIRTUAL MEMORY ARRANGEMENT:\n" + " Loader: %016"PRIx64"->%016"PRIx64"\n" + " TOTAL: %016"PRIx64"->%016"PRIx64"\n" + " ENTRY ADDRESS: %016"PRIx64"\n", + elf.pstart, elf.pend, + v_start, v_end, + elf_uval(&elf, elf.ehdr, e_entry)); + + if ( (page_array = malloc(nr_pages * sizeof(xen_pfn_t))) == NULL ) + { + PERROR("Could not allocate memory.\n"); + goto error_out; + } + + for ( i = 0; i < nr_pages; i++ ) + page_array[i] = i; + for ( i = HVM_BELOW_4G_RAM_END >> PAGE_SHIFT; i < nr_pages; i++ ) + page_array[i] += HVM_BELOW_4G_MMIO_LENGTH >> PAGE_SHIFT; + + /* + * Allocate memory for HVM guest, skipping VGA hole 0xA0000-0xC0000. + * We allocate pages in batches of no more than 8MB to ensure that + * we can be preempted and hence dom0 remains responsive. + */ + rc = xc_domain_memory_populate_physmap( + xc_handle, dom, 0xa0, 0, 0, &page_array[0x00]); + cur_pages = 0xc0; + while ( (rc == 0) && (nr_pages > cur_pages) ) + { + /* Clip count to maximum 8MB extent. */ + unsigned long count = nr_pages - cur_pages; + if ( count > 2048 ) + count = 2048; + + /* Clip partial superpage extents to superpage boundaries. */ + if ( ((cur_pages & (SUPERPAGE_NR_PFNS-1)) != 0) && + (count > (-cur_pages & (SUPERPAGE_NR_PFNS-1))) ) + count = -cur_pages & (SUPERPAGE_NR_PFNS-1); /* clip s.p. tail */ + else if ( ((count & (SUPERPAGE_NR_PFNS-1)) != 0) && + (count > SUPERPAGE_NR_PFNS) ) + count &= ~(SUPERPAGE_NR_PFNS - 1); /* clip non-s.p. tail */ + + /* Attempt to allocate superpage extents. */ + if ( ((count | cur_pages) & (SUPERPAGE_NR_PFNS - 1)) == 0 ) + { + long done; + xen_pfn_t sp_extents[count >> SUPERPAGE_PFN_SHIFT]; + struct xen_memory_reservation sp_req = { + .nr_extents = count >> SUPERPAGE_PFN_SHIFT, + .extent_order = SUPERPAGE_PFN_SHIFT, + .domid = dom + }; + set_xen_guest_handle(sp_req.extent_start, sp_extents); + for ( i = 0; i < sp_req.nr_extents; i++ ) + sp_extents[i] = page_array[cur_pages+(i< 0 ) + { + done <<= SUPERPAGE_PFN_SHIFT; + cur_pages += done; + count -= done; + } + } + + /* Fall back to 4kB extents. */ + if ( count != 0 ) + { + rc = xc_domain_memory_populate_physmap( + xc_handle, dom, count, 0, 0, &page_array[cur_pages]); + cur_pages += count; + } + } + + if ( rc != 0 ) + { + PERROR("Could not allocate memory for HVM guest.\n"); + goto error_out; + } + + if ( loadelfimage(&elf, xc_handle, dom, page_array) != 0 ) + goto error_out; + + if ( (e820_page = xc_map_foreign_range( + xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, + HVM_E820_PAGE >> PAGE_SHIFT)) == NULL ) + goto error_out; + memset(e820_page, 0, PAGE_SIZE); + build_e820map(e820_page, v_end); + munmap(e820_page, PAGE_SIZE); + + /* Map and initialise shared_info page. */ + xatp.domid = dom; + xatp.space = XENMAPSPACE_shared_info; + xatp.idx = 0; + xatp.gpfn = SCRATCH_PFN; + if ( (xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp) != 0) || + ((shared_info = xc_map_foreign_range( + xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, + SCRATCH_PFN)) == NULL) ) + goto error_out; + memset(shared_info, 0, PAGE_SIZE); + /* NB. evtchn_upcall_mask is unused: leave as zero. */ + memset(&shared_info->evtchn_mask[0], 0xff, + sizeof(shared_info->evtchn_mask)); + munmap(shared_info, PAGE_SIZE); + + special_page_nr = (((v_end > HVM_BELOW_4G_RAM_END) + ? (HVM_BELOW_4G_RAM_END >> PAGE_SHIFT) + : (v_end >> PAGE_SHIFT)) + - NR_SPECIAL_PAGES); + + /* Paranoia: clean special pages. */ + for ( i = 0; i < NR_SPECIAL_PAGES; i++ ) + if ( xc_clear_domain_page(xc_handle, dom, special_page_nr + i) ) + goto error_out; + + /* Free the guard page that separates low RAM from special pages. */ + rc = xc_domain_memory_decrease_reservation( + xc_handle, dom, 1, 0, &page_array[special_page_nr]); + if ( rc != 0 ) + { + PERROR("Could not deallocate guard page for HVM guest.\n"); + goto error_out; + } + + xc_set_hvm_param(xc_handle, dom, HVM_PARAM_STORE_PFN, + special_page_nr + SPECIALPAGE_XENSTORE); + xc_set_hvm_param(xc_handle, dom, HVM_PARAM_BUFIOREQ_PFN, + special_page_nr + SPECIALPAGE_BUFIOREQ); + xc_set_hvm_param(xc_handle, dom, HVM_PARAM_IOREQ_PFN, + special_page_nr + SPECIALPAGE_IOREQ); + + /* + * 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( + xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, + special_page_nr + 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_set_hvm_param(xc_handle, dom, HVM_PARAM_IDENT_PT, + (special_page_nr + SPECIALPAGE_IDENT_PT) << PAGE_SHIFT); + + /* Insert JMP instruction at address 0x0 to reach entry point. */ + entry_eip = elf_uval(&elf, elf.ehdr, e_entry); + if ( entry_eip != 0 ) + { + char *page0 = xc_map_foreign_range( + xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, 0); + if ( page0 == NULL ) + goto error_out; + page0[0] = 0xe9; + *(uint32_t *)&page0[1] = entry_eip - 5; + munmap(page0, PAGE_SIZE); + } + + free(page_array); + return 0; + + error_out: + free(page_array); + return -1; +} + +static int xc_hvm_build_internal(int xc_handle, + uint32_t domid, + int memsize, + char *image, + unsigned long image_size) +{ + if ( (image == NULL) || (image_size == 0) ) + { + ERROR("Image required"); + return -1; + } + + return setup_guest(xc_handle, domid, memsize, image, image_size); +} + +static inline int is_loadable_phdr(Elf32_Phdr *phdr) +{ + return ((phdr->p_type == PT_LOAD) && + ((phdr->p_flags & (PF_W|PF_X)) != 0)); +} + +/* xc_hvm_build: + * Create a domain for a virtualized Linux, using files/filenames. + */ +int xc_hvm_build(int xc_handle, + uint32_t domid, + int memsize, + const char *image_name) +{ + char *image; + int sts; + unsigned long image_size; + + if ( (image_name == NULL) || + ((image = xc_read_image(image_name, &image_size)) == NULL) ) + return -1; + + sts = xc_hvm_build_internal(xc_handle, domid, memsize, image, image_size); + + free(image); + + return sts; +} + +/* xc_hvm_build_mem: + * Create a domain for a virtualized Linux, using memory buffers. + */ +int xc_hvm_build_mem(int xc_handle, + uint32_t domid, + int memsize, + const char *image_buffer, + unsigned long image_size) +{ + int sts; + unsigned long img_len; + char *img; + + /* Validate that there is a kernel buffer */ + + if ( (image_buffer == NULL) || (image_size == 0) ) + { + ERROR("kernel image buffer not present"); + return -1; + } + + img = xc_inflate_buffer(image_buffer, image_size, &img_len); + if ( img == NULL ) + { + ERROR("unable to inflate ram disk buffer"); + return -1; + } + + sts = xc_hvm_build_internal(xc_handle, domid, memsize, + img, img_len); + + /* xc_inflate_buffer may return the original buffer pointer (for + for already inflated buffers), so exercise some care in freeing */ + + if ( (img != NULL) && (img != image_buffer) ) + free(img); + + return sts; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_linux.c b/tools/libxc/xc_linux.c new file mode 100644 index 0000000..2480b3c --- /dev/null +++ b/tools/libxc/xc_linux.c @@ -0,0 +1,573 @@ +/****************************************************************************** + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * xc_gnttab functions: + * Copyright (c) 2007-2008, D G Murray + * + * 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 "xc_private.h" + +#include +#include +#include +#include +#include + +int xc_interface_open(void) +{ + int flags, saved_errno; + int fd = open("/proc/xen/privcmd", O_RDWR); + + if ( fd == -1 ) + { + PERROR("Could not obtain handle on privileged command interface"); + return -1; + } + + /* Although we return the file handle as the 'xc handle' the API + does not specify / guarentee that this integer is in fact + a file handle. Thus we must take responsiblity to ensure + it doesn't propagate (ie leak) outside the process */ + if ( (flags = fcntl(fd, F_GETFD)) < 0 ) + { + PERROR("Could not get file handle flags"); + goto error; + } + flags |= FD_CLOEXEC; + if ( fcntl(fd, F_SETFD, flags) < 0 ) + { + PERROR("Could not set file handle flags"); + goto error; + } + + return fd; + + error: + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; +} + +int xc_interface_close(int xc_handle) +{ + return close(xc_handle); +} + +void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int num) +{ + privcmd_mmapbatch_t ioctlx; + void *addr; + addr = mmap(NULL, num*PAGE_SIZE, prot, MAP_SHARED, xc_handle, 0); + if ( addr == MAP_FAILED ) { + perror("xc_map_foreign_batch: mmap failed"); + return NULL; + } + + ioctlx.num=num; + ioctlx.dom=dom; + ioctlx.addr=(unsigned long)addr; + ioctlx.arr=arr; + if ( ioctl(xc_handle, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx) < 0 ) + { + int saved_errno = errno; + perror("xc_map_foreign_batch: ioctl failed"); + (void)munmap(addr, num*PAGE_SIZE); + errno = saved_errno; + return NULL; + } + return addr; + +} + +void *xc_map_foreign_range(int xc_handle, uint32_t dom, + int size, int prot, + unsigned long mfn) +{ + privcmd_mmap_t ioctlx; + privcmd_mmap_entry_t entry; + void *addr; + addr = mmap(NULL, size, prot, MAP_SHARED, xc_handle, 0); + if ( addr == MAP_FAILED ) { + perror("xc_map_foreign_range: mmap failed"); + return NULL; + } + + ioctlx.num=1; + ioctlx.dom=dom; + ioctlx.entry=&entry; + entry.va=(unsigned long) addr; + entry.mfn=mfn; + entry.npages=(size+PAGE_SIZE-1)>>PAGE_SHIFT; + if ( ioctl(xc_handle, IOCTL_PRIVCMD_MMAP, &ioctlx) < 0 ) + { + int saved_errno = errno; + perror("xc_map_foreign_range: ioctl failed"); + (void)munmap(addr, size); + errno = saved_errno; + return NULL; + } + return addr; +} + +void *xc_map_foreign_ranges(int xc_handle, uint32_t dom, + size_t size, int prot, size_t chunksize, + privcmd_mmap_entry_t entries[], int nentries) +{ + privcmd_mmap_t ioctlx; + int i, rc; + void *addr; + + addr = mmap(NULL, size, prot, MAP_SHARED, xc_handle, 0); + if ( addr == MAP_FAILED ) + goto mmap_failed; + + for ( i = 0; i < nentries; i++ ) + { + entries[i].va = (unsigned long)addr + (i * chunksize); + entries[i].npages = chunksize >> PAGE_SHIFT; + } + + ioctlx.num = nentries; + ioctlx.dom = dom; + ioctlx.entry = entries; + + rc = ioctl(xc_handle, IOCTL_PRIVCMD_MMAP, &ioctlx); + if ( rc ) + goto ioctl_failed; + + return addr; + +ioctl_failed: + rc = munmap(addr, size); + if ( rc == -1 ) + ERROR("%s: error in error path\n", __FUNCTION__); + +mmap_failed: + return NULL; +} + +static int do_privcmd(int xc_handle, unsigned int cmd, unsigned long data) +{ + return ioctl(xc_handle, cmd, data); +} + +int do_xen_hypercall(int xc_handle, privcmd_hypercall_t *hypercall) +{ + return do_privcmd(xc_handle, + IOCTL_PRIVCMD_HYPERCALL, + (unsigned long)hypercall); +} + +#define MTAB "/proc/mounts" +#define MAX_PATH 255 +#define _STR(x) #x +#define STR(x) _STR(x) + +static int find_sysfsdir(char *sysfsdir) +{ + FILE *fp; + char type[MAX_PATH + 1]; + + if ( (fp = fopen(MTAB, "r")) == NULL ) + return -1; + + while ( fscanf(fp, "%*s %" + STR(MAX_PATH) + "s %" + STR(MAX_PATH) + "s %*s %*d %*d\n", + sysfsdir, type) == 2 ) + { + if ( strncmp(type, "sysfs", 5) == 0 ) + break; + } + + fclose(fp); + + return ((strncmp(type, "sysfs", 5) == 0) ? 0 : -1); +} + +int xc_find_device_number(const char *name) +{ + FILE *fp; + int i, major, minor; + char sysfsdir[MAX_PATH + 1]; + static char *classlist[] = { "xen", "misc" }; + + for ( i = 0; i < (sizeof(classlist) / sizeof(classlist[0])); i++ ) + { + if ( find_sysfsdir(sysfsdir) < 0 ) + goto not_found; + + /* /class///dev */ + strncat(sysfsdir, "/class/", MAX_PATH); + strncat(sysfsdir, classlist[i], MAX_PATH); + strncat(sysfsdir, "/", MAX_PATH); + strncat(sysfsdir, name, MAX_PATH); + strncat(sysfsdir, "/dev", MAX_PATH); + + if ( (fp = fopen(sysfsdir, "r")) != NULL ) + goto found; + } + + not_found: + errno = -ENOENT; + return -1; + + found: + if ( fscanf(fp, "%d:%d", &major, &minor) != 2 ) + { + fclose(fp); + goto not_found; + } + + fclose(fp); + + return makedev(major, minor); +} + +#define EVTCHN_DEV_NAME "/dev/xen/evtchn" + +int xc_evtchn_open(void) +{ + struct stat st; + int fd; + int devnum; + + devnum = xc_find_device_number("evtchn"); + + /* Make sure any existing device file links to correct device. */ + if ( (lstat(EVTCHN_DEV_NAME, &st) != 0) || !S_ISCHR(st.st_mode) || + (st.st_rdev != devnum) ) + (void)unlink(EVTCHN_DEV_NAME); + + reopen: + if ( (fd = open(EVTCHN_DEV_NAME, O_RDWR)) == -1 ) + { + if ( (errno == ENOENT) && + ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) && + (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600, devnum) == 0) ) + goto reopen; + + PERROR("Could not open event channel interface"); + return -1; + } + + return fd; +} + +int xc_evtchn_close(int xce_handle) +{ + return close(xce_handle); +} + +int xc_evtchn_fd(int xce_handle) +{ + return xce_handle; +} + +int xc_evtchn_notify(int xce_handle, evtchn_port_t port) +{ + struct ioctl_evtchn_notify notify; + + notify.port = port; + + return ioctl(xce_handle, IOCTL_EVTCHN_NOTIFY, ¬ify); +} + +evtchn_port_or_error_t +xc_evtchn_bind_unbound_port(int xce_handle, int domid) +{ + struct ioctl_evtchn_bind_unbound_port bind; + + bind.remote_domain = domid; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_UNBOUND_PORT, &bind); +} + +evtchn_port_or_error_t +xc_evtchn_bind_interdomain(int xce_handle, int domid, + evtchn_port_t remote_port) +{ + struct ioctl_evtchn_bind_interdomain bind; + + bind.remote_domain = domid; + bind.remote_port = remote_port; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind); +} + +evtchn_port_or_error_t +xc_evtchn_bind_virq(int xce_handle, unsigned int virq) +{ + struct ioctl_evtchn_bind_virq bind; + + bind.virq = virq; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_VIRQ, &bind); +} + +int xc_evtchn_unbind(int xce_handle, evtchn_port_t port) +{ + struct ioctl_evtchn_unbind unbind; + + unbind.port = port; + + return ioctl(xce_handle, IOCTL_EVTCHN_UNBIND, &unbind); +} + +evtchn_port_or_error_t +xc_evtchn_pending(int xce_handle) +{ + evtchn_port_t port; + + if ( read_exact(xce_handle, (char *)&port, sizeof(port)) == -1 ) + return -1; + + return port; +} + +int xc_evtchn_unmask(int xce_handle, evtchn_port_t port) +{ + return write_exact(xce_handle, (char *)&port, sizeof(port)); +} + +/* Optionally flush file to disk and discard page cache */ +void discard_file_cache(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 &= ~(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; +} + +#define GNTTAB_DEV_NAME "/dev/xen/gntdev" + +int xc_gnttab_open(void) +{ + struct stat st; + int fd; + int devnum; + + devnum = xc_find_device_number("gntdev"); + + /* Make sure any existing device file links to correct device. */ + if ( (lstat(GNTTAB_DEV_NAME, &st) != 0) || !S_ISCHR(st.st_mode) || + (st.st_rdev != devnum) ) + (void)unlink(GNTTAB_DEV_NAME); + +reopen: + if ( (fd = open(GNTTAB_DEV_NAME, O_RDWR)) == -1 ) + { + if ( (errno == ENOENT) && + ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) && + (mknod(GNTTAB_DEV_NAME, S_IFCHR|0600, devnum) == 0) ) + goto reopen; + + PERROR("Could not open grant table interface"); + return -1; + } + + return fd; +} + +int xc_gnttab_close(int xcg_handle) +{ + return close(xcg_handle); +} + +void *xc_gnttab_map_grant_ref(int xcg_handle, + uint32_t domid, + uint32_t ref, + int prot) +{ + struct ioctl_gntdev_map_grant_ref map; + void *addr; + + map.count = 1; + map.refs[0].domid = domid; + map.refs[0].ref = ref; + + if ( ioctl(xcg_handle, IOCTL_GNTDEV_MAP_GRANT_REF, &map) ) + return NULL; + + addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, xcg_handle, map.index); + if ( addr == MAP_FAILED ) + { + int saved_errno = errno; + struct ioctl_gntdev_unmap_grant_ref unmap_grant; + /* Unmap the driver slots used to store the grant information. */ + perror("xc_gnttab_map_grant_ref: mmap failed"); + unmap_grant.index = map.index; + unmap_grant.count = 1; + ioctl(xcg_handle, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant); + errno = saved_errno; + return NULL; + } + + return addr; +} + +static void *do_gnttab_map_grant_refs(int xcg_handle, + uint32_t count, + uint32_t *domids, + int domids_stride, + uint32_t *refs, + int prot) +{ + struct ioctl_gntdev_map_grant_ref *map; + void *addr = NULL; + int i; + + map = malloc(sizeof(*map) + + (count-1) * sizeof(struct ioctl_gntdev_map_grant_ref)); + if ( map == NULL ) + return NULL; + + for ( i = 0; i < count; i++ ) + { + map->refs[i].domid = domids[i * domids_stride]; + map->refs[i].ref = refs[i]; + } + + map->count = count; + + if ( ioctl(xcg_handle, IOCTL_GNTDEV_MAP_GRANT_REF, map) ) + goto out; + + addr = mmap(NULL, PAGE_SIZE * count, prot, MAP_SHARED, xcg_handle, + map->index); + if ( addr == MAP_FAILED ) + { + int saved_errno = errno; + struct ioctl_gntdev_unmap_grant_ref unmap_grant; + /* Unmap the driver slots used to store the grant information. */ + perror("xc_gnttab_map_grant_refs: mmap failed"); + unmap_grant.index = map->index; + unmap_grant.count = count; + ioctl(xcg_handle, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant); + errno = saved_errno; + addr = NULL; + } + + out: + free(map); + return addr; +} + +void *xc_gnttab_map_grant_refs(int xcg_handle, + uint32_t count, + uint32_t *domids, + uint32_t *refs, + int prot) +{ + return do_gnttab_map_grant_refs(xcg_handle, count, domids, 1, refs, prot); +} + +void *xc_gnttab_map_domain_grant_refs(int xcg_handle, + uint32_t count, + uint32_t domid, + uint32_t *refs, + int prot) +{ + return do_gnttab_map_grant_refs(xcg_handle, count, &domid, 0, refs, prot); +} + +int xc_gnttab_munmap(int xcg_handle, + void *start_address, + uint32_t count) +{ + struct ioctl_gntdev_get_offset_for_vaddr get_offset; + struct ioctl_gntdev_unmap_grant_ref unmap_grant; + int rc; + + if ( start_address == NULL ) + { + errno = EINVAL; + return -1; + } + + /* First, it is necessary to get the offset which was initially used to + * mmap() the pages. + */ + get_offset.vaddr = (unsigned long)start_address; + if ( (rc = ioctl(xcg_handle, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR, + &get_offset)) ) + return rc; + + if ( get_offset.count != count ) + { + errno = EINVAL; + return -1; + } + + /* Next, unmap the memory. */ + if ( (rc = munmap(start_address, count * getpagesize())) ) + return rc; + + /* Finally, unmap the driver slots used to store the grant information. */ + unmap_grant.index = get_offset.offset; + unmap_grant.count = count; + if ( (rc = ioctl(xcg_handle, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant)) ) + return rc; + + return 0; +} + +int xc_gnttab_set_max_grants(int xcg_handle, + uint32_t count) +{ + struct ioctl_gntdev_set_max_grants set_max; + int rc; + + set_max.count = count; + if ( (rc = ioctl(xcg_handle, IOCTL_GNTDEV_SET_MAX_GRANTS, &set_max)) ) + return rc; + + return 0; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_minios.c b/tools/libxc/xc_minios.c new file mode 100644 index 0000000..a4e32ff --- /dev/null +++ b/tools/libxc/xc_minios.c @@ -0,0 +1,425 @@ +/****************************************************************************** + * + * Copyright 2007-2008 Samuel Thibault . + * 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. + */ + +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xc_private.h" + +extern struct wait_queue_head event_queue; + +int xc_interface_open(void) +{ + return alloc_fd(FTYPE_XC); +} + +int xc_interface_close(int xc_handle) +{ + files[xc_handle].type = FTYPE_NONE; + return 0; +} + +void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int num) +{ + unsigned long pt_prot = 0; +#ifdef __ia64__ + /* TODO */ +#else + if (prot & PROT_READ) + pt_prot = L1_PROT_RO; + if (prot & PROT_WRITE) + pt_prot = L1_PROT; +#endif + return map_frames_ex(arr, num, 1, 0, 1, dom, 1, pt_prot); +} + +void *xc_map_foreign_range(int xc_handle, uint32_t dom, + int size, int prot, + unsigned long mfn) +{ + unsigned long pt_prot = 0; +#ifdef __ia64__ + /* TODO */ +#else + if (prot & PROT_READ) + pt_prot = L1_PROT_RO; + if (prot & PROT_WRITE) + pt_prot = L1_PROT; +#endif + assert(!(size % getpagesize())); + return map_frames_ex(&mfn, size / getpagesize(), 0, 1, 1, dom, 0, pt_prot); +} + +void *xc_map_foreign_ranges(int xc_handle, uint32_t dom, + size_t size, int prot, size_t chunksize, + privcmd_mmap_entry_t entries[], int nentries) +{ + unsigned long *mfns; + int i, j, n; + unsigned long pt_prot = 0; + void *ret; +#ifdef __ia64__ + /* TODO */ +#else + if (prot & PROT_READ) + pt_prot = L1_PROT_RO; + if (prot & PROT_WRITE) + pt_prot = L1_PROT; +#endif + + mfns = malloc((size / PAGE_SIZE) * sizeof(*mfns)); + + n = 0; + for (i = 0; i < nentries; i++) + for (j = 0; j < chunksize / PAGE_SIZE; j++) + mfns[n++] = entries[i].mfn + j; + + ret = map_frames_ex(mfns, n, 1, 0, 1, dom, 0, pt_prot); + free(mfns); + return ret; +} + + +int do_xen_hypercall(int xc_handle, privcmd_hypercall_t *hypercall) +{ + multicall_entry_t call; + int i, ret; + + call.op = hypercall->op; + for (i = 0; i < sizeof(hypercall->arg) / sizeof(*hypercall->arg); i++) + call.args[i] = hypercall->arg[i]; + + ret = HYPERVISOR_multicall(&call, 1); + + if (ret < 0) { + errno = -ret; + return -1; + } + if ((long) call.result < 0) { + errno = - (long) call.result; + return -1; + } + return call.result; +} + +int xc_find_device_number(const char *name) +{ + printf("xc_find_device_number(%s)\n", name); + do_exit(); +} + +int xc_evtchn_open(void) +{ + int fd = alloc_fd(FTYPE_EVTCHN), i; + for (i = 0; i < MAX_EVTCHN_PORTS; i++) { + files[fd].evtchn.ports[i].port = -1; + files[fd].evtchn.ports[i].bound = 0; + } + printf("evtchn_open() -> %d\n", fd); + return fd; +} + +int xc_evtchn_close(int xce_handle) +{ + int i; + for (i = 0; i < MAX_EVTCHN_PORTS; i++) + if (files[xce_handle].evtchn.ports[i].bound) + unbind_evtchn(files[xce_handle].evtchn.ports[i].port); + files[xce_handle].type = FTYPE_NONE; + return 0; +} + +int xc_evtchn_fd(int xce_handle) +{ + return xce_handle; +} + +int xc_evtchn_notify(int xce_handle, evtchn_port_t port) +{ + int ret; + + ret = notify_remote_via_evtchn(port); + + if (ret < 0) { + errno = -ret; + ret = -1; + } + return ret; +} + +/* XXX Note: This is not threadsafe */ +static int port_alloc(int xce_handle) { + int i; + for (i= 0; i < MAX_EVTCHN_PORTS; i++) + if (files[xce_handle].evtchn.ports[i].port == -1) + break; + if (i == MAX_EVTCHN_PORTS) { + printf("Too many ports in xc handle\n"); + errno = EMFILE; + return -1; + } + files[xce_handle].evtchn.ports[i].pending = 0; + return i; +} + +static void evtchn_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ + int xce_handle = (intptr_t) data; + int i; + assert(files[xce_handle].type == FTYPE_EVTCHN); + mask_evtchn(port); + for (i= 0; i < MAX_EVTCHN_PORTS; i++) + if (files[xce_handle].evtchn.ports[i].port == port) + break; + if (i == MAX_EVTCHN_PORTS) { + printk("Unknown port for handle %d\n", xce_handle); + return; + } + files[xce_handle].evtchn.ports[i].pending = 1; + files[xce_handle].read = 1; + wake_up(&event_queue); +} + +evtchn_port_or_error_t xc_evtchn_bind_unbound_port(int xce_handle, int domid) +{ + int ret, i; + evtchn_port_t port; + + assert(get_current() == main_thread); + i = port_alloc(xce_handle); + if (i == -1) + return -1; + + printf("xc_evtchn_bind_unbound_port(%d)", domid); + ret = evtchn_alloc_unbound(domid, evtchn_handler, (void*)(intptr_t)xce_handle, &port); + printf(" = %d\n", ret); + + if (ret < 0) { + errno = -ret; + return -1; + } + files[xce_handle].evtchn.ports[i].bound = 1; + files[xce_handle].evtchn.ports[i].port = port; + unmask_evtchn(port); + return port; +} + +evtchn_port_or_error_t xc_evtchn_bind_interdomain(int xce_handle, int domid, + evtchn_port_t remote_port) +{ + evtchn_port_t local_port; + int ret, i; + + assert(get_current() == main_thread); + i = port_alloc(xce_handle); + if (i == -1) + return -1; + + printf("xc_evtchn_bind_interdomain(%d, %"PRId32")", domid, remote_port); + ret = evtchn_bind_interdomain(domid, remote_port, evtchn_handler, (void*)(intptr_t)xce_handle, &local_port); + printf(" = %d\n", ret); + + if (ret < 0) { + errno = -ret; + return -1; + } + files[xce_handle].evtchn.ports[i].bound = 1; + files[xce_handle].evtchn.ports[i].port = local_port; + unmask_evtchn(local_port); + return local_port; +} + +int xc_evtchn_unbind(int xce_handle, evtchn_port_t port) +{ + int i; + for (i = 0; i < MAX_EVTCHN_PORTS; i++) + if (files[xce_handle].evtchn.ports[i].port == port) { + files[xce_handle].evtchn.ports[i].port = -1; + break; + } + if (i == MAX_EVTCHN_PORTS) { + printf("Warning: couldn't find port %"PRId32" for xc handle %x\n", port, xce_handle); + errno = -EINVAL; + return -1; + } + files[xce_handle].evtchn.ports[i].bound = 0; + unbind_evtchn(port); + return 0; +} + +evtchn_port_or_error_t xc_evtchn_bind_virq(int xce_handle, unsigned int virq) +{ + evtchn_port_t port; + int i; + + assert(get_current() == main_thread); + i = port_alloc(xce_handle); + if (i == -1) + return -1; + + printf("xc_evtchn_bind_virq(%d)", virq); + port = bind_virq(virq, evtchn_handler, (void*)(intptr_t)xce_handle); + + if (port < 0) { + errno = -port; + return -1; + } + files[xce_handle].evtchn.ports[i].bound = 1; + files[xce_handle].evtchn.ports[i].port = port; + unmask_evtchn(port); + return port; +} + +evtchn_port_or_error_t xc_evtchn_pending(int xce_handle) +{ + int i; + unsigned long flags; + evtchn_port_t ret = -1; + + local_irq_save(flags); + files[xce_handle].read = 0; + for (i = 0; i < MAX_EVTCHN_PORTS; i++) { + evtchn_port_t port = files[xce_handle].evtchn.ports[i].port; + if (port != -1 && files[xce_handle].evtchn.ports[i].pending) { + if (ret == -1) { + ret = port; + files[xce_handle].evtchn.ports[i].pending = 0; + } else { + files[xce_handle].read = 1; + break; + } + } + } + local_irq_restore(flags); + return ret; +} + +int xc_evtchn_unmask(int xce_handle, evtchn_port_t port) +{ + unmask_evtchn(port); + return 0; +} + +/* Optionally flush file to disk and discard page cache */ +void discard_file_cache(int fd, int flush) +{ + if (flush) + fsync(fd); +} + +int xc_gnttab_open(void) +{ + int xcg_handle; + xcg_handle = alloc_fd(FTYPE_GNTMAP); + gntmap_init(&files[xcg_handle].gntmap); + return xcg_handle; +} + +int xc_gnttab_close(int xcg_handle) +{ + gntmap_fini(&files[xcg_handle].gntmap); + files[xcg_handle].type = FTYPE_NONE; + return 0; +} + +void *xc_gnttab_map_grant_ref(int xcg_handle, + uint32_t domid, + uint32_t ref, + int prot) +{ + return gntmap_map_grant_refs(&files[xcg_handle].gntmap, + 1, + &domid, 0, + &ref, + prot & PROT_WRITE); +} + +void *xc_gnttab_map_grant_refs(int xcg_handle, + uint32_t count, + uint32_t *domids, + uint32_t *refs, + int prot) +{ + return gntmap_map_grant_refs(&files[xcg_handle].gntmap, + count, + domids, 1, + refs, + prot & PROT_WRITE); +} + +void *xc_gnttab_map_domain_grant_refs(int xcg_handle, + uint32_t count, + uint32_t domid, + uint32_t *refs, + int prot) +{ + return gntmap_map_grant_refs(&files[xcg_handle].gntmap, + count, + &domid, 0, + refs, + prot & PROT_WRITE); +} + +int xc_gnttab_munmap(int xcg_handle, + void *start_address, + uint32_t count) +{ + int ret; + ret = gntmap_munmap(&files[xcg_handle].gntmap, + (unsigned long) start_address, + count); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +int xc_gnttab_set_max_grants(int xcg_handle, + uint32_t count) +{ + int ret; + ret = gntmap_set_max_grants(&files[xcg_handle].gntmap, + count); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c new file mode 100644 index 0000000..7fa1607 --- /dev/null +++ b/tools/libxc/xc_misc.c @@ -0,0 +1,369 @@ +/****************************************************************************** + * xc_misc.c + * + * Miscellaneous control interface functions. + */ + +#include "xc_private.h" +#include + +int xc_readconsolering(int xc_handle, + char **pbuffer, + unsigned int *pnr_chars, + int clear, int incremental, uint32_t *pindex) +{ + int ret; + DECLARE_SYSCTL; + char *buffer = *pbuffer; + unsigned int nr_chars = *pnr_chars; + + 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 = lock_pages(buffer, nr_chars)) != 0 ) + return ret; + + if ( (ret = do_sysctl(xc_handle, &sysctl)) == 0 ) + { + *pnr_chars = sysctl.u.readconsole.count; + if ( pindex ) + *pindex = sysctl.u.readconsole.index; + } + + unlock_pages(buffer, nr_chars); + + return ret; +} + +int xc_send_debug_keys(int xc_handle, char *keys) +{ + int ret, len = strlen(keys); + DECLARE_SYSCTL; + + sysctl.cmd = XEN_SYSCTL_debug_keys; + set_xen_guest_handle(sysctl.u.debug_keys.keys, keys); + sysctl.u.debug_keys.nr_keys = len; + + if ( (ret = lock_pages(keys, len)) != 0 ) + return ret; + + ret = do_sysctl(xc_handle, &sysctl); + + unlock_pages(keys, len); + + return ret; +} + +int xc_physinfo(int xc_handle, + 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(xc_handle, &sysctl)) != 0 ) + return ret; + + memcpy(put_info, &sysctl.u.physinfo, sizeof(*put_info)); + + return 0; +} + +int xc_sched_id(int xc_handle, + int *sched_id) +{ + int ret; + DECLARE_SYSCTL; + + sysctl.cmd = XEN_SYSCTL_sched_id; + + if ( (ret = do_sysctl(xc_handle, &sysctl)) != 0 ) + return ret; + + *sched_id = sysctl.u.sched_id.sched_id; + + return 0; +} + +int xc_perfc_control(int xc_handle, + uint32_t opcode, + xc_perfc_desc_t *desc, + xc_perfc_val_t *val, + int *nbr_desc, + int *nbr_val) +{ + int rc; + DECLARE_SYSCTL; + + sysctl.cmd = XEN_SYSCTL_perfc_op; + sysctl.u.perfc_op.cmd = opcode; + set_xen_guest_handle(sysctl.u.perfc_op.desc, desc); + set_xen_guest_handle(sysctl.u.perfc_op.val, val); + + rc = do_sysctl(xc_handle, &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_getcpuinfo(int xc_handle, int max_cpus, + xc_cpuinfo_t *info, int *nr_cpus) +{ + int rc; + DECLARE_SYSCTL; + + sysctl.cmd = XEN_SYSCTL_getcpuinfo; + sysctl.u.getcpuinfo.max_cpus = max_cpus; + set_xen_guest_handle(sysctl.u.getcpuinfo.info, info); + + if ( (rc = lock_pages(info, max_cpus*sizeof(*info))) != 0 ) + return rc; + + rc = do_sysctl(xc_handle, &sysctl); + + unlock_pages(info, max_cpus*sizeof(*info)); + + if ( nr_cpus ) + *nr_cpus = sysctl.u.getcpuinfo.nr_cpus; + + return rc; +} + + +int xc_hvm_set_pci_intx_level( + int xc_handle, domid_t dom, + uint8_t domain, uint8_t bus, uint8_t device, uint8_t intx, + unsigned int level) +{ + DECLARE_HYPERCALL; + struct xen_hvm_set_pci_intx_level arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_pci_intx_level; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.domain = domain; + arg.bus = bus; + arg.device = device; + arg.intx = intx; + arg.level = level; + + if ( (rc = lock_pages(&arg, sizeof(arg))) != 0 ) + { + PERROR("Could not lock memory"); + return rc; + } + + rc = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + return rc; +} + +int xc_hvm_set_isa_irq_level( + int xc_handle, domid_t dom, + uint8_t isa_irq, + unsigned int level) +{ + DECLARE_HYPERCALL; + struct xen_hvm_set_isa_irq_level arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_isa_irq_level; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.isa_irq = isa_irq; + arg.level = level; + + if ( (rc = lock_pages(&arg, sizeof(arg))) != 0 ) + { + PERROR("Could not lock memory"); + return rc; + } + + rc = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + return rc; +} + +int xc_hvm_set_pci_link_route( + int xc_handle, domid_t dom, uint8_t link, uint8_t isa_irq) +{ + DECLARE_HYPERCALL; + struct xen_hvm_set_pci_link_route arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_pci_link_route; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.link = link; + arg.isa_irq = isa_irq; + + if ( (rc = lock_pages(&arg, sizeof(arg))) != 0 ) + { + PERROR("Could not lock memory"); + return rc; + } + + rc = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + return rc; +} + +int xc_hvm_track_dirty_vram( + int xc_handle, domid_t dom, + uint64_t first_pfn, uint64_t nr, + unsigned long *dirty_bitmap) +{ + DECLARE_HYPERCALL; + struct xen_hvm_track_dirty_vram arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_track_dirty_vram; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.first_pfn = first_pfn; + arg.nr = nr; + set_xen_guest_handle(arg.dirty_bitmap, (uint8_t *)dirty_bitmap); + + if ( (rc = lock_pages(&arg, sizeof(arg))) != 0 ) + { + PERROR("Could not lock memory"); + return rc; + } + + rc = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + return rc; +} + +int xc_hvm_modified_memory( + int xc_handle, domid_t dom, uint64_t first_pfn, uint64_t nr) +{ + DECLARE_HYPERCALL; + struct xen_hvm_modified_memory arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_modified_memory; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.first_pfn = first_pfn; + arg.nr = nr; + + if ( (rc = lock_pages(&arg, sizeof(arg))) != 0 ) + { + PERROR("Could not lock memory"); + return rc; + } + + rc = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + return rc; +} + +int xc_hvm_set_mem_type( + int xc_handle, domid_t dom, hvmmem_type_t mem_type, uint64_t first_pfn, uint64_t nr) +{ + DECLARE_HYPERCALL; + struct xen_hvm_set_mem_type arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_mem_type; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.hvmmem_type = mem_type; + arg.first_pfn = first_pfn; + arg.nr = nr; + + if ( (rc = lock_pages(&arg, sizeof(arg))) != 0 ) + { + PERROR("Could not lock memory"); + return rc; + } + + rc = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(&arg, sizeof(arg)); + + return rc; +} + + +void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot, + const xen_pfn_t *arr, int num) +{ + xen_pfn_t *pfn; + void *res; + int i; + + pfn = malloc(num * sizeof(*pfn)); + if (!pfn) + return NULL; + memcpy(pfn, arr, num * sizeof(*pfn)); + + res = xc_map_foreign_batch(xc_handle, dom, prot, pfn, num); + if (res) { + for (i = 0; i < num; i++) { + if ((pfn[i] & 0xF0000000UL) == 0xF0000000UL) { + /* + * xc_map_foreign_batch() doesn't give us an error + * code, so we have to make one up. May not be the + * appropriate one. + */ + errno = EINVAL; + munmap(res, num * PAGE_SIZE); + res = NULL; + break; + } + } + } + + free(pfn); + return res; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_netbsd.c b/tools/libxc/xc_netbsd.c new file mode 100644 index 0000000..aab325f --- /dev/null +++ b/tools/libxc/xc_netbsd.c @@ -0,0 +1,274 @@ +/****************************************************************************** + * + * Copyright 2006 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 "xc_private.h" + +#include +#include +#include + +int xc_interface_open(void) +{ + int flags, saved_errno; + int fd = open("/kern/xen/privcmd", O_RDWR); + + if ( fd == -1 ) + { + PERROR("Could not obtain handle on privileged command interface"); + return -1; + } + + /* Although we return the file handle as the 'xc handle' the API + does not specify / guarentee that this integer is in fact + a file handle. Thus we must take responsiblity to ensure + it doesn't propagate (ie leak) outside the process */ + if ( (flags = fcntl(fd, F_GETFD)) < 0 ) + { + PERROR("Could not get file handle flags"); + goto error; + } + flags |= FD_CLOEXEC; + if ( fcntl(fd, F_SETFD, flags) < 0 ) + { + PERROR("Could not set file handle flags"); + goto error; + } + + return fd; + + error: + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; +} + +int xc_interface_close(int xc_handle) +{ + return close(xc_handle); +} + +void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int num) +{ + privcmd_mmapbatch_t ioctlx; + void *addr; + addr = mmap(NULL, num*PAGE_SIZE, prot, MAP_ANON | MAP_SHARED, -1, 0); + if ( addr == MAP_FAILED ) { + perror("xc_map_foreign_batch: mmap failed"); + return NULL; + } + + ioctlx.num=num; + ioctlx.dom=dom; + ioctlx.addr=(unsigned long)addr; + ioctlx.arr=arr; + if ( ioctl(xc_handle, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx) < 0 ) + { + int saved_errno = errno; + perror("xc_map_foreign_batch: ioctl failed"); + (void)munmap(addr, num*PAGE_SIZE); + errno = saved_errno; + return NULL; + } + return addr; + +} + +void *xc_map_foreign_range(int xc_handle, uint32_t dom, + int size, int prot, + unsigned long mfn) +{ + privcmd_mmap_t ioctlx; + privcmd_mmap_entry_t entry; + void *addr; + addr = mmap(NULL, size, prot, MAP_ANON | MAP_SHARED, -1, 0); + if ( addr == MAP_FAILED ) { + perror("xc_map_foreign_range: mmap failed"); + return NULL; + } + + ioctlx.num=1; + ioctlx.dom=dom; + ioctlx.entry=&entry; + entry.va=(unsigned long) addr; + entry.mfn=mfn; + entry.npages=(size+PAGE_SIZE-1)>>PAGE_SHIFT; + if ( ioctl(xc_handle, IOCTL_PRIVCMD_MMAP, &ioctlx) < 0 ) + { + int saved_errno = errno; + perror("xc_map_foreign_range: ioctl failed"); + (void)munmap(addr, size); + errno = saved_errno; + return NULL; + } + return addr; +} + +void *xc_map_foreign_ranges(int xc_handle, uint32_t dom, + size_t size, int prot, size_t chunksize, + privcmd_mmap_entry_t entries[], int nentries) +{ + privcmd_mmap_t ioctlx; + int i, rc; + void *addr; + + addr = mmap(NULL, size, prot, MAP_ANON | MAP_SHARED, -1, 0); + if (addr == MAP_FAILED) + goto mmap_failed; + + for (i = 0; i < nentries; i++) { + entries[i].va = (uintptr_t)addr + (i * chunksize); + entries[i].npages = chunksize >> PAGE_SHIFT; + } + + ioctlx.num = nentries; + ioctlx.dom = dom; + ioctlx.entry = entries; + + rc = ioctl(xc_handle, IOCTL_PRIVCMD_MMAP, &ioctlx); + if (rc) + goto ioctl_failed; + + return addr; + +ioctl_failed: + rc = munmap(addr, size); + if (rc == -1) + ERROR("%s: error in error path\n", __FUNCTION__); + +mmap_failed: + return NULL; +} + + +static int do_privcmd(int xc_handle, unsigned int cmd, unsigned long data) +{ + int err = ioctl(xc_handle, cmd, data); + if (err == 0) + return 0; + else + return -errno; +} + +int do_xen_hypercall(int xc_handle, privcmd_hypercall_t *hypercall) +{ + int error = do_privcmd(xc_handle, + IOCTL_PRIVCMD_HYPERCALL, + (unsigned long)hypercall); + if (error) + return error; + else + return (hypercall->retval); +} + +#define EVTCHN_DEV_NAME "/dev/xenevt" + +int xc_evtchn_open(void) +{ + return open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR); +} + +int xc_evtchn_close(int xce_handle) +{ + return close(xce_handle); +} + +int xc_evtchn_fd(int xce_handle) +{ + return xce_handle; +} + +int xc_evtchn_notify(int xce_handle, evtchn_port_t port) +{ + struct ioctl_evtchn_notify notify; + + notify.port = port; + + return ioctl(xce_handle, IOCTL_EVTCHN_NOTIFY, ¬ify); +} + +evtchn_port_or_error_t +xc_evtchn_bind_interdomain(int xce_handle, int domid, + evtchn_port_t remote_port) +{ + struct ioctl_evtchn_bind_interdomain bind; + int ret; + + bind.remote_domain = domid; + bind.remote_port = remote_port; + + ret = ioctl(xce_handle, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind); + if (ret == 0) + return bind.port; + else + return -1; +} + +int xc_evtchn_unbind(int xce_handle, evtchn_port_t port) +{ + struct ioctl_evtchn_unbind unbind; + + unbind.port = port; + + return ioctl(xce_handle, IOCTL_EVTCHN_UNBIND, &unbind); +} + +evtchn_port_or_error_t +xc_evtchn_bind_virq(int xce_handle, unsigned int virq) +{ + struct ioctl_evtchn_bind_virq bind; + int err; + + bind.virq = virq; + + err = ioctl(xce_handle, IOCTL_EVTCHN_BIND_VIRQ, &bind); + if (err) + return -1; + else + return bind.port; +} + +evtchn_port_or_error_t +xc_evtchn_pending(int xce_handle) +{ + evtchn_port_t port; + + if ( read_exact(xce_handle, (char *)&port, sizeof(port)) == -1 ) + return -1; + + return port; +} + +int xc_evtchn_unmask(int xce_handle, evtchn_port_t port) +{ + return write_exact(xce_handle, (char *)&port, sizeof(port)); +} + +/* Optionally flush file to disk and discard page cache */ +void discard_file_cache(int fd, int flush) +{ + + if ( flush && (fsync(fd) < 0) ) + { + /*PERROR("Failed to flush file: %s", strerror(errno));*/ + } +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_pagetab.c b/tools/libxc/xc_pagetab.c new file mode 100644 index 0000000..ac8035b --- /dev/null +++ b/tools/libxc/xc_pagetab.c @@ -0,0 +1,192 @@ +/****************************************************************************** + * xc_pagetab.c + * + * Function to translate virtual to physical addresses. + */ +#include "xc_private.h" + +#if defined(__i386__) + +#define L1_PAGETABLE_SHIFT_PAE 12 +#define L2_PAGETABLE_SHIFT_PAE 21 +#define L3_PAGETABLE_SHIFT_PAE 30 + +#define L1_PAGETABLE_SHIFT 12 +#define L2_PAGETABLE_SHIFT 22 + +#define L0_PAGETABLE_MASK_PAE 0x00000ffffffff000ULL +#define L1_PAGETABLE_MASK_PAE 0x1ffULL +#define L2_PAGETABLE_MASK_PAE 0x1ffULL +#define L3_PAGETABLE_MASK_PAE 0x3ULL + +#define L0_PAGETABLE_MASK 0xfffff000ULL +#define L1_PAGETABLE_MASK 0x3ffULL +#define L2_PAGETABLE_MASK 0x3ffULL + +#elif defined(__x86_64__) + +#define L1_PAGETABLE_SHIFT_PAE 12 +#define L2_PAGETABLE_SHIFT_PAE 21 +#define L3_PAGETABLE_SHIFT_PAE 30 +#define L4_PAGETABLE_SHIFT_PAE 39 + +#define L1_PAGETABLE_SHIFT L1_PAGETABLE_SHIFT_PAE +#define L2_PAGETABLE_SHIFT L2_PAGETABLE_SHIFT_PAE + +#define L0_PAGETABLE_MASK_PAE 0x000ffffffffff000ULL +#define L1_PAGETABLE_MASK_PAE 0x1ffULL +#define L2_PAGETABLE_MASK_PAE 0x1ffULL +#define L3_PAGETABLE_MASK_PAE 0x1ffULL +#define L4_PAGETABLE_MASK_PAE 0x1ffULL + +#define L0_PAGETABLE_MASK L0_PAGETABLE_MASK_PAE +#define L1_PAGETABLE_MASK L1_PAGETABLE_MASK_PAE +#define L2_PAGETABLE_MASK L2_PAGETABLE_MASK_PAE + +#endif + +unsigned long xc_translate_foreign_address(int xc_handle, uint32_t dom, + int vcpu, unsigned long long virt ) +{ + vcpu_guest_context_any_t ctx; + unsigned long long cr3; + void *pd, *pt, *pdppage = NULL, *pdp, *pml = NULL; + unsigned long long pde, pte, pdpe, pmle; + unsigned long mfn = 0; +#if defined (__i386__) + static int pt_levels = 0; + + if (pt_levels == 0) { + xen_capabilities_info_t xen_caps = ""; + + if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) + goto out; + if (strstr(xen_caps, "xen-3.0-x86_64")) + pt_levels = 4; + else if (strstr(xen_caps, "xen-3.0-x86_32p")) + pt_levels = 3; + else if (strstr(xen_caps, "xen-3.0-x86_32")) + pt_levels = 2; + else + goto out; + } +#elif defined (__x86_64__) +#define pt_levels 4 +#endif + + if (xc_vcpu_getcontext(xc_handle, dom, vcpu, &ctx) != 0) { + DPRINTF("failed to retreive vcpu context\n"); + goto out; + } + cr3 = ((unsigned long long)xen_cr3_to_pfn(ctx.c.ctrlreg[3])) << PAGE_SHIFT; + + /* Page Map Level 4 */ + +#if defined(__i386__) + pmle = cr3; +#elif defined(__x86_64__) + pml = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, cr3 >> PAGE_SHIFT); + if (pml == NULL) { + DPRINTF("failed to map PML4\n"); + goto out; + } + pmle = *(unsigned long long *)(pml + 8 * ((virt >> L4_PAGETABLE_SHIFT_PAE) & L4_PAGETABLE_MASK_PAE)); + if((pmle & 1) == 0) { + DPRINTF("page entry not present in PML4\n"); + goto out_unmap_pml; + } +#endif + + /* Page Directory Pointer Table */ + + if (pt_levels >= 3) { + pdppage = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pmle >> PAGE_SHIFT); + if (pdppage == NULL) { + DPRINTF("failed to map PDP\n"); + goto out_unmap_pml; + } + if (pt_levels >= 4) + pdp = pdppage; + else + /* PDP is only 32 bit aligned with 3 level pts */ + pdp = pdppage + (pmle & ~(XC_PAGE_MASK | 0x1f)); + + pdpe = *(unsigned long long *)(pdp + 8 * ((virt >> L3_PAGETABLE_SHIFT_PAE) & L3_PAGETABLE_MASK_PAE)); + + if((pdpe & 1) == 0) { + DPRINTF("page entry not present in PDP\n"); + goto out_unmap_pdp; + } + } else { + pdpe = pmle; + } + + /* Page Directory */ + + pd = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pdpe >> PAGE_SHIFT); + if (pd == NULL) { + DPRINTF("failed to map PD\n"); + goto out_unmap_pdp; + } + + if (pt_levels >= 3) + pde = *(unsigned long long *)(pd + 8 * ((virt >> L2_PAGETABLE_SHIFT_PAE) & L2_PAGETABLE_MASK_PAE)); + else + pde = *(unsigned long *)(pd + 4 * ((virt >> L2_PAGETABLE_SHIFT) & L2_PAGETABLE_MASK)); + + if ((pde & 1) == 0) { + DPRINTF("page entry not present in PD\n"); + goto out_unmap_pd; + } + + /* Page Table */ + + if (pde & 0x00000080) { /* 4M page (or 2M in PAE mode) */ + DPRINTF("Cannot currently cope with 2/4M pages\n"); + exit(-1); + } else { /* 4k page */ + pt = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, + pde >> PAGE_SHIFT); + + if (pt == NULL) { + DPRINTF("failed to map PT\n"); + goto out_unmap_pd; + } + + if (pt_levels >= 3) + pte = *(unsigned long long *)(pt + 8 * ((virt >> L1_PAGETABLE_SHIFT_PAE) & L1_PAGETABLE_MASK_PAE)); + else + pte = *(unsigned long *)(pt + 4 * ((virt >> L1_PAGETABLE_SHIFT) & L1_PAGETABLE_MASK)); + + if ((pte & 1) == 0) { + DPRINTF("page entry not present in PT\n"); + goto out_unmap_pt; + } + + if (pt_levels >= 3) + mfn = (pte & L0_PAGETABLE_MASK_PAE) >> PAGE_SHIFT; + else + mfn = (pte & L0_PAGETABLE_MASK) >> PAGE_SHIFT; + } + + out_unmap_pt: + munmap(pt, PAGE_SIZE); + out_unmap_pd: + munmap(pd, PAGE_SIZE); + out_unmap_pdp: + munmap(pdppage, PAGE_SIZE); + out_unmap_pml: + munmap(pml, PAGE_SIZE); + out: + return mfn; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_physdev.c b/tools/libxc/xc_physdev.c new file mode 100644 index 0000000..ec0ebbe --- /dev/null +++ b/tools/libxc/xc_physdev.c @@ -0,0 +1,93 @@ +/****************************************************************************** + * 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) + */ + +#include "xc_private.h" + +int xc_physdev_pci_access_modify(int xc_handle, + uint32_t domid, + int bus, + int dev, + int func, + int enable) +{ + errno = ENOSYS; + return -1; +} + +int xc_physdev_map_pirq(int xc_handle, + int domid, + int index, + int *pirq) +{ + int rc; + struct physdev_map_pirq map; + + if ( !pirq ) + return -EINVAL; + + map.domid = domid; + map.type = MAP_PIRQ_TYPE_GSI; + map.index = index; + map.pirq = *pirq; + + rc = do_physdev_op(xc_handle, PHYSDEVOP_map_pirq, &map); + + if ( !rc ) + *pirq = map.pirq; + + return rc; +} + +int xc_physdev_map_pirq_msi(int xc_handle, + 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 ) + return -EINVAL; + + 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(xc_handle, PHYSDEVOP_map_pirq, &map); + + if ( !rc ) + *pirq = map.pirq; + + return rc; +} + +int xc_physdev_unmap_pirq(int xc_handle, + int domid, + int pirq) +{ + int rc; + struct physdev_unmap_pirq unmap; + + unmap.domid = domid; + unmap.pirq = pirq; + + rc = do_physdev_op(xc_handle, PHYSDEVOP_unmap_pirq, &unmap); + + return rc; +} + diff --git a/tools/libxc/xc_pm.c b/tools/libxc/xc_pm.c new file mode 100644 index 0000000..69f6b6e --- /dev/null +++ b/tools/libxc/xc_pm.c @@ -0,0 +1,170 @@ +/****************************************************************************** + * xc_pm.c - Libxc API for Xen Power Management (Px/Cx/Tx, etc.) statistic + * + * Copyright (c) 2008, Liu Jinsong + * + * 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 "xc_private.h" + +int xc_pm_get_max_px(int xc_handle, 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(xc_handle, &sysctl); + if ( ret ) + return ret; + + *max_px = sysctl.u.get_pmstat.u.getpx.total; + return ret; +} + +int xc_pm_get_pxstat(int xc_handle, int cpuid, struct xc_px_stat *pxpt) +{ + DECLARE_SYSCTL; + int max_px, ret; + + if ( !pxpt || !(pxpt->trans_pt) || !(pxpt->pt) ) + return -EINVAL; + + if ( (ret = xc_pm_get_max_px(xc_handle, cpuid, &max_px)) != 0) + return ret; + + if ( (ret = lock_pages(pxpt->trans_pt, + max_px * max_px * sizeof(uint64_t))) != 0 ) + return ret; + + if ( (ret = lock_pages(pxpt->pt, + max_px * sizeof(struct xc_px_val))) != 0 ) + { + unlock_pages(pxpt->trans_pt, max_px * max_px * sizeof(uint64_t)); + 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, pxpt->trans_pt); + set_xen_guest_handle(sysctl.u.get_pmstat.u.getpx.pt, + (pm_px_val_t *)pxpt->pt); + + ret = xc_sysctl(xc_handle, &sysctl); + if ( ret ) + { + unlock_pages(pxpt->trans_pt, max_px * max_px * sizeof(uint64_t)); + unlock_pages(pxpt->pt, max_px * sizeof(struct xc_px_val)); + 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; + + unlock_pages(pxpt->trans_pt, max_px * max_px * sizeof(uint64_t)); + unlock_pages(pxpt->pt, max_px * sizeof(struct xc_px_val)); + + return ret; +} + +int xc_pm_reset_pxstat(int xc_handle, 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(xc_handle, &sysctl); +} + +int xc_pm_get_max_cx(int xc_handle, 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(xc_handle, &sysctl)) != 0 ) + return ret; + + *max_cx = sysctl.u.get_pmstat.u.getcx.nr; + return ret; +} + +int xc_pm_get_cxstat(int xc_handle, int cpuid, struct xc_cx_stat *cxpt) +{ + DECLARE_SYSCTL; + int max_cx, ret; + + if( !cxpt || !(cxpt->triggers) || !(cxpt->residencies) ) + return -EINVAL; + + if ( (ret = xc_pm_get_max_cx(xc_handle, cpuid, &max_cx)) ) + goto unlock_0; + + if ( (ret = lock_pages(cxpt, sizeof(struct xc_cx_stat))) ) + goto unlock_0; + if ( (ret = lock_pages(cxpt->triggers, max_cx * sizeof(uint64_t))) ) + goto unlock_1; + if ( (ret = lock_pages(cxpt->residencies, max_cx * sizeof(uint64_t))) ) + goto unlock_2; + + sysctl.cmd = XEN_SYSCTL_get_pmstat; + sysctl.u.get_pmstat.type = PMSTAT_get_cxstat; + sysctl.u.get_pmstat.cpuid = cpuid; + set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.triggers, cxpt->triggers); + set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.residencies, + cxpt->residencies); + + if ( (ret = xc_sysctl(xc_handle, &sysctl)) ) + goto unlock_3; + + 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; + +unlock_3: + unlock_pages(cxpt->residencies, max_cx * sizeof(uint64_t)); +unlock_2: + unlock_pages(cxpt->triggers, max_cx * sizeof(uint64_t)); +unlock_1: + unlock_pages(cxpt, sizeof(struct xc_cx_stat)); +unlock_0: + return ret; +} + +int xc_pm_reset_cxstat(int xc_handle, 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(xc_handle, &sysctl); +} diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c new file mode 100644 index 0000000..b37978a --- /dev/null +++ b/tools/libxc/xc_private.c @@ -0,0 +1,638 @@ +/****************************************************************************** + * xc_private.c + * + * Helper functions for the rest of the library. + */ + +#include +#include "xc_private.h" +#include "xg_private.h" +#include +#include + +static pthread_key_t last_error_pkey; +static pthread_once_t last_error_pkey_once = PTHREAD_ONCE_INIT; + +static pthread_key_t errbuf_pkey; +static pthread_once_t errbuf_pkey_once = PTHREAD_ONCE_INIT; + +#if DEBUG +static xc_error_handler error_handler = xc_default_error_handler; +#else +static xc_error_handler error_handler = NULL; +#endif + +void xc_default_error_handler(const xc_error *err) +{ + const char *desc = xc_error_code_to_desc(err->code); + fprintf(stderr, "ERROR %s: %s\n", desc, err->message); +} + +static void +_xc_clean_last_error(void *m) +{ + free(m); + pthread_setspecific(last_error_pkey, NULL); +} + +static void +_xc_init_last_error(void) +{ + pthread_key_create(&last_error_pkey, _xc_clean_last_error); +} + +static xc_error * +_xc_get_last_error(void) +{ + xc_error *last_error; + + pthread_once(&last_error_pkey_once, _xc_init_last_error); + + last_error = pthread_getspecific(last_error_pkey); + if (last_error == NULL) { + last_error = malloc(sizeof(xc_error)); + pthread_setspecific(last_error_pkey, last_error); + xc_clear_last_error(); + } + + return last_error; +} + +const xc_error *xc_get_last_error(void) +{ + return _xc_get_last_error(); +} + +void xc_clear_last_error(void) +{ + xc_error *last_error = _xc_get_last_error(); + last_error->code = XC_ERROR_NONE; + 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"; +} + +xc_error_handler xc_set_error_handler(xc_error_handler handler) +{ + xc_error_handler old = error_handler; + error_handler = handler; + return old; +} + +static void _xc_set_error(int code, const char *msg) +{ + xc_error *last_error = _xc_get_last_error(); + last_error->code = code; + strncpy(last_error->message, msg, XC_MAX_ERROR_MSG_LEN - 1); + last_error->message[XC_MAX_ERROR_MSG_LEN-1] = '\0'; +} + +void xc_set_error(int code, const char *fmt, ...) +{ + int saved_errno = errno; + char msg[XC_MAX_ERROR_MSG_LEN]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, XC_MAX_ERROR_MSG_LEN-1, fmt, args); + msg[XC_MAX_ERROR_MSG_LEN-1] = '\0'; + va_end(args); + + _xc_set_error(code, msg); + + errno = saved_errno; + + if ( error_handler != NULL ) { + xc_error *last_error = _xc_get_last_error(); + error_handler(last_error); + } +} + +int lock_pages(void *addr, size_t len) +{ + int e = 0; +#ifndef __sun__ + void *laddr = (void *)((unsigned long)addr & PAGE_MASK); + size_t llen = (len + ((unsigned long)addr - (unsigned long)laddr) + + PAGE_SIZE - 1) & PAGE_MASK; + e = mlock(laddr, llen); +#endif + return e; +} + +void unlock_pages(void *addr, size_t len) +{ +#ifndef __sun__ + void *laddr = (void *)((unsigned long)addr & PAGE_MASK); + size_t llen = (len + ((unsigned long)addr - (unsigned long)laddr) + + PAGE_SIZE - 1) & PAGE_MASK; + safe_munlock(laddr, llen); +#endif +} + +/* NB: arr must be locked */ +int xc_get_pfn_type_batch(int xc_handle, + uint32_t dom, int num, uint32_t *arr) +{ + DECLARE_DOMCTL; + domctl.cmd = XEN_DOMCTL_getpageframeinfo2; + domctl.domain = (domid_t)dom; + domctl.u.getpageframeinfo2.num = num; + set_xen_guest_handle(domctl.u.getpageframeinfo2.array, arr); + return do_domctl(xc_handle, &domctl); +} + +int xc_mmuext_op( + int xc_handle, + struct mmuext_op *op, + unsigned int nr_ops, + domid_t dom) +{ + DECLARE_HYPERCALL; + long ret = -EINVAL; + + hypercall.op = __HYPERVISOR_mmuext_op; + hypercall.arg[0] = (unsigned long)op; + hypercall.arg[1] = (unsigned long)nr_ops; + hypercall.arg[2] = (unsigned long)0; + hypercall.arg[3] = (unsigned long)dom; + + if ( lock_pages(op, nr_ops*sizeof(*op)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out1; + } + + ret = do_xen_hypercall(xc_handle, &hypercall); + + unlock_pages(op, nr_ops*sizeof(*op)); + + out1: + return ret; +} + +static int flush_mmu_updates(int xc_handle, struct xc_mmu *mmu) +{ + int err = 0; + DECLARE_HYPERCALL; + + if ( mmu->idx == 0 ) + return 0; + + hypercall.op = __HYPERVISOR_mmu_update; + hypercall.arg[0] = (unsigned long)mmu->updates; + hypercall.arg[1] = (unsigned long)mmu->idx; + hypercall.arg[2] = 0; + hypercall.arg[3] = mmu->subject; + + if ( lock_pages(mmu->updates, sizeof(mmu->updates)) != 0 ) + { + PERROR("flush_mmu_updates: mmu updates lock_pages failed"); + err = 1; + goto out; + } + + if ( do_xen_hypercall(xc_handle, &hypercall) < 0 ) + { + ERROR("Failure when submitting mmu updates"); + err = 1; + } + + mmu->idx = 0; + + unlock_pages(mmu->updates, sizeof(mmu->updates)); + + out: + return err; +} + +struct xc_mmu *xc_alloc_mmu_updates(int xc_handle, domid_t dom) +{ + struct xc_mmu *mmu = malloc(sizeof(*mmu)); + if ( mmu == NULL ) + return mmu; + mmu->idx = 0; + mmu->subject = dom; + return mmu; +} + +int xc_add_mmu_update(int xc_handle, 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(xc_handle, mmu); + + return 0; +} + +int xc_flush_mmu_updates(int xc_handle, struct xc_mmu *mmu) +{ + return flush_mmu_updates(xc_handle, mmu); +} + +int xc_memory_op(int xc_handle, + int cmd, + void *arg) +{ + DECLARE_HYPERCALL; + struct xen_memory_reservation *reservation = arg; + struct xen_machphys_mfn_list *xmml = arg; + xen_pfn_t *extent_start; + long ret = -EINVAL; + + hypercall.op = __HYPERVISOR_memory_op; + hypercall.arg[0] = (unsigned long)cmd; + hypercall.arg[1] = (unsigned long)arg; + + switch ( cmd ) + { + case XENMEM_increase_reservation: + case XENMEM_decrease_reservation: + case XENMEM_populate_physmap: + if ( lock_pages(reservation, sizeof(*reservation)) != 0 ) + { + PERROR("Could not lock"); + goto out1; + } + get_xen_guest_handle(extent_start, reservation->extent_start); + if ( (extent_start != NULL) && + (lock_pages(extent_start, + reservation->nr_extents * sizeof(xen_pfn_t)) != 0) ) + { + PERROR("Could not lock"); + unlock_pages(reservation, sizeof(*reservation)); + goto out1; + } + break; + case XENMEM_machphys_mfn_list: + if ( lock_pages(xmml, sizeof(*xmml)) != 0 ) + { + PERROR("Could not lock"); + goto out1; + } + get_xen_guest_handle(extent_start, xmml->extent_start); + if ( lock_pages(extent_start, + xmml->max_extents * sizeof(xen_pfn_t)) != 0 ) + { + PERROR("Could not lock"); + unlock_pages(xmml, sizeof(*xmml)); + goto out1; + } + break; + case XENMEM_add_to_physmap: + if ( lock_pages(arg, sizeof(struct xen_add_to_physmap)) ) + { + PERROR("Could not lock"); + goto out1; + } + break; + case XENMEM_remove_from_physmap: + if ( lock_pages(arg, sizeof(struct xen_remove_from_physmap)) ) + { + PERROR("Could not lock"); + goto out1; + } + break; + case XENMEM_current_reservation: + case XENMEM_maximum_reservation: + case XENMEM_maximum_gpfn: + if ( lock_pages(arg, sizeof(domid_t)) ) + { + PERROR("Could not lock"); + goto out1; + } + break; + } + + ret = do_xen_hypercall(xc_handle, &hypercall); + + switch ( cmd ) + { + case XENMEM_increase_reservation: + case XENMEM_decrease_reservation: + case XENMEM_populate_physmap: + unlock_pages(reservation, sizeof(*reservation)); + get_xen_guest_handle(extent_start, reservation->extent_start); + if ( extent_start != NULL ) + unlock_pages(extent_start, + reservation->nr_extents * sizeof(xen_pfn_t)); + break; + case XENMEM_machphys_mfn_list: + unlock_pages(xmml, sizeof(*xmml)); + get_xen_guest_handle(extent_start, xmml->extent_start); + unlock_pages(extent_start, + xmml->max_extents * sizeof(xen_pfn_t)); + break; + case XENMEM_add_to_physmap: + unlock_pages(arg, sizeof(struct xen_add_to_physmap)); + break; + case XENMEM_remove_from_physmap: + unlock_pages(arg, sizeof(struct xen_remove_from_physmap)); + break; + case XENMEM_current_reservation: + case XENMEM_maximum_reservation: + case XENMEM_maximum_gpfn: + unlock_pages(arg, sizeof(domid_t)); + break; + } + + out1: + return ret; +} + + +long long xc_domain_get_cpu_usage( int xc_handle, 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(xc_handle, &domctl) < 0) ) + { + PERROR("Could not get info on domain"); + return -1; + } + return domctl.u.getvcpuinfo.cpu_time; +} + + +#ifndef __ia64__ +int xc_get_pfn_list(int xc_handle, + uint32_t domid, + uint64_t *pfn_buf, + unsigned long max_pfns) +{ + DECLARE_DOMCTL; + int ret; + 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); + +#ifdef VALGRIND + memset(pfn_buf, 0, max_pfns * sizeof(*pfn_buf)); +#endif + + if ( lock_pages(pfn_buf, max_pfns * sizeof(*pfn_buf)) != 0 ) + { + PERROR("xc_get_pfn_list: pfn_buf lock failed"); + return -1; + } + + ret = do_domctl(xc_handle, &domctl); + + unlock_pages(pfn_buf, max_pfns * sizeof(*pfn_buf)); + + return (ret < 0) ? -1 : domctl.u.getmemlist.num_pfns; +} +#endif + +long xc_get_tot_pages(int xc_handle, uint32_t domid) +{ + DECLARE_DOMCTL; + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)domid; + return (do_domctl(xc_handle, &domctl) < 0) ? + -1 : domctl.u.getdomaininfo.tot_pages; +} + +int xc_copy_to_domain_page(int xc_handle, + uint32_t domid, + unsigned long dst_pfn, + const char *src_page) +{ + void *vaddr = xc_map_foreign_range( + xc_handle, domid, PAGE_SIZE, PROT_WRITE, dst_pfn); + if ( vaddr == NULL ) + return -1; + memcpy(vaddr, src_page, PAGE_SIZE); + munmap(vaddr, PAGE_SIZE); + return 0; +} + +int xc_clear_domain_page(int xc_handle, + uint32_t domid, + unsigned long dst_pfn) +{ + void *vaddr = xc_map_foreign_range( + xc_handle, domid, PAGE_SIZE, PROT_WRITE, dst_pfn); + if ( vaddr == NULL ) + return -1; + memset(vaddr, 0, PAGE_SIZE); + munmap(vaddr, PAGE_SIZE); + return 0; +} + +int xc_domctl(int xc_handle, struct xen_domctl *domctl) +{ + return do_domctl(xc_handle, domctl); +} + +int xc_sysctl(int xc_handle, struct xen_sysctl *sysctl) +{ + return do_sysctl(xc_handle, sysctl); +} + +int xc_version(int xc_handle, int cmd, void *arg) +{ + int rc, argsize = 0; + + switch ( cmd ) + { + case XENVER_extraversion: + argsize = sizeof(xen_extraversion_t); + break; + case XENVER_compile_info: + argsize = sizeof(xen_compile_info_t); + break; + case XENVER_capabilities: + argsize = sizeof(xen_capabilities_info_t); + break; + case XENVER_changeset: + argsize = sizeof(xen_changeset_info_t); + break; + case XENVER_platform_parameters: + argsize = sizeof(xen_platform_parameters_t); + break; + } + + if ( (argsize != 0) && (lock_pages(arg, argsize) != 0) ) + { + PERROR("Could not lock memory for version hypercall"); + return -ENOMEM; + } + +#ifdef VALGRIND + if (argsize != 0) + memset(arg, 0, argsize); +#endif + + rc = do_xen_version(xc_handle, cmd, arg); + + if ( argsize != 0 ) + unlock_pages(arg, argsize); + + return rc; +} + +unsigned long xc_make_page_below_4G( + int xc_handle, uint32_t domid, unsigned long mfn) +{ + xen_pfn_t old_mfn = mfn; + xen_pfn_t new_mfn; + + if ( xc_domain_memory_decrease_reservation( + xc_handle, domid, 1, 0, &old_mfn) != 0 ) + { + DPRINTF("xc_make_page_below_4G decrease failed. mfn=%lx\n",mfn); + return 0; + } + + if ( xc_domain_memory_increase_reservation( + xc_handle, 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); +} + +char *safe_strerror(int errcode) +{ +#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); + 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 ) + 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; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h new file mode 100644 index 0000000..6e49b74 --- /dev/null +++ b/tools/libxc/xc_private.h @@ -0,0 +1,218 @@ + +#ifndef XC_PRIVATE_H +#define XC_PRIVATE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenctrl.h" + +#include + +/* valgrind cannot see when a hypercall has filled in some values. For this + reason, we must zero the privcmd_hypercall_t or domctl/sysctl instance + before a call, if using valgrind. */ +#ifdef VALGRIND +#define DECLARE_HYPERCALL privcmd_hypercall_t hypercall = { 0 } +#define DECLARE_DOMCTL struct xen_domctl domctl = { 0 } +#define DECLARE_SYSCTL struct xen_sysctl sysctl = { 0 } +#define DECLARE_PHYSDEV_OP struct physdev_op physdev_op = { 0 } +#else +#define DECLARE_HYPERCALL privcmd_hypercall_t hypercall +#define DECLARE_DOMCTL struct xen_domctl domctl +#define DECLARE_SYSCTL struct xen_sysctl sysctl +#define DECLARE_PHYSDEV_OP struct physdev_op physdev_op +#endif + +#undef PAGE_SHIFT +#undef PAGE_SIZE +#undef PAGE_MASK +#define PAGE_SHIFT XC_PAGE_SHIFT +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#define DEBUG 1 +#define INFO 1 +#define PROGRESS 0 + +/* +** 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) + +#if INFO +#define IPRINTF(_f, _a...) printf(_f , ## _a) +#else +#define IPRINTF(_f, _a...) ((void)0) +#endif + +#if DEBUG +#define DPRINTF(_f, _a...) fprintf(stderr, _f , ## _a) +#else +#define DPRINTF(_f, _a...) ((void)0) +#endif + +#if PROGRESS +#define PPRINTF(_f, _a...) fprintf(stderr, _f , ## _a) +#else +#define PPRINTF(_f, _a...) +#endif + +char *safe_strerror(int errcode); +void xc_set_error(int code, const char *fmt, ...); + +#define ERROR(_m, _a...) xc_set_error(XC_INTERNAL_ERROR, _m , ## _a ) +#define PERROR(_m, _a...) xc_set_error(XC_INTERNAL_ERROR, _m " (%d = %s)", \ + ## _a , errno, safe_strerror(errno)) + +int lock_pages(void *addr, size_t len); +void unlock_pages(void *addr, size_t len); + +static inline void safe_munlock(const void *addr, size_t len) +{ + int saved_errno = errno; + (void)munlock(addr, len); + errno = saved_errno; +} + +int do_xen_hypercall(int xc_handle, privcmd_hypercall_t *hypercall); + +static inline int do_xen_version(int xc_handle, int cmd, void *dest) +{ + DECLARE_HYPERCALL; + + hypercall.op = __HYPERVISOR_xen_version; + hypercall.arg[0] = (unsigned long) cmd; + hypercall.arg[1] = (unsigned long) dest; + + return do_xen_hypercall(xc_handle, &hypercall); +} + +static inline int do_physdev_op(int xc_handle, int cmd, void *op) +{ + int ret = -1; + + DECLARE_HYPERCALL; + hypercall.op = __HYPERVISOR_physdev_op; + hypercall.arg[0] = (unsigned long) cmd; + hypercall.arg[1] = (unsigned long) op; + + if ( lock_pages(op, sizeof(*op)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out1; + } + + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 ) + { + if ( errno == EACCES ) + DPRINTF("physdev operation failed -- need to" + " rebuild the user-space tool set?\n"); + } + + unlock_pages(op, sizeof(*op)); + +out1: + return ret; +} + +static inline int do_domctl(int xc_handle, struct xen_domctl *domctl) +{ + int ret = -1; + DECLARE_HYPERCALL; + + domctl->interface_version = XEN_DOMCTL_INTERFACE_VERSION; + + hypercall.op = __HYPERVISOR_domctl; + hypercall.arg[0] = (unsigned long)domctl; + + if ( lock_pages(domctl, sizeof(*domctl)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out1; + } + + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 ) + { + if ( errno == EACCES ) + DPRINTF("domctl operation failed -- need to" + " rebuild the user-space tool set?\n"); + } + + unlock_pages(domctl, sizeof(*domctl)); + + out1: + return ret; +} + +static inline int do_sysctl(int xc_handle, struct xen_sysctl *sysctl) +{ + int ret = -1; + DECLARE_HYPERCALL; + + sysctl->interface_version = XEN_SYSCTL_INTERFACE_VERSION; + + hypercall.op = __HYPERVISOR_sysctl; + hypercall.arg[0] = (unsigned long)sysctl; + + if ( lock_pages(sysctl, sizeof(*sysctl)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out1; + } + + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 ) + { + if ( errno == EACCES ) + DPRINTF("sysctl operation failed -- need to" + " rebuild the user-space tool set?\n"); + } + + unlock_pages(sysctl, sizeof(*sysctl)); + + out1: + return ret; +} + +void *xc_map_foreign_ranges(int xc_handle, uint32_t dom, + size_t size, int prot, size_t chunksize, + privcmd_mmap_entry_t entries[], int nentries); + +void *map_domain_va_core(unsigned long domfd, int cpu, void *guest_va, + vcpu_guest_context_any_t *ctxt); +int xc_waitdomain_core(int xc_handle, int domain, int *status, + int options, vcpu_guest_context_any_t *ctxt); + +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(int fd, int flush); + +#define MAX_MMU_UPDATES 1024 +struct xc_mmu { + mmu_update_t updates[MAX_MMU_UPDATES]; + int idx; + domid_t subject; +}; +/* Structure returned by xc_alloc_mmu_updates must be free()'ed by caller. */ +struct xc_mmu *xc_alloc_mmu_updates(int xc_handle, domid_t dom); +int xc_add_mmu_update(int xc_handle, struct xc_mmu *mmu, + unsigned long long ptr, unsigned long long val); +int xc_flush_mmu_updates(int xc_handle, struct xc_mmu *mmu); + +/* Return 0 on success; -1 on error. */ +int read_exact(int fd, void *data, size_t size); +int write_exact(int fd, const void *data, size_t size); + +#endif /* __XC_PRIVATE_H__ */ diff --git a/tools/libxc/xc_ptrace.c b/tools/libxc/xc_ptrace.c new file mode 100644 index 0000000..fa6c3a0 --- /dev/null +++ b/tools/libxc/xc_ptrace.c @@ -0,0 +1,633 @@ +#include +#include +#include + +#include "xc_private.h" +#include "xg_private.h" +#include "xc_ptrace.h" + +#ifdef DEBUG +static char *ptrace_names[] = { + "PTRACE_TRACEME", + "PTRACE_PEEKTEXT", + "PTRACE_PEEKDATA", + "PTRACE_PEEKUSER", + "PTRACE_POKETEXT", + "PTRACE_POKEDATA", + "PTRACE_POKEUSER", + "PTRACE_CONT", + "PTRACE_KILL", + "PTRACE_SINGLESTEP", + "PTRACE_INVALID", + "PTRACE_INVALID", + "PTRACE_GETREGS", + "PTRACE_SETREGS", + "PTRACE_GETFPREGS", + "PTRACE_SETFPREGS", + "PTRACE_ATTACH", + "PTRACE_DETACH", + "PTRACE_GETFPXREGS", + "PTRACE_SETFPXREGS", + "PTRACE_INVALID", + "PTRACE_INVALID", + "PTRACE_INVALID", + "PTRACE_INVALID", + "PTRACE_SYSCALL", +}; +#endif + +static int current_domid = -1; +static int current_isfile; +static int current_is_hvm; + +static uint64_t online_cpumap; +static uint64_t regs_valid; +static vcpu_guest_context_any_t ctxt[MAX_VIRT_CPUS]; + +extern int ffsll(long long int); +#define FOREACH_CPU(cpumap, i) for ( cpumap = online_cpumap; (i = ffsll(cpumap)); cpumap &= ~(1 << (index - 1)) ) + +static int +fetch_regs(int xc_handle, int cpu, int *online) +{ + xc_vcpuinfo_t info; + int retval = 0; + + if (online) + *online = 0; + if ( !(regs_valid & (1 << cpu)) ) + { + retval = xc_vcpu_getcontext(xc_handle, current_domid, + cpu, &ctxt[cpu]); + if ( retval ) + goto done; + regs_valid |= (1 << cpu); + + } + if ( online == NULL ) + goto done; + + retval = xc_vcpu_getinfo(xc_handle, current_domid, cpu, &info); + *online = info.online; + + done: + return retval; +} + +static struct thr_ev_handlers { + thr_ev_handler_t td_create; + thr_ev_handler_t td_death; +} handlers; + +void +xc_register_event_handler(thr_ev_handler_t h, + td_event_e e) +{ + switch (e) { + case TD_CREATE: + handlers.td_create = h; + break; + case TD_DEATH: + handlers.td_death = h; + break; + default: + abort(); /* XXX */ + } +} + +static inline int +paging_enabled(vcpu_guest_context_any_t *v) +{ + unsigned long cr0 = v->c.ctrlreg[0]; + return (cr0 & X86_CR0_PE) && (cr0 & X86_CR0_PG); +} + +/* + * Fetch registers for all online cpus and set the cpumap + * to indicate which cpus are online + * + */ + +static int +get_online_cpumap(int xc_handle, struct xen_domctl_getdomaininfo *d, + uint64_t *cpumap) +{ + int i, online; + + *cpumap = 0; + for (i = 0; i <= d->max_vcpu_id; i++) { + fetch_regs(xc_handle, i, &online); + if (online) + *cpumap |= (1 << i); + } + + return (*cpumap == 0) ? -1 : 0; +} + +/* + * Notify GDB of any vcpus that have come online or gone offline + * update online_cpumap + * + */ + +static void +online_vcpus_changed(uint64_t cpumap) +{ + uint64_t changed_cpumap = cpumap ^ online_cpumap; + int index; + + while ( (index = ffsll(changed_cpumap)) ) { + if ( cpumap & (1 << (index - 1)) ) + { + if (handlers.td_create) handlers.td_create(index - 1); + } else { + IPRINTF("thread death: %d\n", index - 1); + if (handlers.td_death) handlers.td_death(index - 1); + } + changed_cpumap &= ~(1 << (index - 1)); + } + online_cpumap = cpumap; + +} + +/* --------------------- */ +/* XXX application state */ +static long nr_pages = 0; +static uint64_t *page_array = NULL; + +static uint64_t to_ma(int cpu, uint64_t maddr) +{ + return maddr; +} + +static void * +map_domain_va_32( + int xc_handle, + int cpu, + void *guest_va, + int perm) +{ + unsigned long l2e, l1e, l1p, p, va = (unsigned long)guest_va; + uint32_t *l2, *l1; + static void *v[MAX_VIRT_CPUS]; + + l2 = xc_map_foreign_range( + xc_handle, current_domid, PAGE_SIZE, PROT_READ, + xen_cr3_to_pfn(ctxt[cpu].c.ctrlreg[3])); + if ( l2 == NULL ) + return NULL; + + l2e = l2[l2_table_offset_i386(va)]; + munmap(l2, PAGE_SIZE); + if ( !(l2e & _PAGE_PRESENT) ) + return NULL; + l1p = to_ma(cpu, l2e); + l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l1p >> PAGE_SHIFT); + if ( l1 == NULL ) + return NULL; + + l1e = l1[l1_table_offset_i386(va)]; + munmap(l1, PAGE_SIZE); + if ( !(l1e & _PAGE_PRESENT) ) + return NULL; + p = to_ma(cpu, l1e); + if ( v[cpu] != NULL ) + munmap(v[cpu], PAGE_SIZE); + v[cpu] = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p >> PAGE_SHIFT); + if ( v[cpu] == NULL ) + return NULL; + + return (void *)((unsigned long)v[cpu] | (va & (PAGE_SIZE - 1))); +} + + +static void * +map_domain_va_pae( + int xc_handle, + int cpu, + void *guest_va, + int perm) +{ + uint64_t l3e, l2e, l1e, l2p, l1p, p; + unsigned long va = (unsigned long)guest_va; + uint64_t *l3, *l2, *l1; + static void *v[MAX_VIRT_CPUS]; + + l3 = xc_map_foreign_range( + xc_handle, current_domid, PAGE_SIZE, PROT_READ, + xen_cr3_to_pfn(ctxt[cpu].c.ctrlreg[3])); + if ( l3 == NULL ) + return NULL; + + l3e = l3[l3_table_offset_pae(va)]; + munmap(l3, PAGE_SIZE); + if ( !(l3e & _PAGE_PRESENT) ) + return NULL; + l2p = to_ma(cpu, l3e); + l2 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l2p >> PAGE_SHIFT); + if ( l2 == NULL ) + return NULL; + + l2e = l2[l2_table_offset_pae(va)]; + munmap(l2, PAGE_SIZE); + if ( !(l2e & _PAGE_PRESENT) ) + return NULL; + l1p = to_ma(cpu, l2e); + l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l1p >> PAGE_SHIFT); + if ( l1 == NULL ) + return NULL; + + l1e = l1[l1_table_offset_pae(va)]; + munmap(l1, PAGE_SIZE); + if ( !(l1e & _PAGE_PRESENT) ) + return NULL; + p = to_ma(cpu, l1e); + if ( v[cpu] != NULL ) + munmap(v[cpu], PAGE_SIZE); + v[cpu] = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p >> PAGE_SHIFT); + if ( v[cpu] == NULL ) + return NULL; + + return (void *)((unsigned long)v[cpu] | (va & (PAGE_SIZE - 1))); +} + +#ifdef __x86_64__ +static void * +map_domain_va_64( + int xc_handle, + int cpu, + void *guest_va, + int perm) +{ + unsigned long l4e, l3e, l2e, l1e, l3p, l2p, l1p, p, va = (unsigned long)guest_va; + uint64_t *l4, *l3, *l2, *l1; + static void *v[MAX_VIRT_CPUS]; + + if ((ctxt[cpu].c.ctrlreg[4] & 0x20) == 0 ) /* legacy ia32 mode */ + return map_domain_va_32(xc_handle, cpu, guest_va, perm); + + l4 = xc_map_foreign_range( + xc_handle, current_domid, PAGE_SIZE, PROT_READ, + xen_cr3_to_pfn(ctxt[cpu].c.ctrlreg[3])); + if ( l4 == NULL ) + return NULL; + + l4e = l4[l4_table_offset(va)]; + munmap(l4, PAGE_SIZE); + if ( !(l4e & _PAGE_PRESENT) ) + return NULL; + l3p = to_ma(cpu, l4e); + l3 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l3p >> PAGE_SHIFT); + if ( l3 == NULL ) + return NULL; + + l3e = l3[l3_table_offset(va)]; + munmap(l3, PAGE_SIZE); + if ( !(l3e & _PAGE_PRESENT) ) + return NULL; + l2p = to_ma(cpu, l3e); + l2 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l2p >> PAGE_SHIFT); + if ( l2 == NULL ) + return NULL; + + l2e = l2[l2_table_offset(va)]; + munmap(l2, PAGE_SIZE); + if ( !(l2e & _PAGE_PRESENT) ) + return NULL; + l1p = to_ma(cpu, l2e); + if (l2e & 0x80) { /* 2M pages */ + p = to_ma(cpu, l1p + (l1_table_offset(va) << PAGE_SHIFT)); + } else { /* 4K pages */ + l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l1p >> PAGE_SHIFT); + if ( l1 == NULL ) + return NULL; + + l1e = l1[l1_table_offset(va)]; + munmap(l1, PAGE_SIZE); + if ( !(l1e & _PAGE_PRESENT) ) + return NULL; + p = to_ma(cpu, l1e); + } + if ( v[cpu] != NULL ) + munmap(v[cpu], PAGE_SIZE); + v[cpu] = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p >> PAGE_SHIFT); + if ( v[cpu] == NULL ) + return NULL; + + return (void *)((unsigned long)v[cpu] | (va & (PAGE_SIZE - 1))); +} +#endif + +static void * +map_domain_va( + int xc_handle, + int cpu, + void *guest_va, + int perm) +{ + unsigned long va = (unsigned long) guest_va; + long npgs = xc_get_tot_pages(xc_handle, current_domid); + static enum { MODE_UNKNOWN, MODE_64, MODE_32, MODE_PAE } mode; + + if ( mode == MODE_UNKNOWN ) + { + xen_capabilities_info_t caps; + (void)xc_version(xc_handle, XENVER_capabilities, caps); + if ( strstr(caps, "-x86_64") ) + mode = MODE_64; + else if ( strstr(caps, "-x86_32p") ) + mode = MODE_PAE; + else if ( strstr(caps, "-x86_32") ) + mode = MODE_32; + } + + if ( nr_pages != npgs ) + { + if ( nr_pages > 0 ) + free(page_array); + nr_pages = npgs; + if ( (page_array = malloc(nr_pages * sizeof(*page_array))) == NULL ) + { + IPRINTF("Could not allocate memory\n"); + return NULL; + } + if ( xc_get_pfn_list(xc_handle, current_domid, + page_array, nr_pages) != nr_pages ) + { + IPRINTF("Could not get the page frame list\n"); + return NULL; + } + } + + if (fetch_regs(xc_handle, cpu, NULL)) + return NULL; + + if (!paging_enabled(&ctxt[cpu])) { + static void * v; + uint64_t page; + + if ( v != NULL ) + munmap(v, PAGE_SIZE); + + page = to_ma(cpu, va); + + v = xc_map_foreign_range( xc_handle, current_domid, PAGE_SIZE, + perm, page >> PAGE_SHIFT); + + if ( v == NULL ) + return NULL; + + return (void *)(((unsigned long)v) | (va & BSD_PAGE_MASK)); + } +#ifdef __x86_64__ + if ( mode == MODE_64 ) + return map_domain_va_64(xc_handle, cpu, guest_va, perm); +#endif + if ( mode == MODE_PAE ) + return map_domain_va_pae(xc_handle, cpu, guest_va, perm); + /* else ( mode == MODE_32 ) */ + return map_domain_va_32(xc_handle, cpu, guest_va, perm); +} + +int control_c_pressed_flag = 0; + +static int +__xc_waitdomain( + int xc_handle, + int domain, + int *status, + int options) +{ + DECLARE_DOMCTL; + int retval; + struct timespec ts; + uint64_t cpumap; + + ts.tv_sec = 0; + ts.tv_nsec = 10*1000*1000; + + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = domain; + + retry: + retval = do_domctl(xc_handle, &domctl); + if ( retval || (domctl.domain != domain) ) + { + IPRINTF("getdomaininfo failed\n"); + goto done; + } + *status = domctl.u.getdomaininfo.flags; + + if ( options & WNOHANG ) + goto done; + + if (control_c_pressed_flag) { + xc_domain_pause(xc_handle, domain); + control_c_pressed_flag = 0; + goto done; + } + + if ( !(domctl.u.getdomaininfo.flags & XEN_DOMINF_paused) ) + { + nanosleep(&ts,NULL); + goto retry; + } + done: + if (get_online_cpumap(xc_handle, &domctl.u.getdomaininfo, &cpumap)) + IPRINTF("get_online_cpumap failed\n"); + if (online_cpumap != cpumap) + online_vcpus_changed(cpumap); + return retval; + +} + + +long +xc_ptrace( + int xc_handle, + enum __ptrace_request request, + uint32_t domid_tid, + long eaddr, + long edata) +{ + DECLARE_DOMCTL; + struct gdb_regs pt; + long retval = 0; + unsigned long *guest_va; + uint64_t cpumap; + int cpu, index; + void *addr = (char *)eaddr; + void *data = (char *)edata; + + cpu = (request != PTRACE_ATTACH) ? domid_tid : 0; + + switch ( request ) + { + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + if (current_isfile) + guest_va = (unsigned long *)map_domain_va_core( + current_domid, cpu, addr, ctxt); + else + guest_va = (unsigned long *)map_domain_va( + xc_handle, cpu, addr, PROT_READ); + if ( guest_va == NULL ) + goto out_error; + retval = *guest_va; + break; + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + /* XXX assume that all CPUs have the same address space */ + if (current_isfile) + guest_va = (unsigned long *)map_domain_va_core( + current_domid, cpu, addr, ctxt); + else + guest_va = (unsigned long *)map_domain_va( + xc_handle, cpu, addr, PROT_READ|PROT_WRITE); + if ( guest_va == NULL ) + goto out_error; + *guest_va = (unsigned long)data; + break; + + case PTRACE_GETREGS: + if (!current_isfile && fetch_regs(xc_handle, cpu, NULL)) + goto out_error; + SET_PT_REGS(pt, ctxt[cpu].c.user_regs); + memcpy(data, &pt, sizeof(struct gdb_regs)); + break; + + case PTRACE_GETFPREGS: + if (!current_isfile && fetch_regs(xc_handle, cpu, NULL)) + goto out_error; + memcpy(data, &ctxt[cpu].c.fpu_ctxt, sizeof (elf_fpregset_t)); + break; + + case PTRACE_GETFPXREGS: + if (!current_isfile && fetch_regs(xc_handle, cpu, NULL)) + goto out_error; + memcpy(data, &ctxt[cpu].c.fpu_ctxt, sizeof(ctxt[cpu].c.fpu_ctxt)); + break; + + case PTRACE_SETREGS: + if (current_isfile) + goto out_unsupported; /* XXX not yet supported */ + SET_XC_REGS(((struct gdb_regs *)data), ctxt[cpu].c.user_regs); + if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu, + &ctxt[cpu]))) + goto out_error_domctl; + break; + + case PTRACE_SINGLESTEP: + if (current_isfile) + goto out_unsupported; /* XXX not yet supported */ + /* XXX we can still have problems if the user switches threads + * during single-stepping - but that just seems retarded + */ + ctxt[cpu].c.user_regs.eflags |= PSL_T; + if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu, + &ctxt[cpu]))) + goto out_error_domctl; + /* FALLTHROUGH */ + + case PTRACE_CONT: + case PTRACE_DETACH: + if (current_isfile) + goto out_unsupported; /* XXX not yet supported */ + if ( request != PTRACE_SINGLESTEP ) + { + FOREACH_CPU(cpumap, index) { + cpu = index - 1; + if (fetch_regs(xc_handle, cpu, NULL)) + goto out_error; + /* Clear trace flag */ + if ( ctxt[cpu].c.user_regs.eflags & PSL_T ) + { + ctxt[cpu].c.user_regs.eflags &= ~PSL_T; + if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, + cpu, &ctxt[cpu]))) + goto out_error_domctl; + } + } + } + if ( request == PTRACE_DETACH ) + { + if ((retval = xc_domain_setdebugging(xc_handle, current_domid, 0))) + goto out_error_domctl; + } + regs_valid = 0; + if ((retval = xc_domain_unpause(xc_handle, current_domid > 0 ? + current_domid : -current_domid))) + goto out_error_domctl; + break; + + case PTRACE_ATTACH: + current_domid = domid_tid; + current_isfile = (int)edata; + if (current_isfile) + break; + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = current_domid; + retval = do_domctl(xc_handle, &domctl); + if ( retval || (domctl.domain != current_domid) ) + goto out_error_domctl; + if ( domctl.u.getdomaininfo.flags & XEN_DOMINF_paused ) + IPRINTF("domain currently paused\n"); + else if ((retval = xc_domain_pause(xc_handle, current_domid))) + goto out_error_domctl; + current_is_hvm = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_hvm_guest); + if ((retval = xc_domain_setdebugging(xc_handle, current_domid, 1))) + goto out_error_domctl; + + if (get_online_cpumap(xc_handle, &domctl.u.getdomaininfo, &cpumap)) + IPRINTF("get_online_cpumap failed\n"); + if (online_cpumap != cpumap) + online_vcpus_changed(cpumap); + break; + + case PTRACE_TRACEME: + IPRINTF("PTRACE_TRACEME is an invalid request under Xen\n"); + goto out_error; + + default: + goto out_unsupported; /* XXX not yet supported */ + } + + return retval; + + out_error_domctl: + perror("domctl failed"); + out_error: + errno = EINVAL; + return retval; + + out_unsupported: +#ifdef DEBUG + IPRINTF("unsupported xc_ptrace request %s\n", ptrace_names[request]); +#endif + errno = ENOSYS; + return -1; + +} + +int +xc_waitdomain( + int xc_handle, + int domain, + int *status, + int options) +{ + if (current_isfile) + return xc_waitdomain_core(xc_handle, domain, status, options, ctxt); + return __xc_waitdomain(xc_handle, domain, status, options); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_ptrace.h b/tools/libxc/xc_ptrace.h new file mode 100644 index 0000000..baaadb5 --- /dev/null +++ b/tools/libxc/xc_ptrace.h @@ -0,0 +1,160 @@ +#ifndef XC_PTRACE_ +#define XC_PTRACE_ + +#define X86_CR0_PE 0x00000001 /* Enable Protected Mode (RW) */ +#define X86_CR0_PG 0x80000000 /* Paging (RW) */ +#define BSD_PAGE_MASK (PAGE_SIZE-1) +#define PSL_T 0x00000100 /* trace enable bit */ + +#ifdef __x86_64__ +struct gdb_regs +{ + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long rbp; + unsigned long rbx; + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rax; + unsigned long rcx; + unsigned long rdx; + unsigned long rsi; + unsigned long rdi; + unsigned long orig_rax; + unsigned long rip; + unsigned long xcs; + unsigned long rflags; + unsigned long rsp; + unsigned long xss; + unsigned long fs_base; + unsigned long gs_base; + unsigned long xds; + unsigned long xes; + unsigned long xfs; + unsigned long xgs; +}; + +#define SET_PT_REGS(pt, xc) \ +{ \ + pt.r8 = xc.r8; \ + pt.r9 = xc.r9; \ + pt.r10 = xc.r10; \ + pt.r11 = xc.r11; \ + pt.r12 = xc.r12; \ + pt.r13 = xc.r13; \ + pt.r14 = xc.r14; \ + pt.r15 = xc.r15; \ + pt.rbx = xc.rbx; \ + pt.rcx = xc.rcx; \ + pt.rdx = xc.rdx; \ + pt.rsi = xc.rsi; \ + pt.rdi = xc.rdi; \ + pt.rbp = xc.rbp; \ + pt.rax = xc.rax; \ + pt.rip = xc.rip; \ + pt.xcs = xc.cs; \ + pt.rflags = xc.rflags; \ + pt.rsp = xc.rsp; \ + pt.xss = xc.ss; \ + pt.xes = xc.es; \ + pt.xds = xc.ds; \ + pt.xfs = xc.fs; \ + pt.xgs = xc.gs; \ +} + +#define SET_XC_REGS(pt, xc) \ +{ \ + xc.r8 = pt->r8; \ + xc.r9 = pt->r9; \ + xc.r10 = pt->r10; \ + xc.r11 = pt->r11; \ + xc.r12 = pt->r12; \ + xc.r13 = pt->r13; \ + xc.r14 = pt->r14; \ + xc.r15 = pt->r15; \ + xc.rbx = pt->rbx; \ + xc.rcx = pt->rcx; \ + xc.rdx = pt->rdx; \ + xc.rsi = pt->rsi; \ + xc.rdi = pt->rdi; \ + xc.rbp = pt->rbp; \ + xc.rax = pt->rax; \ + xc.rip = pt->rip; \ + xc.cs = pt->xcs; \ + xc.rflags = pt->rflags & 0xffffffff; \ + xc.rsp = pt->rsp; \ + xc.ss = pt->xss; \ + xc.es = pt->xes; \ + xc.ds = pt->xds; \ + xc.fs = pt->xfs; \ + xc.gs = pt->xgs; \ +} + +#elif __i386__ + +struct gdb_regs { + long ebx; /* 0 */ + long ecx; /* 4 */ + long edx; /* 8 */ + long esi; /* 12 */ + long edi; /* 16 */ + long ebp; /* 20 */ + long eax; /* 24 */ + int xds; /* 28 */ + int xes; /* 32 */ + int xfs; /* 36 */ + int xgs; /* 40 */ + long orig_eax; /* 44 */ + long eip; /* 48 */ + int xcs; /* 52 */ + long eflags; /* 56 */ + long esp; /* 60 */ + int xss; /* 64 */ +}; + +#define SET_PT_REGS(pt, xc) \ +{ \ + pt.ebx = xc.ebx; \ + pt.ecx = xc.ecx; \ + pt.edx = xc.edx; \ + pt.esi = xc.esi; \ + pt.edi = xc.edi; \ + pt.ebp = xc.ebp; \ + pt.eax = xc.eax; \ + pt.eip = xc.eip; \ + pt.xcs = xc.cs; \ + pt.eflags = xc.eflags; \ + pt.esp = xc.esp; \ + pt.xss = xc.ss; \ + pt.xes = xc.es; \ + pt.xds = xc.ds; \ + pt.xfs = xc.fs; \ + pt.xgs = xc.gs; \ +} + +#define SET_XC_REGS(pt, xc) \ +{ \ + xc.ebx = pt->ebx; \ + xc.ecx = pt->ecx; \ + xc.edx = pt->edx; \ + xc.esi = pt->esi; \ + xc.edi = pt->edi; \ + xc.ebp = pt->ebp; \ + xc.eax = pt->eax; \ + xc.eip = pt->eip; \ + xc.cs = pt->xcs; \ + xc.eflags = pt->eflags; \ + xc.esp = pt->esp; \ + xc.ss = pt->xss; \ + xc.es = pt->xes; \ + xc.ds = pt->xds; \ + xc.fs = pt->xfs; \ + xc.gs = pt->xgs; \ +} +#endif + +#endif /* XC_PTRACE */ diff --git a/tools/libxc/xc_ptrace_core.c b/tools/libxc/xc_ptrace_core.c new file mode 100644 index 0000000..d4bd6bc --- /dev/null +++ b/tools/libxc/xc_ptrace_core.c @@ -0,0 +1,678 @@ +/* + * New elf format support. + * Copyright (c) 2007 Isaku Yamahata + * VA Linux Systems Japan K.K. + */ + +#include +#include +#include "xc_private.h" +#include "xg_private.h" +#include "xc_ptrace.h" +#include +#include + +/* Leave the code for the old format as is. */ +/* --- compatible layer for old format ------------------------------------- */ +/* XXX application state */ + +static int current_is_hvm_compat = 0; +static long nr_pages_compat = 0; +static unsigned long *p2m_array_compat = NULL; +static unsigned long *m2p_array_compat = NULL; +static unsigned long pages_offset_compat; +static unsigned long cr3_compat[MAX_VIRT_CPUS]; + +/* --------------------- */ + +static unsigned long +map_mtop_offset_compat(unsigned long ma) +{ + return pages_offset_compat + (m2p_array_compat[ma >> PAGE_SHIFT] << PAGE_SHIFT); + return 0; +} + + +static void * +map_domain_va_core_compat(unsigned long domfd, int cpu, void *guest_va, + vcpu_guest_context_t *ctxt) +{ + unsigned long pde, page; + unsigned long va = (unsigned long)guest_va; + void *v; + + static unsigned long cr3_phys[MAX_VIRT_CPUS]; + static unsigned long *cr3_virt[MAX_VIRT_CPUS]; + static unsigned long pde_phys[MAX_VIRT_CPUS]; + static unsigned long *pde_virt[MAX_VIRT_CPUS]; + static unsigned long page_phys[MAX_VIRT_CPUS]; + static unsigned long *page_virt[MAX_VIRT_CPUS]; + + if (cr3_compat[cpu] != cr3_phys[cpu]) + { + cr3_phys[cpu] = cr3_compat[cpu]; + if (cr3_virt[cpu]) + munmap(cr3_virt[cpu], PAGE_SIZE); + v = mmap( + NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, domfd, + map_mtop_offset_compat(xen_cr3_to_pfn(cr3_phys[cpu]))); + if (v == MAP_FAILED) + { + perror("mmap failed"); + return NULL; + } + cr3_virt[cpu] = v; + } + if ((pde = cr3_virt[cpu][l2_table_offset_i386(va)]) == 0) /* logical address */ + return NULL; + if (current_is_hvm_compat) + pde = p2m_array_compat[pde >> PAGE_SHIFT] << PAGE_SHIFT; + if (pde != pde_phys[cpu]) + { + pde_phys[cpu] = pde; + if (pde_virt[cpu]) + munmap(pde_virt[cpu], PAGE_SIZE); + v = mmap( + NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, domfd, + map_mtop_offset_compat(pde_phys[cpu])); + if (v == MAP_FAILED) + return NULL; + pde_virt[cpu] = v; + } + if ((page = pde_virt[cpu][l1_table_offset_i386(va)]) == 0) /* logical address */ + return NULL; + if (current_is_hvm_compat) + page = p2m_array_compat[page >> PAGE_SHIFT] << PAGE_SHIFT; + if (page != page_phys[cpu]) + { + page_phys[cpu] = page; + if (page_virt[cpu]) + munmap(page_virt[cpu], PAGE_SIZE); + v = mmap( + NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, domfd, + map_mtop_offset_compat(page_phys[cpu])); + if (v == MAP_FAILED) + { + IPRINTF("cr3 %lx pde %lx page %lx pti %lx\n", cr3_compat[cpu], pde, page, l1_table_offset_i386(va)); + page_phys[cpu] = 0; + return NULL; + } + page_virt[cpu] = v; + } + return (void *)(((unsigned long)page_virt[cpu]) | (va & BSD_PAGE_MASK)); +} + +static int +xc_waitdomain_core_compat( + int xc_handle, + int domfd, + int *status, + int options, + vcpu_guest_context_t *ctxt) +{ + int nr_vcpus; + int i; + xc_core_header_t header; + + if ( nr_pages_compat == 0 ) + { + if (read(domfd, &header, sizeof(header)) != sizeof(header)) + return -1; + + current_is_hvm_compat = (header.xch_magic == XC_CORE_MAGIC_HVM); + if ( !current_is_hvm_compat && (header.xch_magic != XC_CORE_MAGIC) ) + { + IPRINTF("Magic number missmatch: 0x%08x (file) != " + " 0x%08x (code)\n", header.xch_magic, + XC_CORE_MAGIC); + return -1; + } + + nr_pages_compat = header.xch_nr_pages; + nr_vcpus = header.xch_nr_vcpus; + pages_offset_compat = header.xch_pages_offset; + + if (read(domfd, ctxt, sizeof(vcpu_guest_context_t)*nr_vcpus) != + sizeof(vcpu_guest_context_t)*nr_vcpus) + return -1; + + for (i = 0; i < nr_vcpus; i++) + cr3_compat[i] = ctxt[i].ctrlreg[3]; + + if ((p2m_array_compat = malloc(nr_pages_compat * sizeof(unsigned long))) == NULL) + { + IPRINTF("Could not allocate p2m_array\n"); + return -1; + } + + if (read(domfd, p2m_array_compat, sizeof(unsigned long)*nr_pages_compat) != + sizeof(unsigned long)*nr_pages_compat) + return -1; + + if ((m2p_array_compat = malloc((1<<20) * sizeof(unsigned long))) == NULL) + { + IPRINTF("Could not allocate m2p array\n"); + return -1; + } + bzero(m2p_array_compat, sizeof(unsigned long)* 1 << 20); + + for (i = 0; i < nr_pages_compat; i++) + m2p_array_compat[p2m_array_compat[i]] = i; + } + return 0; +} + + +/* --- new format based on ELF -------------------------------------------- */ +#include "xc_core.h" + +static int +pread_exact(int fd, void* buffer, size_t size, off_t offset) +{ + off_t ret; + unsigned char *buf = buffer; + size_t done = 0; + ret = lseek(fd, offset, SEEK_SET); + if (ret < 0 || ret != offset) + return -1; + + while (done < size) { + ssize_t s = read(fd, buf, size - done); + if (s == -1 && errno == EINTR) + continue; + if (s <= 0) + return -1; + + done += s; + buf += s; + } + return 0; +} + +struct elf_core +{ + int domfd; + Elf64_Ehdr ehdr; + + char* shdr; + + char* shstrtab; + uint64_t shstrtab_size; + + char* note_sec; + uint64_t note_sec_size; +}; + +static int +elf_core_alloc_read_sec_by_index(struct elf_core* ecore, uint16_t index, + char** buf, uint64_t* size); +static int +elf_core_alloc_read_sec_by_name(struct elf_core* ecore, const char* name, + char** buf, uint64_t* size); + +static void +elf_core_free(struct elf_core* ecore) +{ + if (ecore->shdr != NULL) { + free(ecore->shdr); + ecore->shdr = NULL; + } + if (ecore->shstrtab != NULL) { + free(ecore->shstrtab); + ecore->shstrtab = NULL; + } + if (ecore->note_sec != NULL) { + free(ecore->note_sec); + ecore->note_sec = NULL; + } +} + +static int +elf_core_init(struct elf_core* ecore, int domfd) +{ + uint64_t sh_size; + ecore->domfd = domfd; + ecore->shdr = NULL; + ecore->shstrtab = NULL; + ecore->note_sec = NULL; + + if (pread_exact(ecore->domfd, &ecore->ehdr, sizeof(ecore->ehdr), 0) < 0) + goto out; + + /* check elf header */ + if (!IS_ELF(ecore->ehdr) || ecore->ehdr.e_type != ET_CORE) + goto out; + if (ecore->ehdr.e_ident[EI_CLASS] != ELFCLASS64) + goto out; + /* check elf header more: EI_DATA, EI_VERSION, e_machine... */ + + /* read section headers */ + sh_size = ecore->ehdr.e_shentsize * ecore->ehdr.e_shnum; + ecore->shdr = malloc(sh_size); + if (ecore->shdr == NULL) + goto out; + if (pread_exact(ecore->domfd, ecore->shdr, sh_size, + ecore->ehdr.e_shoff) < 0) + goto out; + + /* read shstrtab */ + if (elf_core_alloc_read_sec_by_index(ecore, ecore->ehdr.e_shstrndx, + &ecore->shstrtab, + &ecore->shstrtab_size) < 0) + goto out; + + /* read .note.Xen section */ + if (elf_core_alloc_read_sec_by_name(ecore, XEN_DUMPCORE_SEC_NOTE, + &ecore->note_sec, + &ecore->note_sec_size) < 0) + goto out; + + return 0; +out: + elf_core_free(ecore); + return -1; +} + +static int +elf_core_search_note(struct elf_core* ecore, const char* name, uint32_t type, + void** elfnotep) +{ + const char* note_sec_end = ecore->note_sec + ecore->note_sec_size; + const char* n; + + n = ecore->note_sec; + while (n < note_sec_end) { + const struct elfnote *elfnote = (const struct elfnote *)n; + if (elfnote->namesz == strlen(name) + 1 && + strncmp(elfnote->name, name, elfnote->namesz) == 0 && + elfnote->type == type) { + *elfnotep = (void*)elfnote; + return 0; + } + + n += sizeof(*elfnote) + elfnote->descsz; + } + return -1; +} + +static int +elf_core_alloc_read_sec(struct elf_core* ecore, const Elf64_Shdr* shdr, + char** buf) +{ + int ret; + *buf = malloc(shdr->sh_size); + if (*buf == NULL) + return -1; + ret = pread_exact(ecore->domfd, *buf, shdr->sh_size, shdr->sh_offset); + if (ret < 0) { + free(*buf); + *buf = NULL; + } + return ret; +} + +static Elf64_Shdr* +elf_core_shdr_by_index(struct elf_core* ecore, uint16_t index) +{ + if (index >= ecore->ehdr.e_shnum) + return NULL; + return (Elf64_Shdr*)(ecore->shdr + ecore->ehdr.e_shentsize * index); +} + +static int +elf_core_alloc_read_sec_by_index(struct elf_core* ecore, uint16_t index, + char** buf, uint64_t* size) +{ + Elf64_Shdr* shdr = elf_core_shdr_by_index(ecore, index); + if (shdr == NULL) + return -1; + if (size != NULL) + *size = shdr->sh_size; + return elf_core_alloc_read_sec(ecore, shdr, buf); +} + +static Elf64_Shdr* +elf_core_shdr_by_name(struct elf_core* ecore, const char* name) +{ + const char* s; + for (s = ecore->shdr; + s < ecore->shdr + ecore->ehdr.e_shentsize * ecore->ehdr.e_shnum; + s += ecore->ehdr.e_shentsize) { + Elf64_Shdr* shdr = (Elf64_Shdr*)s; + + if (strncmp(ecore->shstrtab + shdr->sh_name, name, strlen(name)) == 0) + return shdr; + } + + return NULL; +} + +static int +elf_core_read_sec_by_name(struct elf_core* ecore, const char* name, char* buf) +{ + Elf64_Shdr* shdr = elf_core_shdr_by_name(ecore, name); + return pread_exact(ecore->domfd, buf, shdr->sh_size, shdr->sh_offset); + +} + +static int +elf_core_alloc_read_sec_by_name(struct elf_core* ecore, const char* name, + char** buf, uint64_t* size) +{ + Elf64_Shdr* shdr = elf_core_shdr_by_name(ecore, name); + if (shdr == NULL) + return -1; + if (size != NULL) + *size = shdr->sh_size; + return elf_core_alloc_read_sec(ecore, shdr, buf); +} + +/* XXX application state */ +static int current_is_auto_translated_physmap = 0; +static struct xen_dumpcore_p2m* p2m_array = NULL; /* for non auto translated physmap mode */ +static uint64_t p2m_array_size = 0; +static uint64_t* pfn_array = NULL; /* for auto translated physmap mode */ +static uint64_t pfn_array_size = 0; +static long nr_pages = 0; +static uint64_t pages_offset; +static unsigned long cr3[MAX_VIRT_CPUS]; + +static const struct xen_dumpcore_elfnote_format_version_desc +known_format_version[] = +{ + {XEN_DUMPCORE_FORMAT_VERSION((uint64_t)0, (uint64_t)1)}, +}; +#define KNOWN_FORMAT_VERSION_NR \ + (sizeof(known_format_version)/sizeof(known_format_version[0])) + +static unsigned long +map_gmfn_to_offset_elf(unsigned long gmfn) +{ + /* + * linear search + */ + unsigned long i; + if (current_is_auto_translated_physmap) { + if (pfn_array == NULL) + return 0; + for (i = 0; i < pfn_array_size; i++) { + if (pfn_array[i] == gmfn) { + return pages_offset + (i << PAGE_SHIFT); + } + } + } else { + if (p2m_array == NULL) + return 0; + for (i = 0; i < p2m_array_size; i++) { + if (p2m_array[i].gmfn == gmfn) { + return pages_offset + (i << PAGE_SHIFT); + } + } + } + return 0; +} + +static void * +map_domain_va_core_elf(unsigned long domfd, int cpu, void *guest_va, + vcpu_guest_context_t *ctxt) +{ + unsigned long pde, page; + unsigned long va = (unsigned long)guest_va; + unsigned long offset; + void *v; + + static unsigned long cr3_phys[MAX_VIRT_CPUS]; + static unsigned long *cr3_virt[MAX_VIRT_CPUS]; + static unsigned long pde_phys[MAX_VIRT_CPUS]; + static unsigned long *pde_virt[MAX_VIRT_CPUS]; + static unsigned long page_phys[MAX_VIRT_CPUS]; + static unsigned long *page_virt[MAX_VIRT_CPUS]; + + if (cr3[cpu] != cr3_phys[cpu]) + { + if (cr3_virt[cpu]) + { + munmap(cr3_virt[cpu], PAGE_SIZE); + cr3_virt[cpu] = NULL; + cr3_phys[cpu] = 0; + } + offset = map_gmfn_to_offset_elf(xen_cr3_to_pfn(cr3[cpu])); + if (offset == 0) + return NULL; + v = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, domfd, offset); + if (v == MAP_FAILED) + { + perror("mmap failed"); + return NULL; + } + cr3_phys[cpu] = cr3[cpu]; + cr3_virt[cpu] = v; + } + if ((pde = cr3_virt[cpu][l2_table_offset_i386(va)]) == 0) /* logical address */ + return NULL; + if (pde != pde_phys[cpu]) + { + if (pde_virt[cpu]) + { + munmap(pde_virt[cpu], PAGE_SIZE); + pde_virt[cpu] = NULL; + pde_phys[cpu] = 0; + } + offset = map_gmfn_to_offset_elf(pde >> PAGE_SHIFT); + if (offset == 0) + return NULL; + v = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, domfd, offset); + if (v == MAP_FAILED) + return NULL; + pde_phys[cpu] = pde; + pde_virt[cpu] = v; + } + if ((page = pde_virt[cpu][l1_table_offset_i386(va)]) == 0) /* logical address */ + return NULL; + if (page != page_phys[cpu]) + { + if (page_virt[cpu]) + { + munmap(page_virt[cpu], PAGE_SIZE); + page_virt[cpu] = NULL; + page_phys[cpu] = 0; + } + offset = map_gmfn_to_offset_elf(page >> PAGE_SHIFT); + if (offset == 0) + return NULL; + v = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, domfd, offset); + if (v == MAP_FAILED) + { + IPRINTF("cr3 %lx pde %lx page %lx pti %lx\n", + cr3[cpu], pde, page, l1_table_offset_i386(va)); + return NULL; + } + page_phys[cpu] = page; + page_virt[cpu] = v; + } + return (void *)(((unsigned long)page_virt[cpu]) | (va & BSD_PAGE_MASK)); +} + +static int +xc_waitdomain_core_elf( + int xc_handle, + int domfd, + int *status, + int options, + vcpu_guest_context_t *ctxt) +{ + int i; + struct elf_core ecore; + + struct xen_dumpcore_elfnote_none *none; + struct xen_dumpcore_elfnote_header *header; + struct xen_dumpcore_elfnote_xen_version *xen_version; + struct xen_dumpcore_elfnote_format_version *format_version; + + Elf64_Shdr* table_shdr; + Elf64_Shdr* pages_shdr; + + if (elf_core_init(&ecore, domfd) < 0) + goto out; + + /* .note.Xen: none */ + if (elf_core_search_note(&ecore, XEN_DUMPCORE_ELFNOTE_NAME, + XEN_ELFNOTE_DUMPCORE_NONE, (void**)&none) < 0) + goto out; + + /* .note.Xen: header */ + if (elf_core_search_note(&ecore, XEN_DUMPCORE_ELFNOTE_NAME, + XEN_ELFNOTE_DUMPCORE_HEADER, (void**)&header) < 0) + goto out; + if ((header->header.xch_magic != XC_CORE_MAGIC && + header->header.xch_magic != XC_CORE_MAGIC_HVM) || + header->header.xch_nr_vcpus == 0 || + header->header.xch_nr_vcpus >= MAX_VIRT_CPUS || + header->header.xch_nr_pages == 0 || + header->header.xch_page_size != PAGE_SIZE) + goto out; + current_is_auto_translated_physmap = + (header->header.xch_magic == XC_CORE_MAGIC_HVM); + nr_pages = header->header.xch_nr_pages; + + /* .note.Xen: xen_version */ + if (elf_core_search_note(&ecore, XEN_DUMPCORE_ELFNOTE_NAME, + XEN_ELFNOTE_DUMPCORE_XEN_VERSION, + (void**)&xen_version) < 0) + goto out; + if (xen_version->xen_version.pagesize != PAGE_SIZE) + goto out; + + /* .note.Xen: format_version */ + if (elf_core_search_note(&ecore, XEN_DUMPCORE_ELFNOTE_NAME, + XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION, + (void**)&format_version) < 0) + goto out; + for (i = 0; i < KNOWN_FORMAT_VERSION_NR; i++) { + if (format_version->format_version.version == + known_format_version[i].version) + break; + } + if (i == KNOWN_FORMAT_VERSION_NR) { + /* complain if unknown format */ + IPRINTF("warning:unknown format version. %"PRIx64"\n", + format_version->format_version.version); + } + + /* .xen_prstatus: read vcpu_guest_context_t*/ + if (elf_core_read_sec_by_name(&ecore, XEN_DUMPCORE_SEC_PRSTATUS, + (char*)ctxt) < 0) + goto out; + for (i = 0; i < header->header.xch_nr_vcpus; i++) + cr3[i] = ctxt[i].ctrlreg[3]; + + /* read .xen_p2m or .xen_pfn */ + if (current_is_auto_translated_physmap) { + table_shdr = elf_core_shdr_by_name(&ecore, XEN_DUMPCORE_SEC_PFN); + if (table_shdr == NULL) + goto out; + pfn_array_size = table_shdr->sh_size / table_shdr->sh_entsize; + if (pfn_array != NULL) + free(pfn_array); + if (elf_core_alloc_read_sec(&ecore, table_shdr, + (char**)&pfn_array) < 0) + goto out; + if (table_shdr->sh_entsize != sizeof(pfn_array[0])) + goto out; + } else { + table_shdr = elf_core_shdr_by_name(&ecore, XEN_DUMPCORE_SEC_P2M); + if (table_shdr == NULL) + goto out; + p2m_array_size = table_shdr->sh_size / table_shdr->sh_entsize; + if (p2m_array != NULL) + free(p2m_array); + if (elf_core_alloc_read_sec(&ecore, table_shdr, + (char**)&p2m_array) < 0) + goto out; + if (table_shdr->sh_entsize != sizeof(p2m_array[0])) + goto out; + } + if (table_shdr->sh_size / table_shdr->sh_entsize != nr_pages) + goto out; + + /* pages_offset and check the file size */ + pages_shdr = elf_core_shdr_by_name(&ecore, XEN_DUMPCORE_SEC_PAGES); + if (pages_shdr == NULL) + goto out; + pages_offset = pages_shdr->sh_offset; + if ((pages_shdr->sh_size / pages_shdr->sh_entsize) != nr_pages || + pages_shdr->sh_entsize != PAGE_SIZE || + (pages_shdr->sh_addralign % PAGE_SIZE) != 0 || + (pages_offset % PAGE_SIZE) != 0) + goto out; + + elf_core_free(&ecore); + return 0; + +out: + elf_core_free(&ecore); + return -1; +} + +/* --- interface ----------------------------------------------------------- */ + +typedef int (*xc_waitdomain_core_t)(int xc_handle, + int domfd, + int *status, + int options, + vcpu_guest_context_t *ctxt); +typedef void *(*map_domain_va_core_t)(unsigned long domfd, + int cpu, + void *guest_va, + vcpu_guest_context_t *ctxt); +struct xc_core_format_type { + xc_waitdomain_core_t waitdomain_core; + map_domain_va_core_t map_domain_va_core; +}; + +static const struct xc_core_format_type format_type[] = { + {xc_waitdomain_core_elf, map_domain_va_core_elf}, + {xc_waitdomain_core_compat, map_domain_va_core_compat}, +}; +#define NR_FORMAT_TYPE (sizeof(format_type)/sizeof(format_type[0])) + +/* XXX application state */ +static const struct xc_core_format_type* current_format_type = NULL; + +void * +map_domain_va_core(unsigned long domfd, int cpu, void *guest_va, + vcpu_guest_context_any_t *ctxt) +{ + if (current_format_type == NULL) + return NULL; + return (current_format_type->map_domain_va_core)(domfd, cpu, guest_va, + &ctxt->c); +} + +int +xc_waitdomain_core(int xc_handle, int domfd, int *status, int options, + vcpu_guest_context_any_t *ctxt) +{ + int ret; + int i; + + for (i = 0; i < NR_FORMAT_TYPE; i++) { + ret = (format_type[i].waitdomain_core)(xc_handle, domfd, status, + options, &ctxt->c); + if (ret == 0) { + current_format_type = &format_type[i]; + break; + } + } + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xc_resume.c b/tools/libxc/xc_resume.c new file mode 100644 index 0000000..73c2904 --- /dev/null +++ b/tools/libxc/xc_resume.c @@ -0,0 +1,225 @@ +#include "xc_private.h" +#include "xg_private.h" +#include "xg_save_restore.h" + +#if defined(__i386__) || defined(__x86_64__) + +#include +#include +#include + +/* Don't yet support cross-address-size uncooperative resume */ +#define guest_width (sizeof (unsigned long)) + +static int modify_returncode(int xc_handle, uint32_t domid) +{ + vcpu_guest_context_any_t ctxt; + xc_dominfo_t info; + xen_capabilities_info_t caps; + int rc; + + if ( xc_domain_getinfo(xc_handle, domid, 1, &info) != 1 ) + { + PERROR("Could not get domain info"); + return -1; + } + + /* HVM guests without PV drivers do not have a return code to modify. */ + if ( info.hvm ) + { + unsigned long irq = 0; + xc_get_hvm_param(xc_handle, domid, HVM_PARAM_CALLBACK_IRQ, &irq); + if ( !irq ) + return 0; + } + + if ( xc_version(xc_handle, XENVER_capabilities, &caps) != 0 ) + { + PERROR("Could not get Xen capabilities\n"); + return -1; + } + + if ( (rc = xc_vcpu_getcontext(xc_handle, domid, 0, &ctxt)) != 0 ) + return rc; + + if ( !info.hvm ) + ctxt.c.user_regs.eax = 1; + else if ( strstr(caps, "x86_64") ) + ctxt.x64.user_regs.eax = 1; + else + ctxt.x32.user_regs.eax = 1; + + if ( (rc = xc_vcpu_setcontext(xc_handle, domid, 0, &ctxt)) != 0 ) + return rc; + + return 0; +} + +#else + +static int modify_returncode(int xc_handle, uint32_t domid) +{ + return 0; + +} + +#endif + +static int xc_domain_resume_cooperative(int xc_handle, 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(xc_handle, domid)) != 0 ) + return rc; + + domctl.cmd = XEN_DOMCTL_resumedomain; + domctl.domain = domid; + return do_domctl(xc_handle, &domctl); +} + +static int xc_domain_resume_any(int xc_handle, uint32_t domid) +{ + DECLARE_DOMCTL; + xc_dominfo_t info; + int i, rc = -1; +#if defined(__i386__) || defined(__x86_64__) + unsigned long mfn, p2m_size = 0; + 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(xc_handle, 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 ) + { + ERROR("Cannot resume uncooperative HVM guests"); + return rc; + } + + /* Map the shared info frame */ + shinfo = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ, info.shared_info_frame); + if ( shinfo == NULL ) + { + ERROR("Couldn't map shared info"); + goto out; + } + + p2m_size = shinfo->arch.max_pfn; + + p2m_frame_list_list = + xc_map_foreign_range(xc_handle, 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_batch(xc_handle, 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_batch(xc_handle, domid, PROT_READ, + p2m_frame_list, + P2M_FL_ENTRIES); + if ( p2m == NULL ) + { + ERROR("Couldn't map p2m table"); + goto out; + } + + if ( lock_pages(&ctxt, sizeof(ctxt)) ) + { + ERROR("Unable to lock ctxt"); + goto out; + } + + if ( xc_vcpu_getcontext(xc_handle, domid, 0, &ctxt) ) + { + ERROR("Could not get vcpu context"); + goto out; + } + + mfn = ctxt.c.user_regs.edx; + + start_info = xc_map_foreign_range(xc_handle, 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++ ) + xc_vcpu_setcontext(xc_handle, domid, i, NULL); + + /* Ready to resume domain execution now. */ + domctl.cmd = XEN_DOMCTL_resumedomain; + domctl.domain = domid; + rc = do_domctl(xc_handle, &domctl); + +#if defined(__i386__) || defined(__x86_64__) + out: + unlock_pages((void *)&ctxt, sizeof ctxt); + 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. Resume with special return code. + * 2. Reset guest environment so it believes it is resumed in a new + * domain context. + * (2) should be used only for guests which cannot handle the special + * new return code. (1) is always safe (but slower). + */ +int xc_domain_resume(int xc_handle, uint32_t domid, int fast) +{ + return (fast + ? xc_domain_resume_cooperative(xc_handle, domid) + : xc_domain_resume_any(xc_handle, domid)); +} diff --git a/tools/libxc/xc_sedf.c b/tools/libxc/xc_sedf.c new file mode 100644 index 0000000..20cffa5 --- /dev/null +++ b/tools/libxc/xc_sedf.c @@ -0,0 +1,64 @@ +/****************************************************************************** + * xc_sedf.c + * + * API for manipulating parameters of the Simple EDF scheduler. + * + * changes by Stephan Diestelhorst + * based on code + * by Mark Williamson, Copyright (c) 2004 Intel Research Cambridge. + */ + +#include "xc_private.h" + +int xc_sedf_domain_set( + int xc_handle, + uint32_t domid, + uint64_t period, + uint64_t slice, + uint64_t latency, + uint16_t extratime, + uint16_t weight) +{ + DECLARE_DOMCTL; + struct xen_domctl_sched_sedf *p = &domctl.u.scheduler_op.u.sedf; + + domctl.cmd = XEN_DOMCTL_scheduler_op; + domctl.domain = (domid_t)domid; + domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_SEDF; + domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_putinfo; + + p->period = period; + p->slice = slice; + p->latency = latency; + p->extratime = extratime; + p->weight = weight; + return do_domctl(xc_handle, &domctl); +} + +int xc_sedf_domain_get( + int xc_handle, + uint32_t domid, + uint64_t *period, + uint64_t *slice, + uint64_t *latency, + uint16_t *extratime, + uint16_t *weight) +{ + DECLARE_DOMCTL; + int ret; + struct xen_domctl_sched_sedf *p = &domctl.u.scheduler_op.u.sedf; + + domctl.cmd = XEN_DOMCTL_scheduler_op; + domctl.domain = (domid_t)domid; + domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_SEDF; + domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_getinfo; + + ret = do_domctl(xc_handle, &domctl); + + *period = p->period; + *slice = p->slice; + *latency = p->latency; + *extratime = p->extratime; + *weight = p->weight; + return ret; +} diff --git a/tools/libxc/xc_solaris.c b/tools/libxc/xc_solaris.c new file mode 100644 index 0000000..f88a928 --- /dev/null +++ b/tools/libxc/xc_solaris.c @@ -0,0 +1,252 @@ +/****************************************************************************** + * + * Copyright 2006 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 "xc_private.h" + +#include +#include +#include +#include + +int xc_interface_open(void) +{ + int flags, saved_errno; + int fd = open("/dev/xen/privcmd", O_RDWR); + + if ( fd == -1 ) + { + PERROR("Could not obtain handle on privileged command interface"); + return -1; + } + + /* Although we return the file handle as the 'xc handle' the API + does not specify / guarentee that this integer is in fact + a file handle. Thus we must take responsiblity to ensure + it doesn't propagate (ie leak) outside the process */ + if ( (flags = fcntl(fd, F_GETFD)) < 0 ) + { + PERROR("Could not get file handle flags"); + goto error; + } + flags |= FD_CLOEXEC; + if ( fcntl(fd, F_SETFD, flags) < 0 ) + { + PERROR("Could not set file handle flags"); + goto error; + } + + return fd; + + error: + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; +} + +int xc_interface_close(int xc_handle) +{ + return close(xc_handle); +} + +void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int num) +{ + privcmd_mmapbatch_t ioctlx; + void *addr; + addr = mmap(NULL, num*PAGE_SIZE, prot, MAP_SHARED, xc_handle, 0); + if ( addr == MAP_FAILED ) + return NULL; + + ioctlx.num=num; + ioctlx.dom=dom; + ioctlx.addr=(unsigned long)addr; + ioctlx.arr=arr; + if ( ioctl(xc_handle, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx) < 0 ) + { + int saved_errno = errno; + perror("XXXXXXXX"); + (void)munmap(addr, num*PAGE_SIZE); + errno = saved_errno; + return NULL; + } + return addr; + +} + +void *xc_map_foreign_range(int xc_handle, uint32_t dom, + int size, int prot, + unsigned long mfn) +{ + privcmd_mmap_t ioctlx; + privcmd_mmap_entry_t entry; + void *addr; + addr = mmap(NULL, size, prot, MAP_SHARED, xc_handle, 0); + if ( addr == MAP_FAILED ) + return NULL; + + ioctlx.num=1; + ioctlx.dom=dom; + ioctlx.entry=&entry; + entry.va=(unsigned long) addr; + entry.mfn=mfn; + entry.npages=(size+PAGE_SIZE-1)>>PAGE_SHIFT; + if ( ioctl(xc_handle, IOCTL_PRIVCMD_MMAP, &ioctlx) < 0 ) + { + int saved_errno = errno; + (void)munmap(addr, size); + errno = saved_errno; + return NULL; + } + return addr; +} + +void *xc_map_foreign_ranges(int xc_handle, uint32_t dom, + size_t size, int prot, size_t chunksize, + privcmd_mmap_entry_t entries[], int nentries) +{ + privcmd_mmap_t ioctlx; + int i, rc; + void *addr; + + addr = mmap(NULL, size, prot, MAP_SHARED, xc_handle, 0); + if (addr == MAP_FAILED) + goto mmap_failed; + + for (i = 0; i < nentries; i++) { + entries[i].va = (uintptr_t)addr + (i * chunksize); + entries[i].npages = chunksize >> PAGE_SHIFT; + } + + ioctlx.num = nentries; + ioctlx.dom = dom; + ioctlx.entry = entries; + + rc = ioctl(xc_handle, IOCTL_PRIVCMD_MMAP, &ioctlx); + if (rc) + goto ioctl_failed; + +ioctl_failed: + rc = munmap(addr, size); + if (rc == -1) + ERROR("%s: error in error path\n", __FUNCTION__); + +mmap_failed: + return NULL; +} + + +static int do_privcmd(int xc_handle, unsigned int cmd, unsigned long data) +{ + return ioctl(xc_handle, cmd, data); +} + +int do_xen_hypercall(int xc_handle, privcmd_hypercall_t *hypercall) +{ + return do_privcmd(xc_handle, + IOCTL_PRIVCMD_HYPERCALL, + (unsigned long)hypercall); +} + +int xc_evtchn_open(void) +{ + int fd; + + if ( (fd = open("/dev/xen/evtchn", O_RDWR)) == -1 ) + { + PERROR("Could not open event channel interface"); + return -1; + } + + return fd; +} + +int xc_evtchn_close(int xce_handle) +{ + return close(xce_handle); +} + +int xc_evtchn_fd(int xce_handle) +{ + return xce_handle; +} + +int xc_evtchn_notify(int xce_handle, evtchn_port_t port) +{ + struct ioctl_evtchn_notify notify; + + notify.port = port; + + return ioctl(xce_handle, IOCTL_EVTCHN_NOTIFY, ¬ify); +} + +evtchn_port_or_error_t +xc_evtchn_bind_unbound_port(int xce_handle, int domid) +{ + struct ioctl_evtchn_bind_unbound_port bind; + + bind.remote_domain = domid; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_UNBOUND_PORT, &bind); +} + +evtchn_port_or_error_t +xc_evtchn_bind_interdomain(int xce_handle, int domid, + evtchn_port_t remote_port) +{ + struct ioctl_evtchn_bind_interdomain bind; + + bind.remote_domain = domid; + bind.remote_port = remote_port; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind); +} + +evtchn_port_or_error_t +xc_evtchn_bind_virq(int xce_handle, unsigned int virq) +{ + struct ioctl_evtchn_bind_virq bind; + + bind.virq = virq; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_VIRQ, &bind); +} + +int xc_evtchn_unbind(int xce_handle, evtchn_port_t port) +{ + struct ioctl_evtchn_unbind unbind; + + unbind.port = port; + + return ioctl(xce_handle, IOCTL_EVTCHN_UNBIND, &unbind); +} + +evtchn_port_or_error_t +xc_evtchn_pending(int xce_handle) +{ + evtchn_port_t port; + + if ( read_exact(xce_handle, (char *)&port, sizeof(port)) == -1 ) + return -1; + + return port; +} + +int xc_evtchn_unmask(int xce_handle, evtchn_port_t port) +{ + return write_exact(xce_handle, (char *)&port, sizeof(port)); +} + +/* Optionally flush file to disk and discard page cache */ +void discard_file_cache(int fd, int flush) +{ + // TODO: Implement for Solaris! +} diff --git a/tools/libxc/xc_tbuf.c b/tools/libxc/xc_tbuf.c new file mode 100644 index 0000000..3948d0a --- /dev/null +++ b/tools/libxc/xc_tbuf.c @@ -0,0 +1,136 @@ +/****************************************************************************** + * 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 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 "xc_private.h" + +static int tbuf_enable(int xc_handle, 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(xc_handle, &sysctl); +} + +int xc_tbuf_set_size(int xc_handle, 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(xc_handle, &sysctl); +} + +int xc_tbuf_get_size(int xc_handle, unsigned long *size) +{ + 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(xc_handle, &sysctl); + if (rc == 0) + *size = sysctl.u.tbuf_op.size; + return rc; +} + +int xc_tbuf_enable(int xc_handle, 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(xc_handle, pages); + + if ( tbuf_enable(xc_handle, 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(xc_handle, &sysctl); + if ( rc == 0 ) + { + *size = sysctl.u.tbuf_op.size; + *mfn = sysctl.u.tbuf_op.buffer_mfn; + } + + return 0; +} + +int xc_tbuf_disable(int xc_handle) +{ + return tbuf_enable(xc_handle, 0); +} + +int xc_tbuf_set_cpu_mask(int xc_handle, uint32_t mask) +{ + DECLARE_SYSCTL; + int ret = -1; + uint64_t mask64 = mask; + uint8_t bytemap[sizeof(mask64)]; + + sysctl.cmd = XEN_SYSCTL_tbuf_op; + sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; + sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_set_cpu_mask; + + bitmap_64_to_byte(bytemap, &mask64, sizeof (mask64) * 8); + + set_xen_guest_handle(sysctl.u.tbuf_op.cpu_mask.bitmap, bytemap); + sysctl.u.tbuf_op.cpu_mask.nr_cpus = sizeof(bytemap) * 8; + + if ( lock_pages(&bytemap, sizeof(bytemap)) != 0 ) + { + PERROR("Could not lock memory for Xen hypercall"); + goto out; + } + + ret = do_sysctl(xc_handle, &sysctl); + + unlock_pages(&bytemap, sizeof(bytemap)); + + out: + return ret; +} + +int xc_tbuf_set_evt_mask(int xc_handle, 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(xc_handle, &sysctl); +} + diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h new file mode 100644 index 0000000..100749a --- /dev/null +++ b/tools/libxc/xenctrl.h @@ -0,0 +1,1164 @@ +/****************************************************************************** + * xenctrl.h + * + * A library for low-level access to the Xen control interfaces. + * + * Copyright (c) 2003-2004, K A Fraser. + * + * xc_gnttab functions: + * Copyright (c) 2007-2008, D G Murray + */ + +#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 + +#if defined(__i386__) || defined(__x86_64__) +#include +#include +#endif + +#ifdef __ia64__ +#define XC_PAGE_SHIFT 14 +#else +#define XC_PAGE_SHIFT 12 +#endif +#define XC_PAGE_SIZE (1UL << XC_PAGE_SHIFT) +#define XC_PAGE_MASK (~(XC_PAGE_SIZE-1)) + +/* + * DEFINITIONS FOR CPU BARRIERS + */ + +#if defined(__i386__) +#define xen_mb() asm volatile ( "lock; addl $0,0(%%esp)" : : : "memory" ) +#define xen_rmb() asm volatile ( "lock; addl $0,0(%%esp)" : : : "memory" ) +#define xen_wmb() asm volatile ( "" : : : "memory") +#elif defined(__x86_64__) +#define xen_mb() asm volatile ( "mfence" : : : "memory") +#define xen_rmb() asm volatile ( "lfence" : : : "memory") +#define xen_wmb() asm volatile ( "" : : : "memory") +#elif defined(__ia64__) +#define xen_mb() asm volatile ("mf" ::: "memory") +#define xen_rmb() asm volatile ("mf" ::: "memory") +#define xen_wmb() asm volatile ("mf" ::: "memory") +#else +#error "Define barriers" +#endif + +/* + * 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. + * + * 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 or -1 on failure + */ +int xc_interface_open(void); + +/** + * 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. + * + * @parm xc_handle a handle to an open hypervisor interface + * @return 0 on success, -1 otherwise. + */ +int xc_interface_close(int xc_handle); + +/* + * KERNEL INTERFACES + */ + +/* + * Resolve a kernel device name (e.g., "evtchn", "blktap0") into a kernel + * device number. Returns -1 on error (and sets errno). + */ +int xc_find_device_number(const char *name); + +/* + * 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 + +#ifdef __linux__ + +#include +#include + +typedef void (*thr_ev_handler_t)(long); + +void xc_register_event_handler( + thr_ev_handler_t h, + td_event_e e); + +long xc_ptrace( + int xc_handle, + enum __ptrace_request request, + uint32_t domid, + long addr, + long data); + +int xc_waitdomain( + int xc_handle, + int domain, + int *status, + int options); + +#endif /* __linux__ */ + +/* + * 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; + unsigned int shutdown_reason; /* only meaningful if shutdown==1 */ + unsigned long nr_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; +} 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; + +typedef union +{ +#if defined(__i386__) || defined(__x86_64__) + start_info_x86_64_t x64; + start_info_x86_32_t x32; +#endif + start_info_t s; +} start_info_any_t; + + +int xc_domain_create(int xc_handle, + uint32_t ssidref, + xen_domain_handle_t handle, + uint32_t flags, + uint32_t *pdomid); + + +/* 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(int xc_handle, + 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)(void *arg, char *buffer, unsigned int length); + +int xc_domain_dumpcore_via_callback(int xc_handle, + uint32_t domid, + void *arg, + dumpcore_rtn_t dump_rtn); + +/* + * This function sets the maximum number of vcpus that a domain may create. + * + * @parm xc_handle 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(int xc_handle, + 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 xc_handle 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(int xc_handle, + uint32_t domid); +/** + * This function unpauses a domain. The domain should have been previously + * paused. + * + * @parm xc_handle 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(int xc_handle, + 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 xc_handle 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(int xc_handle, + uint32_t domid); + + +/** + * This function resumes a suspended domain. The domain should have + * been previously suspended. + * + * @parm xc_handle 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(int xc_handle, + 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 xc_handle 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(int xc_handle, + uint32_t domid, + int reason); + +int xc_vcpu_setaffinity(int xc_handle, + uint32_t domid, + int vcpu, + uint64_t cpumap); +int xc_vcpu_getaffinity(int xc_handle, + uint32_t domid, + int vcpu, + uint64_t *cpumap); + +/** + * 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 xc_handle 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(int xc_handle, + uint32_t first_domid, + unsigned int max_doms, + xc_dominfo_t *info); + + +/** + * This function will set the execution context for the specified vcpu. + * + * @parm xc_handle 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(int xc_handle, + 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 xc_handle 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(int xc_handle, + uint32_t first_domain, + unsigned int max_domains, + xc_domaininfo_t *info); + +/** + * This function returns information about the context of a hvm domain + * @parm xc_handle 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(int xc_handle, + uint32_t domid, + uint8_t *ctxt_buf, + uint32_t size); + +/** + * This function will set the context for hvm domain + * + * @parm xc_handle 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(int xc_handle, + uint32_t domid, + uint8_t *hvm_ctxt, + uint32_t size); + +/** + * This function returns information about the execution context of a + * particular vcpu of a domain. + * + * @parm xc_handle 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(int xc_handle, + uint32_t domid, + uint32_t vcpu, + vcpu_guest_context_any_t *ctxt); + +typedef xen_domctl_getvcpuinfo_t xc_vcpuinfo_t; +int xc_vcpu_getinfo(int xc_handle, + uint32_t domid, + uint32_t vcpu, + xc_vcpuinfo_t *info); + +long long xc_domain_get_cpu_usage(int xc_handle, + domid_t domid, + int vcpu); + +int xc_domain_sethandle(int xc_handle, uint32_t domid, + xen_domain_handle_t handle); + +typedef xen_domctl_shadow_op_stats_t xc_shadow_op_stats_t; +int xc_shadow_control(int xc_handle, + uint32_t domid, + unsigned int sop, + unsigned long *dirty_bitmap, + unsigned long pages, + unsigned long *mb, + uint32_t mode, + xc_shadow_op_stats_t *stats); + +int xc_sedf_domain_set(int xc_handle, + uint32_t domid, + uint64_t period, uint64_t slice, + uint64_t latency, uint16_t extratime, + uint16_t weight); + +int xc_sedf_domain_get(int xc_handle, + uint32_t domid, + uint64_t* period, uint64_t *slice, + uint64_t *latency, uint16_t *extratime, + uint16_t *weight); + +int xc_sched_credit_domain_set(int xc_handle, + uint32_t domid, + struct xen_domctl_sched_credit *sdom); + +int xc_sched_credit_domain_get(int xc_handle, + uint32_t domid, + struct xen_domctl_sched_credit *sdom); + +/** + * This function sends a trigger to a domain. + * + * @parm xc_handle 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(int xc_handle, + uint32_t domid, + uint32_t trigger, + uint32_t vcpu); + +/** + * This function enables or disable debugging of a domain. + * + * @parm xc_handle 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(int xc_handle, + uint32_t domid, + unsigned int enable); + +/* + * EVENT CHANNEL FUNCTIONS + */ + +/* A port identifier is guaranteed to fit in 31 bits. */ +typedef int 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 xc_handle 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 + */ +evtchn_port_or_error_t +xc_evtchn_alloc_unbound(int xc_handle, + uint32_t dom, + uint32_t remote_dom); + +int xc_evtchn_reset(int xc_handle, + uint32_t dom); + +typedef struct evtchn_status xc_evtchn_status_t; +int xc_evtchn_status(int xc_handle, xc_evtchn_status_t *status); + +/* + * Return a handle to the event channel driver, or -1 on failure, in which case + * errno will be set appropriately. + */ +int xc_evtchn_open(void); + +/* + * Close a handle previously allocated with xc_evtchn_open(). + */ +int xc_evtchn_close(int xce_handle); + +/* + * Return an fd that can be select()ed on for further calls to + * xc_evtchn_pending(). + */ +int xc_evtchn_fd(int xce_handle); + +/* + * Notify the given event channel. Returns -1 on failure, in which case + * errno will be set appropriately. + */ +int xc_evtchn_notify(int xce_handle, evtchn_port_t port); + +/* + * Returns a new event port awaiting interdomain connection from the given + * domain ID, or -1 on failure, in which case errno will be set appropriately. + */ +evtchn_port_or_error_t +xc_evtchn_bind_unbound_port(int xce_handle, int domid); + +/* + * Returns a new event port bound to the remote port for the given domain ID, + * or -1 on failure, in which case errno will be set appropriately. + */ +evtchn_port_or_error_t +xc_evtchn_bind_interdomain(int xce_handle, int domid, + evtchn_port_t remote_port); + +/* + * Bind an event channel to the given VIRQ. Returns the event channel bound to + * the VIRQ, or -1 on failure, in which case errno will be set appropriately. + */ +evtchn_port_or_error_t +xc_evtchn_bind_virq(int xce_handle, unsigned int virq); + +/* + * Unbind the given event channel. Returns -1 on failure, in which case errno + * will be set appropriately. + */ +int xc_evtchn_unbind(int xce_handle, evtchn_port_t port); + +/* + * Return the next event channel to become pending, or -1 on failure, in which + * case errno will be set appropriately. + */ +evtchn_port_or_error_t +xc_evtchn_pending(int xce_handle); + +/* + * Unmask the given event channel. Returns -1 on failure, in which case errno + * will be set appropriately. + */ +int xc_evtchn_unmask(int xce_handle, evtchn_port_t port); + +int xc_physdev_pci_access_modify(int xc_handle, + uint32_t domid, + int bus, + int dev, + int func, + int enable); + +int xc_readconsolering(int xc_handle, + char **pbuffer, + unsigned int *pnr_chars, + int clear, int incremental, uint32_t *pindex); + +int xc_send_debug_keys(int xc_handle, char *keys); + +typedef xen_sysctl_physinfo_t xc_physinfo_t; +typedef uint32_t xc_cpu_to_node_t; +int xc_physinfo(int xc_handle, + xc_physinfo_t *info); + +int xc_sched_id(int xc_handle, + int *sched_id); + +typedef xen_sysctl_cpuinfo_t xc_cpuinfo_t; +int xc_getcpuinfo(int xc_handle, int max_cpus, + xc_cpuinfo_t *info, int *nr_cpus); + +int xc_domain_setmaxmem(int xc_handle, + uint32_t domid, + unsigned int max_memkb); + +int xc_domain_set_memmap_limit(int xc_handle, + uint32_t domid, + unsigned long map_limitkb); + +int xc_domain_set_time_offset(int xc_handle, + uint32_t domid, + int32_t time_offset_seconds); + +int xc_domain_memory_increase_reservation(int xc_handle, + uint32_t domid, + unsigned long nr_extents, + unsigned int extent_order, + unsigned int mem_flags, + xen_pfn_t *extent_start); + +int xc_domain_memory_decrease_reservation(int xc_handle, + uint32_t domid, + unsigned long nr_extents, + unsigned int extent_order, + xen_pfn_t *extent_start); + +int xc_domain_memory_populate_physmap(int xc_handle, + uint32_t domid, + unsigned long nr_extents, + unsigned int extent_order, + unsigned int mem_flags, + xen_pfn_t *extent_start); + +int xc_domain_memory_translate_gpfn_list(int xc_handle, + uint32_t domid, + unsigned long nr_gpfns, + xen_pfn_t *gpfn_list, + xen_pfn_t *mfn_list); + +int xc_domain_ioport_permission(int xc_handle, + uint32_t domid, + uint32_t first_port, + uint32_t nr_ports, + uint32_t allow_access); + +int xc_domain_irq_permission(int xc_handle, + uint32_t domid, + uint8_t pirq, + uint8_t allow_access); + +int xc_domain_iomem_permission(int xc_handle, + uint32_t domid, + unsigned long first_mfn, + unsigned long nr_mfns, + uint8_t allow_access); + +int xc_domain_pin_memory_cacheattr(int xc_handle, + uint32_t domid, + uint64_t start, + uint64_t end, + uint32_t type); + +unsigned long xc_make_page_below_4G(int xc_handle, 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; +/* IMPORTANT: The caller is responsible for mlock()'ing the @desc and @val + arrays. */ +int xc_perfc_control(int xc_handle, + uint32_t op, + xc_perfc_desc_t *desc, + xc_perfc_val_t *val, + int *nbr_desc, + int *nbr_val); + +/** + * 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. + * + * In Linux, the ring queue for the control channel is accessible by mapping + * the shared_info_frame (from xc_domain_getinfo()) + 2048. The structure + * stored there is of type control_if_t. + * + * @parm xc_handle 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(int xc_handle, uint32_t dom, + int size, int prot, + unsigned long mfn ); + +void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot, + const xen_pfn_t *arr, int num ); + +/** + * Like xc_map_foreign_pages(), except it can succeeed partially. + * When a page cannot be mapped, its PFN in @arr is or'ed with + * 0xF0000000 to indicate the error. + */ +void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int num ); + +/** + * Translates a virtual address in the context of a given domain and + * vcpu returning the machine page frame number of the associated + * page. + * + * @parm xc_handle 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(int xc_handle, 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(int xc_handle, uint32_t domid, uint64_t *pfn_buf, + unsigned long max_pfns); + +unsigned long xc_ia64_fpsr_default(void); + +int xc_copy_to_domain_page(int xc_handle, uint32_t domid, + unsigned long dst_pfn, const char *src_page); + +int xc_clear_domain_page(int xc_handle, uint32_t domid, + unsigned long dst_pfn); + +long xc_get_max_pages(int xc_handle, uint32_t domid); + +int xc_mmuext_op(int xc_handle, struct mmuext_op *op, unsigned int nr_ops, + domid_t dom); + +int xc_memory_op(int xc_handle, int cmd, void *arg); + +int xc_get_pfn_type_batch(int xc_handle, uint32_t dom, + int num, uint32_t *arr); + + +/* Get current total pages allocated to a domain. */ +long xc_get_tot_pages(int xc_handle, 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 xc_handle 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(int xc_handle, int min_width, int max_width, int node, + uint64_t *bytes); + +/* + * Trace Buffer Operations + */ + +/** + * xc_tbuf_enable - enable tracing buffers + * + * @parm xc_handle 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(int xc_handle, unsigned long pages, + unsigned long *mfn, unsigned long *size); + +/* + * Disable tracing buffers. + */ +int xc_tbuf_disable(int xc_handle); + +/** + * 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 xc_handle 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(int xc_handle, 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 xc_handle 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(int xc_handle, unsigned long *size); + +int xc_tbuf_set_cpu_mask(int xc_handle, uint32_t mask); + +int xc_tbuf_set_evt_mask(int xc_handle, uint32_t mask); + +int xc_domctl(int xc_handle, struct xen_domctl *domctl); +int xc_sysctl(int xc_handle, struct xen_sysctl *sysctl); + +int xc_version(int xc_handle, int cmd, void *arg); + +int xc_acm_op(int xc_handle, int cmd, void *arg, unsigned long arg_size); + +int xc_flask_op(int xc_handle, flask_op_t *op); + +/* + * Subscribe to state changes in a domain via evtchn. + * Returns -1 on failure, in which case errno will be set appropriately. + */ +int xc_domain_subscribe_for_suspend( + int xc_handle, domid_t domid, evtchn_port_t port); + +/************************** + * GRANT TABLE OPERATIONS * + **************************/ + +/* + * Return a handle to the grant table driver, or -1 on failure, in which case + * errno will be set appropriately. + */ +int xc_gnttab_open(void); + +/* + * Close a handle previously allocated with xc_gnttab_open(). + */ +int xc_gnttab_close(int xcg_handle); + +/* + * Memory maps a grant reference from one domain to a local address range. + * Mappings should be unmapped with xc_gnttab_munmap. Returns NULL on failure. + * + * @parm xcg_handle a handle on an open grant table interface + * @parm domid the domain to map memory from + * @parm ref the grant reference ID to map + * @parm prot same flag as in mmap() + */ +void *xc_gnttab_map_grant_ref(int xcg_handle, + uint32_t domid, + uint32_t ref, + int prot); + +/** + * Memory maps one or more grant references from one or more domains to a + * contiguous local address range. Mappings should be unmapped with + * xc_gnttab_munmap. Returns NULL on failure. + * + * @parm xcg_handle a handle on an open grant table interface + * @parm count the number of grant references to be mapped + * @parm domids an array of @count domain IDs by which the corresponding @refs + * were granted + * @parm refs an array of @count grant references to be mapped + * @parm prot same flag as in mmap() + */ +void *xc_gnttab_map_grant_refs(int xcg_handle, + uint32_t count, + uint32_t *domids, + uint32_t *refs, + int prot); + +/** + * Memory maps one or more grant references from one domain to a + * contiguous local address range. Mappings should be unmapped with + * xc_gnttab_munmap. Returns NULL on failure. + * + * @parm xcg_handle a handle on an open grant table interface + * @parm count the number of grant references to be mapped + * @parm domid the domain to map memory from + * @parm refs an array of @count grant references to be mapped + * @parm prot same flag as in mmap() + */ +void *xc_gnttab_map_domain_grant_refs(int xcg_handle, + uint32_t count, + uint32_t domid, + uint32_t *refs, + int prot); + +/* + * Unmaps the @count pages starting at @start_address, which were mapped by a + * call to xc_gnttab_map_grant_ref or xc_gnttab_map_grant_refs. Returns zero + * on success, otherwise sets errno and returns non-zero. + */ +int xc_gnttab_munmap(int xcg_handle, + void *start_address, + uint32_t count); + +/* + * Sets the maximum number of grants that may be mapped by the given instance + * to @count. + * + * N.B. This function must be called after opening the handle, and before any + * other functions are invoked on it. + * + * N.B. When variable-length grants are mapped, fragmentation may be observed, + * and it may not be possible to satisfy requests up to the maximum number + * of grants. + */ +int xc_gnttab_set_max_grants(int xcg_handle, + uint32_t count); + +int xc_physdev_map_pirq(int xc_handle, + int domid, + int index, + int *pirq); + +int xc_physdev_map_pirq_msi(int xc_handle, + int domid, + int index, + int *pirq, + int devfn, + int bus, + int entry_nr, + uint64_t table_base); + +int xc_physdev_unmap_pirq(int xc_handle, + int domid, + int pirq); + +int xc_hvm_set_pci_intx_level( + int xc_handle, domid_t dom, + uint8_t domain, uint8_t bus, uint8_t device, uint8_t intx, + unsigned int level); +int xc_hvm_set_isa_irq_level( + int xc_handle, domid_t dom, + uint8_t isa_irq, + unsigned int level); + +int xc_hvm_set_pci_link_route( + int xc_handle, domid_t dom, uint8_t link, uint8_t isa_irq); + + +/* + * Track dirty bit changes in the VRAM area + * + * All of this is done atomically: + * - get the dirty bitmap since the last call + * - set up dirty tracking area for period up to the next call + * - clear the dirty tracking area. + * + * Returns -ENODATA and does not fill bitmap if the area has changed since the + * last call. + */ +int xc_hvm_track_dirty_vram( + int xc_handle, domid_t dom, + uint64_t first_pfn, uint64_t nr, + unsigned long *bitmap); + +/* + * Notify that some pages got modified by the Device Model + */ +int xc_hvm_modified_memory( + int xc_handle, domid_t dom, uint64_t first_pfn, uint64_t nr); + +/* + * Set a range of memory to a specific type. + * Allowed types are HVMMEM_ram_rw, HVMMEM_ram_ro, HVMMEM_mmio_dm + */ +int xc_hvm_set_mem_type( + int xc_handle, domid_t dom, hvmmem_type_t memtype, uint64_t first_pfn, uint64_t nr); + + +typedef enum { + XC_ERROR_NONE = 0, + XC_INTERNAL_ERROR = 1, + XC_INVALID_KERNEL = 2, + XC_INVALID_PARAM = 3, + XC_OUT_OF_MEMORY = 4, +} xc_error_code; + +#define XC_MAX_ERROR_MSG_LEN 1024 +typedef struct { + int code; + char message[XC_MAX_ERROR_MSG_LEN]; +} xc_error; + +/* + * Return a pointer to the last error. This pointer and the + * data pointed to are only valid until the next call to + * libxc. + */ +const xc_error *xc_get_last_error(void); + +/* + * Clear the last error + */ +void xc_clear_last_error(void); + +typedef void (*xc_error_handler)(const xc_error *err); + +/* + * The default error handler which prints to stderr + */ +void xc_default_error_handler(const xc_error *err); + +/* + * Convert an error code into a text description + */ +const char *xc_error_code_to_desc(int code); + +/* + * Registers a callback to handle errors + */ +xc_error_handler xc_set_error_handler(xc_error_handler handler); + +int xc_set_hvm_param(int handle, domid_t dom, int param, unsigned long value); +int xc_get_hvm_param(int handle, domid_t dom, int param, unsigned long *value); + +/* IA64 specific, nvram save */ +int xc_ia64_save_to_nvram(int xc_handle, uint32_t dom); + +/* IA64 specific, nvram init */ +int xc_ia64_nvram_init(int xc_handle, char *dom_name, uint32_t dom); + +/* IA64 specific, set guest OS type optimizations */ +int xc_ia64_set_os_type(int xc_handle, char *guest_os_type, uint32_t dom); + +/* HVM guest pass-through */ +int xc_assign_device(int xc_handle, + uint32_t domid, + uint32_t machine_bdf); + +int xc_get_device_group(int xc_handle, + uint32_t domid, + uint32_t machine_bdf, + uint32_t max_sdevs, + uint32_t *num_sdevs, + uint32_t *sdev_array); + +int xc_test_assign_device(int xc_handle, + uint32_t domid, + uint32_t machine_bdf); + +int xc_deassign_device(int xc_handle, + uint32_t domid, + uint32_t machine_bdf); + +int xc_domain_memory_mapping(int xc_handle, + uint32_t domid, + unsigned long first_gfn, + unsigned long first_mfn, + unsigned long nr_mfns, + uint32_t add_mapping); + +int xc_domain_ioport_mapping(int xc_handle, + 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( + int xc_handle, + uint32_t domid, + uint32_t gvec, + uint32_t pirq, + uint32_t gflags); + +int xc_domain_bind_pt_irq(int xc_handle, + 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(int xc_handle, + 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(int xc_handle, + uint32_t domid, + uint8_t machine_irq, + uint8_t bus, + uint8_t device, + uint8_t intx); + +int xc_domain_bind_pt_isa_irq(int xc_handle, + uint32_t domid, + uint8_t machine_irq); + +int xc_domain_set_machine_address_size(int handle, + uint32_t domid, + unsigned int width); +int xc_domain_get_machine_address_size(int handle, + uint32_t domid); + +int xc_domain_suppress_spurious_page_faults(int handle, + uint32_t domid); + +/* Set the target domain */ +int xc_domain_set_target(int xc_handle, + uint32_t domid, + uint32_t target); + +#if defined(__i386__) || defined(__x86_64__) +int xc_cpuid_check(int xc, + const unsigned int *input, + const char **config, + char **config_transformed); +int xc_cpuid_set(int xc, + domid_t domid, + const unsigned int *input, + const char **config, + char **config_transformed); +int xc_cpuid_apply_policy(int xc, + domid_t domid); +void xc_cpuid_to_str(const unsigned int *regs, + char **strs); +#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(int xc_handle, int cpuid, int *max_px); +int xc_pm_get_pxstat(int xc_handle, int cpuid, struct xc_px_stat *pxpt); +int xc_pm_reset_pxstat(int xc_handle, int cpuid); + +struct xc_cx_stat { + uint32_t nr; /* entry nr in triggers & residencies, including 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 */ +}; +typedef struct xc_cx_stat xc_cx_stat_t; + +int xc_pm_get_max_cx(int xc_handle, int cpuid, int *max_cx); +int xc_pm_get_cxstat(int xc_handle, int cpuid, struct xc_cx_stat *cxpt); +int xc_pm_reset_cxstat(int xc_handle, int cpuid); + +int xc_cpu_online(int xc_handle, int cpu); +int xc_cpu_offline(int xc_handle, int cpu); +#endif /* XENCTRL_H */ diff --git a/tools/libxc/xenguest.h b/tools/libxc/xenguest.h new file mode 100644 index 0000000..ba60326 --- /dev/null +++ b/tools/libxc/xenguest.h @@ -0,0 +1,139 @@ +/****************************************************************************** + * xenguest.h + * + * A library for guest domain management in Xen. + * + * Copyright (c) 2003-2004, K A Fraser. + */ + +#ifndef XENGUEST_H +#define XENGUEST_H + +#define XCFLAGS_LIVE 1 +#define XCFLAGS_DEBUG 2 +#define XCFLAGS_HVM 4 +#define XCFLAGS_STDVGA 8 + + +/** + * This function will save a running domain. + * + * @parm xc_handle a handle to an open hypervisor interface + * @parm fd the file descriptor to save a domain to + * @parm dom the id of the domain + * @return 0 on success, -1 on failure + */ +int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, + uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */, + int (*suspend)(void), int hvm, + void *(*init_qemu_maps)(int, unsigned), /* HVM only */ + void (*qemu_flip_buffer)(int, int)); /* HVM only */ + + +/** + * This function will restore a saved domain. + * + * @parm xc_handle 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 + * @return 0 on success, -1 on failure + */ +int xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, + unsigned int store_evtchn, unsigned long *store_mfn, + unsigned int console_evtchn, unsigned long *console_mfn, + unsigned int hvm, unsigned int pae); + +/** + * This function will create a domain for a paravirtualized Linux + * using file names pointing to kernel and ramdisk + * + * @parm xc_handle 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(int xc_handle, + 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); + +/** The same interface, but the dom structure is managed by the caller */ +struct xc_dom_image; +int xc_dom_linux_build(int xc_handle, + struct xc_dom_image *dom, + uint32_t domid, + unsigned int mem_mb, + const char *image_name, + const char *ramdisk_name, + unsigned long flags, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn); + +/** + * This function will create a domain for a paravirtualized Linux + * using buffers for kernel and initrd + * + * @parm xc_handle a handle to an open hypervisor interface + * @parm domid the id of the domain + * @parm mem_mb memory size in megabytes + * @parm image_buffer buffer containing kernel image + * @parm image_size size of the kernel image buffer + * @parm initrd_buffer name of the ramdisk image file + * @parm initrd_size size of the ramdisk buffer + * @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_mem(int xc_handle, + uint32_t domid, + unsigned int mem_mb, + const char *image_buffer, + unsigned long image_size, + const char *initrd_buffer, + unsigned long initrd_size, + 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); + +int xc_hvm_build(int xc_handle, + uint32_t domid, + int memsize, + const char *image_name); + +int xc_hvm_build_mem(int xc_handle, + uint32_t domid, + int memsize, + const char *image_buffer, + unsigned long image_size); + +#endif /* XENGUEST_H */ diff --git a/tools/libxc/xg_private.c b/tools/libxc/xg_private.c new file mode 100644 index 0000000..d762093 --- /dev/null +++ b/tools/libxc/xg_private.c @@ -0,0 +1,210 @@ +/****************************************************************************** + * xg_private.c + * + * Helper functions for the rest of the library. + */ + +#include +#include +#include +#include +#include + +#include "xg_private.h" + +char *xc_read_image(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"); + 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 */ + goto out; + default: + *size += bytes; + break; + } + } +#undef CHUNK + + out: + if ( *size == 0 ) + { + PERROR("Could not read kernel image"); + free(image); + image = NULL; + } + else 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(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]))))); + + bzero(&zStream, 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); + 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( + int xc_handle, unsigned int type, unsigned long mfn, domid_t dom) +{ + struct mmuext_op op; + + op.cmd = type; + op.arg1.mfn = mfn; + + if ( xc_mmuext_op(xc_handle, &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); +} + +__attribute__((weak)) + int xc_hvm_build(int xc_handle, + uint32_t domid, + int memsize, + const char *image_name) +{ + errno = ENOSYS; + return -1; +} + +void *xg_memalign(size_t alignment, size_t size) +{ +#if defined(_POSIX_C_SOURCE) && !defined(__sun__) + int ret; + void *ptr; + ret = posix_memalign(&ptr, alignment, size); + if (ret != 0) + return NULL; + return ptr; +#elif defined(__NetBSD__) || defined(__OpenBSD__) + return valloc(size); +#else + return memalign(alignment, size); +#endif +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxc/xg_private.h b/tools/libxc/xg_private.h new file mode 100644 index 0000000..1e74509 --- /dev/null +++ b/tools/libxc/xg_private.h @@ -0,0 +1,178 @@ +#ifndef XG_PRIVATE_H +#define XG_PRIVATE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenctrl.h" +#include "xenguest.h" +#include "xc_private.h" + +#include +#include + +#ifndef ELFSIZE +#include +#if UINT_MAX == ULONG_MAX +#define ELFSIZE 32 +#else +#define ELFSIZE 64 +#endif +#endif + +char *xc_read_image(const char *filename, unsigned long *size); +char *xc_inflate_buffer(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 L1_PAGETABLE_SHIFT_I386 12 +#define L2_PAGETABLE_SHIFT_I386 22 +#define L1_PAGETABLE_ENTRIES_I386 1024 +#define L2_PAGETABLE_ENTRIES_I386 1024 + +#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 + +#if defined(__i386__) +#define L1_PAGETABLE_SHIFT L1_PAGETABLE_SHIFT_I386 +#define L2_PAGETABLE_SHIFT L2_PAGETABLE_SHIFT_I386 +#define L1_PAGETABLE_ENTRIES L1_PAGETABLE_ENTRIES_I386 +#define L2_PAGETABLE_ENTRIES L2_PAGETABLE_ENTRIES_I386 +#elif defined(__x86_64__) +#define L1_PAGETABLE_SHIFT L1_PAGETABLE_SHIFT_X86_64 +#define L2_PAGETABLE_SHIFT L2_PAGETABLE_SHIFT_X86_64 +#define L3_PAGETABLE_SHIFT L3_PAGETABLE_SHIFT_X86_64 +#define L4_PAGETABLE_SHIFT L4_PAGETABLE_SHIFT_X86_64 +#define L1_PAGETABLE_ENTRIES L1_PAGETABLE_ENTRIES_X86_64 +#define L2_PAGETABLE_ENTRIES L2_PAGETABLE_ENTRIES_X86_64 +#define L3_PAGETABLE_ENTRIES L3_PAGETABLE_ENTRIES_X86_64 +#define L4_PAGETABLE_ENTRIES L4_PAGETABLE_ENTRIES_X86_64 +#endif + +typedef uint32_t l1_pgentry_32_t; +typedef uint32_t l2_pgentry_32_t; +typedef uint64_t l1_pgentry_64_t; +typedef uint64_t l2_pgentry_64_t; +typedef uint64_t l3_pgentry_64_t; +typedef uint64_t l4_pgentry_64_t; + +#if defined(__i386__) +typedef l1_pgentry_32_t l1_pgentry_t; +typedef l2_pgentry_32_t l2_pgentry_t; +#elif defined(__x86_64__) +typedef l1_pgentry_64_t l1_pgentry_t; +typedef l2_pgentry_64_t l2_pgentry_t; +typedef l3_pgentry_64_t l3_pgentry_t; +typedef l4_pgentry_64_t l4_pgentry_t; +#endif + +#define l1_table_offset_i386(_a) \ + (((_a) >> L1_PAGETABLE_SHIFT_I386) & (L1_PAGETABLE_ENTRIES_I386 - 1)) +#define l2_table_offset_i386(_a) \ + (((_a) >> L2_PAGETABLE_SHIFT_I386) & (L2_PAGETABLE_ENTRIES_I386 - 1)) + +#define l1_table_offset_pae(_a) \ + (((_a) >> L1_PAGETABLE_SHIFT_PAE) & (L1_PAGETABLE_ENTRIES_PAE - 1)) +#define l2_table_offset_pae(_a) \ + (((_a) >> L2_PAGETABLE_SHIFT_PAE) & (L2_PAGETABLE_ENTRIES_PAE - 1)) +#define l3_table_offset_pae(_a) \ + (((_a) >> L3_PAGETABLE_SHIFT_PAE) & (L3_PAGETABLE_ENTRIES_PAE - 1)) + +#define l1_table_offset_x86_64(_a) \ + (((_a) >> L1_PAGETABLE_SHIFT_X86_64) & (L1_PAGETABLE_ENTRIES_X86_64 - 1)) +#define l2_table_offset_x86_64(_a) \ + (((_a) >> L2_PAGETABLE_SHIFT_X86_64) & (L2_PAGETABLE_ENTRIES_X86_64 - 1)) +#define l3_table_offset_x86_64(_a) \ + (((_a) >> L3_PAGETABLE_SHIFT_X86_64) & (L3_PAGETABLE_ENTRIES_X86_64 - 1)) +#define l4_table_offset_x86_64(_a) \ + (((_a) >> L4_PAGETABLE_SHIFT_X86_64) & (L4_PAGETABLE_ENTRIES_X86_64 - 1)) + +#if defined(__i386__) +#define l1_table_offset(_a) l1_table_offset_i386(_a) +#define l2_table_offset(_a) l2_table_offset_i386(_a) +#elif defined(__x86_64__) +#define l1_table_offset(_a) l1_table_offset_x86_64(_a) +#define l2_table_offset(_a) l2_table_offset_x86_64(_a) +#define l3_table_offset(_a) l3_table_offset_x86_64(_a) +#define l4_table_offset(_a) l4_table_offset_x86_64(_a) +#endif + +#define PAGE_SHIFT_X86 12 +#define PAGE_SIZE_X86 (1UL << PAGE_SHIFT_X86) +#define PAGE_MASK_X86 (~(PAGE_SIZE_X86-1)) + +#define PAGE_SHIFT_IA64 14 +#define PAGE_SIZE_IA64 (1UL << PAGE_SHIFT_IA64) +#define PAGE_MASK_IA64 (~(PAGE_SIZE_IA64-1)) + +#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) + + +/* 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) */ + +/* Number of xen_pfn_t in a page */ + +#define FPP (PAGE_SIZE/(guest_width)) + +/* Number of entries in the pfn_to_mfn_frame_list_list */ +#define P2M_FLL_ENTRIES (((p2m_size)+(FPP*FPP)-1)/(FPP*FPP)) + +/* Number of entries in the pfn_to_mfn_frame_list */ +#define P2M_FL_ENTRIES (((p2m_size)+FPP-1)/FPP) + +/* Size in bytes of the pfn_to_mfn_frame_list */ +#define P2M_GUEST_FL_SIZE ((P2M_FL_ENTRIES) * (guest_width)) +#define P2M_TOOLS_FL_SIZE ((P2M_FL_ENTRIES) * \ + MAX((sizeof (xen_pfn_t)), guest_width)) + +/* Masks for PTE<->PFN conversions */ +#define MADDR_BITS_X86 ((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) + + +#define PAEKERN_no 0 +#define PAEKERN_yes 1 +#define PAEKERN_extended_cr3 2 +#define PAEKERN_bimodal 3 + +int pin_table(int xc_handle, unsigned int type, unsigned long mfn, + domid_t dom); + +void *xg_memalign(size_t alignment, size_t size); + +#endif /* XG_PRIVATE_H */ diff --git a/tools/libxc/xg_save_restore.h b/tools/libxc/xg_save_restore.h new file mode 100644 index 0000000..5d39982 --- /dev/null +++ b/tools/libxc/xg_save_restore.h @@ -0,0 +1,153 @@ +/* +** xg_save_restore.h +** +** Defintions and utilities for save / restore. +*/ + +#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(int xc_handle, 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; + DECLARE_DOMCTL; + + if (xc_version(xc_handle, XENVER_platform_parameters, &xen_params) != 0) + return 0; + + if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) + return 0; + + *max_mfn = xc_memory_op(xc_handle, XENMEM_maximum_ram_page, NULL); + + *hvirt_start = xen_params.virt_start; + + memset(&domctl, 0, sizeof(domctl)); + domctl.domain = dom; + domctl.cmd = XEN_DOMCTL_get_address_size; + + if ( do_domctl(xc_handle, &domctl) != 0 ) + return 0; + + *guest_width = domctl.u.address_size.size / 8; + + /* 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 if (strstr(xen_caps, "xen-3.0-x86_32")) + *pt_levels = 2; + 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) ((guest_width==8) ? ((_p)->x64._f) : ((_p)->x32._f)) + +#define SET_FIELD(_p, _f, _v) do { \ + if (guest_width == 8) \ + (_p)->x64._f = (_v); \ + else \ + (_p)->x32._f = (_v); \ +} while (0) + +#define UNFOLD_CR3(_c) \ + ((uint64_t)((guest_width == 8) \ + ? ((_c) >> 12) \ + : (((uint32_t)(_c) >> 12) | ((uint32_t)(_c) << 20)))) + +#define FOLD_CR3(_c) \ + ((uint64_t)((guest_width == 8) \ + ? ((uint64_t)(_c)) << 12 \ + : (((uint32_t)(_c) << 12) | ((uint32_t)(_c) >> 20)))) + +#define MEMCPY_FIELD(_d, _s, _f) do { \ + if (guest_width == 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) do { \ + if (guest_width == 8) \ + memset(&(_p)->x64._f[0], (_v), sizeof((_p)->x64._f)); \ + else \ + memset(&(_p)->x32._f[0], (_v), sizeof((_p)->x32._f)); \ +} while (0) + +#ifndef MAX +#define MAX(_a, _b) ((_a) >= (_b) ? (_a) : (_b)) +#endif +#ifndef MIN +#define MIN(_a, _b) ((_a) <= (_b) ? (_a) : (_b)) +#endif diff --git a/tools/libxen/COPYING b/tools/libxen/COPYING new file mode 100644 index 0000000..b124cf5 --- /dev/null +++ b/tools/libxen/COPYING @@ -0,0 +1,510 @@ + + 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.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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +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! + + diff --git a/tools/libxen/Makefile b/tools/libxen/Makefile new file mode 100644 index 0000000..0d1ec77 --- /dev/null +++ b/tools/libxen/Makefile @@ -0,0 +1,74 @@ +# +# Copyright (c) 2006-2007, XenSource 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 1.0 +MINOR = 0 + +CFLAGS += -Iinclude \ + $(shell xml2-config --cflags) \ + $(shell curl-config --cflags) \ + -fPIC + +LDFLAGS += $(shell xml2-config --libs) \ + $(shell curl-config --libs) + +LIBXENAPI_HDRS = $(wildcard include/xen/api/*.h) include/xen/api/xen_all.h +LIBXENAPI_OBJS = $(patsubst %.c, %.o, $(wildcard src/*.c)) + +TEST_PROGRAMS = test/test_bindings test/test_event_handling + +.PHONY: all +all: libxenapi.so libxenapi.a + +libxenapi.so: libxenapi.so.$(MAJOR) + ln -sf $< $@ + +libxenapi.so.$(MAJOR): libxenapi.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libxenapi.so.$(MAJOR).$(MINOR): $(LIBXENAPI_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenapi.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ + +libxenapi.a: $(LIBXENAPI_OBJS) + $(AR) rcs libxenapi.a $^ + +$(TEST_PROGRAMS): test/%: test/%.o libxenapi.so + $(CC) $(LDFLAGS) -o $@ $< -L . -lxenapi + + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/api + $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) + $(INSTALL_PROG) libxenapi.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) + ln -sf libxenapi.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libxenapi.so.$(MAJOR) + ln -sf libxenapi.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libxenapi.so + $(INSTALL_DATA) libxenapi.a $(DESTDIR)$(LIBDIR) + set -e; for i in $(LIBXENAPI_HDRS); do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEDIR)/xen/api; \ + done + + +.PHONY: clean +clean: + rm -f `find -name *.o` + rm -f libxenapi.so* + rm -f libxenapi.a + rm -f $(TEST_PROGRAMS) diff --git a/tools/libxen/Makefile.dist b/tools/libxen/Makefile.dist new file mode 100644 index 0000000..23a4b13 --- /dev/null +++ b/tools/libxen/Makefile.dist @@ -0,0 +1,115 @@ +# +# Copyright (c) 2006-2007, XenSource 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +MAJOR = 1.0 +MINOR = 0 + +CFLAGS = -Iinclude \ + $(shell xml2-config --cflags) \ + $(shell curl-config --cflags) \ + -W -Wall -Wmissing-prototypes -Werror -std=c99 -O2 -fPIC + +LDFLAGS = $(shell xml2-config --libs) \ + $(shell curl-config --libs) + +# -h for Solaris +SONAME_LDFLAG ?= -soname +# -R /usr/sfw/$(LIBDIR) -shared for Solaris +SHLIB_CFLAGS ?= -shared + +# ginstall for Solaris +INSTALL = install +INSTALL_DIR = $(INSTALL) -d -m0755 -p +INSTALL_DATA = $(INSTALL) -m0644 -p + +LIBXENAPI_HDRS = $(wildcard include/*.h) +LIBXENAPI_OBJS = $(patsubst %.c, %.o, $(wildcard src/*.c)) + +TEST_PROGRAMS = test/test_bindings test/test_event_handling + +TARBALL_DEST = libxenapi-$(MAJOR).$(MINOR) + +.PHONY: all +all: $(TEST_PROGRAMS) + +libxenapi.so: libxenapi.so.$(MAJOR) + ln -sf $< $@ + +libxenapi.so.$(MAJOR): libxenapi.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libxenapi.so.$(MAJOR).$(MINOR): $(LIBXENAPI_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenapi.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ + +libxenapi.a: $(LIBXENAPI_OBJS) + $(AR) rcs libxenapi.a $^ + +$(TEST_PROGRAMS): test/%: test/%.o libxenapi.so + $(CC) $(LDFLAGS) -o $@ $< -L . -lxenapi + + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)/xen/api + $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) + $(INSTALL_PROG) libxenapi.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) + ln -sf libxenapi.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libxenapi.so.$(MAJOR) + ln -sf libxenapi.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libxenapi.so + $(INSTALL_DATA) libxenapi.a $(DESTDIR)$(LIBDIR) + set -e; for i in $(LIBXENAPI_HDRS); do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEDIR)/xen/api; \ + done + + +.PHONY: tarball +tarball: $(TARBALL_DEST).tar.bz2 + +$(TARBALL_DEST).tar.bz2: all + rm -Rf $(TARBALL_DEST) + mkdir -p $(TARBALL_DEST)/include/xen/api + mkdir -p $(TARBALL_DEST)/src + mkdir -p $(TARBALL_DEST)/test + cp COPYING $(TARBALL_DEST) + cp Makefile.dist $(TARBALL_DEST)/Makefile + cp Makefile.dist $(TARBALL_DEST)/Makefile.dist + cp README $(TARBALL_DEST) + cp include/*.h $(TARBALL_DEST)/include + cp include/xen/api/*.h $(TARBALL_DEST)/include/xen/api + cp src/*.c $(TARBALL_DEST)/src + cp test/*.c $(TARBALL_DEST)/test + fakeroot chown root:root -R $(TARBALL_DEST) + fakeroot tar cjf $(TARBALL_DEST).tar.bz2 $(TARBALL_DEST) + + +.PHONY: clean +clean: + rm -f `find -name *.o` + rm -f libxenapi.so* + rm -f libxenapi.a + rm -f $(TEST_PROGRAMS) + + +.PHONY: uberheader +uberheader: include/xen/api/xen_all.h +include/xen/api/xen_all.h:: + echo "/* This file is autogenerated */" >$@ + echo "#ifndef XEN_API_XEN_ALL_H" >>$@ + echo "#define XEN_API_XEN_ALL_H" >>$@ + ls include/xen/api/*.h | grep -v xen_all.h | grep -v _decl.h | \ + sed 's,^include/\(.*\)$$,#include <\1>,g' >>$@ + echo "#endif" >>$@ diff --git a/tools/libxen/README b/tools/libxen/README new file mode 100644 index 0000000..2dd79f4 --- /dev/null +++ b/tools/libxen/README @@ -0,0 +1,55 @@ +Xen API C Bindings +================== + +This distribution is the source code to the proposed Xen API C bindings. + +The Xen API project will define an XML-RPC protocol for remote and local +management of Xen-based systems, and a set of bindings for these XML-RPC calls +into a number of languages (this package contains those to the C language). + +The intention is to standardise these XML-RPC calls, and then the Xen project +will guarantee that that wire protocol will be supported for the long term. +The bindings will also be supported in the Xen tree, giving a stable +foundation for Xen management tools and middlewares, in particular the Xen CIM +providers and libvirt. + +THIS IS A WORK IN PROGRESS. The API and bindings are under active design and +development, and this is a snapshot release for developers only. Both the API +and the C bindings are scheduled to be stabilised by the Xen 3.0.4 release +i.e. October 2006 at the earliest. + +These bindings are open-source (LGPL), and will be committed as libraries to +the Xen trees for all to use after the Xen 3.0.3 release. + +We welcome any discussion about this library and the API in general. Please +join the Xen-API mailing list if you are interested in this project. I (Ewan +Mellor) will collate all the feedback from that list and push out new versions +of the document and the bindings as and when. + + +URLs +---- + +Xen-API wiki page: +http://wiki.xensource.com/xenwiki/XenApi + +Xen-API mailing list: + http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-api + + +Usage +----- + +The bindings depend upon libxml2, the XML toolkit from the GNOME project; the +test program depends upon libcurl3 also. On Debian, you need the packages +libxml2-dev and libcurl3-dev. + +To compile, type make. To compile the test also, type make +test/test_bindings, remembering the additional dependency. + +To run the test, do + +LD_LIBRARY_PATH=. ./test/test_bindings + +where is the fragment of the server URL that follows the http://, for +example "localhost:8005/RPC2". diff --git a/tools/libxen/include/xen/api/xen_acmpolicy.h b/tools/libxen/include/xen/api/xen_acmpolicy.h new file mode 100644 index 0000000..43aac58 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_acmpolicy.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2007, IBM Corp. + * Copyright (c) 2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_ACMPOLICY_H +#define XEN_ACMPOLICY_H + +#include "xen_common.h" +#include "xen_string_string_map.h" +#include "xen_xspolicy_decl.h" +#include "xen_vm_decl.h" + +/* + * Data structures. + */ + +typedef struct xen_acmpolicy_record +{ + xen_xspolicy handle; + char *uuid; + char *repr; + xs_instantiationflags flags; + xs_type type; +} xen_acmpolicy_record; + +/** + * Allocate a xen_acmpolicy_record. + */ +extern xen_acmpolicy_record * +xen_acmpolicy_record_alloc(void); + +/** + * Free the given xen_xspolicy_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_acmpolicy_record_free(xen_acmpolicy_record *record); + + +/** + * Data structures for the policy's header + */ +typedef struct xen_acm_header +{ + char *policyname; + char *policyurl; + char *date; + char *reference; + char *namespaceurl; + char *version; +} xen_acm_header; + +extern xen_acm_header * +xen_acm_header_alloc(void); + +extern void +xen_acm_header_free(xen_acm_header *hdr); + +/** + * Get the referenced policy's record. + */ +extern bool +xen_acmpolicy_get_record(xen_session *session, xen_acmpolicy_record **result, + xen_xspolicy xspolicy); + +/** + * Get the header of a policy. + */ +extern bool +xen_acmpolicy_get_header(xen_session *session, xen_acm_header **hdr, + xen_xspolicy xspolicy); + + +/** + * Get the XML representation of the policy. + */ +extern bool +xen_acmpolicy_get_xml(xen_session *session, char **xml, + xen_xspolicy xspolicy); + +/** + * Get the mapping file of the policy. + */ +extern bool +xen_acmpolicy_get_map(xen_session *session, char **map, + xen_xspolicy xspolicy); + +/** + * Get the binary representation (base64-encoded) of the policy. + */ +extern bool +xen_acmpolicy_get_binary(xen_session *session, char **binary, + xen_xspolicy xspolicy); + +/** + * Get the binary representation (base64-encoded) of the currently + * enforced policy. + */ +extern bool +xen_acmpolicy_get_enforced_binary(xen_session *session, char **binary, + xen_xspolicy xspolicy); + +/** + * Get the ACM ssidref of the given VM. + */ +extern bool +xen_acmpolicy_get_VM_ssidref(xen_session *session, int64_t *result, + xen_vm vm); + +/** + * Get the UUID field of the given policy. + */ +extern bool +xen_acmpolicy_get_uuid(xen_session *session, char **result, + xen_xspolicy xspolicy); + +#endif diff --git a/tools/libxen/include/xen/api/xen_all.h b/tools/libxen/include/xen/api/xen_all.h new file mode 100644 index 0000000..4d08226 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_all.h @@ -0,0 +1,40 @@ +/* This file is autogenerated */ +#ifndef XEN_API_XEN_ALL_H +#define XEN_API_XEN_ALL_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif diff --git a/tools/libxen/include/xen/api/xen_common.h b/tools/libxen/include/xen/api/xen_common.h new file mode 100644 index 0000000..ebcebd9 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_common.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2006 XenSource, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_COMMON_H +#define XEN_COMMON_H + + +#include +#include +#include +#include + +#include "xen/api/xen_host_decl.h" + + +typedef bool (*xen_result_func)(const void *data, size_t len, + void *result_handle); + + +/** + * len does not include a terminating \0. + */ +typedef int (*xen_call_func)(const void *, size_t len, void *user_handle, + void *result_handle, + xen_result_func result_func); + + +typedef struct +{ + xen_call_func call_func; + void *handle; + const char *session_id; + bool ok; + char **error_description; + int error_description_count; +} xen_session; + + +typedef struct xen_session_record +{ + char *uuid; + struct xen_host_record_opt *this_host; + char *this_user; + time_t last_active; +} xen_session_record; + + +/** + * Allocate a xen_session_record. + */ +extern xen_session_record * +xen_session_record_alloc(void); + + +/** + * Free the given xen_session_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_session_record_free(xen_session_record *record); + + +struct xen_task_; +typedef struct xen_task_ * xen_task_id; + + +typedef struct +{ + int progress; + long eta; + /* !!! RESULT */ +} xen_task_status; + + +typedef struct +{ + int major; + int minor; + int patch; + char *extraversion; +} xen_version; + + +/** + * Free the given xen_version, and all referenced values. + */ +extern void xen_version_free(xen_version *version); + + +/** + * Return the version of this client-side library. This will be the major, + * minor, and extraversion of the Xen release with which it was released, + * plus the library's own version as the patch. + */ +extern xen_version *xen_get_client_side_version(void); + + +extern bool +xen_uuid_string_to_bytes(char *uuid, char **bytes); + + +extern bool +xen_uuid_bytes_to_string(char *bytes, char **uuid); + + +extern void +xen_uuid_free(char *uuid); + + +extern void +xen_uuid_bytes_free(char *bytes); + + +/** + * Initialise this library. Call this before starting to use this library. + * Note that since this library depends upon libxml2, you should also call + * xmlInitParser as appropriate for your program. + */ +extern +void xen_init(void); + + +/** + * Clear up this library. Call when you have finished using this library. + * Note that since this library depends upon libxml2, you should also call + * xmlCleanupParser as appropriate for your program. + */ +extern +void xen_fini(void); + + +/** + * Log in at the server, and allocate a xen_session to represent this session. + */ +extern xen_session * +xen_session_login_with_password(xen_call_func call_func, void *handle, + const char *uname, const char *pwd); + + +/** + * Log out at the server, and free the xen_session. + */ +extern void +xen_session_logout(xen_session *session); + + +/** + * Clear any error condition recorded on this session. + */ +void +xen_session_clear_error(xen_session *session); + + +/** + * Get the UUID of the second given session. Set *result to point at a + * string, yours to free. + */ +extern bool +xen_session_get_uuid(xen_session *session, char **result, + xen_session *self_session); + + +/** + * Get the this_host field of the second given session. Set *result to be a + * handle to that host. + */ +extern bool +xen_session_get_this_host(xen_session *session, xen_host *result, + xen_session *self_session); + + +/** + * Get the this_user field of the second given session. Set *result to point + * at a string, yours to free. + */ +extern bool +xen_session_get_this_user(xen_session *session, char **result, + xen_session *self_session); + + +/** + * Get the last_active field of the given session, and place it in *result. + */ +extern bool +xen_session_get_last_active(xen_session *session, time_t *result, + xen_session *self_session); + +/** + * Get a record containing the current state of the second given session. + */ +extern bool +xen_session_get_record(xen_session *session, xen_session_record **result, + xen_session *self_session); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_console.h b/tools/libxen/include/xen/api/xen_console.h new file mode 100644 index 0000000..19bfdeb --- /dev/null +++ b/tools/libxen/include/xen/api/xen_console.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CONSOLE_H +#define XEN_CONSOLE_H + +#include +#include +#include +#include +#include + + +/* + * The console class. + * + * A console. + */ + + +/** + * Free the given xen_console. The given handle must have been + * allocated by this library. + */ +extern void +xen_console_free(xen_console console); + + +typedef struct xen_console_set +{ + size_t size; + xen_console *contents[]; +} xen_console_set; + +/** + * Allocate a xen_console_set of the given size. + */ +extern xen_console_set * +xen_console_set_alloc(size_t size); + +/** + * Free the given xen_console_set. The given set must have been + * allocated by this library. + */ +extern void +xen_console_set_free(xen_console_set *set); + + +typedef struct xen_console_record +{ + xen_console handle; + char *uuid; + enum xen_console_protocol protocol; + char *location; + struct xen_vm_record_opt *vm; + xen_string_string_map *other_config; +} xen_console_record; + +/** + * Allocate a xen_console_record. + */ +extern xen_console_record * +xen_console_record_alloc(void); + +/** + * Free the given xen_console_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_console_record_free(xen_console_record *record); + + +typedef struct xen_console_record_opt +{ + bool is_record; + union + { + xen_console handle; + xen_console_record *record; + } u; +} xen_console_record_opt; + +/** + * Allocate a xen_console_record_opt. + */ +extern xen_console_record_opt * +xen_console_record_opt_alloc(void); + +/** + * Free the given xen_console_record_opt, and all referenced values. + * The given record_opt must have been allocated by this library. + */ +extern void +xen_console_record_opt_free(xen_console_record_opt *record_opt); + + +typedef struct xen_console_record_set +{ + size_t size; + xen_console_record *contents[]; +} xen_console_record_set; + +/** + * Allocate a xen_console_record_set of the given size. + */ +extern xen_console_record_set * +xen_console_record_set_alloc(size_t size); + +/** + * Free the given xen_console_record_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_console_record_set_free(xen_console_record_set *set); + + + +typedef struct xen_console_record_opt_set +{ + size_t size; + xen_console_record_opt *contents[]; +} xen_console_record_opt_set; + +/** + * Allocate a xen_console_record_opt_set of the given size. + */ +extern xen_console_record_opt_set * +xen_console_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_console_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_console_record_opt_set_free(xen_console_record_opt_set *set); + + +/** + * Get a record containing the current state of the given console. + */ +extern bool +xen_console_get_record(xen_session *session, xen_console_record **result, xen_console console); + + +/** + * Get a reference to the console instance with the specified UUID. + */ +extern bool +xen_console_get_by_uuid(xen_session *session, xen_console *result, char *uuid); + + +/** + * Create a new console instance, and return its handle. + */ +extern bool +xen_console_create(xen_session *session, xen_console *result, xen_console_record *record); + + +/** + * Destroy the specified console instance. + */ +extern bool +xen_console_destroy(xen_session *session, xen_console console); + + +/** + * Get the uuid field of the given console. + */ +extern bool +xen_console_get_uuid(xen_session *session, char **result, xen_console console); + + +/** + * Get the protocol field of the given console. + */ +extern bool +xen_console_get_protocol(xen_session *session, enum xen_console_protocol *result, xen_console console); + + +/** + * Get the location field of the given console. + */ +extern bool +xen_console_get_location(xen_session *session, char **result, xen_console console); + + +/** + * Get the VM field of the given console. + */ +extern bool +xen_console_get_vm(xen_session *session, xen_vm *result, xen_console console); + + +/** + * Get the other_config field of the given console. + */ +extern bool +xen_console_get_other_config(xen_session *session, xen_string_string_map **result, xen_console console); + + +/** + * Set the other_config field of the given console. + */ +extern bool +xen_console_set_other_config(xen_session *session, xen_console console, xen_string_string_map *other_config); + + +/** + * Add the given key-value pair to the other_config field of the given + * console. + */ +extern bool +xen_console_add_to_other_config(xen_session *session, xen_console console, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * other_config field of the given console. If the key is not in that Map, + * then do nothing. + */ +extern bool +xen_console_remove_from_other_config(xen_session *session, xen_console console, char *key); + + +/** + * Return a list of all the consoles known to the system. + */ +extern bool +xen_console_get_all(xen_session *session, struct xen_console_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_console_decl.h b/tools/libxen/include/xen/api/xen_console_decl.h new file mode 100644 index 0000000..432ff76 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_console_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CONSOLE_DECL_H +#define XEN_CONSOLE_DECL_H + +typedef void *xen_console; + +struct xen_console_set; +struct xen_console_record; +struct xen_console_record_set; +struct xen_console_record_opt; +struct xen_console_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_console_protocol.h b/tools/libxen/include/xen/api/xen_console_protocol.h new file mode 100644 index 0000000..413ad7a --- /dev/null +++ b/tools/libxen/include/xen/api/xen_console_protocol.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CONSOLE_PROTOCOL_H +#define XEN_CONSOLE_PROTOCOL_H + + +#include + + +enum xen_console_protocol +{ + /** + * VT100 terminal + */ + XEN_CONSOLE_PROTOCOL_VT100, + + /** + * Remote FrameBuffer protocol (as used in VNC) + */ + XEN_CONSOLE_PROTOCOL_RFB, + + /** + * Remote Desktop Protocol + */ + XEN_CONSOLE_PROTOCOL_RDP +}; + + +typedef struct xen_console_protocol_set +{ + size_t size; + enum xen_console_protocol contents[]; +} xen_console_protocol_set; + +/** + * Allocate a xen_console_protocol_set of the given size. + */ +extern xen_console_protocol_set * +xen_console_protocol_set_alloc(size_t size); + +/** + * Free the given xen_console_protocol_set. The given set must have + * been allocated by this library. + */ +extern void +xen_console_protocol_set_free(xen_console_protocol_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_console_protocol_to_string(enum xen_console_protocol val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_console_protocol +xen_console_protocol_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_crashdump.h b/tools/libxen/include/xen/api/xen_crashdump.h new file mode 100644 index 0000000..145e041 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_crashdump.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CRASHDUMP_H +#define XEN_CRASHDUMP_H + +#include +#include +#include +#include + + +/* + * The crashdump class. + * + * A VM crashdump. + */ + + +/** + * Free the given xen_crashdump. The given handle must have been + * allocated by this library. + */ +extern void +xen_crashdump_free(xen_crashdump crashdump); + + +typedef struct xen_crashdump_set +{ + size_t size; + xen_crashdump *contents[]; +} xen_crashdump_set; + +/** + * Allocate a xen_crashdump_set of the given size. + */ +extern xen_crashdump_set * +xen_crashdump_set_alloc(size_t size); + +/** + * Free the given xen_crashdump_set. The given set must have been + * allocated by this library. + */ +extern void +xen_crashdump_set_free(xen_crashdump_set *set); + + +typedef struct xen_crashdump_record +{ + xen_crashdump handle; + char *uuid; + struct xen_vm_record_opt *vm; + struct xen_vdi_record_opt *vdi; +} xen_crashdump_record; + +/** + * Allocate a xen_crashdump_record. + */ +extern xen_crashdump_record * +xen_crashdump_record_alloc(void); + +/** + * Free the given xen_crashdump_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_crashdump_record_free(xen_crashdump_record *record); + + +typedef struct xen_crashdump_record_opt +{ + bool is_record; + union + { + xen_crashdump handle; + xen_crashdump_record *record; + } u; +} xen_crashdump_record_opt; + +/** + * Allocate a xen_crashdump_record_opt. + */ +extern xen_crashdump_record_opt * +xen_crashdump_record_opt_alloc(void); + +/** + * Free the given xen_crashdump_record_opt, and all referenced values. + * The given record_opt must have been allocated by this library. + */ +extern void +xen_crashdump_record_opt_free(xen_crashdump_record_opt *record_opt); + + +typedef struct xen_crashdump_record_set +{ + size_t size; + xen_crashdump_record *contents[]; +} xen_crashdump_record_set; + +/** + * Allocate a xen_crashdump_record_set of the given size. + */ +extern xen_crashdump_record_set * +xen_crashdump_record_set_alloc(size_t size); + +/** + * Free the given xen_crashdump_record_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_crashdump_record_set_free(xen_crashdump_record_set *set); + + + +typedef struct xen_crashdump_record_opt_set +{ + size_t size; + xen_crashdump_record_opt *contents[]; +} xen_crashdump_record_opt_set; + +/** + * Allocate a xen_crashdump_record_opt_set of the given size. + */ +extern xen_crashdump_record_opt_set * +xen_crashdump_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_crashdump_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_crashdump_record_opt_set_free(xen_crashdump_record_opt_set *set); + + +/** + * Get a record containing the current state of the given crashdump. + */ +extern bool +xen_crashdump_get_record(xen_session *session, xen_crashdump_record **result, xen_crashdump crashdump); + + +/** + * Get a reference to the crashdump instance with the specified UUID. + */ +extern bool +xen_crashdump_get_by_uuid(xen_session *session, xen_crashdump *result, char *uuid); + + +/** + * Get the uuid field of the given crashdump. + */ +extern bool +xen_crashdump_get_uuid(xen_session *session, char **result, xen_crashdump crashdump); + + +/** + * Get the VM field of the given crashdump. + */ +extern bool +xen_crashdump_get_vm(xen_session *session, xen_vm *result, xen_crashdump crashdump); + + +/** + * Get the VDI field of the given crashdump. + */ +extern bool +xen_crashdump_get_vdi(xen_session *session, xen_vdi *result, xen_crashdump crashdump); + + +/** + * Destroy the specified crashdump. + */ +extern bool +xen_crashdump_destroy(xen_session *session, xen_crashdump self); + + +/** + * Return a list of all the crashdumps known to the system. + */ +extern bool +xen_crashdump_get_all(xen_session *session, struct xen_crashdump_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_crashdump_decl.h b/tools/libxen/include/xen/api/xen_crashdump_decl.h new file mode 100644 index 0000000..4eb6d54 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_crashdump_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CRASHDUMP_DECL_H +#define XEN_CRASHDUMP_DECL_H + +typedef void *xen_crashdump; + +struct xen_crashdump_set; +struct xen_crashdump_record; +struct xen_crashdump_record_set; +struct xen_crashdump_record_opt; +struct xen_crashdump_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_event.h b/tools/libxen/include/xen/api/xen_event.h new file mode 100644 index 0000000..6a2845b --- /dev/null +++ b/tools/libxen/include/xen/api/xen_event.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_EVENT_H +#define XEN_EVENT_H + +#include +#include +#include +#include + + +/* + * The event class. + * + * Asynchronous event registration and handling. + */ + + + +typedef struct xen_event_record +{ + int64_t id; + time_t timestamp; + char *class; + enum xen_event_operation operation; + char *ref; + char *obj_uuid; +} xen_event_record; + +/** + * Allocate a xen_event_record. + */ +extern xen_event_record * +xen_event_record_alloc(void); + +/** + * Free the given xen_event_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_event_record_free(xen_event_record *record); + + +typedef struct xen_event_record_set +{ + size_t size; + xen_event_record *contents[]; +} xen_event_record_set; + +/** + * Allocate a xen_event_record_set of the given size. + */ +extern xen_event_record_set * +xen_event_record_set_alloc(size_t size); + +/** + * Free the given xen_event_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_event_record_set_free(xen_event_record_set *set); + + +/** + * Registers this session with the event system. Specifying the empty + * list will register for all classes. + */ +extern bool +xen_event_register(xen_session *session, struct xen_string_set *classes); + + +/** + * Unregisters this session with the event system. + */ +extern bool +xen_event_unregister(xen_session *session, struct xen_string_set *classes); + + +/** + * Blocking call which returns a (possibly empty) batch of events. + */ +extern bool +xen_event_next(xen_session *session, struct xen_event_record_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_event_decl.h b/tools/libxen/include/xen/api/xen_event_decl.h new file mode 100644 index 0000000..856991f --- /dev/null +++ b/tools/libxen/include/xen/api/xen_event_decl.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_EVENT_DECL_H +#define XEN_EVENT_DECL_H + +struct xen_event_record; +struct xen_event_record_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_event_operation.h b/tools/libxen/include/xen/api/xen_event_operation.h new file mode 100644 index 0000000..31cce20 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_event_operation.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_EVENT_OPERATION_H +#define XEN_EVENT_OPERATION_H + + +#include + + +enum xen_event_operation +{ + /** + * An object has been created + */ + XEN_EVENT_OPERATION_ADD, + + /** + * An object has been deleted + */ + XEN_EVENT_OPERATION_DEL, + + /** + * An object has been modified + */ + XEN_EVENT_OPERATION_MOD +}; + + +typedef struct xen_event_operation_set +{ + size_t size; + enum xen_event_operation contents[]; +} xen_event_operation_set; + +/** + * Allocate a xen_event_operation_set of the given size. + */ +extern xen_event_operation_set * +xen_event_operation_set_alloc(size_t size); + +/** + * Free the given xen_event_operation_set. The given set must have + * been allocated by this library. + */ +extern void +xen_event_operation_set_free(xen_event_operation_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_event_operation_to_string(enum xen_event_operation val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_event_operation +xen_event_operation_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_host.h b/tools/libxen/include/xen/api/xen_host.h new file mode 100644 index 0000000..9ca40d7 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_host.h @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_HOST_H +#define XEN_HOST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * The host class. + * + * A physical host. + */ + + +/** + * Free the given xen_host. The given handle must have been allocated + * by this library. + */ +extern void +xen_host_free(xen_host host); + + +typedef struct xen_host_set +{ + size_t size; + xen_host *contents[]; +} xen_host_set; + +/** + * Allocate a xen_host_set of the given size. + */ +extern xen_host_set * +xen_host_set_alloc(size_t size); + +/** + * Free the given xen_host_set. The given set must have been allocated + * by this library. + */ +extern void +xen_host_set_free(xen_host_set *set); + + +typedef struct xen_host_record +{ + xen_host handle; + char *uuid; + char *name_label; + char *name_description; + int64_t api_version_major; + int64_t api_version_minor; + char *api_version_vendor; + xen_string_string_map *api_version_vendor_implementation; + bool enabled; + xen_string_string_map *software_version; + xen_string_string_map *other_config; + struct xen_string_set *capabilities; + xen_string_string_map *cpu_configuration; + char *sched_policy; + struct xen_string_set *supported_bootloaders; + struct xen_vm_record_opt_set *resident_vms; + xen_string_string_map *logging; + struct xen_pif_record_opt_set *pifs; + struct xen_sr_record_opt *suspend_image_sr; + struct xen_sr_record_opt *crash_dump_sr; + struct xen_pbd_record_opt_set *pbds; + struct xen_host_cpu_record_opt_set *host_cpus; + struct xen_host_metrics_record_opt *metrics; +} xen_host_record; + +/** + * Allocate a xen_host_record. + */ +extern xen_host_record * +xen_host_record_alloc(void); + +/** + * Free the given xen_host_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_host_record_free(xen_host_record *record); + + +typedef struct xen_host_record_opt +{ + bool is_record; + union + { + xen_host handle; + xen_host_record *record; + } u; +} xen_host_record_opt; + +/** + * Allocate a xen_host_record_opt. + */ +extern xen_host_record_opt * +xen_host_record_opt_alloc(void); + +/** + * Free the given xen_host_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_host_record_opt_free(xen_host_record_opt *record_opt); + + +typedef struct xen_host_record_set +{ + size_t size; + xen_host_record *contents[]; +} xen_host_record_set; + +/** + * Allocate a xen_host_record_set of the given size. + */ +extern xen_host_record_set * +xen_host_record_set_alloc(size_t size); + +/** + * Free the given xen_host_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_host_record_set_free(xen_host_record_set *set); + + + +typedef struct xen_host_record_opt_set +{ + size_t size; + xen_host_record_opt *contents[]; +} xen_host_record_opt_set; + +/** + * Allocate a xen_host_record_opt_set of the given size. + */ +extern xen_host_record_opt_set * +xen_host_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_host_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_host_record_opt_set_free(xen_host_record_opt_set *set); + + +/** + * Get a record containing the current state of the given host. + */ +extern bool +xen_host_get_record(xen_session *session, xen_host_record **result, xen_host host); + + +/** + * Get a reference to the host instance with the specified UUID. + */ +extern bool +xen_host_get_by_uuid(xen_session *session, xen_host *result, char *uuid); + + +/** + * Get all the host instances with the given label. + */ +extern bool +xen_host_get_by_name_label(xen_session *session, struct xen_host_set **result, char *label); + + +/** + * Get the uuid field of the given host. + */ +extern bool +xen_host_get_uuid(xen_session *session, char **result, xen_host host); + + +/** + * Get the name/label field of the given host. + */ +extern bool +xen_host_get_name_label(xen_session *session, char **result, xen_host host); + + +/** + * Get the name/description field of the given host. + */ +extern bool +xen_host_get_name_description(xen_session *session, char **result, xen_host host); + + +/** + * Get the API_version/major field of the given host. + */ +extern bool +xen_host_get_api_version_major(xen_session *session, int64_t *result, xen_host host); + + +/** + * Get the API_version/minor field of the given host. + */ +extern bool +xen_host_get_api_version_minor(xen_session *session, int64_t *result, xen_host host); + + +/** + * Get the API_version/vendor field of the given host. + */ +extern bool +xen_host_get_api_version_vendor(xen_session *session, char **result, xen_host host); + + +/** + * Get the API_version/vendor_implementation field of the given host. + */ +extern bool +xen_host_get_api_version_vendor_implementation(xen_session *session, xen_string_string_map **result, xen_host host); + + +/** + * Get the enabled field of the given host. + */ +extern bool +xen_host_get_enabled(xen_session *session, bool *result, xen_host host); + + +/** + * Get the software_version field of the given host. + */ +extern bool +xen_host_get_software_version(xen_session *session, xen_string_string_map **result, xen_host host); + + +/** + * Get the other_config field of the given host. + */ +extern bool +xen_host_get_other_config(xen_session *session, xen_string_string_map **result, xen_host host); + + +/** + * Get the capabilities field of the given host. + */ +extern bool +xen_host_get_capabilities(xen_session *session, struct xen_string_set **result, xen_host host); + + +/** + * Get the cpu_configuration field of the given host. + */ +extern bool +xen_host_get_cpu_configuration(xen_session *session, xen_string_string_map **result, xen_host host); + + +/** + * Get the sched_policy field of the given host. + */ +extern bool +xen_host_get_sched_policy(xen_session *session, char **result, xen_host host); + + +/** + * Get the supported_bootloaders field of the given host. + */ +extern bool +xen_host_get_supported_bootloaders(xen_session *session, struct xen_string_set **result, xen_host host); + + +/** + * Get the resident_VMs field of the given host. + */ +extern bool +xen_host_get_resident_vms(xen_session *session, struct xen_vm_set **result, xen_host host); + + +/** + * Get the logging field of the given host. + */ +extern bool +xen_host_get_logging(xen_session *session, xen_string_string_map **result, xen_host host); + + +/** + * Get the PIFs field of the given host. + */ +extern bool +xen_host_get_pifs(xen_session *session, struct xen_pif_set **result, xen_host host); + + +/** + * Get the suspend_image_sr field of the given host. + */ +extern bool +xen_host_get_suspend_image_sr(xen_session *session, xen_sr *result, xen_host host); + + +/** + * Get the crash_dump_sr field of the given host. + */ +extern bool +xen_host_get_crash_dump_sr(xen_session *session, xen_sr *result, xen_host host); + + +/** + * Get the PBDs field of the given host. + */ +extern bool +xen_host_get_pbds(xen_session *session, struct xen_pbd_set **result, xen_host host); + + +/** + * Get the host_CPUs field of the given host. + */ +extern bool +xen_host_get_host_cpus(xen_session *session, struct xen_host_cpu_set **result, xen_host host); + + +/** + * Get the metrics field of the given host. + */ +extern bool +xen_host_get_metrics(xen_session *session, xen_host_metrics *result, xen_host host); + + +/** + * Set the name/label field of the given host. + */ +extern bool +xen_host_set_name_label(xen_session *session, xen_host host, char *label); + + +/** + * Set the name/description field of the given host. + */ +extern bool +xen_host_set_name_description(xen_session *session, xen_host host, char *description); + + +/** + * Set the other_config field of the given host. + */ +extern bool +xen_host_set_other_config(xen_session *session, xen_host host, xen_string_string_map *other_config); + + +/** + * Add the given key-value pair to the other_config field of the given + * host. + */ +extern bool +xen_host_add_to_other_config(xen_session *session, xen_host host, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * other_config field of the given host. If the key is not in that Map, then + * do nothing. + */ +extern bool +xen_host_remove_from_other_config(xen_session *session, xen_host host, char *key); + + +/** + * Set the logging field of the given host. + */ +extern bool +xen_host_set_logging(xen_session *session, xen_host host, xen_string_string_map *logging); + + +/** + * Add the given key-value pair to the logging field of the given host. + */ +extern bool +xen_host_add_to_logging(xen_session *session, xen_host host, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the logging + * field of the given host. If the key is not in that Map, then do nothing. + */ +extern bool +xen_host_remove_from_logging(xen_session *session, xen_host host, char *key); + + +/** + * Set the suspend_image_sr field of the given host. + */ +extern bool +xen_host_set_suspend_image_sr(xen_session *session, xen_host host, xen_sr suspend_image_sr); + + +/** + * Set the crash_dump_sr field of the given host. + */ +extern bool +xen_host_set_crash_dump_sr(xen_session *session, xen_host host, xen_sr crash_dump_sr); + + +/** + * Puts the host into a state in which no new VMs can be started. + * Currently active VMs on the host continue to execute. + */ +extern bool +xen_host_disable(xen_session *session, xen_host host); + + +/** + * Puts the host into a state in which new VMs can be started. + */ +extern bool +xen_host_enable(xen_session *session, xen_host host); + + +/** + * Shutdown the host. (This function can only be called if there are no + * currently running VMs on the host and it is disabled.). + */ +extern bool +xen_host_shutdown(xen_session *session, xen_host host); + + +/** + * Reboot the host. (This function can only be called if there are no + * currently running VMs on the host and it is disabled.). + */ +extern bool +xen_host_reboot(xen_session *session, xen_host host); + + +/** + * Get the host xen dmesg. + */ +extern bool +xen_host_dmesg(xen_session *session, char **result, xen_host host); + + +/** + * Get the host xen dmesg, and clear the buffer. + */ +extern bool +xen_host_dmesg_clear(xen_session *session, char **result, xen_host host); + + +/** + * Get the host's log file. + */ +extern bool +xen_host_get_log(xen_session *session, char **result, xen_host host); + + +/** + * Inject the given string as debugging keys into Xen. + */ +extern bool +xen_host_send_debug_keys(xen_session *session, xen_host host, char *keys); + + +/** + * List all supported methods. + */ +extern bool +xen_host_list_methods(xen_session *session, struct xen_string_set **result); + + +/** + * Return a list of all the hosts known to the system. + */ +extern bool +xen_host_get_all(xen_session *session, struct xen_host_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_host_cpu.h b/tools/libxen/include/xen/api/xen_host_cpu.h new file mode 100644 index 0000000..f1bdb71 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_host_cpu.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_HOST_CPU_H +#define XEN_HOST_CPU_H + +#include +#include +#include + + +/* + * The host_cpu class. + * + * A physical CPU. + */ + + +/** + * Free the given xen_host_cpu. The given handle must have been + * allocated by this library. + */ +extern void +xen_host_cpu_free(xen_host_cpu host_cpu); + + +typedef struct xen_host_cpu_set +{ + size_t size; + xen_host_cpu *contents[]; +} xen_host_cpu_set; + +/** + * Allocate a xen_host_cpu_set of the given size. + */ +extern xen_host_cpu_set * +xen_host_cpu_set_alloc(size_t size); + +/** + * Free the given xen_host_cpu_set. The given set must have been + * allocated by this library. + */ +extern void +xen_host_cpu_set_free(xen_host_cpu_set *set); + + +typedef struct xen_host_cpu_record +{ + xen_host_cpu handle; + char *uuid; + struct xen_host_record_opt *host; + int64_t number; + char *vendor; + int64_t speed; + char *modelname; + char *stepping; + char *flags; + char *features; + double utilisation; +} xen_host_cpu_record; + +/** + * Allocate a xen_host_cpu_record. + */ +extern xen_host_cpu_record * +xen_host_cpu_record_alloc(void); + +/** + * Free the given xen_host_cpu_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_host_cpu_record_free(xen_host_cpu_record *record); + + +typedef struct xen_host_cpu_record_opt +{ + bool is_record; + union + { + xen_host_cpu handle; + xen_host_cpu_record *record; + } u; +} xen_host_cpu_record_opt; + +/** + * Allocate a xen_host_cpu_record_opt. + */ +extern xen_host_cpu_record_opt * +xen_host_cpu_record_opt_alloc(void); + +/** + * Free the given xen_host_cpu_record_opt, and all referenced values. + * The given record_opt must have been allocated by this library. + */ +extern void +xen_host_cpu_record_opt_free(xen_host_cpu_record_opt *record_opt); + + +typedef struct xen_host_cpu_record_set +{ + size_t size; + xen_host_cpu_record *contents[]; +} xen_host_cpu_record_set; + +/** + * Allocate a xen_host_cpu_record_set of the given size. + */ +extern xen_host_cpu_record_set * +xen_host_cpu_record_set_alloc(size_t size); + +/** + * Free the given xen_host_cpu_record_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_host_cpu_record_set_free(xen_host_cpu_record_set *set); + + + +typedef struct xen_host_cpu_record_opt_set +{ + size_t size; + xen_host_cpu_record_opt *contents[]; +} xen_host_cpu_record_opt_set; + +/** + * Allocate a xen_host_cpu_record_opt_set of the given size. + */ +extern xen_host_cpu_record_opt_set * +xen_host_cpu_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_host_cpu_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_host_cpu_record_opt_set_free(xen_host_cpu_record_opt_set *set); + + +/** + * Get a record containing the current state of the given host_cpu. + */ +extern bool +xen_host_cpu_get_record(xen_session *session, xen_host_cpu_record **result, xen_host_cpu host_cpu); + + +/** + * Get a reference to the host_cpu instance with the specified UUID. + */ +extern bool +xen_host_cpu_get_by_uuid(xen_session *session, xen_host_cpu *result, char *uuid); + + +/** + * Get the uuid field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_uuid(xen_session *session, char **result, xen_host_cpu host_cpu); + + +/** + * Get the host field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_host(xen_session *session, xen_host *result, xen_host_cpu host_cpu); + + +/** + * Get the number field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_number(xen_session *session, int64_t *result, xen_host_cpu host_cpu); + + +/** + * Get the vendor field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_vendor(xen_session *session, char **result, xen_host_cpu host_cpu); + + +/** + * Get the speed field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_speed(xen_session *session, int64_t *result, xen_host_cpu host_cpu); + + +/** + * Get the modelname field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_modelname(xen_session *session, char **result, xen_host_cpu host_cpu); + + +/** + * Get the stepping field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_stepping(xen_session *session, char **result, xen_host_cpu host_cpu); + + +/** + * Get the flags field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_flags(xen_session *session, char **result, xen_host_cpu host_cpu); + + +/** + * Get the features field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_features(xen_session *session, char **result, xen_host_cpu host_cpu); + + +/** + * Get the utilisation field of the given host_cpu. + */ +extern bool +xen_host_cpu_get_utilisation(xen_session *session, double *result, xen_host_cpu host_cpu); + + +/** + * Return a list of all the host_cpus known to the system. + */ +extern bool +xen_host_cpu_get_all(xen_session *session, struct xen_host_cpu_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_host_cpu_decl.h b/tools/libxen/include/xen/api/xen_host_cpu_decl.h new file mode 100644 index 0000000..1e9be29 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_host_cpu_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_HOST_CPU_DECL_H +#define XEN_HOST_CPU_DECL_H + +typedef void *xen_host_cpu; + +struct xen_host_cpu_set; +struct xen_host_cpu_record; +struct xen_host_cpu_record_set; +struct xen_host_cpu_record_opt; +struct xen_host_cpu_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_host_decl.h b/tools/libxen/include/xen/api/xen_host_decl.h new file mode 100644 index 0000000..79d974b --- /dev/null +++ b/tools/libxen/include/xen/api/xen_host_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_HOST_DECL_H +#define XEN_HOST_DECL_H + +typedef void *xen_host; + +struct xen_host_set; +struct xen_host_record; +struct xen_host_record_set; +struct xen_host_record_opt; +struct xen_host_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_host_metrics.h b/tools/libxen/include/xen/api/xen_host_metrics.h new file mode 100644 index 0000000..2055362 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_host_metrics.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_HOST_METRICS_H +#define XEN_HOST_METRICS_H + +#include +#include + + +/* + * The host_metrics class. + * + * The metrics associated with a host. + */ + + +/** + * Free the given xen_host_metrics. The given handle must have been + * allocated by this library. + */ +extern void +xen_host_metrics_free(xen_host_metrics host_metrics); + + +typedef struct xen_host_metrics_set +{ + size_t size; + xen_host_metrics *contents[]; +} xen_host_metrics_set; + +/** + * Allocate a xen_host_metrics_set of the given size. + */ +extern xen_host_metrics_set * +xen_host_metrics_set_alloc(size_t size); + +/** + * Free the given xen_host_metrics_set. The given set must have been + * allocated by this library. + */ +extern void +xen_host_metrics_set_free(xen_host_metrics_set *set); + + +typedef struct xen_host_metrics_record +{ + xen_host_metrics handle; + char *uuid; + int64_t memory_total; + int64_t memory_free; + time_t last_updated; +} xen_host_metrics_record; + +/** + * Allocate a xen_host_metrics_record. + */ +extern xen_host_metrics_record * +xen_host_metrics_record_alloc(void); + +/** + * Free the given xen_host_metrics_record, and all referenced values. + * The given record must have been allocated by this library. + */ +extern void +xen_host_metrics_record_free(xen_host_metrics_record *record); + + +typedef struct xen_host_metrics_record_opt +{ + bool is_record; + union + { + xen_host_metrics handle; + xen_host_metrics_record *record; + } u; +} xen_host_metrics_record_opt; + +/** + * Allocate a xen_host_metrics_record_opt. + */ +extern xen_host_metrics_record_opt * +xen_host_metrics_record_opt_alloc(void); + +/** + * Free the given xen_host_metrics_record_opt, and all referenced + * values. The given record_opt must have been allocated by this library. + */ +extern void +xen_host_metrics_record_opt_free(xen_host_metrics_record_opt *record_opt); + + +typedef struct xen_host_metrics_record_set +{ + size_t size; + xen_host_metrics_record *contents[]; +} xen_host_metrics_record_set; + +/** + * Allocate a xen_host_metrics_record_set of the given size. + */ +extern xen_host_metrics_record_set * +xen_host_metrics_record_set_alloc(size_t size); + +/** + * Free the given xen_host_metrics_record_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_host_metrics_record_set_free(xen_host_metrics_record_set *set); + + + +typedef struct xen_host_metrics_record_opt_set +{ + size_t size; + xen_host_metrics_record_opt *contents[]; +} xen_host_metrics_record_opt_set; + +/** + * Allocate a xen_host_metrics_record_opt_set of the given size. + */ +extern xen_host_metrics_record_opt_set * +xen_host_metrics_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_host_metrics_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_host_metrics_record_opt_set_free(xen_host_metrics_record_opt_set *set); + + +/** + * Get a record containing the current state of the given host_metrics. + */ +extern bool +xen_host_metrics_get_record(xen_session *session, xen_host_metrics_record **result, xen_host_metrics host_metrics); + + +/** + * Get a reference to the host_metrics instance with the specified + * UUID. + */ +extern bool +xen_host_metrics_get_by_uuid(xen_session *session, xen_host_metrics *result, char *uuid); + + +/** + * Get the uuid field of the given host_metrics. + */ +extern bool +xen_host_metrics_get_uuid(xen_session *session, char **result, xen_host_metrics host_metrics); + + +/** + * Get the memory/total field of the given host_metrics. + */ +extern bool +xen_host_metrics_get_memory_total(xen_session *session, int64_t *result, xen_host_metrics host_metrics); + + +/** + * Get the memory/free field of the given host_metrics. + */ +extern bool +xen_host_metrics_get_memory_free(xen_session *session, int64_t *result, xen_host_metrics host_metrics); + + +/** + * Get the last_updated field of the given host_metrics. + */ +extern bool +xen_host_metrics_get_last_updated(xen_session *session, time_t *result, xen_host_metrics host_metrics); + + +/** + * Return a list of all the host_metrics instances known to the system. + */ +extern bool +xen_host_metrics_get_all(xen_session *session, struct xen_host_metrics_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_host_metrics_decl.h b/tools/libxen/include/xen/api/xen_host_metrics_decl.h new file mode 100644 index 0000000..8b73554 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_host_metrics_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_HOST_METRICS_DECL_H +#define XEN_HOST_METRICS_DECL_H + +typedef void *xen_host_metrics; + +struct xen_host_metrics_set; +struct xen_host_metrics_record; +struct xen_host_metrics_record_set; +struct xen_host_metrics_record_opt; +struct xen_host_metrics_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_int_float_map.h b/tools/libxen/include/xen/api/xen_int_float_map.h new file mode 100644 index 0000000..058c45e --- /dev/null +++ b/tools/libxen/include/xen/api/xen_int_float_map.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_INT_FLOAT_MAP_H +#define XEN_INT_FLOAT_MAP_H + + +#include + + +typedef struct xen_int_float_map_contents +{ + int64_t key; + double val; +} xen_int_float_map_contents; + + +typedef struct xen_int_float_map +{ + size_t size; + xen_int_float_map_contents contents[]; +} xen_int_float_map; + +/** + * Allocate a xen_int_float_map of the given size. + */ +extern xen_int_float_map * +xen_int_float_map_alloc(size_t size); + +/** + * Free the given xen_int_float_map, and all referenced values. The + * given map must have been allocated by this library. + */ +extern void +xen_int_float_map_free(xen_int_float_map *map); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_int_int_map.h b/tools/libxen/include/xen/api/xen_int_int_map.h new file mode 100644 index 0000000..71295a6 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_int_int_map.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_INT_INT_MAP_H +#define XEN_INT_INT_MAP_H + + +#include + + +typedef struct xen_int_int_map_contents +{ + int64_t key; + int64_t val; +} xen_int_int_map_contents; + + +typedef struct xen_int_int_map +{ + size_t size; + xen_int_int_map_contents contents[]; +} xen_int_int_map; + +/** + * Allocate a xen_int_int_map of the given size. + */ +extern xen_int_int_map * +xen_int_int_map_alloc(size_t size); + +/** + * Free the given xen_int_int_map, and all referenced values. The + * given map must have been allocated by this library. + */ +extern void +xen_int_int_map_free(xen_int_int_map *map); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_int_string_set_map.h b/tools/libxen/include/xen/api/xen_int_string_set_map.h new file mode 100644 index 0000000..bca1a6b --- /dev/null +++ b/tools/libxen/include/xen/api/xen_int_string_set_map.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_INT_STRING_SET_MAP_H +#define XEN_INT_STRING_SET_MAP_H + + +#include + + +typedef struct xen_int_string_set_map_contents +{ + int64_t key; + struct xen_string_set *val; +} xen_int_string_set_map_contents; + + +typedef struct xen_int_string_set_map +{ + size_t size; + xen_int_string_set_map_contents contents[]; +} xen_int_string_set_map; + +/** + * Allocate a xen_int_string_set_map of the given size. + */ +extern xen_int_string_set_map * +xen_int_string_set_map_alloc(size_t size); + +/** + * Free the given xen_int_string_set_map, and all referenced values. + * The given map must have been allocated by this library. + */ +extern void +xen_int_string_set_map_free(xen_int_string_set_map *map); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_network.h b/tools/libxen/include/xen/api/xen_network.h new file mode 100644 index 0000000..bf8295a --- /dev/null +++ b/tools/libxen/include/xen/api/xen_network.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_NETWORK_H +#define XEN_NETWORK_H + +#include +#include +#include +#include +#include + + +/* + * The network class. + * + * A virtual network. + */ + + +/** + * Free the given xen_network. The given handle must have been + * allocated by this library. + */ +extern void +xen_network_free(xen_network network); + + +typedef struct xen_network_set +{ + size_t size; + xen_network *contents[]; +} xen_network_set; + +/** + * Allocate a xen_network_set of the given size. + */ +extern xen_network_set * +xen_network_set_alloc(size_t size); + +/** + * Free the given xen_network_set. The given set must have been + * allocated by this library. + */ +extern void +xen_network_set_free(xen_network_set *set); + + +typedef struct xen_network_record +{ + xen_network handle; + char *uuid; + char *name_label; + char *name_description; + struct xen_vif_record_opt_set *vifs; + struct xen_pif_record_opt_set *pifs; + xen_string_string_map *other_config; +} xen_network_record; + +/** + * Allocate a xen_network_record. + */ +extern xen_network_record * +xen_network_record_alloc(void); + +/** + * Free the given xen_network_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_network_record_free(xen_network_record *record); + + +typedef struct xen_network_record_opt +{ + bool is_record; + union + { + xen_network handle; + xen_network_record *record; + } u; +} xen_network_record_opt; + +/** + * Allocate a xen_network_record_opt. + */ +extern xen_network_record_opt * +xen_network_record_opt_alloc(void); + +/** + * Free the given xen_network_record_opt, and all referenced values. + * The given record_opt must have been allocated by this library. + */ +extern void +xen_network_record_opt_free(xen_network_record_opt *record_opt); + + +typedef struct xen_network_record_set +{ + size_t size; + xen_network_record *contents[]; +} xen_network_record_set; + +/** + * Allocate a xen_network_record_set of the given size. + */ +extern xen_network_record_set * +xen_network_record_set_alloc(size_t size); + +/** + * Free the given xen_network_record_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_network_record_set_free(xen_network_record_set *set); + + + +typedef struct xen_network_record_opt_set +{ + size_t size; + xen_network_record_opt *contents[]; +} xen_network_record_opt_set; + +/** + * Allocate a xen_network_record_opt_set of the given size. + */ +extern xen_network_record_opt_set * +xen_network_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_network_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_network_record_opt_set_free(xen_network_record_opt_set *set); + + +/** + * Get a record containing the current state of the given network. + */ +extern bool +xen_network_get_record(xen_session *session, xen_network_record **result, xen_network network); + + +/** + * Get a reference to the network instance with the specified UUID. + */ +extern bool +xen_network_get_by_uuid(xen_session *session, xen_network *result, char *uuid); + + +/** + * Create a new network instance, and return its handle. + */ +extern bool +xen_network_create(xen_session *session, xen_network *result, xen_network_record *record); + + +/** + * Destroy the specified network instance. + */ +extern bool +xen_network_destroy(xen_session *session, xen_network network); + + +/** + * Get all the network instances with the given label. + */ +extern bool +xen_network_get_by_name_label(xen_session *session, struct xen_network_set **result, char *label); + + +/** + * Get the uuid field of the given network. + */ +extern bool +xen_network_get_uuid(xen_session *session, char **result, xen_network network); + + +/** + * Get the name/label field of the given network. + */ +extern bool +xen_network_get_name_label(xen_session *session, char **result, xen_network network); + + +/** + * Get the name/description field of the given network. + */ +extern bool +xen_network_get_name_description(xen_session *session, char **result, xen_network network); + + +/** + * Get the VIFs field of the given network. + */ +extern bool +xen_network_get_vifs(xen_session *session, struct xen_vif_set **result, xen_network network); + + +/** + * Get the PIFs field of the given network. + */ +extern bool +xen_network_get_pifs(xen_session *session, struct xen_pif_set **result, xen_network network); + + +/** + * Get the other_config field of the given network. + */ +extern bool +xen_network_get_other_config(xen_session *session, xen_string_string_map **result, xen_network network); + + +/** + * Set the name/label field of the given network. + */ +extern bool +xen_network_set_name_label(xen_session *session, xen_network network, char *label); + + +/** + * Set the name/description field of the given network. + */ +extern bool +xen_network_set_name_description(xen_session *session, xen_network network, char *description); + + +/** + * Set the other_config field of the given network. + */ +extern bool +xen_network_set_other_config(xen_session *session, xen_network network, xen_string_string_map *other_config); + + +/** + * Add the given key-value pair to the other_config field of the given + * network. + */ +extern bool +xen_network_add_to_other_config(xen_session *session, xen_network network, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * other_config field of the given network. If the key is not in that Map, + * then do nothing. + */ +extern bool +xen_network_remove_from_other_config(xen_session *session, xen_network network, char *key); + + +/** + * Return a list of all the networks known to the system. + */ +extern bool +xen_network_get_all(xen_session *session, struct xen_network_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_network_decl.h b/tools/libxen/include/xen/api/xen_network_decl.h new file mode 100644 index 0000000..dbfb3e9 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_network_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_NETWORK_DECL_H +#define XEN_NETWORK_DECL_H + +typedef void *xen_network; + +struct xen_network_set; +struct xen_network_record; +struct xen_network_record_set; +struct xen_network_record_opt; +struct xen_network_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_on_crash_behaviour.h b/tools/libxen/include/xen/api/xen_on_crash_behaviour.h new file mode 100644 index 0000000..47792c6 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_on_crash_behaviour.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_ON_CRASH_BEHAVIOUR_H +#define XEN_ON_CRASH_BEHAVIOUR_H + + +#include + + +enum xen_on_crash_behaviour +{ + /** + * destroy the VM state + */ + XEN_ON_CRASH_BEHAVIOUR_DESTROY, + + /** + * record a coredump and then destroy the VM state + */ + XEN_ON_CRASH_BEHAVIOUR_COREDUMP_AND_DESTROY, + + /** + * restart the VM + */ + XEN_ON_CRASH_BEHAVIOUR_RESTART, + + /** + * record a coredump and then restart the VM + */ + XEN_ON_CRASH_BEHAVIOUR_COREDUMP_AND_RESTART, + + /** + * leave the crashed VM as-is + */ + XEN_ON_CRASH_BEHAVIOUR_PRESERVE, + + /** + * rename the crashed VM and start a new copy + */ + XEN_ON_CRASH_BEHAVIOUR_RENAME_RESTART +}; + + +typedef struct xen_on_crash_behaviour_set +{ + size_t size; + enum xen_on_crash_behaviour contents[]; +} xen_on_crash_behaviour_set; + +/** + * Allocate a xen_on_crash_behaviour_set of the given size. + */ +extern xen_on_crash_behaviour_set * +xen_on_crash_behaviour_set_alloc(size_t size); + +/** + * Free the given xen_on_crash_behaviour_set. The given set must have + * been allocated by this library. + */ +extern void +xen_on_crash_behaviour_set_free(xen_on_crash_behaviour_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_on_crash_behaviour_to_string(enum xen_on_crash_behaviour val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_on_crash_behaviour +xen_on_crash_behaviour_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_on_normal_exit.h b/tools/libxen/include/xen/api/xen_on_normal_exit.h new file mode 100644 index 0000000..cd5a813 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_on_normal_exit.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_ON_NORMAL_EXIT_H +#define XEN_ON_NORMAL_EXIT_H + + +#include + + +enum xen_on_normal_exit +{ + /** + * destroy the VM state + */ + XEN_ON_NORMAL_EXIT_DESTROY, + + /** + * restart the VM + */ + XEN_ON_NORMAL_EXIT_RESTART +}; + + +typedef struct xen_on_normal_exit_set +{ + size_t size; + enum xen_on_normal_exit contents[]; +} xen_on_normal_exit_set; + +/** + * Allocate a xen_on_normal_exit_set of the given size. + */ +extern xen_on_normal_exit_set * +xen_on_normal_exit_set_alloc(size_t size); + +/** + * Free the given xen_on_normal_exit_set. The given set must have been + * allocated by this library. + */ +extern void +xen_on_normal_exit_set_free(xen_on_normal_exit_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_on_normal_exit_to_string(enum xen_on_normal_exit val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_on_normal_exit +xen_on_normal_exit_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_pbd.h b/tools/libxen/include/xen/api/xen_pbd.h new file mode 100644 index 0000000..224d2d4 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_pbd.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_PBD_H +#define XEN_PBD_H + +#include +#include +#include +#include +#include + + +/* + * The PBD class. + * + * The physical block devices through which hosts access SRs. + */ + + +/** + * Free the given xen_pbd. The given handle must have been allocated + * by this library. + */ +extern void +xen_pbd_free(xen_pbd pbd); + + +typedef struct xen_pbd_set +{ + size_t size; + xen_pbd *contents[]; +} xen_pbd_set; + +/** + * Allocate a xen_pbd_set of the given size. + */ +extern xen_pbd_set * +xen_pbd_set_alloc(size_t size); + +/** + * Free the given xen_pbd_set. The given set must have been allocated + * by this library. + */ +extern void +xen_pbd_set_free(xen_pbd_set *set); + + +typedef struct xen_pbd_record +{ + xen_pbd handle; + char *uuid; + struct xen_host_record_opt *host; + struct xen_sr_record_opt *sr; + xen_string_string_map *device_config; + bool currently_attached; +} xen_pbd_record; + +/** + * Allocate a xen_pbd_record. + */ +extern xen_pbd_record * +xen_pbd_record_alloc(void); + +/** + * Free the given xen_pbd_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_pbd_record_free(xen_pbd_record *record); + + +typedef struct xen_pbd_record_opt +{ + bool is_record; + union + { + xen_pbd handle; + xen_pbd_record *record; + } u; +} xen_pbd_record_opt; + +/** + * Allocate a xen_pbd_record_opt. + */ +extern xen_pbd_record_opt * +xen_pbd_record_opt_alloc(void); + +/** + * Free the given xen_pbd_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_pbd_record_opt_free(xen_pbd_record_opt *record_opt); + + +typedef struct xen_pbd_record_set +{ + size_t size; + xen_pbd_record *contents[]; +} xen_pbd_record_set; + +/** + * Allocate a xen_pbd_record_set of the given size. + */ +extern xen_pbd_record_set * +xen_pbd_record_set_alloc(size_t size); + +/** + * Free the given xen_pbd_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_pbd_record_set_free(xen_pbd_record_set *set); + + + +typedef struct xen_pbd_record_opt_set +{ + size_t size; + xen_pbd_record_opt *contents[]; +} xen_pbd_record_opt_set; + +/** + * Allocate a xen_pbd_record_opt_set of the given size. + */ +extern xen_pbd_record_opt_set * +xen_pbd_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_pbd_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_pbd_record_opt_set_free(xen_pbd_record_opt_set *set); + + +/** + * Get a record containing the current state of the given PBD. + */ +extern bool +xen_pbd_get_record(xen_session *session, xen_pbd_record **result, xen_pbd pbd); + + +/** + * Get a reference to the PBD instance with the specified UUID. + */ +extern bool +xen_pbd_get_by_uuid(xen_session *session, xen_pbd *result, char *uuid); + + +/** + * Create a new PBD instance, and return its handle. + */ +extern bool +xen_pbd_create(xen_session *session, xen_pbd *result, xen_pbd_record *record); + + +/** + * Destroy the specified PBD instance. + */ +extern bool +xen_pbd_destroy(xen_session *session, xen_pbd pbd); + + +/** + * Get the uuid field of the given PBD. + */ +extern bool +xen_pbd_get_uuid(xen_session *session, char **result, xen_pbd pbd); + + +/** + * Get the host field of the given PBD. + */ +extern bool +xen_pbd_get_host(xen_session *session, xen_host *result, xen_pbd pbd); + + +/** + * Get the SR field of the given PBD. + */ +extern bool +xen_pbd_get_sr(xen_session *session, xen_sr *result, xen_pbd pbd); + + +/** + * Get the device_config field of the given PBD. + */ +extern bool +xen_pbd_get_device_config(xen_session *session, xen_string_string_map **result, xen_pbd pbd); + + +/** + * Get the currently_attached field of the given PBD. + */ +extern bool +xen_pbd_get_currently_attached(xen_session *session, bool *result, xen_pbd pbd); + + +/** + * Return a list of all the PBDs known to the system. + */ +extern bool +xen_pbd_get_all(xen_session *session, struct xen_pbd_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_pbd_decl.h b/tools/libxen/include/xen/api/xen_pbd_decl.h new file mode 100644 index 0000000..c7e324e --- /dev/null +++ b/tools/libxen/include/xen/api/xen_pbd_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_PBD_DECL_H +#define XEN_PBD_DECL_H + +typedef void *xen_pbd; + +struct xen_pbd_set; +struct xen_pbd_record; +struct xen_pbd_record_set; +struct xen_pbd_record_opt; +struct xen_pbd_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_pif.h b/tools/libxen/include/xen/api/xen_pif.h new file mode 100644 index 0000000..d36efd0 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_pif.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_PIF_H +#define XEN_PIF_H + +#include +#include +#include +#include +#include + + +/* + * The PIF class. + * + * A physical network interface (note separate VLANs are represented as + * several PIFs). + */ + + +/** + * Free the given xen_pif. The given handle must have been allocated + * by this library. + */ +extern void +xen_pif_free(xen_pif pif); + + +typedef struct xen_pif_set +{ + size_t size; + xen_pif *contents[]; +} xen_pif_set; + +/** + * Allocate a xen_pif_set of the given size. + */ +extern xen_pif_set * +xen_pif_set_alloc(size_t size); + +/** + * Free the given xen_pif_set. The given set must have been allocated + * by this library. + */ +extern void +xen_pif_set_free(xen_pif_set *set); + + +typedef struct xen_pif_record +{ + xen_pif handle; + char *uuid; + char *device; + struct xen_network_record_opt *network; + struct xen_host_record_opt *host; + char *mac; + int64_t mtu; + int64_t vlan; + struct xen_pif_metrics_record_opt *metrics; +} xen_pif_record; + +/** + * Allocate a xen_pif_record. + */ +extern xen_pif_record * +xen_pif_record_alloc(void); + +/** + * Free the given xen_pif_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_pif_record_free(xen_pif_record *record); + + +typedef struct xen_pif_record_opt +{ + bool is_record; + union + { + xen_pif handle; + xen_pif_record *record; + } u; +} xen_pif_record_opt; + +/** + * Allocate a xen_pif_record_opt. + */ +extern xen_pif_record_opt * +xen_pif_record_opt_alloc(void); + +/** + * Free the given xen_pif_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_pif_record_opt_free(xen_pif_record_opt *record_opt); + + +typedef struct xen_pif_record_set +{ + size_t size; + xen_pif_record *contents[]; +} xen_pif_record_set; + +/** + * Allocate a xen_pif_record_set of the given size. + */ +extern xen_pif_record_set * +xen_pif_record_set_alloc(size_t size); + +/** + * Free the given xen_pif_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_pif_record_set_free(xen_pif_record_set *set); + + + +typedef struct xen_pif_record_opt_set +{ + size_t size; + xen_pif_record_opt *contents[]; +} xen_pif_record_opt_set; + +/** + * Allocate a xen_pif_record_opt_set of the given size. + */ +extern xen_pif_record_opt_set * +xen_pif_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_pif_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_pif_record_opt_set_free(xen_pif_record_opt_set *set); + + +/** + * Get a record containing the current state of the given PIF. + */ +extern bool +xen_pif_get_record(xen_session *session, xen_pif_record **result, xen_pif pif); + + +/** + * Get a reference to the PIF instance with the specified UUID. + */ +extern bool +xen_pif_get_by_uuid(xen_session *session, xen_pif *result, char *uuid); + + +/** + * Get the uuid field of the given PIF. + */ +extern bool +xen_pif_get_uuid(xen_session *session, char **result, xen_pif pif); + + +/** + * Get the device field of the given PIF. + */ +extern bool +xen_pif_get_device(xen_session *session, char **result, xen_pif pif); + + +/** + * Get the network field of the given PIF. + */ +extern bool +xen_pif_get_network(xen_session *session, xen_network *result, xen_pif pif); + + +/** + * Get the host field of the given PIF. + */ +extern bool +xen_pif_get_host(xen_session *session, xen_host *result, xen_pif pif); + + +/** + * Get the MAC field of the given PIF. + */ +extern bool +xen_pif_get_mac(xen_session *session, char **result, xen_pif pif); + + +/** + * Get the MTU field of the given PIF. + */ +extern bool +xen_pif_get_mtu(xen_session *session, int64_t *result, xen_pif pif); + + +/** + * Get the VLAN field of the given PIF. + */ +extern bool +xen_pif_get_vlan(xen_session *session, int64_t *result, xen_pif pif); + + +/** + * Get the metrics field of the given PIF. + */ +extern bool +xen_pif_get_metrics(xen_session *session, xen_pif_metrics *result, xen_pif pif); + + +/** + * Set the device field of the given PIF. + */ +extern bool +xen_pif_set_device(xen_session *session, xen_pif pif, char *device); + + +/** + * Set the MAC field of the given PIF. + */ +extern bool +xen_pif_set_mac(xen_session *session, xen_pif pif, char *mac); + + +/** + * Set the MTU field of the given PIF. + */ +extern bool +xen_pif_set_mtu(xen_session *session, xen_pif pif, int64_t mtu); + + +/** + * Set the VLAN field of the given PIF. + */ +extern bool +xen_pif_set_vlan(xen_session *session, xen_pif pif, int64_t vlan); + + +/** + * Create a VLAN interface from an existing physical interface. + */ +extern bool +xen_pif_create_vlan(xen_session *session, xen_pif *result, char *device, xen_network network, xen_host host, int64_t vlan); + + +/** + * Destroy the interface (provided it is a synthetic interface like a + * VLAN; fail if it is a physical interface). + */ +extern bool +xen_pif_destroy(xen_session *session, xen_pif self); + + +/** + * Return a list of all the PIFs known to the system. + */ +extern bool +xen_pif_get_all(xen_session *session, struct xen_pif_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_pif_decl.h b/tools/libxen/include/xen/api/xen_pif_decl.h new file mode 100644 index 0000000..6d49bdb --- /dev/null +++ b/tools/libxen/include/xen/api/xen_pif_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_PIF_DECL_H +#define XEN_PIF_DECL_H + +typedef void *xen_pif; + +struct xen_pif_set; +struct xen_pif_record; +struct xen_pif_record_set; +struct xen_pif_record_opt; +struct xen_pif_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_pif_metrics.h b/tools/libxen/include/xen/api/xen_pif_metrics.h new file mode 100644 index 0000000..06b9d26 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_pif_metrics.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_PIF_METRICS_H +#define XEN_PIF_METRICS_H + +#include +#include + + +/* + * The PIF_metrics class. + * + * The metrics associated with a physical network interface. + */ + + +/** + * Free the given xen_pif_metrics. The given handle must have been + * allocated by this library. + */ +extern void +xen_pif_metrics_free(xen_pif_metrics pif_metrics); + + +typedef struct xen_pif_metrics_set +{ + size_t size; + xen_pif_metrics *contents[]; +} xen_pif_metrics_set; + +/** + * Allocate a xen_pif_metrics_set of the given size. + */ +extern xen_pif_metrics_set * +xen_pif_metrics_set_alloc(size_t size); + +/** + * Free the given xen_pif_metrics_set. The given set must have been + * allocated by this library. + */ +extern void +xen_pif_metrics_set_free(xen_pif_metrics_set *set); + + +typedef struct xen_pif_metrics_record +{ + xen_pif_metrics handle; + char *uuid; + double io_read_kbs; + double io_write_kbs; + time_t last_updated; +} xen_pif_metrics_record; + +/** + * Allocate a xen_pif_metrics_record. + */ +extern xen_pif_metrics_record * +xen_pif_metrics_record_alloc(void); + +/** + * Free the given xen_pif_metrics_record, and all referenced values. + * The given record must have been allocated by this library. + */ +extern void +xen_pif_metrics_record_free(xen_pif_metrics_record *record); + + +typedef struct xen_pif_metrics_record_opt +{ + bool is_record; + union + { + xen_pif_metrics handle; + xen_pif_metrics_record *record; + } u; +} xen_pif_metrics_record_opt; + +/** + * Allocate a xen_pif_metrics_record_opt. + */ +extern xen_pif_metrics_record_opt * +xen_pif_metrics_record_opt_alloc(void); + +/** + * Free the given xen_pif_metrics_record_opt, and all referenced + * values. The given record_opt must have been allocated by this library. + */ +extern void +xen_pif_metrics_record_opt_free(xen_pif_metrics_record_opt *record_opt); + + +typedef struct xen_pif_metrics_record_set +{ + size_t size; + xen_pif_metrics_record *contents[]; +} xen_pif_metrics_record_set; + +/** + * Allocate a xen_pif_metrics_record_set of the given size. + */ +extern xen_pif_metrics_record_set * +xen_pif_metrics_record_set_alloc(size_t size); + +/** + * Free the given xen_pif_metrics_record_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_pif_metrics_record_set_free(xen_pif_metrics_record_set *set); + + + +typedef struct xen_pif_metrics_record_opt_set +{ + size_t size; + xen_pif_metrics_record_opt *contents[]; +} xen_pif_metrics_record_opt_set; + +/** + * Allocate a xen_pif_metrics_record_opt_set of the given size. + */ +extern xen_pif_metrics_record_opt_set * +xen_pif_metrics_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_pif_metrics_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_pif_metrics_record_opt_set_free(xen_pif_metrics_record_opt_set *set); + + +/** + * Get a record containing the current state of the given PIF_metrics. + */ +extern bool +xen_pif_metrics_get_record(xen_session *session, xen_pif_metrics_record **result, xen_pif_metrics pif_metrics); + + +/** + * Get a reference to the PIF_metrics instance with the specified UUID. + */ +extern bool +xen_pif_metrics_get_by_uuid(xen_session *session, xen_pif_metrics *result, char *uuid); + + +/** + * Get the uuid field of the given PIF_metrics. + */ +extern bool +xen_pif_metrics_get_uuid(xen_session *session, char **result, xen_pif_metrics pif_metrics); + + +/** + * Get the io/read_kbs field of the given PIF_metrics. + */ +extern bool +xen_pif_metrics_get_io_read_kbs(xen_session *session, double *result, xen_pif_metrics pif_metrics); + + +/** + * Get the io/write_kbs field of the given PIF_metrics. + */ +extern bool +xen_pif_metrics_get_io_write_kbs(xen_session *session, double *result, xen_pif_metrics pif_metrics); + + +/** + * Get the last_updated field of the given PIF_metrics. + */ +extern bool +xen_pif_metrics_get_last_updated(xen_session *session, time_t *result, xen_pif_metrics pif_metrics); + + +/** + * Return a list of all the PIF_metrics instances known to the system. + */ +extern bool +xen_pif_metrics_get_all(xen_session *session, struct xen_pif_metrics_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_pif_metrics_decl.h b/tools/libxen/include/xen/api/xen_pif_metrics_decl.h new file mode 100644 index 0000000..f6ef2fd --- /dev/null +++ b/tools/libxen/include/xen/api/xen_pif_metrics_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_PIF_METRICS_DECL_H +#define XEN_PIF_METRICS_DECL_H + +typedef void *xen_pif_metrics; + +struct xen_pif_metrics_set; +struct xen_pif_metrics_record; +struct xen_pif_metrics_record_set; +struct xen_pif_metrics_record_opt; +struct xen_pif_metrics_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_sr.h b/tools/libxen/include/xen/api/xen_sr.h new file mode 100644 index 0000000..08204a2 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_sr.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_SR_H +#define XEN_SR_H + +#include +#include +#include +#include +#include + + +/* + * The SR class. + * + * A storage repository. + */ + + +/** + * Free the given xen_sr. The given handle must have been allocated by + * this library. + */ +extern void +xen_sr_free(xen_sr sr); + + +typedef struct xen_sr_set +{ + size_t size; + xen_sr *contents[]; +} xen_sr_set; + +/** + * Allocate a xen_sr_set of the given size. + */ +extern xen_sr_set * +xen_sr_set_alloc(size_t size); + +/** + * Free the given xen_sr_set. The given set must have been allocated + * by this library. + */ +extern void +xen_sr_set_free(xen_sr_set *set); + + +typedef struct xen_sr_record +{ + xen_sr handle; + char *uuid; + char *name_label; + char *name_description; + struct xen_vdi_record_opt_set *vdis; + struct xen_pbd_record_opt_set *pbds; + int64_t virtual_allocation; + int64_t physical_utilisation; + int64_t physical_size; + char *type; + char *content_type; +} xen_sr_record; + +/** + * Allocate a xen_sr_record. + */ +extern xen_sr_record * +xen_sr_record_alloc(void); + +/** + * Free the given xen_sr_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_sr_record_free(xen_sr_record *record); + + +typedef struct xen_sr_record_opt +{ + bool is_record; + union + { + xen_sr handle; + xen_sr_record *record; + } u; +} xen_sr_record_opt; + +/** + * Allocate a xen_sr_record_opt. + */ +extern xen_sr_record_opt * +xen_sr_record_opt_alloc(void); + +/** + * Free the given xen_sr_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_sr_record_opt_free(xen_sr_record_opt *record_opt); + + +typedef struct xen_sr_record_set +{ + size_t size; + xen_sr_record *contents[]; +} xen_sr_record_set; + +/** + * Allocate a xen_sr_record_set of the given size. + */ +extern xen_sr_record_set * +xen_sr_record_set_alloc(size_t size); + +/** + * Free the given xen_sr_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_sr_record_set_free(xen_sr_record_set *set); + + + +typedef struct xen_sr_record_opt_set +{ + size_t size; + xen_sr_record_opt *contents[]; +} xen_sr_record_opt_set; + +/** + * Allocate a xen_sr_record_opt_set of the given size. + */ +extern xen_sr_record_opt_set * +xen_sr_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_sr_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_sr_record_opt_set_free(xen_sr_record_opt_set *set); + + +/** + * Get a record containing the current state of the given SR. + */ +extern bool +xen_sr_get_record(xen_session *session, xen_sr_record **result, xen_sr sr); + + +/** + * Get a reference to the SR instance with the specified UUID. + */ +extern bool +xen_sr_get_by_uuid(xen_session *session, xen_sr *result, char *uuid); + + +/** + * Get all the SR instances with the given label. + */ +extern bool +xen_sr_get_by_name_label(xen_session *session, struct xen_sr_set **result, char *label); + + +/** + * Get the uuid field of the given SR. + */ +extern bool +xen_sr_get_uuid(xen_session *session, char **result, xen_sr sr); + + +/** + * Get the name/label field of the given SR. + */ +extern bool +xen_sr_get_name_label(xen_session *session, char **result, xen_sr sr); + + +/** + * Get the name/description field of the given SR. + */ +extern bool +xen_sr_get_name_description(xen_session *session, char **result, xen_sr sr); + + +/** + * Get the VDIs field of the given SR. + */ +extern bool +xen_sr_get_vdis(xen_session *session, struct xen_vdi_set **result, xen_sr sr); + + +/** + * Get the PBDs field of the given SR. + */ +extern bool +xen_sr_get_pbds(xen_session *session, struct xen_pbd_set **result, xen_sr sr); + + +/** + * Get the virtual_allocation field of the given SR. + */ +extern bool +xen_sr_get_virtual_allocation(xen_session *session, int64_t *result, xen_sr sr); + + +/** + * Get the physical_utilisation field of the given SR. + */ +extern bool +xen_sr_get_physical_utilisation(xen_session *session, int64_t *result, xen_sr sr); + + +/** + * Get the physical_size field of the given SR. + */ +extern bool +xen_sr_get_physical_size(xen_session *session, int64_t *result, xen_sr sr); + + +/** + * Get the type field of the given SR. + */ +extern bool +xen_sr_get_type(xen_session *session, char **result, xen_sr sr); + + +/** + * Get the content_type field of the given SR. + */ +extern bool +xen_sr_get_content_type(xen_session *session, char **result, xen_sr sr); + + +/** + * Set the name/label field of the given SR. + */ +extern bool +xen_sr_set_name_label(xen_session *session, xen_sr sr, char *label); + + +/** + * Set the name/description field of the given SR. + */ +extern bool +xen_sr_set_name_description(xen_session *session, xen_sr sr, char *description); + + +/** + * Return a set of all the SR types supported by the system. + */ +extern bool +xen_sr_get_supported_types(xen_session *session, struct xen_string_set **result); + + +/** + * Return a list of all the SRs known to the system. + */ +extern bool +xen_sr_get_all(xen_session *session, struct xen_sr_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_sr_decl.h b/tools/libxen/include/xen/api/xen_sr_decl.h new file mode 100644 index 0000000..ae7d4f6 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_sr_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_SR_DECL_H +#define XEN_SR_DECL_H + +typedef void *xen_sr; + +struct xen_sr_set; +struct xen_sr_record; +struct xen_sr_record_set; +struct xen_sr_record_opt; +struct xen_sr_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_string_set.h b/tools/libxen/include/xen/api/xen_string_set.h new file mode 100644 index 0000000..a14af94 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_string_set.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_STRING_SET_H +#define XEN_STRING_SET_H + + +#include "xen_common.h" + + +typedef struct xen_string_set +{ + size_t size; + char *contents[]; +} xen_string_set; + + +/** + * Allocate a xen_string_set of the given size. + */ +extern xen_string_set * +xen_string_set_alloc(size_t size); + +/** + * Free the given xen_string_set. The given set must have been allocated + * by this library. + */ +extern void +xen_string_set_free(xen_string_set *set); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_string_string_map.h b/tools/libxen/include/xen/api/xen_string_string_map.h new file mode 100644 index 0000000..d800442 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_string_string_map.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_STRING_STRING_MAP_H +#define XEN_STRING_STRING_MAP_H + + +#include + + +typedef struct xen_string_string_map_contents +{ + char *key; + char *val; +} xen_string_string_map_contents; + + +typedef struct xen_string_string_map +{ + size_t size; + xen_string_string_map_contents contents[]; +} xen_string_string_map; + +/** + * Allocate a xen_string_string_map of the given size. + */ +extern xen_string_string_map * +xen_string_string_map_alloc(size_t size); + +/** + * Free the given xen_string_string_map, and all referenced values. + * The given map must have been allocated by this library. + */ +extern void +xen_string_string_map_free(xen_string_string_map *map); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_user.h b/tools/libxen/include/xen/api/xen_user.h new file mode 100644 index 0000000..a6d9dd9 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_user.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_USER_H +#define XEN_USER_H + +#include +#include + + +/* + * The user class. + * + * A user of the system. + */ + + +/** + * Free the given xen_user. The given handle must have been allocated + * by this library. + */ +extern void +xen_user_free(xen_user user); + + +typedef struct xen_user_set +{ + size_t size; + xen_user *contents[]; +} xen_user_set; + +/** + * Allocate a xen_user_set of the given size. + */ +extern xen_user_set * +xen_user_set_alloc(size_t size); + +/** + * Free the given xen_user_set. The given set must have been allocated + * by this library. + */ +extern void +xen_user_set_free(xen_user_set *set); + + +typedef struct xen_user_record +{ + xen_user handle; + char *uuid; + char *short_name; + char *fullname; +} xen_user_record; + +/** + * Allocate a xen_user_record. + */ +extern xen_user_record * +xen_user_record_alloc(void); + +/** + * Free the given xen_user_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_user_record_free(xen_user_record *record); + + +typedef struct xen_user_record_opt +{ + bool is_record; + union + { + xen_user handle; + xen_user_record *record; + } u; +} xen_user_record_opt; + +/** + * Allocate a xen_user_record_opt. + */ +extern xen_user_record_opt * +xen_user_record_opt_alloc(void); + +/** + * Free the given xen_user_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_user_record_opt_free(xen_user_record_opt *record_opt); + + +typedef struct xen_user_record_set +{ + size_t size; + xen_user_record *contents[]; +} xen_user_record_set; + +/** + * Allocate a xen_user_record_set of the given size. + */ +extern xen_user_record_set * +xen_user_record_set_alloc(size_t size); + +/** + * Free the given xen_user_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_user_record_set_free(xen_user_record_set *set); + + + +typedef struct xen_user_record_opt_set +{ + size_t size; + xen_user_record_opt *contents[]; +} xen_user_record_opt_set; + +/** + * Allocate a xen_user_record_opt_set of the given size. + */ +extern xen_user_record_opt_set * +xen_user_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_user_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_user_record_opt_set_free(xen_user_record_opt_set *set); + + +/** + * Get a record containing the current state of the given user. + */ +extern bool +xen_user_get_record(xen_session *session, xen_user_record **result, xen_user user); + + +/** + * Get a reference to the user instance with the specified UUID. + */ +extern bool +xen_user_get_by_uuid(xen_session *session, xen_user *result, char *uuid); + + +/** + * Create a new user instance, and return its handle. + */ +extern bool +xen_user_create(xen_session *session, xen_user *result, xen_user_record *record); + + +/** + * Destroy the specified user instance. + */ +extern bool +xen_user_destroy(xen_session *session, xen_user user); + + +/** + * Get the uuid field of the given user. + */ +extern bool +xen_user_get_uuid(xen_session *session, char **result, xen_user user); + + +/** + * Get the short_name field of the given user. + */ +extern bool +xen_user_get_short_name(xen_session *session, char **result, xen_user user); + + +/** + * Get the fullname field of the given user. + */ +extern bool +xen_user_get_fullname(xen_session *session, char **result, xen_user user); + + +/** + * Set the fullname field of the given user. + */ +extern bool +xen_user_set_fullname(xen_session *session, xen_user user, char *fullname); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_user_decl.h b/tools/libxen/include/xen/api/xen_user_decl.h new file mode 100644 index 0000000..723a8bf --- /dev/null +++ b/tools/libxen/include/xen/api/xen_user_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_USER_DECL_H +#define XEN_USER_DECL_H + +typedef void *xen_user; + +struct xen_user_set; +struct xen_user_record; +struct xen_user_record_set; +struct xen_user_record_opt; +struct xen_user_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vbd.h b/tools/libxen/include/xen/api/xen_vbd.h new file mode 100644 index 0000000..9806c5e --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vbd.h @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VBD_H +#define XEN_VBD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * The VBD class. + * + * A virtual block device. + */ + + +/** + * Free the given xen_vbd. The given handle must have been allocated + * by this library. + */ +extern void +xen_vbd_free(xen_vbd vbd); + + +typedef struct xen_vbd_set +{ + size_t size; + xen_vbd *contents[]; +} xen_vbd_set; + +/** + * Allocate a xen_vbd_set of the given size. + */ +extern xen_vbd_set * +xen_vbd_set_alloc(size_t size); + +/** + * Free the given xen_vbd_set. The given set must have been allocated + * by this library. + */ +extern void +xen_vbd_set_free(xen_vbd_set *set); + + +typedef struct xen_vbd_record +{ + xen_vbd handle; + char *uuid; + struct xen_vm_record_opt *vm; + struct xen_vdi_record_opt *vdi; + char *device; + bool bootable; + enum xen_vbd_mode mode; + enum xen_vbd_type type; + bool currently_attached; + int64_t status_code; + char *status_detail; + xen_string_string_map *runtime_properties; + char *qos_algorithm_type; + xen_string_string_map *qos_algorithm_params; + struct xen_string_set *qos_supported_algorithms; + struct xen_vbd_metrics_record_opt *metrics; +} xen_vbd_record; + +/** + * Allocate a xen_vbd_record. + */ +extern xen_vbd_record * +xen_vbd_record_alloc(void); + +/** + * Free the given xen_vbd_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_vbd_record_free(xen_vbd_record *record); + + +typedef struct xen_vbd_record_opt +{ + bool is_record; + union + { + xen_vbd handle; + xen_vbd_record *record; + } u; +} xen_vbd_record_opt; + +/** + * Allocate a xen_vbd_record_opt. + */ +extern xen_vbd_record_opt * +xen_vbd_record_opt_alloc(void); + +/** + * Free the given xen_vbd_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_vbd_record_opt_free(xen_vbd_record_opt *record_opt); + + +typedef struct xen_vbd_record_set +{ + size_t size; + xen_vbd_record *contents[]; +} xen_vbd_record_set; + +/** + * Allocate a xen_vbd_record_set of the given size. + */ +extern xen_vbd_record_set * +xen_vbd_record_set_alloc(size_t size); + +/** + * Free the given xen_vbd_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_vbd_record_set_free(xen_vbd_record_set *set); + + + +typedef struct xen_vbd_record_opt_set +{ + size_t size; + xen_vbd_record_opt *contents[]; +} xen_vbd_record_opt_set; + +/** + * Allocate a xen_vbd_record_opt_set of the given size. + */ +extern xen_vbd_record_opt_set * +xen_vbd_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vbd_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_vbd_record_opt_set_free(xen_vbd_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VBD. + */ +extern bool +xen_vbd_get_record(xen_session *session, xen_vbd_record **result, xen_vbd vbd); + + +/** + * Get a reference to the VBD instance with the specified UUID. + */ +extern bool +xen_vbd_get_by_uuid(xen_session *session, xen_vbd *result, char *uuid); + + +/** + * Create a new VBD instance, and return its handle. + */ +extern bool +xen_vbd_create(xen_session *session, xen_vbd *result, xen_vbd_record *record); + + +/** + * Destroy the specified VBD instance. + */ +extern bool +xen_vbd_destroy(xen_session *session, xen_vbd vbd); + + +/** + * Get the uuid field of the given VBD. + */ +extern bool +xen_vbd_get_uuid(xen_session *session, char **result, xen_vbd vbd); + + +/** + * Get the VM field of the given VBD. + */ +extern bool +xen_vbd_get_vm(xen_session *session, xen_vm *result, xen_vbd vbd); + + +/** + * Get the VDI field of the given VBD. + */ +extern bool +xen_vbd_get_vdi(xen_session *session, xen_vdi *result, xen_vbd vbd); + + +/** + * Get the device field of the given VBD. + */ +extern bool +xen_vbd_get_device(xen_session *session, char **result, xen_vbd vbd); + + +/** + * Get the bootable field of the given VBD. + */ +extern bool +xen_vbd_get_bootable(xen_session *session, bool *result, xen_vbd vbd); + + +/** + * Get the mode field of the given VBD. + */ +extern bool +xen_vbd_get_mode(xen_session *session, enum xen_vbd_mode *result, xen_vbd vbd); + + +/** + * Get the type field of the given VBD. + */ +extern bool +xen_vbd_get_type(xen_session *session, enum xen_vbd_type *result, xen_vbd vbd); + + +/** + * Get the currently_attached field of the given VBD. + */ +extern bool +xen_vbd_get_currently_attached(xen_session *session, bool *result, xen_vbd vbd); + + +/** + * Get the status_code field of the given VBD. + */ +extern bool +xen_vbd_get_status_code(xen_session *session, int64_t *result, xen_vbd vbd); + + +/** + * Get the status_detail field of the given VBD. + */ +extern bool +xen_vbd_get_status_detail(xen_session *session, char **result, xen_vbd vbd); + + +/** + * Get the runtime_properties field of the given VBD. + */ +extern bool +xen_vbd_get_runtime_properties(xen_session *session, xen_string_string_map **result, xen_vbd vbd); + + +/** + * Get the qos/algorithm_type field of the given VBD. + */ +extern bool +xen_vbd_get_qos_algorithm_type(xen_session *session, char **result, xen_vbd vbd); + + +/** + * Get the qos/algorithm_params field of the given VBD. + */ +extern bool +xen_vbd_get_qos_algorithm_params(xen_session *session, xen_string_string_map **result, xen_vbd vbd); + + +/** + * Get the qos/supported_algorithms field of the given VBD. + */ +extern bool +xen_vbd_get_qos_supported_algorithms(xen_session *session, struct xen_string_set **result, xen_vbd vbd); + + +/** + * Get the metrics field of the given VBD. + */ +extern bool +xen_vbd_get_metrics(xen_session *session, xen_vbd_metrics *result, xen_vbd vbd); + + +/** + * Set the device field of the given VBD. + */ +extern bool +xen_vbd_set_device(xen_session *session, xen_vbd vbd, char *device); + + +/** + * Set the bootable field of the given VBD. + */ +extern bool +xen_vbd_set_bootable(xen_session *session, xen_vbd vbd, bool bootable); + + +/** + * Set the mode field of the given VBD. + */ +extern bool +xen_vbd_set_mode(xen_session *session, xen_vbd vbd, enum xen_vbd_mode mode); + + +/** + * Set the type field of the given VBD. + */ +extern bool +xen_vbd_set_type(xen_session *session, xen_vbd vbd, enum xen_vbd_type type); + + +/** + * Set the qos/algorithm_type field of the given VBD. + */ +extern bool +xen_vbd_set_qos_algorithm_type(xen_session *session, xen_vbd vbd, char *algorithm_type); + + +/** + * Set the qos/algorithm_params field of the given VBD. + */ +extern bool +xen_vbd_set_qos_algorithm_params(xen_session *session, xen_vbd vbd, xen_string_string_map *algorithm_params); + + +/** + * Add the given key-value pair to the qos/algorithm_params field of + * the given VBD. + */ +extern bool +xen_vbd_add_to_qos_algorithm_params(xen_session *session, xen_vbd vbd, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * qos/algorithm_params field of the given VBD. If the key is not in that + * Map, then do nothing. + */ +extern bool +xen_vbd_remove_from_qos_algorithm_params(xen_session *session, xen_vbd vbd, char *key); + + +/** + * Change the media in the device for CDROM-like devices only. For + * other devices, detach the VBD and attach a new one. + */ +extern bool +xen_vbd_media_change(xen_session *session, xen_vbd vbd, xen_vdi vdi); + + +/** + * Hotplug the specified VBD, dynamically attaching it to the running + * VM. + */ +extern bool +xen_vbd_plug(xen_session *session, xen_vbd self); + + +/** + * Hot-unplug the specified VBD, dynamically unattaching it from the + * running VM. + */ +extern bool +xen_vbd_unplug(xen_session *session, xen_vbd self); + + +/** + * Return a list of all the VBDs known to the system. + */ +extern bool +xen_vbd_get_all(xen_session *session, struct xen_vbd_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vbd_decl.h b/tools/libxen/include/xen/api/xen_vbd_decl.h new file mode 100644 index 0000000..018af3b --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vbd_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VBD_DECL_H +#define XEN_VBD_DECL_H + +typedef void *xen_vbd; + +struct xen_vbd_set; +struct xen_vbd_record; +struct xen_vbd_record_set; +struct xen_vbd_record_opt; +struct xen_vbd_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vbd_metrics.h b/tools/libxen/include/xen/api/xen_vbd_metrics.h new file mode 100644 index 0000000..1cb1a2b --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vbd_metrics.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VBD_METRICS_H +#define XEN_VBD_METRICS_H + +#include +#include + + +/* + * The VBD_metrics class. + * + * The metrics associated with a virtual block device. + */ + + +/** + * Free the given xen_vbd_metrics. The given handle must have been + * allocated by this library. + */ +extern void +xen_vbd_metrics_free(xen_vbd_metrics vbd_metrics); + + +typedef struct xen_vbd_metrics_set +{ + size_t size; + xen_vbd_metrics *contents[]; +} xen_vbd_metrics_set; + +/** + * Allocate a xen_vbd_metrics_set of the given size. + */ +extern xen_vbd_metrics_set * +xen_vbd_metrics_set_alloc(size_t size); + +/** + * Free the given xen_vbd_metrics_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vbd_metrics_set_free(xen_vbd_metrics_set *set); + + +typedef struct xen_vbd_metrics_record +{ + xen_vbd_metrics handle; + char *uuid; + double io_read_kbs; + double io_write_kbs; + time_t last_updated; +} xen_vbd_metrics_record; + +/** + * Allocate a xen_vbd_metrics_record. + */ +extern xen_vbd_metrics_record * +xen_vbd_metrics_record_alloc(void); + +/** + * Free the given xen_vbd_metrics_record, and all referenced values. + * The given record must have been allocated by this library. + */ +extern void +xen_vbd_metrics_record_free(xen_vbd_metrics_record *record); + + +typedef struct xen_vbd_metrics_record_opt +{ + bool is_record; + union + { + xen_vbd_metrics handle; + xen_vbd_metrics_record *record; + } u; +} xen_vbd_metrics_record_opt; + +/** + * Allocate a xen_vbd_metrics_record_opt. + */ +extern xen_vbd_metrics_record_opt * +xen_vbd_metrics_record_opt_alloc(void); + +/** + * Free the given xen_vbd_metrics_record_opt, and all referenced + * values. The given record_opt must have been allocated by this library. + */ +extern void +xen_vbd_metrics_record_opt_free(xen_vbd_metrics_record_opt *record_opt); + + +typedef struct xen_vbd_metrics_record_set +{ + size_t size; + xen_vbd_metrics_record *contents[]; +} xen_vbd_metrics_record_set; + +/** + * Allocate a xen_vbd_metrics_record_set of the given size. + */ +extern xen_vbd_metrics_record_set * +xen_vbd_metrics_record_set_alloc(size_t size); + +/** + * Free the given xen_vbd_metrics_record_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_vbd_metrics_record_set_free(xen_vbd_metrics_record_set *set); + + + +typedef struct xen_vbd_metrics_record_opt_set +{ + size_t size; + xen_vbd_metrics_record_opt *contents[]; +} xen_vbd_metrics_record_opt_set; + +/** + * Allocate a xen_vbd_metrics_record_opt_set of the given size. + */ +extern xen_vbd_metrics_record_opt_set * +xen_vbd_metrics_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vbd_metrics_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_vbd_metrics_record_opt_set_free(xen_vbd_metrics_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VBD_metrics. + */ +extern bool +xen_vbd_metrics_get_record(xen_session *session, xen_vbd_metrics_record **result, xen_vbd_metrics vbd_metrics); + + +/** + * Get a reference to the VBD_metrics instance with the specified UUID. + */ +extern bool +xen_vbd_metrics_get_by_uuid(xen_session *session, xen_vbd_metrics *result, char *uuid); + + +/** + * Get the uuid field of the given VBD_metrics. + */ +extern bool +xen_vbd_metrics_get_uuid(xen_session *session, char **result, xen_vbd_metrics vbd_metrics); + + +/** + * Get the io/read_kbs field of the given VBD_metrics. + */ +extern bool +xen_vbd_metrics_get_io_read_kbs(xen_session *session, double *result, xen_vbd_metrics vbd_metrics); + + +/** + * Get the io/write_kbs field of the given VBD_metrics. + */ +extern bool +xen_vbd_metrics_get_io_write_kbs(xen_session *session, double *result, xen_vbd_metrics vbd_metrics); + + +/** + * Get the last_updated field of the given VBD_metrics. + */ +extern bool +xen_vbd_metrics_get_last_updated(xen_session *session, time_t *result, xen_vbd_metrics vbd_metrics); + + +/** + * Return a list of all the VBD_metrics instances known to the system. + */ +extern bool +xen_vbd_metrics_get_all(xen_session *session, struct xen_vbd_metrics_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vbd_metrics_decl.h b/tools/libxen/include/xen/api/xen_vbd_metrics_decl.h new file mode 100644 index 0000000..501aa8f --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vbd_metrics_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VBD_METRICS_DECL_H +#define XEN_VBD_METRICS_DECL_H + +typedef void *xen_vbd_metrics; + +struct xen_vbd_metrics_set; +struct xen_vbd_metrics_record; +struct xen_vbd_metrics_record_set; +struct xen_vbd_metrics_record_opt; +struct xen_vbd_metrics_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vbd_mode.h b/tools/libxen/include/xen/api/xen_vbd_mode.h new file mode 100644 index 0000000..8cab27e --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vbd_mode.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VBD_MODE_H +#define XEN_VBD_MODE_H + + +#include + + +enum xen_vbd_mode +{ + /** + * disk is mounted read-only + */ + XEN_VBD_MODE_RO, + + /** + * disk is mounted read-write + */ + XEN_VBD_MODE_RW +}; + + +typedef struct xen_vbd_mode_set +{ + size_t size; + enum xen_vbd_mode contents[]; +} xen_vbd_mode_set; + +/** + * Allocate a xen_vbd_mode_set of the given size. + */ +extern xen_vbd_mode_set * +xen_vbd_mode_set_alloc(size_t size); + +/** + * Free the given xen_vbd_mode_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vbd_mode_set_free(xen_vbd_mode_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_vbd_mode_to_string(enum xen_vbd_mode val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_vbd_mode +xen_vbd_mode_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vbd_type.h b/tools/libxen/include/xen/api/xen_vbd_type.h new file mode 100644 index 0000000..d19aef8 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vbd_type.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VBD_TYPE_H +#define XEN_VBD_TYPE_H + + +#include + + +enum xen_vbd_type +{ + /** + * VBD will appear to guest as CD + */ + XEN_VBD_TYPE_CD, + + /** + * VBD will appear to guest as disk + */ + XEN_VBD_TYPE_DISK +}; + + +typedef struct xen_vbd_type_set +{ + size_t size; + enum xen_vbd_type contents[]; +} xen_vbd_type_set; + +/** + * Allocate a xen_vbd_type_set of the given size. + */ +extern xen_vbd_type_set * +xen_vbd_type_set_alloc(size_t size); + +/** + * Free the given xen_vbd_type_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vbd_type_set_free(xen_vbd_type_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_vbd_type_to_string(enum xen_vbd_type val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_vbd_type +xen_vbd_type_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vdi.h b/tools/libxen/include/xen/api/xen_vdi.h new file mode 100644 index 0000000..a6ad303 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vdi.h @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VDI_H +#define XEN_VDI_H + +#include +#include +#include +#include +#include +#include +#include + + +/* + * The VDI class. + * + * A virtual disk image. + */ + + +/** + * Free the given xen_vdi. The given handle must have been allocated + * by this library. + */ +extern void +xen_vdi_free(xen_vdi vdi); + + +typedef struct xen_vdi_set +{ + size_t size; + xen_vdi *contents[]; +} xen_vdi_set; + +/** + * Allocate a xen_vdi_set of the given size. + */ +extern xen_vdi_set * +xen_vdi_set_alloc(size_t size); + +/** + * Free the given xen_vdi_set. The given set must have been allocated + * by this library. + */ +extern void +xen_vdi_set_free(xen_vdi_set *set); + + +typedef struct xen_vdi_record +{ + xen_vdi handle; + char *uuid; + char *name_label; + char *name_description; + struct xen_sr_record_opt *sr; + struct xen_vbd_record_opt_set *vbds; + struct xen_crashdump_record_opt_set *crash_dumps; + int64_t virtual_size; + int64_t physical_utilisation; + enum xen_vdi_type type; + bool sharable; + bool read_only; + xen_string_string_map *other_config; +} xen_vdi_record; + +/** + * Allocate a xen_vdi_record. + */ +extern xen_vdi_record * +xen_vdi_record_alloc(void); + +/** + * Free the given xen_vdi_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_vdi_record_free(xen_vdi_record *record); + + +typedef struct xen_vdi_record_opt +{ + bool is_record; + union + { + xen_vdi handle; + xen_vdi_record *record; + } u; +} xen_vdi_record_opt; + +/** + * Allocate a xen_vdi_record_opt. + */ +extern xen_vdi_record_opt * +xen_vdi_record_opt_alloc(void); + +/** + * Free the given xen_vdi_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_vdi_record_opt_free(xen_vdi_record_opt *record_opt); + + +typedef struct xen_vdi_record_set +{ + size_t size; + xen_vdi_record *contents[]; +} xen_vdi_record_set; + +/** + * Allocate a xen_vdi_record_set of the given size. + */ +extern xen_vdi_record_set * +xen_vdi_record_set_alloc(size_t size); + +/** + * Free the given xen_vdi_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_vdi_record_set_free(xen_vdi_record_set *set); + + + +typedef struct xen_vdi_record_opt_set +{ + size_t size; + xen_vdi_record_opt *contents[]; +} xen_vdi_record_opt_set; + +/** + * Allocate a xen_vdi_record_opt_set of the given size. + */ +extern xen_vdi_record_opt_set * +xen_vdi_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vdi_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_vdi_record_opt_set_free(xen_vdi_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VDI. + */ +extern bool +xen_vdi_get_record(xen_session *session, xen_vdi_record **result, xen_vdi vdi); + + +/** + * Get a reference to the VDI instance with the specified UUID. + */ +extern bool +xen_vdi_get_by_uuid(xen_session *session, xen_vdi *result, char *uuid); + + +/** + * Create a new VDI instance, and return its handle. + */ +extern bool +xen_vdi_create(xen_session *session, xen_vdi *result, xen_vdi_record *record); + + +/** + * Destroy the specified VDI instance. + */ +extern bool +xen_vdi_destroy(xen_session *session, xen_vdi vdi); + + +/** + * Get all the VDI instances with the given label. + */ +extern bool +xen_vdi_get_by_name_label(xen_session *session, struct xen_vdi_set **result, char *label); + + +/** + * Get the uuid field of the given VDI. + */ +extern bool +xen_vdi_get_uuid(xen_session *session, char **result, xen_vdi vdi); + + +/** + * Get the name/label field of the given VDI. + */ +extern bool +xen_vdi_get_name_label(xen_session *session, char **result, xen_vdi vdi); + + +/** + * Get the name/description field of the given VDI. + */ +extern bool +xen_vdi_get_name_description(xen_session *session, char **result, xen_vdi vdi); + + +/** + * Get the SR field of the given VDI. + */ +extern bool +xen_vdi_get_sr(xen_session *session, xen_sr *result, xen_vdi vdi); + + +/** + * Get the VBDs field of the given VDI. + */ +extern bool +xen_vdi_get_vbds(xen_session *session, struct xen_vbd_set **result, xen_vdi vdi); + + +/** + * Get the crash_dumps field of the given VDI. + */ +extern bool +xen_vdi_get_crash_dumps(xen_session *session, struct xen_crashdump_set **result, xen_vdi vdi); + + +/** + * Get the virtual_size field of the given VDI. + */ +extern bool +xen_vdi_get_virtual_size(xen_session *session, int64_t *result, xen_vdi vdi); + + +/** + * Get the physical_utilisation field of the given VDI. + */ +extern bool +xen_vdi_get_physical_utilisation(xen_session *session, int64_t *result, xen_vdi vdi); + + +/** + * Get the type field of the given VDI. + */ +extern bool +xen_vdi_get_type(xen_session *session, enum xen_vdi_type *result, xen_vdi vdi); + + +/** + * Get the sharable field of the given VDI. + */ +extern bool +xen_vdi_get_sharable(xen_session *session, bool *result, xen_vdi vdi); + + +/** + * Get the read_only field of the given VDI. + */ +extern bool +xen_vdi_get_read_only(xen_session *session, bool *result, xen_vdi vdi); + + +/** + * Get the other_config field of the given VDI. + */ +extern bool +xen_vdi_get_other_config(xen_session *session, xen_string_string_map **result, xen_vdi vdi); + + +/** + * Set the name/label field of the given VDI. + */ +extern bool +xen_vdi_set_name_label(xen_session *session, xen_vdi vdi, char *label); + + +/** + * Set the name/description field of the given VDI. + */ +extern bool +xen_vdi_set_name_description(xen_session *session, xen_vdi vdi, char *description); + + +/** + * Set the virtual_size field of the given VDI. + */ +extern bool +xen_vdi_set_virtual_size(xen_session *session, xen_vdi vdi, int64_t virtual_size); + + +/** + * Set the sharable field of the given VDI. + */ +extern bool +xen_vdi_set_sharable(xen_session *session, xen_vdi vdi, bool sharable); + + +/** + * Set the read_only field of the given VDI. + */ +extern bool +xen_vdi_set_read_only(xen_session *session, xen_vdi vdi, bool read_only); + + +/** + * Set the other_config field of the given VDI. + */ +extern bool +xen_vdi_set_other_config(xen_session *session, xen_vdi vdi, xen_string_string_map *other_config); + + +/** + * Add the given key-value pair to the other_config field of the given + * VDI. + */ +extern bool +xen_vdi_add_to_other_config(xen_session *session, xen_vdi vdi, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * other_config field of the given VDI. If the key is not in that Map, then + * do nothing. + */ +extern bool +xen_vdi_remove_from_other_config(xen_session *session, xen_vdi vdi, char *key); + + +/** + * Return a list of all the VDIs known to the system. + */ +extern bool +xen_vdi_get_all(xen_session *session, struct xen_vdi_set **result); + + +/** + * Set the security label of a VDI. + */ +extern bool +xen_vdi_set_security_label(xen_session *session, int64_t *result, xen_vdi vdi, + char *label, char *oldlabel); + +/** + * Get the security label of a VDI. + */ +extern bool +xen_vdi_get_security_label(xen_session *session, char **result, xen_vdi vdi); + +#endif diff --git a/tools/libxen/include/xen/api/xen_vdi_decl.h b/tools/libxen/include/xen/api/xen_vdi_decl.h new file mode 100644 index 0000000..13a328e --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vdi_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VDI_DECL_H +#define XEN_VDI_DECL_H + +typedef void *xen_vdi; + +struct xen_vdi_set; +struct xen_vdi_record; +struct xen_vdi_record_set; +struct xen_vdi_record_opt; +struct xen_vdi_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vdi_type.h b/tools/libxen/include/xen/api/xen_vdi_type.h new file mode 100644 index 0000000..d98a2ab --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vdi_type.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VDI_TYPE_H +#define XEN_VDI_TYPE_H + + +#include + + +enum xen_vdi_type +{ + /** + * a disk that may be replaced on upgrade + */ + XEN_VDI_TYPE_SYSTEM, + + /** + * a disk that is always preserved on upgrade + */ + XEN_VDI_TYPE_USER, + + /** + * a disk that may be reformatted on upgrade + */ + XEN_VDI_TYPE_EPHEMERAL, + + /** + * a disk that stores a suspend image + */ + XEN_VDI_TYPE_SUSPEND, + + /** + * a disk that stores VM crashdump information + */ + XEN_VDI_TYPE_CRASHDUMP +}; + + +typedef struct xen_vdi_type_set +{ + size_t size; + enum xen_vdi_type contents[]; +} xen_vdi_type_set; + +/** + * Allocate a xen_vdi_type_set of the given size. + */ +extern xen_vdi_type_set * +xen_vdi_type_set_alloc(size_t size); + +/** + * Free the given xen_vdi_type_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vdi_type_set_free(xen_vdi_type_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_vdi_type_to_string(enum xen_vdi_type val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_vdi_type +xen_vdi_type_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vif.h b/tools/libxen/include/xen/api/xen_vif.h new file mode 100644 index 0000000..3fb8d71 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vif.h @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VIF_H +#define XEN_VIF_H + +#include +#include +#include +#include +#include +#include +#include + + +/* + * The VIF class. + * + * A virtual network interface. + */ + + +/** + * Free the given xen_vif. The given handle must have been allocated + * by this library. + */ +extern void +xen_vif_free(xen_vif vif); + + +typedef struct xen_vif_set +{ + size_t size; + xen_vif *contents[]; +} xen_vif_set; + +/** + * Allocate a xen_vif_set of the given size. + */ +extern xen_vif_set * +xen_vif_set_alloc(size_t size); + +/** + * Free the given xen_vif_set. The given set must have been allocated + * by this library. + */ +extern void +xen_vif_set_free(xen_vif_set *set); + + +typedef struct xen_vif_record +{ + xen_vif handle; + char *uuid; + char *device; + struct xen_network_record_opt *network; + struct xen_vm_record_opt *vm; + char *mac; + int64_t mtu; + bool currently_attached; + int64_t status_code; + char *status_detail; + xen_string_string_map *runtime_properties; + char *qos_algorithm_type; + xen_string_string_map *qos_algorithm_params; + struct xen_string_set *qos_supported_algorithms; + struct xen_vif_metrics_record_opt *metrics; +} xen_vif_record; + +/** + * Allocate a xen_vif_record. + */ +extern xen_vif_record * +xen_vif_record_alloc(void); + +/** + * Free the given xen_vif_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_vif_record_free(xen_vif_record *record); + + +typedef struct xen_vif_record_opt +{ + bool is_record; + union + { + xen_vif handle; + xen_vif_record *record; + } u; +} xen_vif_record_opt; + +/** + * Allocate a xen_vif_record_opt. + */ +extern xen_vif_record_opt * +xen_vif_record_opt_alloc(void); + +/** + * Free the given xen_vif_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_vif_record_opt_free(xen_vif_record_opt *record_opt); + + +typedef struct xen_vif_record_set +{ + size_t size; + xen_vif_record *contents[]; +} xen_vif_record_set; + +/** + * Allocate a xen_vif_record_set of the given size. + */ +extern xen_vif_record_set * +xen_vif_record_set_alloc(size_t size); + +/** + * Free the given xen_vif_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_vif_record_set_free(xen_vif_record_set *set); + + + +typedef struct xen_vif_record_opt_set +{ + size_t size; + xen_vif_record_opt *contents[]; +} xen_vif_record_opt_set; + +/** + * Allocate a xen_vif_record_opt_set of the given size. + */ +extern xen_vif_record_opt_set * +xen_vif_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vif_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_vif_record_opt_set_free(xen_vif_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VIF. + */ +extern bool +xen_vif_get_record(xen_session *session, xen_vif_record **result, xen_vif vif); + + +/** + * Get a reference to the VIF instance with the specified UUID. + */ +extern bool +xen_vif_get_by_uuid(xen_session *session, xen_vif *result, char *uuid); + + +/** + * Create a new VIF instance, and return its handle. + */ +extern bool +xen_vif_create(xen_session *session, xen_vif *result, xen_vif_record *record); + + +/** + * Destroy the specified VIF instance. + */ +extern bool +xen_vif_destroy(xen_session *session, xen_vif vif); + + +/** + * Get the uuid field of the given VIF. + */ +extern bool +xen_vif_get_uuid(xen_session *session, char **result, xen_vif vif); + + +/** + * Get the device field of the given VIF. + */ +extern bool +xen_vif_get_device(xen_session *session, char **result, xen_vif vif); + + +/** + * Get the network field of the given VIF. + */ +extern bool +xen_vif_get_network(xen_session *session, xen_network *result, xen_vif vif); + + +/** + * Get the VM field of the given VIF. + */ +extern bool +xen_vif_get_vm(xen_session *session, xen_vm *result, xen_vif vif); + + +/** + * Get the MAC field of the given VIF. + */ +extern bool +xen_vif_get_mac(xen_session *session, char **result, xen_vif vif); + + +/** + * Get the MTU field of the given VIF. + */ +extern bool +xen_vif_get_mtu(xen_session *session, int64_t *result, xen_vif vif); + + +/** + * Get the currently_attached field of the given VIF. + */ +extern bool +xen_vif_get_currently_attached(xen_session *session, bool *result, xen_vif vif); + + +/** + * Get the status_code field of the given VIF. + */ +extern bool +xen_vif_get_status_code(xen_session *session, int64_t *result, xen_vif vif); + + +/** + * Get the status_detail field of the given VIF. + */ +extern bool +xen_vif_get_status_detail(xen_session *session, char **result, xen_vif vif); + + +/** + * Get the runtime_properties field of the given VIF. + */ +extern bool +xen_vif_get_runtime_properties(xen_session *session, xen_string_string_map **result, xen_vif vif); + + +/** + * Get the qos/algorithm_type field of the given VIF. + */ +extern bool +xen_vif_get_qos_algorithm_type(xen_session *session, char **result, xen_vif vif); + + +/** + * Get the qos/algorithm_params field of the given VIF. + */ +extern bool +xen_vif_get_qos_algorithm_params(xen_session *session, xen_string_string_map **result, xen_vif vif); + + +/** + * Get the qos/supported_algorithms field of the given VIF. + */ +extern bool +xen_vif_get_qos_supported_algorithms(xen_session *session, struct xen_string_set **result, xen_vif vif); + + +/** + * Get the metrics field of the given VIF. + */ +extern bool +xen_vif_get_metrics(xen_session *session, xen_vif_metrics *result, xen_vif vif); + + +/** + * Set the device field of the given VIF. + */ +extern bool +xen_vif_set_device(xen_session *session, xen_vif vif, char *device); + + +/** + * Set the MAC field of the given VIF. + */ +extern bool +xen_vif_set_mac(xen_session *session, xen_vif vif, char *mac); + + +/** + * Set the MTU field of the given VIF. + */ +extern bool +xen_vif_set_mtu(xen_session *session, xen_vif vif, int64_t mtu); + + +/** + * Set the qos/algorithm_type field of the given VIF. + */ +extern bool +xen_vif_set_qos_algorithm_type(xen_session *session, xen_vif vif, char *algorithm_type); + + +/** + * Set the qos/algorithm_params field of the given VIF. + */ +extern bool +xen_vif_set_qos_algorithm_params(xen_session *session, xen_vif vif, xen_string_string_map *algorithm_params); + + +/** + * Add the given key-value pair to the qos/algorithm_params field of + * the given VIF. + */ +extern bool +xen_vif_add_to_qos_algorithm_params(xen_session *session, xen_vif vif, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * qos/algorithm_params field of the given VIF. If the key is not in that + * Map, then do nothing. + */ +extern bool +xen_vif_remove_from_qos_algorithm_params(xen_session *session, xen_vif vif, char *key); + + +/** + * Hotplug the specified VIF, dynamically attaching it to the running + * VM. + */ +extern bool +xen_vif_plug(xen_session *session, xen_vif self); + + +/** + * Hot-unplug the specified VIF, dynamically unattaching it from the + * running VM. + */ +extern bool +xen_vif_unplug(xen_session *session, xen_vif self); + + +/** + * Return a list of all the VIFs known to the system. + */ +extern bool +xen_vif_get_all(xen_session *session, struct xen_vif_set **result); + + +/** + * Set the security label of a VIF. + */ +extern bool +xen_vif_set_security_label(xen_session *session, int64_t *result, xen_vif vif, + char *label, char *oldlabel); + + +/** + * Get the security label of a VIF. + */ +extern bool +xen_vif_get_security_label(xen_session *session, char **result, xen_vif vif); + +#endif diff --git a/tools/libxen/include/xen/api/xen_vif_decl.h b/tools/libxen/include/xen/api/xen_vif_decl.h new file mode 100644 index 0000000..0d4c59d --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vif_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VIF_DECL_H +#define XEN_VIF_DECL_H + +typedef void *xen_vif; + +struct xen_vif_set; +struct xen_vif_record; +struct xen_vif_record_set; +struct xen_vif_record_opt; +struct xen_vif_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vif_metrics.h b/tools/libxen/include/xen/api/xen_vif_metrics.h new file mode 100644 index 0000000..a363137 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vif_metrics.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VIF_METRICS_H +#define XEN_VIF_METRICS_H + +#include +#include + + +/* + * The VIF_metrics class. + * + * The metrics associated with a virtual network device. + */ + + +/** + * Free the given xen_vif_metrics. The given handle must have been + * allocated by this library. + */ +extern void +xen_vif_metrics_free(xen_vif_metrics vif_metrics); + + +typedef struct xen_vif_metrics_set +{ + size_t size; + xen_vif_metrics *contents[]; +} xen_vif_metrics_set; + +/** + * Allocate a xen_vif_metrics_set of the given size. + */ +extern xen_vif_metrics_set * +xen_vif_metrics_set_alloc(size_t size); + +/** + * Free the given xen_vif_metrics_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vif_metrics_set_free(xen_vif_metrics_set *set); + + +typedef struct xen_vif_metrics_record +{ + xen_vif_metrics handle; + char *uuid; + double io_read_kbs; + double io_write_kbs; + time_t last_updated; +} xen_vif_metrics_record; + +/** + * Allocate a xen_vif_metrics_record. + */ +extern xen_vif_metrics_record * +xen_vif_metrics_record_alloc(void); + +/** + * Free the given xen_vif_metrics_record, and all referenced values. + * The given record must have been allocated by this library. + */ +extern void +xen_vif_metrics_record_free(xen_vif_metrics_record *record); + + +typedef struct xen_vif_metrics_record_opt +{ + bool is_record; + union + { + xen_vif_metrics handle; + xen_vif_metrics_record *record; + } u; +} xen_vif_metrics_record_opt; + +/** + * Allocate a xen_vif_metrics_record_opt. + */ +extern xen_vif_metrics_record_opt * +xen_vif_metrics_record_opt_alloc(void); + +/** + * Free the given xen_vif_metrics_record_opt, and all referenced + * values. The given record_opt must have been allocated by this library. + */ +extern void +xen_vif_metrics_record_opt_free(xen_vif_metrics_record_opt *record_opt); + + +typedef struct xen_vif_metrics_record_set +{ + size_t size; + xen_vif_metrics_record *contents[]; +} xen_vif_metrics_record_set; + +/** + * Allocate a xen_vif_metrics_record_set of the given size. + */ +extern xen_vif_metrics_record_set * +xen_vif_metrics_record_set_alloc(size_t size); + +/** + * Free the given xen_vif_metrics_record_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_vif_metrics_record_set_free(xen_vif_metrics_record_set *set); + + + +typedef struct xen_vif_metrics_record_opt_set +{ + size_t size; + xen_vif_metrics_record_opt *contents[]; +} xen_vif_metrics_record_opt_set; + +/** + * Allocate a xen_vif_metrics_record_opt_set of the given size. + */ +extern xen_vif_metrics_record_opt_set * +xen_vif_metrics_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vif_metrics_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_vif_metrics_record_opt_set_free(xen_vif_metrics_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VIF_metrics. + */ +extern bool +xen_vif_metrics_get_record(xen_session *session, xen_vif_metrics_record **result, xen_vif_metrics vif_metrics); + + +/** + * Get a reference to the VIF_metrics instance with the specified UUID. + */ +extern bool +xen_vif_metrics_get_by_uuid(xen_session *session, xen_vif_metrics *result, char *uuid); + + +/** + * Get the uuid field of the given VIF_metrics. + */ +extern bool +xen_vif_metrics_get_uuid(xen_session *session, char **result, xen_vif_metrics vif_metrics); + + +/** + * Get the io/read_kbs field of the given VIF_metrics. + */ +extern bool +xen_vif_metrics_get_io_read_kbs(xen_session *session, double *result, xen_vif_metrics vif_metrics); + + +/** + * Get the io/write_kbs field of the given VIF_metrics. + */ +extern bool +xen_vif_metrics_get_io_write_kbs(xen_session *session, double *result, xen_vif_metrics vif_metrics); + + +/** + * Get the last_updated field of the given VIF_metrics. + */ +extern bool +xen_vif_metrics_get_last_updated(xen_session *session, time_t *result, xen_vif_metrics vif_metrics); + + +/** + * Return a list of all the VIF_metrics instances known to the system. + */ +extern bool +xen_vif_metrics_get_all(xen_session *session, struct xen_vif_metrics_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vif_metrics_decl.h b/tools/libxen/include/xen/api/xen_vif_metrics_decl.h new file mode 100644 index 0000000..5734723 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vif_metrics_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VIF_METRICS_DECL_H +#define XEN_VIF_METRICS_DECL_H + +typedef void *xen_vif_metrics; + +struct xen_vif_metrics_set; +struct xen_vif_metrics_record; +struct xen_vif_metrics_record_set; +struct xen_vif_metrics_record_opt; +struct xen_vif_metrics_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm.h b/tools/libxen/include/xen/api/xen_vm.h new file mode 100644 index 0000000..07b5f51 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm.h @@ -0,0 +1,908 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_H +#define XEN_VM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * The VM class. + * + * A virtual machine (or 'guest'). + */ + + +/** + * Free the given xen_vm. The given handle must have been allocated by + * this library. + */ +extern void +xen_vm_free(xen_vm vm); + + +typedef struct xen_vm_set +{ + size_t size; + xen_vm *contents[]; +} xen_vm_set; + +/** + * Allocate a xen_vm_set of the given size. + */ +extern xen_vm_set * +xen_vm_set_alloc(size_t size); + +/** + * Free the given xen_vm_set. The given set must have been allocated + * by this library. + */ +extern void +xen_vm_set_free(xen_vm_set *set); + + +typedef struct xen_vm_record +{ + xen_vm handle; + char *uuid; + enum xen_vm_power_state power_state; + char *name_label; + char *name_description; + int64_t user_version; + bool is_a_template; + bool auto_power_on; + struct xen_vdi_record_opt *suspend_vdi; + struct xen_host_record_opt *resident_on; + int64_t memory_static_max; + int64_t memory_dynamic_max; + int64_t memory_dynamic_min; + int64_t memory_static_min; + xen_string_string_map *vcpus_params; + int64_t vcpus_max; + int64_t vcpus_at_startup; + enum xen_on_normal_exit actions_after_shutdown; + enum xen_on_normal_exit actions_after_reboot; + enum xen_on_crash_behaviour actions_after_crash; + struct xen_console_record_opt_set *consoles; + struct xen_vif_record_opt_set *vifs; + struct xen_vbd_record_opt_set *vbds; + struct xen_crashdump_record_opt_set *crash_dumps; + struct xen_vtpm_record_opt_set *vtpms; + char *pv_bootloader; + char *pv_kernel; + char *pv_ramdisk; + char *pv_args; + char *pv_bootloader_args; + char *hvm_boot_policy; + xen_string_string_map *hvm_boot_params; + xen_string_string_map *platform; + char *pci_bus; + xen_string_string_map *other_config; + int64_t domid; + bool is_control_domain; + struct xen_vm_metrics_record_opt *metrics; + struct xen_vm_guest_metrics_record_opt *guest_metrics; + char *security_label; +} xen_vm_record; + +/** + * Allocate a xen_vm_record. + */ +extern xen_vm_record * +xen_vm_record_alloc(void); + +/** + * Free the given xen_vm_record, and all referenced values. The given + * record must have been allocated by this library. + */ +extern void +xen_vm_record_free(xen_vm_record *record); + + +typedef struct xen_vm_record_opt +{ + bool is_record; + union + { + xen_vm handle; + xen_vm_record *record; + } u; +} xen_vm_record_opt; + +/** + * Allocate a xen_vm_record_opt. + */ +extern xen_vm_record_opt * +xen_vm_record_opt_alloc(void); + +/** + * Free the given xen_vm_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_vm_record_opt_free(xen_vm_record_opt *record_opt); + + +typedef struct xen_vm_record_set +{ + size_t size; + xen_vm_record *contents[]; +} xen_vm_record_set; + +/** + * Allocate a xen_vm_record_set of the given size. + */ +extern xen_vm_record_set * +xen_vm_record_set_alloc(size_t size); + +/** + * Free the given xen_vm_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_vm_record_set_free(xen_vm_record_set *set); + + + +typedef struct xen_vm_record_opt_set +{ + size_t size; + xen_vm_record_opt *contents[]; +} xen_vm_record_opt_set; + +/** + * Allocate a xen_vm_record_opt_set of the given size. + */ +extern xen_vm_record_opt_set * +xen_vm_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vm_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_vm_record_opt_set_free(xen_vm_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VM. + */ +extern bool +xen_vm_get_record(xen_session *session, xen_vm_record **result, xen_vm vm); + + +/** + * Get a reference to the VM instance with the specified UUID. + */ +extern bool +xen_vm_get_by_uuid(xen_session *session, xen_vm *result, char *uuid); + + +/** + * Create a new VM instance, and return its handle. + */ +extern bool +xen_vm_create(xen_session *session, xen_vm *result, xen_vm_record *record); + + +/** + * Destroy the specified VM. The VM is completely removed from the + * system. This function can only be called when the VM is in the Halted + * State. + */ +extern bool +xen_vm_destroy(xen_session *session, xen_vm vm); + + +/** + * Get all the VM instances with the given label. + */ +extern bool +xen_vm_get_by_name_label(xen_session *session, struct xen_vm_set **result, char *label); + + +/** + * Get the uuid field of the given VM. + */ +extern bool +xen_vm_get_uuid(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the power_state field of the given VM. + */ +extern bool +xen_vm_get_power_state(xen_session *session, enum xen_vm_power_state *result, xen_vm vm); + + +/** + * Get the name/label field of the given VM. + */ +extern bool +xen_vm_get_name_label(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the name/description field of the given VM. + */ +extern bool +xen_vm_get_name_description(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the user_version field of the given VM. + */ +extern bool +xen_vm_get_user_version(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the is_a_template field of the given VM. + */ +extern bool +xen_vm_get_is_a_template(xen_session *session, bool *result, xen_vm vm); + + +/** + * Get the auto_power_on field of the given VM. + */ +extern bool +xen_vm_get_auto_power_on(xen_session *session, bool *result, xen_vm vm); + + +/** + * Get the suspend_VDI field of the given VM. + */ +extern bool +xen_vm_get_suspend_vdi(xen_session *session, xen_vdi *result, xen_vm vm); + + +/** + * Get the resident_on field of the given VM. + */ +extern bool +xen_vm_get_resident_on(xen_session *session, xen_host *result, xen_vm vm); + + +/** + * Get the memory/static_max field of the given VM. + */ +extern bool +xen_vm_get_memory_static_max(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the memory/dynamic_max field of the given VM. + */ +extern bool +xen_vm_get_memory_dynamic_max(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the memory/dynamic_min field of the given VM. + */ +extern bool +xen_vm_get_memory_dynamic_min(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the memory/static_min field of the given VM. + */ +extern bool +xen_vm_get_memory_static_min(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the VCPUs/params field of the given VM. + */ +extern bool +xen_vm_get_vcpus_params(xen_session *session, xen_string_string_map **result, xen_vm vm); + + +/** + * Get the VCPUs/max field of the given VM. + */ +extern bool +xen_vm_get_vcpus_max(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the VCPUs/at_startup field of the given VM. + */ +extern bool +xen_vm_get_vcpus_at_startup(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the actions/after_shutdown field of the given VM. + */ +extern bool +xen_vm_get_actions_after_shutdown(xen_session *session, enum xen_on_normal_exit *result, xen_vm vm); + + +/** + * Get the actions/after_reboot field of the given VM. + */ +extern bool +xen_vm_get_actions_after_reboot(xen_session *session, enum xen_on_normal_exit *result, xen_vm vm); + + +/** + * Get the actions/after_crash field of the given VM. + */ +extern bool +xen_vm_get_actions_after_crash(xen_session *session, enum xen_on_crash_behaviour *result, xen_vm vm); + + +/** + * Get the consoles field of the given VM. + */ +extern bool +xen_vm_get_consoles(xen_session *session, struct xen_console_set **result, xen_vm vm); + + +/** + * Get the VIFs field of the given VM. + */ +extern bool +xen_vm_get_vifs(xen_session *session, struct xen_vif_set **result, xen_vm vm); + + +/** + * Get the VBDs field of the given VM. + */ +extern bool +xen_vm_get_vbds(xen_session *session, struct xen_vbd_set **result, xen_vm vm); + + +/** + * Get the crash_dumps field of the given VM. + */ +extern bool +xen_vm_get_crash_dumps(xen_session *session, struct xen_crashdump_set **result, xen_vm vm); + + +/** + * Get the VTPMs field of the given VM. + */ +extern bool +xen_vm_get_vtpms(xen_session *session, struct xen_vtpm_set **result, xen_vm vm); + + +/** + * Get the PV/bootloader field of the given VM. + */ +extern bool +xen_vm_get_pv_bootloader(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the PV/kernel field of the given VM. + */ +extern bool +xen_vm_get_pv_kernel(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the PV/ramdisk field of the given VM. + */ +extern bool +xen_vm_get_pv_ramdisk(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the PV/args field of the given VM. + */ +extern bool +xen_vm_get_pv_args(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the PV/bootloader_args field of the given VM. + */ +extern bool +xen_vm_get_pv_bootloader_args(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the HVM/boot_policy field of the given VM. + */ +extern bool +xen_vm_get_hvm_boot_policy(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the HVM/boot_params field of the given VM. + */ +extern bool +xen_vm_get_hvm_boot_params(xen_session *session, xen_string_string_map **result, xen_vm vm); + + +/** + * Get the platform field of the given VM. + */ +extern bool +xen_vm_get_platform(xen_session *session, xen_string_string_map **result, xen_vm vm); + + +/** + * Get the PCI_bus field of the given VM. + */ +extern bool +xen_vm_get_pci_bus(xen_session *session, char **result, xen_vm vm); + + +/** + * Get the other_config field of the given VM. + */ +extern bool +xen_vm_get_other_config(xen_session *session, xen_string_string_map **result, xen_vm vm); + + +/** + * Get the domid field of the given VM. + */ +extern bool +xen_vm_get_domid(xen_session *session, int64_t *result, xen_vm vm); + + +/** + * Get the is_control_domain field of the given VM. + */ +extern bool +xen_vm_get_is_control_domain(xen_session *session, bool *result, xen_vm vm); + + +/** + * Get the metrics field of the given VM. + */ +extern bool +xen_vm_get_metrics(xen_session *session, xen_vm_metrics *result, xen_vm vm); + + +/** + * Get the guest_metrics field of the given VM. + */ +extern bool +xen_vm_get_guest_metrics(xen_session *session, xen_vm_guest_metrics *result, xen_vm vm); + + +/** + * Set the name/label field of the given VM. + */ +extern bool +xen_vm_set_name_label(xen_session *session, xen_vm vm, char *label); + + +/** + * Set the name/description field of the given VM. + */ +extern bool +xen_vm_set_name_description(xen_session *session, xen_vm vm, char *description); + + +/** + * Set the user_version field of the given VM. + */ +extern bool +xen_vm_set_user_version(xen_session *session, xen_vm vm, int64_t user_version); + + +/** + * Set the is_a_template field of the given VM. + */ +extern bool +xen_vm_set_is_a_template(xen_session *session, xen_vm vm, bool is_a_template); + + +/** + * Set the auto_power_on field of the given VM. + */ +extern bool +xen_vm_set_auto_power_on(xen_session *session, xen_vm vm, bool auto_power_on); + + +/** + * Set the memory/static_max field of the given VM. + */ +extern bool +xen_vm_set_memory_static_max(xen_session *session, xen_vm vm, int64_t static_max); + + +/** + * Set the memory/dynamic_max field of the given VM. + */ +extern bool +xen_vm_set_memory_dynamic_max(xen_session *session, xen_vm vm, int64_t dynamic_max); + + +/** + * Set the memory/dynamic_min field of the given VM. + */ +extern bool +xen_vm_set_memory_dynamic_min(xen_session *session, xen_vm vm, int64_t dynamic_min); + + +/** + * Set the memory/static_min field of the given VM. + */ +extern bool +xen_vm_set_memory_static_min(xen_session *session, xen_vm vm, int64_t static_min); + + +/** + * Set the VCPUs/params field of the given VM. + */ +extern bool +xen_vm_set_vcpus_params(xen_session *session, xen_vm vm, xen_string_string_map *params); + + +/** + * Add the given key-value pair to the VCPUs/params field of the given + * VM. + */ +extern bool +xen_vm_add_to_vcpus_params(xen_session *session, xen_vm vm, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * VCPUs/params field of the given VM. If the key is not in that Map, then do + * nothing. + */ +extern bool +xen_vm_remove_from_vcpus_params(xen_session *session, xen_vm vm, char *key); + + +/** + * Set the VCPUs/max field of the given VM. + */ +extern bool +xen_vm_set_vcpus_max(xen_session *session, xen_vm vm, int64_t max); + + +/** + * Set the VCPUs/at_startup field of the given VM. + */ +extern bool +xen_vm_set_vcpus_at_startup(xen_session *session, xen_vm vm, int64_t at_startup); + + +/** + * Set the actions/after_shutdown field of the given VM. + */ +extern bool +xen_vm_set_actions_after_shutdown(xen_session *session, xen_vm vm, enum xen_on_normal_exit after_shutdown); + + +/** + * Set the actions/after_reboot field of the given VM. + */ +extern bool +xen_vm_set_actions_after_reboot(xen_session *session, xen_vm vm, enum xen_on_normal_exit after_reboot); + + +/** + * Set the actions/after_crash field of the given VM. + */ +extern bool +xen_vm_set_actions_after_crash(xen_session *session, xen_vm vm, enum xen_on_crash_behaviour after_crash); + + +/** + * Set the PV/bootloader field of the given VM. + */ +extern bool +xen_vm_set_pv_bootloader(xen_session *session, xen_vm vm, char *bootloader); + + +/** + * Set the PV/kernel field of the given VM. + */ +extern bool +xen_vm_set_pv_kernel(xen_session *session, xen_vm vm, char *kernel); + + +/** + * Set the PV/ramdisk field of the given VM. + */ +extern bool +xen_vm_set_pv_ramdisk(xen_session *session, xen_vm vm, char *ramdisk); + + +/** + * Set the PV/args field of the given VM. + */ +extern bool +xen_vm_set_pv_args(xen_session *session, xen_vm vm, char *args); + + +/** + * Set the PV/bootloader_args field of the given VM. + */ +extern bool +xen_vm_set_pv_bootloader_args(xen_session *session, xen_vm vm, char *bootloader_args); + + +/** + * Set the HVM/boot_policy field of the given VM. + */ +extern bool +xen_vm_set_hvm_boot_policy(xen_session *session, xen_vm vm, char *boot_policy); + + +/** + * Set the HVM/boot_params field of the given VM. + */ +extern bool +xen_vm_set_hvm_boot_params(xen_session *session, xen_vm vm, xen_string_string_map *boot_params); + + +/** + * Add the given key-value pair to the HVM/boot_params field of the + * given VM. + */ +extern bool +xen_vm_add_to_hvm_boot_params(xen_session *session, xen_vm vm, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * HVM/boot_params field of the given VM. If the key is not in that Map, then + * do nothing. + */ +extern bool +xen_vm_remove_from_hvm_boot_params(xen_session *session, xen_vm vm, char *key); + + +/** + * Set the platform field of the given VM. + */ +extern bool +xen_vm_set_platform(xen_session *session, xen_vm vm, xen_string_string_map *platform); + + +/** + * Add the given key-value pair to the platform field of the given VM. + */ +extern bool +xen_vm_add_to_platform(xen_session *session, xen_vm vm, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the platform + * field of the given VM. If the key is not in that Map, then do nothing. + */ +extern bool +xen_vm_remove_from_platform(xen_session *session, xen_vm vm, char *key); + + +/** + * Set the PCI_bus field of the given VM. + */ +extern bool +xen_vm_set_pci_bus(xen_session *session, xen_vm vm, char *pci_bus); + + +/** + * Set the other_config field of the given VM. + */ +extern bool +xen_vm_set_other_config(xen_session *session, xen_vm vm, xen_string_string_map *other_config); + + +/** + * Add the given key-value pair to the other_config field of the given + * VM. + */ +extern bool +xen_vm_add_to_other_config(xen_session *session, xen_vm vm, char *key, char *value); + + +/** + * Remove the given key and its corresponding value from the + * other_config field of the given VM. If the key is not in that Map, then do + * nothing. + */ +extern bool +xen_vm_remove_from_other_config(xen_session *session, xen_vm vm, char *key); + + +/** + * Clones the specified VM, making a new VM. Clone automatically + * exploits the capabilities of the underlying storage repository in which the + * VM's disk images are stored (e.g. Copy on Write). This function can only + * be called when the VM is in the Halted State. + */ +extern bool +xen_vm_clone(xen_session *session, xen_vm *result, xen_vm vm, char *new_name); + + +/** + * Start the specified VM. This function can only be called with the + * VM is in the Halted State. + */ +extern bool +xen_vm_start(xen_session *session, xen_vm vm, bool start_paused); + + +/** + * Pause the specified VM. This can only be called when the specified + * VM is in the Running state. + */ +extern bool +xen_vm_pause(xen_session *session, xen_vm vm); + + +/** + * Resume the specified VM. This can only be called when the specified + * VM is in the Paused state. + */ +extern bool +xen_vm_unpause(xen_session *session, xen_vm vm); + + +/** + * Attempt to cleanly shutdown the specified VM. (Note: this may not be + * supported---e.g. if a guest agent is not installed). + * + * Once shutdown has been completed perform poweroff action specified in guest + * configuration. + * + * This can only be called when the specified VM is in the Running state. + */ +extern bool +xen_vm_clean_shutdown(xen_session *session, xen_vm vm); + + +/** + * Attempt to cleanly shutdown the specified VM (Note: this may not be + * supported---e.g. if a guest agent is not installed). + * + * Once shutdown has been completed perform reboot action specified in guest + * configuration. + * + * This can only be called when the specified VM is in the Running state. + */ +extern bool +xen_vm_clean_reboot(xen_session *session, xen_vm vm); + + +/** + * Stop executing the specified VM without attempting a clean shutdown. + * Then perform poweroff action specified in VM configuration. + */ +extern bool +xen_vm_hard_shutdown(xen_session *session, xen_vm vm); + + +/** + * Stop executing the specified VM without attempting a clean shutdown. + * Then perform reboot action specified in VM configuration + */ +extern bool +xen_vm_hard_reboot(xen_session *session, xen_vm vm); + + +/** + * Suspend the specified VM to disk. This can only be called when the + * specified VM is in the Running state. + */ +extern bool +xen_vm_suspend(xen_session *session, xen_vm vm); + + +/** + * Awaken the specified VM and resume it. This can only be called when + * the specified VM is in the Suspended state. + */ +extern bool +xen_vm_resume(xen_session *session, xen_vm vm, bool start_paused); + + +/** + * Set this VM's VCPUs/at_startup value, and set the same value on the + * VM, if running + */ +extern bool +xen_vm_set_vcpus_number_live(xen_session *session, xen_vm self, int64_t nvcpu); + + +/** + * Add the given key-value pair to VM.VCPUs_params, and apply that + * value on the running VM. + */ +extern bool +xen_vm_add_to_vcpus_params_live(xen_session *session, xen_vm self, char *key, char *value); + + +/** + * Set memory_dynamic_max in database and on running VM. + */ +extern bool +xen_vm_set_memory_dynamic_max_live(xen_session *session, xen_vm self, int64_t max); + + +/** + * Set memory_dynamic_min in database and on running VM. + */ +extern bool +xen_vm_set_memory_dynamic_min_live(xen_session *session, xen_vm self, int64_t min); + + +/** + * Send the given key as a sysrq to this VM. The key is specified as a + * single character (a String of length 1). This can only be called when the + * specified VM is in the Running state. + */ +extern bool +xen_vm_send_sysrq(xen_session *session, xen_vm vm, char *key); + + +/** + * Send the named trigger to this VM. This can only be called when the + * specified VM is in the Running state. + */ +extern bool +xen_vm_send_trigger(xen_session *session, xen_vm vm, char *trigger); + + +/** + * Migrate the VM to another host. This can only be called when the + * specified VM is in the Running state. + */ +extern bool +xen_vm_migrate(xen_session *session, xen_vm vm, char *dest, bool live, xen_string_string_map *options); + + +/** + * Return a list of all the VMs known to the system. + */ +extern bool +xen_vm_get_all(xen_session *session, struct xen_vm_set **result); + + +/** + * Set the security label of a domain. + */ +extern bool +xen_vm_set_security_label(xen_session *session, int64_t *result, xen_vm vm, + char *label, char *oldlabel); + +/** + * Get the security label of a domain. + */ +extern bool +xen_vm_get_security_label(xen_session *session, char **result, xen_vm vm); + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm_decl.h b/tools/libxen/include/xen/api/xen_vm_decl.h new file mode 100644 index 0000000..f4ca986 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_DECL_H +#define XEN_VM_DECL_H + +typedef void *xen_vm; + +struct xen_vm_set; +struct xen_vm_record; +struct xen_vm_record_set; +struct xen_vm_record_opt; +struct xen_vm_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm_guest_metrics.h b/tools/libxen/include/xen/api/xen_vm_guest_metrics.h new file mode 100644 index 0000000..fd4d23a --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm_guest_metrics.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_GUEST_METRICS_H +#define XEN_VM_GUEST_METRICS_H + +#include +#include +#include + + +/* + * The VM_guest_metrics class. + * + * The metrics reported by the guest (as opposed to inferred from outside). + */ + + +/** + * Free the given xen_vm_guest_metrics. The given handle must have + * been allocated by this library. + */ +extern void +xen_vm_guest_metrics_free(xen_vm_guest_metrics vm_guest_metrics); + + +typedef struct xen_vm_guest_metrics_set +{ + size_t size; + xen_vm_guest_metrics *contents[]; +} xen_vm_guest_metrics_set; + +/** + * Allocate a xen_vm_guest_metrics_set of the given size. + */ +extern xen_vm_guest_metrics_set * +xen_vm_guest_metrics_set_alloc(size_t size); + +/** + * Free the given xen_vm_guest_metrics_set. The given set must have + * been allocated by this library. + */ +extern void +xen_vm_guest_metrics_set_free(xen_vm_guest_metrics_set *set); + + +typedef struct xen_vm_guest_metrics_record +{ + xen_vm_guest_metrics handle; + char *uuid; + xen_string_string_map *os_version; + xen_string_string_map *pv_drivers_version; + xen_string_string_map *memory; + xen_string_string_map *disks; + xen_string_string_map *networks; + xen_string_string_map *other; + time_t last_updated; +} xen_vm_guest_metrics_record; + +/** + * Allocate a xen_vm_guest_metrics_record. + */ +extern xen_vm_guest_metrics_record * +xen_vm_guest_metrics_record_alloc(void); + +/** + * Free the given xen_vm_guest_metrics_record, and all referenced + * values. The given record must have been allocated by this library. + */ +extern void +xen_vm_guest_metrics_record_free(xen_vm_guest_metrics_record *record); + + +typedef struct xen_vm_guest_metrics_record_opt +{ + bool is_record; + union + { + xen_vm_guest_metrics handle; + xen_vm_guest_metrics_record *record; + } u; +} xen_vm_guest_metrics_record_opt; + +/** + * Allocate a xen_vm_guest_metrics_record_opt. + */ +extern xen_vm_guest_metrics_record_opt * +xen_vm_guest_metrics_record_opt_alloc(void); + +/** + * Free the given xen_vm_guest_metrics_record_opt, and all referenced + * values. The given record_opt must have been allocated by this library. + */ +extern void +xen_vm_guest_metrics_record_opt_free(xen_vm_guest_metrics_record_opt *record_opt); + + +typedef struct xen_vm_guest_metrics_record_set +{ + size_t size; + xen_vm_guest_metrics_record *contents[]; +} xen_vm_guest_metrics_record_set; + +/** + * Allocate a xen_vm_guest_metrics_record_set of the given size. + */ +extern xen_vm_guest_metrics_record_set * +xen_vm_guest_metrics_record_set_alloc(size_t size); + +/** + * Free the given xen_vm_guest_metrics_record_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_vm_guest_metrics_record_set_free(xen_vm_guest_metrics_record_set *set); + + + +typedef struct xen_vm_guest_metrics_record_opt_set +{ + size_t size; + xen_vm_guest_metrics_record_opt *contents[]; +} xen_vm_guest_metrics_record_opt_set; + +/** + * Allocate a xen_vm_guest_metrics_record_opt_set of the given size. + */ +extern xen_vm_guest_metrics_record_opt_set * +xen_vm_guest_metrics_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vm_guest_metrics_record_opt_set, and all + * referenced values. The given set must have been allocated by this library. + */ +extern void +xen_vm_guest_metrics_record_opt_set_free(xen_vm_guest_metrics_record_opt_set *set); + + +/** + * Get a record containing the current state of the given + * VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_record(xen_session *session, xen_vm_guest_metrics_record **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get a reference to the VM_guest_metrics instance with the specified + * UUID. + */ +extern bool +xen_vm_guest_metrics_get_by_uuid(xen_session *session, xen_vm_guest_metrics *result, char *uuid); + + +/** + * Get the uuid field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_uuid(xen_session *session, char **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the os_version field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_os_version(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the PV_drivers_version field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_pv_drivers_version(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the memory field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_memory(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the disks field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_disks(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the networks field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_networks(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the other field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_other(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Get the last_updated field of the given VM_guest_metrics. + */ +extern bool +xen_vm_guest_metrics_get_last_updated(xen_session *session, time_t *result, xen_vm_guest_metrics vm_guest_metrics); + + +/** + * Return a list of all the VM_guest_metrics instances known to the + * system. + */ +extern bool +xen_vm_guest_metrics_get_all(xen_session *session, struct xen_vm_guest_metrics_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm_guest_metrics_decl.h b/tools/libxen/include/xen/api/xen_vm_guest_metrics_decl.h new file mode 100644 index 0000000..837528b --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm_guest_metrics_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_GUEST_METRICS_DECL_H +#define XEN_VM_GUEST_METRICS_DECL_H + +typedef void *xen_vm_guest_metrics; + +struct xen_vm_guest_metrics_set; +struct xen_vm_guest_metrics_record; +struct xen_vm_guest_metrics_record_set; +struct xen_vm_guest_metrics_record_opt; +struct xen_vm_guest_metrics_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm_metrics.h b/tools/libxen/include/xen/api/xen_vm_metrics.h new file mode 100644 index 0000000..128c1a3 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm_metrics.h @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_METRICS_H +#define XEN_VM_METRICS_H + +#include +#include +#include +#include +#include +#include +#include + + +/* + * The VM_metrics class. + * + * The metrics associated with a VM. + */ + + +/** + * Free the given xen_vm_metrics. The given handle must have been + * allocated by this library. + */ +extern void +xen_vm_metrics_free(xen_vm_metrics vm_metrics); + + +typedef struct xen_vm_metrics_set +{ + size_t size; + xen_vm_metrics *contents[]; +} xen_vm_metrics_set; + +/** + * Allocate a xen_vm_metrics_set of the given size. + */ +extern xen_vm_metrics_set * +xen_vm_metrics_set_alloc(size_t size); + +/** + * Free the given xen_vm_metrics_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vm_metrics_set_free(xen_vm_metrics_set *set); + + +typedef struct xen_vm_metrics_record +{ + xen_vm_metrics handle; + char *uuid; + int64_t memory_actual; + int64_t vcpus_number; + xen_int_float_map *vcpus_utilisation; + xen_int_int_map *vcpus_cpu; + xen_string_string_map *vcpus_params; + xen_int_string_set_map *vcpus_flags; + struct xen_string_set *state; + time_t start_time; + time_t last_updated; +} xen_vm_metrics_record; + +/** + * Allocate a xen_vm_metrics_record. + */ +extern xen_vm_metrics_record * +xen_vm_metrics_record_alloc(void); + +/** + * Free the given xen_vm_metrics_record, and all referenced values. + * The given record must have been allocated by this library. + */ +extern void +xen_vm_metrics_record_free(xen_vm_metrics_record *record); + + +typedef struct xen_vm_metrics_record_opt +{ + bool is_record; + union + { + xen_vm_metrics handle; + xen_vm_metrics_record *record; + } u; +} xen_vm_metrics_record_opt; + +/** + * Allocate a xen_vm_metrics_record_opt. + */ +extern xen_vm_metrics_record_opt * +xen_vm_metrics_record_opt_alloc(void); + +/** + * Free the given xen_vm_metrics_record_opt, and all referenced values. + * The given record_opt must have been allocated by this library. + */ +extern void +xen_vm_metrics_record_opt_free(xen_vm_metrics_record_opt *record_opt); + + +typedef struct xen_vm_metrics_record_set +{ + size_t size; + xen_vm_metrics_record *contents[]; +} xen_vm_metrics_record_set; + +/** + * Allocate a xen_vm_metrics_record_set of the given size. + */ +extern xen_vm_metrics_record_set * +xen_vm_metrics_record_set_alloc(size_t size); + +/** + * Free the given xen_vm_metrics_record_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_vm_metrics_record_set_free(xen_vm_metrics_record_set *set); + + + +typedef struct xen_vm_metrics_record_opt_set +{ + size_t size; + xen_vm_metrics_record_opt *contents[]; +} xen_vm_metrics_record_opt_set; + +/** + * Allocate a xen_vm_metrics_record_opt_set of the given size. + */ +extern xen_vm_metrics_record_opt_set * +xen_vm_metrics_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vm_metrics_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_vm_metrics_record_opt_set_free(xen_vm_metrics_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_record(xen_session *session, xen_vm_metrics_record **result, xen_vm_metrics vm_metrics); + + +/** + * Get a reference to the VM_metrics instance with the specified UUID. + */ +extern bool +xen_vm_metrics_get_by_uuid(xen_session *session, xen_vm_metrics *result, char *uuid); + + +/** + * Get the uuid field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_uuid(xen_session *session, char **result, xen_vm_metrics vm_metrics); + + +/** + * Get the memory/actual field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_memory_actual(xen_session *session, int64_t *result, xen_vm_metrics vm_metrics); + + +/** + * Get the VCPUs/number field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_vcpus_number(xen_session *session, int64_t *result, xen_vm_metrics vm_metrics); + + +/** + * Get the VCPUs/utilisation field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_vcpus_utilisation(xen_session *session, xen_int_float_map **result, xen_vm_metrics vm_metrics); + + +/** + * Get the VCPUs/CPU field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_vcpus_cpu(xen_session *session, xen_int_int_map **result, xen_vm_metrics vm_metrics); + + +/** + * Get the VCPUs/params field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_vcpus_params(xen_session *session, xen_string_string_map **result, xen_vm_metrics vm_metrics); + + +/** + * Get the VCPUs/flags field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_vcpus_flags(xen_session *session, xen_int_string_set_map **result, xen_vm_metrics vm_metrics); + + +/** + * Get the state field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_state(xen_session *session, struct xen_string_set **result, xen_vm_metrics vm_metrics); + + +/** + * Get the start_time field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_start_time(xen_session *session, time_t *result, xen_vm_metrics vm_metrics); + + +/** + * Get the last_updated field of the given VM_metrics. + */ +extern bool +xen_vm_metrics_get_last_updated(xen_session *session, time_t *result, xen_vm_metrics vm_metrics); + + +/** + * Return a list of all the VM_metrics instances known to the system. + */ +extern bool +xen_vm_metrics_get_all(xen_session *session, struct xen_vm_metrics_set **result); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm_metrics_decl.h b/tools/libxen/include/xen/api/xen_vm_metrics_decl.h new file mode 100644 index 0000000..76810b5 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm_metrics_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_METRICS_DECL_H +#define XEN_VM_METRICS_DECL_H + +typedef void *xen_vm_metrics; + +struct xen_vm_metrics_set; +struct xen_vm_metrics_record; +struct xen_vm_metrics_record_set; +struct xen_vm_metrics_record_opt; +struct xen_vm_metrics_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_vm_power_state.h b/tools/libxen/include/xen/api/xen_vm_power_state.h new file mode 100644 index 0000000..b9b2acd --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vm_power_state.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VM_POWER_STATE_H +#define XEN_VM_POWER_STATE_H + + +#include + + +enum xen_vm_power_state +{ + /** + * Halted + */ + XEN_VM_POWER_STATE_HALTED, + + /** + * Paused + */ + XEN_VM_POWER_STATE_PAUSED, + + /** + * Running + */ + XEN_VM_POWER_STATE_RUNNING, + + /** + * Suspended + */ + XEN_VM_POWER_STATE_SUSPENDED, + + /** + * Crashed + */ + XEN_VM_POWER_STATE_CRASHED, + + /** + * Some other unknown state + */ + XEN_VM_POWER_STATE_UNKNOWN +}; + + +typedef struct xen_vm_power_state_set +{ + size_t size; + enum xen_vm_power_state contents[]; +} xen_vm_power_state_set; + +/** + * Allocate a xen_vm_power_state_set of the given size. + */ +extern xen_vm_power_state_set * +xen_vm_power_state_set_alloc(size_t size); + +/** + * Free the given xen_vm_power_state_set. The given set must have been + * allocated by this library. + */ +extern void +xen_vm_power_state_set_free(xen_vm_power_state_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_vm_power_state_to_string(enum xen_vm_power_state val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_vm_power_state +xen_vm_power_state_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vtpm.h b/tools/libxen/include/xen/api/xen_vtpm.h new file mode 100644 index 0000000..9b3b2eb --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vtpm.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006-2007, XenSource Inc. + * Copyright (c) 2006, IBM 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; 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VTPM_H +#define XEN_VTPM_H + +#include +#include +#include + + +/* + * The VTPM class. + * + * A virtual TPM device. + */ + + +/** + * Free the given xen_vtpm. The given handle must have been allocated + * by this library. + */ +extern void +xen_vtpm_free(xen_vtpm vtpm); + + +typedef struct xen_vtpm_set +{ + size_t size; + xen_vtpm *contents[]; +} xen_vtpm_set; + +/** + * Allocate a xen_vtpm_set of the given size. + */ +extern xen_vtpm_set * +xen_vtpm_set_alloc(size_t size); + +/** + * Free the given xen_vtpm_set. The given set must have been allocated + * by this library. + */ +extern void +xen_vtpm_set_free(xen_vtpm_set *set); + + +typedef struct xen_vtpm_record +{ + xen_vtpm handle; + char *uuid; + struct xen_vm_record_opt *vm; + struct xen_vm_record_opt *backend; + xen_string_string_map *other_config; +} xen_vtpm_record; + +/** + * Allocate a xen_vtpm_record. + */ +extern xen_vtpm_record * +xen_vtpm_record_alloc(void); + +/** + * Free the given xen_vtpm_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_vtpm_record_free(xen_vtpm_record *record); + + +typedef struct xen_vtpm_record_opt +{ + bool is_record; + union + { + xen_vtpm handle; + xen_vtpm_record *record; + } u; +} xen_vtpm_record_opt; + +/** + * Allocate a xen_vtpm_record_opt. + */ +extern xen_vtpm_record_opt * +xen_vtpm_record_opt_alloc(void); + +/** + * Free the given xen_vtpm_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_vtpm_record_opt_free(xen_vtpm_record_opt *record_opt); + + +typedef struct xen_vtpm_record_set +{ + size_t size; + xen_vtpm_record *contents[]; +} xen_vtpm_record_set; + +/** + * Allocate a xen_vtpm_record_set of the given size. + */ +extern xen_vtpm_record_set * +xen_vtpm_record_set_alloc(size_t size); + +/** + * Free the given xen_vtpm_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_vtpm_record_set_free(xen_vtpm_record_set *set); + + + +typedef struct xen_vtpm_record_opt_set +{ + size_t size; + xen_vtpm_record_opt *contents[]; +} xen_vtpm_record_opt_set; + +/** + * Allocate a xen_vtpm_record_opt_set of the given size. + */ +extern xen_vtpm_record_opt_set * +xen_vtpm_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_vtpm_record_opt_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_vtpm_record_opt_set_free(xen_vtpm_record_opt_set *set); + + +/** + * Get a record containing the current state of the given VTPM. + */ +extern bool +xen_vtpm_get_record(xen_session *session, xen_vtpm_record **result, xen_vtpm vtpm); + + +/** + * Get a reference to the VTPM instance with the specified UUID. + */ +extern bool +xen_vtpm_get_by_uuid(xen_session *session, xen_vtpm *result, char *uuid); + + +/** + * Create a new VTPM instance, and return its handle. + */ +extern bool +xen_vtpm_create(xen_session *session, xen_vtpm *result, xen_vtpm_record *record); + + +/** + * Destroy the specified VTPM instance. + */ +extern bool +xen_vtpm_destroy(xen_session *session, xen_vtpm vtpm); + + +/** + * Get the uuid field of the given VTPM. + */ +extern bool +xen_vtpm_get_uuid(xen_session *session, char **result, xen_vtpm vtpm); + + +/** + * Get the VM field of the given VTPM. + */ +extern bool +xen_vtpm_get_vm(xen_session *session, xen_vm *result, xen_vtpm vtpm); + + +/** + * Get the backend field of the given VTPM. + */ +extern bool +xen_vtpm_get_backend(xen_session *session, xen_vm *result, xen_vtpm vtpm); + + +/** + * Get the other_config field of the given VTPM. + */ +extern bool +xen_vtpm_get_other_config(xen_session *session, + xen_string_string_map **result, + xen_vtpm vtpm); + + +/** + * Set the other_config field of the given VTPM. + */ +extern bool +xen_vtpm_set_other_config(xen_session *session, + xen_vtpm vtpm, + xen_string_string_map *other_config); + + +#endif diff --git a/tools/libxen/include/xen/api/xen_vtpm_decl.h b/tools/libxen/include/xen/api/xen_vtpm_decl.h new file mode 100644 index 0000000..7798e38 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_vtpm_decl.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * Copyright (c) 2006, IBM 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; 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_VTPM_DECL_H +#define XEN_VTPM_DECL_H + +typedef void *xen_vtpm; + +struct xen_vtpm_set; +struct xen_vtpm_record; +struct xen_vtpm_record_set; +struct xen_vtpm_record_opt; +struct xen_vtpm_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen/api/xen_xspolicy.h b/tools/libxen/include/xen/api/xen_xspolicy.h new file mode 100644 index 0000000..f685f15 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_xspolicy.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2007, IBM Corp. + * Copyright (c) 2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_XSPOLICY_H +#define XEN_XSPOLICY_H + +#include "xen_common.h" +#include "xen_xspolicy_decl.h" +#include "xen_string_string_map.h" + + +/* + * The XSPolicy and associated data structures. + * + */ +typedef int64_t xs_type; +typedef int64_t xs_instantiationflags; + +enum xs_type { + XS_POLICY_ACM = (1 << 0), +}; + +enum xs_instantiationflags { + XS_INST_NONE = 0, + XS_INST_BOOT = (1 << 0), + XS_INST_LOAD = (1 << 1), +}; + + +/* Error codes returned by xend following XSPolicy operations */ +#define XSERR_BASE 0x1000 + +#define XSERR_SUCCESS 0 +#define XSERR_GENERAL_FAILURE 1 + XSERR_BASE +#define XSERR_BAD_XML 2 + XSERR_BASE +#define XSERR_XML_PROCESSING 3 + XSERR_BASE +#define XSERR_POLICY_INCONSISTENT 4 + XSERR_BASE +#define XSERR_FILE_ERROR 5 + XSERR_BASE +#define XSERR_BAD_RESOURCE_FORMAT 6 + XSERR_BASE +#define XSERR_BAD_LABEL_FORMAT 7 + XSERR_BASE +#define XSERR_RESOURCE_NOT_LABELED 8 + XSERR_BASE +#define XSERR_RESOURCE_ALREADY_LABELED 9 + XSERR_BASE +#define XSERR_WRONG_POLICY_TYPE 10 + XSERR_BASE +#define XSERR_BOOTPOLICY_INSTALLED 11 + XSERR_BASE +#define XSERR_NO_DEFAULT_BOOT_TITLE 12 + XSERR_BASE +#define XSERR_POLICY_LOAD_FAILED 13 + XSERR_BASE +#define XSERR_POLICY_LOADED 14 + XSERR_BASE +#define XSERR_POLICY_TYPE_UNSUPPORTED 15 + XSERR_BASE +#define XSERR_BAD_CONFLICTSET 20 + XSERR_BASE +#define XSERR_RESOURCE_IN_USE 21 + XSERR_BASE +#define XSERR_BAD_POLICY_NAME 22 + XSERR_BASE +#define XSERR_RESOURCE_ACCESS 23 + XSERR_BASE +#define XSERR_HV_OP_FAILED 24 + XSERR_BASE +#define XSERR_BOOTPOLICY_INSTALL_ERROR 25 + XSERR_BASE +#define XSERR_VM_NOT_AUTHORIZED 26 + XSERR_BASE +#define XSERR_VM_IN_CONFLICT 27 + XSERR_BASE + + +/** + * Free the given xen_xspolicy. The given handle must have been allocated + * by this library. + */ +extern void +xen_xspolicy_free(xen_xspolicy xspolicy); + + +typedef struct xen_xspolicy_set +{ + size_t size; + xen_xspolicy *contents[]; +} xen_xspolicy_set; + +/** + * Allocate a xen_xspolicy_set of the given size. + */ +extern xen_xspolicy_set * +xen_xspolicy_set_alloc(size_t size); + +/** + * Free the given xen_xspolicy_set. The given set must have been allocated + * by this library. + */ +extern void +xen_xspolicy_set_free(xen_xspolicy_set *set); + + +typedef struct xen_xspolicy_record +{ + xen_xspolicy handle; + char *uuid; + char *repr; + xs_instantiationflags flags; + xs_type type; +} xen_xspolicy_record; + +/** + * Allocate a xen_xspolicy_record. + */ +extern xen_xspolicy_record * +xen_xspolicy_record_alloc(void); + +/** + * Free the given xen_xspolicy_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_xspolicy_record_free(xen_xspolicy_record *record); + + +typedef struct xen_xspolicy_record_opt +{ + bool is_record; + union + { + xen_xspolicy handle; + xen_xspolicy_record *record; + } u; +} xen_xspolicy_record_opt; + +/** + * Allocate a xen_xspolicy_record_opt. + */ +extern xen_xspolicy_record_opt * +xen_xspolicy_record_opt_alloc(void); + +/** + * Free the given xen_xspolicy_record_opt, and all referenced values. The + * given record_opt must have been allocated by this library. + */ +extern void +xen_xspolicy_record_opt_free(xen_xspolicy_record_opt *record_opt); + + +typedef struct xen_xspolicy_record_set +{ + size_t size; + xen_xspolicy_record *contents[]; +} xen_xspolicy_record_set; + +/** + * Allocate a xen_xspolicy_record_set of the given size. + */ +extern xen_xspolicy_record_set * +xen_xspolicy_record_set_alloc(size_t size); + +/** + * Free the given xen_xspolicy_record_set, and all referenced values. The + * given set must have been allocated by this library. + */ +extern void +xen_xspolicy_record_set_free(xen_xspolicy_record_set *set); + +/** + * Data structures and function declarations for an XS Policy's state + * information. + */ +typedef struct xen_xs_policystate +{ + xen_xspolicy_record_opt *xs_ref; + int64_t xserr; + char *repr; + xs_type type; + xs_instantiationflags flags; + char *version; + char *errors; +} xen_xs_policystate; + +extern void +xen_xs_policystate_free(xen_xs_policystate *state); + + +/** + * Get the referenced policy's record. + */ +extern bool +xen_xspolicy_get_record(xen_session *session, xen_xspolicy_record **result, + xen_xspolicy xspolicy); + +/** + * Get the UUID field of the given policy. + */ +extern bool +xen_xspolicy_get_uuid(xen_session *session, char **result, + xen_xspolicy xspolicy); + +/** + * Get a policy given it's UUID + */ +extern bool +xen_xspolicy_get_by_uuid(xen_session *session, xen_xspolicy *result, + char *uuid); + + +/** + * Get the types of policies supported by the system. + */ +extern bool +xen_xspolicy_get_xstype(xen_session *session, xs_type *result); + + +/** + * Get information about the currently managed policy. + * (The API allows only one policy to be on the system.) + */ +extern bool +xen_xspolicy_get_xspolicy(xen_session *session, xen_xs_policystate **result); + +/** + * Activate the referenced policy by loading it into the hypervisor. + */ +extern bool +xen_xspolicy_activate_xspolicy(xen_session *session, int64_t *result, + xen_xspolicy xspolicy, + xs_instantiationflags flags); + + +/** + * Set the system's policy to the given information comprising + * type of policy, the xml representation of the policy, some flags + * on whether to load the policy immediately and whether to overwrite + * an existing policy on the system. + */ +extern bool +xen_xspolicy_set_xspolicy(xen_session *session, xen_xs_policystate **result, + xs_type type, char *repr, int64_t flags, + bool overwrite); + + + +/** + * Attempt to reset the system's policy to the DEFAULT policy for the + * respective policy type. This is done by updating the system and therefore + * underlies the same restrictions of a policy update. This operation may + * for example fail if other domains than Domain-0 are running and have + * different labels than Domain-0. + */ +extern bool +xen_xspolicy_reset_xspolicy(xen_session *session, xen_xs_policystate **result, + xs_type type); + + +/** + * Remove any policy from having the system booted with. + */ +extern bool +xen_xspolicy_rm_xsbootpolicy(xen_session *session); + +/** + * Retrieve all labeled resources. + */ +extern bool +xen_xspolicy_get_labeled_resources(xen_session *session, + xen_string_string_map **resources); + +/** + * Label a resource such as for example a hard drive partition or file + */ +extern bool +xen_xspolicy_set_resource_label(xen_session *session, + char *resource, char *label, + char *oldlabel); + +/** + * Get the label of a resource. + */ +extern bool +xen_xspolicy_get_resource_label(xen_session *session, char **label, + char *resource); + +/** + * Check whether a VM with the given VM-label could run. + */ +extern bool +xen_xspolicy_can_run(xen_session *session, int64_t *result, + char *security_label); + +#endif diff --git a/tools/libxen/include/xen/api/xen_xspolicy_decl.h b/tools/libxen/include/xen/api/xen_xspolicy_decl.h new file mode 100644 index 0000000..08e7252 --- /dev/null +++ b/tools/libxen/include/xen/api/xen_xspolicy_decl.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007, IBM Corp. + * Copyright (c) 2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_XSPOLICY_DECL_H +#define XEN_XSPOLICY_DECL_H + +typedef void *xen_xspolicy; + +struct xen_xspolicy_set; +struct xen_xspolicy_record; +struct xen_xspolicy_record_set; +struct xen_xspolicy_record_opt; +struct xen_xspolicy_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen_console_protocol_internal.h b/tools/libxen/include/xen_console_protocol_internal.h new file mode 100644 index 0000000..d2ddaa0 --- /dev/null +++ b/tools/libxen/include/xen_console_protocol_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_console_protocol. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_CONSOLE_PROTOCOL_INTERNAL_H +#define XEN_CONSOLE_PROTOCOL_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_console_protocol_abstract_type_; +extern const abstract_type xen_console_protocol_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_event_operation_internal.h b/tools/libxen/include/xen_event_operation_internal.h new file mode 100644 index 0000000..3c4f70c --- /dev/null +++ b/tools/libxen/include/xen_event_operation_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_event_operation. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_EVENT_OPERATION_INTERNAL_H +#define XEN_EVENT_OPERATION_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_event_operation_abstract_type_; +extern const abstract_type xen_event_operation_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_internal.h b/tools/libxen/include/xen_internal.h new file mode 100644 index 0000000..21f75de --- /dev/null +++ b/tools/libxen/include/xen_internal.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2006-2007 XenSource, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_INTERNAL_H +#define XEN_INTERNAL_H + + +#include +#include +#include + +#include + + +enum abstract_typename +{ + VOID, + STRING, + INT, + FLOAT, + BOOL, + DATETIME, + SET, + MAP, + STRUCT, + REF, + ENUM, + ENUMSET +}; + + +typedef struct +{ + size_t size; + void *contents[]; +} arbitrary_set; + + +typedef struct struct_member struct_member; + + +typedef struct abstract_type +{ + enum abstract_typename typename; + const struct abstract_type *child; + const char * (*enum_marshaller)(int); + int (*enum_demarshaller)(xen_session *, const char *); + size_t struct_size; + size_t member_count; + const struct_member *members; +} abstract_type; + + +struct struct_member +{ + const char *key; + const struct abstract_type *type; + int offset; +}; + + +extern const abstract_type abstract_type_string; +extern const abstract_type abstract_type_int; +extern const abstract_type abstract_type_float; +extern const abstract_type abstract_type_bool; +extern const abstract_type abstract_type_datetime; +extern const abstract_type abstract_type_ref; + +extern const abstract_type abstract_type_string_set; +extern const abstract_type abstract_type_ref_set; + +extern const abstract_type abstract_type_string_string_map; +extern const abstract_type abstract_type_int_float_map; +extern const abstract_type abstract_type_int_int_map; +extern const abstract_type abstract_type_int_string_set_map; + + +typedef struct abstract_value +{ + const abstract_type *type; + union + { + const char *string_val; + int64_t int_val; + int enum_val; + double float_val; + bool bool_val; + arbitrary_set *set_val; + void *struct_val; + time_t datetime_val; + } u; +} abstract_value; + + +extern void +xen_call_(xen_session *s, const char *method_name, abstract_value params[], + int param_count, const abstract_type *result_type, void *value); + + +#define XEN_CALL_(method_name__) \ + xen_call_(session, method_name__, param_values, \ + sizeof(param_values) / sizeof(param_values[0]), \ + &result_type, result) \ + + +extern char * +xen_strdup_(const char *in); + + +extern int +xen_enum_lookup_(xen_session *session, const char *str, + const char **lookup_table, int n); + +#define ENUM_LOOKUP(session__, str__, lookup_table__) \ + xen_enum_lookup_(session__, str__, lookup_table__, \ + sizeof(lookup_table__) / \ + sizeof(lookup_table__[0])) \ + +#define XEN_ALLOC(type__) \ +type__ * \ +type__ ## _alloc() \ +{ \ + return calloc(1, sizeof(type__)); \ +} \ + + +#define XEN_FREE(type__) \ +void \ +type__ ## _free(type__ handle) \ +{ \ + free(handle); \ +} \ + + +#define XEN_SET_ALLOC_FREE(type__) \ + XEN_SET_ALLOC(type__) \ + XEN_SET_FREE(type__) + + +#define XEN_SET_ALLOC(type__) \ +type__ ## _set * \ +type__ ## _set_alloc(size_t size) \ +{ \ + type__ ## _set *result = calloc(1, sizeof(type__ ## _set) + \ + size * sizeof(type__)); \ + result->size = size; \ + return result; \ +} + + +#define XEN_SET_FREE(type__) \ +void type__ ## _set_free(type__ ## _set *set) \ +{ \ + if (set == NULL) \ + return; \ + for (size_t i = 0; i < set->size; i++) \ + type__ ## _free(set->contents[i]); \ + free(set); \ +} + + +#define XEN_RECORD_OPT_FREE(type__) \ +void type__ ## _record_opt_free(type__ ## _record_opt *opt) { \ + if (opt == NULL) return; \ + if (opt->is_record) \ + type__ ## _record_free(opt->u.record); \ + else \ + type__ ## _free(opt->u.handle); \ + free(opt); \ +} + + +#endif diff --git a/tools/libxen/include/xen_on_crash_behaviour_internal.h b/tools/libxen/include/xen_on_crash_behaviour_internal.h new file mode 100644 index 0000000..bd6f1fa --- /dev/null +++ b/tools/libxen/include/xen_on_crash_behaviour_internal.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_on_crash_behaviour. Internal to this library -- do not use from + * outside. + */ + + +#ifndef XEN_ON_CRASH_BEHAVIOUR_INTERNAL_H +#define XEN_ON_CRASH_BEHAVIOUR_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_on_crash_behaviour_abstract_type_; +extern const abstract_type xen_on_crash_behaviour_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_on_normal_exit_internal.h b/tools/libxen/include/xen_on_normal_exit_internal.h new file mode 100644 index 0000000..ffff46a --- /dev/null +++ b/tools/libxen/include/xen_on_normal_exit_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_on_normal_exit. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_ON_NORMAL_EXIT_INTERNAL_H +#define XEN_ON_NORMAL_EXIT_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_on_normal_exit_abstract_type_; +extern const abstract_type xen_on_normal_exit_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_vbd_mode_internal.h b/tools/libxen/include/xen_vbd_mode_internal.h new file mode 100644 index 0000000..4f05681 --- /dev/null +++ b/tools/libxen/include/xen_vbd_mode_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_vbd_mode. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_VBD_MODE_INTERNAL_H +#define XEN_VBD_MODE_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_vbd_mode_abstract_type_; +extern const abstract_type xen_vbd_mode_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_vbd_type_internal.h b/tools/libxen/include/xen_vbd_type_internal.h new file mode 100644 index 0000000..5f027cf --- /dev/null +++ b/tools/libxen/include/xen_vbd_type_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_vbd_type. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_VBD_TYPE_INTERNAL_H +#define XEN_VBD_TYPE_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_vbd_type_abstract_type_; +extern const abstract_type xen_vbd_type_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_vdi_type_internal.h b/tools/libxen/include/xen_vdi_type_internal.h new file mode 100644 index 0000000..9c575ff --- /dev/null +++ b/tools/libxen/include/xen_vdi_type_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_vdi_type. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_VDI_TYPE_INTERNAL_H +#define XEN_VDI_TYPE_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_vdi_type_abstract_type_; +extern const abstract_type xen_vdi_type_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_vm_power_state_internal.h b/tools/libxen/include/xen_vm_power_state_internal.h new file mode 100644 index 0000000..a3f5af1 --- /dev/null +++ b/tools/libxen/include/xen_vm_power_state_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_vm_power_state. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_VM_POWER_STATE_INTERNAL_H +#define XEN_VM_POWER_STATE_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_vm_power_state_abstract_type_; +extern const abstract_type xen_vm_power_state_set_abstract_type_; + + +#endif diff --git a/tools/libxen/src/xen_acmpolicy.c b/tools/libxen/src/xen_acmpolicy.c new file mode 100644 index 0000000..f8d6919 --- /dev/null +++ b/tools/libxen/src/xen_acmpolicy.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2007, IBM Corp. + * Copyright (c) 2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include "xen/api/xen_common.h" +#include "xen/api/xen_xspolicy.h" +#include "xen/api/xen_acmpolicy.h" + + +static const struct_member xen_acmpolicy_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_acmpolicy_record, uuid) }, + { .key = "flags", + .type = &abstract_type_int, + .offset = offsetof(xen_acmpolicy_record, flags) }, + { .key = "repr", + .type = &abstract_type_string, + .offset = offsetof(xen_acmpolicy_record, repr) }, + { .key = "type", + .type = &abstract_type_int, + .offset = offsetof(xen_acmpolicy_record, type) }, + }; + +const abstract_type xen_acmpolicy_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_acmpolicy_record), + .member_count = + sizeof(xen_acmpolicy_record_struct_members) / sizeof(struct_member), + .members = xen_acmpolicy_record_struct_members + }; + + +static const struct_member xen_acm_header_struct_members[] = + { + { .key = "policyname", + .type = &abstract_type_string, + .offset = offsetof(xen_acm_header, policyname) }, + { .key = "policyurl", + .type = &abstract_type_string, + .offset = offsetof(xen_acm_header, policyurl) }, + { .key = "date", + .type = &abstract_type_string, + .offset = offsetof(xen_acm_header, date) }, + { .key = "reference", + .type = &abstract_type_string, + .offset = offsetof(xen_acm_header, reference) }, + { .key = "namespaceurl", + .type = &abstract_type_string, + .offset = offsetof(xen_acm_header, namespaceurl) }, + { .key = "version", + .type = &abstract_type_string, + .offset = offsetof(xen_acm_header, version) }, + }; + +const abstract_type xen_acm_header_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_acm_header), + .member_count = + sizeof(xen_acm_header_struct_members) / + sizeof(struct_member), + .members = xen_acm_header_struct_members, + }; + +void +xen_acm_header_free(xen_acm_header *shdr) +{ + if (shdr == NULL) + { + return; + } + free(shdr->policyname); + free(shdr->policyurl); + free(shdr->date); + free(shdr->reference); + free(shdr->namespaceurl); + free(shdr->version); + free(shdr); +} + + +void +xen_acmpolicy_record_free(xen_acmpolicy_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->repr); + free(record); +} + + + +bool +xen_acmpolicy_get_record(xen_session *session, xen_acmpolicy_record **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy } + }; + + abstract_type result_type = xen_acmpolicy_record_abstract_type_; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_acmpolicy_get_header(xen_session *session, + xen_acm_header **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy }, + }; + + abstract_type result_type = xen_acm_header_abstract_type_; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_header"); + return session->ok; +} + + +bool +xen_acmpolicy_get_xml(xen_session *session, + char **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_xml"); + return session->ok; +} + + +bool +xen_acmpolicy_get_map(xen_session *session, + char **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_map"); + return session->ok; +} + + +bool +xen_acmpolicy_get_binary(xen_session *session, char **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_binary"); + return session->ok; +} + + +bool +xen_acmpolicy_get_enforced_binary(xen_session *session, char **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_enforced_binary"); + return session->ok; +} + + +bool +xen_acmpolicy_get_VM_ssidref(xen_session *session, + int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("ACMPolicy.get_VM_ssidref"); + return session->ok; +} + + +bool +xen_acmpolicy_get_uuid(xen_session *session, char **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("ACMPolicy.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_common.c b/tools/libxen/src/xen_common.c new file mode 100644 index 0000000..10dd156 --- /dev/null +++ b/tools/libxen/src/xen_common.c @@ -0,0 +1,1772 @@ +/* + * Copyright (c) 2006-2007 XenSource, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "xen/api/xen_common.h" +#include "xen/api/xen_host.h" +#include "xen_internal.h" +#include "xen/api/xen_int_float_map.h" +#include "xen/api/xen_int_int_map.h" +#include "xen/api/xen_int_string_set_map.h" +#include "xen/api/xen_string_string_map.h" + + +/* + * Whether to ignore missing structure entries. This is not something we + * want to do, once the API has stabilised, as it indicates that the server is + * broken, but at the moment, complaining is just slowing development down. + */ +#define PERMISSIVE 1 + + +static xmlXPathCompExprPtr responsePath = NULL; +static xmlXPathCompExprPtr faultPath = NULL; + + +typedef struct +{ + size_t size; + void *contents[]; +} arbitrary_map; + + +typedef struct +{ + void *handle; +} arbitrary_record; + + +typedef struct +{ + bool is_record; + union + { + char *handle; + arbitrary_record *record; + } u; +} arbitrary_record_opt; + + +static char * +make_body(const char *, abstract_value [], int); + +static void +parse_result(xen_session *, const char *, const abstract_type *, void *); + +static void +add_value(xmlNode *, const char *, const char *); +static void +add_param(xmlNode *, const char *, const char *); + +static xmlNode * +add_param_struct(xmlNode *); +static xmlNode * +add_struct_array(xmlNode *, const char *); +static xmlNode * +add_nested_struct(xmlNode *, const char *); +static void +add_struct_member(xmlNode *, const char *, const char *, const char *); +static void +add_unnamed_value(xmlNode *, const char *, const char *, const char *); + +static void +add_struct_value(const struct abstract_type *, void *, + void (*)(xmlNode *, const char *, const char *, + const char *), + const char *, xmlNode *); + +static xmlNode * +add_container(xmlNode *parent, const char *name); + +static void +call_raw(xen_session *, const char *, abstract_value [], int, + const abstract_type *, void *); + +static void +parse_structmap_value(xen_session *, xmlNode *, const abstract_type *, + void *); + +static size_t size_of_member(const abstract_type *); + +static const char * +get_val_as_string(const struct abstract_type *, void *, char *, size_t); + + +void +xen_init(void) +{ + responsePath = + xmlXPathCompile( + BAD_CAST( + "/methodResponse/params/param/value/struct/member/value")); + faultPath = + xmlXPathCompile( + BAD_CAST("/methodResponse/fault/value/struct/member/value")); +} + + +void +xen_fini(void) +{ + xmlXPathFreeCompExpr(responsePath); + xmlXPathFreeCompExpr(faultPath); + responsePath = NULL; + faultPath = NULL; +} + + +void +xen_session_record_free(xen_session_record *record) +{ + if (record == NULL) + { + return; + } + free(record->uuid); + xen_host_record_opt_free(record->this_host); + free(record->this_user); + free(record); +} + + +xen_session * +xen_session_login_with_password(xen_call_func call_func, void *handle, + const char *uname, const char *pwd) +{ + abstract_value params[] = + { + { .type = &abstract_type_string, + .u.string_val = uname }, + { .type = &abstract_type_string, + .u.string_val = pwd } + }; + + xen_session *session = malloc(sizeof(xen_session)); + session->call_func = call_func; + session->handle = handle; + session->session_id = NULL; + session->ok = true; + session->error_description = NULL; + session->error_description_count = 0; + + call_raw(session, "session.login_with_password", params, 2, + &abstract_type_string, &session->session_id); + + return session; +} + + +void +xen_session_logout(xen_session *session) +{ + abstract_value params[] = + { + }; + xen_call_(session, "session.logout", params, 0, NULL, NULL); + + if (session->error_description != NULL) + { + for (int i = 0; i < session->error_description_count; i++) + { + free(session->error_description[i]); + } + free(session->error_description); + } + + free((char *)session->session_id); + free(session); +} + + +void +xen_session_clear_error(xen_session *session) +{ + if (session->error_description != NULL) + { + for (int i = 0; i < session->error_description_count; i++) + { + free(session->error_description[i]); + } + free(session->error_description); + } + session->error_description = NULL; + session->error_description_count = 0; + session->ok = true; +} + + +bool +xen_session_get_uuid(xen_session *session, char **result, + xen_session *self_session) +{ + abstract_value params[] = + { + { .type = &abstract_type_string, + .u.string_val = self_session->session_id } + }; + + xen_call_(session, "session.get_uuid", params, 1, + &abstract_type_string, result); + return session->ok; +} + + +bool +xen_session_get_this_host(xen_session *session, xen_host *result, + xen_session *self_session) +{ + abstract_value params[] = + { + { .type = &abstract_type_string, + .u.string_val = self_session->session_id } + }; + + xen_call_(session, "session.get_this_host", params, 1, + &abstract_type_string, result); + return session->ok; +} + + +bool +xen_session_get_this_user(xen_session *session, char **result, + xen_session *self_session) +{ + abstract_value params[] = + { + { .type = &abstract_type_string, + .u.string_val = self_session->session_id } + }; + + xen_call_(session, "session.get_this_user", params, 1, + &abstract_type_string, result); + return session->ok; +} + + +bool +xen_session_get_last_active(xen_session *session, time_t *result, + xen_session *self_session) +{ + abstract_value params[] = + { + { .type = &abstract_type_string, + .u.string_val = self_session->session_id } + }; + + xen_call_(session, "session.get_last_active", params, 1, + &abstract_type_datetime, result); + return session->ok; +} + + +static const struct_member xen_session_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_session_record, uuid) }, + { .key = "this_host", + .type = &abstract_type_ref, + .offset = offsetof(xen_session_record, this_host) }, + { .key = "this_user", + .type = &abstract_type_string, + .offset = offsetof(xen_session_record, this_user) }, + { .key = "last_active", + .type = &abstract_type_datetime, + .offset = offsetof(xen_session_record, last_active) }, + }; + +const abstract_type xen_session_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_session_record), + .member_count = + sizeof(xen_session_record_struct_members) / sizeof(struct_member), + .members = xen_session_record_struct_members + }; + + +bool +xen_session_get_record(xen_session *session, xen_session_record **result, + xen_session *self_session) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self_session->session_id } + }; + + abstract_type result_type = xen_session_record_abstract_type_; + + *result = NULL; + XEN_CALL_("session.get_record"); + + return session->ok; +} + + +#define X "%02x" +#define UUID_FORMAT X X X X "-" X X "-" X X "-" X X "-" X X X X X X + + +bool +xen_uuid_string_to_bytes(char *uuid, char **bytes) +{ + unsigned int buf[16]; + + *bytes = NULL; + + if (strlen(uuid) != 36) + return false; + + if (16 != sscanf(uuid, UUID_FORMAT, + buf + 0, buf + 1, buf + 2, buf + 3, + buf + 4, buf + 5, + buf + 6, buf + 7, + buf + 8, buf + 9, + buf + 10, buf + 11, buf + 12, buf + 13, buf + 14, + buf + 15)) + { + return false; + } + + *bytes = malloc(16); + if (*bytes == NULL) + return false; + + for (int i = 0; i < 16; i++) { + (*bytes)[i] = (char)buf[i]; + } + + return true; +} + + +bool +xen_uuid_bytes_to_string(char *bytes, char **uuid) +{ + *uuid = malloc(37); + if (*uuid == NULL) + return false; + + sprintf(*uuid, UUID_FORMAT, + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], + bytes[6], bytes[7], + bytes[8], bytes[9], + bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); + + return true; +} + + +#undef UUID_FORMAT +#undef X + + +void +xen_uuid_free(char *uuid) +{ + free(uuid); +} + + +void +xen_uuid_bytes_free(char *bytes) +{ + free(bytes); +} + + +/** + * @param value A pointer to the correct location as per the given + * result_type. Will be populated if the call succeeds. In that case, and if + * value is a char **, the char * itself must be freed by the caller. + */ +void +xen_call_(xen_session *s, const char *method_name, + abstract_value params[], int param_count, + const abstract_type *result_type, void *value) +{ + abstract_value *full_params; + if (!s->ok) + { + return; + } + + full_params = malloc(sizeof(abstract_value) * (param_count + 1)); + + full_params[0].type = &abstract_type_string; + full_params[0].u.string_val = s->session_id; + + memcpy(full_params + 1, params, param_count * sizeof(abstract_value)); + + call_raw(s, method_name, full_params, param_count + 1, result_type, + value); + + free(full_params); +} + + +static bool +bufferAdd(const void *data, size_t len, void *buffer) +{ + return 0 == xmlBufferAdd((xmlBufferPtr)buffer, data, len); +} + + +static void +call_raw(xen_session *s, const char *method_name, + abstract_value params[], int param_count, + const abstract_type *result_type, void *value) +{ + xmlBufferPtr buffer = xmlBufferCreate(); + char *body = make_body(method_name, params, param_count); + int error_code = + s->call_func(body, strlen(body), s->handle, buffer, &bufferAdd); + free(body); + if (error_code) + { + char **strings = malloc(2 * sizeof(char *)); + + strings[0] = xen_strdup_("TRANSPORT_FAULT"); + strings[1] = malloc(20); + snprintf(strings[1], 20, "%d", error_code); + + s->ok = false; + s->error_description = strings; + s->error_description_count = 2; + } + else + { + parse_result(s, (char *)xmlBufferContent(buffer), result_type, value); + } + xmlBufferFree(buffer); +} + + +static void server_error(xen_session *session, const char *error_string) +{ + char **strings; + if (!session->ok) + { + /* Don't wipe out the earlier error message with this one. */ + return; + } + + strings = malloc(2 * sizeof(char *)); + + strings[0] = xen_strdup_("SERVER_FAULT"); + strings[1] = xen_strdup_(error_string); + + session->ok = false; + session->error_description = strings; + session->error_description_count = 2; +} + + +static void server_error_2(xen_session *session, const char *error_string, + const char *param) +{ + char **strings; + if (!session->ok) + { + /* Don't wipe out the earlier error message with this one. */ + return; + } + + strings = malloc(3 * sizeof(char *)); + + strings[0] = xen_strdup_("SERVER_FAULT_2"); + strings[1] = xen_strdup_(error_string); + strings[2] = xen_strdup_(param); + + session->ok = false; + session->error_description = strings; + session->error_description_count = 3; +} + + +static bool is_node(xmlNode *n, char *type) +{ + return + n->type == XML_ELEMENT_NODE && + 0 == strcmp((char *)n->name, type); +} + + +static bool is_container_node(xmlNode *n, char *type) +{ + return + is_node(n, type) && + n->children != NULL && + n->children == n->last && + n->children->type == XML_ELEMENT_NODE; +} + + +/** + * @return The contents of the given value, or NULL if this is not a node with + * the given type. If not NULL, the result must be freed with xmlFree(). + */ +static xmlChar *string_from_value(xmlNode *n, char *type) +{ + /* + XYZ is normal, but the XML-RPC spec also + allows XYZ where XYZ is to be interpreted as a string. + */ + + if (is_container_node(n, "value") && + 0 == strcmp((char *)n->children->name, type)) + { + return + n->children->children == NULL ? + xmlStrdup(BAD_CAST("")) : + xmlNodeGetContent(n->children->children); + } + else if (0 == strcmp(type, "string") && is_node(n, "value")) + { + return + n->children == NULL ? + xmlStrdup(BAD_CAST("")) : + xmlNodeGetContent(n->children); + } + else + { + return NULL; + } +} + + +/** + * Find the name node that is a child of the given one, and return its + * contents, or NULL if this has no such node. If not NULL, the result must + * be freed with xmlFree(). + */ +static xmlChar *string_from_name(xmlNode *n) +{ + xmlNode *cur = n->children; + + while (cur != NULL) + { + if (0 == strcmp((char *)cur->name, "name")) + { + return xmlNodeGetContent(cur); + } + cur = cur->next; + } + + return NULL; +} + + +static int count_children(xmlNode *n, const char *name) +{ + int result = 0; + xmlNode *cur = n->children; + + while (cur != NULL) + { + if (0 == strcmp((char *)cur->name, name)) + { + result++; + } + cur = cur->next; + } + + return result; +} + + +static void destring(xen_session *s, xmlChar *name, const abstract_type *type, + void *value) +{ + switch (type->typename) + { + case STRING: + *((char **)value) = xen_strdup_((const char *)name); + break; + + case INT: + *((int64_t *)value) = atoll((const char *)name); + break; + + case FLOAT: + *((double *)value) = atof((const char *)name); + break; + + default: + server_error(s, "Invalid Map key type"); + } +} + + +/** + * result_type : STRING => value : char **, the char * is yours. + * result_type : ENUM => value : int * + * result_type : INT => value : int64_t * + * result_type : FLOAT => value : double * + * result_type : BOOL => value : bool * + * result_type : DATETIME => value : time_t * + * result_type : SET => value : arbitrary_set **, the set is yours. + * result_type : MAP => value : arbitrary_map **, the map is yours. + * result_type : OPT => value : arbitrary_record_opt **, + * the record is yours, the handle is + * filled. + * result_type : STRUCT => value : void **, the void * is yours. + */ +static void parse_into(xen_session *s, xmlNode *value_node, + const abstract_type *result_type, void *value, + int slot) +{ + if (result_type == NULL) + { + xmlChar *string = string_from_value(value_node, "string"); + if (string == NULL || strcmp((char *)string, "")) + { + server_error(s, + "Expected Void from the server, but didn't get it"); + } + else + { + free(string); + } + + return; + } + + switch (result_type->typename) + { + case STRING: + { + xmlChar *string = string_from_value(value_node, "string"); + if (string == NULL) + { + server_error( + s, "Expected a String from the server, but didn't get one"); + } + else + { + ((char **)value)[slot] = xen_strdup_((const char *)string); + free(string); + } + } + break; + + case ENUM: + { + xmlChar *string = string_from_value(value_node, "string"); + if (string == NULL) + { +#if PERMISSIVE + fprintf(stderr, + "Expected an Enum from the server, but didn't get one\n"); + ((int *)value)[slot] = 0; +#else + server_error( + s, "Expected an Enum from the server, but didn't get one"); +#endif + } + else + { + ((int *)value)[slot] = + result_type->enum_demarshaller(s, (const char *)string); + free(string); + } + } + break; + + case INT: + { + xmlChar *string = string_from_value(value_node, "string"); + if (string == NULL) + { + server_error( + s, "Expected an Int from the server, but didn't get one"); + } + else + { + ((int64_t *)value)[slot] = (int64_t)atoll((char *)string); + free(string); + } + } + break; + + case FLOAT: + { + xmlChar *string = string_from_value(value_node, "double"); + if (string == NULL) + { +#if PERMISSIVE + fprintf(stderr, + "Expected a Float from the server, but didn't get one\n"); + ((double *)value)[slot] = 0.0; +#else + server_error( + s, "Expected a Float from the server, but didn't get one"); +#endif + } + else + { + ((double *)value)[slot] = atof((char *)string); + free(string); + } + } + break; + + case BOOL: + { + xmlChar *string = string_from_value(value_node, "boolean"); + if (string == NULL) + { +#if PERMISSIVE + fprintf(stderr, + "Expected a Bool from the server, but didn't get one\n"); + ((bool *)value)[slot] = false; +#else + server_error( + s, "Expected a Bool from the server, but didn't get one"); +#endif + } + else + { + ((bool *)value)[slot] = (0 == strcmp((char *)string, "1")); + free(string); + } + } + break; + + case DATETIME: + { + xmlChar *string = string_from_value(value_node, "dateTime.iso8601"); + if (string == NULL) + { + server_error( + s, "Expected an DateTime from the server but didn't get one"); + } + else + { + struct tm tm; + memset(&tm, 0, sizeof(tm)); + strptime((char *)string, "%Y%m%dT%H:%M:%S", &tm); + ((time_t *)value)[slot] = (time_t)mktime(&tm); + free(string); + } + } + break; + + case SET: + { + if (!is_container_node(value_node, "value") || + !is_container_node(value_node->children, "array")) + { + server_error(s, + "Expected Set from the server, but didn't get it"); + } + else + { + arbitrary_set *set; + xmlNode *cur, *data_node = value_node->children->children; + int i, n = count_children(data_node, "value"); + + const abstract_type *member_type = result_type->child; + size_t member_size = size_of_member(member_type); + + set = calloc(1, sizeof(arbitrary_set) + member_size * n); + set->size = n; + i = 0; + cur = data_node->children; + + while (cur != NULL) + { + if (0 == strcmp((char *)cur->name, "value")) + { + parse_into(s, cur, member_type, set->contents, i); + i++; + } + cur = cur->next; + } + + ((arbitrary_set **)value)[slot] = set; + } + } + break; + + case MAP: + { + if (!is_container_node(value_node, "value") || + value_node->children->type != XML_ELEMENT_NODE || + 0 != strcmp((char *)value_node->children->name, "struct")) + { + server_error(s, + "Expected Map from the server, but didn't get it"); + } + else + { + arbitrary_map *map; + xmlNode *cur, *struct_node = value_node->children; + int i, n = count_children(struct_node, "member"); + + size_t struct_size = result_type->struct_size; + + const struct struct_member *key_member = result_type->members; + const struct struct_member *val_member = result_type->members + 1; + + map = calloc(1, sizeof(arbitrary_map) + struct_size * n); + map->size = n; + i = 0; + cur = struct_node->children; + + while (cur != NULL) + { + if (0 == strcmp((char *)cur->name, "member")) + { + xmlChar *name; + if (cur->children == NULL || cur->last == cur->children) + { + server_error(s, "Malformed Map"); + free(map); + return; + } + + name = string_from_name(cur); + if (name == NULL) + { + server_error(s, "Malformed Map"); + free(map); + return; + } + + destring(s, name, key_member->type, + ((void *)(map + 1)) + + (i * struct_size) + + key_member->offset); + xmlFree(name); + if (!s->ok) + { + free(map); + return; + } + + parse_structmap_value(s, cur, val_member->type, + ((void *)(map + 1)) + + (i * struct_size) + + val_member->offset); + if (!s->ok) + { + free(map); + return; + } + i++; + } + cur = cur->next; + } + + ((arbitrary_map **)value)[slot] = map; + } + } + break; + + case STRUCT: + { + if (!is_container_node(value_node, "value") || + value_node->children->type != XML_ELEMENT_NODE || + 0 != strcmp((char *)value_node->children->name, "struct") || + value_node->children->children == NULL) + { + server_error(s, + "Expected Map from the server, but didn't get it"); + } + else + { + xmlNode *struct_node = value_node->children; + + void *result = calloc(1, result_type->struct_size); + xmlNode *cur = struct_node->children; + + size_t member_count = result_type->member_count; + + const struct_member **checklist = + malloc(sizeof(const struct_member *) * member_count); + int seen_count = 0; + + while (cur != NULL) + { + if (0 == strcmp((char *)cur->name, "member")) + { + xmlChar *name; + if (cur->children == NULL || cur->last == cur->children) + { + server_error(s, "Malformed Struct"); + free(result); + free(checklist); + return; + } + + name = string_from_name(cur); + if (name == NULL) + { + server_error(s, "Malformed Struct"); + free(result); + free(checklist); + return; + } + + for (size_t i = 0; i < member_count; i++) + { + const struct_member *mem = result_type->members + i; + + if (0 == strcmp((char *)name, mem->key)) + { + parse_structmap_value(s, cur, mem->type, + result + mem->offset); + checklist[seen_count] = mem; + seen_count++; + break; + } + } + + /* Note that we're skipping unknown fields implicitly. + This means that we'll be forward compatible with + new servers. */ + + xmlFree(name); + + if (!s->ok) + { + free(result); + free(checklist); + return; + } + } + cur = cur->next; + } + + /* Check that we've filled all fields. */ + for (size_t i = 0; i < member_count; i++) + { + const struct_member *mem = result_type->members + i; + int j; + + for (j = 0; j < seen_count; j++) + { + if (checklist[j] == mem) + { + break; + } + } + + if (j == seen_count) + { +#if PERMISSIVE + fprintf(stderr, + "Struct did not contain expected field %s.\n", + mem->key); +#else + server_error_2(s, + "Struct did not contain expected field", + mem->key); + free(result); + free(checklist); + return; +#endif + } + } + + free(checklist); + ((void **)value)[slot] = result; + } + } + break; + + case REF: + { + arbitrary_record_opt *record_opt = + calloc(1, sizeof(arbitrary_record_opt)); + + record_opt->is_record = false; + parse_into(s, value_node, &abstract_type_string, + &(record_opt->u.handle), 0); + + ((arbitrary_record_opt **)value)[slot] = record_opt; + } + break; + + default: + assert(false); + } +} + + +static size_t size_of_member(const abstract_type *type) +{ + switch (type->typename) + { + case STRING: + return sizeof(char *); + +/* + case INT: + return sizeof(int64_t); + + case FLOAT: + return sizeof(double); + + case BOOL: + return sizeof(bool); +*/ + case ENUM: + return sizeof(int); + + case REF: + return sizeof(arbitrary_record_opt *); + + case STRUCT: + return type->struct_size; + + default: + assert(false); + } +} + + +static void parse_structmap_value(xen_session *s, xmlNode *n, + const abstract_type *type, void *value) +{ + xmlNode *cur = n->children; + + while (cur != NULL) + { + if (0 == strcmp((char *)cur->name, "value")) + { + parse_into(s, cur, type, value, 0); + return; + } + cur = cur->next; + } + + server_error(s, "Missing value in Map/Struct"); +} + + +static void parse_fault(xen_session *session, xmlXPathContextPtr xpathCtx) +{ + xmlNode *fault_node0, *fault_node1; + xmlChar *fault_code_str, *fault_string_str; + char **strings; + + xmlXPathObjectPtr xpathObj = xmlXPathCompiledEval(faultPath, xpathCtx); + if (xpathObj == NULL) + { + server_error(session, "Method response is neither result nor fault"); + return; + } + + if (xpathObj->type != XPATH_NODESET || + xpathObj->nodesetval->nodeNr != 2) + { + xmlXPathFreeObject(xpathObj); + server_error(session, "Method response is neither result nor fault"); + return; + } + + fault_node0 = xpathObj->nodesetval->nodeTab[0]; + fault_node1 = xpathObj->nodesetval->nodeTab[1]; + + fault_code_str = string_from_value(fault_node0, "int"); + if (fault_code_str == NULL) + { + fault_code_str = string_from_value(fault_node0, "i4"); + } + if (fault_code_str == NULL) + { + xmlXPathFreeObject(xpathObj); + server_error(session, "Fault code is malformed"); + return; + } + + fault_string_str = string_from_value(fault_node1, "string"); + if (fault_string_str == NULL) + { + xmlFree(fault_code_str); + xmlXPathFreeObject(xpathObj); + server_error(session, "Fault string is malformed"); + return; + } + + strings = malloc(3 * sizeof(char *)); + + strings[0] = xen_strdup_("FAULT"); + strings[1] = xen_strdup_((char *)fault_code_str); + strings[2] = xen_strdup_((char *)fault_string_str); + + session->ok = false; + session->error_description = strings; + session->error_description_count = 3; + + xmlFree(fault_code_str); + xmlFree(fault_string_str); + xmlXPathFreeObject(xpathObj); +} + + +static void parse_failure(xen_session *session, xmlNode *node) +{ + abstract_type error_description_type = + { .typename = SET, + .child = &abstract_type_string }; + arbitrary_set *error_descriptions; + + parse_into(session, node, &error_description_type, &error_descriptions, + 0); + + if (session->ok) + { + char **c, **strings; + int n; + + session->ok = false; + + c = (char **)error_descriptions->contents; + n = error_descriptions->size; + + strings = malloc(n * sizeof(char *)); + for (int i = 0; i < n; i++) + { + strings[i] = c[i]; + } + + session->error_description_count = n; + session->error_description = strings; + } + + free(error_descriptions); +} + + +/** + * Parameters as for xen_call_() above. + */ +static void parse_result(xen_session *session, const char *result, + const abstract_type *result_type, void *value) +{ + xmlDocPtr doc = + xmlReadMemory(result, strlen(result), "", NULL, XML_PARSE_NONET); + xmlXPathContextPtr xpathCtx; + xmlXPathObjectPtr xpathObj; + xmlNode *node0, *node1; + xmlChar *status_code; + + if (doc == NULL) + { + server_error(session, "Couldn't parse the server response"); + return; + } + + xpathCtx = xmlXPathNewContext(doc); + if (xpathCtx == NULL) + { + xmlFreeDoc(doc); + server_error(session, "Couldn't create XPath context"); + return; + } + + xpathObj = xmlXPathCompiledEval(responsePath, xpathCtx); + if (xpathObj == NULL) + { + parse_fault(session, xpathCtx); + + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + return; + } + + if (xpathObj->type != XPATH_NODESET || + xpathObj->nodesetval->nodeNr != 2) + { + parse_fault(session, xpathCtx); + + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + return; + } + + node0 = xpathObj->nodesetval->nodeTab[0]; + node1 = xpathObj->nodesetval->nodeTab[1]; + + status_code = string_from_value(node0, "string"); + if (status_code == NULL) + { + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + server_error(session, "Server response does not have a Status"); + return; + } + + if (strcmp((char *)status_code, "Success")) + { + parse_failure(session, node1); + + xmlFree(status_code); + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + return; + } + + parse_into(session, node1, result_type, value, 0); + + xmlFree(status_code); + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); +} + + +static void +make_body_add_type(enum abstract_typename typename, abstract_value *v, + xmlNode *params_node) +{ + char buf[20]; + switch (typename) + { + case STRING: + add_param(params_node, "string", v->u.string_val); + break; + + case INT: + snprintf(buf, sizeof(buf), "%"PRId64, v->u.int_val); + add_param(params_node, "string", buf); + break; + + case FLOAT: + snprintf(buf, sizeof(buf), "%lf", v->u.float_val); + add_param(params_node, "double", buf); + break; + + case BOOL: + add_param(params_node, "boolean", v->u.bool_val ? "1" : "0"); + break; + + case VOID: + add_param(params_node, "string", ""); + break; + + case ENUM: + add_param(params_node, "string", + v->type->enum_marshaller(v->u.enum_val)); + break; + + case SET: + { + const struct abstract_type *member_type = v->type->child; + arbitrary_set *set_val = v->u.struct_val; + abstract_value v; + xmlNode *data_node = add_param_struct(params_node); + + for (size_t i = 0; i < set_val->size; i++) + { + switch (member_type->typename) { + case STRING: + v.u.string_val = (char *)set_val->contents[i]; + make_body_add_type(member_type->typename, &v, data_node); + break; + default: + assert(false); + } + } + } + break; + + case STRUCT: + { + size_t member_count = v->type->member_count; + + xmlNode *struct_node = add_param_struct(params_node); + + for (size_t i = 0; i < member_count; i++) + { + const struct struct_member *mem = v->type->members + i; + const char *key = mem->key; + void *struct_value = v->u.struct_val; + + add_struct_value(mem->type, struct_value + mem->offset, + add_struct_member, key, struct_node); + } + } + break; + + case MAP: + { + const struct struct_member *member = v->type->members; + arbitrary_map *map_val = v->u.struct_val; + xmlNode *param_node = add_param_struct(params_node); + for (size_t i = 0; i < map_val->size; i++) { + enum abstract_typename typename_key = member[0].type->typename; + enum abstract_typename typename_val = member[1].type->typename; + int offset_key = member[0].offset; + int offset_val = member[1].offset; + int struct_size = v->type->struct_size; + + switch (typename_key) { + case STRING: { + char **addr = (void *)(map_val + 1) + + (i * struct_size) + + offset_key; + char *key = *addr; + + switch (typename_val) { + case STRING: { + char *val; + addr = (void *)(map_val + 1) + + (i * struct_size) + + offset_val; + val = *addr; + add_struct_member(param_node, key, "string", val); + break; + } + default: + assert(false); + } + break; + } + default: + assert(false); + } + } + } + break; + + + default: + assert(false); + } +} + + +static char * +make_body(const char *method_name, abstract_value params[], int param_count) +{ + xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + xmlNode *params_node, *methodCall = xmlNewNode(NULL, BAD_CAST "methodCall"); + xmlBufferPtr buffer; + xmlSaveCtxtPtr save_ctxt; + xmlChar *content; + + xmlDocSetRootElement(doc, methodCall); + + xmlNewChild(methodCall, NULL, BAD_CAST "methodName", + BAD_CAST method_name); + + params_node = xmlNewChild(methodCall, NULL, BAD_CAST "params", NULL); + + for (int p = 0; p < param_count; p++) + { + abstract_value *v = params + p; + make_body_add_type(v->type->typename, v, params_node); + } + + buffer = xmlBufferCreate(); + save_ctxt = xmlSaveToBuffer(buffer, NULL, XML_SAVE_NO_XHTML); + + if (xmlSaveDoc(save_ctxt, doc) == -1) + { + return NULL; + } + + xmlFreeDoc(doc); + xmlSaveClose(save_ctxt); + content = xmlStrdup(xmlBufferContent(buffer)); + xmlBufferFree(buffer); + return (char *)content; +} + + +static void +add_struct_value(const struct abstract_type *type, void *value, + void (*adder)(xmlNode *node, const char *key, + const char *type, const char *val), + const char *key, xmlNode *node) +{ + char buf[20]; + + switch (type->typename) + { + case REF: + case STRING: + case INT: + case ENUM: + { + const char *val_as_string = + get_val_as_string(type, value, buf, sizeof(buf)); + adder(node, key, "string", val_as_string); + } + break; + + case FLOAT: + { + double val = *(double *)value; + snprintf(buf, sizeof(buf), "%lf", val); + adder(node, key, "double", buf); + } + break; + + case BOOL: + { + bool val = *(bool *)value; + adder(node, key, "boolean", val ? "1" : "0"); + } + break; + + case SET: + { + const struct abstract_type *member_type = type->child; + size_t member_size = size_of_member(member_type); + arbitrary_set *set_val = *(arbitrary_set **)value; + + if (set_val != NULL) + { + xmlNode *data_node = add_struct_array(node, key); + + for (size_t i = 0; i < set_val->size; i++) + { + void *member_value = (char *)set_val->contents + + (i * member_size); + add_struct_value(member_type, member_value, + add_unnamed_value, NULL, data_node); + } + } + } + break; + + case STRUCT: + { + assert(false); + /* XXX Nested structures aren't supported yet, but + fortunately we don't need them, because we don't have + any "deep create" calls. This will need to be + fixed. */ + } + break; + + case MAP: + { + size_t member_size = type->struct_size; + const struct abstract_type *l_type = type->members[0].type; + const struct abstract_type *r_type = type->members[1].type; + int l_offset = type->members[0].offset; + int r_offset = type->members[1].offset; + + arbitrary_map *map_val = *(arbitrary_map **)value; + + if (map_val != NULL) + { + xmlNode *struct_node = add_nested_struct(node, key); + + for (size_t i = 0; i < map_val->size; i++) + { + void *contents = (void *)map_val->contents; + void *l_value = contents + (i * member_size) + l_offset; + void *r_value = contents + (i * member_size) + r_offset; + + const char *l_value_as_string = + get_val_as_string(l_type, l_value, buf, sizeof(buf)); + + add_struct_value(r_type, r_value, add_struct_member, + l_value_as_string, struct_node); + } + } + } + break; + + default: + assert(false); + } +} + + +static const char * +get_val_as_string(const struct abstract_type *type, void *value, char *buf, + size_t bufsize) +{ + switch (type->typename) + { + case REF: + { + arbitrary_record_opt *val = *(arbitrary_record_opt **)value; + if (val != NULL) + { + if (val->is_record) + { + return val->u.record->handle; + } + else + { + return val->u.handle; + } + } + else + { + return NULL; + } + } + break; + + case STRING: + { + return *(char **)value; + } + break; + + case INT: + { + int64_t val = *(int64_t *)value; + snprintf(buf, bufsize, "%"PRId64, val); + return buf; + } + break; + + case ENUM: + { + int val = *(int *)value; + return type->enum_marshaller(val); + } + break; + + default: + assert(false); + } +} + + +static xmlNode * +add_container(xmlNode *parent, const char *name) +{ + return xmlNewChild(parent, NULL, BAD_CAST name, NULL); +} + + +static void +add_param(xmlNode *params_node, const char *type, const char *value) +{ + xmlNode *param_node = add_container(params_node, "param"); + add_value(param_node, type, value); +} + + +static void +add_value(xmlNode *parent, const char *type, const char *value) +{ + xmlNode *value_node = add_container(parent, "value"); + xmlNewChild(value_node, NULL, BAD_CAST type, BAD_CAST value); +} + + +static void +add_unnamed_value(xmlNode *parent, const char *name, const char *type, + const char *value) +{ + (void)name; + add_value(parent, type, value); +} + + +static xmlNode * +add_param_struct(xmlNode *params_node) +{ + xmlNode *param_node = add_container(params_node, "param"); + xmlNode *value_node = add_container(param_node, "value"); + + return xmlNewChild(value_node, NULL, BAD_CAST "struct", NULL); +} + + +static void +add_struct_member(xmlNode *struct_node, const char *name, const char *type, + const char *value) +{ + xmlNode *member_node = add_container(struct_node, "member"); + + xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name); + + add_value(member_node, type, value); +} + + +static xmlNode * +add_struct_array(xmlNode *struct_node, const char *name) +{ + xmlNode *member_node = add_container(struct_node, "member"); + xmlNode *value_node, *array_node; + + xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name); + + value_node = add_container(member_node, "value"); + array_node = add_container(value_node, "array"); + + return add_container(array_node, "data"); +} + + +static xmlNode * +add_nested_struct(xmlNode *struct_node, const char *name) +{ + xmlNode *member_node = add_container(struct_node, "member"); + xmlNode *value_node; + + xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name); + + value_node = add_container(member_node, "value"); + + return add_container(value_node, "struct"); +} + + +int xen_enum_lookup_(xen_session *session, const char *str, + const char **lookup_table, int n) +{ + if (str != NULL) + { + for (int i = 0; i < n; i++) + { + if (0 == strcmp(str, lookup_table[i])) + { + return i; + } + } + } + + server_error_2(session, "Bad enum string", str); + return 0; +} + + +char * +xen_strdup_(const char *in) +{ + char *result = malloc(strlen(in) + 1); + strcpy(result, in); + return result; +} + + +const abstract_type abstract_type_string = { .typename = STRING }; +const abstract_type abstract_type_int = { .typename = INT }; +const abstract_type abstract_type_float = { .typename = FLOAT }; +const abstract_type abstract_type_bool = { .typename = BOOL }; +const abstract_type abstract_type_datetime = { .typename = DATETIME }; +const abstract_type abstract_type_ref = { .typename = REF }; + +const abstract_type abstract_type_string_set = + { + .typename = SET, + .child = &abstract_type_string + }; + +const abstract_type abstract_type_ref_set = + { + .typename = SET, + .child = &abstract_type_ref + }; + +static const struct struct_member string_string_members[] = +{ + { + .type = &abstract_type_string, + .offset = offsetof(xen_string_string_map_contents, key) + }, + { + .type = &abstract_type_string, + .offset = offsetof(xen_string_string_map_contents, val) + } +}; +const abstract_type abstract_type_string_string_map = + { + .typename = MAP, + .struct_size = sizeof(xen_string_string_map_contents), + .members = string_string_members + }; + +static struct struct_member int_float_members[] = +{ + { + .type = &abstract_type_int, + .offset = offsetof(xen_int_float_map_contents, key) + }, + { + .type = &abstract_type_float, + .offset = offsetof(xen_int_float_map_contents, val) + } +}; +const abstract_type abstract_type_int_float_map = + { + .typename = MAP, + .struct_size = sizeof(xen_int_float_map_contents), + .members = int_float_members + }; + +static struct struct_member int_int_members[] = +{ + { + .type = &abstract_type_int, + .offset = offsetof(xen_int_int_map_contents, key) + }, + { + .type = &abstract_type_int, + .offset = offsetof(xen_int_int_map_contents, val) + } +}; +const abstract_type abstract_type_int_int_map = + { + .typename = MAP, + .struct_size = sizeof(xen_int_int_map_contents), + .members = int_int_members + }; + +static struct struct_member int_string_set_members[] = +{ + { + .type = &abstract_type_int, + .offset = offsetof(xen_int_string_set_map_contents, key) + }, + { + .type = &abstract_type_string_set, + .offset = offsetof(xen_int_string_set_map_contents, val) + } +}; +const abstract_type abstract_type_int_string_set_map = + { + .typename = MAP, + .struct_size = sizeof(xen_int_string_set_map_contents), + .members = int_string_set_members + }; diff --git a/tools/libxen/src/xen_console.c b/tools/libxen/src/xen_console.c new file mode 100644 index 0000000..2b45498 --- /dev/null +++ b/tools/libxen/src/xen_console.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_console_protocol_internal.h" +#include "xen_internal.h" +#include +#include +#include +#include + + +XEN_FREE(xen_console) +XEN_SET_ALLOC_FREE(xen_console) +XEN_ALLOC(xen_console_record) +XEN_SET_ALLOC_FREE(xen_console_record) +XEN_ALLOC(xen_console_record_opt) +XEN_RECORD_OPT_FREE(xen_console) +XEN_SET_ALLOC_FREE(xen_console_record_opt) + + +static const struct_member xen_console_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_console_record, uuid) }, + { .key = "protocol", + .type = &xen_console_protocol_abstract_type_, + .offset = offsetof(xen_console_record, protocol) }, + { .key = "location", + .type = &abstract_type_string, + .offset = offsetof(xen_console_record, location) }, + { .key = "VM", + .type = &abstract_type_ref, + .offset = offsetof(xen_console_record, vm) }, + { .key = "other_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_console_record, other_config) } + }; + +const abstract_type xen_console_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_console_record), + .member_count = + sizeof(xen_console_record_struct_members) / sizeof(struct_member), + .members = xen_console_record_struct_members + }; + + +void +xen_console_record_free(xen_console_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->location); + xen_vm_record_opt_free(record->vm); + xen_string_string_map_free(record->other_config); + free(record); +} + + +bool +xen_console_get_record(xen_session *session, xen_console_record **result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = xen_console_record_abstract_type_; + + *result = NULL; + XEN_CALL_("console.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_console_get_by_uuid(xen_session *session, xen_console *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_by_uuid"); + return session->ok; +} + + +bool +xen_console_create(xen_session *session, xen_console *result, xen_console_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_console_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.create"); + return session->ok; +} + + +bool +xen_console_destroy(xen_session *session, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + xen_call_(session, "console.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_console_get_protocol(xen_session *session, enum xen_console_protocol *result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = xen_console_protocol_abstract_type_; + XEN_CALL_("console.get_protocol"); + return session->ok; +} + + +bool +xen_console_get_location(xen_session *session, char **result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_location"); + return session->ok; +} + + +bool +xen_console_get_vm(xen_session *session, xen_vm *result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_VM"); + return session->ok; +} + + +bool +xen_console_get_other_config(xen_session *session, xen_string_string_map **result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("console.get_other_config"); + return session->ok; +} + + +bool +xen_console_set_other_config(xen_session *session, xen_console console, xen_string_string_map *other_config) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)other_config } + }; + + xen_call_(session, "console.set_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_console_add_to_other_config(xen_session *session, xen_console console, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "console.add_to_other_config", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_console_remove_from_other_config(xen_session *session, xen_console console, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "console.remove_from_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_console_get_all(xen_session *session, struct xen_console_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "console.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_console_get_uuid(xen_session *session, char **result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_console_protocol.c b/tools/libxen/src/xen_console_protocol.c new file mode 100644 index 0000000..031d64d --- /dev/null +++ b/tools/libxen/src/xen_console_protocol.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_console_protocol_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "vt100", + "rfb", + "rdp" +}; + + +extern xen_console_protocol_set * +xen_console_protocol_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_console_protocol_set) + + size * sizeof(enum xen_console_protocol)); +} + + +extern void +xen_console_protocol_set_free(xen_console_protocol_set *set) +{ + free(set); +} + + +const char * +xen_console_protocol_to_string(enum xen_console_protocol val) +{ + return lookup_table[val]; +} + + +extern enum xen_console_protocol +xen_console_protocol_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_console_protocol_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_console_protocol_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_console_protocol_from_string + }; + + +const abstract_type xen_console_protocol_set_abstract_type_ = + { + .typename = SET, + .child = &xen_console_protocol_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_crashdump.c b/tools/libxen/src/xen_crashdump.c new file mode 100644 index 0000000..ba2121b --- /dev/null +++ b/tools/libxen/src/xen_crashdump.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include + + +XEN_FREE(xen_crashdump) +XEN_SET_ALLOC_FREE(xen_crashdump) +XEN_ALLOC(xen_crashdump_record) +XEN_SET_ALLOC_FREE(xen_crashdump_record) +XEN_ALLOC(xen_crashdump_record_opt) +XEN_RECORD_OPT_FREE(xen_crashdump) +XEN_SET_ALLOC_FREE(xen_crashdump_record_opt) + + +static const struct_member xen_crashdump_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_crashdump_record, uuid) }, + { .key = "VM", + .type = &abstract_type_ref, + .offset = offsetof(xen_crashdump_record, vm) }, + { .key = "VDI", + .type = &abstract_type_ref, + .offset = offsetof(xen_crashdump_record, vdi) } + }; + +const abstract_type xen_crashdump_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_crashdump_record), + .member_count = + sizeof(xen_crashdump_record_struct_members) / sizeof(struct_member), + .members = xen_crashdump_record_struct_members + }; + + +void +xen_crashdump_record_free(xen_crashdump_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_vm_record_opt_free(record->vm); + xen_vdi_record_opt_free(record->vdi); + free(record); +} + + +bool +xen_crashdump_get_record(xen_session *session, xen_crashdump_record **result, xen_crashdump crashdump) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = crashdump } + }; + + abstract_type result_type = xen_crashdump_record_abstract_type_; + + *result = NULL; + XEN_CALL_("crashdump.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_crashdump_get_by_uuid(xen_session *session, xen_crashdump *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("crashdump.get_by_uuid"); + return session->ok; +} + + +bool +xen_crashdump_get_vm(xen_session *session, xen_vm *result, xen_crashdump crashdump) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = crashdump } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("crashdump.get_VM"); + return session->ok; +} + + +bool +xen_crashdump_get_vdi(xen_session *session, xen_vdi *result, xen_crashdump crashdump) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = crashdump } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("crashdump.get_VDI"); + return session->ok; +} + + +bool +xen_crashdump_destroy(xen_session *session, xen_crashdump self) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self } + }; + + xen_call_(session, "crashdump.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_crashdump_get_all(xen_session *session, struct xen_crashdump_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "crashdump.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_crashdump_get_uuid(xen_session *session, char **result, xen_crashdump crashdump) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = crashdump } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("crashdump.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_event.c b/tools/libxen/src/xen_event.c new file mode 100644 index 0000000..30e8d60 --- /dev/null +++ b/tools/libxen/src/xen_event.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_event_operation_internal.h" +#include "xen_internal.h" +#include +#include + + +XEN_ALLOC(xen_event_record) +XEN_SET_ALLOC_FREE(xen_event_record) + + +static const struct_member xen_event_record_struct_members[] = + { + { .key = "id", + .type = &abstract_type_int, + .offset = offsetof(xen_event_record, id) }, + { .key = "timestamp", + .type = &abstract_type_datetime, + .offset = offsetof(xen_event_record, timestamp) }, + { .key = "class", + .type = &abstract_type_string, + .offset = offsetof(xen_event_record, class) }, + { .key = "operation", + .type = &xen_event_operation_abstract_type_, + .offset = offsetof(xen_event_record, operation) }, + { .key = "ref", + .type = &abstract_type_string, + .offset = offsetof(xen_event_record, ref) }, + { .key = "obj_uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_event_record, obj_uuid) } + }; + +const abstract_type xen_event_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_event_record), + .member_count = + sizeof(xen_event_record_struct_members) / sizeof(struct_member), + .members = xen_event_record_struct_members + }; + + +const abstract_type xen_event_record_set_abstract_type_ = + { + .typename = SET, + .child = &xen_event_record_abstract_type_ + }; + + +void +xen_event_record_free(xen_event_record *record) +{ + if (record == NULL) + { + return; + } + free(record->class); + free(record->ref); + free(record->obj_uuid); + free(record); +} + + +bool +xen_event_register(xen_session *session, struct xen_string_set *classes) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string_set, + .u.set_val = (arbitrary_set *)classes } + }; + + xen_call_(session, "event.register", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_event_unregister(xen_session *session, struct xen_string_set *classes) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string_set, + .u.set_val = (arbitrary_set *)classes } + }; + + xen_call_(session, "event.unregister", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_event_next(xen_session *session, struct xen_event_record_set **result) +{ + + abstract_type result_type = xen_event_record_set_abstract_type_; + + *result = NULL; + xen_call_(session, "event.next", NULL, 0, &result_type, result); + return session->ok; +} diff --git a/tools/libxen/src/xen_event_operation.c b/tools/libxen/src/xen_event_operation.c new file mode 100644 index 0000000..48a7c36 --- /dev/null +++ b/tools/libxen/src/xen_event_operation.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_event_operation_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "add", + "del", + "mod" +}; + + +extern xen_event_operation_set * +xen_event_operation_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_event_operation_set) + + size * sizeof(enum xen_event_operation)); +} + + +extern void +xen_event_operation_set_free(xen_event_operation_set *set) +{ + free(set); +} + + +const char * +xen_event_operation_to_string(enum xen_event_operation val) +{ + return lookup_table[val]; +} + + +extern enum xen_event_operation +xen_event_operation_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_event_operation_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_event_operation_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_event_operation_from_string + }; + + diff --git a/tools/libxen/src/xen_host.c b/tools/libxen/src/xen_host.c new file mode 100644 index 0000000..0739c4f --- /dev/null +++ b/tools/libxen/src/xen_host.c @@ -0,0 +1,891 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +XEN_FREE(xen_host) +XEN_SET_ALLOC_FREE(xen_host) +XEN_ALLOC(xen_host_record) +XEN_SET_ALLOC_FREE(xen_host_record) +XEN_ALLOC(xen_host_record_opt) +XEN_RECORD_OPT_FREE(xen_host) +XEN_SET_ALLOC_FREE(xen_host_record_opt) + + +static const struct_member xen_host_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_host_record, uuid) }, + { .key = "name_label", + .type = &abstract_type_string, + .offset = offsetof(xen_host_record, name_label) }, + { .key = "name_description", + .type = &abstract_type_string, + .offset = offsetof(xen_host_record, name_description) }, + { .key = "API_version_major", + .type = &abstract_type_int, + .offset = offsetof(xen_host_record, api_version_major) }, + { .key = "API_version_minor", + .type = &abstract_type_int, + .offset = offsetof(xen_host_record, api_version_minor) }, + { .key = "API_version_vendor", + .type = &abstract_type_string, + .offset = offsetof(xen_host_record, api_version_vendor) }, + { .key = "API_version_vendor_implementation", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_host_record, api_version_vendor_implementation) }, + { .key = "enabled", + .type = &abstract_type_bool, + .offset = offsetof(xen_host_record, enabled) }, + { .key = "software_version", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_host_record, software_version) }, + { .key = "other_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_host_record, other_config) }, + { .key = "capabilities", + .type = &abstract_type_string_set, + .offset = offsetof(xen_host_record, capabilities) }, + { .key = "cpu_configuration", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_host_record, cpu_configuration) }, + { .key = "sched_policy", + .type = &abstract_type_string, + .offset = offsetof(xen_host_record, sched_policy) }, + { .key = "supported_bootloaders", + .type = &abstract_type_string_set, + .offset = offsetof(xen_host_record, supported_bootloaders) }, + { .key = "resident_VMs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_host_record, resident_vms) }, + { .key = "logging", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_host_record, logging) }, + { .key = "PIFs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_host_record, pifs) }, + { .key = "suspend_image_sr", + .type = &abstract_type_ref, + .offset = offsetof(xen_host_record, suspend_image_sr) }, + { .key = "crash_dump_sr", + .type = &abstract_type_ref, + .offset = offsetof(xen_host_record, crash_dump_sr) }, + { .key = "PBDs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_host_record, pbds) }, + { .key = "host_CPUs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_host_record, host_cpus) }, + { .key = "metrics", + .type = &abstract_type_ref, + .offset = offsetof(xen_host_record, metrics) } + }; + +const abstract_type xen_host_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_host_record), + .member_count = + sizeof(xen_host_record_struct_members) / sizeof(struct_member), + .members = xen_host_record_struct_members + }; + + +void +xen_host_record_free(xen_host_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->name_label); + free(record->name_description); + free(record->api_version_vendor); + xen_string_string_map_free(record->api_version_vendor_implementation); + xen_string_string_map_free(record->software_version); + xen_string_string_map_free(record->other_config); + xen_string_set_free(record->capabilities); + xen_string_string_map_free(record->cpu_configuration); + free(record->sched_policy); + xen_string_set_free(record->supported_bootloaders); + xen_vm_record_opt_set_free(record->resident_vms); + xen_string_string_map_free(record->logging); + xen_pif_record_opt_set_free(record->pifs); + xen_sr_record_opt_free(record->suspend_image_sr); + xen_sr_record_opt_free(record->crash_dump_sr); + xen_pbd_record_opt_set_free(record->pbds); + xen_host_cpu_record_opt_set_free(record->host_cpus); + xen_host_metrics_record_opt_free(record->metrics); + free(record); +} + + +bool +xen_host_get_record(xen_session *session, xen_host_record **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = xen_host_record_abstract_type_; + + *result = NULL; + XEN_CALL_("host.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_host_get_by_uuid(xen_session *session, xen_host *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_by_uuid"); + return session->ok; +} + + +bool +xen_host_get_by_name_label(xen_session *session, struct xen_host_set **result, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = label } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_by_name_label"); + return session->ok; +} + + +bool +xen_host_get_name_label(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_name_label"); + return session->ok; +} + + +bool +xen_host_get_name_description(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_name_description"); + return session->ok; +} + + +bool +xen_host_get_api_version_major(xen_session *session, int64_t *result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("host.get_API_version_major"); + return session->ok; +} + + +bool +xen_host_get_api_version_minor(xen_session *session, int64_t *result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("host.get_API_version_minor"); + return session->ok; +} + + +bool +xen_host_get_api_version_vendor(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_API_version_vendor"); + return session->ok; +} + + +bool +xen_host_get_api_version_vendor_implementation(xen_session *session, xen_string_string_map **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("host.get_API_version_vendor_implementation"); + return session->ok; +} + + +bool +xen_host_get_enabled(xen_session *session, bool *result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("host.get_enabled"); + return session->ok; +} + + +bool +xen_host_get_software_version(xen_session *session, xen_string_string_map **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("host.get_software_version"); + return session->ok; +} + + +bool +xen_host_get_other_config(xen_session *session, xen_string_string_map **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("host.get_other_config"); + return session->ok; +} + + +bool +xen_host_get_capabilities(xen_session *session, struct xen_string_set **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_capabilities"); + return session->ok; +} + + +bool +xen_host_get_cpu_configuration(xen_session *session, xen_string_string_map **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("host.get_cpu_configuration"); + return session->ok; +} + + +bool +xen_host_get_sched_policy(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_sched_policy"); + return session->ok; +} + + +bool +xen_host_get_supported_bootloaders(xen_session *session, struct xen_string_set **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_supported_bootloaders"); + return session->ok; +} + + +bool +xen_host_get_resident_vms(xen_session *session, struct xen_vm_set **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_resident_VMs"); + return session->ok; +} + + +bool +xen_host_get_logging(xen_session *session, xen_string_string_map **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("host.get_logging"); + return session->ok; +} + + +bool +xen_host_get_pifs(xen_session *session, struct xen_pif_set **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_PIFs"); + return session->ok; +} + + +bool +xen_host_get_suspend_image_sr(xen_session *session, xen_sr *result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_suspend_image_sr"); + return session->ok; +} + + +bool +xen_host_get_crash_dump_sr(xen_session *session, xen_sr *result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_crash_dump_sr"); + return session->ok; +} + + +bool +xen_host_get_pbds(xen_session *session, struct xen_pbd_set **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_PBDs"); + return session->ok; +} + + +bool +xen_host_get_host_cpus(xen_session *session, struct xen_host_cpu_set **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("host.get_host_CPUs"); + return session->ok; +} + + +bool +xen_host_get_metrics(xen_session *session, xen_host_metrics *result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_metrics"); + return session->ok; +} + + +bool +xen_host_set_name_label(xen_session *session, xen_host host, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = label } + }; + + xen_call_(session, "host.set_name_label", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_set_name_description(xen_session *session, xen_host host, char *description) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = description } + }; + + xen_call_(session, "host.set_name_description", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_set_other_config(xen_session *session, xen_host host, xen_string_string_map *other_config) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)other_config } + }; + + xen_call_(session, "host.set_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_add_to_other_config(xen_session *session, xen_host host, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "host.add_to_other_config", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_host_remove_from_other_config(xen_session *session, xen_host host, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "host.remove_from_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_set_logging(xen_session *session, xen_host host, xen_string_string_map *logging) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)logging } + }; + + xen_call_(session, "host.set_logging", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_add_to_logging(xen_session *session, xen_host host, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "host.add_to_logging", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_host_remove_from_logging(xen_session *session, xen_host host, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "host.remove_from_logging", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_set_suspend_image_sr(xen_session *session, xen_host host, xen_sr suspend_image_sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = suspend_image_sr } + }; + + xen_call_(session, "host.set_suspend_image_sr", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_set_crash_dump_sr(xen_session *session, xen_host host, xen_sr crash_dump_sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = crash_dump_sr } + }; + + xen_call_(session, "host.set_crash_dump_sr", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_disable(xen_session *session, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + xen_call_(session, "host.disable", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_host_enable(xen_session *session, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + xen_call_(session, "host.enable", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_host_shutdown(xen_session *session, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + xen_call_(session, "host.shutdown", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_host_reboot(xen_session *session, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + xen_call_(session, "host.reboot", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_host_dmesg(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.dmesg"); + return session->ok; +} + + +bool +xen_host_dmesg_clear(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.dmesg_clear"); + return session->ok; +} + + +bool +xen_host_get_log(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_log"); + return session->ok; +} + + +bool +xen_host_send_debug_keys(xen_session *session, xen_host host, char *keys) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_string, + .u.string_val = keys } + }; + + xen_call_(session, "host.send_debug_keys", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_host_list_methods(xen_session *session, struct xen_string_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "host.list_methods", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_host_get_all(xen_session *session, struct xen_host_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "host.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_host_get_uuid(xen_session *session, char **result, xen_host host) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_host_cpu.c b/tools/libxen/src/xen_host_cpu.c new file mode 100644 index 0000000..6e84b49 --- /dev/null +++ b/tools/libxen/src/xen_host_cpu.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include + + +XEN_FREE(xen_host_cpu) +XEN_SET_ALLOC_FREE(xen_host_cpu) +XEN_ALLOC(xen_host_cpu_record) +XEN_SET_ALLOC_FREE(xen_host_cpu_record) +XEN_ALLOC(xen_host_cpu_record_opt) +XEN_RECORD_OPT_FREE(xen_host_cpu) +XEN_SET_ALLOC_FREE(xen_host_cpu_record_opt) + + +static const struct_member xen_host_cpu_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_host_cpu_record, uuid) }, + { .key = "host", + .type = &abstract_type_ref, + .offset = offsetof(xen_host_cpu_record, host) }, + { .key = "number", + .type = &abstract_type_int, + .offset = offsetof(xen_host_cpu_record, number) }, + { .key = "vendor", + .type = &abstract_type_string, + .offset = offsetof(xen_host_cpu_record, vendor) }, + { .key = "speed", + .type = &abstract_type_int, + .offset = offsetof(xen_host_cpu_record, speed) }, + { .key = "modelname", + .type = &abstract_type_string, + .offset = offsetof(xen_host_cpu_record, modelname) }, + { .key = "stepping", + .type = &abstract_type_string, + .offset = offsetof(xen_host_cpu_record, stepping) }, + { .key = "flags", + .type = &abstract_type_string, + .offset = offsetof(xen_host_cpu_record, flags) }, + { .key = "features", + .type = &abstract_type_string, + .offset = offsetof(xen_host_cpu_record, features) }, + { .key = "utilisation", + .type = &abstract_type_float, + .offset = offsetof(xen_host_cpu_record, utilisation) } + }; + +const abstract_type xen_host_cpu_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_host_cpu_record), + .member_count = + sizeof(xen_host_cpu_record_struct_members) / sizeof(struct_member), + .members = xen_host_cpu_record_struct_members + }; + + +void +xen_host_cpu_record_free(xen_host_cpu_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_host_record_opt_free(record->host); + free(record->vendor); + free(record->modelname); + free(record->stepping); + free(record->flags); + free(record->features); + free(record); +} + + +bool +xen_host_cpu_get_record(xen_session *session, xen_host_cpu_record **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = xen_host_cpu_record_abstract_type_; + + *result = NULL; + XEN_CALL_("host_cpu.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_host_cpu_get_by_uuid(xen_session *session, xen_host_cpu *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_by_uuid"); + return session->ok; +} + + +bool +xen_host_cpu_get_host(xen_session *session, xen_host *result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_host"); + return session->ok; +} + + +bool +xen_host_cpu_get_number(xen_session *session, int64_t *result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("host_cpu.get_number"); + return session->ok; +} + + +bool +xen_host_cpu_get_vendor(xen_session *session, char **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_vendor"); + return session->ok; +} + + +bool +xen_host_cpu_get_speed(xen_session *session, int64_t *result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("host_cpu.get_speed"); + return session->ok; +} + + +bool +xen_host_cpu_get_modelname(xen_session *session, char **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_modelname"); + return session->ok; +} + + +bool +xen_host_cpu_get_stepping(xen_session *session, char **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_stepping"); + return session->ok; +} + + +bool +xen_host_cpu_get_flags(xen_session *session, char **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_flags"); + return session->ok; +} + + +bool +xen_host_cpu_get_features(xen_session *session, char **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_features"); + return session->ok; +} + + +bool +xen_host_cpu_get_utilisation(xen_session *session, double *result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("host_cpu.get_utilisation"); + return session->ok; +} + + +bool +xen_host_cpu_get_all(xen_session *session, struct xen_host_cpu_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "host_cpu.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_host_cpu_get_uuid(xen_session *session, char **result, xen_host_cpu host_cpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_cpu } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_cpu.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_host_metrics.c b/tools/libxen/src/xen_host_metrics.c new file mode 100644 index 0000000..976f4c5 --- /dev/null +++ b/tools/libxen/src/xen_host_metrics.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include + + +XEN_FREE(xen_host_metrics) +XEN_SET_ALLOC_FREE(xen_host_metrics) +XEN_ALLOC(xen_host_metrics_record) +XEN_SET_ALLOC_FREE(xen_host_metrics_record) +XEN_ALLOC(xen_host_metrics_record_opt) +XEN_RECORD_OPT_FREE(xen_host_metrics) +XEN_SET_ALLOC_FREE(xen_host_metrics_record_opt) + + +static const struct_member xen_host_metrics_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_host_metrics_record, uuid) }, + { .key = "memory_total", + .type = &abstract_type_int, + .offset = offsetof(xen_host_metrics_record, memory_total) }, + { .key = "memory_free", + .type = &abstract_type_int, + .offset = offsetof(xen_host_metrics_record, memory_free) }, + { .key = "last_updated", + .type = &abstract_type_datetime, + .offset = offsetof(xen_host_metrics_record, last_updated) } + }; + +const abstract_type xen_host_metrics_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_host_metrics_record), + .member_count = + sizeof(xen_host_metrics_record_struct_members) / sizeof(struct_member), + .members = xen_host_metrics_record_struct_members + }; + + +void +xen_host_metrics_record_free(xen_host_metrics_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record); +} + + +bool +xen_host_metrics_get_record(xen_session *session, xen_host_metrics_record **result, xen_host_metrics host_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_metrics } + }; + + abstract_type result_type = xen_host_metrics_record_abstract_type_; + + *result = NULL; + XEN_CALL_("host_metrics.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_host_metrics_get_by_uuid(xen_session *session, xen_host_metrics *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_metrics.get_by_uuid"); + return session->ok; +} + + +bool +xen_host_metrics_get_memory_total(xen_session *session, int64_t *result, xen_host_metrics host_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_metrics } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("host_metrics.get_memory_total"); + return session->ok; +} + + +bool +xen_host_metrics_get_memory_free(xen_session *session, int64_t *result, xen_host_metrics host_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_metrics } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("host_metrics.get_memory_free"); + return session->ok; +} + + +bool +xen_host_metrics_get_last_updated(xen_session *session, time_t *result, xen_host_metrics host_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("host_metrics.get_last_updated"); + return session->ok; +} + + +bool +xen_host_metrics_get_all(xen_session *session, struct xen_host_metrics_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "host_metrics.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_host_metrics_get_uuid(xen_session *session, char **result, xen_host_metrics host_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = host_metrics } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("host_metrics.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_int_float_map.c b/tools/libxen/src/xen_int_float_map.c new file mode 100644 index 0000000..ed3bf48 --- /dev/null +++ b/tools/libxen/src/xen_int_float_map.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "xen_internal.h" +#include +#include + + +xen_int_float_map * +xen_int_float_map_alloc(size_t size) +{ + xen_int_float_map *result = calloc(1, sizeof(xen_int_float_map) + + size * sizeof(struct xen_int_float_map_contents)); + result->size = size; + return result; +} + + +void +xen_int_float_map_free(xen_int_float_map *map) +{ + free(map); +} diff --git a/tools/libxen/src/xen_int_int_map.c b/tools/libxen/src/xen_int_int_map.c new file mode 100644 index 0000000..3d87c22 --- /dev/null +++ b/tools/libxen/src/xen_int_int_map.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "xen_internal.h" +#include +#include + + +xen_int_int_map * +xen_int_int_map_alloc(size_t size) +{ + xen_int_int_map *result = calloc(1, sizeof(xen_int_int_map) + + size * sizeof(struct xen_int_int_map_contents)); + result->size = size; + return result; +} + + +void +xen_int_int_map_free(xen_int_int_map *map) +{ + free(map); +} diff --git a/tools/libxen/src/xen_int_string_set_map.c b/tools/libxen/src/xen_int_string_set_map.c new file mode 100644 index 0000000..7a22cfd --- /dev/null +++ b/tools/libxen/src/xen_int_string_set_map.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "xen_internal.h" +#include +#include +#include + + +xen_int_string_set_map * +xen_int_string_set_map_alloc(size_t size) +{ + xen_int_string_set_map *result = calloc(1, sizeof(xen_int_string_set_map) + + size * sizeof(struct xen_int_string_set_map_contents)); + result->size = size; + return result; +} + + +void +xen_int_string_set_map_free(xen_int_string_set_map *map) +{ + size_t n; + + if (map == NULL) + { + return; + } + + n = map->size; + for (size_t i = 0; i < n; i++) + { + + xen_string_set_free(map->contents[i].val); + } + + free(map); +} diff --git a/tools/libxen/src/xen_network.c b/tools/libxen/src/xen_network.c new file mode 100644 index 0000000..4b03ead --- /dev/null +++ b/tools/libxen/src/xen_network.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include +#include + + +XEN_FREE(xen_network) +XEN_SET_ALLOC_FREE(xen_network) +XEN_ALLOC(xen_network_record) +XEN_SET_ALLOC_FREE(xen_network_record) +XEN_ALLOC(xen_network_record_opt) +XEN_RECORD_OPT_FREE(xen_network) +XEN_SET_ALLOC_FREE(xen_network_record_opt) + + +static const struct_member xen_network_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_network_record, uuid) }, + { .key = "name_label", + .type = &abstract_type_string, + .offset = offsetof(xen_network_record, name_label) }, + { .key = "name_description", + .type = &abstract_type_string, + .offset = offsetof(xen_network_record, name_description) }, + { .key = "VIFs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_network_record, vifs) }, + { .key = "PIFs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_network_record, pifs) }, + { .key = "other_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_network_record, other_config) } + }; + +const abstract_type xen_network_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_network_record), + .member_count = + sizeof(xen_network_record_struct_members) / sizeof(struct_member), + .members = xen_network_record_struct_members + }; + + +void +xen_network_record_free(xen_network_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->name_label); + free(record->name_description); + xen_vif_record_opt_set_free(record->vifs); + xen_pif_record_opt_set_free(record->pifs); + xen_string_string_map_free(record->other_config); + free(record); +} + + +bool +xen_network_get_record(xen_session *session, xen_network_record **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = xen_network_record_abstract_type_; + + *result = NULL; + XEN_CALL_("network.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_network_get_by_uuid(xen_session *session, xen_network *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("network.get_by_uuid"); + return session->ok; +} + + +bool +xen_network_create(xen_session *session, xen_network *result, xen_network_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_network_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("network.create"); + return session->ok; +} + + +bool +xen_network_destroy(xen_session *session, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + xen_call_(session, "network.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_network_get_by_name_label(xen_session *session, struct xen_network_set **result, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = label } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("network.get_by_name_label"); + return session->ok; +} + + +bool +xen_network_get_name_label(xen_session *session, char **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("network.get_name_label"); + return session->ok; +} + + +bool +xen_network_get_name_description(xen_session *session, char **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("network.get_name_description"); + return session->ok; +} + + +bool +xen_network_get_vifs(xen_session *session, struct xen_vif_set **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("network.get_VIFs"); + return session->ok; +} + + +bool +xen_network_get_pifs(xen_session *session, struct xen_pif_set **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("network.get_PIFs"); + return session->ok; +} + + +bool +xen_network_get_other_config(xen_session *session, xen_string_string_map **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("network.get_other_config"); + return session->ok; +} + + +bool +xen_network_set_name_label(xen_session *session, xen_network network, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network }, + { .type = &abstract_type_string, + .u.string_val = label } + }; + + xen_call_(session, "network.set_name_label", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_network_set_name_description(xen_session *session, xen_network network, char *description) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network }, + { .type = &abstract_type_string, + .u.string_val = description } + }; + + xen_call_(session, "network.set_name_description", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_network_set_other_config(xen_session *session, xen_network network, xen_string_string_map *other_config) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)other_config } + }; + + xen_call_(session, "network.set_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_network_add_to_other_config(xen_session *session, xen_network network, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "network.add_to_other_config", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_network_remove_from_other_config(xen_session *session, xen_network network, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "network.remove_from_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_network_get_all(xen_session *session, struct xen_network_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "network.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_network_get_uuid(xen_session *session, char **result, xen_network network) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = network } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("network.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_on_crash_behaviour.c b/tools/libxen/src/xen_on_crash_behaviour.c new file mode 100644 index 0000000..abd25db --- /dev/null +++ b/tools/libxen/src/xen_on_crash_behaviour.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_on_crash_behaviour_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "destroy", + "coredump_and_destroy", + "restart", + "coredump_and_restart", + "preserve", + "rename_restart" +}; + + +extern xen_on_crash_behaviour_set * +xen_on_crash_behaviour_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_on_crash_behaviour_set) + + size * sizeof(enum xen_on_crash_behaviour)); +} + + +extern void +xen_on_crash_behaviour_set_free(xen_on_crash_behaviour_set *set) +{ + free(set); +} + + +const char * +xen_on_crash_behaviour_to_string(enum xen_on_crash_behaviour val) +{ + return lookup_table[val]; +} + + +extern enum xen_on_crash_behaviour +xen_on_crash_behaviour_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_on_crash_behaviour_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_on_crash_behaviour_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_on_crash_behaviour_from_string + }; + + +const abstract_type xen_on_crash_behaviour_set_abstract_type_ = + { + .typename = SET, + .child = &xen_on_crash_behaviour_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_on_normal_exit.c b/tools/libxen/src/xen_on_normal_exit.c new file mode 100644 index 0000000..bbb4e27 --- /dev/null +++ b/tools/libxen/src/xen_on_normal_exit.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_on_normal_exit_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "destroy", + "restart" +}; + + +extern xen_on_normal_exit_set * +xen_on_normal_exit_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_on_normal_exit_set) + + size * sizeof(enum xen_on_normal_exit)); +} + + +extern void +xen_on_normal_exit_set_free(xen_on_normal_exit_set *set) +{ + free(set); +} + + +const char * +xen_on_normal_exit_to_string(enum xen_on_normal_exit val) +{ + return lookup_table[val]; +} + + +extern enum xen_on_normal_exit +xen_on_normal_exit_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_on_normal_exit_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_on_normal_exit_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_on_normal_exit_from_string + }; + + +const abstract_type xen_on_normal_exit_set_abstract_type_ = + { + .typename = SET, + .child = &xen_on_normal_exit_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_pbd.c b/tools/libxen/src/xen_pbd.c new file mode 100644 index 0000000..d3702ef --- /dev/null +++ b/tools/libxen/src/xen_pbd.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include +#include + + +XEN_FREE(xen_pbd) +XEN_SET_ALLOC_FREE(xen_pbd) +XEN_ALLOC(xen_pbd_record) +XEN_SET_ALLOC_FREE(xen_pbd_record) +XEN_ALLOC(xen_pbd_record_opt) +XEN_RECORD_OPT_FREE(xen_pbd) +XEN_SET_ALLOC_FREE(xen_pbd_record_opt) + + +static const struct_member xen_pbd_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_pbd_record, uuid) }, + { .key = "host", + .type = &abstract_type_ref, + .offset = offsetof(xen_pbd_record, host) }, + { .key = "SR", + .type = &abstract_type_ref, + .offset = offsetof(xen_pbd_record, sr) }, + { .key = "device_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_pbd_record, device_config) }, + { .key = "currently_attached", + .type = &abstract_type_bool, + .offset = offsetof(xen_pbd_record, currently_attached) } + }; + +const abstract_type xen_pbd_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_pbd_record), + .member_count = + sizeof(xen_pbd_record_struct_members) / sizeof(struct_member), + .members = xen_pbd_record_struct_members + }; + + +void +xen_pbd_record_free(xen_pbd_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_host_record_opt_free(record->host); + xen_sr_record_opt_free(record->sr); + xen_string_string_map_free(record->device_config); + free(record); +} + + +bool +xen_pbd_get_record(xen_session *session, xen_pbd_record **result, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + abstract_type result_type = xen_pbd_record_abstract_type_; + + *result = NULL; + XEN_CALL_("PBD.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_pbd_get_by_uuid(xen_session *session, xen_pbd *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PBD.get_by_uuid"); + return session->ok; +} + + +bool +xen_pbd_create(xen_session *session, xen_pbd *result, xen_pbd_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_pbd_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PBD.create"); + return session->ok; +} + + +bool +xen_pbd_destroy(xen_session *session, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + xen_call_(session, "PBD.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_pbd_get_host(xen_session *session, xen_host *result, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PBD.get_host"); + return session->ok; +} + + +bool +xen_pbd_get_sr(xen_session *session, xen_sr *result, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PBD.get_SR"); + return session->ok; +} + + +bool +xen_pbd_get_device_config(xen_session *session, xen_string_string_map **result, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("PBD.get_device_config"); + return session->ok; +} + + +bool +xen_pbd_get_currently_attached(xen_session *session, bool *result, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("PBD.get_currently_attached"); + return session->ok; +} + + +bool +xen_pbd_get_all(xen_session *session, struct xen_pbd_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "PBD.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_pbd_get_uuid(xen_session *session, char **result, xen_pbd pbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PBD.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_pif.c b/tools/libxen/src/xen_pif.c new file mode 100644 index 0000000..7cf9a74 --- /dev/null +++ b/tools/libxen/src/xen_pif.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include +#include + + +XEN_FREE(xen_pif) +XEN_SET_ALLOC_FREE(xen_pif) +XEN_ALLOC(xen_pif_record) +XEN_SET_ALLOC_FREE(xen_pif_record) +XEN_ALLOC(xen_pif_record_opt) +XEN_RECORD_OPT_FREE(xen_pif) +XEN_SET_ALLOC_FREE(xen_pif_record_opt) + + +static const struct_member xen_pif_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_pif_record, uuid) }, + { .key = "device", + .type = &abstract_type_string, + .offset = offsetof(xen_pif_record, device) }, + { .key = "network", + .type = &abstract_type_ref, + .offset = offsetof(xen_pif_record, network) }, + { .key = "host", + .type = &abstract_type_ref, + .offset = offsetof(xen_pif_record, host) }, + { .key = "MAC", + .type = &abstract_type_string, + .offset = offsetof(xen_pif_record, mac) }, + { .key = "MTU", + .type = &abstract_type_int, + .offset = offsetof(xen_pif_record, mtu) }, + { .key = "VLAN", + .type = &abstract_type_int, + .offset = offsetof(xen_pif_record, vlan) }, + { .key = "metrics", + .type = &abstract_type_ref, + .offset = offsetof(xen_pif_record, metrics) } + }; + +const abstract_type xen_pif_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_pif_record), + .member_count = + sizeof(xen_pif_record_struct_members) / sizeof(struct_member), + .members = xen_pif_record_struct_members + }; + + +void +xen_pif_record_free(xen_pif_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->device); + xen_network_record_opt_free(record->network); + xen_host_record_opt_free(record->host); + free(record->mac); + xen_pif_metrics_record_opt_free(record->metrics); + free(record); +} + + +bool +xen_pif_get_record(xen_session *session, xen_pif_record **result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = xen_pif_record_abstract_type_; + + *result = NULL; + XEN_CALL_("PIF.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_pif_get_by_uuid(xen_session *session, xen_pif *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_by_uuid"); + return session->ok; +} + + +bool +xen_pif_get_device(xen_session *session, char **result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_device"); + return session->ok; +} + + +bool +xen_pif_get_network(xen_session *session, xen_network *result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_network"); + return session->ok; +} + + +bool +xen_pif_get_host(xen_session *session, xen_host *result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_host"); + return session->ok; +} + + +bool +xen_pif_get_mac(xen_session *session, char **result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_MAC"); + return session->ok; +} + + +bool +xen_pif_get_mtu(xen_session *session, int64_t *result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("PIF.get_MTU"); + return session->ok; +} + + +bool +xen_pif_get_vlan(xen_session *session, int64_t *result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("PIF.get_VLAN"); + return session->ok; +} + + +bool +xen_pif_get_metrics(xen_session *session, xen_pif_metrics *result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_metrics"); + return session->ok; +} + + +bool +xen_pif_set_device(xen_session *session, xen_pif pif, char *device) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif }, + { .type = &abstract_type_string, + .u.string_val = device } + }; + + xen_call_(session, "PIF.set_device", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_pif_set_mac(xen_session *session, xen_pif pif, char *mac) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif }, + { .type = &abstract_type_string, + .u.string_val = mac } + }; + + xen_call_(session, "PIF.set_MAC", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_pif_set_mtu(xen_session *session, xen_pif pif, int64_t mtu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif }, + { .type = &abstract_type_int, + .u.int_val = mtu } + }; + + xen_call_(session, "PIF.set_MTU", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_pif_set_vlan(xen_session *session, xen_pif pif, int64_t vlan) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif }, + { .type = &abstract_type_int, + .u.int_val = vlan } + }; + + xen_call_(session, "PIF.set_VLAN", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_pif_create_vlan(xen_session *session, xen_pif *result, char *device, xen_network network, xen_host host, int64_t vlan) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = device }, + { .type = &abstract_type_string, + .u.string_val = network }, + { .type = &abstract_type_string, + .u.string_val = host }, + { .type = &abstract_type_int, + .u.int_val = vlan } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.create_VLAN"); + return session->ok; +} + + +bool +xen_pif_destroy(xen_session *session, xen_pif self) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self } + }; + + xen_call_(session, "PIF.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_pif_get_all(xen_session *session, struct xen_pif_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "PIF.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_pif_get_uuid(xen_session *session, char **result, xen_pif pif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_pif_metrics.c b/tools/libxen/src/xen_pif_metrics.c new file mode 100644 index 0000000..f150f7f --- /dev/null +++ b/tools/libxen/src/xen_pif_metrics.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include + + +XEN_FREE(xen_pif_metrics) +XEN_SET_ALLOC_FREE(xen_pif_metrics) +XEN_ALLOC(xen_pif_metrics_record) +XEN_SET_ALLOC_FREE(xen_pif_metrics_record) +XEN_ALLOC(xen_pif_metrics_record_opt) +XEN_RECORD_OPT_FREE(xen_pif_metrics) +XEN_SET_ALLOC_FREE(xen_pif_metrics_record_opt) + + +static const struct_member xen_pif_metrics_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_pif_metrics_record, uuid) }, + { .key = "io_read_kbs", + .type = &abstract_type_float, + .offset = offsetof(xen_pif_metrics_record, io_read_kbs) }, + { .key = "io_write_kbs", + .type = &abstract_type_float, + .offset = offsetof(xen_pif_metrics_record, io_write_kbs) }, + { .key = "last_updated", + .type = &abstract_type_datetime, + .offset = offsetof(xen_pif_metrics_record, last_updated) } + }; + +const abstract_type xen_pif_metrics_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_pif_metrics_record), + .member_count = + sizeof(xen_pif_metrics_record_struct_members) / sizeof(struct_member), + .members = xen_pif_metrics_record_struct_members + }; + + +void +xen_pif_metrics_record_free(xen_pif_metrics_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record); +} + + +bool +xen_pif_metrics_get_record(xen_session *session, xen_pif_metrics_record **result, xen_pif_metrics pif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif_metrics } + }; + + abstract_type result_type = xen_pif_metrics_record_abstract_type_; + + *result = NULL; + XEN_CALL_("PIF_metrics.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_pif_metrics_get_by_uuid(xen_session *session, xen_pif_metrics *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF_metrics.get_by_uuid"); + return session->ok; +} + + +bool +xen_pif_metrics_get_io_read_kbs(xen_session *session, double *result, xen_pif_metrics pif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif_metrics } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("PIF_metrics.get_io_read_kbs"); + return session->ok; +} + + +bool +xen_pif_metrics_get_io_write_kbs(xen_session *session, double *result, xen_pif_metrics pif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif_metrics } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("PIF_metrics.get_io_write_kbs"); + return session->ok; +} + + +bool +xen_pif_metrics_get_last_updated(xen_session *session, time_t *result, xen_pif_metrics pif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("PIF_metrics.get_last_updated"); + return session->ok; +} + + +bool +xen_pif_metrics_get_all(xen_session *session, struct xen_pif_metrics_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "PIF_metrics.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_pif_metrics_get_uuid(xen_session *session, char **result, xen_pif_metrics pif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = pif_metrics } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("PIF_metrics.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_sr.c b/tools/libxen/src/xen_sr.c new file mode 100644 index 0000000..b2ff310 --- /dev/null +++ b/tools/libxen/src/xen_sr.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include + + +XEN_FREE(xen_sr) +XEN_SET_ALLOC_FREE(xen_sr) +XEN_ALLOC(xen_sr_record) +XEN_SET_ALLOC_FREE(xen_sr_record) +XEN_ALLOC(xen_sr_record_opt) +XEN_RECORD_OPT_FREE(xen_sr) +XEN_SET_ALLOC_FREE(xen_sr_record_opt) + + +static const struct_member xen_sr_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_sr_record, uuid) }, + { .key = "name_label", + .type = &abstract_type_string, + .offset = offsetof(xen_sr_record, name_label) }, + { .key = "name_description", + .type = &abstract_type_string, + .offset = offsetof(xen_sr_record, name_description) }, + { .key = "VDIs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_sr_record, vdis) }, + { .key = "PBDs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_sr_record, pbds) }, + { .key = "virtual_allocation", + .type = &abstract_type_int, + .offset = offsetof(xen_sr_record, virtual_allocation) }, + { .key = "physical_utilisation", + .type = &abstract_type_int, + .offset = offsetof(xen_sr_record, physical_utilisation) }, + { .key = "physical_size", + .type = &abstract_type_int, + .offset = offsetof(xen_sr_record, physical_size) }, + { .key = "type", + .type = &abstract_type_string, + .offset = offsetof(xen_sr_record, type) }, + { .key = "content_type", + .type = &abstract_type_string, + .offset = offsetof(xen_sr_record, content_type) } + }; + +const abstract_type xen_sr_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_sr_record), + .member_count = + sizeof(xen_sr_record_struct_members) / sizeof(struct_member), + .members = xen_sr_record_struct_members + }; + + +void +xen_sr_record_free(xen_sr_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->name_label); + free(record->name_description); + xen_vdi_record_opt_set_free(record->vdis); + xen_pbd_record_opt_set_free(record->pbds); + free(record->type); + free(record->content_type); + free(record); +} + + +bool +xen_sr_get_record(xen_session *session, xen_sr_record **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = xen_sr_record_abstract_type_; + + *result = NULL; + XEN_CALL_("SR.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_sr_get_by_uuid(xen_session *session, xen_sr *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("SR.get_by_uuid"); + return session->ok; +} + + +bool +xen_sr_get_by_name_label(xen_session *session, struct xen_sr_set **result, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = label } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("SR.get_by_name_label"); + return session->ok; +} + + +bool +xen_sr_get_name_label(xen_session *session, char **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("SR.get_name_label"); + return session->ok; +} + + +bool +xen_sr_get_name_description(xen_session *session, char **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("SR.get_name_description"); + return session->ok; +} + + +bool +xen_sr_get_vdis(xen_session *session, struct xen_vdi_set **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("SR.get_VDIs"); + return session->ok; +} + + +bool +xen_sr_get_pbds(xen_session *session, struct xen_pbd_set **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("SR.get_PBDs"); + return session->ok; +} + + +bool +xen_sr_get_virtual_allocation(xen_session *session, int64_t *result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("SR.get_virtual_allocation"); + return session->ok; +} + + +bool +xen_sr_get_physical_utilisation(xen_session *session, int64_t *result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("SR.get_physical_utilisation"); + return session->ok; +} + + +bool +xen_sr_get_physical_size(xen_session *session, int64_t *result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("SR.get_physical_size"); + return session->ok; +} + + +bool +xen_sr_get_type(xen_session *session, char **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("SR.get_type"); + return session->ok; +} + + +bool +xen_sr_get_content_type(xen_session *session, char **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("SR.get_content_type"); + return session->ok; +} + + +bool +xen_sr_set_name_label(xen_session *session, xen_sr sr, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr }, + { .type = &abstract_type_string, + .u.string_val = label } + }; + + xen_call_(session, "SR.set_name_label", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_sr_set_name_description(xen_session *session, xen_sr sr, char *description) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr }, + { .type = &abstract_type_string, + .u.string_val = description } + }; + + xen_call_(session, "SR.set_name_description", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_sr_get_supported_types(xen_session *session, struct xen_string_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "SR.get_supported_types", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_sr_get_all(xen_session *session, struct xen_sr_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "SR.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_sr_get_uuid(xen_session *session, char **result, xen_sr sr) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = sr } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("SR.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_string_set.c b/tools/libxen/src/xen_string_set.c new file mode 100644 index 0000000..11bc6ce --- /dev/null +++ b/tools/libxen/src/xen_string_set.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "xen_internal.h" +#include + + +xen_string_set * +xen_string_set_alloc(size_t size) +{ + xen_string_set *result = calloc(1, sizeof(xen_string_set) + + size * sizeof(char *)); + result->size = size; + return result; +} + +void +xen_string_set_free(xen_string_set *set) +{ + size_t n; + if (set == NULL) + { + return; + } + n = set->size; + for (size_t i = 0; i < n; i++) + { + free(set->contents[i]); + } + + free(set); +} diff --git a/tools/libxen/src/xen_string_set.h b/tools/libxen/src/xen_string_set.h new file mode 100644 index 0000000..a14af94 --- /dev/null +++ b/tools/libxen/src/xen_string_set.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_STRING_SET_H +#define XEN_STRING_SET_H + + +#include "xen_common.h" + + +typedef struct xen_string_set +{ + size_t size; + char *contents[]; +} xen_string_set; + + +/** + * Allocate a xen_string_set of the given size. + */ +extern xen_string_set * +xen_string_set_alloc(size_t size); + +/** + * Free the given xen_string_set. The given set must have been allocated + * by this library. + */ +extern void +xen_string_set_free(xen_string_set *set); + + +#endif diff --git a/tools/libxen/src/xen_string_string_map.c b/tools/libxen/src/xen_string_string_map.c new file mode 100644 index 0000000..2cf7997 --- /dev/null +++ b/tools/libxen/src/xen_string_string_map.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "xen_internal.h" +#include +#include + + +xen_string_string_map * +xen_string_string_map_alloc(size_t size) +{ + xen_string_string_map *result = calloc(1, sizeof(xen_string_string_map) + + size * sizeof(struct xen_string_string_map_contents)); + result->size = size; + return result; +} + + +void +xen_string_string_map_free(xen_string_string_map *map) +{ + size_t n; + if (map == NULL) + { + return; + } + + n = map->size; + for (size_t i = 0; i < n; i++) + { + free(map->contents[i].key); + free(map->contents[i].val); + } + + free(map); +} diff --git a/tools/libxen/src/xen_user.c b/tools/libxen/src/xen_user.c new file mode 100644 index 0000000..377f8f8 --- /dev/null +++ b/tools/libxen/src/xen_user.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include + + +XEN_FREE(xen_user) +XEN_SET_ALLOC_FREE(xen_user) +XEN_ALLOC(xen_user_record) +XEN_SET_ALLOC_FREE(xen_user_record) +XEN_ALLOC(xen_user_record_opt) +XEN_RECORD_OPT_FREE(xen_user) +XEN_SET_ALLOC_FREE(xen_user_record_opt) + + +static const struct_member xen_user_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_user_record, uuid) }, + { .key = "short_name", + .type = &abstract_type_string, + .offset = offsetof(xen_user_record, short_name) }, + { .key = "fullname", + .type = &abstract_type_string, + .offset = offsetof(xen_user_record, fullname) } + }; + +const abstract_type xen_user_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_user_record), + .member_count = + sizeof(xen_user_record_struct_members) / sizeof(struct_member), + .members = xen_user_record_struct_members + }; + + +void +xen_user_record_free(xen_user_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->short_name); + free(record->fullname); + free(record); +} + + +bool +xen_user_get_record(xen_session *session, xen_user_record **result, xen_user user) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = user } + }; + + abstract_type result_type = xen_user_record_abstract_type_; + + *result = NULL; + XEN_CALL_("user.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_user_get_by_uuid(xen_session *session, xen_user *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("user.get_by_uuid"); + return session->ok; +} + + +bool +xen_user_create(xen_session *session, xen_user *result, xen_user_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_user_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("user.create"); + return session->ok; +} + + +bool +xen_user_destroy(xen_session *session, xen_user user) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = user } + }; + + xen_call_(session, "user.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_user_get_short_name(xen_session *session, char **result, xen_user user) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = user } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("user.get_short_name"); + return session->ok; +} + + +bool +xen_user_get_fullname(xen_session *session, char **result, xen_user user) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = user } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("user.get_fullname"); + return session->ok; +} + + +bool +xen_user_set_fullname(xen_session *session, xen_user user, char *fullname) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = user }, + { .type = &abstract_type_string, + .u.string_val = fullname } + }; + + xen_call_(session, "user.set_fullname", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_user_get_uuid(xen_session *session, char **result, xen_user user) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = user } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("user.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vbd.c b/tools/libxen/src/xen_vbd.c new file mode 100644 index 0000000..9efbca4 --- /dev/null +++ b/tools/libxen/src/xen_vbd.c @@ -0,0 +1,626 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include "xen_vbd_mode_internal.h" +#include "xen_vbd_type_internal.h" +#include +#include +#include +#include +#include +#include + + +XEN_FREE(xen_vbd) +XEN_SET_ALLOC_FREE(xen_vbd) +XEN_ALLOC(xen_vbd_record) +XEN_SET_ALLOC_FREE(xen_vbd_record) +XEN_ALLOC(xen_vbd_record_opt) +XEN_RECORD_OPT_FREE(xen_vbd) +XEN_SET_ALLOC_FREE(xen_vbd_record_opt) + + +static const struct_member xen_vbd_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vbd_record, uuid) }, + { .key = "VM", + .type = &abstract_type_ref, + .offset = offsetof(xen_vbd_record, vm) }, + { .key = "VDI", + .type = &abstract_type_ref, + .offset = offsetof(xen_vbd_record, vdi) }, + { .key = "device", + .type = &abstract_type_string, + .offset = offsetof(xen_vbd_record, device) }, + { .key = "bootable", + .type = &abstract_type_bool, + .offset = offsetof(xen_vbd_record, bootable) }, + { .key = "mode", + .type = &xen_vbd_mode_abstract_type_, + .offset = offsetof(xen_vbd_record, mode) }, + { .key = "type", + .type = &xen_vbd_type_abstract_type_, + .offset = offsetof(xen_vbd_record, type) }, + { .key = "currently_attached", + .type = &abstract_type_bool, + .offset = offsetof(xen_vbd_record, currently_attached) }, + { .key = "status_code", + .type = &abstract_type_int, + .offset = offsetof(xen_vbd_record, status_code) }, + { .key = "status_detail", + .type = &abstract_type_string, + .offset = offsetof(xen_vbd_record, status_detail) }, + { .key = "runtime_properties", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vbd_record, runtime_properties) }, + { .key = "qos_algorithm_type", + .type = &abstract_type_string, + .offset = offsetof(xen_vbd_record, qos_algorithm_type) }, + { .key = "qos_algorithm_params", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vbd_record, qos_algorithm_params) }, + { .key = "qos_supported_algorithms", + .type = &abstract_type_string_set, + .offset = offsetof(xen_vbd_record, qos_supported_algorithms) }, + { .key = "metrics", + .type = &abstract_type_ref, + .offset = offsetof(xen_vbd_record, metrics) } + }; + +const abstract_type xen_vbd_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vbd_record), + .member_count = + sizeof(xen_vbd_record_struct_members) / sizeof(struct_member), + .members = xen_vbd_record_struct_members + }; + + +void +xen_vbd_record_free(xen_vbd_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_vm_record_opt_free(record->vm); + xen_vdi_record_opt_free(record->vdi); + free(record->device); + free(record->status_detail); + xen_string_string_map_free(record->runtime_properties); + free(record->qos_algorithm_type); + xen_string_string_map_free(record->qos_algorithm_params); + xen_string_set_free(record->qos_supported_algorithms); + xen_vbd_metrics_record_opt_free(record->metrics); + free(record); +} + + +bool +xen_vbd_get_record(xen_session *session, xen_vbd_record **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = xen_vbd_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VBD.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vbd_get_by_uuid(xen_session *session, xen_vbd *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_by_uuid"); + return session->ok; +} + + +bool +xen_vbd_create(xen_session *session, xen_vbd *result, xen_vbd_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_vbd_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.create"); + return session->ok; +} + + +bool +xen_vbd_destroy(xen_session *session, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + xen_call_(session, "VBD.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_get_vm(xen_session *session, xen_vm *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_VM"); + return session->ok; +} + + +bool +xen_vbd_get_vdi(xen_session *session, xen_vdi *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_VDI"); + return session->ok; +} + + +bool +xen_vbd_get_device(xen_session *session, char **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_device"); + return session->ok; +} + + +bool +xen_vbd_get_bootable(xen_session *session, bool *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VBD.get_bootable"); + return session->ok; +} + + +bool +xen_vbd_get_mode(xen_session *session, enum xen_vbd_mode *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = xen_vbd_mode_abstract_type_; + XEN_CALL_("VBD.get_mode"); + return session->ok; +} + + +bool +xen_vbd_get_type(xen_session *session, enum xen_vbd_type *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = xen_vbd_type_abstract_type_; + XEN_CALL_("VBD.get_type"); + return session->ok; +} + + +bool +xen_vbd_get_currently_attached(xen_session *session, bool *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VBD.get_currently_attached"); + return session->ok; +} + + +bool +xen_vbd_get_status_code(xen_session *session, int64_t *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VBD.get_status_code"); + return session->ok; +} + + +bool +xen_vbd_get_status_detail(xen_session *session, char **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_status_detail"); + return session->ok; +} + + +bool +xen_vbd_get_runtime_properties(xen_session *session, xen_string_string_map **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VBD.get_runtime_properties"); + return session->ok; +} + + +bool +xen_vbd_get_qos_algorithm_type(xen_session *session, char **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_qos_algorithm_type"); + return session->ok; +} + + +bool +xen_vbd_get_qos_algorithm_params(xen_session *session, xen_string_string_map **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VBD.get_qos_algorithm_params"); + return session->ok; +} + + +bool +xen_vbd_get_qos_supported_algorithms(xen_session *session, struct xen_string_set **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VBD.get_qos_supported_algorithms"); + return session->ok; +} + + +bool +xen_vbd_get_metrics(xen_session *session, xen_vbd_metrics *result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_metrics"); + return session->ok; +} + + +bool +xen_vbd_set_device(xen_session *session, xen_vbd vbd, char *device) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_string, + .u.string_val = device } + }; + + xen_call_(session, "VBD.set_device", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_set_bootable(xen_session *session, xen_vbd vbd, bool bootable) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_bool, + .u.bool_val = bootable } + }; + + xen_call_(session, "VBD.set_bootable", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_set_mode(xen_session *session, xen_vbd vbd, enum xen_vbd_mode mode) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &xen_vbd_mode_abstract_type_, + .u.enum_val = mode } + }; + + xen_call_(session, "VBD.set_mode", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_set_type(xen_session *session, xen_vbd vbd, enum xen_vbd_type type) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &xen_vbd_type_abstract_type_, + .u.enum_val = type } + }; + + xen_call_(session, "VBD.set_type", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_set_qos_algorithm_type(xen_session *session, xen_vbd vbd, char *algorithm_type) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_string, + .u.string_val = algorithm_type } + }; + + xen_call_(session, "VBD.set_qos_algorithm_type", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_set_qos_algorithm_params(xen_session *session, xen_vbd vbd, xen_string_string_map *algorithm_params) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)algorithm_params } + }; + + xen_call_(session, "VBD.set_qos_algorithm_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_add_to_qos_algorithm_params(xen_session *session, xen_vbd vbd, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VBD.add_to_qos_algorithm_params", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_remove_from_qos_algorithm_params(xen_session *session, xen_vbd vbd, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VBD.remove_from_qos_algorithm_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_media_change(xen_session *session, xen_vbd vbd, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd }, + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + xen_call_(session, "VBD.media_change", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_plug(xen_session *session, xen_vbd self) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self } + }; + + xen_call_(session, "VBD.plug", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_unplug(xen_session *session, xen_vbd self) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self } + }; + + xen_call_(session, "VBD.unplug", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vbd_get_all(xen_session *session, struct xen_vbd_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VBD.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vbd_get_uuid(xen_session *session, char **result, xen_vbd vbd) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vbd_metrics.c b/tools/libxen/src/xen_vbd_metrics.c new file mode 100644 index 0000000..c4f336c --- /dev/null +++ b/tools/libxen/src/xen_vbd_metrics.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include + + +XEN_FREE(xen_vbd_metrics) +XEN_SET_ALLOC_FREE(xen_vbd_metrics) +XEN_ALLOC(xen_vbd_metrics_record) +XEN_SET_ALLOC_FREE(xen_vbd_metrics_record) +XEN_ALLOC(xen_vbd_metrics_record_opt) +XEN_RECORD_OPT_FREE(xen_vbd_metrics) +XEN_SET_ALLOC_FREE(xen_vbd_metrics_record_opt) + + +static const struct_member xen_vbd_metrics_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vbd_metrics_record, uuid) }, + { .key = "io_read_kbs", + .type = &abstract_type_float, + .offset = offsetof(xen_vbd_metrics_record, io_read_kbs) }, + { .key = "io_write_kbs", + .type = &abstract_type_float, + .offset = offsetof(xen_vbd_metrics_record, io_write_kbs) }, + { .key = "last_updated", + .type = &abstract_type_datetime, + .offset = offsetof(xen_vbd_metrics_record, last_updated) } + }; + +const abstract_type xen_vbd_metrics_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vbd_metrics_record), + .member_count = + sizeof(xen_vbd_metrics_record_struct_members) / sizeof(struct_member), + .members = xen_vbd_metrics_record_struct_members + }; + + +void +xen_vbd_metrics_record_free(xen_vbd_metrics_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record); +} + + +bool +xen_vbd_metrics_get_record(xen_session *session, xen_vbd_metrics_record **result, xen_vbd_metrics vbd_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd_metrics } + }; + + abstract_type result_type = xen_vbd_metrics_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VBD_metrics.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vbd_metrics_get_by_uuid(xen_session *session, xen_vbd_metrics *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD_metrics.get_by_uuid"); + return session->ok; +} + + +bool +xen_vbd_metrics_get_io_read_kbs(xen_session *session, double *result, xen_vbd_metrics vbd_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd_metrics } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("VBD_metrics.get_io_read_kbs"); + return session->ok; +} + + +bool +xen_vbd_metrics_get_io_write_kbs(xen_session *session, double *result, xen_vbd_metrics vbd_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd_metrics } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("VBD_metrics.get_io_write_kbs"); + return session->ok; +} + + +bool +xen_vbd_metrics_get_last_updated(xen_session *session, time_t *result, xen_vbd_metrics vbd_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("VBD_metrics.get_last_updated"); + return session->ok; +} + + +bool +xen_vbd_metrics_get_all(xen_session *session, struct xen_vbd_metrics_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VBD_metrics.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vbd_metrics_get_uuid(xen_session *session, char **result, xen_vbd_metrics vbd_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vbd_metrics } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VBD_metrics.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vbd_mode.c b/tools/libxen/src/xen_vbd_mode.c new file mode 100644 index 0000000..71abc42 --- /dev/null +++ b/tools/libxen/src/xen_vbd_mode.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_vbd_mode_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "RO", + "RW" +}; + + +extern xen_vbd_mode_set * +xen_vbd_mode_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_vbd_mode_set) + + size * sizeof(enum xen_vbd_mode)); +} + + +extern void +xen_vbd_mode_set_free(xen_vbd_mode_set *set) +{ + free(set); +} + + +const char * +xen_vbd_mode_to_string(enum xen_vbd_mode val) +{ + return lookup_table[val]; +} + + +extern enum xen_vbd_mode +xen_vbd_mode_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_vbd_mode_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_vbd_mode_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_vbd_mode_from_string + }; + + +const abstract_type xen_vbd_mode_set_abstract_type_ = + { + .typename = SET, + .child = &xen_vbd_mode_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_vbd_type.c b/tools/libxen/src/xen_vbd_type.c new file mode 100644 index 0000000..0572635 --- /dev/null +++ b/tools/libxen/src/xen_vbd_type.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_vbd_type_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "CD", + "Disk" +}; + + +extern xen_vbd_type_set * +xen_vbd_type_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_vbd_type_set) + + size * sizeof(enum xen_vbd_type)); +} + + +extern void +xen_vbd_type_set_free(xen_vbd_type_set *set) +{ + free(set); +} + + +const char * +xen_vbd_type_to_string(enum xen_vbd_type val) +{ + return lookup_table[val]; +} + + +extern enum xen_vbd_type +xen_vbd_type_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_vbd_type_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_vbd_type_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_vbd_type_from_string + }; + + +const abstract_type xen_vbd_type_set_abstract_type_ = + { + .typename = SET, + .child = &xen_vbd_type_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_vdi.c b/tools/libxen/src/xen_vdi.c new file mode 100644 index 0000000..f2182cc --- /dev/null +++ b/tools/libxen/src/xen_vdi.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include "xen_vdi_type_internal.h" +#include +#include +#include +#include +#include +#include + + +XEN_FREE(xen_vdi) +XEN_SET_ALLOC_FREE(xen_vdi) +XEN_ALLOC(xen_vdi_record) +XEN_SET_ALLOC_FREE(xen_vdi_record) +XEN_ALLOC(xen_vdi_record_opt) +XEN_RECORD_OPT_FREE(xen_vdi) +XEN_SET_ALLOC_FREE(xen_vdi_record_opt) + + +static const struct_member xen_vdi_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vdi_record, uuid) }, + { .key = "name_label", + .type = &abstract_type_string, + .offset = offsetof(xen_vdi_record, name_label) }, + { .key = "name_description", + .type = &abstract_type_string, + .offset = offsetof(xen_vdi_record, name_description) }, + { .key = "SR", + .type = &abstract_type_ref, + .offset = offsetof(xen_vdi_record, sr) }, + { .key = "VBDs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vdi_record, vbds) }, + { .key = "crash_dumps", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vdi_record, crash_dumps) }, + { .key = "virtual_size", + .type = &abstract_type_int, + .offset = offsetof(xen_vdi_record, virtual_size) }, + { .key = "physical_utilisation", + .type = &abstract_type_int, + .offset = offsetof(xen_vdi_record, physical_utilisation) }, + { .key = "type", + .type = &xen_vdi_type_abstract_type_, + .offset = offsetof(xen_vdi_record, type) }, + { .key = "sharable", + .type = &abstract_type_bool, + .offset = offsetof(xen_vdi_record, sharable) }, + { .key = "read_only", + .type = &abstract_type_bool, + .offset = offsetof(xen_vdi_record, read_only) }, + { .key = "other_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vdi_record, other_config) } + }; + +const abstract_type xen_vdi_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vdi_record), + .member_count = + sizeof(xen_vdi_record_struct_members) / sizeof(struct_member), + .members = xen_vdi_record_struct_members + }; + + +void +xen_vdi_record_free(xen_vdi_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->name_label); + free(record->name_description); + xen_sr_record_opt_free(record->sr); + xen_vbd_record_opt_set_free(record->vbds); + xen_crashdump_record_opt_set_free(record->crash_dumps); + xen_string_string_map_free(record->other_config); + free(record); +} + + +bool +xen_vdi_get_record(xen_session *session, xen_vdi_record **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = xen_vdi_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VDI.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vdi_get_by_uuid(xen_session *session, xen_vdi *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.get_by_uuid"); + return session->ok; +} + + +bool +xen_vdi_create(xen_session *session, xen_vdi *result, xen_vdi_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_vdi_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.create"); + return session->ok; +} + + +bool +xen_vdi_destroy(xen_session *session, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + xen_call_(session, "VDI.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_get_by_name_label(xen_session *session, struct xen_vdi_set **result, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = label } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VDI.get_by_name_label"); + return session->ok; +} + + +bool +xen_vdi_get_name_label(xen_session *session, char **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.get_name_label"); + return session->ok; +} + + +bool +xen_vdi_get_name_description(xen_session *session, char **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.get_name_description"); + return session->ok; +} + + +bool +xen_vdi_get_sr(xen_session *session, xen_sr *result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.get_SR"); + return session->ok; +} + + +bool +xen_vdi_get_vbds(xen_session *session, struct xen_vbd_set **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VDI.get_VBDs"); + return session->ok; +} + + +bool +xen_vdi_get_crash_dumps(xen_session *session, struct xen_crashdump_set **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VDI.get_crash_dumps"); + return session->ok; +} + + +bool +xen_vdi_get_virtual_size(xen_session *session, int64_t *result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VDI.get_virtual_size"); + return session->ok; +} + + +bool +xen_vdi_get_physical_utilisation(xen_session *session, int64_t *result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VDI.get_physical_utilisation"); + return session->ok; +} + + +bool +xen_vdi_get_type(xen_session *session, enum xen_vdi_type *result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = xen_vdi_type_abstract_type_; + XEN_CALL_("VDI.get_type"); + return session->ok; +} + + +bool +xen_vdi_get_sharable(xen_session *session, bool *result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VDI.get_sharable"); + return session->ok; +} + + +bool +xen_vdi_get_read_only(xen_session *session, bool *result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VDI.get_read_only"); + return session->ok; +} + + +bool +xen_vdi_get_other_config(xen_session *session, xen_string_string_map **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VDI.get_other_config"); + return session->ok; +} + + +bool +xen_vdi_set_name_label(xen_session *session, xen_vdi vdi, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_string, + .u.string_val = label } + }; + + xen_call_(session, "VDI.set_name_label", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_set_name_description(xen_session *session, xen_vdi vdi, char *description) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_string, + .u.string_val = description } + }; + + xen_call_(session, "VDI.set_name_description", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_set_virtual_size(xen_session *session, xen_vdi vdi, int64_t virtual_size) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_int, + .u.int_val = virtual_size } + }; + + xen_call_(session, "VDI.set_virtual_size", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_set_sharable(xen_session *session, xen_vdi vdi, bool sharable) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_bool, + .u.bool_val = sharable } + }; + + xen_call_(session, "VDI.set_sharable", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_set_read_only(xen_session *session, xen_vdi vdi, bool read_only) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_bool, + .u.bool_val = read_only } + }; + + xen_call_(session, "VDI.set_read_only", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_set_other_config(xen_session *session, xen_vdi vdi, xen_string_string_map *other_config) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)other_config } + }; + + xen_call_(session, "VDI.set_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_add_to_other_config(xen_session *session, xen_vdi vdi, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VDI.add_to_other_config", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_remove_from_other_config(xen_session *session, xen_vdi vdi, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VDI.remove_from_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vdi_get_all(xen_session *session, struct xen_vdi_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VDI.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vdi_get_uuid(xen_session *session, char **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.get_uuid"); + return session->ok; +} + + +bool +xen_vdi_set_security_label(xen_session *session, int64_t *result, xen_vdi vdi, + char *label, char *oldlabel) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + { .type = &abstract_type_string, + .u.string_val = label }, + { .type = &abstract_type_string, + .u.string_val = oldlabel }, + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("VDI.set_security_label"); + return session->ok; +} + + +bool +xen_vdi_get_security_label(xen_session *session, char **result, xen_vdi vdi) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vdi }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VDI.get_security_label"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vdi_type.c b/tools/libxen/src/xen_vdi_type.c new file mode 100644 index 0000000..4f0bda7 --- /dev/null +++ b/tools/libxen/src/xen_vdi_type.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_vdi_type_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "system", + "user", + "ephemeral", + "suspend", + "crashdump" +}; + + +extern xen_vdi_type_set * +xen_vdi_type_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_vdi_type_set) + + size * sizeof(enum xen_vdi_type)); +} + + +extern void +xen_vdi_type_set_free(xen_vdi_type_set *set) +{ + free(set); +} + + +const char * +xen_vdi_type_to_string(enum xen_vdi_type val) +{ + return lookup_table[val]; +} + + +extern enum xen_vdi_type +xen_vdi_type_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_vdi_type_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_vdi_type_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_vdi_type_from_string + }; + + +const abstract_type xen_vdi_type_set_abstract_type_ = + { + .typename = SET, + .child = &xen_vdi_type_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_vif.c b/tools/libxen/src/xen_vif.c new file mode 100644 index 0000000..ac6147f --- /dev/null +++ b/tools/libxen/src/xen_vif.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include +#include +#include + + +XEN_FREE(xen_vif) +XEN_SET_ALLOC_FREE(xen_vif) +XEN_ALLOC(xen_vif_record) +XEN_SET_ALLOC_FREE(xen_vif_record) +XEN_ALLOC(xen_vif_record_opt) +XEN_RECORD_OPT_FREE(xen_vif) +XEN_SET_ALLOC_FREE(xen_vif_record_opt) + + +static const struct_member xen_vif_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vif_record, uuid) }, + { .key = "device", + .type = &abstract_type_string, + .offset = offsetof(xen_vif_record, device) }, + { .key = "network", + .type = &abstract_type_ref, + .offset = offsetof(xen_vif_record, network) }, + { .key = "VM", + .type = &abstract_type_ref, + .offset = offsetof(xen_vif_record, vm) }, + { .key = "MAC", + .type = &abstract_type_string, + .offset = offsetof(xen_vif_record, mac) }, + { .key = "MTU", + .type = &abstract_type_int, + .offset = offsetof(xen_vif_record, mtu) }, + { .key = "currently_attached", + .type = &abstract_type_bool, + .offset = offsetof(xen_vif_record, currently_attached) }, + { .key = "status_code", + .type = &abstract_type_int, + .offset = offsetof(xen_vif_record, status_code) }, + { .key = "status_detail", + .type = &abstract_type_string, + .offset = offsetof(xen_vif_record, status_detail) }, + { .key = "runtime_properties", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vif_record, runtime_properties) }, + { .key = "qos_algorithm_type", + .type = &abstract_type_string, + .offset = offsetof(xen_vif_record, qos_algorithm_type) }, + { .key = "qos_algorithm_params", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vif_record, qos_algorithm_params) }, + { .key = "qos_supported_algorithms", + .type = &abstract_type_string_set, + .offset = offsetof(xen_vif_record, qos_supported_algorithms) }, + { .key = "metrics", + .type = &abstract_type_ref, + .offset = offsetof(xen_vif_record, metrics) } + }; + +const abstract_type xen_vif_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vif_record), + .member_count = + sizeof(xen_vif_record_struct_members) / sizeof(struct_member), + .members = xen_vif_record_struct_members + }; + + +void +xen_vif_record_free(xen_vif_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->device); + xen_network_record_opt_free(record->network); + xen_vm_record_opt_free(record->vm); + free(record->mac); + free(record->status_detail); + xen_string_string_map_free(record->runtime_properties); + free(record->qos_algorithm_type); + xen_string_string_map_free(record->qos_algorithm_params); + xen_string_set_free(record->qos_supported_algorithms); + xen_vif_metrics_record_opt_free(record->metrics); + free(record); +} + + +bool +xen_vif_get_record(xen_session *session, xen_vif_record **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = xen_vif_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VIF.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vif_get_by_uuid(xen_session *session, xen_vif *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_by_uuid"); + return session->ok; +} + + +bool +xen_vif_create(xen_session *session, xen_vif *result, xen_vif_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_vif_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.create"); + return session->ok; +} + + +bool +xen_vif_destroy(xen_session *session, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + xen_call_(session, "VIF.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_get_device(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_device"); + return session->ok; +} + + +bool +xen_vif_get_network(xen_session *session, xen_network *result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_network"); + return session->ok; +} + + +bool +xen_vif_get_vm(xen_session *session, xen_vm *result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_VM"); + return session->ok; +} + + +bool +xen_vif_get_mac(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_MAC"); + return session->ok; +} + + +bool +xen_vif_get_mtu(xen_session *session, int64_t *result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VIF.get_MTU"); + return session->ok; +} + + +bool +xen_vif_get_currently_attached(xen_session *session, bool *result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VIF.get_currently_attached"); + return session->ok; +} + + +bool +xen_vif_get_status_code(xen_session *session, int64_t *result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VIF.get_status_code"); + return session->ok; +} + + +bool +xen_vif_get_status_detail(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_status_detail"); + return session->ok; +} + + +bool +xen_vif_get_runtime_properties(xen_session *session, xen_string_string_map **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VIF.get_runtime_properties"); + return session->ok; +} + + +bool +xen_vif_get_qos_algorithm_type(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_qos_algorithm_type"); + return session->ok; +} + + +bool +xen_vif_get_qos_algorithm_params(xen_session *session, xen_string_string_map **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VIF.get_qos_algorithm_params"); + return session->ok; +} + + +bool +xen_vif_get_qos_supported_algorithms(xen_session *session, struct xen_string_set **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VIF.get_qos_supported_algorithms"); + return session->ok; +} + + +bool +xen_vif_get_metrics(xen_session *session, xen_vif_metrics *result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_metrics"); + return session->ok; +} + + +bool +xen_vif_set_device(xen_session *session, xen_vif vif, char *device) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = device } + }; + + xen_call_(session, "VIF.set_device", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_set_mac(xen_session *session, xen_vif vif, char *mac) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = mac } + }; + + xen_call_(session, "VIF.set_MAC", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_set_mtu(xen_session *session, xen_vif vif, int64_t mtu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_int, + .u.int_val = mtu } + }; + + xen_call_(session, "VIF.set_MTU", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_set_qos_algorithm_type(xen_session *session, xen_vif vif, char *algorithm_type) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = algorithm_type } + }; + + xen_call_(session, "VIF.set_qos_algorithm_type", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_set_qos_algorithm_params(xen_session *session, xen_vif vif, xen_string_string_map *algorithm_params) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)algorithm_params } + }; + + xen_call_(session, "VIF.set_qos_algorithm_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_add_to_qos_algorithm_params(xen_session *session, xen_vif vif, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VIF.add_to_qos_algorithm_params", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_remove_from_qos_algorithm_params(xen_session *session, xen_vif vif, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VIF.remove_from_qos_algorithm_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_plug(xen_session *session, xen_vif self) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self } + }; + + xen_call_(session, "VIF.plug", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_unplug(xen_session *session, xen_vif self) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self } + }; + + xen_call_(session, "VIF.unplug", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vif_get_all(xen_session *session, struct xen_vif_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VIF.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vif_get_uuid(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_uuid"); + return session->ok; +} + + +bool +xen_vif_set_security_label(xen_session *session, int64_t *result, xen_vif vif, + char *label, char *oldlabel) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + { .type = &abstract_type_string, + .u.string_val = label }, + { .type = &abstract_type_string, + .u.string_val = oldlabel }, + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("VIF.set_security_label"); + return session->ok; +} + + +bool +xen_vif_get_security_label(xen_session *session, char **result, xen_vif vif) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF.get_security_label"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vif_metrics.c b/tools/libxen/src/xen_vif_metrics.c new file mode 100644 index 0000000..9b876e5 --- /dev/null +++ b/tools/libxen/src/xen_vif_metrics.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include + + +XEN_FREE(xen_vif_metrics) +XEN_SET_ALLOC_FREE(xen_vif_metrics) +XEN_ALLOC(xen_vif_metrics_record) +XEN_SET_ALLOC_FREE(xen_vif_metrics_record) +XEN_ALLOC(xen_vif_metrics_record_opt) +XEN_RECORD_OPT_FREE(xen_vif_metrics) +XEN_SET_ALLOC_FREE(xen_vif_metrics_record_opt) + + +static const struct_member xen_vif_metrics_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vif_metrics_record, uuid) }, + { .key = "io_read_kbs", + .type = &abstract_type_float, + .offset = offsetof(xen_vif_metrics_record, io_read_kbs) }, + { .key = "io_write_kbs", + .type = &abstract_type_float, + .offset = offsetof(xen_vif_metrics_record, io_write_kbs) }, + { .key = "last_updated", + .type = &abstract_type_datetime, + .offset = offsetof(xen_vif_metrics_record, last_updated) } + }; + +const abstract_type xen_vif_metrics_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vif_metrics_record), + .member_count = + sizeof(xen_vif_metrics_record_struct_members) / sizeof(struct_member), + .members = xen_vif_metrics_record_struct_members + }; + + +void +xen_vif_metrics_record_free(xen_vif_metrics_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record); +} + + +bool +xen_vif_metrics_get_record(xen_session *session, xen_vif_metrics_record **result, xen_vif_metrics vif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif_metrics } + }; + + abstract_type result_type = xen_vif_metrics_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VIF_metrics.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vif_metrics_get_by_uuid(xen_session *session, xen_vif_metrics *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF_metrics.get_by_uuid"); + return session->ok; +} + + +bool +xen_vif_metrics_get_io_read_kbs(xen_session *session, double *result, xen_vif_metrics vif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif_metrics } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("VIF_metrics.get_io_read_kbs"); + return session->ok; +} + + +bool +xen_vif_metrics_get_io_write_kbs(xen_session *session, double *result, xen_vif_metrics vif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif_metrics } + }; + + abstract_type result_type = abstract_type_float; + + XEN_CALL_("VIF_metrics.get_io_write_kbs"); + return session->ok; +} + + +bool +xen_vif_metrics_get_last_updated(xen_session *session, time_t *result, xen_vif_metrics vif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("VIF_metrics.get_last_updated"); + return session->ok; +} + + +bool +xen_vif_metrics_get_all(xen_session *session, struct xen_vif_metrics_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VIF_metrics.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vif_metrics_get_uuid(xen_session *session, char **result, xen_vif_metrics vif_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vif_metrics } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VIF_metrics.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vm.c b/tools/libxen/src/xen_vm.c new file mode 100644 index 0000000..2b501e5 --- /dev/null +++ b/tools/libxen/src/xen_vm.c @@ -0,0 +1,1783 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include "xen_on_crash_behaviour_internal.h" +#include "xen_on_normal_exit_internal.h" +#include "xen_vm_power_state_internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +XEN_FREE(xen_vm) +XEN_SET_ALLOC_FREE(xen_vm) +XEN_ALLOC(xen_vm_record) +XEN_SET_ALLOC_FREE(xen_vm_record) +XEN_ALLOC(xen_vm_record_opt) +XEN_RECORD_OPT_FREE(xen_vm) +XEN_SET_ALLOC_FREE(xen_vm_record_opt) + + +static const struct_member xen_vm_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, uuid) }, + { .key = "power_state", + .type = &xen_vm_power_state_abstract_type_, + .offset = offsetof(xen_vm_record, power_state) }, + { .key = "name_label", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, name_label) }, + { .key = "name_description", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, name_description) }, + { .key = "user_version", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, user_version) }, + { .key = "is_a_template", + .type = &abstract_type_bool, + .offset = offsetof(xen_vm_record, is_a_template) }, + { .key = "auto_power_on", + .type = &abstract_type_bool, + .offset = offsetof(xen_vm_record, auto_power_on) }, + { .key = "suspend_VDI", + .type = &abstract_type_ref, + .offset = offsetof(xen_vm_record, suspend_vdi) }, + { .key = "resident_on", + .type = &abstract_type_ref, + .offset = offsetof(xen_vm_record, resident_on) }, + { .key = "memory_static_max", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, memory_static_max) }, + { .key = "memory_dynamic_max", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, memory_dynamic_max) }, + { .key = "memory_dynamic_min", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, memory_dynamic_min) }, + { .key = "memory_static_min", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, memory_static_min) }, + { .key = "VCPUs_params", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_record, vcpus_params) }, + { .key = "VCPUs_max", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, vcpus_max) }, + { .key = "VCPUs_at_startup", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, vcpus_at_startup) }, + { .key = "actions_after_shutdown", + .type = &xen_on_normal_exit_abstract_type_, + .offset = offsetof(xen_vm_record, actions_after_shutdown) }, + { .key = "actions_after_reboot", + .type = &xen_on_normal_exit_abstract_type_, + .offset = offsetof(xen_vm_record, actions_after_reboot) }, + { .key = "actions_after_crash", + .type = &xen_on_crash_behaviour_abstract_type_, + .offset = offsetof(xen_vm_record, actions_after_crash) }, + { .key = "consoles", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vm_record, consoles) }, + { .key = "VIFs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vm_record, vifs) }, + { .key = "VBDs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vm_record, vbds) }, + { .key = "crash_dumps", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vm_record, crash_dumps) }, + { .key = "VTPMs", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vm_record, vtpms) }, + { .key = "PV_bootloader", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, pv_bootloader) }, + { .key = "PV_kernel", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, pv_kernel) }, + { .key = "PV_ramdisk", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, pv_ramdisk) }, + { .key = "PV_args", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, pv_args) }, + { .key = "PV_bootloader_args", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, pv_bootloader_args) }, + { .key = "HVM_boot_policy", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, hvm_boot_policy) }, + { .key = "HVM_boot_params", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_record, hvm_boot_params) }, + { .key = "platform", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_record, platform) }, + { .key = "PCI_bus", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, pci_bus) }, + { .key = "other_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_record, other_config) }, + { .key = "domid", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_record, domid) }, + { .key = "is_control_domain", + .type = &abstract_type_bool, + .offset = offsetof(xen_vm_record, is_control_domain) }, + { .key = "metrics", + .type = &abstract_type_ref, + .offset = offsetof(xen_vm_record, metrics) }, + { .key = "guest_metrics", + .type = &abstract_type_ref, + .offset = offsetof(xen_vm_record, guest_metrics) }, + { .key = "security_label", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_record, security_label) } + }; + +const abstract_type xen_vm_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vm_record), + .member_count = + sizeof(xen_vm_record_struct_members) / sizeof(struct_member), + .members = xen_vm_record_struct_members + }; + + +void +xen_vm_record_free(xen_vm_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->name_label); + free(record->name_description); + xen_vdi_record_opt_free(record->suspend_vdi); + xen_host_record_opt_free(record->resident_on); + xen_string_string_map_free(record->vcpus_params); + xen_console_record_opt_set_free(record->consoles); + xen_vif_record_opt_set_free(record->vifs); + xen_vbd_record_opt_set_free(record->vbds); + xen_crashdump_record_opt_set_free(record->crash_dumps); + xen_vtpm_record_opt_set_free(record->vtpms); + free(record->pv_bootloader); + free(record->pv_kernel); + free(record->pv_ramdisk); + free(record->pv_args); + free(record->pv_bootloader_args); + free(record->hvm_boot_policy); + xen_string_string_map_free(record->hvm_boot_params); + xen_string_string_map_free(record->platform); + free(record->pci_bus); + xen_string_string_map_free(record->other_config); + xen_vm_metrics_record_opt_free(record->metrics); + xen_vm_guest_metrics_record_opt_free(record->guest_metrics); + free(record->security_label); + free(record); +} + + +bool +xen_vm_get_record(xen_session *session, xen_vm_record **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = xen_vm_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VM.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vm_get_by_uuid(xen_session *session, xen_vm *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_by_uuid"); + return session->ok; +} + + +bool +xen_vm_create(xen_session *session, xen_vm *result, xen_vm_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_vm_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.create"); + return session->ok; +} + + +bool +xen_vm_destroy(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_get_by_name_label(xen_session *session, struct xen_vm_set **result, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = label } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_by_name_label"); + return session->ok; +} + + +bool +xen_vm_get_power_state(xen_session *session, enum xen_vm_power_state *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = xen_vm_power_state_abstract_type_; + XEN_CALL_("VM.get_power_state"); + return session->ok; +} + + +bool +xen_vm_get_name_label(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_name_label"); + return session->ok; +} + + +bool +xen_vm_get_name_description(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_name_description"); + return session->ok; +} + + +bool +xen_vm_get_user_version(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_user_version"); + return session->ok; +} + + +bool +xen_vm_get_is_a_template(xen_session *session, bool *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VM.get_is_a_template"); + return session->ok; +} + + +bool +xen_vm_get_auto_power_on(xen_session *session, bool *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VM.get_auto_power_on"); + return session->ok; +} + + +bool +xen_vm_get_suspend_vdi(xen_session *session, xen_vdi *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_suspend_VDI"); + return session->ok; +} + + +bool +xen_vm_get_resident_on(xen_session *session, xen_host *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_resident_on"); + return session->ok; +} + + +bool +xen_vm_get_memory_static_max(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_memory_static_max"); + return session->ok; +} + + +bool +xen_vm_get_memory_dynamic_max(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_memory_dynamic_max"); + return session->ok; +} + + +bool +xen_vm_get_memory_dynamic_min(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_memory_dynamic_min"); + return session->ok; +} + + +bool +xen_vm_get_memory_static_min(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_memory_static_min"); + return session->ok; +} + + +bool +xen_vm_get_vcpus_params(xen_session *session, xen_string_string_map **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM.get_VCPUs_params"); + return session->ok; +} + + +bool +xen_vm_get_vcpus_max(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_VCPUs_max"); + return session->ok; +} + + +bool +xen_vm_get_vcpus_at_startup(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_VCPUs_at_startup"); + return session->ok; +} + + +bool +xen_vm_get_actions_after_shutdown(xen_session *session, enum xen_on_normal_exit *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = xen_on_normal_exit_abstract_type_; + XEN_CALL_("VM.get_actions_after_shutdown"); + return session->ok; +} + + +bool +xen_vm_get_actions_after_reboot(xen_session *session, enum xen_on_normal_exit *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = xen_on_normal_exit_abstract_type_; + XEN_CALL_("VM.get_actions_after_reboot"); + return session->ok; +} + + +bool +xen_vm_get_actions_after_crash(xen_session *session, enum xen_on_crash_behaviour *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = xen_on_crash_behaviour_abstract_type_; + XEN_CALL_("VM.get_actions_after_crash"); + return session->ok; +} + + +bool +xen_vm_get_consoles(xen_session *session, struct xen_console_set **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_consoles"); + return session->ok; +} + + +bool +xen_vm_get_vifs(xen_session *session, struct xen_vif_set **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_VIFs"); + return session->ok; +} + + +bool +xen_vm_get_vbds(xen_session *session, struct xen_vbd_set **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_VBDs"); + return session->ok; +} + + +bool +xen_vm_get_crash_dumps(xen_session *session, struct xen_crashdump_set **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_crash_dumps"); + return session->ok; +} + + +bool +xen_vm_get_vtpms(xen_session *session, struct xen_vtpm_set **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_VTPMs"); + return session->ok; +} + + +bool +xen_vm_get_pv_bootloader(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_PV_bootloader"); + return session->ok; +} + + +bool +xen_vm_get_pv_kernel(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_PV_kernel"); + return session->ok; +} + + +bool +xen_vm_get_pv_ramdisk(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_PV_ramdisk"); + return session->ok; +} + + +bool +xen_vm_get_pv_args(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_PV_args"); + return session->ok; +} + + +bool +xen_vm_get_pv_bootloader_args(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_PV_bootloader_args"); + return session->ok; +} + + +bool +xen_vm_get_hvm_boot_policy(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_HVM_boot_policy"); + return session->ok; +} + + +bool +xen_vm_get_hvm_boot_params(xen_session *session, xen_string_string_map **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM.get_HVM_boot_params"); + return session->ok; +} + + +bool +xen_vm_get_platform(xen_session *session, xen_string_string_map **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM.get_platform"); + return session->ok; +} + + +bool +xen_vm_get_pci_bus(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_PCI_bus"); + return session->ok; +} + + +bool +xen_vm_get_other_config(xen_session *session, xen_string_string_map **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM.get_other_config"); + return session->ok; +} + + +bool +xen_vm_get_domid(xen_session *session, int64_t *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM.get_domid"); + return session->ok; +} + + +bool +xen_vm_get_is_control_domain(xen_session *session, bool *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_bool; + + XEN_CALL_("VM.get_is_control_domain"); + return session->ok; +} + + +bool +xen_vm_get_metrics(xen_session *session, xen_vm_metrics *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_metrics"); + return session->ok; +} + + +bool +xen_vm_get_guest_metrics(xen_session *session, xen_vm_guest_metrics *result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_guest_metrics"); + return session->ok; +} + + +bool +xen_vm_set_name_label(xen_session *session, xen_vm vm, char *label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = label } + }; + + xen_call_(session, "VM.set_name_label", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_name_description(xen_session *session, xen_vm vm, char *description) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = description } + }; + + xen_call_(session, "VM.set_name_description", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_user_version(xen_session *session, xen_vm vm, int64_t user_version) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = user_version } + }; + + xen_call_(session, "VM.set_user_version", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_is_a_template(xen_session *session, xen_vm vm, bool is_a_template) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_bool, + .u.bool_val = is_a_template } + }; + + xen_call_(session, "VM.set_is_a_template", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_auto_power_on(xen_session *session, xen_vm vm, bool auto_power_on) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_bool, + .u.bool_val = auto_power_on } + }; + + xen_call_(session, "VM.set_auto_power_on", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_memory_static_max(xen_session *session, xen_vm vm, int64_t static_max) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = static_max } + }; + + xen_call_(session, "VM.set_memory_static_max", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_memory_dynamic_max(xen_session *session, xen_vm vm, int64_t dynamic_max) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = dynamic_max } + }; + + xen_call_(session, "VM.set_memory_dynamic_max", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_memory_dynamic_min(xen_session *session, xen_vm vm, int64_t dynamic_min) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = dynamic_min } + }; + + xen_call_(session, "VM.set_memory_dynamic_min", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_memory_static_min(xen_session *session, xen_vm vm, int64_t static_min) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = static_min } + }; + + xen_call_(session, "VM.set_memory_static_min", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_vcpus_params(xen_session *session, xen_vm vm, xen_string_string_map *params) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)params } + }; + + xen_call_(session, "VM.set_VCPUs_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_add_to_vcpus_params(xen_session *session, xen_vm vm, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VM.add_to_VCPUs_params", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_remove_from_vcpus_params(xen_session *session, xen_vm vm, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VM.remove_from_VCPUs_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_vcpus_max(xen_session *session, xen_vm vm, int64_t max) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = max } + }; + + xen_call_(session, "VM.set_VCPUs_max", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_vcpus_at_startup(xen_session *session, xen_vm vm, int64_t at_startup) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_int, + .u.int_val = at_startup } + }; + + xen_call_(session, "VM.set_VCPUs_at_startup", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_actions_after_shutdown(xen_session *session, xen_vm vm, enum xen_on_normal_exit after_shutdown) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &xen_on_normal_exit_abstract_type_, + .u.enum_val = after_shutdown } + }; + + xen_call_(session, "VM.set_actions_after_shutdown", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_actions_after_reboot(xen_session *session, xen_vm vm, enum xen_on_normal_exit after_reboot) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &xen_on_normal_exit_abstract_type_, + .u.enum_val = after_reboot } + }; + + xen_call_(session, "VM.set_actions_after_reboot", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_actions_after_crash(xen_session *session, xen_vm vm, enum xen_on_crash_behaviour after_crash) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &xen_on_crash_behaviour_abstract_type_, + .u.enum_val = after_crash } + }; + + xen_call_(session, "VM.set_actions_after_crash", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_pv_bootloader(xen_session *session, xen_vm vm, char *bootloader) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = bootloader } + }; + + xen_call_(session, "VM.set_PV_bootloader", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_pv_kernel(xen_session *session, xen_vm vm, char *kernel) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = kernel } + }; + + xen_call_(session, "VM.set_PV_kernel", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_pv_ramdisk(xen_session *session, xen_vm vm, char *ramdisk) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = ramdisk } + }; + + xen_call_(session, "VM.set_PV_ramdisk", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_pv_args(xen_session *session, xen_vm vm, char *args) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = args } + }; + + xen_call_(session, "VM.set_PV_args", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_pv_bootloader_args(xen_session *session, xen_vm vm, char *bootloader_args) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = bootloader_args } + }; + + xen_call_(session, "VM.set_PV_bootloader_args", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_hvm_boot_policy(xen_session *session, xen_vm vm, char *boot_policy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = boot_policy } + }; + + xen_call_(session, "VM.set_HVM_boot_policy", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_hvm_boot_params(xen_session *session, xen_vm vm, xen_string_string_map *boot_params) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)boot_params } + }; + + xen_call_(session, "VM.set_HVM_boot_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_add_to_hvm_boot_params(xen_session *session, xen_vm vm, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VM.add_to_HVM_boot_params", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_remove_from_hvm_boot_params(xen_session *session, xen_vm vm, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VM.remove_from_HVM_boot_params", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_platform(xen_session *session, xen_vm vm, xen_string_string_map *platform) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)platform } + }; + + xen_call_(session, "VM.set_platform", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_add_to_platform(xen_session *session, xen_vm vm, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VM.add_to_platform", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_remove_from_platform(xen_session *session, xen_vm vm, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VM.remove_from_platform", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_pci_bus(xen_session *session, xen_vm vm, char *pci_bus) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = pci_bus } + }; + + xen_call_(session, "VM.set_PCI_bus", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_other_config(xen_session *session, xen_vm vm, xen_string_string_map *other_config) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)other_config } + }; + + xen_call_(session, "VM.set_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_add_to_other_config(xen_session *session, xen_vm vm, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VM.add_to_other_config", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_remove_from_other_config(xen_session *session, xen_vm vm, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VM.remove_from_other_config", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_clone(xen_session *session, xen_vm *result, xen_vm vm, char *new_name) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = new_name } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.clone"); + return session->ok; +} + + +bool +xen_vm_start(xen_session *session, xen_vm vm, bool start_paused) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_bool, + .u.bool_val = start_paused } + }; + + xen_call_(session, "VM.start", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_pause(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.pause", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_unpause(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.unpause", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_clean_shutdown(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.clean_shutdown", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_clean_reboot(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.clean_reboot", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_hard_shutdown(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.hard_shutdown", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_hard_reboot(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.hard_reboot", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_suspend(xen_session *session, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + xen_call_(session, "VM.suspend", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_resume(xen_session *session, xen_vm vm, bool start_paused) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_bool, + .u.bool_val = start_paused } + }; + + xen_call_(session, "VM.resume", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_vcpus_number_live(xen_session *session, xen_vm self, int64_t nvcpu) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self }, + { .type = &abstract_type_int, + .u.int_val = nvcpu } + }; + + xen_call_(session, "VM.set_VCPUs_number_live", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_add_to_vcpus_params_live(xen_session *session, xen_vm self, char *key, char *value) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self }, + { .type = &abstract_type_string, + .u.string_val = key }, + { .type = &abstract_type_string, + .u.string_val = value } + }; + + xen_call_(session, "VM.add_to_VCPUs_params_live", param_values, 3, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_memory_dynamic_max_live(xen_session *session, xen_vm self, int64_t max) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self }, + { .type = &abstract_type_int, + .u.int_val = max } + }; + + xen_call_(session, "VM.set_memory_dynamic_max_live", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_set_memory_dynamic_min_live(xen_session *session, xen_vm self, int64_t min) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = self }, + { .type = &abstract_type_int, + .u.int_val = min } + }; + + xen_call_(session, "VM.set_memory_dynamic_min_live", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_send_sysrq(xen_session *session, xen_vm vm, char *key) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = key } + }; + + xen_call_(session, "VM.send_sysrq", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_send_trigger(xen_session *session, xen_vm vm, char *trigger) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = trigger } + }; + + xen_call_(session, "VM.send_trigger", param_values, 2, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_migrate(xen_session *session, xen_vm vm, char *dest, bool live, xen_string_string_map *options) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = dest }, + { .type = &abstract_type_bool, + .u.bool_val = live }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)options } + }; + + xen_call_(session, "VM.migrate", param_values, 4, NULL, NULL); + return session->ok; +} + + +bool +xen_vm_get_all(xen_session *session, struct xen_vm_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VM.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vm_get_uuid(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_uuid"); + return session->ok; +} + + +bool +xen_vm_set_security_label(xen_session *session, int64_t *result, xen_vm vm, + char *label, char *oldlabel) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + { .type = &abstract_type_string, + .u.string_val = label }, + { .type = &abstract_type_string, + .u.string_val = oldlabel }, + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("VM.set_security_label"); + return session->ok; +} + + +bool +xen_vm_get_security_label(xen_session *session, char **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm }, + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM.get_security_label"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vm_guest_metrics.c b/tools/libxen/src/xen_vm_guest_metrics.c new file mode 100644 index 0000000..251b65e --- /dev/null +++ b/tools/libxen/src/xen_vm_guest_metrics.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include + + +XEN_FREE(xen_vm_guest_metrics) +XEN_SET_ALLOC_FREE(xen_vm_guest_metrics) +XEN_ALLOC(xen_vm_guest_metrics_record) +XEN_SET_ALLOC_FREE(xen_vm_guest_metrics_record) +XEN_ALLOC(xen_vm_guest_metrics_record_opt) +XEN_RECORD_OPT_FREE(xen_vm_guest_metrics) +XEN_SET_ALLOC_FREE(xen_vm_guest_metrics_record_opt) + + +static const struct_member xen_vm_guest_metrics_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_guest_metrics_record, uuid) }, + { .key = "os_version", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_guest_metrics_record, os_version) }, + { .key = "PV_drivers_version", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_guest_metrics_record, pv_drivers_version) }, + { .key = "memory", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_guest_metrics_record, memory) }, + { .key = "disks", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_guest_metrics_record, disks) }, + { .key = "networks", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_guest_metrics_record, networks) }, + { .key = "other", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_guest_metrics_record, other) }, + { .key = "last_updated", + .type = &abstract_type_datetime, + .offset = offsetof(xen_vm_guest_metrics_record, last_updated) } + }; + +const abstract_type xen_vm_guest_metrics_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vm_guest_metrics_record), + .member_count = + sizeof(xen_vm_guest_metrics_record_struct_members) / sizeof(struct_member), + .members = xen_vm_guest_metrics_record_struct_members + }; + + +void +xen_vm_guest_metrics_record_free(xen_vm_guest_metrics_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_string_string_map_free(record->os_version); + xen_string_string_map_free(record->pv_drivers_version); + xen_string_string_map_free(record->memory); + xen_string_string_map_free(record->disks); + xen_string_string_map_free(record->networks); + xen_string_string_map_free(record->other); + free(record); +} + + +bool +xen_vm_guest_metrics_get_record(xen_session *session, xen_vm_guest_metrics_record **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = xen_vm_guest_metrics_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_by_uuid(xen_session *session, xen_vm_guest_metrics *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_by_uuid"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_os_version(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_os_version"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_pv_drivers_version(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_PV_drivers_version"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_memory(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_memory"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_disks(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_disks"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_networks(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_networks"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_other(xen_session *session, xen_string_string_map **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_other"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_last_updated(xen_session *session, time_t *result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("VM_guest_metrics.get_last_updated"); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_all(xen_session *session, struct xen_vm_guest_metrics_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VM_guest_metrics.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vm_guest_metrics_get_uuid(xen_session *session, char **result, xen_vm_guest_metrics vm_guest_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_guest_metrics } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM_guest_metrics.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vm_metrics.c b/tools/libxen/src/xen_vm_metrics.c new file mode 100644 index 0000000..7f2ab94 --- /dev/null +++ b/tools/libxen/src/xen_vm_metrics.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include +#include +#include +#include + + +XEN_FREE(xen_vm_metrics) +XEN_SET_ALLOC_FREE(xen_vm_metrics) +XEN_ALLOC(xen_vm_metrics_record) +XEN_SET_ALLOC_FREE(xen_vm_metrics_record) +XEN_ALLOC(xen_vm_metrics_record_opt) +XEN_RECORD_OPT_FREE(xen_vm_metrics) +XEN_SET_ALLOC_FREE(xen_vm_metrics_record_opt) + + +static const struct_member xen_vm_metrics_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vm_metrics_record, uuid) }, + { .key = "memory_actual", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_metrics_record, memory_actual) }, + { .key = "VCPUs_number", + .type = &abstract_type_int, + .offset = offsetof(xen_vm_metrics_record, vcpus_number) }, + { .key = "VCPUs_utilisation", + .type = &abstract_type_int_float_map, + .offset = offsetof(xen_vm_metrics_record, vcpus_utilisation) }, + { .key = "VCPUs_CPU", + .type = &abstract_type_int_int_map, + .offset = offsetof(xen_vm_metrics_record, vcpus_cpu) }, + { .key = "VCPUs_params", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vm_metrics_record, vcpus_params) }, + { .key = "VCPUs_flags", + .type = &abstract_type_int_string_set_map, + .offset = offsetof(xen_vm_metrics_record, vcpus_flags) }, + { .key = "state", + .type = &abstract_type_string_set, + .offset = offsetof(xen_vm_metrics_record, state) }, + { .key = "start_time", + .type = &abstract_type_datetime, + .offset = offsetof(xen_vm_metrics_record, start_time) }, + { .key = "last_updated", + .type = &abstract_type_datetime, + .offset = offsetof(xen_vm_metrics_record, last_updated) } + }; + +const abstract_type xen_vm_metrics_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vm_metrics_record), + .member_count = + sizeof(xen_vm_metrics_record_struct_members) / sizeof(struct_member), + .members = xen_vm_metrics_record_struct_members + }; + + +void +xen_vm_metrics_record_free(xen_vm_metrics_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_int_float_map_free(record->vcpus_utilisation); + xen_int_int_map_free(record->vcpus_cpu); + xen_string_string_map_free(record->vcpus_params); + xen_int_string_set_map_free(record->vcpus_flags); + xen_string_set_free(record->state); + free(record); +} + + +bool +xen_vm_metrics_get_record(xen_session *session, xen_vm_metrics_record **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = xen_vm_metrics_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VM_metrics.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vm_metrics_get_by_uuid(xen_session *session, xen_vm_metrics *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM_metrics.get_by_uuid"); + return session->ok; +} + + +bool +xen_vm_metrics_get_memory_actual(xen_session *session, int64_t *result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM_metrics.get_memory_actual"); + return session->ok; +} + + +bool +xen_vm_metrics_get_vcpus_number(xen_session *session, int64_t *result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_int; + + XEN_CALL_("VM_metrics.get_VCPUs_number"); + return session->ok; +} + + +bool +xen_vm_metrics_get_vcpus_utilisation(xen_session *session, xen_int_float_map **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_int_float_map; + + *result = NULL; + XEN_CALL_("VM_metrics.get_VCPUs_utilisation"); + return session->ok; +} + + +bool +xen_vm_metrics_get_vcpus_cpu(xen_session *session, xen_int_int_map **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_int_int_map; + + *result = NULL; + XEN_CALL_("VM_metrics.get_VCPUs_CPU"); + return session->ok; +} + + +bool +xen_vm_metrics_get_vcpus_params(xen_session *session, xen_string_string_map **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VM_metrics.get_VCPUs_params"); + return session->ok; +} + + +bool +xen_vm_metrics_get_vcpus_flags(xen_session *session, xen_int_string_set_map **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_int_string_set_map; + + *result = NULL; + XEN_CALL_("VM_metrics.get_VCPUs_flags"); + return session->ok; +} + + +bool +xen_vm_metrics_get_state(xen_session *session, struct xen_string_set **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM_metrics.get_state"); + return session->ok; +} + + +bool +xen_vm_metrics_get_start_time(xen_session *session, time_t *result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("VM_metrics.get_start_time"); + return session->ok; +} + + +bool +xen_vm_metrics_get_last_updated(xen_session *session, time_t *result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_datetime; + + XEN_CALL_("VM_metrics.get_last_updated"); + return session->ok; +} + + +bool +xen_vm_metrics_get_all(xen_session *session, struct xen_vm_metrics_set **result) +{ + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + xen_call_(session, "VM_metrics.get_all", NULL, 0, &result_type, result); + return session->ok; +} + + +bool +xen_vm_metrics_get_uuid(xen_session *session, char **result, xen_vm_metrics vm_metrics) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm_metrics } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VM_metrics.get_uuid"); + return session->ok; +} diff --git a/tools/libxen/src/xen_vm_power_state.c b/tools/libxen/src/xen_vm_power_state.c new file mode 100644 index 0000000..377f9ec --- /dev/null +++ b/tools/libxen/src/xen_vm_power_state.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006-2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include +#include "xen_vm_power_state_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "Halted", + "Paused", + "Running", + "Suspended", + "Crashed", + "Unknown" +}; + + +extern xen_vm_power_state_set * +xen_vm_power_state_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_vm_power_state_set) + + size * sizeof(enum xen_vm_power_state)); +} + + +extern void +xen_vm_power_state_set_free(xen_vm_power_state_set *set) +{ + free(set); +} + + +const char * +xen_vm_power_state_to_string(enum xen_vm_power_state val) +{ + return lookup_table[val]; +} + + +extern enum xen_vm_power_state +xen_vm_power_state_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_vm_power_state_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_vm_power_state_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_vm_power_state_from_string + }; + + +const abstract_type xen_vm_power_state_set_abstract_type_ = + { + .typename = SET, + .child = &xen_vm_power_state_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_vtpm.c b/tools/libxen/src/xen_vtpm.c new file mode 100644 index 0000000..0ca7b41 --- /dev/null +++ b/tools/libxen/src/xen_vtpm.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * Copyright (c) 2006, IBM 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; 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include +#include +#include + + +XEN_FREE(xen_vtpm) +XEN_SET_ALLOC_FREE(xen_vtpm) +XEN_ALLOC(xen_vtpm_record) +XEN_SET_ALLOC_FREE(xen_vtpm_record) +XEN_ALLOC(xen_vtpm_record_opt) +XEN_RECORD_OPT_FREE(xen_vtpm) +XEN_SET_ALLOC_FREE(xen_vtpm_record_opt) + + +static const struct_member xen_vtpm_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_vtpm_record, uuid) }, + { .key = "VM", + .type = &abstract_type_ref, + .offset = offsetof(xen_vtpm_record, vm) }, + { .key = "backend", + .type = &abstract_type_ref, + .offset = offsetof(xen_vtpm_record, backend) }, + { .key = "other_config", + .type = &abstract_type_string_string_map, + .offset = offsetof(xen_vtpm_record, other_config) } + }; + +const abstract_type xen_vtpm_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_vtpm_record), + .member_count = + sizeof(xen_vtpm_record_struct_members) / sizeof(struct_member), + .members = xen_vtpm_record_struct_members + }; + + +void +xen_vtpm_record_free(xen_vtpm_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + xen_vm_record_opt_free(record->vm); + xen_vm_record_opt_free(record->backend); + xen_string_string_map_free(record->other_config); + free(record); +} + + +bool +xen_vtpm_get_record(xen_session *session, xen_vtpm_record **result, xen_vtpm vtpm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm } + }; + + abstract_type result_type = xen_vtpm_record_abstract_type_; + + *result = NULL; + XEN_CALL_("VTPM.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_vtpm_get_by_uuid(xen_session *session, xen_vtpm *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VTPM.get_by_uuid"); + return session->ok; +} + + +bool +xen_vtpm_create(xen_session *session, xen_vtpm *result, xen_vtpm_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_vtpm_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VTPM.create"); + return session->ok; +} + + +bool +xen_vtpm_destroy(xen_session *session, xen_vtpm vtpm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm } + }; + + xen_call_(session, "VTPM.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_vtpm_get_vm(xen_session *session, xen_vm *result, xen_vtpm vtpm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VTPM.get_VM"); + return session->ok; +} + + +bool +xen_vtpm_get_backend(xen_session *session, xen_vm *result, xen_vtpm vtpm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VTPM.get_backend"); + return session->ok; +} + + +bool +xen_vtpm_get_uuid(xen_session *session, char **result, xen_vtpm vtpm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("VTPM.get_uuid"); + return session->ok; +} + + +bool +xen_vtpm_get_other_config(xen_session *session, xen_string_string_map **result, + xen_vtpm vtpm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm } + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("VTPM.get_other_config"); + return session->ok; +} + + +bool +xen_vtpm_set_other_config(xen_session *session, xen_vtpm vtpm, + xen_string_string_map *other_config) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vtpm }, + { .type = &abstract_type_string_string_map, + .u.set_val = (arbitrary_set *)other_config } + }; + + xen_call_(session, "VTPM.set_other_config", param_values, 2, NULL, NULL); + return session->ok; +} diff --git a/tools/libxen/src/xen_xspolicy.c b/tools/libxen/src/xen_xspolicy.c new file mode 100644 index 0000000..fe0dc25 --- /dev/null +++ b/tools/libxen/src/xen_xspolicy.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2007, IBM Corp. + * Copyright (c) 2007, XenSource 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_internal.h" +#include "xen/api/xen_common.h" +#include "xen/api/xen_xspolicy.h" + + +XEN_FREE(xen_xspolicy) +XEN_SET_ALLOC_FREE(xen_xspolicy) +XEN_RECORD_OPT_FREE(xen_xspolicy) + +static const struct_member xen_xspolicy_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_xspolicy_record, uuid) }, + { .key = "flags", + .type = &abstract_type_int, + .offset = offsetof(xen_xspolicy_record, flags) }, + { .key = "repr", + .type = &abstract_type_string, + .offset = offsetof(xen_xspolicy_record, repr) }, + { .key = "type", + .type = &abstract_type_int, + .offset = offsetof(xen_xspolicy_record, type) }, + }; + +const abstract_type xen_xspolicy_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_xspolicy_record), + .member_count = + sizeof(xen_xspolicy_record_struct_members) / sizeof(struct_member), + .members = xen_xspolicy_record_struct_members + }; + + +static const struct_member xen_xs_policystate_struct_members[] = + { + { .key = "xs_ref", + .type = &abstract_type_ref, + .offset = offsetof(xen_xs_policystate, xs_ref) }, + { .key = "xserr", + .type = &abstract_type_int, + .offset = offsetof(xen_xs_policystate, xserr) }, + { .key = "repr", + .type = &abstract_type_string, + .offset = offsetof(xen_xs_policystate, repr) }, + { .key = "type", + .type = &abstract_type_int, + .offset = offsetof(xen_xs_policystate, type) }, + { .key = "flags", + .type = &abstract_type_int, + .offset = offsetof(xen_xs_policystate, flags) }, + { .key = "version", + .type = &abstract_type_string, + .offset = offsetof(xen_xs_policystate, version) }, + { .key = "errors", + .type = &abstract_type_string, + .offset = offsetof(xen_xs_policystate, errors) }, + }; + +const abstract_type xen_xs_policystate_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_xs_policystate), + .member_count = + sizeof(xen_xs_policystate_struct_members) / + sizeof(struct_member), + .members = xen_xs_policystate_struct_members, + }; + + + + +void +xen_xs_policystate_free(xen_xs_policystate *state) +{ + if (state == NULL) + { + return; + } + xen_xspolicy_record_opt_free(state->xs_ref); + free(state->repr); + free(state->errors); + free(state->version); + free(state); +} + + +void +xen_xspolicy_record_free(xen_xspolicy_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->repr); + free(record); +} + + +bool +xen_xspolicy_get_record(xen_session *session, xen_xspolicy_record **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy } + }; + + abstract_type result_type = xen_xspolicy_record_abstract_type_; + + *result = NULL; + XEN_CALL_("XSPolicy.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_xspolicy_get_uuid(xen_session *session, char **result, + xen_xspolicy xspolicy) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("XSPolicy.get_uuid"); + return session->ok; +} + + +bool +xen_xspolicy_get_by_uuid(xen_session *session, xen_xspolicy *result, + char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("XSPolicy.get_by_uuid"); + return session->ok; +} + + +bool +xen_xspolicy_get_xstype(xen_session *session, xs_type *result) +{ + abstract_value param_values[] = + { + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("XSPolicy.get_xstype"); + return session->ok; +} + + +bool +xen_xspolicy_set_xspolicy(xen_session *session, xen_xs_policystate **result, + xs_type type, char *repr, + xs_instantiationflags flags, + bool overwrite) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_int, + .u.int_val = type }, + { .type = &abstract_type_string, + .u.string_val = repr }, + { .type = &abstract_type_int, + .u.int_val = flags }, + { .type = &abstract_type_bool, + .u.bool_val = overwrite } + }; + + abstract_type result_type = xen_xs_policystate_abstract_type_; + + *result = NULL; + XEN_CALL_("XSPolicy.set_xspolicy"); + return session->ok; +} + + +bool +xen_xspolicy_reset_xspolicy(xen_session *session, xen_xs_policystate **result, + xs_type type) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_int, + .u.int_val = type }, + }; + + abstract_type result_type = xen_xs_policystate_abstract_type_; + + *result = NULL; + XEN_CALL_("XSPolicy.reset_xspolicy"); + return session->ok; +} + + +bool +xen_xspolicy_get_xspolicy(xen_session *session, xen_xs_policystate **result) +{ + abstract_value param_values[] = + { + }; + + abstract_type result_type = xen_xs_policystate_abstract_type_; + + *result = NULL; + XEN_CALL_("XSPolicy.get_xspolicy"); + return session->ok; +} + + +bool +xen_xspolicy_get_labeled_resources(xen_session *session, + xen_string_string_map **result) +{ + abstract_value param_values[] = + { + }; + + abstract_type result_type = abstract_type_string_string_map; + + *result = NULL; + XEN_CALL_("XSPolicy.get_labeled_resources"); + return session->ok; +} + + +bool +xen_xspolicy_set_resource_label(xen_session *session, + char *resource, char *label, + char *oldlabel) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = resource }, + { .type = &abstract_type_string, + .u.string_val = label }, + { .type = &abstract_type_string, + .u.string_val = oldlabel }, + }; + + xen_call_(session, "XSPolicy.set_resource_label", param_values, 3, + NULL, NULL); + return session->ok; +} + + +bool +xen_xspolicy_get_resource_label(xen_session *session, char **result, + char *resource) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = resource }, + }; + + abstract_type result_type = abstract_type_string; + XEN_CALL_("XSPolicy.get_resource_label"); + return session->ok; +} + + +bool +xen_xspolicy_rm_xsbootpolicy(xen_session *session) +{ + abstract_value param_values[] = + { + }; + + xen_call_(session, "XSPolicy.rm_xsbootpolicy", param_values, 0, + NULL, NULL); + return session->ok; +} + + +bool +xen_xspolicy_activate_xspolicy(xen_session *session, + xs_instantiationflags *result, + xen_xspolicy xspolicy, + xs_instantiationflags flags) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = xspolicy }, + { .type = &abstract_type_int, + .u.int_val = flags }, + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("XSPolicy.activate_xspolicy"); + return session->ok; +} + + +bool +xen_xspolicy_can_run(xen_session *session, int64_t *result, + char *security_label) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = security_label } + }; + + abstract_type result_type = abstract_type_int; + + *result = 0; + XEN_CALL_("XSPolicy.can_run"); + return session->ok; +} diff --git a/tools/libxen/test/test_bindings.c b/tools/libxen/test/test_bindings.c new file mode 100644 index 0000000..566a038 --- /dev/null +++ b/tools/libxen/test/test_bindings.c @@ -0,0 +1,804 @@ +/* + * Copyright (c) 2006-2007 XenSource, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +//#define PRINT_XML + +static void usage() +{ + fprintf(stderr, +"Usage:\n" +"\n" +" test_bindings \n" +"\n" +"where\n" +" is a fragment of the server's URL, e.g. localhost:8005/RPC2;\n" +" is the username to use at the server; and\n" +" is the password.\n"); + + exit(EXIT_FAILURE); +} + + +static char *url; + + +typedef struct +{ + xen_result_func func; + void *handle; +} xen_comms; + + +static xen_vm create_new_vm(xen_session *session, bool hvm); +static void print_session_info(xen_session *session); +static void print_methods(xen_session *session); +static void print_vm_power_state(xen_session *session, xen_vm vm); +static void print_vm_metrics(xen_session *session, xen_vm vm); + + +static size_t +write_func(void *ptr, size_t size, size_t nmemb, xen_comms *comms) +{ + size_t n = size * nmemb; +#ifdef PRINT_XML + printf("\n\n---Result from server -----------------------\n"); + printf("%s\n",((char*) ptr)); + fflush(stdout); +#endif + return comms->func(ptr, n, comms->handle) ? n : 0; +} + + +static int +call_func(const void *data, size_t len, void *user_handle, + void *result_handle, xen_result_func result_func) +{ + (void)user_handle; + +#ifdef PRINT_XML + printf("\n\n---Data to server: -----------------------\n"); + printf("%s\n",((char*) data)); + fflush(stdout); +#endif + + CURL *curl = curl_easy_init(); + if (!curl) { + return -1; + } + + xen_comms comms = { + .func = result_func, + .handle = result_handle + }; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_MUTE, 1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_func); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &comms); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); + + CURLcode result = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + + return result; +} + + +static void print_error(xen_session *session) +{ + fprintf(stderr, "Error: %d", session->error_description_count); + for (int i = 0; i < session->error_description_count; i++) + { + fprintf(stderr, "%s ", session->error_description[i]); + } + fprintf(stderr, "\n"); +} + + +int main(int argc, char **argv) +{ + if (argc != 4) + { + usage(); + } + + url = argv[1]; + char *username = argv[2]; + char *password = argv[3]; + + xmlInitParser(); + xen_init(); + curl_global_init(CURL_GLOBAL_ALL); + +#define CLEANUP \ + do { \ + xen_session_logout(session); \ + curl_global_cleanup(); \ + xen_fini(); \ + xmlCleanupParser(); \ + } while(0) \ + + + xen_session *session = + xen_session_login_with_password(call_func, NULL, username, password); + + print_session_info(session); + if (!session->ok) + { + /* Error has been logged, just clean up. */ + CLEANUP; + return 1; + } + + print_methods(session); + if (!session->ok) + { + /* Error has been logged, just clean up. */ + CLEANUP; + return 1; + } + + xen_vm vm; + if (!xen_vm_get_by_uuid(session, &vm, + "00000000-0000-0000-0000-000000000000")) + { + print_error(session); + CLEANUP; + return 1; + } + + char *vm_uuid; + if (!xen_vm_get_uuid(session, &vm_uuid, vm)) + { + print_error(session); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + char *vm_uuid_bytes; + if (!xen_uuid_string_to_bytes(vm_uuid, &vm_uuid_bytes)) + { + fprintf(stderr, "xen_uuid_string_to_bytes failed.\n"); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_vm_record *vm_record; + if (!xen_vm_get_record(session, &vm_record, vm)) + { + print_error(session); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_host host; + if (!xen_session_get_this_host(session, &host, session)) + { + print_error(session); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_string_string_map *versions; + if (!xen_host_get_software_version(session, &versions, host)) + { + print_error(session); + xen_host_free(host); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + char *dmesg; + if (!xen_host_dmesg(session, &dmesg, host)) + { + print_error(session); + xen_string_string_map_free(versions); + xen_host_free(host); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_string_set *supported_bootloaders; + if (!xen_host_get_supported_bootloaders(session, &supported_bootloaders, + host)) + { + print_error(session); + free(dmesg); + xen_string_string_map_free(versions); + xen_host_free(host); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_string_set *capabilities; + if (!xen_host_get_capabilities(session, &capabilities, host)) + { + print_error(session); + free(dmesg); + xen_string_set_free(supported_bootloaders); + xen_string_string_map_free(versions); + xen_host_free(host); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_string_string_map *cpu_configuration; + if (!xen_host_get_cpu_configuration(session, &cpu_configuration, host)) + { + print_error(session); + free(dmesg); + xen_string_set_free(capabilities); + xen_string_set_free(supported_bootloaders); + xen_string_string_map_free(versions); + xen_host_free(host); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + char *sched_policy; + if (!xen_host_get_sched_policy(session, &sched_policy, host)) + { + print_error(session); + xen_string_string_map_free(cpu_configuration); + xen_string_set_free(capabilities); + xen_string_set_free(supported_bootloaders); + xen_string_string_map_free(versions); + xen_host_free(host); + xen_vm_record_free(vm_record); + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + xen_vm_free(vm); + CLEANUP; + return 1; + } + + printf("%s.\n", vm_uuid); + + printf("In bytes, the VM UUID is "); + for (int i = 0; i < 15; i++) + { + printf("%x, ", (unsigned int)vm_uuid_bytes[i]); + } + printf("%x.\n", (unsigned int)vm_uuid_bytes[15]); + + printf("%zd.\n", versions->size); + + for (size_t i = 0; i < versions->size; i++) + { + printf("%s -> %s.\n", versions->contents[i].key, + versions->contents[i].val); + } + + printf("Host dmesg follows:\n%s\n\n", dmesg); + + printf("Host supports the following bootloaders:"); + for (size_t i = 0; i < supported_bootloaders->size; i++) + { + printf(" %s", supported_bootloaders->contents[i]); + } + printf("\n"); + + printf("Host has the following capabilities:"); + for (size_t i = 0; i < capabilities->size; i++) + { + printf(" %s", capabilities->contents[i]); + } + printf("\n"); + + printf("Host has the following CPU configuration:\n"); + for (size_t i = 0; i < cpu_configuration->size; i++) + { + printf(" %s -> %s.\n", cpu_configuration->contents[i].key, + cpu_configuration->contents[i].val); + } + + printf("Current scheduler policy: %s.\n\n", sched_policy); + + printf("%s.\n", vm_record->uuid); + + printf("Resident on %s.\n", (char *)vm_record->resident_on->u.handle); + + printf("%s.\n", xen_vm_power_state_to_string(vm_record->power_state)); + + xen_uuid_bytes_free(vm_uuid_bytes); + xen_uuid_free(vm_uuid); + + xen_vm_record_free(vm_record); + + xen_host_free(host); + xen_string_string_map_free(versions); + free(dmesg); + xen_string_set_free(supported_bootloaders); + xen_string_set_free(capabilities); + xen_string_string_map_free(cpu_configuration); + free(sched_policy); + + print_vm_metrics(session, vm); + if (!session->ok) + { + /* Error has been logged, just clean up. */ + xen_vm_free(vm); + CLEANUP; + return 1; + } + + xen_vm_free(vm); + + xen_vm new_vm = create_new_vm(session, true); + if (!session->ok) + { + /* Error has been logged, just clean up. */ + CLEANUP; + return 1; + } + + print_vm_power_state(session, new_vm); + if (!session->ok) + { + /* Error has been logged, just clean up. */ + xen_vm_free(new_vm); + CLEANUP; + return 1; + } + + xen_vm_free(new_vm); + CLEANUP; + + return 0; +} + + +/** + * Creation of a new VM, using the Named Parameters idiom. Allocate the + * xen_vm_record here, but the sets through the library. Either + * allocation patterns can be used, as long as the allocation and free are + * paired correctly. + */ +static xen_vm create_new_vm(xen_session *session, bool hvm) +{ + xen_string_string_map *vcpus_params = xen_string_string_map_alloc(1); + vcpus_params->contents[0].key = strdup("weight"); + vcpus_params->contents[0].val = strdup("300"); + + xen_string_string_map *hvm_boot_params; + if (hvm) + { + hvm_boot_params = xen_string_string_map_alloc(1); + hvm_boot_params->contents[0].key = strdup("order"); + hvm_boot_params->contents[0].val = strdup("cd"); + } + else + { + hvm_boot_params = NULL; + } + + xen_vm_record vm_record = + { + .name_label = hvm ? "NewHVM" : "NewPV", + .name_description = hvm ? "New HVM VM" : "New PV VM", + .user_version = 1, + .is_a_template = false, + .memory_static_max = 256 * 1024 * 1024, + .memory_dynamic_max = 256 * 1024 * 1024, + .memory_dynamic_min = 128 * 1024 * 1024, + .memory_static_min = 128 * 1024 * 1024, + .vcpus_params = vcpus_params, + .vcpus_max = 4, + .vcpus_at_startup = 2, + .actions_after_shutdown = XEN_ON_NORMAL_EXIT_DESTROY, + .actions_after_reboot = XEN_ON_NORMAL_EXIT_RESTART, + .actions_after_crash = XEN_ON_CRASH_BEHAVIOUR_RESTART, + .hvm_boot_policy = hvm ? "BIOS order" : NULL, + .hvm_boot_params = hvm ? hvm_boot_params : NULL, + .pv_bootloader = hvm ? NULL : "pygrub", + .pv_kernel = hvm ? NULL : "/boot/vmlinuz-2.6.16.33-xen", + }; + + xen_vm vm; + xen_vm_create(session, &vm, &vm_record); + + xen_string_string_map_free(vcpus_params); + xen_string_string_map_free(hvm_boot_params); + + if (!session->ok) + { + fprintf(stderr, "VM creation failed.\n"); + print_error(session); + return NULL; + } + + + /* + * Create a new disk for the new VM. + */ + xen_sr_set *srs; + if (!xen_sr_get_by_name_label(session, &srs, "QCoW") || + srs->size < 1) + { + fprintf(stderr, "SR lookup failed.\n"); + print_error(session); + xen_vm_free(vm); + return NULL; + } + + xen_sr_record_opt sr_record = + { + .u.handle = srs->contents[0] + }; + xen_vdi_record vdi0_record = + { + .name_label = "MyRootFS", + .name_description = "MyRootFS description", + .sr = &sr_record, + .virtual_size = (INT64_C(1) << 30), // 1GiB + .type = XEN_VDI_TYPE_SYSTEM, + .sharable = false, + .read_only = false + }; + + xen_vdi vdi0; + if (!xen_vdi_create(session, &vdi0, &vdi0_record)) + { + fprintf(stderr, "VDI creation failed.\n"); + print_error(session); + + xen_sr_set_free(srs); + xen_vm_free(vm); + return NULL; + } + + + xen_vm_record_opt vm_record_opt = + { + .u.handle = vm + }; + xen_vdi_record_opt vdi0_record_opt = + { + .u.handle = vdi0 + }; + xen_vbd_record vbd0_record = + { + .vm = &vm_record_opt, + .vdi = &vdi0_record_opt, + .device = "xvda1", + .mode = XEN_VBD_MODE_RW, + .bootable = 1, + }; + + xen_vbd vbd0; + if (!xen_vbd_create(session, &vbd0, &vbd0_record)) + { + fprintf(stderr, "VBD creation failed.\n"); + print_error(session); + + xen_vdi_free(vdi0); + xen_sr_set_free(srs); + xen_vm_free(vm); + return NULL; + } + + xen_console vnc_console = NULL; + if (hvm) { + xen_console_record vnc_console_record = + { + .protocol = XEN_CONSOLE_PROTOCOL_RFB, + .vm = &vm_record_opt, + }; + + if (!xen_console_create(session, &vnc_console, &vnc_console_record)) + { + fprintf(stderr, "VNC console creation failed.\n"); + print_error(session); + + xen_vbd_free(vbd0); + xen_vdi_free(vdi0); + xen_sr_set_free(srs); + xen_vm_free(vm); + return NULL; + } + } + + char *vm_uuid; + char *vdi0_uuid; + char *vbd0_uuid; + char *vnc_uuid = NULL; + + xen_vm_get_uuid(session, &vm_uuid, vm); + xen_vdi_get_uuid(session, &vdi0_uuid, vdi0); + xen_vbd_get_uuid(session, &vbd0_uuid, vbd0); + if (hvm) { + xen_console_get_uuid(session, &vnc_uuid, vnc_console); + } + + if (!session->ok) + { + fprintf(stderr, "get_uuid call failed.\n"); + print_error(session); + + xen_uuid_free(vm_uuid); + xen_uuid_free(vdi0_uuid); + xen_uuid_free(vbd0_uuid); + xen_uuid_free(vnc_uuid); + xen_vbd_free(vbd0); + xen_vdi_free(vdi0); + xen_console_free(vnc_console); + xen_sr_set_free(srs); + xen_vm_free(vm); + return NULL; + } + + if (hvm) { + printf("Created a new HVM VM, with UUID %s, VDI UUID %s, VBD " + "UUID %s, and VNC console UUID %s.\n", + vm_uuid, vdi0_uuid, vbd0_uuid, vnc_uuid); + } + else { + printf("Created a new PV VM, with UUID %s, VDI UUID %s, and VBD " + "UUID %s.\n", + vm_uuid, vdi0_uuid, vbd0_uuid); + } + + xen_uuid_free(vm_uuid); + xen_uuid_free(vdi0_uuid); + xen_uuid_free(vbd0_uuid); + xen_uuid_free(vnc_uuid); + xen_vbd_free(vbd0); + xen_vdi_free(vdi0); + xen_console_free(vnc_console); + xen_sr_set_free(srs); + + return vm; +} + + +/** + * Print the power state for the given VM. + */ +static void print_vm_power_state(xen_session *session, xen_vm vm) +{ + char *vm_uuid; + enum xen_vm_power_state power_state; + + if (!xen_vm_get_uuid(session, &vm_uuid, vm)) + { + print_error(session); + return; + } + + if (!xen_vm_get_power_state(session, &power_state, vm)) + { + xen_uuid_free(vm_uuid); + print_error(session); + return; + } + + printf("VM %s power state is %s.\n", vm_uuid, + xen_vm_power_state_to_string(power_state)); + + xen_uuid_free(vm_uuid); + + fflush(stdout); +} + + +/** + * Workaround for whinging GCCs, as suggested by strftime(3). + */ +static size_t my_strftime(char *s, size_t max, const char *fmt, + const struct tm *tm) +{ + return strftime(s, max, fmt, tm); +} + + +/** + * Print some session details. + */ +static void print_session_info(xen_session *session) +{ + xen_session_record *record; + if (!xen_session_get_record(session, &record, session)) + { + print_error(session); + return; + } + + printf("Session UUID: %s.\n", record->uuid); + printf("Session user: %s.\n", record->this_user); + char time[256]; + struct tm *tm = localtime(&record->last_active); + my_strftime(time, 256, "Session last active: %c, local time.\n", tm); + printf(time); + + char *uuid = NULL; + char *this_user = NULL; + xen_session_get_uuid(session, &uuid, session); + xen_session_get_this_user(session, &this_user, session); + + if (!session->ok) + { + free(uuid); + free(this_user); + xen_session_record_free(record); + print_error(session); + return; + } + + assert(!strcmp(record->uuid, uuid)); + assert(!strcmp(record->this_user, this_user)); + + free(uuid); + free(this_user); + xen_session_record_free(record); + + fflush(stdout); +} + + +static int pstrcmp(const void *p1, const void *p2) +{ + return strcmp(*(char **)p1, *(char **)p2); +} + + +/** + * Print the list of supported methods. + */ +static void print_methods(xen_session *session) +{ + xen_string_set *methods; + + if (!xen_host_list_methods(session, &methods)) + { + print_error(session); + goto done; + } + + printf("%zd.\n", methods->size); + qsort(methods->contents, methods->size, sizeof(char *), pstrcmp); + + printf("Supported methods:\n"); + for (size_t i = 0; i < methods->size; i++) + { + printf(" %s\n", methods->contents[i]); + } + fflush(stdout); + +done: + xen_string_set_free(methods); +} + + +/** + * Print the metrics for the given VM. + */ +static void print_vm_metrics(xen_session *session, xen_vm vm) +{ + xen_vm_metrics vm_metrics; + if (!xen_vm_get_metrics(session, &vm_metrics, vm)) + { + print_error(session); + return; + } + + xen_vm_metrics_record *vm_metrics_record; + if (!xen_vm_metrics_get_record(session, &vm_metrics_record, vm_metrics)) + { + xen_vm_metrics_free(vm_metrics); + print_error(session); + return; + } + + char time[256]; + struct tm *tm = localtime(&vm_metrics_record->last_updated); + my_strftime(time, 256, "Metrics updated at %c, local time.\n", tm); + printf(time); + + tm = localtime(&vm_metrics_record->start_time); + my_strftime(time, 256, "VM running since %c, local time.\n", tm); + printf(time); + + for (size_t i = 0; i < vm_metrics_record->vcpus_utilisation->size; i++) + { + printf("%"PRId64" -> %lf.\n", + vm_metrics_record->vcpus_utilisation->contents[i].key, + vm_metrics_record->vcpus_utilisation->contents[i].val); + } + + printf("VCPU -> PCPU mapping:\n"); + for (size_t i = 0; i < vm_metrics_record->vcpus_cpu->size; i++) + { + printf(" %"PRId64" -> %"PRId64".\n", + vm_metrics_record->vcpus_cpu->contents[i].key, + vm_metrics_record->vcpus_cpu->contents[i].val); + } + + printf("Live scheduling parameters:\n"); + for (size_t i = 0; i < vm_metrics_record->vcpus_params->size; i++) + { + printf(" %s -> %s.\n", + vm_metrics_record->vcpus_params->contents[i].key, + vm_metrics_record->vcpus_params->contents[i].val); + } + + for (size_t i = 0; i < vm_metrics_record->vcpus_flags->size; i++) + { + printf("%"PRId64" -> ", + vm_metrics_record->vcpus_flags->contents[i].key); + xen_string_set *s = vm_metrics_record->vcpus_flags->contents[i].val; + for (size_t j = 0; j < s->size; j++) + { + printf("%s", s->contents[j]); + if (j + 1 != s->size) + { + printf(", "); + } + } + printf("\n"); + } + + xen_vm_metrics_record_free(vm_metrics_record); + xen_vm_metrics_free(vm_metrics); + + fflush(stdout); +} diff --git a/tools/libxen/test/test_event_handling.c b/tools/libxen/test/test_event_handling.c new file mode 100644 index 0000000..188b585 --- /dev/null +++ b/tools/libxen/test/test_event_handling.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2006-2007 XenSource, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +//#define PRINT_XML + +static void usage() +{ + fprintf(stderr, +"Usage:\n" +"\n" +" test_event_handling \n" +"\n" +"where\n" +" is the server's host and port, e.g. localhost:9363;\n" +" is the username to use at the server; and\n" +" is the password.\n"); + + exit(EXIT_FAILURE); +} + + +static char *url; + + +typedef struct +{ + xen_result_func func; + void *handle; +} xen_comms; + + +static size_t +write_func(void *ptr, size_t size, size_t nmemb, xen_comms *comms) +{ + size_t n = size * nmemb; +#ifdef PRINT_XML + printf("\n\n---Result from server -----------------------\n"); + printf("%s\n",((char*) ptr)); + fflush(stdout); +#endif + return comms->func(ptr, n, comms->handle) ? n : 0; +} + + +static int +call_func(const void *data, size_t len, void *user_handle, + void *result_handle, xen_result_func result_func) +{ + (void)user_handle; + +#ifdef PRINT_XML + printf("\n\n---Data to server: -----------------------\n"); + printf("%s\n",((char*) data)); + fflush(stdout); +#endif + + CURL *curl = curl_easy_init(); + if (!curl) { + return -1; + } + + xen_comms comms = { + .func = result_func, + .handle = result_handle + }; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_MUTE, 1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_func); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &comms); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); + + CURLcode result = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + + return result; +} + + +static void print_error(xen_session *session) +{ + fprintf(stderr, "Error: %d", session->error_description_count); + for (int i = 0; i < session->error_description_count; i++) + { + fprintf(stderr, "%s ", session->error_description[i]); + } + fprintf(stderr, "\n"); +} + + +/** + * Workaround for whinging GCCs, as suggested by strftime(3). + */ +static size_t my_strftime(char *s, size_t max, const char *fmt, + const struct tm *tm) +{ + return strftime(s, max, fmt, tm); +} + + +int main(int argc, char **argv) +{ + if (argc != 4) + { + usage(); + } + + url = argv[1]; + char *username = argv[2]; + char *password = argv[3]; + + xmlInitParser(); + xen_init(); + curl_global_init(CURL_GLOBAL_ALL); + +#define CLEANUP \ + do { \ + xen_session_logout(session); \ + curl_global_cleanup(); \ + xen_fini(); \ + xmlCleanupParser(); \ + } while(0) \ + + + xen_session *session = + xen_session_login_with_password(call_func, NULL, username, password); + + struct xen_string_set *classes = xen_string_set_alloc(0); + xen_event_register(session, classes); + xen_string_set_free(classes); + + if (!session->ok) + { + print_error(session); + CLEANUP; + return 1; + } + + while (true) + { + struct xen_event_record_set *events; + if (!xen_event_next(session, &events)) + { + print_error(session); + CLEANUP; + return 1; + } + + for (size_t i = 0; i < events->size; i++) + { + xen_event_record *ev = events->contents[i]; + char time[256]; + struct tm *tm = localtime(&ev->timestamp); + my_strftime(time, 256, "%c, local time", tm); + printf("Event received: ID = %"PRId64", %s.\n", ev->id, time); + switch (ev->operation) + { + case XEN_EVENT_OPERATION_ADD: + printf("%s created with UUID %s.\n", ev->class, ev->obj_uuid); + break; + + case XEN_EVENT_OPERATION_DEL: + printf("%s with UUID %s deleted.\n", ev->class, ev->obj_uuid); + break; + + case XEN_EVENT_OPERATION_MOD: + printf("%s with UUID %s modified.\n", ev->class, ev->obj_uuid); + break; + default: + assert(false); + } + } + + xen_event_record_set_free(events); + } + + CLEANUP; + + return 0; +} diff --git a/tools/misc/Makefile b/tools/misc/Makefile new file mode 100644 index 0000000..40e7bbf --- /dev/null +++ b/tools/misc/Makefile @@ -0,0 +1,57 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -Werror + +INCLUDES += -I $(XEN_XC) +INCLUDES += -I $(XEN_LIBXC) +INCLUDES += -I $(XEN_INCLUDE) +CFLAGS += $(INCLUDES) + +HDRS = $(wildcard *.h) + +TARGETS-y := xenperf xenpm +TARGETS-$(CONFIG_X86) += xen-detect +TARGETS := $(TARGETS-y) + +SUBDIRS-$(CONFIG_LOMOUNT) += lomount +SUBDIRS-$(CONFIG_MINITERM) += miniterm +SUBDIRS := $(SUBDIRS-y) + +INSTALL_BIN-y := xencons +INSTALL_BIN-$(CONFIG_X86) += xen-detect +INSTALL_BIN := $(INSTALL_BIN-y) + +INSTALL_SBIN-y := netfix xm xen-bugtool xen-python-path xend xenperf xsview xenpm +INSTALL_SBIN := $(INSTALL_SBIN-y) + +DEFAULT_PYTHON_PATH := $(shell $(XEN_ROOT)/tools/python/get-path) +PYTHON_PATH ?= $(DEFAULT_PYTHON_PATH) +INSTALL_PYTHON_PROG = $(XEN_ROOT)/tools/python/install-wrap \ +"$(PYTHON_PATH)" $(INSTALL_PROG) + +.PHONY: all +all: build + +.PHONY: build +build: $(TARGETS) + set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d; done + +.PHONY: install +install: build + $(INSTALL_DIR) $(DESTDIR)$(BINDIR) + $(INSTALL_DIR) $(DESTDIR)$(SBINDIR) + $(INSTALL_PYTHON_PROG) $(INSTALL_BIN) $(DESTDIR)$(BINDIR) + $(INSTALL_PYTHON_PROG) $(INSTALL_SBIN) $(DESTDIR)$(SBINDIR) + set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d install-recurse; done + +.PHONY: clean +clean: + $(RM) *.o $(TARGETS) *~ + set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done + +%.o: %.c $(HDRS) Makefile + $(CC) -c $(CFLAGS) -o $@ $< + +xenperf xenpm: %: %.o Makefile + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LDFLAGS_libxenctrl) diff --git a/tools/misc/fakei386xen b/tools/misc/fakei386xen new file mode 100755 index 0000000..e1462c2 --- /dev/null +++ b/tools/misc/fakei386xen @@ -0,0 +1,32 @@ +#!/bin/bash + +cln () { +while [ $# -gt 0 ]; do + ( + test -f "$1" || { echo "$1: No such file or directory" 1>&2; exit 1; } + { cp $1 cln$$ && rm $1 && mv cln$$ $1; } || { rm -f cln$$; exit 1; } + ) + shift +done +} + + +for i in `find include/asm-xen arch/xen -type l | xargs ls -l | egrep '../(asm-)?i386/' | awk '{print $9}'` +do + echo $i + cln $i +done + +mv include/asm-i386 include/asm-Xi386 +mv include/asm-xen include/asm-i386 +ln -s asm-i386 include/asm-xen +rm include/asm +ln -s asm-i386 include/asm +mv arch/i386 arch/Xi386 +mv arch/xen arch/i386 +ln -s i386 arch/xen + +mv Makefile XMakefile +#sed -e 's/^EXTRAVERSION =.*/EXTRAVERSION = -xen/' Makefile +echo ARCH=i386 >Makefile ; cat XMakefile >>Makefile + diff --git a/tools/misc/lomount/Makefile b/tools/misc/lomount/Makefile new file mode 100644 index 0000000..7eafce1 --- /dev/null +++ b/tools/misc/lomount/Makefile @@ -0,0 +1,27 @@ +XEN_ROOT=../../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -Werror + +HDRS = $(wildcard *.h) +OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) + +BIN = lomount + +.PHONY: all +all: build + +.PHONY: build +build: $(BIN) + +.PHONY: install +install install-recurse: build + $(INSTALL_PROG) $(BIN) $(SCRIPTS) $(DESTDIR)$(BINDIR) + +.PHONY: clean +clean: + $(RM) *.a *.so *.o *.rpm $(BIN) + +%: %.c $(HDRS) Makefile + $(CC) $(CFLAGS) -o $@ $< + diff --git a/tools/misc/lomount/lomount.c b/tools/misc/lomount/lomount.c new file mode 100644 index 0000000..74859e6 --- /dev/null +++ b/tools/misc/lomount/lomount.c @@ -0,0 +1,435 @@ +/* + * lomount - utility to mount partitions in a hard disk image + * + * Copyright (c) 2004 Jim Brown + * Copyright (c) 2004 Brad Watson + * Copyright (c) 2004 Mulyadi Santosa + * Major rewrite by Tristan Gingold: + * - Handle GPT partitions + * - Handle large files + * + * 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. + */ + +/* + * Return code: + * + * bit 7 set: lomount wrapper failed + * bit 7 clear: lomount wrapper ok; mount's return code in low 7 bits + * 0 success + */ + +enum +{ + ERR_USAGE = 0x80, // Incorrect usage + ERR_PART_PARSE, // Failed to parse partition table + ERR_NO_PART, // No such partition + ERR_NO_EPART, // No such extended partition + ERR_MOUNT // Other failure of mount command +}; + +#include +#include +#include +#include +#include +#include +#include + +#define BUF 4096 + +#define SECSIZE 512 + +struct pentry +{ + unsigned char bootable; + unsigned char start_head; + unsigned int start_cylinder; + unsigned char start_sector; + unsigned char system; + unsigned char end_head; + unsigned int end_cylinder; + unsigned char end_sector; + unsigned long long start_sector_abs; + unsigned long long no_of_sectors_abs; +}; + +static void +disp_entry (struct pentry *p) +{ + printf ("%10llu - %10llu: %02x %x\n", + SECSIZE * p->start_sector_abs, + SECSIZE * (p->start_sector_abs + p->no_of_sectors_abs - 1), + p->system, + p->bootable); +} + +static unsigned long +read_le4 (unsigned char *p) +{ + return (unsigned long) p[0] + | ((unsigned long) p[1] << 8) + | ((unsigned long) p[2] << 16) + | ((unsigned long) p[3] << 24); +} + +static unsigned long long +read_le8 (unsigned char *p) +{ + return (unsigned long long) p[0] + | ((unsigned long long) p[1] << 8) + | ((unsigned long long) p[2] << 16) + | ((unsigned long long) p[3] << 24) + | ((unsigned long long) p[4] << 32) + | ((unsigned long long) p[5] << 40) + | ((unsigned long long) p[6] << 48) + | ((unsigned long long) p[7] << 56); +} + +/* Return true if the partition table is a GPT protective MBR. */ +static int +check_gpt (struct pentry *part, int nbr_part) +{ + if (nbr_part != 4) + return 0; + if (part[0].system == 0xee + && part[1].no_of_sectors_abs == 0 + && part[2].no_of_sectors_abs == 0 + && part[3].no_of_sectors_abs == 0) + return 1; + return 0; +} + +static int +load_gpt (const char *diskimage, struct pentry *parttbl[]) +{ + FILE *fd; + size_t size; + int fail = -1; + unsigned char data[SECSIZE]; + unsigned long long entries_lba; + unsigned long entry_size; + struct pentry *part; + int nbr_part; + unsigned long long off; + int i; + + fd = fopen(diskimage, "r"); + if (fd == NULL) + { + perror(diskimage); + goto done; + } + fseeko (fd, SECSIZE, SEEK_SET); + size = fread (&data, 1, sizeof(data), fd); + if (size < (size_t)sizeof(data)) + { + fprintf(stderr, "Could not read the GPT header of %s.\n", + diskimage); + goto done; + } + + if (memcmp (data, "EFI PART", 8) != 0) + { + fprintf (stderr, "Bad GPT signature\n"); + goto done; + } + + entries_lba = read_le8 (&data[72]); + nbr_part = read_le4 (&data[80]); + entry_size = read_le4 (&data[84]); + +#ifdef DEBUG + fprintf(stderr, "lba entries: %llu, nbr_part: %u, entry_size: %lu\n", + entries_lba, nbr_part, entry_size); +#endif + part = malloc (nbr_part * sizeof (struct pentry)); + if (part == NULL) + { + fprintf(stderr,"Cannot allocate memory\n"); + goto done; + } + memset (part, 0, nbr_part * sizeof (struct pentry)); + *parttbl = part; + + off = entries_lba * SECSIZE; + for (i = 0; i < nbr_part; i++) + { + static const char unused_guid[16] = {0}; + fseeko (fd, off, SEEK_SET); + size = fread (&data, 1, 128, fd); + if (size < 128) + { + fprintf(stderr, "Could not read a GPT entry of %s.\n", + diskimage); + goto done; + } + if (memcmp (&data[0], unused_guid, 16) == 0) + { + part[i].start_sector_abs = 0; + part[i].no_of_sectors_abs = 0; + } + else + { + part[i].start_sector_abs = read_le8 (&data[32]); + part[i].no_of_sectors_abs = read_le8 (&data[40]); +#ifdef DEBUG + fprintf (stderr, "%d: %llu - %llu\n", i, + part[i].start_sector_abs, + part[i].no_of_sectors_abs); +#endif + /* Convert end to a number. */ + part[i].no_of_sectors_abs -= + part[i].start_sector_abs - 1; + } + off += entry_size; + } + + fail = nbr_part; + +done: + if (fd) + fclose(fd); + return fail; +} + +/* Read an MBR entry. */ +static void +read_mbr_record (unsigned char pi[16], struct pentry *res) +{ + res->bootable = *pi; + res->start_head = *(pi + 1); + res->start_cylinder = *(pi + 3) | ((*(pi + 2) << 2) & 0x300); + res->start_sector = *(pi + 2) & 0x3f; + res->system = *(pi + 4); + res->end_head = *(pi + 5); + res->end_cylinder = *(pi + 7) | ((*(pi + 6) << 2) & 0x300); + res->end_sector = *(pi + 6) & 0x3f; + res->start_sector_abs = read_le4 (&pi[8]); + res->no_of_sectors_abs = read_le4 (&pi[12]); +} + +/* Returns the number of partitions, -1 in case of failure. */ +int load_mbr(const char *diskimage, struct pentry *parttbl[]) +{ + FILE *fd; + size_t size; + int fail = -1; + int nbr_part; + int i; + unsigned char *pi; + unsigned char data [SECSIZE]; + unsigned long long extent; + struct pentry *part; + + nbr_part = 0; + + fd = fopen(diskimage, "r"); + if (fd == NULL) + { + perror(diskimage); + goto done; + } + size = fread (&data, 1, sizeof(data), fd); + if (size < (size_t)sizeof(data)) + { + fprintf(stderr, "Could not read the entire first sector of %s.\n", diskimage); + goto done; + } + + if (data [510] != 0x55 || data [511] != 0xaa) + { + fprintf(stderr,"MBR signature mismatch (invalid partition table?)\n"); + goto done; + } + + /* There is at most 4*4 + 4 = 20 entries, also there should be only + one extended partition. */ + part = malloc (20 * sizeof (struct pentry)); + if (part == NULL) + { + fprintf(stderr,"Cannot allocate memory\n"); + goto done; + } + *parttbl = part; + + /* Read MBR. */ + nbr_part = 4; + for (i = 0; i < 4; i++) + { + pi = &data [446 + 16 * i]; + read_mbr_record (pi, &part[i]); + } + + /* Read extended partitions. */ + for (i = 0; i < 4; i++) + { + if (part[i].system == 0xF || part[i].system == 0x5) + { + int j; + + extent = part[i].start_sector_abs * SECSIZE; + + fseeko (fd, extent, SEEK_SET); + size = fread (&data, 1, sizeof(data), fd); + if (size < (size_t)sizeof(data)) + { + fprintf(stderr, "Could not read extended partition of %s.", diskimage); + goto done; + } + + for (j = 0; j < 4; j++) + { + int n; + pi = &data [446 + 16 * j]; + n = nbr_part + j; + read_mbr_record (pi, &part[n]); + } + + nbr_part += 4; + } + } + + fail = nbr_part; + +done: + if (fd) + fclose(fd); + return fail; +} + +void usage(void) +{ + fprintf(stderr, "Usage: lomount [-verbose] [OPTIONS] -diskimage FILE -partition NUM [OPTIONS]\n"); + fprintf(stderr, "All OPTIONS are passed through to 'mount'.\n"); + fprintf(stderr, "ex. lomount -t fs-type -diskimage hda.img -partition 1 /mnt\n"); + exit(ERR_USAGE); +} + +int main(int argc, char ** argv) +{ + int status; + int nbr_part; + struct pentry *parttbl; + char buf[BUF], argv2[BUF]; + const char * diskimage = NULL; + int partition = 0; + unsigned long long sec, num, pnum; + int i; + size_t argv2_len = sizeof(argv2); + int verbose = 0; + + argv2[0] = '\0'; + + for (i = 1; i < argc; i ++) + { + if (strcmp(argv[i], "-diskimage")==0) + { + if (i == argc-1) + usage(); + i++; + diskimage = argv[i]; + } + else if (strcmp(argv[i], "-partition")==0) + { + if (i == argc-1) + usage(); + i++; + partition = atoi(argv[i]); + } + else if (strcmp(argv[i], "-verbose")==0) + { + verbose++; + } + else + { + size_t len = strlen(argv[i]); + if (len >= argv2_len-1) + usage(); + strcat(argv2, argv[i]); + strcat(argv2, " "); + len -= (len+1); + } + } + if (! diskimage || partition < 0) + usage(); + + nbr_part = load_mbr(diskimage, &parttbl); + if (check_gpt (parttbl, nbr_part)) { + free (parttbl); + nbr_part = load_gpt (diskimage, &parttbl); + } + if (nbr_part < 0) + return ERR_PART_PARSE; + if (partition == 0) + { + printf("Please specify a partition number. Table is:\n"); + printf("Num Start - End OS Bootable\n"); + for (i = 0; i < nbr_part; i++) + { + if (parttbl[i].no_of_sectors_abs != 0) + { + printf ("%2d: ", i + 1); + disp_entry (&parttbl[i]); + } + } + if (partition == 0) + return 0; + } + /* NOTE: need to make sure this always rounds down */ + //sec = total_known_sectors / sizeof_diskimage; + /* The above doesn't work unless the disk image is completely + filled by partitions ... unused space will thrown off the + sector size. The calculation assumes the disk image is + completely filled, and that the few sectors used to store + the partition table/MBR are few enough that the calculated + value is off by (larger than) a value less than one. */ + sec = 512; /* TODO: calculate real sector size */ +#ifdef DEBUG + printf("sec: %llu\n", sec); +#endif + if (partition > nbr_part) + { + fprintf(stderr, "Bad partition number\n"); + return ERR_NO_EPART; + } + num = parttbl[partition-1].start_sector_abs; + if (num == 0) + { + fprintf(stderr, "Partition %d was not found in %s.\n", + partition, diskimage); + return ERR_NO_PART; + } + + pnum = sec * num; +#ifdef DEBUG + printf("offset = %llu\n", pnum); +#endif + snprintf(buf, sizeof(buf), "mount -oloop,offset=%lld %s %s", + pnum, diskimage, argv2); + if (verbose) + printf("%s\n", buf); + + status = system(buf); + if (WIFEXITED(status)) + status = WEXITSTATUS(status); + else + status = ERR_MOUNT; + return status; +} diff --git a/tools/misc/miniterm/Makefile b/tools/misc/miniterm/Makefile new file mode 100644 index 0000000..116ef1f --- /dev/null +++ b/tools/misc/miniterm/Makefile @@ -0,0 +1,22 @@ +XEN_ROOT:=../../.. +include $(XEN_ROOT)/tools/Rules.mk + +TARGET = miniterm + +.PHONY: all +all: $(TARGET) + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(BINDIR) + $(INSTALL_PROG) $(TARGET) $(DESTDIR)$(BINDIR) + +.PHONY: install-recurse + : No sense in installing miniterm on the Xen box. + +.PHONY: clean +clean: + $(RM) *.o $(TARGET) *~ + +$(TARGET): $(TARGET).c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< diff --git a/tools/misc/miniterm/README b/tools/misc/miniterm/README new file mode 100644 index 0000000..2ca4501 --- /dev/null +++ b/tools/misc/miniterm/README @@ -0,0 +1,13 @@ +This is a modified version of the miniterm program distributed as part +of the Linux Programmer's Guide (LPG) by Sven Goldt. + +It is intended to be used as a dumb raw terminal for debugging Xen +machines over the serial line. + +By default it will connect to COM1 (/dev/ttyS0) at 115200 baud. +These options can be modified as follows: + miniterm [-b] [-d] + +'ctrl-b' quits miniterm. + + -- Keir Fraser (21/9/2003) \ No newline at end of file diff --git a/tools/misc/miniterm/miniterm.c b/tools/misc/miniterm/miniterm.c new file mode 100644 index 0000000..3f8043d --- /dev/null +++ b/tools/misc/miniterm/miniterm.c @@ -0,0 +1,195 @@ +/****************************************************************************** + * miniterm.c + * + * Adapted from the example program distributed with the Linux Programmer's + * Guide (LPG). This has been robustified and tweaked to work as a debugging + * terminal for Xen-based machines. + * + * Modifications are released under GPL and copyright (c) 2003, K A Fraser + * The original copyright message and license is fully intact below. + */ + +/* + * AUTHOR: Sven Goldt (goldt@math.tu-berlin.de) + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_BAUDRATE 115200 +#define DEFAULT_SERDEVICE "/dev/ttyS0" +#define ENDMINITERM 0x1d + +volatile int stop = 0; + +void child_handler(int s) +{ + stop = 1; +} + +int cook_baud(int baud) +{ + int cooked_baud = 0; + switch ( baud ) + { + case 50: cooked_baud = B50; break; + case 75: cooked_baud = B75; break; + case 110: cooked_baud = B110; break; + case 134: cooked_baud = B134; break; + case 150: cooked_baud = B150; break; + case 200: cooked_baud = B200; break; + case 300: cooked_baud = B300; break; + case 600: cooked_baud = B600; break; + case 1200: cooked_baud = B1200; break; + case 1800: cooked_baud = B1800; break; + case 2400: cooked_baud = B2400; break; + case 4800: cooked_baud = B4800; break; + case 9600: cooked_baud = B9600; break; + case 19200: cooked_baud = B19200; break; + case 38400: cooked_baud = B38400; break; + case 57600: cooked_baud = B57600; break; + case 115200: cooked_baud = B115200; break; + } + return cooked_baud; +} + +int main(int argc, char **argv) +{ + int fd, c, cooked_baud = cook_baud(DEFAULT_BAUDRATE); + char *sername = DEFAULT_SERDEVICE; + struct termios oldsertio, newsertio, oldstdtio, newstdtio; + struct sigaction sa; + static char start_str[] = + "************ REMOTE CONSOLE: CTRL-] TO QUIT ********\r\n"; + static char end_str[] = + "\n************ REMOTE CONSOLE EXITED *****************\n"; + + while ( --argc != 0 ) + { + char *p = argv[argc]; + if ( *p++ != '-' ) + goto usage; + if ( *p == 'b' ) + { + p++; + if ( (cooked_baud = cook_baud(atoi(p))) == 0 ) + { + fprintf(stderr, "Bad baud rate '%d'\n", atoi(p)); + goto usage; + } + } + else if ( *p == 'd' ) + { + sername = ++p; + if ( *sername == '\0' ) + goto usage; + } + else + goto usage; + } + + /* Not a controlling tty: CTRL-C shouldn't kill us. */ + fd = open(sername, O_RDWR | O_NOCTTY); + if ( fd < 0 ) + { + perror(sername); + exit(-1); + } + + tcgetattr(fd, &oldsertio); /* save current modem settings */ + + /* + * 8 data, no parity, 1 stop bit. Ignore modem control lines. Enable + * receive. Set appropriate baud rate. NO HARDWARE FLOW CONTROL! + */ + newsertio.c_cflag = cooked_baud | CS8 | CLOCAL | CREAD; + + /* Raw input. Ignore errors and breaks. */ + newsertio.c_iflag = IGNBRK | IGNPAR; + + /* Raw output. */ + newsertio.c_oflag = OPOST; + + /* No echo and no signals. */ + newsertio.c_lflag = 0; + + /* blocking read until 1 char arrives */ + newsertio.c_cc[VMIN]=1; + newsertio.c_cc[VTIME]=0; + + /* now clean the modem line and activate the settings for modem */ + tcflush(fd, TCIFLUSH); + tcsetattr(fd,TCSANOW,&newsertio); + + /* next stop echo and buffering for stdin */ + tcgetattr(0,&oldstdtio); + tcgetattr(0,&newstdtio); /* get working stdtio */ + newstdtio.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + newstdtio.c_oflag &= ~OPOST; + newstdtio.c_cflag &= ~(CSIZE | PARENB); + newstdtio.c_cflag |= CS8; + newstdtio.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + newstdtio.c_cc[VMIN]=1; + newstdtio.c_cc[VTIME]=0; + tcsetattr(0,TCSANOW,&newstdtio); + + /* Terminal settings done: now enter the main I/O loops. */ + switch ( fork() ) + { + case 0: + close(1); /* stdout not needed */ + for ( c = (char)getchar(); c != ENDMINITERM; c = (char)getchar() ) + write(fd,&c,1); + tcsetattr(fd,TCSANOW,&oldsertio); + tcsetattr(0,TCSANOW,&oldstdtio); + close(fd); + exit(0); /* will send a SIGCHLD to the parent */ + break; + case -1: + perror("fork"); + tcsetattr(fd,TCSANOW,&oldsertio); + close(fd); + exit(-1); + default: + write(1, start_str, strlen(start_str)); + close(0); /* stdin not needed */ + sa.sa_handler = child_handler; + sa.sa_flags = 0; + sigaction(SIGCHLD,&sa,NULL); /* handle dying child */ + while ( !stop ) + { + read(fd,&c,1); /* modem */ + c = (char)c; + write(1,&c,1); /* stdout */ + } + wait(NULL); /* wait for child to die or it will become a zombie */ + write(1, end_str, strlen(end_str)); + break; + } + + return 0; + + usage: + printf("miniterm [-b] [-d]\n"); + printf("Default baud rate: %d\n", DEFAULT_BAUDRATE); + printf("Default device: %s\n", DEFAULT_SERDEVICE); + return 1; +} diff --git a/tools/misc/netfix b/tools/misc/netfix new file mode 100644 index 0000000..669a954 --- /dev/null +++ b/tools/misc/netfix @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- mode: python; -*- +#============================================================================ +# Copyright (C) 2004 Mike Wray +#============================================================================ +# Move the IP address from eth0 onto the Xen bridge (xenbr0). +# Only works if the bridge control utils (brctl) have been installed. +#============================================================================ + +from getopt import getopt + +# add fallback path for non-native python path installs if needed +sys.path.append('/usr/lib/python') +sys.path.append('/usr/lib64/python') +from xen.util.Brctl import * + +short_options = 'hvqni:b:c' +long_options = ['help', 'verbose', 'quiet', + 'interface=', 'bridge=', 'create'] + +defaults['interface'] = 'eth0' +defaults['bridge'] = 'xenbr0' + +def usage(): + print """Usage: + %s [options] + + Reconfigure routing so that has the IP address from + . This lets IP carry on working when + is attached to for virtual networking. + Uses brctl to add to , + so this can be run before any domains have been created. + """ % sys.argv[0] + print """ + -i, --interface interface, default %(interface)s. + -b, --bridge bridge, default %(bridge)s. + -c, --create create the bridge. + -v, --verbose Print commands. + -q, --quiet Don't print commands. + -n, --dry-run Don't execute commands. + -h, --help Print this help. + """ % defaults + sys.exit(1) + + +def main(): + lopts = set_opts(Opts(defaults)) + lopts.dryrun = 0 + (options, args) = getopt(sys.argv[1:], short_options, long_options) + if args: usage() + for k, v in options: + if k in ['-h', '--help']: + usage() + elif k in ['-c', '--create']: + lopts.create = 1 + elif k in ['-i', '--interface']: + lopts.interface = v + elif k in ['-b', '--bridge']: + lopts.bridge = v + elif k in ['-q', '--quiet']: + lopts.verbose = 0 + elif k in ['-v', '--verbose']: + lopts.verbose = 1 + elif k in ['-n', '--dry-run']: + lopts.dryrun = 1 + reconfigure(lopts.interface, lopts.bridge) + +if __name__ == '__main__': + main() diff --git a/tools/misc/nsplitd/Makefile b/tools/misc/nsplitd/Makefile new file mode 100644 index 0000000..d15bb4d --- /dev/null +++ b/tools/misc/nsplitd/Makefile @@ -0,0 +1,25 @@ +XEN_ROOT := ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFILES = $(wildcard *.c) + +HDRS = $(wildcard *.h) +OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) + +TARGET = nsplitd + +.PHONY: all +all: $(TARGET) + +.PHONY: install +install: all + +.PHONY: clean +clean: + $(RM) *.o $(TARGET) *~ + +$(TARGET): $(OBJS) + $(HOSTCC) $(HOSTCFLAGS) -o $@ $^ + +%.o: %.c $(HDRS) Makefile + $(HOSTCC) $(HOSTCFLAGS) -c -o $@ $< diff --git a/tools/misc/nsplitd/nsplitd.c b/tools/misc/nsplitd/nsplitd.c new file mode 100644 index 0000000..32f0b56 --- /dev/null +++ b/tools/misc/nsplitd/nsplitd.c @@ -0,0 +1,686 @@ +/* + * nsplitd.c + * --------- + * + * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ + * + * Copyright (c) 1995, University of Cambridge Computer Laboratory, + * Copyright (c) 1995, Richard Black, All Rights Reserved. + * + * + * A complete re-implementation of DME's nsplitd for use from inetd + * + */ + +/* The basic stream comes in (via inetd) and we then conenct to + * somewhere else providing a loop-through service, except we offer + * two other ports for connection - one of which gets a second channel + * using the top bit to distinguish, and the other is a master control + * port (normally used for gdb) which gets complete exclusive access + * for its duration. + * + * Originally designed for multiplexing a xwcons/telnet with a gdb + * post-mortem debugging session. + * + * Here is a picture: + * + * port0 (from inetd) + * 8-bit connection / + * made by us <----> nsplitd <-----gdbport (default port0+2) + * to host:port/tcp |\ + * | port1 (default port0+1) + * \ + * control (default port0+3) + * + * If port1 is explicitly disabled (through a command-line option) then + * port0 becomes 8-bit clean. + */ + +/* + * N.B.: We do NOT support 8 bit stdin/stdout usage on a + * /dev/... because to do that right involves much messing with ioctl + * and TIOC... etc. If you want to do that sort of thing then the + * right way to do it is to chain this onto wconsd (which does know + * about and understand all the ioctl and TIOC grief). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif + +#define DB(x) /* ((x), fflush(stderr)) */ + +extern char *optarg; + +extern int optind, opterr, optopt; + +static char *prog_name; + +static void usage(void) +{ + fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n", + prog_name); + fprintf(stderr, "usage: %s [-h][-g]" + "[-c][-8] host:service\n", + prog_name); + exit(1); +} + +static void fault(char *format, ...) +{ + va_list ap; + char logbuf[1024]; + + va_start(ap, format); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, format, ap); + fflush(stderr); + va_end(ap); + + /* XXX This is a bit dubious, but there is no vsyslog */ + va_start(ap, format); + vsnprintf(logbuf, sizeof(logbuf), format, ap); + syslog(LOG_ERR, logbuf); + va_end(ap); + exit(1); +} + +static int getservice(char *name, unsigned short *port) +{ + struct servent *se; + + if (!name) return -1; + + if (isdigit(name[0])) + *port = atoi(name); + else + { + if (!(se = getservbyname(name, "tcp"))) + return -1; + *port = ntohs(se->s_port); + } + return 0; +} + +/* + * connect_host: connect to ("name", "port") + */ +static int connect_host (char *name, unsigned int port) +{ + int fd; + struct hostent *hostent; + struct sockaddr_in sin; + int on; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + fault("socket"); + + if (!(hostent = gethostbyname(name))) + fault("gethostbyname: %s: %s\n", name, strerror(errno)); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr)); + + if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) + fault("connect: %s:%u: %s\n", name, port, strerror(errno)); + + on = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); + + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + + return fd; +} + +/* + * open a tcp socket and start listening for connections on it + */ +static int startlistening(unsigned short port) +{ + int fd, on; + struct sockaddr_in sin; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + fault("socket"); + + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + sin.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, &sin, sizeof(sin)) < 0) + fault("bind: %u: %s\n", port, strerror(errno)); + + if (listen(fd, 1) < 0) + fault("listen: %s\n", strerror(errno)); + + return fd; +} + +static void noblock(int fd) +{ + int on=1; + + if (ioctl(fd, FIONBIO, &on) < 0) + fault("ioctl: FIONBIO: %s\n", strerror(errno)); +} + + +/* You might not believe this, but fd_sets don't have to be a 32-bit + * integer. In particular, in glibc2 it is an array of unsigned + * longs. Hence, this hacked up FD_SET_rjb() that works out if it + * would have been a nop. */ +#define FD_SET_rjb(fd, setp) \ +do { \ + if ((fd) != 32) \ + FD_SET((fd), (setp)); \ +} while(0) + +#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0) + +#define MAXSIZE 256 + +/* ----------------------------------------------------------------- + * The main bit of the algorithm. Note we use 32 to mean not connected + * because this gives us 1<<32 == 0. We could have done this one + * character at a time, but that would have been very inefficient and + * not the unix way. */ +static int debug; + +static void doit(int actl, int acto, int lish, int lisg, int lisc) +{ + int acth, actg, actc; + int gdbmode = FALSE; + char gibuf[MAXSIZE], oibuf[MAXSIZE]; + char libuf[MAXSIZE], lobuf[MAXSIZE]; + char hibuf[MAXSIZE], hobuf[MAXSIZE]; + char ctlbuf[MAXSIZE]; + fd_set rdfs, wrfs, exfs; + int gicc, oicc, licc, locc, hicc, hocc, ctlcc; + char *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr; + int rc, fromlen; + struct sockaddr_in from; + + gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0; + acth = actg = actc = 32; /* XXX yummy */ + + noblock(actl); + noblock(acto); + + for(;;) + { + FD_ZERO(&rdfs); + FD_ZERO(&wrfs); + FD_ZERO(&exfs); + + /* always take input from the control port (if it's connected) */ + FD_SET_rjb(actc, &rdfs); + + if (gdbmode) + { + if (oicc) + FD_SET_rjb(actg, &wrfs); + else + FD_SET_rjb(acto, &rdfs); + + if (gicc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(actg, &rdfs); + } + else + { + /* There is no such thing as oibuf because its been split into + * lobuf and hobuf + */ + if (locc || hocc) + { + if (locc) + FD_SET_rjb(actl, &wrfs); + if (hocc) + FD_SET_rjb(acth, &wrfs); + } + else + FD_SET_rjb(acto, &rdfs); + + if (licc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(actl, &rdfs); + + if (hicc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(acth, &rdfs); + } + + if (acth == 32 && lish>=0) FD_SET_rjb(lish, &rdfs); + if (actg == 32) FD_SET_rjb(lisg, &rdfs); + if (actc == 32) FD_SET_rjb(lisc, &rdfs); + + /* now make exfs the union of the read and write fd sets, plus + * "actl" */ + { + int i; + exfs = rdfs; + for(i=0; i<32; i++) /* XXX we only copy fd numbers up to 31 */ + if (FD_ISSET(i, &wrfs)) + FD_SET_rjb(i, &exfs); + FD_SET_rjb(actl, &exfs); + } + + /* XXX AND: can't print something of type fd_set as %x - it + * might be an array */ + DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n", + prog_name, rdfs, wrfs, exfs)); + + if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0) + fault("select: %s\n", strerror(errno)); + + DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n", + prog_name, rdfs, wrfs, exfs)); + + /* XXX it appears that a non-blocking socket may not show up + * correctly in exfs but instead goes readable with no data in + * it. Thus we check for zero and goto the appropriate close + * method. */ + + /* Deal with exceptions */ + if (FD_ISSET_rjb(actg, &exfs)) + { + exfs_actg: + close(actg); + gdbmode = FALSE; + oicc = 0; + oiptr = oibuf; + actg = 32; + continue; /* because assumptions changed */ + } + if (FD_ISSET_rjb(acth, &exfs)) + { + exfs_acth: + close(acth); + hicc = hocc = 0; + hiptr = hibuf; + hoptr = hibuf; + acth = 32; + continue; /* because assumptions changed */ + } + if (FD_ISSET_rjb(actl, &exfs) || + FD_ISSET_rjb(acto, &exfs)) + { + exfs_actl: + exfs_acto: + /* Thats all folks ... */ + break; + } + if (FD_ISSET_rjb(actc, &exfs)) + { + exfs_ctl: + close(actc); + actc = 32; + ctlcc = 0; + continue; + } + + /* Deal with reading */ + if (FD_ISSET_rjb(acto, &rdfs)) + { + if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0) + fault("read acto: %d: %s\n", oicc, strerror(errno)); + if (!oicc) goto exfs_acto; + + if (!gdbmode) + { + int t; + + assert((locc == 0) && (hocc == 0)); + loptr = lobuf; + hoptr = hobuf; + + if (lish>=0) { + for(t=0; t=0 && (FD_ISSET_rjb(lish, &rdfs))) + { + fromlen = sizeof(from); + if ((acth = accept(lish, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept: %m"); + acth = 32; + } + else + { + noblock(acth); + hicc = hocc = 0; + syslog(LOG_INFO, "highbit client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs))) + { + fromlen = sizeof(from); + if ((actg = accept(lisg, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept: %m"); + actg = 32; + } + else + { + noblock(actg); + gicc = 0; + gdbmode = TRUE; + syslog(LOG_INFO, "gdb client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs))) + { + fromlen = sizeof(from); + if ((actc = accept(lisc, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept (ctl): %m"); + actc = 32; + } + else + { + noblock(actc); + syslog(LOG_INFO, "ctl client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + /* Back to top of loop */ + } + + /* We are bailing because one of the primary connections has gone + * away. We close these all explicitly here because that way the + * timeout on reusing the port numbers is smnaller. */ + + close(acth); + close(actg); + /* XXX AND: why are we closing all these "character counts" ?? */ + close(gicc); + close(oicc); + close(licc); + close(locc); + close(hicc); + close(hocc); +} + +/* + * ------------------------------------------------------------ + */ +int main(int argc, char **argv) +{ + /* In general, suffix "l" is low channel, "h" is high channel, "g" + * is gdb channel, "c" is control channel and "o" is output channel. + */ + struct sockaddr_in from; + int infd = 0, outfd; + unsigned short portl, porth, portg, portc, porto; + int on = 1, c; + char *outname, *outservice; + int fromlen; + int lish, lisg, lisc; +#if 0 + FILE *newerr; +#endif /* 0 */ + + prog_name = argv[0]; + + if (isatty(infd)) + usage(); + + /* Here, then not just a simple idiot. */ + + signal(SIGPIPE, SIG_IGN); + + openlog(prog_name, LOG_PID, LOG_DAEMON); + + fromlen = sizeof(from); + if (getsockname(infd, &from, &fromlen) < 0) + fault("getsockname: %s", strerror(errno)); + if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET)) + fault("not an inet socket (family=%d)\n", from.sin_family); + + portl = ntohs(from.sin_port); + porth = portl+1; + portg = porth+1; + portc = portg+1; + + fromlen = sizeof(from); + if (getpeername(infd, &from, &fromlen) < 0) + fault("getpeername: %s", strerror(errno)); + if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET)) + fault("not an inet socket (family=%d)\n", from.sin_family); + + syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl, + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + + if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + + /* from here on, we map stderr to output on the connection so we can + * report errors to the remote user. + */ +#if 0 + if (!(newerr = fdopen(infd, "w"))) + syslog(LOG_WARNING, "fdopen: %m"); + else + *stderr = *newerr; +#endif + + while((c = getopt(argc, argv, "d8h:g:c:")) != EOF) + { + switch(c) + { + case 'd': + debug++; + break; + + case 'h': + /* high bit port */ + if (getservice(optarg, &porth) < 0) + fault("getservice failed (high port '%s')\n", optarg); + break; + + case 'g': + /* gdb port */ + if (getservice(optarg, &portg) < 0) + fault("getservice failed (gdb port '%s')\n", optarg); + break; + + case 'c': + /* control port */ + if (getservice(optarg, &portc) < 0) + fault("getservice failed (control port '%s')\n", optarg); + break; + + case '8': + /* 8-bit clean; no high port */ + porth=0; + break; + + default: + fault("bad argument list!\n"); + } + } + + if (argc != optind + 1) + fault("unparsed arguments (%d!=%d)\n", argc, optind+1); + + outname = argv[optind]; + if (!(outservice = strchr(outname, ':'))) + fault("output arg '%s' doesn't contain ':'\n", outname); + *outservice++ = 0; + if (getservice(outservice, &porto) < 0) + fault("getservice failed (output port '%s')\n", outservice); + + /* Time to start the sockets */ + + if (porth) { + lish = startlistening(porth); + } else { + lish = -1; + } + lisg = startlistening(portg); + lisc = startlistening(portc); + + outfd = connect_host(outname, porto); + + doit(infd, outfd, lish, lisg, lisc); + + syslog(LOG_INFO, "terminating normally\n"); + + fclose(stderr); + + closelog(); + exit(0); +} + +/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */ diff --git a/tools/misc/sxp-pretty b/tools/misc/sxp-pretty new file mode 100644 index 0000000..4b8eaed --- /dev/null +++ b/tools/misc/sxp-pretty @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- mode: python; -*- +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2007 XenSource Inc. +#============================================================================ + + +import commands +import os.path +import pprint +import sys + +result = commands.getstatusoutput(os.path.join(os.path.dirname(sys.argv[0]), + 'xen-python-path')) +if result[0] != 0: + print >>sys.stderr, result[1] + sys.exit(1) + +sys.path.append(result[1]) + +import xen.xend.sxp as sxp + +def main(): + if len(sys.argv) == 1 or sys.argv[1] in ['', '-']: + s = sxp.parse(sys.stdin) + else: + s = sxp.parse(open(sys.argv[1])) + + pprint.pprint(s) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/misc/xen-bugtool b/tools/misc/xen-bugtool new file mode 100644 index 0000000..cf41c8c --- /dev/null +++ b/tools/misc/xen-bugtool @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# -*- mode: python; -*- + +# Copyright (c) 2005, XenSource Ltd. + +import sys + +sys.path.append('/usr/lib/python') +sys.path.append('/usr/lib64/python') + +from xen.util import bugtool + + +if __name__ == "__main__": + try: + sys.exit(bugtool.main()) + except KeyboardInterrupt: + print "\nInterrupted." + sys.exit(1) diff --git a/tools/misc/xen-clone.README b/tools/misc/xen-clone.README new file mode 100644 index 0000000..dfa86d1 --- /dev/null +++ b/tools/misc/xen-clone.README @@ -0,0 +1,23 @@ + +xen-clone + +usage: xen-clone + +This script can be used to 'bk clone' and build a xen and xenolinux image +from the master BK repository, either from a local copy, or from the +public repository bk://xen.bkbits.net/xeno.bk + +In many circumstances, it can be invoked without any arguments and +just `does the right thing'. + +The default dest_dir is 'xeno-clone', relative to the current directory. + +To build xenolinux, the script needs a pristine copy of the equivalent +linux tree. The script looks in a couple of places on the local filesystem, +then tries a download from from ftp://ftp.kernel.org/pub/linux/kernel/v2.4/ + +The script also tries a number of optional UCCL site-specific operations +that configure the test machine booting infrastructure to boot the +resultant image. + + diff --git a/tools/misc/xen-detect.c b/tools/misc/xen-detect.c new file mode 100644 index 0000000..c918945 --- /dev/null +++ b/tools/misc/xen-detect.c @@ -0,0 +1,108 @@ +/****************************************************************************** + * 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 + +static int pv_context; + +static void cpuid(uint32_t idx, + uint32_t *eax, + uint32_t *ebx, + uint32_t *ecx, + uint32_t *edx) +{ + asm volatile ( + "test %1,%1 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid" + : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) + : "0" (idx), "1" (pv_context) ); +} + +static int check_for_xen(void) +{ + uint32_t eax, ebx, ecx, edx; + char signature[13]; + + cpuid(0x40000000, &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) || (eax < 0x40000002) ) + return 0; + + cpuid(0x40000001, &eax, &ebx, &ecx, &edx); + printf("Running in %s context on Xen v%d.%d.\n", + pv_context ? "PV" : "HVM", (uint16_t)(eax >> 16), (uint16_t)eax); + return 1; +} + +int main(void) +{ + pid_t pid; + int status; + uint32_t dummy; + + /* Check for execution in HVM context. */ + if ( check_for_xen() ) + return 0; + + /* Now we check for execution in PV context. */ + pv_context = 1; + + /* + * Fork a child to test the paravirtualised CPUID instruction. + * If executed outside Xen PV context, the extended opcode will fault. + */ + pid = fork(); + switch ( pid ) + { + case 0: + /* Child: test paravirtualised CPUID opcode and then exit cleanly. */ + cpuid(0x40000000, &dummy, &dummy, &dummy, &dummy); + exit(0); + case -1: + fprintf(stderr, "Fork failed.\n"); + return 0; + } + + /* + * Parent waits for child to terminate and checks for clean exit. + * Only if the exit is clean is it safe for us to try the extended CPUID. + */ + waitpid(pid, &status, 0); + if ( WIFEXITED(status) && check_for_xen() ) + return 0; + + printf("Not running on Xen.\n"); + return 0; +} diff --git a/tools/misc/xen-python-path b/tools/misc/xen-python-path new file mode 100644 index 0000000..57774a3 --- /dev/null +++ b/tools/misc/xen-python-path @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- mode: python; -*- +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 XenSource Inc. +#============================================================================ + + +# Use the auxbin module in Xend to determine the correct Python path. We +# take the first installed instance of auxbin that we find, and then run it +# to determine the correct path, appending that to sys.path. + +AUXBIN = 'xen/util/auxbin.py' + +import os +import os.path +import sys + +usr = os.path.dirname(os.path.dirname(sys.argv[0])) +list = [ os.path.join(usr,'lib64') ] +list += [ os.path.join(usr,'lib') ] +list += ['/usr/lib64', '/usr/lib'] + +for l in list: + for p in ['python%s' % sys.version[:3], 'python']: + for k in ['', 'site-packages/']: + d = os.path.join(l, p, k) + if os.path.exists(os.path.join(d, AUXBIN)): + sys.path.append(d) + import xen.util.auxbin + print os.path.join(xen.util.auxbin.libpath(), p) + sys.exit(0) + +print >>sys.stderr, "Cannot find Xen Python modules." +sys.exit(1) diff --git a/tools/misc/xencons b/tools/misc/xencons new file mode 100755 index 0000000..8bd3178 --- /dev/null +++ b/tools/misc/xencons @@ -0,0 +1,92 @@ +#!/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])) diff --git a/tools/misc/xend b/tools/misc/xend new file mode 100644 index 0000000..2cbdf61 --- /dev/null +++ b/tools/misc/xend @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- mode: python; -*- +#============================================================================ +# Copyright (C) 2004 Mike Wray +# Copyright (C) 2005-2006 XenSource Inc +#============================================================================ + +"""Xen management daemon. + Provides console server and HTTP management api. + + Run: + xend start + + Restart: + xend restart + + The daemon is stopped with: + xend stop + + The daemon should reconnect to device control interfaces + and recover its state when restarted. + + On Solaris, the daemons are SMF managed, and you should not attempt + to start xend by hand. +""" +import fcntl +import glob +import os +import os.path +import sys +import socket +import signal +import time +import commands + +xpp = os.path.join(os.path.dirname(sys.argv[0]), 'xen-python-path') +if os.path.exists(xpp): + result = commands.getstatusoutput(xpp) + if result[0] != 0: + print >>sys.stderr, result[1] + sys.exit(1) + sys.path.append(result[1]) + +from xen.xend.server import SrvDaemon + +class CheckError(ValueError): + pass + +def hline(): + print >>sys.stderr, "*" * 70 + +def msg(message): + print >>sys.stderr, "*" * 3, message + +def check_logging(): + """Check python logging is installed and raise an error if not. + Logging is standard from Python 2.3 on. + """ + try: + import logging + except ImportError: + hline() + msg("Python logging is not installed.") + msg("Use 'make install-logging' at the xen root to install.") + msg("") + msg("Alternatively download and install from") + msg("http://www.red-dove.com/python_logging.html") + hline() + raise CheckError("logging is not installed") + +def check_user(): + """Check that the effective user id is 0 (root). + """ + if os.geteuid() != 0: + hline() + msg("Xend must be run as root.") + hline() + raise CheckError("invalid user") + +def start_daemon(daemon, *args): + if os.fork() == 0: + os.execvp(daemon, (daemon,) + args) + +def start_xenstored(): + pidfname = "/var/run/xenstore.pid" + try: + f = open(pidfname, "a") + try: + fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + rootdir = os.getenv("XENSTORED_ROOTDIR") or "/var/lib/xenstored" + for i in glob.glob(rootdir + "/tdb*"): + try: + os.unlink(i) + except: + pass + os.unlink(pidfname) + except: + pass + f.close() + except: + pass + XENSTORED_TRACE = os.getenv("XENSTORED_TRACE") + cmd = "xenstored --pid-file /var/run/xenstore.pid" + if XENSTORED_TRACE: + cmd += " -T /var/log/xen/xenstored-trace.log" + s,o = commands.getstatusoutput(cmd) + +def start_consoled(): + XENCONSOLED_TRACE = os.getenv("XENCONSOLED_TRACE") + args = "" + if XENCONSOLED_TRACE: + args += "--log=" + XENCONSOLED_TRACE + start_daemon("xenconsoled", args) + +def start_blktapctrl(): + start_daemon("blktapctrl", "") + +def main(): + try: + check_logging() + check_user() + except CheckError: + sys.exit(1) + + daemon = SrvDaemon.instance() + if not sys.argv[1:]: + print 'usage: %s {start|stop|reload|restart}' % sys.argv[0] + elif sys.argv[1] == 'start': + if os.uname()[0] != "SunOS": + start_xenstored() + start_consoled() + start_blktapctrl() + return daemon.start() + elif sys.argv[1] == 'trace_start': + start_xenstored() + start_consoled() + start_blktapctrl() + return daemon.start(trace=1) + elif sys.argv[1] == 'stop': + return daemon.stop() + elif sys.argv[1] == 'reload': + return daemon.reloadConfig() + elif sys.argv[1] == 'restart': + start_xenstored() + start_consoled() + start_blktapctrl() + return daemon.stop() or daemon.start() + elif sys.argv[1] == 'status': + return daemon.status() + else: + print 'not an option:', sys.argv[1] + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/misc/xenperf.c b/tools/misc/xenperf.c new file mode 100644 index 0000000..36d1f1d --- /dev/null +++ b/tools/misc/xenperf.c @@ -0,0 +1,227 @@ +/* -*- 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 + +static int lock_pages(void *addr, size_t len) +{ + int e = 0; +#ifndef __sun__ + e = mlock(addr, len); +#endif + return (e); +} + +static void unlock_pages(void *addr, size_t len) +{ +#ifndef __sun__ + munlock(addr, len); +#endif +} + +int main(int argc, char *argv[]) +{ + int i, j, xc_handle; + xc_perfc_desc_t *pcd; + 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()) == -1 ) + { + fprintf(stderr, "Error opening xc interface: %d (%s)\n", + errno, strerror(errno)); + return 1; + } + + if ( reset ) + { + if ( xc_perfc_control(xc_handle, XEN_SYSCTL_PERFCOP_reset, + NULL, NULL, NULL, NULL) != 0 ) + { + fprintf(stderr, "Error reseting performance counters: %d (%s)\n", + errno, strerror(errno)); + return 1; + } + + return 0; + } + + if ( xc_perfc_control(xc_handle, XEN_SYSCTL_PERFCOP_query, + NULL, NULL, &num_desc, &num_val) != 0 ) + { + fprintf(stderr, "Error getting number of perf counters: %d (%s)\n", + errno, strerror(errno)); + return 1; + } + + pcd = malloc(sizeof(*pcd) * num_desc); + pcv = malloc(sizeof(*pcv) * num_val); + + if ( pcd == NULL + || lock_pages(pcd, sizeof(*pcd) * num_desc) != 0 + || pcv == NULL + || lock_pages(pcv, sizeof(*pcv) * num_val) != 0) + { + fprintf(stderr, "Could not alloc or lock buffers: %d (%s)\n", + errno, strerror(errno)); + exit(-1); + } + + if ( xc_perfc_control(xc_handle, XEN_SYSCTL_PERFCOP_query, + pcd, pcv, NULL, NULL) != 0 ) + { + fprintf(stderr, "Error getting perf counter: %d (%s)\n", + errno, strerror(errno)); + return 1; + } + + unlock_pages(pcd, sizeof(*pcd) * num_desc); + unlock_pages(pcv, sizeof(*pcv) * num_val); + + 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; + } + + return 0; +} diff --git a/tools/misc/xenpm.c b/tools/misc/xenpm.c new file mode 100644 index 0000000..618aa27 --- /dev/null +++ b/tools/misc/xenpm.c @@ -0,0 +1,216 @@ +/* + * 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include + +#include +#include + +int main(int argc, char **argv) +{ + int xc_fd; + int i, j, ret = 0; + int cinfo = 0, pinfo = 0; + int ch; + xc_physinfo_t physinfo = { 0 }; + + while ( (ch = getopt(argc, argv, "cp")) != -1 ) + { + switch ( ch ) + { + case 'c': + cinfo = 1; + break; + case 'p': + pinfo = 1; + break; + default: + fprintf(stderr, "%s [-p] [-c]\n", argv[0]); + return -1; + } + } + + if ( !cinfo && !pinfo ) + { + cinfo = 1; + pinfo = 1; + } + + xc_fd = xc_interface_open(); + if ( xc_fd < 0 ) + { + fprintf(stderr, "failed to get the handler\n"); + return xc_fd; + } + + ret = xc_physinfo(xc_fd, &physinfo); + if ( ret ) + { + fprintf(stderr, "failed to get the processor information\n"); + xc_interface_close(xc_fd); + return ret; + } + + /* print out the C state information */ + if ( cinfo ) + { + int max_cx_num = 0; + struct xc_cx_stat cxstatinfo, *cxstat = &cxstatinfo; + + for ( i = 0; i < physinfo.nr_cpus; i++ ) + { + ret = xc_pm_get_max_cx(xc_fd, i, &max_cx_num); + if ( ret ) + { + if ( errno == ENODEV ) + { + fprintf(stderr, "Xen cpuidle is not enabled!\n"); + break; + } + else + { + fprintf(stderr, "[CPU%d] failed to get max C-state\n", i); + continue; + } + } + + cxstat->triggers = malloc(max_cx_num * sizeof(uint64_t)); + if ( !cxstat->triggers ) + { + fprintf(stderr, "failed to malloc for C-states triggers\n"); + break; + } + cxstat->residencies = malloc(max_cx_num * sizeof(uint64_t)); + if ( !cxstat->residencies ) + { + fprintf(stderr, "failed to malloc for C-states residencies\n"); + free(cxstat->triggers); + break; + } + + ret = xc_pm_get_cxstat(xc_fd, i, cxstat); + if( ret ) + { + fprintf(stderr, "[CPU%d] failed to get C-states statistics " + "information\n", i); + free(cxstat->triggers); + free(cxstat->residencies); + continue; + } + + printf("cpu id : %d\n", i); + printf("total C-states : %d\n", cxstat->nr); + printf("idle time(ms) : %"PRIu64"\n", + cxstat->idle_time/1000000UL); + for ( j = 0; j < cxstat->nr; j++ ) + { + printf("C%d : transition [%020"PRIu64"]\n", + j, cxstat->triggers[j]); + printf(" residency [%020"PRIu64" ms]\n", + cxstat->residencies[j]*1000000UL/3579/1000000UL); + } + + free(cxstat->triggers); + free(cxstat->residencies); + + printf("\n"); + } + } + + /* print out P state information */ + if ( pinfo ) + { + int max_px_num = 0; + struct xc_px_stat pxstatinfo, *pxstat = &pxstatinfo; + + for ( i = 0; i < physinfo.nr_cpus; i++ ) + { + ret = xc_pm_get_max_px(xc_fd, i, &max_px_num); + if ( ret ) + { + if ( errno == ENODEV ) + { + printf("Xen cpufreq is not enabled!\n"); + break; + } + else + { + fprintf(stderr, "[CPU%d] failed to get max P-state\n", i); + continue; + } + } + + pxstat->trans_pt = malloc(max_px_num * max_px_num * + sizeof(uint64_t)); + if ( !pxstat->trans_pt ) + { + fprintf(stderr, "failed to malloc for P-states " + "transition table\n"); + break; + } + pxstat->pt = malloc(max_px_num * sizeof(struct xc_px_val)); + if ( !pxstat->pt ) + { + fprintf(stderr, "failed to malloc for P-states table\n"); + free(pxstat->pt); + break; + } + + ret = xc_pm_get_pxstat(xc_fd, i, pxstat); + if( ret ) + { + fprintf(stderr, "[CPU%d] failed to get P-states " + "statistics information\n", i); + free(pxstat->trans_pt); + free(pxstat->pt); + continue; + } + + printf("cpu id : %d\n", i); + 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 ( j = 0; j < pxstat->total; j++ ) + { + if ( pxstat->cur == j ) + printf("*P%d", j); + else + printf("P%d ", j); + printf(" : freq [%04"PRIu64" MHz]\n", + pxstat->pt[j].freq); + printf(" transition [%020"PRIu64"]\n", + pxstat->pt[j].count); + printf(" residency [%020"PRIu64" ms]\n", + pxstat->pt[j].residency/1000000UL); + } + + free(pxstat->trans_pt); + free(pxstat->pt); + + printf("\n"); + } + } + + xc_interface_close(xc_fd); + return ret; +} + diff --git a/tools/misc/xensymoops b/tools/misc/xensymoops new file mode 100755 index 0000000..835d187 --- /dev/null +++ b/tools/misc/xensymoops @@ -0,0 +1,118 @@ +#!/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 ) diff --git a/tools/misc/xm b/tools/misc/xm new file mode 100755 index 0000000..80972cc --- /dev/null +++ b/tools/misc/xm @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# -*- mode: python; -*- +import sys + +# add fallback path for non-native python path installs if needed +sys.path.append('/usr/lib/python') +sys.path.append('/usr/lib64/python') +from xen.xm import main + +main.main(sys.argv) diff --git a/tools/misc/xsview b/tools/misc/xsview new file mode 100644 index 0000000..c672655 --- /dev/null +++ b/tools/misc/xsview @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import sys + +sys.path.append('/usr/lib/python') +sys.path.append('/usr/lib64/python') +from xen.xsview import main + +main.main(sys.argv) + + diff --git a/tools/pygrub/Makefile b/tools/pygrub/Makefile new file mode 100644 index 0000000..3629d89 --- /dev/null +++ b/tools/pygrub/Makefile @@ -0,0 +1,25 @@ + +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +.PHONY: all +all: build +.PHONY: build +build: + CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py build + +.PHONY: install +ifndef XEN_PYTHON_NATIVE_INSTALL +install: LIBPATH=$(shell PYTHONPATH=../python/xen/util python -c "import auxbin; print auxbin.libpath()") +install: all + CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --home="$(DESTDIR)/usr" --prefix="" --install-lib="$(DESTDIR)$(LIBPATH)/python" + $(INSTALL_DIR) $(DESTDIR)/var/run/xend/boot +else +install: all + CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)" + $(INSTALL_DIR) $(DESTDIR)/var/run/xend/boot +endif + +.PHONY: clean +clean: + rm -rf build tmp *.pyc *.pyo *.o *.a *~ a.out diff --git a/tools/pygrub/README b/tools/pygrub/README new file mode 100644 index 0000000..0dd9c62 --- /dev/null +++ b/tools/pygrub/README @@ -0,0 +1,15 @@ +pygrub 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 + diff --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py new file mode 100644 index 0000000..52dcf57 --- /dev/null +++ b/tools/pygrub/setup.py @@ -0,0 +1,29 @@ +from 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 ] + ) diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py new file mode 100644 index 0000000..2192be6 --- /dev/null +++ b/tools/pygrub/src/GrubConf.py @@ -0,0 +1,257 @@ +# +# GrubConf.py - Simple grub.conf parsing +# +# 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, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, sys +import logging + +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(")", "") + 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(")", "") + self._part = int(val) + part = property(get_part, set_part) + +class GrubImage(object): + def __init__(self, lines): + self.reset(lines) + + 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): + self._root = self._initrd = self._kernel = self._args = None + self.title = "" + self.lines = [] + map(self.set_from_line, 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) + + 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) + + # set up command handlers + commands = { "title": "title", + "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 + + 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") + + 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("title"): + if len(img) > 0: + self.add_image(GrubImage(img)) + img = [l] + continue + + if len(img) > 0: + 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 len(img) > 0: + self.add_image(GrubImage(img)) + + 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 = -1 + else: + self._default = int(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 + + +if __name__ == "__main__": + if sys.argv < 2: + raise RuntimeError, "Need a grub.conf to read" + g = GrubConfigFile(sys.argv[1]) + for i in g.images: + print i #, i.title, i.root, i.kernel, i.args, i.initrd diff --git a/tools/pygrub/src/LiloConf.py b/tools/pygrub/src/LiloConf.py new file mode 100644 index 0000000..9d69a94 --- /dev/null +++ b/tools/pygrub/src/LiloConf.py @@ -0,0 +1,165 @@ +# +#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 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", + "prompt": None, + "relocatable": None, + } + +if __name__ == "__main__": + if sys.argv < 2: + raise RuntimeError, "Need a grub.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 diff --git a/tools/pygrub/src/__init__.py b/tools/pygrub/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c new file mode 100644 index 0000000..8ec0dec --- /dev/null +++ b/tools/pygrub/src/fsimage/fsimage.c @@ -0,0 +1,323 @@ +/* + * 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); +} diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub new file mode 100644 index 0000000..ae15af1 --- /dev/null +++ b/tools/pygrub/src/pygrub @@ -0,0 +1,695 @@ +#!/usr/bin/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, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, sys, string, struct, tempfile, re +import copy +import logging +import platform + +import curses, _curses, curses.wrapper, curses.textpad, curses.ascii +import getopt + +sys.path = [ '/usr/lib/python', '/usr/lib64/python' ] + sys.path + +import fsimage +import grub.GrubConf +import grub.LiloConf + +PYGRUB_VER = 0.6 + +def enable_cursor(ison): + if ison: + val = 2 + else: + val = 0 + + try: + curses.curs_set(val) + except _curses.error: + pass + +def is_disk_image(file): + fd = os.open(file, os.O_RDONLY) + buf = os.read(fd, 512) + os.close(fd) + + if len(buf) >= 512 and \ + struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,): + return True + return False + +def get_active_partition(file): + """Find the offset for the start of the first active partition " + "in the disk image file.""" + + fd = os.open(file, os.O_RDONLY) + buf = os.read(fd, 512) + for poff in (446, 462, 478, 494): # partition offsets + # active partition has 0x80 as the first byte + if 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: + 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(10, 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.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 = 1 + img = copy.deepcopy(origimg) + while 1: + draw() + self.entry_win.erase() + self.entry_win.box() + for idx in range(1, len(img.lines)): + # current line should be highlighted + if idx == curline: + self.entry_win.attron(curses.A_REVERSE) + + # trim the line + l = img.lines[idx].ljust(70) + if len(l) > 70: + l = l[:69] + ">" + + self.entry_win.addstr(idx, 2, l) + if idx == curline: + self.entry_win.attroff(curses.A_REVERSE) + 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 + elif c == curses.KEY_DOWN: + curline += 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 < 1: + curline = 1 + 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 = grub.GrubConf.GrubImage(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,) + + if platform.machine() == 'ia64': + self.cf = grub.LiloConf.LiloConfigFile() + # common distributions + file_list = ("/efi/debian/elilo.conf", "/efi/gentoo/elilo.conf", + "/efi/redflag/elilo.conf", "/efi/redhat/elilo.conf", + "/efi/SuSE/elilo.conf",) + # fallbacks + file_list += ("/efi/boot/elilo.conf", "/elilo.conf",) + else: + self.cf = grub.GrubConf.GrubConfigFile() + file_list = ("/boot/grub/menu.lst", "/boot/grub/grub.conf", + "/grub/menu.lst", "/grub/grub.conf") + + if not fs: + # set the config file and parse it + self.cf.filename = fn + self.cf.parse() + return + + for f in file_list: + if fs.file_exists(f): + self.cf.filename = f + break + if self.cf.filename is None: + raise RuntimeError, "couldn't find bootloader config file in the image provided." + f = fs.open_file(self.cf.filename) + buf = f.read() + del f + self.cf.parse(buf) + + def run(self): + timeout = int(self.cf.timeout) + + self.selected_image = self.cf.default + 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() + 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.") + 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'): + self.command_line_mode() + break + elif c == ord('a'): + # 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"): + 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'): + img = self.cf.images[self.selected_image] + self.edit_entry(img) + 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): + global g + global sel + + def run_main(scr, *args): + global sel + global g + sel = g.run() + + g = Grub(file, fs) + if interactive: + curses.wrapper(run_main) + else: + sel = g.cf.default + + # 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) + + img = g.cf.images[sel] + + grubcfg = { "kernel": None, "ramdisk": None, "args": None } + + grubcfg["kernel"] = img.kernel[1] + if img.initrd: + grubcfg["ramdisk"] = img.initrd[1] + if img.args: + grubcfg["args"] = img.args + + return grubcfg + +# 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"): + return cfg + + # darned python + longmode = (sys.maxint != 2147483647L) + if not longmode: + longmode = os.uname()[4] == "x86_64" + if not longmode: + if (os.access("/usr/bin/isainfo", os.R_OK) and + os.popen("/usr/bin/isainfo -b").read() == "64\n"): + longmode = True + + if not cfg["kernel"]: + cfg["kernel"] = "/platform/i86xpv/kernel/unix" + cfg["ramdisk"] = "/platform/i86pc/boot_archive" + if longmode: + cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix" + cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive" + + # 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 + +if __name__ == "__main__": + sel = None + + def usage(): + print >> sys.stderr, "Usage: %s [-q|--quiet] [-i|--interactive] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] " %(sys.argv[0],) + + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], 'qih::', + ["quiet", "interactive", "help", "output=", + "entry=", "kernel=", "ramdisk=", "args=", + "isconfig"]) + except getopt.GetoptError: + usage() + sys.exit(1) + + if len(args) < 1: + usage() + sys.exit(1) + file = args[0] + + output = None + entry = None + interactive = True + isconfig = False + + # what was passed in + incfg = { "kernel": None, "ramdisk": None, "args": None } + # 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 ("-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 ("--entry",): + entry = a + # specifying the entry to boot implies non-interactive + interactive = False + elif o in ("--isconfig",): + isconfig = True + + 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) + print " kernel: %s" % chosencfg["kernel"] + if img.initrd: + 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: + fs = fsimage.open(file, get_fs_offset(file), bootfsgroup[0]) + else: + fs = fsimage.open(file, get_fs_offset(file)) + + chosencfg = sniff_solaris(fs, incfg) + + if not chosencfg["kernel"]: + chosencfg = run_grub(file, entry, fs) + + data = fs.open_file(chosencfg["kernel"]).read() + (tfd, bootcfg["kernel"]) = tempfile.mkstemp(prefix="boot_kernel.", + dir="/var/run/xend/boot") + os.write(tfd, data) + os.close(tfd) + + if chosencfg["ramdisk"]: + data = fs.open_file(chosencfg["ramdisk"],).read() + (tfd, bootcfg["ramdisk"]) = tempfile.mkstemp(prefix="boot_ramdisk.", + dir="/var/run/xend/boot") + os.write(tfd, data) + os.close(tfd) + else: + initrd = None + + sxp = "linux (kernel %s)" % bootcfg["kernel"] + if bootcfg["ramdisk"]: + sxp += "(ramdisk %s)" % bootcfg["ramdisk"] + if chosencfg["args"]: + zfsinfo = fsimage.getbootstring(fs) + if zfsinfo is None: + sxp += "(args \"%s\")" % chosencfg["args"] + else: + e = re.compile("zfs-bootfs=[\w\-\.\:@/]+" ) + (chosencfg["args"],count) = e.subn(zfsinfo, chosencfg["args"]) + if count == 0: + chosencfg["args"] += " -B %s" % zfsinfo + sxp += "(args \"%s\")" % (chosencfg["args"]) + + sys.stdout.flush() + os.write(fd, sxp) + diff --git a/tools/python/Makefile b/tools/python/Makefile new file mode 100644 index 0000000..c2e5c9c --- /dev/null +++ b/tools/python/Makefile @@ -0,0 +1,87 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +.PHONY: all +all: build + +# For each new supported translation, add its name here, eg 'fr_FR' +# to cause the .po file to be built & installed, eg +LINGUAS := +POPACKAGE := xen-xm +PODIR := xen/xm/messages +POTFILE := $(PODIR)/xen-xm.pot +I18NSRCFILES = $(shell find xen/xm/ -name '*.py') +CATALOGS = $(patsubst %,xen/xm/messages/%.mo,$(LINGUAS)) +NLSDIR = /usr/share/locale + +.PHONY: build buildpy +buildpy: + CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py build + +build: buildpy refresh-pot refresh-po $(CATALOGS) + +# NB we take care to only update the .pot file it strings have +# actually changed. This is complicated by the embedded date +# string, hence the sed black magic. This avoids the expensive +# re-generation of .po files on every single build +refresh-pot: $(I18NSRCFILES) + xgettext --default-domain=$(POPACAKGE) \ + --keyword=N_ \ + --keyword=_ \ + -o $(POTFILE)-tmp \ + $(I18NSRCFILES) + sed -f remove-potcdate.sed < $(POTFILE) > $(POTFILE)-1 + sed -f remove-potcdate.sed < $(POTFILE)-tmp > $(POTFILE)-2 + set -e; if cmp -s $(POTFILE)-1 $(POTFILE)-2; then \ + rm -f $(POTFILE)-tmp $(POTFILE)-1 $(POTFILE)-2; \ + else \ + mv $(POTFILE)-tmp $(POTFILE); \ + rm -f $(POTFILE)-1 $(POTFILE)-2; \ + fi + +refresh-po: $(POTFILE) + set -e; for l in $(LINGUAS); do \ + if $(MSGMERGE) $(PODIR)/$$l.po $(POTFILE) > $(PODIR)/$$l-tmp ; then \ + mv -f $(PODIR)/$$l-tmp $(PODIR)/$$l.po ; \ + echo "$(MSGMERGE) of $$l.po succeeded" ; \ + else \ + echo "$(MSGMERGE) of $$l.po failed" ; \ + rm -f $(PODIR)/$$l-tmp ; \ + fi \ + done + +%.mo: %.po + $(MSGFMT) -c -o $@ $< + +.PHONY: install +ifndef XEN_PYTHON_NATIVE_INSTALL +install: LIBPATH=$(shell PYTHONPATH=xen/util python -c "import auxbin; print auxbin.libpath()") +install: install-messages install-dtd + CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --home="$(DESTDIR)/usr" --prefix="" --force --install-lib="$(DESTDIR)$(LIBPATH)/python" +else +install: install-messages install-dtd + CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)" --force +endif + +install-dtd: all + $(INSTALL_DIR) $(DESTDIR)/usr/share/xen + $(INSTALL_DATA) xen/xm/create.dtd $(DESTDIR)/usr/share/xen + +install-messages: all + set -e; if which $(MSGFMT) >/dev/null ; then \ + mkdir -p $(DESTDIR)$(NLSDIR); \ + for l in $(LINGUAS); do \ + $(INSTALL_DIR) $(DESTDIR)$(NLSDIR)/$$l; \ + $(INSTALL_DIR) $(DESTDIR)$(NLSDIR)/$$l/LC_MESSAGES; \ + $(INSTALL_DATA) $(PODIR)/$$l.mo \ + $(DESTDIR)$(NLSDIR)/$$l/LC_MESSAGES/$(POPACKAGE).mo; \ + done ; \ + fi + +.PHONY: test +test: + export LD_LIBRARY_PATH=$$(readlink -f ../libxc):$$(readlink -f ../xenstore); python test.py -b -u + +.PHONY: clean +clean: + rm -rf build *.pyc *.pyo *.o *.a *~ $(CATALOGS) xen/util/auxbin.pyc diff --git a/tools/python/README b/tools/python/README new file mode 100644 index 0000000..8fffef3 --- /dev/null +++ b/tools/python/README @@ -0,0 +1,3 @@ +The 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. diff --git a/tools/python/README.XendConfig b/tools/python/README.XendConfig new file mode 100644 index 0000000..9776f33 --- /dev/null +++ b/tools/python/README.XendConfig @@ -0,0 +1,161 @@ +XendConfig parameters +===================== + +Things that are empty means there is no direct mapping. + +In order to make the XendConfig fully backwards compatible, it needs a +representation of all the below parameters. Where both columns have +values, it means we can have a direct translation. + +Where the Legacy Config value does not exist, it means we have to make +up the value on whether we supported it or not. + +Where the Legacy config value is prefixed with an '!', it means it is +not a direct mapping and needs a translation function. + +Where the Xen API config value does not exist, it means we have to add +a parameter outside of the Xen API Configuration to support it. + + +Xen API Config Legacy Config +-------------- ------------- +uuid uuid +power_state !state (and xc_getinfo) +name_label name +name_description +user_version +is_a_template +resident_on +memory_static_min memory +memory_static_max maxmem +memory_actual +memory_dynamic_min +memory_dynamic_max +vcpus_policy !set_credit/set_sedf +vcpus_params !set_credit/set_sedf +vcpus_number vcpus +vcpus_utilisation +vcpus_features_required +vcpus_features_can_use +vcpus_features_force_on +vcpus_features_force_off +actions_after_shutdown on_poweroff +actions_after_reboot on_reboot +actions_after_suspend +actions_after_crash on_crash + +vifs !(devices.vifs) + - uuid dev.uuid + - name + - type vif.type + - device + - network + - vm + - MAC vif.mac + - MTU + - io_read_kbs !vif.rate + - io_write_kbs !vif.rate + vif.bridge + vif.script + vif.ip + vif.vifname (backend name) + +vbds !(devices.vbds) + - uuid + - vm + - vdi + - device vbd.uname + - mode !vbd.mode + - driver vbd.driver + - io_read_kbs + - io_write_kbs + +tpm_instance tpm.instance +tpm_backend tpm.backend +bios_boot image.boot? +platform_std_VGA image.stdvga +platform_serial image.serial +platform_localtime !localtime (bool) +platform_clock_offset +platform_enable_audio !image.soundhw (bool) +builder (sxp root name) +boot_method +kernel_kernel kernel.kernel +kernel_initrd kernel.ramdisk +kernel_args !kernel.args +grub_cmdline bootloader_args +PCI_bus +tools_version + +otherConfig + - image image (see image.*) + - shadow_memory shadow_memory + - security security + - vcpu_avail vcpu_avail + - features features + - on_xend_stop on_xend_stop + - on_xend_start on_xend_start + - start_time start_time + - cpus cpus (?) + max_vcpu_id + + >> only from xc + - online_vcpus xc.online_vcpus + - status xc.status + - cpu_time xc.cpu_time + - shutdown_reason xc.shutdown_reason + - up_time xc.uptime + - crashed xc.crashed + - dying xc.dying + - shutdown xc.shutdown + + image.type (linux or hvm) + image.root + image.ip + image.nographic + image.vnc + image.sdl + image.monitor + image.vncdisplay + image.vncunused + image.hvm.device_model + image.hvm.display + image.hvm.xauthority + image.hvm.vncconsole + image.hvm.pae + image.hvm.acpi (also in image.devices) + image.hvm.apic + image.hvm.devices.boot + image.hvm.devices.fda + image.hvm.devices.fdb + image.hvm.devices.soundhw + image.hvm.devices.isa + image.hvm.devices.vcpus? + image.hvm.devices.acpi + image.hvm.devices.usb + image.hvm.devices.usbdevice + + + dev.backend + dev.dom + dev.id + + pci.domain + pci.bus + pci.slot + pci.func + + pciquirk.pci_ids + pciquirk.pci_config_space_fields + pciquirk.unconstrained_dev_ids + + irq.irq + +- vcpu (probably not needed, only in XM and generated dynamically) + vcpu.number + vcpu.online + vcpu.blocked + vcpu.running + vcpu.cpu_time + vcpu.cpu + vcpu.cpumap diff --git a/tools/python/README.sxpcfg b/tools/python/README.sxpcfg new file mode 100644 index 0000000..7a17fd1 --- /dev/null +++ b/tools/python/README.sxpcfg @@ -0,0 +1,118 @@ +Map of all supported SXP configuration options +---------------------------------------------- + +uuid +vcpus +maxmem +memory +name +on_poweroff +on_reboot +on_crash +bootloader +kernel_kernel +kernel_initrd +kernel_args +localtime + +shadow_memory +security +ssidref (deprecated) +vcpu_avail +cpu_weight (deprecated) +bootloader_args +features +on_xend_stop +on_xend_start +start_time +cpu (deprecated) +cpus + +(xc getinfo) +domid +online_vcpus +status +cpu_time +shutdown_reason +(xm list --long) +up_time + +image + - kernel + - ramdisk + - args + - ip + - root + (configVNC) + - nographic + - vnc + - sdl + - vncdisplay + - vncunused + (HVM) + - device_model + - display + - xauthority + - vncconsole + - pae + - acpi + - apic + (parseDeviceModel) + - boot + - fda + - fdb + - soundhw + - localtime + - monitor + - serial + - stdvga + - isa + - vcpus + - acpi + - usb + - usbdevice + +(all devices) + - backend + - dom + - id + - uuid + +vbd + - uname + - dev (ioemu:, .. etc) + - mode (r, w. w!) + +vif + - type + - mac + - bridge + - model + - rate + - vifname + - script + - ip + +pci + - domain + - bus + - slot + - func + (Xen 2.0) + - dev + +io + - from + - to + +tpm + - pref_instance + - instance + +pciquirk + - pci_ids + - pci_config_space_fields + - unconstrained_dev_ids + +irq + - irq diff --git a/tools/python/ZPL-2.0 b/tools/python/ZPL-2.0 new file mode 100644 index 0000000..5582f08 --- /dev/null +++ b/tools/python/ZPL-2.0 @@ -0,0 +1,59 @@ +Zope 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. \ No newline at end of file diff --git a/tools/python/get-path b/tools/python/get-path new file mode 100755 index 0000000..ae67279 --- /dev/null +++ b/tools/python/get-path @@ -0,0 +1,22 @@ +#! /usr/bin/env bash +set -e + +check () { + set +e + p=`type -p python$v` + r=$? + set -e + if [ $r = 0 ]; then + echo >&2 "${0##*/}: will use #!$p for python programs" + printf "%s\n" "$p" + exit 0 + fi +} + +v="$(python -V 2>&1)" +v="${v#* }" +check +v="${v%.*}" +check +echo >&2 'python version not determined, will use env to find python at runtime' +printf "/usr/bin/env python\n" diff --git a/tools/python/install-wrap b/tools/python/install-wrap new file mode 100755 index 0000000..29db25d --- /dev/null +++ b/tools/python/install-wrap @@ -0,0 +1,44 @@ +#! /usr/bin/env bash +# 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 +# .../get-path alongside this script + +set -e +if [ $# -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=("${install[@]}" "$1"); shift; break ;; + -*) install=("${install[@]}" "$1"); shift ;; + *) break ;; + esac +done +while [ $# -gt 1 ]; do + srcs=("${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 [ "x$org" = x ]; then + "${install[@]}" "$srcf" "$destf" + continue + fi + tmpf="$destf.tmp" + "${install[@]}" "$srcf" "$tmpf" + printf >"$tmpf" "#!%s\n" "$pythonpath" + sed -e 1d "$srcf" >>"$tmpf" + mv -f "$tmpf" "$destf" +done diff --git a/tools/python/logging/logging-0.4.9.2/PKG-INFO b/tools/python/logging/logging-0.4.9.2/PKG-INFO new file mode 100644 index 0000000..6f7ee38 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/PKG-INFO @@ -0,0 +1,25 @@ +Metadata-Version: 1.0 +Name: logging +Version: 0.4.9.2 +Summary: A logging module for Python +Home-page: http://www.red-dove.com/python_logging.html +Author: Vinay Sajip +Author-email: vinay_sajip@red-dove.com +License: Copyright (C) 2001-2004 by Vinay Sajip. All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Vinay Sajip +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +Description: This module is intended to provide a standard error logging mechanism in Python as per PEP 282. +Platform: UNKNOWN diff --git a/tools/python/logging/logging-0.4.9.2/README.txt b/tools/python/logging/logging-0.4.9.2/README.txt new file mode 100644 index 0000000..3d38889 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/README.txt @@ -0,0 +1,311 @@ +This module is intended to provide error logging functionality for Python +programs. It aims to be aligned with Python Enhancement Proposal (PEP) 282. + +For more information on the package itself, see python_logging.html. In it, +there is a description of each file in the distribution. +You can also use pydoc to browse the interfaces. + +Change History +-------------- + +Version Date Description +============================================================================= +0.4.9.2 28 Feb 2004 Traceback text is now cached. + Tracebacks can be propagated across sockets as text. + Added makeLogRecord() to allow a LogRecord to be + created from a dictionary. + Closing a handler now removes it from the internal list + used by shutdown(). + Made close() call flush() for handlers where this makes + sense (thanks to Jim Jewett). + The exc_info keyword parameter can be used to pass an + exception tuple as well as a flag indicating that the + current exception should be logged. + A shutdown hook is registered to call shutdown() on + application (Python) exit (thanks to Jim Jewett). + Removed redundant error check in setLoggerClass(). + Added RESET_ERROR to logging.config. + SocketHandler now uses an exponential backoff strategy + (thanks to Robert Olson). + Minor documentation corrections. +----------------------------------------------------------------------------- +0.4.8 22 Apr 2003 Made _listener global in stopListening(). + Made listen() correctly pass the specified port. + Removed some redundant imports in __init__.py. + Added the record being processed as a parameter to + handleError (thanks to Gordon den Otter for the idea). + Handler.handle returns the result of applying the + filter to the record (thanks to Gordon den Otter for + the idea). + Added a seek(0, 2) in RotatingFileHandler before the + tell() call. This is because under Windows, tell() + returns 0 until the first actual write (thanks to + Gordon den Otter for the patch). + Altered findCaller to not use inspect (thanks to + Jeremy Hylton for the patch). + Renamed warn and WARN to warning and WARNING. This may + break existing code, but the standard Python module + will use warning/WARNING rather than warn/WARN. The + fatal and FATAL synonyms for critical and CRITICAL + have also been removed. + Added defaultEncoding and some support for encoding + Unicode messages (thanks to Stéphane Bidoul for the + suggestion). + Added process ID to the list of LogRecord attributes. + Modified Logger.removeHandler so that it does not + close the handler on removal. + Modified SMTPHandler to treat a single "to address" + correctly (thanks to Anthony Baxter). + Modified SMTPHandler to add a date header to the SMTP + message (thanks to David Driver for the suggestion). + Modified HTTPHandler to factor out the mapping of + a LogRecord to a dictionary (thanks to Franz Glasner + for the patch). +----------------------------------------------------------------------------- +0.4.7 15 Nov 2002 Made into a package with three modules: __init__ (the + core code), handlers (all handlers other than + FileHandler and its bases) and config (all the config + stuff). Before doing this: + Updated docstrings to include a short line, then a + blank line, then more descriptive text. + Renamed 'lvl' to 'level' in various functions. + Changed FileHandler to use "a" and "w" instead of "a+" + and "w+". + Moved log file rotation functionality from FileHandler + to a new class RotatingFileHandler. + Improved docstring describing rollover. + Updated makePickle to use 4-byte length and struct + module, likewise logrecv.py. Also updated on-the-fly + config reader to use 4-byte length/struct module. + Altered ConfigParser test to look at 'readline' rather + than 'read'. + Added optional "defaults" argument to fileConfig, to + be passed to ConfigParser. + Renamed ALL to NOTSET to avoid confusion. + Commented out getRootLogger(), as obsolete. + To do regression testing, run log_test.py and compare + the created files stdout.log and stderr.log against + the files stdout.exp and stderr.exp. They should match + except fir a couple of exception messages which give + absolute file paths. + Updated python_logging.html to remove links to + logging_pydoc.html, which has been removed from the + distribution. + Changed default for raiseExceptions to 1. +----------------------------------------------------------------------------- +0.4.6 08 Jul 2002 Added raiseExceptions to allow conditional propagation + of exceptions which occur during handling. + Added converter to Formatter to allow use of any + function to convert time from seconds to a tuple. It + still defaults to time.localtime but now you can also + use time.gmtime. + Added log_test22.py to test the conversion feature. + Changed rootlogger default level to WARN - was DEBUG. + Updated some docstrings. + Moved import of threading to where thread is imported. + If either is unavailable, threading support is off. + Updated minor defects in python_logging.html. + Check to see if ConfigParser has readfp method; if it + does and an object with a 'read' method is passed in, + assumes a file-like object and uses readfp to read it + in. +----------------------------------------------------------------------------- +0.4.5 04 Jun 2002 Fixed bug which caused problem if no args to message + (suggested by Hye-Shik Chang). + Fixed bug in _fixupParents (thanks to Nicholas Veeser) + and added log_test19.py as a test case for this bug. + Added getMessage to LogRecord (code was moved here from + Formatter.format) + Applied str() to record.msg to allow arbitrary classes + to determine the formatting (as msg can now be a class + instance). + Table of Contents added to python_logging.html, the + section on Loggers updated, and the logconf.ini file + section annotated. + Added log_test20.py which demonstrates how to use + class instances to provide alternatives to numeric + severities as mechanisms for control of logging. + Added log_test21.py which builds on log_test20.py to + show how you can use a regular expression-based Filter + for flexible matching similar to e.g. Protomatter + Syslog, where you can filter on e.g. "a.*" or "*.b" or + "a.*.c". + _levelNames changed to contain reverse mappings as well + as forward mappings (leveltext->level as well as level + -> leveltext). The reverse mappings are used by + fileConfig(). + fileConfig() now more forgiving of missing options in + .ini file - sensible defaults now used when some + options are absent. Also, eval() is used less when + interpreting .ini file contents - int() and dict lookup + are used in more places. +----------------------------------------------------------------------------- +0.4.4 02 May 2002 getEffectiveLevel() returns ALL instead of None when + nothing found. Modified references to level=0 to + level=ALL in a couple of places. + SocketHandler now inherits from Handler (it used to + inherit from StreamHandler, for no good reason). + getLock() renamed to createLock(). + Docstring tidy-ups, and some tidying up of + DatagramHandler. + Factored out unpickling in logrecv.py. + Added log_test18.py to illustrate MatchFilter, which is + a general matching filter. + Improved FileHandler.doRollover() so that the base + file name is always the most recent, then .1, then .2 + etc. up to the maximum backup count. Renamed formal + args and attributes used in rollover. + Changed LogRecord attributes lvl -> levelno, level -> + levelname (less ambiguity) + Formatter.format searches for "%(asctime)" rather than + "(asctime)" + Renamed _start_time to _startTime + Formatter.formatTime now returns the time + Altered logrecv.py to support stopping servers + programmatically + Added log_test.py as overall test harness + basicConfig() can now be safely called more than once + Modified test scripts to make it easier to call them + from log_test.py + Moved SOAPHandler from core to log_test13.py. It's not + general enough to be in the core; most production use + will have differing RPC signatures. +----------------------------------------------------------------------------- +0.4.3 14 Apr 2002 Bug fix one-off error message to go to sys.stderr + rather than sys.stdout. + logrecv.py fix TCP for busy network. + Thread safety - added locking to Handler and for shared + data in module, and log_test16.py to test it. + Added socket listener to allow on-the-fly configuration + and added log_test17.py to test it. +----------------------------------------------------------------------------- +0.4.2 11 Apr 2002 Bug fix fileConfig() - setup of MemoryHandler target + and errors when loggers have no handlers set or + handlers have no formatters set + logconf.py - seems to hang if window closed when combo + dropdown is showing - added code to close popup on exit + Some tweaks to _srcfile computation (normpath added) + findCaller() optimized, now a lot faster! + Logger.removeHandler now closes the handler before + removing it + fileConfig() removes existing handlers before adding + the new set, to avoid memory leakage when repeated + calls are made + Fixed logrecv.py bug which hogged CPU time when TCP + connection was closed from the client + Added log_test14.py to demonstrate/test a DBHandler + which writes logging records into an RDBMS using the + Python Database API 2.0 (to run, you need something + which supports this already installed - I tested with + mxODBC) + Made getLogger name argument optional - returns root + logger if omitted + Altered Filter to take a string initializer, filtering + a sub-hierarchy rooted at a particular point (idea from + Denis S. Otkidach). + Added log_test15.py to test Filter initializer + Minor docstring changes +----------------------------------------------------------------------------- +0.4.1 03 Apr 2002 Bug fix SMTPHandler - extra \r\n needed (Oleg Orlov) + Added BufferingHandler, BufferingFormatter + Renamed getChainedPriority to getEffectiveLevel + Removed Logger.getRoot as it is redundant + Added log_test9.py to test Buffering classes and + to show an XMLFormatter example. + Added setLoggerClass. + Added log_test10.py to test setLoggerClass, using an + example Logger-derived class which outputs exception + info even for DEBUG level logging calls + Added log_test11.py to test a buffering implementation + of SMTPHandler + Changed logging call implementation to allow keyword + arguments (Kevin Butler and others) + Changed default SysLogHandler implementation. + Renamed "additive" to "propagate" as it better + describes the attribute. + Added HTTPHandler. + Modified logrecv.py to remove "both" option and to add + "HTTP" and "SOAP" options (SOAP option needs you to + have PyXML-0.6.6 and ZSI installed - for logrecv.py + only, and not for the core logging module itself). + Added log_test12.py to test HTTPHandler. + Added log_test13.py to test SOAPHandler. + Formatted to Python source guidelines (spaces, indent + of 4, within 80 columns). + More method renamings (result of feedback) - _handle() + renamed to emit(), _logRecord() renamed to handle(). + Renamed FATAL to CRITICAL (David Goodger), but left + fatal() and FATAL in (until PEP is changed) + Changed configuration file format to ConfigParser + format. + Factored filter application functionality out to a new + Filterer class. The isLoggable() method is renamed to + filter() in both Filter and Filterer classes. + Altered SMTPHandler __init__ to accept (host, port) + for the mail internet address. + Added GUI configurator which uses Tkinter and the new + configuration file format. (See logconf.py and an + example configuration file in logconf.ini) + Altered log_test3.py to test with the new file format. +----------------------------------------------------------------------------- +0.4 21 Mar 2002 Incorporated comments/patches from Ollie Rutherfurd: + -Added level filtering for handlers. + -Return root logger if no name specified in getLogger. + Incorporated comments from Greg Ward: + -Added distutils setup.py script. + Added formatter initialization in Handler.__init__. + Tidied up docstrings. + Added removeHandler to Logger. + Added removeFilter to Logger and Handler. + logrecv.py modified to keep connection alive until + client closes it. + SocketHandler modified to not reset connection after + each logging event. + Added shutdown function which closes open sockets + Renamed DEFAULT_LOGGING_PORT->DEFAULT_TCP_LOGGING_PORT + Added DEFAULT_UDP_LOGGING_PORT + Added log_test4.py (example of arbitrary levels) + Added addLevelName, changed behaviour of getLevelName + Fixed bugs in DatagramHandler + Added SMTPHandler implementation + Added log_test5.py to test SMTPHandler + Added SysLogHandler (contribution from Nicolas Untz + based on Sam Rushing's syslog.py) + Modified log_test1.py to add a SysLogHandler + Added rollover functionality to FileHandler + Added NTEventLogHandler (based on Win32 extensions) + Added MemoryHandler implementation + Added log_test7.py to test MemoryHandler + Added log_test8.py to test FileHandler rollover + Added logException method to Logger + Added formatException method to Formatter + Added log_test6.py to test NTEventHandler and + logException + Numerous internal method renamings (sorry - but better + to do this now, rather than when we enter beta status). +----------------------------------------------------------------------------- +0.3 14 Mar 2002 First public release, for early feedback +----------------------------------------------------------------------------- +0.2 Consolidated into single file (for internal use only) +----------------------------------------------------------------------------- +0.1 Initial implementation (for internal use only) +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +COPYRIGHT +----------------------------------------------------------------------------- +Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Vinay Sajip +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/tools/python/logging/logging-0.4.9.2/default.css b/tools/python/logging/logging-0.4.9.2/default.css new file mode 100644 index 0000000..0c7ce5b --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/default.css @@ -0,0 +1,32 @@ +BODY, P, UL, OL {font-family: Verdana, Arial, Helvetica, Geneva, sans-serif ; background: white; color: black; font-size: 10pt; } +CODE {font-family: "Courier New", Courier, Monaco, monospace ; } +PRE {font-family: "Courier New", Courier, Monaco, monospace ; } +.program { color: navy; font-family: "Courier New", Courier, Monaco, monospace } +.output { color: maroon } +HR {color: gray ; } +.navbar { font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; background: #FFFF66 ; border-style: none none solid none; border-width: thin; padding: 2px;} +.navarea { background: #FFFF66 ; } +TR { font-size: 10pt } +.red { color: #FF0000 } +.maroon { color: #800000 } +.comment { color: green } +.optional { color: silver } +.strong { font-weight: bold } +TR.tblheading { background: silver ; } +.notrecommended {color: #666666 ; } +.deemphasized {color: #666666 ; } +TABLE.densetable {font-size: 80% ; } +TH { text-align: left; background: silver } +TR.summaryrow { font-size: 90%; background: #00FFFF ; } +CAPTION {font-size: 100% ; } +.embeddedfloat {float: right; margin-left: 3em} +.bigtd { font-size: 18pt } +TD, .normaltd { vertical-align: top; background: #EEEEEE; font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; font-size: 10pt } +H1, H2, H3, H4, H5, H6 {font-family: Verdana, Arial, Helvetica, Geneva, sans-serif ; } +H4 {margin-bottom: 0.5em } +H5, H6 {margin-bottom: 0.1em } +A:link {color: blue ; } +A:visited {color: purple ; } +A:active {color: red ; } +A { font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; font-size: 10pt } +.warning { color: red } \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/liblogging.tex b/tools/python/logging/logging-0.4.9.2/liblogging.tex new file mode 100644 index 0000000..bc39ab2 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/liblogging.tex @@ -0,0 +1,1281 @@ +\section{\module{logging} --- + Logging facility for Python} + +\declaremodule{standard}{logging} + +% These apply to all modules, and may be given more than once: + +\moduleauthor{Vinay Sajip}{vinay_sajip@red-dove.com} +\sectionauthor{Vinay Sajip}{vinay_sajip@red-dove.com} + +\modulesynopsis{Logging module for Python based on \pep{282}.} + +\indexii{Errors}{logging} + +\versionadded{2.3} +This module defines functions and classes which implement a flexible +error logging system for applications. + +Logging is performed by calling methods on instances of the +\class{Logger} class (hereafter called \dfn{loggers}). Each instance has a +name, and they are conceptually arranged in a name space hierarchy +using dots (periods) as separators. For example, a logger named +"scan" is the parent of loggers "scan.text", "scan.html" and "scan.pdf". +Logger names can be anything you want, and indicate the area of an +application in which a logged message originates. + +Logged messages also have levels of importance associated with them. +The default levels provided are \constant{DEBUG}, \constant{INFO}, +\constant{WARNING}, \constant{ERROR} and \constant{CRITICAL}. As a +convenience, you indicate the importance of a logged message by calling +an appropriate method of \class{Logger}. The methods are +\method{debug()}, \method{info()}, \method{warning()}, \method{error()} and +\method{critical()}, which mirror the default levels. You are not +constrained to use these levels: you can specify your own and use a +more general \class{Logger} method, \method{log()}, which takes an +explicit level argument. + +Levels can also be associated with loggers, being set either by the +developer or through loading a saved logging configuration. When a +logging method is called on a logger, the logger compares its own +level with the level associated with the method call. If the logger's +level is higher than the method call's, no logging message is actually +generated. This is the basic mechanism controlling the verbosity of +logging output. + +Logging messages are encoded as instances of the \class{LogRecord} class. +When a logger decides to actually log an event, an \class{LogRecord} +instance is created from the logging message. + +Logging messages are subjected to a dispatch mechanism through the +use of \dfn{handlers}, which are instances of subclasses of the +\class{Handler} class. Handlers are responsible for ensuring that a logged +message (in the form of a \class{LogRecord}) ends up in a particular +location (or set of locations) which is useful for the target audience for +that message (such as end users, support desk staff, system administrators, +developers). Handlers are passed \class{LogRecord} instances intended for +particular destinations. Each logger can have zero, one or more handlers +associated with it (via the \method{addHandler} method of \class{Logger}). +In addition to any handlers directly associated with a logger, +\emph{all handlers associated with all ancestors of the logger} are +called to dispatch the message. + +Just as for loggers, handlers can have levels associated with them. +A handler's level acts as a filter in the same way as a logger's level does. +If a handler decides to actually dispatch an event, the \method{emit()} method +is used to send the message to its destination. Most user-defined subclasses +of \class{Handler} will need to override this \method{emit()}. + +In addition to the base \class{Handler} class, many useful subclasses +are provided: + +\begin{enumerate} + +\item \class{StreamHandler} instances send error messages to +streams (file-like objects). + +\item \class{FileHandler} instances send error messages to disk +files. + +\item \class{RotatingFileHandler} instances send error messages to disk +files, with support for maximum log file sizes and log file rotation. + +\item \class{SocketHandler} instances send error messages to +TCP/IP sockets. + +\item \class{DatagramHandler} instances send error messages to UDP +sockets. + +\item \class{SMTPHandler} instances send error messages to a +designated email address. + +\item \class{SysLogHandler} instances send error messages to a +\UNIX{} syslog daemon, possibly on a remote machine. + +\item \class{NTEventLogHandler} instances send error messages to a +Windows NT/2000/XP event log. + +\item \class{MemoryHandler} instances send error messages to a +buffer in memory, which is flushed whenever specific criteria are +met. + +\item \class{HTTPHandler} instances send error messages to an +HTTP server using either \samp{GET} or \samp{POST} semantics. + +\end{enumerate} + +The \class{StreamHandler} and \class{FileHandler} classes are defined +in the core logging package. The other handlers are defined in a sub- +module, \module{logging.handlers}. (There is also another sub-module, +\module{logging.config}, for configuration functionality.) + +Logged messages are formatted for presentation through instances of the +\class{Formatter} class. They are initialized with a format string +suitable for use with the \% operator and a dictionary. + +For formatting multiple messages in a batch, instances of +\class{BufferingFormatter} can be used. In addition to the format string +(which is applied to each message in the batch), there is provision for +header and trailer format strings. + +When filtering based on logger level and/or handler level is not enough, +instances of \class{Filter} can be added to both \class{Logger} and +\class{Handler} instances (through their \method{addFilter()} method). +Before deciding to process a message further, both loggers and handlers +consult all their filters for permission. If any filter returns a false +value, the message is not processed further. + +The basic \class{Filter} functionality allows filtering by specific logger +name. If this feature is used, messages sent to the named logger and its +children are allowed through the filter, and all others dropped. + +In addition to the classes described above, there are a number of module- +level functions. + +\begin{funcdesc}{getLogger}{\optional{name}} +Return a logger with the specified name or, if no name is specified, return +a logger which is the root logger of the hierarchy. + +All calls to this function with a given name return the same logger instance. +This means that logger instances never need to be passed between different +parts of an application. +\end{funcdesc} + +\begin{funcdesc}{debug}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{DEBUG} on the root logger. +The \var{msg} is the message format string, and the \var{args} are the +arguments which are merged into \var{msg}. The only keyword argument in +\var{kwargs} which is inspected is \var{exc_info} which, if it does not +evaluate as false, causes exception information (via a call to +\function{sys.exc_info()}) to be added to the logging message. +\end{funcdesc} + +\begin{funcdesc}{info}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{INFO} on the root logger. +The arguments are interpreted as for \function{debug()}. +\end{funcdesc} + +\begin{funcdesc}{warning}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{WARNING} on the root logger. +The arguments are interpreted as for \function{debug()}. +\end{funcdesc} + +\begin{funcdesc}{error}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{ERROR} on the root logger. +The arguments are interpreted as for \function{debug()}. +\end{funcdesc} + +\begin{funcdesc}{critical}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{CRITICAL} on the root logger. +The arguments are interpreted as for \function{debug()}. +\end{funcdesc} + +\begin{funcdesc}{exception}{msg\optional{, *args}} +Logs a message with level \constant{ERROR} on the root logger. +The arguments are interpreted as for \function{debug()}. Exception info +is added to the logging message. This function should only be called +from an exception handler. +\end{funcdesc} + +\begin{funcdesc}{disable}{lvl} +Provides an overriding level \var{lvl} for all loggers which takes +precedence over the logger's own level. When the need arises to +temporarily throttle logging output down across the whole application, +this function can be useful. +\end{funcdesc} + +\begin{funcdesc}{addLevelName}{lvl, levelName} +Associates level \var{lvl} with text \var{levelName} in an internal +dictionary, which is used to map numeric levels to a textual +representation, for example when a \class{Formatter} formats a message. +This function can also be used to define your own levels. The only +constraints are that all levels used must be registered using this +function, levels should be positive integers and they should increase +in increasing order of severity. +\end{funcdesc} + +\begin{funcdesc}{getLevelName}{lvl} +Returns the textual representation of logging level \var{lvl}. If the +level is one of the predefined levels \constant{CRITICAL}, +\constant{ERROR}, \constant{WARNING}, \constant{INFO} or \constant{DEBUG} +then you get the corresponding string. If you have associated levels +with names using \function{addLevelName()} then the name you have associated +with \var{lvl} is returned. Otherwise, the string "Level \%s" \% lvl is +returned. +\end{funcdesc} + +\begin{funcdesc}{makeLogRecord}{attrdict} +Creates and returns a new \class{LogRecord} instance whose attributes are +defined by \var{attrdict}. This function is useful for taking a pickled +\class{LogRecord} attribute dictionary, sent over a socket, and reconstituting +it as a \class{LogRecord} instance at the receiving end. +\end{funcdesc} + +\begin{funcdesc}{basicConfig}{} +Does basic configuration for the logging system by creating a +\class{StreamHandler} with a default \class{Formatter} and adding it to +the root logger. The functions \function{debug()}, \function{info()}, +\function{warning()}, \function{error()} and \function{critical()} will call +\function{basicConfig()} automatically if no handlers are defined for the +root logger. +\end{funcdesc} + +\begin{funcdesc}{shutdown}{} +Informs the logging system to perform an orderly shutdown by flushing and +closing all handlers. +\end{funcdesc} + +\begin{funcdesc}{setLoggerClass}{klass} +Tells the logging system to use the class \var{klass} when instantiating a +logger. The class should define \method{__init__()} such that only a name +argument is required, and the \method{__init__()} should call +\method{Logger.__init__()}. This function is typically called before any +loggers are instantiated by applications which need to use custom logger +behavior. +\end{funcdesc} + + +\begin{seealso} + \seepep{282}{A Logging System} + {The proposal which described this feature for inclusion in + the Python standard library.} + \seelink{http://www.red-dove.com/python_logging.html} + {Original Python \module{logging} package} + {This is the original source for the \module{logging} + package. The version of the package available from this + site is suitable for use with Python 1.5.2, 2.1.x and 2.2.x, which + do not include the \module{logging} package in the standard + library.} +\end{seealso} + + +\subsection{Logger Objects} + +Loggers have the following attributes and methods. Note that Loggers are +never instantiated directly, but always through the module-level function +\function{logging.getLogger(name)}. + +\begin{datadesc}{propagate} +If this evaluates to false, logging messages are not passed by this +logger or by child loggers to higher level (ancestor) loggers. The +constructor sets this attribute to 1. +\end{datadesc} + +\begin{methoddesc}{setLevel}{lvl} +Sets the threshold for this logger to \var{lvl}. Logging messages +which are less severe than \var{lvl} will be ignored. When a logger is +created, the level is set to \constant{NOTSET} (which causes all messages +to be processed in the root logger, or delegation to the parent in non-root +loggers). +\end{methoddesc} + +\begin{methoddesc}{isEnabledFor}{lvl} +Indicates if a message of severity \var{lvl} would be processed by +this logger. This method checks first the module-level level set by +\function{logging.disable(lvl)} and then the logger's effective level as +determined by \method{getEffectiveLevel()}. +\end{methoddesc} + +\begin{methoddesc}{getEffectiveLevel}{} +Indicates the effective level for this logger. If a value other than +\constant{NOTSET} has been set using \method{setLevel()}, it is returned. +Otherwise, the hierarchy is traversed towards the root until a value +other than \constant{NOTSET} is found, and that value is returned. +\end{methoddesc} + +\begin{methoddesc}{debug}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{DEBUG} on this logger. +The \var{msg} is the message format string, and the \var{args} are the +arguments which are merged into \var{msg}. The only keyword argument in +\var{kwargs} which is inspected is \var{exc_info} which, if it does not +evaluate as false, causes exception information (via a call to +\function{sys.exc_info()}) to be added to the logging message. +\end{methoddesc} + +\begin{methoddesc}{info}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{INFO} on this logger. +The arguments are interpreted as for \method{debug()}. +\end{methoddesc} + +\begin{methoddesc}{warning}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{WARNING} on this logger. +The arguments are interpreted as for \method{debug()}. +\end{methoddesc} + +\begin{methoddesc}{error}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{ERROR} on this logger. +The arguments are interpreted as for \method{debug()}. +\end{methoddesc} + +\begin{methoddesc}{critical}{msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \constant{CRITICAL} on this logger. +The arguments are interpreted as for \method{debug()}. +\end{methoddesc} + +\begin{methoddesc}{log}{lvl, msg\optional{, *args\optional{, **kwargs}}} +Logs a message with level \var{lvl} on this logger. +The other arguments are interpreted as for \method{debug()}. +\end{methoddesc} + +\begin{methoddesc}{exception}{msg\optional{, *args}} +Logs a message with level \constant{ERROR} on this logger. +The arguments are interpreted as for \method{debug()}. Exception info +is added to the logging message. This method should only be called +from an exception handler. +\end{methoddesc} + +\begin{methoddesc}{addFilter}{filt} +Adds the specified filter \var{filt} to this logger. +\end{methoddesc} + +\begin{methoddesc}{removeFilter}{filt} +Removes the specified filter \var{filt} from this logger. +\end{methoddesc} + +\begin{methoddesc}{filter}{record} +Applies this logger's filters to the record and returns a true value if +the record is to be processed. +\end{methoddesc} + +\begin{methoddesc}{addHandler}{hdlr} +Adds the specified handler \var{hdlr} to this logger. +\end{methoddesc} + +\begin{methoddesc}{removeHandler}{hdlr} +Removes the specified handler \var{hdlr} from this logger. +\end{methoddesc} + +\begin{methoddesc}{findCaller}{} +Finds the caller's source filename and line number. Returns the filename +and line number as a 2-element tuple. +\end{methoddesc} + +\begin{methoddesc}{handle}{record} +Handles a record by passing it to all handlers associated with this logger +and its ancestors (until a false value of \var{propagate} is found). +This method is used for unpickled records received from a socket, as well +as those created locally. Logger-level filtering is applied using +\method{filter()}. +\end{methoddesc} + +\begin{methoddesc}{makeRecord}{name, lvl, fn, lno, msg, args, exc_info} +This is a factory method which can be overridden in subclasses to create +specialized \class{LogRecord} instances. +\end{methoddesc} + +\subsection{Handler Objects} + +Handlers have the following attributes and methods. Note that +\class{Handler} is never instantiated directly; this class acts as a +base for more useful subclasses. However, the \method{__init__()} +method in subclasses needs to call \method{Handler.__init__()}. + +\begin{methoddesc}{__init__}{level=\constant{NOTSET}} +Initializes the \class{Handler} instance by setting its level, setting +the list of filters to the empty list and creating a lock (using +\method{createLock()}) for serializing access to an I/O mechanism. +\end{methoddesc} + +\begin{methoddesc}{createLock}{} +Initializes a thread lock which can be used to serialize access to +underlying I/O functionality which may not be threadsafe. +\end{methoddesc} + +\begin{methoddesc}{acquire}{} +Acquires the thread lock created with \method{createLock()}. +\end{methoddesc} + +\begin{methoddesc}{release}{} +Releases the thread lock acquired with \method{acquire()}. +\end{methoddesc} + +\begin{methoddesc}{setLevel}{lvl} +Sets the threshold for this handler to \var{lvl}. Logging messages which are +less severe than \var{lvl} will be ignored. When a handler is created, the +level is set to \constant{NOTSET} (which causes all messages to be processed). +\end{methoddesc} + +\begin{methoddesc}{setFormatter}{form} +Sets the \class{Formatter} for this handler to \var{form}. +\end{methoddesc} + +\begin{methoddesc}{addFilter}{filt} +Adds the specified filter \var{filt} to this handler. +\end{methoddesc} + +\begin{methoddesc}{removeFilter}{filt} +Removes the specified filter \var{filt} from this handler. +\end{methoddesc} + +\begin{methoddesc}{filter}{record} +Applies this handler's filters to the record and returns a true value if +the record is to be processed. +\end{methoddesc} + +\begin{methoddesc}{flush}{} +Ensure all logging output has been flushed. This version does +nothing and is intended to be implemented by subclasses. +\end{methoddesc} + +\begin{methoddesc}{close}{} +Tidy up any resources used by the handler. This version does +nothing and is intended to be implemented by subclasses. +\end{methoddesc} + +\begin{methoddesc}{handle}{record} +Conditionally emits the specified logging record, depending on +filters which may have been added to the handler. Wraps the actual +emission of the record with acquisition/release of the I/O thread +lock. +\end{methoddesc} + +\begin{methoddesc}{handleError}{record} +This method should be called from handlers when an exception is +encountered during an \method{emit()} call. By default it does nothing, +which means that exceptions get silently ignored. This is what is +mostly wanted for a logging system - most users will not care +about errors in the logging system, they are more interested in +application errors. You could, however, replace this with a custom +handler if you wish. The specified record is the one which was being +processed when the exception occurred. +\end{methoddesc} + +\begin{methoddesc}{format}{record} +Do formatting for a record - if a formatter is set, use it. +Otherwise, use the default formatter for the module. +\end{methoddesc} + +\begin{methoddesc}{emit}{record} +Do whatever it takes to actually log the specified logging record. +This version is intended to be implemented by subclasses and so +raises a \exception{NotImplementedError}. +\end{methoddesc} + +\subsubsection{StreamHandler} + +The \class{StreamHandler} class sends logging output to streams such as +\var{sys.stdout}, \var{sys.stderr} or any file-like object (or, more +precisely, any object which supports \method{write()} and \method{flush()} +methods). + +\begin{classdesc}{StreamHandler}{\optional{strm}} +Returns a new instance of the \class{StreamHandler} class. If \var{strm} is +specified, the instance will use it for logging output; otherwise, +\var{sys.stderr} will be used. +\end{classdesc} + +\begin{methoddesc}{emit}{record} +If a formatter is specified, it is used to format the record. +The record is then written to the stream with a trailing newline. +If exception information is present, it is formatted using +\function{traceback.print_exception()} and appended to the stream. +\end{methoddesc} + +\begin{methoddesc}{flush}{} +Flushes the stream by calling its \method{flush()} method. Note that +the \method{close()} method is inherited from \class{Handler} and +so does nothing, so an explicit \method{flush()} call may be needed +at times. +\end{methoddesc} + +\subsubsection{FileHandler} + +The \class{FileHandler} class sends logging output to a disk file. +It inherits the output functionality from \class{StreamHandler}. + +\begin{classdesc}{FileHandler}{filename\optional{, mode}} +Returns a new instance of the \class{FileHandler} class. The specified +file is opened and used as the stream for logging. If \var{mode} is +not specified, \constant{'a'} is used. By default, the file grows +indefinitely. +\end{classdesc} + +\begin{methoddesc}{close}{} +Closes the file. +\end{methoddesc} + +\begin{methoddesc}{emit}{record} +Outputs the record to the file. +\end{methoddesc} + +\subsubsection{RotatingFileHandler} + +The \class{RotatingFileHandler} class supports rotation of disk log files. + +\begin{classdesc}{RotatingFileHandler}{filename\optional{, mode\optional{, + maxBytes\optional{, backupCount}}}} +Returns a new instance of the \class{RotatingFileHandler} class. The +specified file is opened and used as the stream for logging. If +\var{mode} is not specified, \code{'a'} is used. By default, the +file grows indefinitely. + +You can use the \var{maxBytes} and +\var{backupCount} values to allow the file to \dfn{rollover} at a +predetermined size. When the size is about to be exceeded, the file is +closed and a new file is silently opened for output. Rollover occurs +whenever the current log file is nearly \var{maxBytes} in length; if +\var{maxBytes} is zero, rollover never occurs. If \var{backupCount} +is non-zero, the system will save old log files by appending the +extensions ".1", ".2" etc., to the filename. For example, with +a \var{backupCount} of 5 and a base file name of +\file{app.log}, you would get \file{app.log}, +\file{app.log.1}, \file{app.log.2}, up to \file{app.log.5}. The file being +written to is always \file{app.log}. When this file is filled, it is +closed and renamed to \file{app.log.1}, and if files \file{app.log.1}, +\file{app.log.2}, etc. exist, then they are renamed to \file{app.log.2}, +\file{app.log.3} etc. respectively. +\end{classdesc} + +\begin{methoddesc}{doRollover}{} +Does a rollover, as described above. +\end{methoddesc} + +\begin{methoddesc}{emit}{record} +Outputs the record to the file, catering for rollover as described +in \method{setRollover()}. +\end{methoddesc} + +\subsubsection{SocketHandler} + +The \class{SocketHandler} class sends logging output to a network +socket. The base class uses a TCP socket. + +\begin{classdesc}{SocketHandler}{host, port} +Returns a new instance of the \class{SocketHandler} class intended to +communicate with a remote machine whose address is given by \var{host} +and \var{port}. +\end{classdesc} + +\begin{methoddesc}{close}{} +Closes the socket. +\end{methoddesc} + +\begin{methoddesc}{handleError}{} +\end{methoddesc} + +\begin{methoddesc}{emit}{} +Pickles the record's attribute dictionary and writes it to the socket in +binary format. If there is an error with the socket, silently drops the +packet. If the connection was previously lost, re-establishes the connection. +To unpickle the record at the receiving end into a LogRecord, use the +\function{makeLogRecord} function. +\end{methoddesc} + +\begin{methoddesc}{handleError}{} +Handles an error which has occurred during \method{emit()}. The +most likely cause is a lost connection. Closes the socket so that +we can retry on the next event. +\end{methoddesc} + +\begin{methoddesc}{makeSocket}{} +This is a factory method which allows subclasses to define the precise +type of socket they want. The default implementation creates a TCP +socket (\constant{socket.SOCK_STREAM}). +\end{methoddesc} + +\begin{methoddesc}{makePickle}{record} +Pickles the record's attribute dictionary in binary format with a length +prefix, and returns it ready for transmission across the socket. +\end{methoddesc} + +\begin{methoddesc}{send}{packet} +Send a pickled string \var{packet} to the socket. This function allows +for partial sends which can happen when the network is busy. +\end{methoddesc} + +\subsubsection{DatagramHandler} + +The \class{DatagramHandler} class inherits from \class{SocketHandler} +to support sending logging messages over UDP sockets. + +\begin{classdesc}{DatagramHandler}{host, port} +Returns a new instance of the \class{DatagramHandler} class intended to +communicate with a remote machine whose address is given by \var{host} +and \var{port}. +\end{classdesc} + +\begin{methoddesc}{emit}{} +Pickles the record's attribute dictionary and writes it to the socket in +binary format. If there is an error with the socket, silently drops the +packet. +To unpickle the record at the receiving end into a LogRecord, use the +\function{makeLogRecord} function. +\end{methoddesc} + +\begin{methoddesc}{makeSocket}{} +The factory method of \class{SocketHandler} is here overridden to create +a UDP socket (\constant{socket.SOCK_DGRAM}). +\end{methoddesc} + +\begin{methoddesc}{send}{s} +Send a pickled string to a socket. +\end{methoddesc} + +\subsubsection{SysLogHandler} + +The \class{SysLogHandler} class supports sending logging messages to a +remote or local \UNIX{} syslog. + +\begin{classdesc}{SysLogHandler}{\optional{address\optional{, facility}}} +Returns a new instance of the \class{SysLogHandler} class intended to +communicate with a remote \UNIX{} machine whose address is given by +\var{address} in the form of a \code{(\var{host}, \var{port})} +tuple. If \var{address} is not specified, \code{('localhost', 514)} is +used. The address is used to open a UDP socket. If \var{facility} is +not specified, \constant{LOG_USER} is used. +\end{classdesc} + +\begin{methoddesc}{close}{} +Closes the socket to the remote host. +\end{methoddesc} + +\begin{methoddesc}{emit}{record} +The record is formatted, and then sent to the syslog server. If +exception information is present, it is \emph{not} sent to the server. +\end{methoddesc} + +\begin{methoddesc}{encodePriority}{facility, priority} +Encodes the facility and priority into an integer. You can pass in strings +or integers - if strings are passed, internal mapping dictionaries are used +to convert them to integers. +\end{methoddesc} + +\subsubsection{NTEventLogHandler} + +The \class{NTEventLogHandler} class supports sending logging messages +to a local Windows NT, Windows 2000 or Windows XP event log. Before +you can use it, you need Mark Hammond's Win32 extensions for Python +installed. + +\begin{classdesc}{NTEventLogHandler}{appname\optional{, + dllname\optional{, logtype}}} +Returns a new instance of the \class{NTEventLogHandler} class. The +\var{appname} is used to define the application name as it appears in the +event log. An appropriate registry entry is created using this name. +The \var{dllname} should give the fully qualified pathname of a .dll or .exe +which contains message definitions to hold in the log (if not specified, +\code{'win32service.pyd'} is used - this is installed with the Win32 +extensions and contains some basic placeholder message definitions. +Note that use of these placeholders will make your event logs big, as the +entire message source is held in the log. If you want slimmer logs, you have +to pass in the name of your own .dll or .exe which contains the message +definitions you want to use in the event log). The \var{logtype} is one of +\code{'Application'}, \code{'System'} or \code{'Security'}, and +defaults to \code{'Application'}. +\end{classdesc} + +\begin{methoddesc}{close}{} +At this point, you can remove the application name from the registry as a +source of event log entries. However, if you do this, you will not be able +to see the events as you intended in the Event Log Viewer - it needs to be +able to access the registry to get the .dll name. The current version does +not do this (in fact it doesn't do anything). +\end{methoddesc} + +\begin{methoddesc}{emit}{record} +Determines the message ID, event category and event type, and then logs the +message in the NT event log. +\end{methoddesc} + +\begin{methoddesc}{getEventCategory}{record} +Returns the event category for the record. Override this if you +want to specify your own categories. This version returns 0. +\end{methoddesc} + +\begin{methoddesc}{getEventType}{record} +Returns the event type for the record. Override this if you want +to specify your own types. This version does a mapping using the +handler's typemap attribute, which is set up in \method{__init__()} +to a dictionary which contains mappings for \constant{DEBUG}, +\constant{INFO}, \constant{WARNING}, \constant{ERROR} and +\constant{CRITICAL}. If you are using your own levels, you will either need +to override this method or place a suitable dictionary in the +handler's \var{typemap} attribute. +\end{methoddesc} + +\begin{methoddesc}{getMessageID}{record} +Returns the message ID for the record. If you are using your +own messages, you could do this by having the \var{msg} passed to the +logger being an ID rather than a format string. Then, in here, +you could use a dictionary lookup to get the message ID. This +version returns 1, which is the base message ID in +\file{win32service.pyd}. +\end{methoddesc} + +\subsubsection{SMTPHandler} + +The \class{SMTPHandler} class supports sending logging messages to an email +address via SMTP. + +\begin{classdesc}{SMTPHandler}{mailhost, fromaddr, toaddrs, subject} +Returns a new instance of the \class{SMTPHandler} class. The +instance is initialized with the from and to addresses and subject +line of the email. The \var{toaddrs} should be a list of strings without +domain names (That's what the \var{mailhost} is for). To specify a +non-standard SMTP port, use the (host, port) tuple format for the +\var{mailhost} argument. If you use a string, the standard SMTP port +is used. +\end{classdesc} + +\begin{methoddesc}{emit}{record} +Formats the record and sends it to the specified addressees. +\end{methoddesc} + +\begin{methoddesc}{getSubject}{record} +If you want to specify a subject line which is record-dependent, +override this method. +\end{methoddesc} + +\subsubsection{MemoryHandler} + +The \class{MemoryHandler} supports buffering of logging records in memory, +periodically flushing them to a \dfn{target} handler. Flushing occurs +whenever the buffer is full, or when an event of a certain severity or +greater is seen. + +\class{MemoryHandler} is a subclass of the more general +\class{BufferingHandler}, which is an abstract class. This buffers logging +records in memory. Whenever each record is added to the buffer, a +check is made by calling \method{shouldFlush()} to see if the buffer +should be flushed. If it should, then \method{flush()} is expected to +do the needful. + +\begin{classdesc}{BufferingHandler}{capacity} +Initializes the handler with a buffer of the specified capacity. +\end{classdesc} + +\begin{methoddesc}{emit}{record} +Appends the record to the buffer. If \method{shouldFlush()} returns true, +calls \method{flush()} to process the buffer. +\end{methoddesc} + +\begin{methoddesc}{flush}{} +You can override this to implement custom flushing behavior. This version +just zaps the buffer to empty. +\end{methoddesc} + +\begin{methoddesc}{shouldFlush}{record} +Returns true if the buffer is up to capacity. This method can be +overridden to implement custom flushing strategies. +\end{methoddesc} + +\begin{classdesc}{MemoryHandler}{capacity\optional{, flushLevel +\optional{, target}}} +Returns a new instance of the \class{MemoryHandler} class. The +instance is initialized with a buffer size of \var{capacity}. If +\var{flushLevel} is not specified, \constant{ERROR} is used. If no +\var{target} is specified, the target will need to be set using +\method{setTarget()} before this handler does anything useful. +\end{classdesc} + +\begin{methoddesc}{close}{} +Calls \method{flush()}, sets the target to \constant{None} and +clears the buffer. +\end{methoddesc} + +\begin{methoddesc}{flush}{} +For a \class{MemoryHandler}, flushing means just sending the buffered +records to the target, if there is one. Override if you want +different behavior. +\end{methoddesc} + +\begin{methoddesc}{setTarget}{target} +Sets the target handler for this handler. +\end{methoddesc} + +\begin{methoddesc}{shouldFlush}{record} +Checks for buffer full or a record at the \var{flushLevel} or higher. +\end{methoddesc} + +\subsubsection{HTTPHandler} + +The \class{HTTPHandler} class supports sending logging messages to a +Web server, using either \samp{GET} or \samp{POST} semantics. + +\begin{classdesc}{HTTPHandler}{host, url\optional{, method}} +Returns a new instance of the \class{HTTPHandler} class. The +instance is initialized with a host address, url and HTTP method. +If no \var{method} is specified, \samp{GET} is used. +\end{classdesc} + +\begin{methoddesc}{emit}{record} +Sends the record to the Web server as an URL-encoded dictionary. +\end{methoddesc} + +\subsection{Formatter Objects} + +\class{Formatter}s have the following attributes and methods. They are +responsible for converting a \class{LogRecord} to (usually) a string +which can be interpreted by either a human or an external system. The +base +\class{Formatter} allows a formatting string to be specified. If none is +supplied, the default value of \code{'\%(message)s\e'} is used. + +A Formatter can be initialized with a format string which makes use of +knowledge of the \class{LogRecord} attributes - such as the default value +mentioned above making use of the fact that the user's message and +arguments are pre-formatted into a LogRecord's \var{message} +attribute. This format string contains standard python \%-style +mapping keys. See section \ref{typesseq-strings}, ``String Formatting +Operations,'' for more information on string formatting. + +Currently, the useful mapping keys in a LogRecord are: + +\begin{tableii}{l|l}{code}{Format}{Description} +\lineii{\%(name)s} {Name of the logger (logging channel).} +\lineii{\%(levelno)s} {Numeric logging level for the message + (\constant{DEBUG}, \constant{INFO}, + \constant{WARNING}, \constant{ERROR}, + \constant{CRITICAL}).} +\lineii{\%(levelname)s}{Text logging level for the message + (\code{'DEBUG'}, \code{'INFO'}, + \code{'WARNING'}, \code{'ERROR'}, + \code{'CRITICAL'}).} +\lineii{\%(pathname)s} {Full pathname of the source file where the logging + call was issued (if available).} +\lineii{\%(filename)s} {Filename portion of pathname.} +\lineii{\%(module)s} {Module (name portion of filename).} +\lineii{\%(lineno)d} {Source line number where the logging call was issued + (if available).} +\lineii{\%(created)f} {Time when the LogRecord was created (as + returned by \function{time.time()}).} +\lineii{\%(asctime)s} {Human-readable time when the LogRecord was created. + By default this is of the form + ``2003-07-08 16:49:45,896'' (the numbers after the + comma are millisecond portion of the time).} +\lineii{\%(msecs)d} {Millisecond portion of the time when the + \class{LogRecord} was created.} +\lineii{\%(thread)d} {Thread ID (if available).} +\lineii{\%(process)d} {Process ID (if available).} +\lineii{\%(message)s} {The logged message, computed as \code{msg \% args}.} +\end{tableii} + +\begin{classdesc}{Formatter}{\optional{fmt\optional{, datefmt}}} +Returns a new instance of the \class{Formatter} class. The +instance is initialized with a format string for the message as a whole, +as well as a format string for the date/time portion of a message. If +no \var{fmt} is specified, \code{'\%(message)s'} is used. If no \var{datefmt} +is specified, the ISO8601 date format is used. +\end{classdesc} + +\begin{methoddesc}{format}{record} +The record's attribute dictionary is used as the operand to a +string formatting operation. Returns the resulting string. +Before formatting the dictionary, a couple of preparatory steps +are carried out. The \var{message} attribute of the record is computed +using \var{msg} \% \var{args}. If the formatting string contains +\code{'(asctime)'}, \method{formatTime()} is called to format the +event time. If there is exception information, it is formatted using +\method{formatException()} and appended to the message. +\end{methoddesc} + +\begin{methoddesc}{formatTime}{record\optional{, datefmt}} +This method should be called from \method{format()} by a formatter which +wants to make use of a formatted time. This method can be overridden +in formatters to provide for any specific requirement, but the +basic behavior is as follows: if \var{datefmt} (a string) is specified, +it is used with \function{time.strftime()} to format the creation time of the +record. Otherwise, the ISO8601 format is used. The resulting +string is returned. +\end{methoddesc} + +\begin{methoddesc}{formatException}{exc_info} +Formats the specified exception information (a standard exception tuple +as returned by \function{sys.exc_info()}) as a string. This default +implementation just uses \function{traceback.print_exception()}. +The resulting string is returned. +\end{methoddesc} + +\subsection{Filter Objects} + +\class{Filter}s can be used by \class{Handler}s and \class{Logger}s for +more sophisticated filtering than is provided by levels. The base filter +class only allows events which are below a certain point in the logger +hierarchy. For example, a filter initialized with "A.B" will allow events +logged by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", +"B.A.B" etc. If initialized with the empty string, all events are passed. + +\begin{classdesc}{Filter}{\optional{name}} +Returns an instance of the \class{Filter} class. If \var{name} is specified, +it names a logger which, together with its children, will have its events +allowed through the filter. If no name is specified, allows every event. +\end{classdesc} + +\begin{methoddesc}{filter}{record} +Is the specified record to be logged? Returns zero for no, nonzero for +yes. If deemed appropriate, the record may be modified in-place by this +method. +\end{methoddesc} + +\subsection{LogRecord Objects} + +LogRecord instances are created every time something is logged. They +contain all the information pertinent to the event being logged. The +main information passed in is in msg and args, which are combined +using msg \% args to create the message field of the record. The record +also includes information such as when the record was created, the +source line where the logging call was made, and any exception +information to be logged. + +LogRecord has no methods; it's just a repository for information about the +logging event. The only reason it's a class rather than a dictionary is to +facilitate extension. + +\begin{classdesc}{LogRecord}{name, lvl, pathname, lineno, msg, args, + exc_info} +Returns an instance of \class{LogRecord} initialized with interesting +information. The \var{name} is the logger name; \var{lvl} is the +numeric level; \var{pathname} is the absolute pathname of the source +file in which the logging call was made; \var{lineno} is the line +number in that file where the logging call is found; \var{msg} is the +user-supplied message (a format string); \var{args} is the tuple +which, together with \var{msg}, makes up the user message; and +\var{exc_info} is the exception tuple obtained by calling +\function{sys.exc_info() }(or \constant{None}, if no exception information +is available). +\end{classdesc} + +\subsection{Thread Safety} + +The logging module is intended to be thread-safe without any special work +needing to be done by its clients. It achieves this though using threading +locks; there is one lock to serialize access to the module's shared data, +and each handler also creates a lock to serialize access to its underlying +I/O. + +\subsection{Configuration} + + +\subsubsection{Configuration functions} + +The following functions allow the logging module to be +configured. Before they can be used, you must import +\module{logging.config}. Their use is optional --- you can configure +the logging module entirely by making calls to the main API (defined +in \module{logging} itself) and defining handlers which are declared +either in \module{logging} or \module{logging.handlers}. + +\begin{funcdesc}{fileConfig}{fname\optional{, defaults}} +Reads the logging configuration from a ConfigParser-format file named +\var{fname}. This function can be called several times from an application, +allowing an end user the ability to select from various pre-canned +configurations (if the developer provides a mechanism to present the +choices and load the chosen configuration). Defaults to be passed to +ConfigParser can be specified in the \var{defaults} argument. +\end{funcdesc} + +\begin{funcdesc}{listen}{\optional{port}} +Starts up a socket server on the specified port, and listens for new +configurations. If no port is specified, the module's default +\constant{DEFAULT_LOGGING_CONFIG_PORT} is used. Logging configurations +will be sent as a file suitable for processing by \function{fileConfig()}. +Returns a \class{Thread} instance on which you can call \method{start()} +to start the server, and which you can \method{join()} when appropriate. +To stop the server, call \function{stopListening()}. +\end{funcdesc} + +\begin{funcdesc}{stopListening}{} +Stops the listening server which was created with a call to +\function{listen()}. This is typically called before calling \method{join()} +on the return value from \function{listen()}. +\end{funcdesc} + +\subsubsection{Configuration file format} + +The configuration file format understood by \function{fileConfig} is +based on ConfigParser functionality. The file must contain sections +called \code{[loggers]}, \code{[handlers]} and \code{[formatters]} +which identify by name the entities of each type which are defined in +the file. For each such entity, there is a separate section which +identified how that entity is configured. Thus, for a logger named +\code{log01} in the \code{[loggers]} section, the relevant +configuration details are held in a section +\code{[logger_log01]}. Similarly, a handler called \code{hand01} in +the \code{[handlers]} section will have its configuration held in a +section called \code{[handler_hand01]}, while a formatter called +\code{form01} in the \code{[formatters]} section will have its +configuration specified in a section called +\code{[formatter_form01]}. The root logger configuration must be +specified in a section called \code{[logger_root]}. + +Examples of these sections in the file are given below. + +\begin{verbatim} +[loggers] +keys=root,log02,log03,log04,log05,log06,log07 + +[handlers] +keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09 + +[formatters] +keys=form01,form02,form03,form04,form05,form06,form07,form08,form09 +\end{verbatim} + +The root logger must specify a level and a list of handlers. An +example of a root logger section is given below. + +\begin{verbatim} +[logger_root] +level=NOTSET +handlers=hand01 +\end{verbatim} + +The \code{level} entry can be one of \code{DEBUG, INFO, WARNING, +ERROR, CRITICAL} or \code{NOTSET}. For the root logger only, +\code{NOTSET} means that all messages will be logged. Level values are +\function{eval()}uated in the context of the \code{logging} package's +namespace. + +The \code{handlers} entry is a comma-separated list of handler names, +which must appear in the \code{[handlers]} section. These names must +appear in the \code{[handlers]} section and have corresponding +sections in the configuration file. + +For loggers other than the root logger, some additional information is +required. This is illustrated by the following example. + +\begin{verbatim} +[logger_parser] +level=DEBUG +handlers=hand01 +propagate=1 +qualname=compiler.parser +\end{verbatim} + +The \code{level} and \code{handlers} entries are interpreted as for +the root logger, except that if a non-root logger's level is specified +as \code{NOTSET}, the system consults loggers higher up the hierarchy +to determine the effective level of the logger. The \code{propagate} +entry is set to 1 to indicate that messages must propagate to handlers +higher up the logger hierarchy from this logger, or 0 to indicate that +messages are \strong{not} propagated to handlers up the hierarchy. The +\code{qualname} entry is the hierarchical channel name of the logger, +that is to say the name used by the application to get the logger. + +Sections which specify handler configuration are exemplified by the +following. + +\begin{verbatim} +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +args=(sys.stdout,) +\end{verbatim} + +The \code{class} entry indicates the handler's class (as determined by +\function{eval()} in the \code{logging} package's namespace). The +\code{level} is interpreted as for loggers, and \code{NOTSET} is taken +to mean "log everything". + +The \code{formatter} entry indicates the key name of the formatter for +this handler. If blank, a default formatter +(\code{logging._defaultFormatter}) is used. If a name is specified, it +must appear in the \code{[formatters]} section and have a +corresponding section in the configuration file. + +The \code{args} entry, when \function{eval()}uated in the context of +the \code{logging} package's namespace, is the list of arguments to +the constructor for the handler class. Refer to the constructors for +the relevant handlers, or to the examples below, to see how typical +entries are constructed. + +\begin{verbatim} +[handler_hand02] +class=FileHandler +level=DEBUG +formatter=form02 +args=('python.log', 'w') + +[handler_hand03] +class=handlers.SocketHandler +level=INFO +formatter=form03 +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT) + +[handler_hand04] +class=handlers.DatagramHandler +level=WARN +formatter=form04 +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT) + +[handler_hand05] +class=handlers.SysLogHandler +level=ERROR +formatter=form05 +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) + +[handler_hand06] +class=NTEventLogHandler +level=CRITICAL +formatter=form06 +args=('Python Application', '', 'Application') + +[handler_hand07] +class=SMTPHandler +level=WARN +formatter=form07 +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') + +[handler_hand08] +class=MemoryHandler +level=NOTSET +formatter=form08 +target= +args=(10, ERROR) + +[handler_hand09] +class=HTTPHandler +level=NOTSET +formatter=form09 +args=('localhost:9022', '/log', 'GET') +\end{verbatim} + +Sections which specify formatter configuration are typified by the following. + +\begin{verbatim} +[formatter_form01] +format=F1 %(asctime)s %(levelname)s %(message)s +datefmt= +\end{verbatim} + +The \code{format} entry is the overall format string, and the +\code{datefmt} entry is the \function{strftime()}-compatible date/time format +string. If empty, the package substitutes ISO8601 format date/times, which +is almost equivalent to specifying the date format string "%Y-%m-%d %H:%M:%S". +The ISO8601 format also specifies milliseconds, which are appended to the +result of using the above format string, with a comma separator. An example +time in ISO8601 format is \code{2003-01-23 00:29:50,411}. + +\subsection{Using the logging package} + +\subsubsection{Simplest usage} + +Here's a simple example which shows the most casual usage of the logging +package. + +\begin{verbatim} +import logging +logging.debug("Houston, we have a %s", "thorny problem") +logging.info("Houston, we have a %s", "interesting problem") +logging.warning("Houston, we have a %s", "bit of a problem") +logging.error("Houston, we have a %s", "major problem") +logging.critical("Houston, we have a %s", "major disaster") +try: + infinity = 1 / 0 +except: + logging.exception("Houston, we have an %s", "unexpected problem") +\end{verbatim} + +If you run the above example, this will produce: + +\begin{verbatim} +WARNING:root:Houston, we have a bit of a problem +ERROR:root:Houston, we have a major problem +CRITICAL:root:Houston, we have a major disaster +ERROR:root:Houston, we have an unexpected problem +Traceback (most recent call last): + File "C:\Projects\RDC\Python\packages\logging\test\tmp.py", line 8, in ? + infinity = 1 / 0 +ZeroDivisionError: integer division or modulo by zero +\end{verbatim} + +The reason you get this output is that the default format is + +\begin{verbatim} +"%(levelname)s:%(name)s:%(message)s". +\end{verbatim} + +When you invoke functions \function{info()}, \function{warning()} etc. in the +logging package itself, these calls are delegated to the correspondingly +named methods in the root logger. This is why the logger name shown in the above +logging output is "root". If the root logger has no handlers configured, the +logging package creates a console handler and adds it to the root logger +automatically. (It does this by calling the \function{basicConfig()}, which you +can also call directly from your own code.) + +By default, events with a severity below WARNING are suppressed. Notice +that the \function{exception()} function acts like \function{error()}, except +that a traceback is appended to the log entry. + +\subsubsection{Logging to the console} + +Here's a simple example which logs all messages to the console. We use a named +logger: + +\begin{verbatim} +import logging +logging.basicConfig() +logger = logging.getLogger('myapp') +logger.setLevel(logging.DEBUG) +logger.debug("Houston, we have a %s", "thorny problem") +logger.info("Houston, we have a %s", "interesting problem") +logger.warning("Houston, we have a %s", "bit of a problem") +logger.error("Houston, we have a %s", "major problem") +logger.critical("Houston, we have a %s", "major disaster") +try: + infinity = 1 / 0 +except: + logger.exception("Houston, we have an %s", "unexpected problem") +\end{verbatim} + +Here's the corresponding output: + +\begin{verbatim} +DEBUG:myapp:Houston, we have a thorny problem +INFO:myapp:Houston, we have a interesting problem +WARNING:myapp:Houston, we have a bit of a problem +ERROR:myapp:Houston, we have a major problem +CRITICAL:myapp:Houston, we have a major disaster +ERROR:myapp:Houston, we have an unexpected problem +Traceback (most recent call last): + File "C:\Projects\RDC\Python\packages\logging\test\tmp.py", line 11, in ? + infinity = 1 / 0 +ZeroDivisionError: integer division or modulo by zero +\end{verbatim} + +As you can see, the specified logger name now appears in the output, and +DEBUG and INFO messages are included in the output because we explicitly +asked for them via the call to \method{setLevel()}. + +\subsubsection{Logging to a file} + +Here's a simple logging example that just logs to a file. In order, +it creates a \class{Logger} instance, then a \class{FileHandler} +and a \class{Formatter}. It attaches the \class{Formatter} to the +\class{FileHandler}, then the \class{FileHandler} to the \class{Logger}. +Finally, it sets a debug level for the logger. + +\begin{verbatim} +import logging +logger = logging.getLogger('myapp') +hdlr = logging.FileHandler('/var/tmp/myapp.log') +formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') +hdlr.setFormatter(formatter) +logger.addHandler(hdlr) +logger.setLevel(logging.WARNING) +\end{verbatim} + +We can use this logger object now to write entries to the log file: + +\begin{verbatim} +logger.error('We have a problem') +logger.info('While this is just chatty') +\end{verbatim} + +If we look in the file that was created, we'll see something like this: +\begin{verbatim} +2003-07-08 16:49:45,896 ERROR We have a problem +\end{verbatim} + +The info message was not written to the file - we called the \method{setLevel} +method to say we only wanted \code{WARNING} or worse, so the info message is +discarded. + +The timestamp is of the form +``year-month-day hour:minutes:seconds,milliseconds.'' +Note that despite the three digits of precision in the milliseconds field, +not all systems provide time with this much precision. + +\subsubsection{Logging to a rotating set of files} + diff --git a/tools/python/logging/logging-0.4.9.2/logging/__init__.py b/tools/python/logging/logging-0.4.9.2/logging/__init__.py new file mode 100644 index 0000000..75946e0 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/logging/__init__.py @@ -0,0 +1,1225 @@ +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +Logging package for Python. Based on PEP 282 and comments thereto in +comp.lang.python, and influenced by Apache's log4j system. + +Should work under Python versions >= 1.5.2, except that source line +information is not available unless 'sys._getframe()' is. + +Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. + +To use, simply 'import logging' and log away! +""" + +import sys, os, types, time, string, cStringIO + +try: + import thread + import threading +except ImportError: + thread = None + +__author__ = "Vinay Sajip " +__status__ = "beta" +__version__ = "0.4.9.2" +__date__ = "28 February 2004" + +#--------------------------------------------------------------------------- +# Miscellaneous module data +#--------------------------------------------------------------------------- + +# +#_srcfile is used when walking the stack to check when we've got the first +# caller stack frame. +# +if string.lower(__file__[-4:]) in ['.pyc', '.pyo']: + _srcfile = __file__[:-4] + '.py' +else: + _srcfile = __file__ +_srcfile = os.path.normcase(_srcfile) + +# _srcfile is only used in conjunction with sys._getframe(). +# To provide compatibility with older versions of Python, set _srcfile +# to None if _getframe() is not available; this value will prevent +# findCaller() from being called. +if not hasattr(sys, "_getframe"): + _srcfile = None + +# +#_startTime is used as the base when calculating the relative time of events +# +_startTime = time.time() + +# +#raiseExceptions is used to see if exceptions during handling should be +#propagated +# +raiseExceptions = 1 + +#--------------------------------------------------------------------------- +# Level related stuff +#--------------------------------------------------------------------------- +# +# Default levels and level names, these can be replaced with any positive set +# of values having corresponding names. There is a pseudo-level, NOTSET, which +# is only really there as a lower limit for user-defined levels. Handlers and +# loggers are initialized with NOTSET so that they will log all messages, even +# at user-defined levels. +# +CRITICAL = 50 +FATAL = CRITICAL +ERROR = 40 +WARNING = 30 +WARN = WARNING +INFO = 20 +DEBUG = 10 +NOTSET = 0 + +_levelNames = { + CRITICAL : 'CRITICAL', + ERROR : 'ERROR', + WARNING : 'WARNING', + INFO : 'INFO', + DEBUG : 'DEBUG', + NOTSET : 'NOTSET', + 'CRITICAL' : CRITICAL, + 'ERROR' : ERROR, + 'WARN' : WARNING, + 'WARNING' : WARNING, + 'INFO' : INFO, + 'DEBUG' : DEBUG, + 'NOTSET' : NOTSET, +} + +def getLevelName(level): + """ + Return the textual representation of logging level 'level'. + + If the level is one of the predefined levels (CRITICAL, ERROR, WARNING, + INFO, DEBUG) then you get the corresponding string. If you have + associated levels with names using addLevelName then the name you have + associated with 'level' is returned. Otherwise, the string + "Level %s" % level is returned. + """ + return _levelNames.get(level, ("Level %s" % level)) + +def addLevelName(level, levelName): + """ + Associate 'levelName' with 'level'. + + This is used when converting levels to text during message formatting. + """ + _acquireLock() + try: #unlikely to cause an exception, but you never know... + _levelNames[level] = levelName + _levelNames[levelName] = level + finally: + _releaseLock() + +#--------------------------------------------------------------------------- +# Thread-related stuff +#--------------------------------------------------------------------------- + +# +#_lock is used to serialize access to shared data structures in this module. +#This needs to be an RLock because fileConfig() creates Handlers and so +#might arbitrary user threads. Since Handler.__init__() updates the shared +#dictionary _handlers, it needs to acquire the lock. But if configuring, +#the lock would already have been acquired - so we need an RLock. +#The same argument applies to Loggers and Manager.loggerDict. +# +_lock = None + +def _acquireLock(): + """ + Acquire the module-level lock for serializing access to shared data. + + This should be released with _releaseLock(). + """ + global _lock + if (not _lock) and thread: + _lock = threading.RLock() + if _lock: + _lock.acquire() + +def _releaseLock(): + """ + Release the module-level lock acquired by calling _acquireLock(). + """ + if _lock: + _lock.release() + +#--------------------------------------------------------------------------- +# The logging record +#--------------------------------------------------------------------------- + +class LogRecord: + """ + A LogRecord instance represents an event being logged. + + LogRecord instances are created every time something is logged. They + contain all the information pertinent to the event being logged. The + main information passed in is in msg and args, which are combined + using str(msg) % args to create the message field of the record. The + record also includes information such as when the record was created, + the source line where the logging call was made, and any exception + information to be logged. + """ + def __init__(self, name, level, pathname, lineno, msg, args, exc_info): + """ + Initialize a logging record with interesting information. + """ + ct = time.time() + self.name = name + self.msg = msg + self.args = args + self.levelname = getLevelName(level) + self.levelno = level + self.pathname = pathname + try: + self.filename = os.path.basename(pathname) + self.module = os.path.splitext(self.filename)[0] + except: + self.filename = pathname + self.module = "Unknown module" + self.exc_info = exc_info + self.exc_text = None # used to cache the traceback text + self.lineno = lineno + self.created = ct + self.msecs = (ct - long(ct)) * 1000 + self.relativeCreated = (self.created - _startTime) * 1000 + if thread: + self.thread = thread.get_ident() + else: + self.thread = None + if hasattr(os, 'getpid'): + self.process = os.getpid() + else: + self.process = None + + def __str__(self): + return ''%(self.name, self.levelno, + self.pathname, self.lineno, self.msg) + + def getMessage(self): + """ + Return the message for this LogRecord. + + Return the message for this LogRecord after merging any user-supplied + arguments with the message. + """ + if not hasattr(types, "UnicodeType"): #if no unicode support... + msg = str(self.msg) + else: + try: + msg = str(self.msg) + except UnicodeError: + msg = self.msg #Defer encoding till later + if self.args: + msg = msg % self.args + return msg + +def makeLogRecord(dict): + """ + Make a LogRecord whose attributes are defined by the specified dictionary, + This function is useful for converting a logging event received over + a socket connection (which is sent as a dictionary) into a LogRecord + instance. + """ + rv = LogRecord(None, None, "", 0, "", (), None) + rv.__dict__.update(dict) + return rv + +#--------------------------------------------------------------------------- +# Formatter classes and functions +#--------------------------------------------------------------------------- + +class Formatter: + """ + Formatter instances are used to convert a LogRecord to text. + + Formatters need to know how a LogRecord is constructed. They are + responsible for converting a LogRecord to (usually) a string which can + be interpreted by either a human or an external system. The base Formatter + allows a formatting string to be specified. If none is supplied, the + default value of "%s(message)\\n" is used. + + The Formatter can be initialized with a format string which makes use of + knowledge of the LogRecord attributes - e.g. the default value mentioned + above makes use of the fact that the user's message and arguments are pre- + formatted into a LogRecord's message attribute. Currently, the useful + attributes in a LogRecord are described by: + + %(name)s Name of the logger (logging channel) + %(levelno)s Numeric logging level for the message (DEBUG, INFO, + WARNING, ERROR, CRITICAL) + %(levelname)s Text logging level for the message ("DEBUG", "INFO", + "WARNING", "ERROR", "CRITICAL") + %(pathname)s Full pathname of the source file where the logging + call was issued (if available) + %(filename)s Filename portion of pathname + %(module)s Module (name portion of filename) + %(lineno)d Source line number where the logging call was issued + (if available) + %(created)f Time when the LogRecord was created (time.time() + return value) + %(asctime)s Textual time when the LogRecord was created + %(msecs)d Millisecond portion of the creation time + %(relativeCreated)d Time in milliseconds when the LogRecord was created, + relative to the time the logging module was loaded + (typically at application startup time) + %(thread)d Thread ID (if available) + %(process)d Process ID (if available) + %(message)s The result of record.getMessage(), computed just as + the record is emitted + """ + + converter = time.localtime + + def __init__(self, fmt=None, datefmt=None): + """ + Initialize the formatter with specified format strings. + + Initialize the formatter either with the specified format string, or a + default as described above. Allow for specialized date formatting with + the optional datefmt argument (if omitted, you get the ISO8601 format). + """ + if fmt: + self._fmt = fmt + else: + self._fmt = "%(message)s" + self.datefmt = datefmt + + def formatTime(self, record, datefmt=None): + """ + Return the creation time of the specified LogRecord as formatted text. + + This method should be called from format() by a formatter which + wants to make use of a formatted time. This method can be overridden + in formatters to provide for any specific requirement, but the + basic behaviour is as follows: if datefmt (a string) is specified, + it is used with time.strftime() to format the creation time of the + record. Otherwise, the ISO8601 format is used. The resulting + string is returned. This function uses a user-configurable function + to convert the creation time to a tuple. By default, time.localtime() + is used; to change this for a particular formatter instance, set the + 'converter' attribute to a function with the same signature as + time.localtime() or time.gmtime(). To change it for all formatters, + for example if you want all logging times to be shown in GMT, + set the 'converter' attribute in the Formatter class. + """ + ct = self.converter(record.created) + if datefmt: + s = time.strftime(datefmt, ct) + else: + t = time.strftime("%Y-%m-%d %H:%M:%S", ct) + s = "%s,%03d" % (t, record.msecs) + return s + + def formatException(self, ei): + """ + Format and return the specified exception information as a string. + + This default implementation just uses + traceback.print_exception() + """ + import traceback + sio = cStringIO.StringIO() + traceback.print_exception(ei[0], ei[1], ei[2], None, sio) + s = sio.getvalue() + sio.close() + if s[-1] == "\n": + s = s[:-1] + return s + + def format(self, record): + """ + Format the specified record as text. + + The record's attribute dictionary is used as the operand to a + string formatting operation which yields the returned string. + Before formatting the dictionary, a couple of preparatory steps + are carried out. The message attribute of the record is computed + using LogRecord.getMessage(). If the formatting string contains + "%(asctime)", formatTime() is called to format the event time. + If there is exception information, it is formatted using + formatException() and appended to the message. + """ + record.message = record.getMessage() + if string.find(self._fmt,"%(asctime)") >= 0: + record.asctime = self.formatTime(record, self.datefmt) + s = self._fmt % record.__dict__ + if record.exc_info: + # Cache the traceback text to avoid converting it multiple times + # (it's constant anyway) + if not record.exc_text: + record.exc_text = self.formatException(record.exc_info) + if record.exc_text: + if s[-1] != "\n": + s = s + "\n" + s = s + record.exc_text + return s + +# +# The default formatter to use when no other is specified +# +_defaultFormatter = Formatter() + +class BufferingFormatter: + """ + A formatter suitable for formatting a number of records. + """ + def __init__(self, linefmt=None): + """ + Optionally specify a formatter which will be used to format each + individual record. + """ + if linefmt: + self.linefmt = linefmt + else: + self.linefmt = _defaultFormatter + + def formatHeader(self, records): + """ + Return the header string for the specified records. + """ + return "" + + def formatFooter(self, records): + """ + Return the footer string for the specified records. + """ + return "" + + def format(self, records): + """ + Format the specified records and return the result as a string. + """ + rv = "" + if len(records) > 0: + rv = rv + self.formatHeader(records) + for record in records: + rv = rv + self.linefmt.format(record) + rv = rv + self.formatFooter(records) + return rv + +#--------------------------------------------------------------------------- +# Filter classes and functions +#--------------------------------------------------------------------------- + +class Filter: + """ + Filter instances are used to perform arbitrary filtering of LogRecords. + + Loggers and Handlers can optionally use Filter instances to filter + records as desired. The base filter class only allows events which are + below a certain point in the logger hierarchy. For example, a filter + initialized with "A.B" will allow events logged by loggers "A.B", + "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If + initialized with the empty string, all events are passed. + """ + def __init__(self, name=''): + """ + Initialize a filter. + + Initialize with the name of the logger which, together with its + children, will have its events allowed through the filter. If no + name is specified, allow every event. + """ + self.name = name + self.nlen = len(name) + + def filter(self, record): + """ + Determine if the specified record is to be logged. + + Is the specified record to be logged? Returns 0 for no, nonzero for + yes. If deemed appropriate, the record may be modified in-place. + """ + if self.nlen == 0: + return 1 + elif self.name == record.name: + return 1 + elif string.find(record.name, self.name, 0, self.nlen) != 0: + return 0 + return (record.name[self.nlen] == ".") + +class Filterer: + """ + A base class for loggers and handlers which allows them to share + common code. + """ + def __init__(self): + """ + Initialize the list of filters to be an empty list. + """ + self.filters = [] + + def addFilter(self, filter): + """ + Add the specified filter to this handler. + """ + if not (filter in self.filters): + self.filters.append(filter) + + def removeFilter(self, filter): + """ + Remove the specified filter from this handler. + """ + if filter in self.filters: + self.filters.remove(filter) + + def filter(self, record): + """ + Determine if a record is loggable by consulting all the filters. + + The default is to allow the record to be logged; any filter can veto + this and the record is then dropped. Returns a zero value if a record + is to be dropped, else non-zero. + """ + rv = 1 + for f in self.filters: + if not f.filter(record): + rv = 0 + break + return rv + +#--------------------------------------------------------------------------- +# Handler classes and functions +#--------------------------------------------------------------------------- + +_handlers = {} #repository of handlers (for flushing when shutdown called) + +class Handler(Filterer): + """ + Handler instances dispatch logging events to specific destinations. + + The base handler class. Acts as a placeholder which defines the Handler + interface. Handlers can optionally use Formatter instances to format + records as desired. By default, no formatter is specified; in this case, + the 'raw' message as determined by record.message is logged. + """ + def __init__(self, level=NOTSET): + """ + Initializes the instance - basically setting the formatter to None + and the filter list to empty. + """ + Filterer.__init__(self) + self.level = level + self.formatter = None + #get the module data lock, as we're updating a shared structure. + _acquireLock() + try: #unlikely to raise an exception, but you never know... + _handlers[self] = 1 + finally: + _releaseLock() + self.createLock() + + def createLock(self): + """ + Acquire a thread lock for serializing access to the underlying I/O. + """ + if thread: + self.lock = thread.allocate_lock() + else: + self.lock = None + + def acquire(self): + """ + Acquire the I/O thread lock. + """ + if self.lock: + self.lock.acquire() + + def release(self): + """ + Release the I/O thread lock. + """ + if self.lock: + self.lock.release() + + def setLevel(self, level): + """ + Set the logging level of this handler. + """ + self.level = level + + def format(self, record): + """ + Format the specified record. + + If a formatter is set, use it. Otherwise, use the default formatter + for the module. + """ + if self.formatter: + fmt = self.formatter + else: + fmt = _defaultFormatter + return fmt.format(record) + + def emit(self, record): + """ + Do whatever it takes to actually log the specified logging record. + + This version is intended to be implemented by subclasses and so + raises a NotImplementedError. + """ + raise NotImplementedError, 'emit must be implemented '\ + 'by Handler subclasses' + + def handle(self, record): + """ + Conditionally emit the specified logging record. + + Emission depends on filters which may have been added to the handler. + Wrap the actual emission of the record with acquisition/release of + the I/O thread lock. Returns whether the filter passed the record for + emission. + """ + rv = self.filter(record) + if rv: + self.acquire() + try: + self.emit(record) + finally: + self.release() + return rv + + def setFormatter(self, fmt): + """ + Set the formatter for this handler. + """ + self.formatter = fmt + + def flush(self): + """ + Ensure all logging output has been flushed. + + This version does nothing and is intended to be implemented by + subclasses. + """ + pass + + def close(self): + """ + Tidy up any resources used by the handler. + + This version does removes the handler from an internal list + of handlers which is closed when shutdown() is called. Subclasses + should ensure that this gets called from overridden close() + methods. + """ + #get the module data lock, as we're updating a shared structure. + _acquireLock() + try: #unlikely to raise an exception, but you never know... + del _handlers[self] + finally: + _releaseLock() + + def handleError(self, record): + """ + Handle errors which occur during an emit() call. + + This method should be called from handlers when an exception is + encountered during an emit() call. If raiseExceptions is false, + exceptions get silently ignored. This is what is mostly wanted + for a logging system - most users will not care about errors in + the logging system, they are more interested in application errors. + You could, however, replace this with a custom handler if you wish. + The record which was being processed is passed in to this method. + """ + if raiseExceptions: + import traceback + ei = sys.exc_info() + traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) + del ei + +class StreamHandler(Handler): + """ + A handler class which writes logging records, appropriately formatted, + to a stream. Note that this class does not close the stream, as + sys.stdout or sys.stderr may be used. + """ + def __init__(self, strm=None): + """ + Initialize the handler. + + If strm is not specified, sys.stderr is used. + """ + Handler.__init__(self) + if not strm: + strm = sys.stderr + self.stream = strm + self.formatter = None + + def flush(self): + """ + Flushes the stream. + """ + self.stream.flush() + + def emit(self, record): + """ + Emit a record. + + If a formatter is specified, it is used to format the record. + The record is then written to the stream with a trailing newline + [N.B. this may be removed depending on feedback]. If exception + information is present, it is formatted using + traceback.print_exception and appended to the stream. + """ + try: + msg = self.format(record) + if not hasattr(types, "UnicodeType"): #if no unicode support... + self.stream.write("%s\n" % msg) + else: + try: + self.stream.write("%s\n" % msg) + except UnicodeError: + self.stream.write("%s\n" % msg.encode("UTF-8")) + self.flush() + except: + self.handleError(record) + +class FileHandler(StreamHandler): + """ + A handler class which writes formatted logging records to disk files. + """ + def __init__(self, filename, mode="a"): + """ + Open the specified file and use it as the stream for logging. + """ + StreamHandler.__init__(self, open(filename, mode)) + self.baseFilename = filename + self.mode = mode + + def close(self): + """ + Closes the stream. + """ + self.flush() + self.stream.close() + StreamHandler.close(self) + +#--------------------------------------------------------------------------- +# Manager classes and functions +#--------------------------------------------------------------------------- + +class PlaceHolder: + """ + PlaceHolder instances are used in the Manager logger hierarchy to take + the place of nodes for which no loggers have been defined. This class is + intended for internal use only and not as part of the public API. + """ + def __init__(self, alogger): + """ + Initialize with the specified logger being a child of this placeholder. + """ + self.loggers = [alogger] + + def append(self, alogger): + """ + Add the specified logger as a child of this placeholder. + """ + if alogger not in self.loggers: + self.loggers.append(alogger) + +# +# Determine which class to use when instantiating loggers. +# +_loggerClass = None + +def setLoggerClass(klass): + """ + Set the class to be used when instantiating a logger. The class should + define __init__() such that only a name argument is required, and the + __init__() should call Logger.__init__() + """ + if klass != Logger: + if not issubclass(klass, Logger): + raise TypeError, "logger not derived from logging.Logger: " + \ + klass.__name__ + global _loggerClass + _loggerClass = klass + +class Manager: + """ + There is [under normal circumstances] just one Manager instance, which + holds the hierarchy of loggers. + """ + def __init__(self, rootnode): + """ + Initialize the manager with the root node of the logger hierarchy. + """ + self.root = rootnode + self.disable = 0 + self.emittedNoHandlerWarning = 0 + self.loggerDict = {} + + def getLogger(self, name): + """ + Get a logger with the specified name (channel name), creating it + if it doesn't yet exist. + + If a PlaceHolder existed for the specified name [i.e. the logger + didn't exist but a child of it did], replace it with the created + logger and fix up the parent/child references which pointed to the + placeholder to now point to the logger. + """ + rv = None + _acquireLock() + try: + if self.loggerDict.has_key(name): + rv = self.loggerDict[name] + if isinstance(rv, PlaceHolder): + ph = rv + rv = _loggerClass(name) + rv.manager = self + self.loggerDict[name] = rv + self._fixupChildren(ph, rv) + self._fixupParents(rv) + else: + rv = _loggerClass(name) + rv.manager = self + self.loggerDict[name] = rv + self._fixupParents(rv) + finally: + _releaseLock() + return rv + + def _fixupParents(self, alogger): + """ + Ensure that there are either loggers or placeholders all the way + from the specified logger to the root of the logger hierarchy. + """ + name = alogger.name + i = string.rfind(name, ".") + rv = None + while (i > 0) and not rv: + substr = name[:i] + if not self.loggerDict.has_key(substr): + self.loggerDict[substr] = PlaceHolder(alogger) + else: + obj = self.loggerDict[substr] + if isinstance(obj, Logger): + rv = obj + else: + assert isinstance(obj, PlaceHolder) + obj.append(alogger) + i = string.rfind(name, ".", 0, i - 1) + if not rv: + rv = self.root + alogger.parent = rv + + def _fixupChildren(self, ph, alogger): + """ + Ensure that children of the placeholder ph are connected to the + specified logger. + """ + for c in ph.loggers: + if string.find(c.parent.name, alogger.name) <> 0: + alogger.parent = c.parent + c.parent = alogger + +#--------------------------------------------------------------------------- +# Logger classes and functions +#--------------------------------------------------------------------------- + +class Logger(Filterer): + """ + Instances of the Logger class represent a single logging channel. A + "logging channel" indicates an area of an application. Exactly how an + "area" is defined is up to the application developer. Since an + application can have any number of areas, logging channels are identified + by a unique string. Application areas can be nested (e.g. an area + of "input processing" might include sub-areas "read CSV files", "read + XLS files" and "read Gnumeric files"). To cater for this natural nesting, + channel names are organized into a namespace hierarchy where levels are + separated by periods, much like the Java or Python package namespace. So + in the instance given above, channel names might be "input" for the upper + level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. + There is no arbitrary limit to the depth of nesting. + """ + def __init__(self, name, level=NOTSET): + """ + Initialize the logger with a name and an optional level. + """ + Filterer.__init__(self) + self.name = name + self.level = level + self.parent = None + self.propagate = 1 + self.handlers = [] + self.disabled = 0 + + def setLevel(self, level): + """ + Set the logging level of this logger. + """ + self.level = level + +# def getRoot(self): +# """ +# Get the root of the logger hierarchy. +# """ +# return Logger.root + + def debug(self, msg, *args, **kwargs): + """ + Log 'msg % args' with severity 'DEBUG'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) + """ + if self.manager.disable >= DEBUG: + return + if DEBUG >= self.getEffectiveLevel(): + apply(self._log, (DEBUG, msg, args), kwargs) + + def info(self, msg, *args, **kwargs): + """ + Log 'msg % args' with severity 'INFO'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.info("Houston, we have a %s", "interesting problem", exc_info=1) + """ + if self.manager.disable >= INFO: + return + if INFO >= self.getEffectiveLevel(): + apply(self._log, (INFO, msg, args), kwargs) + + def warning(self, msg, *args, **kwargs): + """ + Log 'msg % args' with severity 'WARNING'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1) + """ + if self.manager.disable >= WARNING: + return + if self.isEnabledFor(WARNING): + apply(self._log, (WARNING, msg, args), kwargs) + + warn = warning + + def error(self, msg, *args, **kwargs): + """ + Log 'msg % args' with severity 'ERROR'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.error("Houston, we have a %s", "major problem", exc_info=1) + """ + if self.manager.disable >= ERROR: + return + if self.isEnabledFor(ERROR): + apply(self._log, (ERROR, msg, args), kwargs) + + def exception(self, msg, *args): + """ + Convenience method for logging an ERROR with exception information. + """ + apply(self.error, (msg,) + args, {'exc_info': 1}) + + def critical(self, msg, *args, **kwargs): + """ + Log 'msg % args' with severity 'CRITICAL'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.critical("Houston, we have a %s", "major disaster", exc_info=1) + """ + if self.manager.disable >= CRITICAL: + return + if CRITICAL >= self.getEffectiveLevel(): + apply(self._log, (CRITICAL, msg, args), kwargs) + + fatal = critical + + def log(self, level, msg, *args, **kwargs): + """ + Log 'msg % args' with the severity 'level'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.log(level, "We have a %s", "mysterious problem", exc_info=1) + """ + if self.manager.disable >= level: + return + if self.isEnabledFor(level): + apply(self._log, (level, msg, args), kwargs) + + def findCaller(self): + """ + Find the stack frame of the caller so that we can note the source + file name and line number. + """ + f = sys._getframe(1) + while 1: + co = f.f_code + filename = os.path.normcase(co.co_filename) + if filename == _srcfile: + f = f.f_back + continue + return filename, f.f_lineno + + def makeRecord(self, name, level, fn, lno, msg, args, exc_info): + """ + A factory method which can be overridden in subclasses to create + specialized LogRecords. + """ + return LogRecord(name, level, fn, lno, msg, args, exc_info) + + def _log(self, level, msg, args, exc_info=None): + """ + Low-level logging routine which creates a LogRecord and then calls + all the handlers of this logger to handle the record. + """ + if _srcfile: + fn, lno = self.findCaller() + else: + fn, lno = "", 0 + if exc_info: + if type(exc_info) != types.TupleType: + exc_info = sys.exc_info() + record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info) + self.handle(record) + + def handle(self, record): + """ + Call the handlers for the specified record. + + This method is used for unpickled records received from a socket, as + well as those created locally. Logger-level filtering is applied. + """ + if (not self.disabled) and self.filter(record): + self.callHandlers(record) + + def addHandler(self, hdlr): + """ + Add the specified handler to this logger. + """ + if not (hdlr in self.handlers): + self.handlers.append(hdlr) + + def removeHandler(self, hdlr): + """ + Remove the specified handler from this logger. + """ + if hdlr in self.handlers: + #hdlr.close() + self.handlers.remove(hdlr) + + def callHandlers(self, record): + """ + Pass a record to all relevant handlers. + + Loop through all handlers for this logger and its parents in the + logger hierarchy. If no handler was found, output a one-off error + message to sys.stderr. Stop searching up the hierarchy whenever a + logger with the "propagate" attribute set to zero is found - that + will be the last logger whose handlers are called. + """ + c = self + found = 0 + while c: + for hdlr in c.handlers: + found = found + 1 + if record.levelno >= hdlr.level: + hdlr.handle(record) + if not c.propagate: + c = None #break out + else: + c = c.parent + if (found == 0) and not self.manager.emittedNoHandlerWarning: + sys.stderr.write("No handlers could be found for logger" + " \"%s\"\n" % self.name) + self.manager.emittedNoHandlerWarning = 1 + + def getEffectiveLevel(self): + """ + Get the effective level for this logger. + + Loop through this logger and its parents in the logger hierarchy, + looking for a non-zero logging level. Return the first one found. + """ + logger = self + while logger: + if logger.level: + return logger.level + logger = logger.parent + return NOTSET + + def isEnabledFor(self, level): + """ + Is this logger enabled for level 'level'? + """ + if self.manager.disable >= level: + return 0 + return level >= self.getEffectiveLevel() + +class RootLogger(Logger): + """ + A root logger is not that different to any other logger, except that + it must have a logging level and there is only one instance of it in + the hierarchy. + """ + def __init__(self, level): + """ + Initialize the logger with the name "root". + """ + Logger.__init__(self, "root", level) + +_loggerClass = Logger + +root = RootLogger(WARNING) +Logger.root = root +Logger.manager = Manager(Logger.root) + +#--------------------------------------------------------------------------- +# Configuration classes and functions +#--------------------------------------------------------------------------- + +BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s" + +def basicConfig(): + """ + Do basic configuration for the logging system by creating a + StreamHandler with a default Formatter and adding it to the + root logger. + """ + if len(root.handlers) == 0: + hdlr = StreamHandler() + fmt = Formatter(BASIC_FORMAT) + hdlr.setFormatter(fmt) + root.addHandler(hdlr) + +#--------------------------------------------------------------------------- +# Utility functions at module level. +# Basically delegate everything to the root logger. +#--------------------------------------------------------------------------- + +def getLogger(name=None): + """ + Return a logger with the specified name, creating it if necessary. + + If no name is specified, return the root logger. + """ + if name: + return Logger.manager.getLogger(name) + else: + return root + +#def getRootLogger(): +# """ +# Return the root logger. +# +# Note that getLogger('') now does the same thing, so this function is +# deprecated and may disappear in the future. +# """ +# return root + +def critical(msg, *args, **kwargs): + """ + Log a message with severity 'CRITICAL' on the root logger. + """ + if len(root.handlers) == 0: + basicConfig() + apply(root.critical, (msg,)+args, kwargs) + +fatal = critical + +def error(msg, *args, **kwargs): + """ + Log a message with severity 'ERROR' on the root logger. + """ + if len(root.handlers) == 0: + basicConfig() + apply(root.error, (msg,)+args, kwargs) + +def exception(msg, *args): + """ + Log a message with severity 'ERROR' on the root logger, + with exception information. + """ + apply(error, (msg,)+args, {'exc_info': 1}) + +def warning(msg, *args, **kwargs): + """ + Log a message with severity 'WARNING' on the root logger. + """ + if len(root.handlers) == 0: + basicConfig() + apply(root.warning, (msg,)+args, kwargs) + +warn = warning + +def info(msg, *args, **kwargs): + """ + Log a message with severity 'INFO' on the root logger. + """ + if len(root.handlers) == 0: + basicConfig() + apply(root.info, (msg,)+args, kwargs) + +def debug(msg, *args, **kwargs): + """ + Log a message with severity 'DEBUG' on the root logger. + """ + if len(root.handlers) == 0: + basicConfig() + apply(root.debug, (msg,)+args, kwargs) + +def disable(level): + """ + Disable all logging calls less severe than 'level'. + """ + root.manager.disable = level + +def shutdown(): + """ + Perform any cleanup actions in the logging system (e.g. flushing + buffers). + + Should be called at application exit. + """ + for h in _handlers.keys(): + h.flush() + h.close() + +#Let's try and shutdown automatically on application exit... +try: + import atexit + atexit.register(shutdown) +except ImportError: # for Python versions < 2.0 + def exithook(status, old_exit=sys.exit): + try: + shutdown() + finally: + old_exit(status) + + sys.exit = exithook diff --git a/tools/python/logging/logging-0.4.9.2/logging/config.py b/tools/python/logging/logging-0.4.9.2/logging/config.py new file mode 100644 index 0000000..c9ffab0 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/logging/config.py @@ -0,0 +1,301 @@ +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +Configuration functions for the logging package for Python. The core package +is based on PEP 282 and comments thereto in comp.lang.python, and influenced +by Apache's log4j system. + +Should work under Python versions >= 1.5.2, except that source line +information is not available unless 'sys._getframe()' is. + +Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. + +To use, simply 'import logging' and log away! +""" + +import sys, logging, logging.handlers, string, thread, threading, socket, struct, os + +from SocketServer import ThreadingTCPServer, StreamRequestHandler + + +DEFAULT_LOGGING_CONFIG_PORT = 9030 + +if sys.platform == "win32": + RESET_ERROR = 10054 #WSAECONNRESET +else: + RESET_ERROR = 104 #ECONNRESET + +# +# The following code implements a socket listener for on-the-fly +# reconfiguration of logging. +# +# _listener holds the server object doing the listening +_listener = None + +def fileConfig(fname, defaults=None): + """ + Read the logging configuration from a ConfigParser-format file. + + This can be called several times from an application, allowing an end user + the ability to select from various pre-canned configurations (if the + developer provides a mechanism to present the choices and load the chosen + configuration). + In versions of ConfigParser which have the readfp method [typically + shipped in 2.x versions of Python], you can pass in a file-like object + rather than a filename, in which case the file-like object will be read + using readfp. + """ + import ConfigParser + + cp = ConfigParser.ConfigParser(defaults) + if hasattr(cp, 'readfp') and hasattr(fname, 'readline'): + cp.readfp(fname) + else: + cp.read(fname) + #first, do the formatters... + flist = cp.get("formatters", "keys") + if len(flist): + flist = string.split(flist, ",") + formatters = {} + for form in flist: + sectname = "formatter_%s" % form + opts = cp.options(sectname) + if "format" in opts: + fs = cp.get(sectname, "format", 1) + else: + fs = None + if "datefmt" in opts: + dfs = cp.get(sectname, "datefmt", 1) + else: + dfs = None + f = logging.Formatter(fs, dfs) + formatters[form] = f + #next, do the handlers... + #critical section... + logging._acquireLock() + try: + try: + #first, lose the existing handlers... + logging._handlers.clear() + #now set up the new ones... + hlist = cp.get("handlers", "keys") + if len(hlist): + hlist = string.split(hlist, ",") + handlers = {} + fixups = [] #for inter-handler references + for hand in hlist: + sectname = "handler_%s" % hand + klass = cp.get(sectname, "class") + opts = cp.options(sectname) + if "formatter" in opts: + fmt = cp.get(sectname, "formatter") + else: + fmt = "" + klass = eval(klass, vars(logging)) + args = cp.get(sectname, "args") + args = eval(args, vars(logging)) + h = apply(klass, args) + if "level" in opts: + level = cp.get(sectname, "level") + h.setLevel(logging._levelNames[level]) + if len(fmt): + h.setFormatter(formatters[fmt]) + #temporary hack for FileHandler and MemoryHandler. + if klass == logging.handlers.MemoryHandler: + if "target" in opts: + target = cp.get(sectname,"target") + else: + target = "" + if len(target): #the target handler may not be loaded yet, so keep for later... + fixups.append((h, target)) + handlers[hand] = h + #now all handlers are loaded, fixup inter-handler references... + for fixup in fixups: + h = fixup[0] + t = fixup[1] + h.setTarget(handlers[t]) + #at last, the loggers...first the root... + llist = cp.get("loggers", "keys") + llist = string.split(llist, ",") + llist.remove("root") + sectname = "logger_root" + root = logging.root + log = root + opts = cp.options(sectname) + if "level" in opts: + level = cp.get(sectname, "level") + log.setLevel(logging._levelNames[level]) + for h in root.handlers[:]: + root.removeHandler(h) + hlist = cp.get(sectname, "handlers") + if len(hlist): + hlist = string.split(hlist, ",") + for hand in hlist: + log.addHandler(handlers[hand]) + #and now the others... + #we don't want to lose the existing loggers, + #since other threads may have pointers to them. + #existing is set to contain all existing loggers, + #and as we go through the new configuration we + #remove any which are configured. At the end, + #what's left in existing is the set of loggers + #which were in the previous configuration but + #which are not in the new configuration. + existing = root.manager.loggerDict.keys() + #now set up the new ones... + for log in llist: + sectname = "logger_%s" % log + qn = cp.get(sectname, "qualname") + opts = cp.options(sectname) + if "propagate" in opts: + propagate = cp.getint(sectname, "propagate") + else: + propagate = 1 + logger = logging.getLogger(qn) + if qn in existing: + existing.remove(qn) + if "level" in opts: + level = cp.get(sectname, "level") + logger.setLevel(logging._levelNames[level]) + for h in logger.handlers[:]: + logger.removeHandler(h) + logger.propagate = propagate + logger.disabled = 0 + hlist = cp.get(sectname, "handlers") + if len(hlist): + hlist = string.split(hlist, ",") + for hand in hlist: + logger.addHandler(handlers[hand]) + #Disable any old loggers. There's no point deleting + #them as other threads may continue to hold references + #and by disabling them, you stop them doing any logging. + for log in existing: + root.manager.loggerDict[log].disabled = 1 + except: + import traceback + ei = sys.exc_info() + traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) + del ei + finally: + logging._releaseLock() + +def listen(port=DEFAULT_LOGGING_CONFIG_PORT): + """ + Start up a socket server on the specified port, and listen for new + configurations. + + These will be sent as a file suitable for processing by fileConfig(). + Returns a Thread object on which you can call start() to start the server, + and which you can join() when appropriate. To stop the server, call + stopListening(). + """ + if not thread: + raise NotImplementedError, "listen() needs threading to work" + + class ConfigStreamHandler(StreamRequestHandler): + """ + Handler for a logging configuration request. + + It expects a completely new logging configuration and uses fileConfig + to install it. + """ + def handle(self): + """ + Handle a request. + + Each request is expected to be a 4-byte length, + followed by the config file. Uses fileConfig() to do the + grunt work. + """ + import tempfile + try: + conn = self.connection + chunk = conn.recv(4) + if len(chunk) == 4: + slen = struct.unpack(">L", chunk)[0] + chunk = self.connection.recv(slen) + while len(chunk) < slen: + chunk = chunk + conn.recv(slen - len(chunk)) + #Apply new configuration. We'd like to be able to + #create a StringIO and pass that in, but unfortunately + #1.5.2 ConfigParser does not support reading file + #objects, only actual files. So we create a temporary + #file and remove it later. + file = tempfile.mktemp(".ini") + f = open(file, "w") + f.write(chunk) + f.close() + fileConfig(file) + os.remove(file) + except socket.error, e: + if type(e.args) != types.TupleType: + raise + else: + errcode = e.args[0] + if errcode != RESET_ERROR: + raise + + class ConfigSocketReceiver(ThreadingTCPServer): + """ + A simple TCP socket-based logging config receiver. + """ + + allow_reuse_address = 1 + + def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT, + handler=None): + ThreadingTCPServer.__init__(self, (host, port), handler) + logging._acquireLock() + self.abort = 0 + logging._releaseLock() + self.timeout = 1 + + def serve_until_stopped(self): + import select + abort = 0 + while not abort: + rd, wr, ex = select.select([self.socket.fileno()], + [], [], + self.timeout) + if rd: + self.handle_request() + logging._acquireLock() + abort = self.abort + logging._releaseLock() + + def serve(rcvr, hdlr, port): + server = rcvr(port=port, handler=hdlr) + global _listener + logging._acquireLock() + _listener = server + logging._releaseLock() + server.serve_until_stopped() + + return threading.Thread(target=serve, + args=(ConfigSocketReceiver, + ConfigStreamHandler, port)) + +def stopListening(): + """ + Stop the listening server which was created with a call to listen(). + """ + global _listener + if _listener: + logging._acquireLock() + _listener.abort = 1 + _listener = None + logging._releaseLock() diff --git a/tools/python/logging/logging-0.4.9.2/logging/handlers.py b/tools/python/logging/logging-0.4.9.2/logging/handlers.py new file mode 100644 index 0000000..99a543e --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/logging/handlers.py @@ -0,0 +1,787 @@ +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +Additional handlers for the logging package for Python. The core package is +based on PEP 282 and comments thereto in comp.lang.python, and influenced by +Apache's log4j system. + +Should work under Python versions >= 1.5.2, except that source line +information is not available unless 'sys._getframe()' is. + +Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. + +To use, simply 'import logging' and log away! +""" + +import sys, logging, socket, types, os, string, cPickle, struct, time + +from SocketServer import ThreadingTCPServer, StreamRequestHandler + +# +# Some constants... +# + +DEFAULT_TCP_LOGGING_PORT = 9020 +DEFAULT_UDP_LOGGING_PORT = 9021 +DEFAULT_HTTP_LOGGING_PORT = 9022 +DEFAULT_SOAP_LOGGING_PORT = 9023 +SYSLOG_UDP_PORT = 514 + + +class RotatingFileHandler(logging.FileHandler): + def __init__(self, filename, mode="a", maxBytes=0, backupCount=0): + """ + Open the specified file and use it as the stream for logging. + + By default, the file grows indefinitely. You can specify particular + values of maxBytes and backupCount to allow the file to rollover at + a predetermined size. + + Rollover occurs whenever the current log file is nearly maxBytes in + length. If backupCount is >= 1, the system will successively create + new files with the same pathname as the base file, but with extensions + ".1", ".2" etc. appended to it. For example, with a backupCount of 5 + and a base file name of "app.log", you would get "app.log", + "app.log.1", "app.log.2", ... through to "app.log.5". The file being + written to is always "app.log" - when it gets filled up, it is closed + and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc. + exist, then they are renamed to "app.log.2", "app.log.3" etc. + respectively. + + If maxBytes is zero, rollover never occurs. + """ + logging.FileHandler.__init__(self, filename, mode) + self.maxBytes = maxBytes + self.backupCount = backupCount + if maxBytes > 0: + self.mode = "a" + + def doRollover(self): + """ + Do a rollover, as described in __init__(). + """ + + self.stream.close() + if self.backupCount > 0: + for i in range(self.backupCount - 1, 0, -1): + sfn = "%s.%d" % (self.baseFilename, i) + dfn = "%s.%d" % (self.baseFilename, i + 1) + if os.path.exists(sfn): + #print "%s -> %s" % (sfn, dfn) + if os.path.exists(dfn): + os.remove(dfn) + os.rename(sfn, dfn) + dfn = self.baseFilename + ".1" + if os.path.exists(dfn): + os.remove(dfn) + os.rename(self.baseFilename, dfn) + #print "%s -> %s" % (self.baseFilename, dfn) + self.stream = open(self.baseFilename, "w") + + def emit(self, record): + """ + Emit a record. + + Output the record to the file, catering for rollover as described + in doRollover(). + """ + if self.maxBytes > 0: # are we rolling over? + msg = "%s\n" % self.format(record) + self.stream.seek(0, 2) #due to non-posix-compliant Windows feature + if self.stream.tell() + len(msg) >= self.maxBytes: + self.doRollover() + logging.FileHandler.emit(self, record) + + +class SocketHandler(logging.Handler): + """ + A handler class which writes logging records, in pickle format, to + a streaming socket. The socket is kept open across logging calls. + If the peer resets it, an attempt is made to reconnect on the next call. + The pickle which is sent is that of the LogRecord's attribute dictionary + (__dict__), so that the receiver does not need to have the logging module + installed in order to process the logging event. + + To unpickle the record at the receiving end into a LogRecord, use the + makeLogRecord function. + """ + + def __init__(self, host, port): + """ + Initializes the handler with a specific host address and port. + + The attribute 'closeOnError' is set to 1 - which means that if + a socket error occurs, the socket is silently closed and then + reopened on the next logging call. + """ + logging.Handler.__init__(self) + self.host = host + self.port = port + self.sock = None + self.closeOnError = 0 + self.retryTime = None + # + # Exponential backoff parameters. + # + self.retryStart = 1.0 + self.retryMax = 30.0 + self.retryFactor = 2.0 + + def makeSocket(self): + """ + A factory method which allows subclasses to define the precise + type of socket they want. + """ + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((self.host, self.port)) + return s + + def createSocket(self): + """ + Try to create a socket, using an exponential backoff with + a max retry time. Thanks to Robert Olson for the original patch + (SF #815911) which has been slightly refactored. + """ + now = time.time() + # Either retryTime is None, in which case this + # is the first time back after a disconnect, or + # we've waited long enough. + if self.retryTime is None: + attempt = 1 + else: + attempt = (now >= self.retryTime) + if attempt: + try: + self.sock = self.makeSocket() + self.retryTime = None # next time, no delay before trying + except: + #Creation failed, so set the retry time and return. + if self.retryTime is None: + self.retryPeriod = self.retryStart + else: + self.retryPeriod = self.retryPeriod * self.retryFactor + if self.retryPeriod > self.retryMax: + self.retryPeriod = self.retryMax + self.retryTime = now + self.retryPeriod + + def send(self, s): + """ + Send a pickled string to the socket. + + This function allows for partial sends which can happen when the + network is busy. + """ + if self.sock is None: + self.createSocket() + #self.sock can be None either because we haven't reached the retry + #time yet, or because we have reached the retry time and retried, + #but are still unable to connect. + if self.sock: + try: + if hasattr(self.sock, "sendall"): + self.sock.sendall(s) + else: + sentsofar = 0 + left = len(s) + while left > 0: + sent = self.sock.send(s[sentsofar:]) + sentsofar = sentsofar + sent + left = left - sent + except socket.error: + self.sock.close() + self.sock = None # so we can call createSocket next time + + def makePickle(self, record): + """ + Pickles the record in binary format with a length prefix, and + returns it ready for transmission across the socket. + """ + ei = record.exc_info + if ei: + dummy = self.format(record) # just to get traceback text into record.exc_text + record.exc_info = None # to avoid Unpickleable error + s = cPickle.dumps(record.__dict__, 1) + if ei: + record.exc_info = ei # for next handler + slen = struct.pack(">L", len(s)) + return slen + s + + def handleError(self, record): + """ + Handle an error during logging. + + An error has occurred during logging. Most likely cause - + connection lost. Close the socket so that we can retry on the + next event. + """ + if self.closeOnError and self.sock: + self.sock.close() + self.sock = None #try to reconnect next time + else: + logging.Handler.handleError(self, record) + + def emit(self, record): + """ + Emit a record. + + Pickles the record and writes it to the socket in binary format. + If there is an error with the socket, silently drop the packet. + If there was a problem with the socket, re-establishes the + socket. + """ + try: + s = self.makePickle(record) + self.send(s) + except: + self.handleError(record) + + def close(self): + """ + Closes the socket. + """ + if self.sock: + self.sock.close() + self.sock = None + logging.Handler.close(self) + +class DatagramHandler(SocketHandler): + """ + A handler class which writes logging records, in pickle format, to + a datagram socket. The pickle which is sent is that of the LogRecord's + attribute dictionary (__dict__), so that the receiver does not need to + have the logging module installed in order to process the logging event. + + To unpickle the record at the receiving end into a LogRecord, use the + makeLogRecord function. + + """ + def __init__(self, host, port): + """ + Initializes the handler with a specific host address and port. + """ + SocketHandler.__init__(self, host, port) + self.closeOnError = 0 + + def makeSocket(self): + """ + The factory method of SocketHandler is here overridden to create + a UDP socket (SOCK_DGRAM). + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + return s + + def send(self, s): + """ + Send a pickled string to a socket. + + This function no longer allows for partial sends which can happen + when the network is busy - UDP does not guarantee delivery and + can deliver packets out of sequence. + """ + self.sock.sendto(s, (self.host, self.port)) + +class SysLogHandler(logging.Handler): + """ + A handler class which sends formatted logging records to a syslog + server. Based on Sam Rushing's syslog module: + http://www.nightmare.com/squirl/python-ext/misc/syslog.py + Contributed by Nicolas Untz (after which minor refactoring changes + have been made). + """ + + # from : + # ====================================================================== + # priorities/facilities are encoded into a single 32-bit quantity, where + # the bottom 3 bits are the priority (0-7) and the top 28 bits are the + # facility (0-big number). Both the priorities and the facilities map + # roughly one-to-one to strings in the syslogd(8) source code. This + # mapping is included in this file. + # + # priorities (these are ordered) + + LOG_EMERG = 0 # system is unusable + LOG_ALERT = 1 # action must be taken immediately + LOG_CRIT = 2 # critical conditions + LOG_ERR = 3 # error conditions + LOG_WARNING = 4 # warning conditions + LOG_NOTICE = 5 # normal but significant condition + LOG_INFO = 6 # informational + LOG_DEBUG = 7 # debug-level messages + + # facility codes + LOG_KERN = 0 # kernel messages + LOG_USER = 1 # random user-level messages + LOG_MAIL = 2 # mail system + LOG_DAEMON = 3 # system daemons + LOG_AUTH = 4 # security/authorization messages + LOG_SYSLOG = 5 # messages generated internally by syslogd + LOG_LPR = 6 # line printer subsystem + LOG_NEWS = 7 # network news subsystem + LOG_UUCP = 8 # UUCP subsystem + LOG_CRON = 9 # clock daemon + LOG_AUTHPRIV = 10 # security/authorization messages (private) + + # other codes through 15 reserved for system use + LOG_LOCAL0 = 16 # reserved for local use + LOG_LOCAL1 = 17 # reserved for local use + LOG_LOCAL2 = 18 # reserved for local use + LOG_LOCAL3 = 19 # reserved for local use + LOG_LOCAL4 = 20 # reserved for local use + LOG_LOCAL5 = 21 # reserved for local use + LOG_LOCAL6 = 22 # reserved for local use + LOG_LOCAL7 = 23 # reserved for local use + + priority_names = { + "alert": LOG_ALERT, + "crit": LOG_CRIT, + "critical": LOG_CRIT, + "debug": LOG_DEBUG, + "emerg": LOG_EMERG, + "err": LOG_ERR, + "error": LOG_ERR, # DEPRECATED + "info": LOG_INFO, + "notice": LOG_NOTICE, + "panic": LOG_EMERG, # DEPRECATED + "warn": LOG_WARNING, # DEPRECATED + "warning": LOG_WARNING, + } + + facility_names = { + "auth": LOG_AUTH, + "authpriv": LOG_AUTHPRIV, + "cron": LOG_CRON, + "daemon": LOG_DAEMON, + "kern": LOG_KERN, + "lpr": LOG_LPR, + "mail": LOG_MAIL, + "news": LOG_NEWS, + "security": LOG_AUTH, # DEPRECATED + "syslog": LOG_SYSLOG, + "user": LOG_USER, + "uucp": LOG_UUCP, + "local0": LOG_LOCAL0, + "local1": LOG_LOCAL1, + "local2": LOG_LOCAL2, + "local3": LOG_LOCAL3, + "local4": LOG_LOCAL4, + "local5": LOG_LOCAL5, + "local6": LOG_LOCAL6, + "local7": LOG_LOCAL7, + } + + def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER): + """ + Initialize a handler. + + If address is specified as a string, UNIX socket is used. + If facility is not specified, LOG_USER is used. + """ + logging.Handler.__init__(self) + + self.address = address + self.facility = facility + if type(address) == types.StringType: + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + # syslog may require either DGRAM or STREAM sockets + try: + self.socket.connect(address) + except socket.error: + self.socket.close() + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.connect(address) + self.unixsocket = 1 + else: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.unixsocket = 0 + + self.formatter = None + + # curious: when talking to the unix-domain '/dev/log' socket, a + # zero-terminator seems to be required. this string is placed + # into a class variable so that it can be overridden if + # necessary. + log_format_string = '<%d>%s\000' + + def encodePriority (self, facility, priority): + """ + Encode the facility and priority. You can pass in strings or + integers - if strings are passed, the facility_names and + priority_names mapping dictionaries are used to convert them to + integers. + """ + if type(facility) == types.StringType: + facility = self.facility_names[facility] + if type(priority) == types.StringType: + priority = self.priority_names[priority] + return (facility << 3) | priority + + def close (self): + """ + Closes the socket. + """ + if self.unixsocket: + self.socket.close() + logging.Handler.close(self) + + def emit(self, record): + """ + Emit a record. + + The record is formatted, and then sent to the syslog server. If + exception information is present, it is NOT sent to the server. + """ + msg = self.format(record) + """ + We need to convert record level to lowercase, maybe this will + change in the future. + """ + msg = self.log_format_string % ( + self.encodePriority(self.facility, + string.lower(record.levelname)), + msg) + try: + if self.unixsocket: + self.socket.send(msg) + else: + self.socket.sendto(msg, self.address) + except: + self.handleError(record) + +class SMTPHandler(logging.Handler): + """ + A handler class which sends an SMTP email for each logging event. + """ + def __init__(self, mailhost, fromaddr, toaddrs, subject): + """ + Initialize the handler. + + Initialize the instance with the from and to addresses and subject + line of the email. To specify a non-standard SMTP port, use the + (host, port) tuple format for the mailhost argument. + """ + logging.Handler.__init__(self) + if type(mailhost) == types.TupleType: + host, port = mailhost + self.mailhost = host + self.mailport = port + else: + self.mailhost = mailhost + self.mailport = None + self.fromaddr = fromaddr + if type(toaddrs) == types.StringType: + toaddrs = [toaddrs] + self.toaddrs = toaddrs + self.subject = subject + + def getSubject(self, record): + """ + Determine the subject for the email. + + If you want to specify a subject line which is record-dependent, + override this method. + """ + return self.subject + + weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + + monthname = [None, + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + + def date_time(self): + """Return the current date and time formatted for a MIME header.""" + year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time()) + s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + self.weekdayname[wd], + day, self.monthname[month], year, + hh, mm, ss) + return s + + def emit(self, record): + """ + Emit a record. + + Format the record and send it to the specified addressees. + """ + try: + import smtplib + port = self.mailport + if not port: + port = smtplib.SMTP_PORT + smtp = smtplib.SMTP(self.mailhost, port) + msg = self.format(record) + msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % ( + self.fromaddr, + string.join(self.toaddrs, ","), + self.getSubject(record), + self.date_time(), msg) + smtp.sendmail(self.fromaddr, self.toaddrs, msg) + smtp.quit() + except: + self.handleError(record) + +class NTEventLogHandler(logging.Handler): + """ + A handler class which sends events to the NT Event Log. Adds a + registry entry for the specified application name. If no dllname is + provided, win32service.pyd (which contains some basic message + placeholders) is used. Note that use of these placeholders will make + your event logs big, as the entire message source is held in the log. + If you want slimmer logs, you have to pass in the name of your own DLL + which contains the message definitions you want to use in the event log. + """ + def __init__(self, appname, dllname=None, logtype="Application"): + logging.Handler.__init__(self) + try: + import win32evtlogutil, win32evtlog + self.appname = appname + self._welu = win32evtlogutil + if not dllname: + dllname = os.path.split(self._welu.__file__) + dllname = os.path.split(dllname[0]) + dllname = os.path.join(dllname[0], r'win32service.pyd') + self.dllname = dllname + self.logtype = logtype + self._welu.AddSourceToRegistry(appname, dllname, logtype) + self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE + self.typemap = { + logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE, + logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE, + logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE, + logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE, + logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE, + } + except ImportError: + print "The Python Win32 extensions for NT (service, event "\ + "logging) appear not to be available." + self._welu = None + + def getMessageID(self, record): + """ + Return the message ID for the event record. If you are using your + own messages, you could do this by having the msg passed to the + logger being an ID rather than a formatting string. Then, in here, + you could use a dictionary lookup to get the message ID. This + version returns 1, which is the base message ID in win32service.pyd. + """ + return 1 + + def getEventCategory(self, record): + """ + Return the event category for the record. + + Override this if you want to specify your own categories. This version + returns 0. + """ + return 0 + + def getEventType(self, record): + """ + Return the event type for the record. + + Override this if you want to specify your own types. This version does + a mapping using the handler's typemap attribute, which is set up in + __init__() to a dictionary which contains mappings for DEBUG, INFO, + WARNING, ERROR and CRITICAL. If you are using your own levels you will + either need to override this method or place a suitable dictionary in + the handler's typemap attribute. + """ + return self.typemap.get(record.levelno, self.deftype) + + def emit(self, record): + """ + Emit a record. + + Determine the message ID, event category and event type. Then + log the message in the NT event log. + """ + if self._welu: + try: + id = self.getMessageID(record) + cat = self.getEventCategory(record) + type = self.getEventType(record) + msg = self.format(record) + self._welu.ReportEvent(self.appname, id, cat, type, [msg]) + except: + self.handleError(record) + + def close(self): + """ + Clean up this handler. + + You can remove the application name from the registry as a + source of event log entries. However, if you do this, you will + not be able to see the events as you intended in the Event Log + Viewer - it needs to be able to access the registry to get the + DLL name. + """ + #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype) + logging.Handler.close(self) + +class HTTPHandler(logging.Handler): + """ + A class which sends records to a Web server, using either GET or + POST semantics. + """ + def __init__(self, host, url, method="GET"): + """ + Initialize the instance with the host, the request URL, and the method + ("GET" or "POST") + """ + logging.Handler.__init__(self) + method = string.upper(method) + if method not in ["GET", "POST"]: + raise ValueError, "method must be GET or POST" + self.host = host + self.url = url + self.method = method + + def mapLogRecord(self, record): + """ + Default implementation of mapping the log record into a dict + that is sent as the CGI data. Overwrite in your class. + Contributed by Franz Glasner. + """ + return record.__dict__ + + def emit(self, record): + """ + Emit a record. + + Send the record to the Web server as an URL-encoded dictionary + """ + try: + import httplib, urllib + h = httplib.HTTP(self.host) + url = self.url + data = urllib.urlencode(self.mapLogRecord(record)) + if self.method == "GET": + if (string.find(url, '?') >= 0): + sep = '&' + else: + sep = '?' + url = url + "%c%s" % (sep, data) + h.putrequest(self.method, url) + if self.method == "POST": + h.putheader("Content-length", str(len(data))) + h.endheaders() + if self.method == "POST": + h.send(data) + h.getreply() #can't do anything with the result + except: + self.handleError(record) + +class BufferingHandler(logging.Handler): + """ + A handler class which buffers logging records in memory. Whenever each + record is added to the buffer, a check is made to see if the buffer should + be flushed. If it should, then flush() is expected to do what's needed. + """ + def __init__(self, capacity): + """ + Initialize the handler with the buffer size. + """ + logging.Handler.__init__(self) + self.capacity = capacity + self.buffer = [] + + def shouldFlush(self, record): + """ + Should the handler flush its buffer? + + Returns true if the buffer is up to capacity. This method can be + overridden to implement custom flushing strategies. + """ + return (len(self.buffer) >= self.capacity) + + def emit(self, record): + """ + Emit a record. + + Append the record. If shouldFlush() tells us to, call flush() to process + the buffer. + """ + self.buffer.append(record) + if self.shouldFlush(record): + self.flush() + + def flush(self): + """ + Override to implement custom flushing behaviour. + + This version just zaps the buffer to empty. + """ + self.buffer = [] + + def close(self): + """ + Close the handler. + + This version just flushes and chains to the parent class' close(). + """ + self.flush() + logging.Handler.close(self) + +class MemoryHandler(BufferingHandler): + """ + A handler class which buffers logging records in memory, periodically + flushing them to a target handler. Flushing occurs whenever the buffer + is full, or when an event of a certain severity or greater is seen. + """ + def __init__(self, capacity, flushLevel=logging.ERROR, target=None): + """ + Initialize the handler with the buffer size, the level at which + flushing should occur and an optional target. + + Note that without a target being set either here or via setTarget(), + a MemoryHandler is no use to anyone! + """ + BufferingHandler.__init__(self, capacity) + self.flushLevel = flushLevel + self.target = target + + def shouldFlush(self, record): + """ + Check for buffer full or a record at the flushLevel or higher. + """ + return (len(self.buffer) >= self.capacity) or \ + (record.levelno >= self.flushLevel) + + def setTarget(self, target): + """ + Set the target handler for this handler. + """ + self.target = target + + def flush(self): + """ + For a MemoryHandler, flushing means just sending the buffered + records to the target, if there is one. Override if you want + different behaviour. + """ + if self.target: + for record in self.buffer: + self.target.handle(record) + self.buffer = [] + + def close(self): + """ + Flush, set the target to None and lose the buffer. + """ + self.flush() + self.target = None + BufferingHandler.close(self) diff --git a/tools/python/logging/logging-0.4.9.2/python_logging.html b/tools/python/logging/logging-0.4.9.2/python_logging.html new file mode 100644 index 0000000..b759ef9 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/python_logging.html @@ -0,0 +1,1183 @@ + + + + + + + + + + + +A Logging System for Python + + + + + + + + + + + + +
A Logging System for PythonHome
+ Download
+ Copyright & License
+ Recent Changes
+
"Oh, I'm a lumberjack and I'm okay..." (Monty Python, The Lumberjack Song)
+ +

Table of Contents

+
Abstract
+Motivation
+Influences
+A Simple Example
+Control Flow
+Levels
+Loggers
+Handlers
+Formatters
+Filters
+Configuration
+The GUI Configurator
+Case Scenarios
+Thread Safety
+On-The-Fly Reconfiguration
+Module-Level Convenience Functions
+Performance
+Implementation Status
+Acknowledgements
+Still To Do
+Download and Installation
+Change History
+Copyright and License
+ +

Abstract

+ +

There is a need for a standard logging system in Python, as comprehensively documented +in PEP 282 and +enthusiastically endorsed by the BDFL in the Parade of the PEPs. By a happy +coincidence, the package described here was already in development and fairly close in +intent and design to the description in the aforementioned PEP, borrowing as it did +heavily from JSR-47 (now JDK 1.4's java.util.logging package) and log4j. This page describes it in more detail. +As I have tweaked the package to meet comments on PEP 282, I have structured this page in +the same way as the original PEP.

+ +

Motivation

+ +

The Python community has been incredibly helpful to me, a relative newcomer to the +language. Python and its community has certainly saved me much time and effort, and it +seems appropriate to give something back to the community by offering up this package for +people to try. Any feedback will be gratefully accepted.

+ +

Influences

+ +

This package owes its greatest debt to Apache log4j. Due notice was also taken of log4j's +comprehensive critique of +JSR47. This package bears a close resemblance to log4j, but is not a close translation +(as, for example, log4p appears to be). I have +attempted to be more minimalist (and hopefully more Pythonic) in my approach. You be the +judge!

+ +

A Simple Example

+ +

Using the package doesn't get much simpler. It is packaged as a standard Python package called (unsurprisingly) logging. You just need to import logging and you're ready to go. Minimal example:

+ +
+# --- app.py --------------------------------------------------------------------
+import logging
+
+logging.warn("Hello")
+logging.error("Still here...")
+logging.warn("Goodbye")
+
+ +

When you run app.py, the results are:

+ +
+WARN:root:Hello
+ERROR:root:Still here...
+WARN:root:Goodbye
+
+ +

Don't worry about the format of the output - it's all configurable. Here's a slightly +more involved example; if you've just looked at PEP 282 you will probably get a feeling of +dejà vu. (This is intentional.)

+ +
+# --- mymodule.py --------------------------------------------------------------------
+import logging
+log = logging.getLogger("MyModule")
+
+def doIt():
+    log.debug("doin' stuff")
+    #do stuff...but suppose an error occurs?
+    raise TypeError, "bogus type error for testing"
+
+# --- myapp.py -----------------------------------------------------------------------
+import logging, mymodule
+
+logging.basicConfig()
+
+log = logging.getLogger("MyApp")
+log.setLevel(logging.DEBUG) #set verbosity to show all messages of severity >= DEBUG
+log.info("Starting my app")
+try:
+    mymodule.doIt()
+except Exception, e:
+    log.exception("There was a problem.")
+log.info("Ending my app")
+
+ +

When you run myapp.py, the results are:

+ +
+INFO:MyApp:Starting my app
+ERROR:MyApp:There was a problem.
+Traceback (most recent call last):
+  File "myapp.py", line 9, in ?
+    mymodule.doIt()
+  File "mymodule.py", line 7, in doIt
+    raise TypeError, "Bogus type error for testing"
+TypeError: Bogus type error for testing
+INFO:MyApp:Ending my app
+
+ +

But don't worry - the above output is not hardcoded into the package. It's just an +example of what you can do with very little work. As you can see, exceptions are handled +as one would expect.

+ +

Control Flow

+ +

The package pretty much matches the PEP regarding control flow. The user of the package +makes logging calls on instances of Logger, which are organized into a +hierarchy based on a "dotted name" namespace. This hierarchy is embodied in an +encapsulated singleton Manager instance (which can be ignored by users of the +package, for most purposes). Based on the type of logging call and the logging +configuration (see below), the call may be passed through a set of Filter +instances to decide whether it should be dropped. If not, then the logger consults a set +of Handler instances which are associated with it, and asks each handler +instance to "handle" the logging event. By default, the system moves up the +namespace hierarchy and invokes handlers on all loggers at or above the level of the +logger on which the logging call was made. (You can override this by setting a logger's +"propagate" attribute to 0 - no traversal up the hierarchy is made from such a +logger. But I'm getting ahead of myself...)

+ +

Handlers are passed LogRecord instances which (should) contain all the +information we're interested in logging. Handlers, too, can invoke filters to determine +whether a record should be dropped. If not, then the handler takes a handler-specific +action to actually log the record to a file, the console or whatever.

+ +

Levels

+ +

The following levels are implemented by default:

+ +
+DEBUG
+INFO
+WARN
+ERROR
+CRITICAL
+
+ +

The CRITICAL level replaces the earlier FATAL level. You can use either (for now), but CRITICAL is preferred since FATAL implies that the application is about to terminate. This is not true for many systems which use logging - for example, a Web server application which encounters a CRITICAL condition (e.g. running out of resources) will still try to keep going as best it can.

+

FATAL (and the corresponding fatal() methods) may be removed in future versions of the package. Currently, CRITICAL is synonymous with FATAL and critical() methods are synonymous with fatal().

+

Exceptions logged via exception() use the ERROR level for logging. If it is desired to log exception information with arbitrary logging levels, this can be done by passing a keyword argument exc_info with a true value to the logging methods (see the pydoc for more details).

+

The levels are not deeply hardcoded into the package - the number of levels, their numeric values and their textual representation are all configurable. The above levels represent the experience of the log4j community and so are provided as the default levels for users who do not have very specific requirements in this area.

+

The example script log_test4.py shows the use of bespoke logging levels (as well as filtering by level at logger and handler, as well as use of filter classes).

+ +

Loggers

+ +

The package implements loggers pretty much as mentioned in the PEP, except that the manager class is called Manager rather than LogManager.

+

Each Logger instance represents "an area" of the application. This somewhat nebulous definition is needed because it's entirely up to each application developer to define an application's "areas".

For example, an application which reads and processes spreadsheet-type data in different formats might have an overall area "input", concerned with reading input files; and areas "input.csv", "input.xls" and "input.gnu", related to processing comma-separated-value, Excel and Gnumeric input files. Logging messages relating to the overall input function (e.g. deciding which files to process) might be logged used the logger named "input"; logging messages relating to reading individual files might be sent to any of "input.csv", "input.xls" or "input.gnu" depending on the type of file being read.

The advantage of the hierarchical structure is that logging verbosity may be controlled either at the high level or the low level. The levels are loosely coupled and new levels can easily be added at a later date, e.g."input.wks" for reading Lotus-123 format files. It's also possible to do things like routing messages relating to Excel file input to whoever is working on Excel imports, messages related to Gnumeric file processing to a different developer, and so on. Even if the same person works on both, they can at different times focus logging verbosity on particular areas of interest - for example, when debugging Excel imports, they can set the "input.xls" logger's verbosity to DEBUG and others to CRITICAL, and when moving to debug Gnumeric imports, they can reduce the "input.xls" verbosity by setting the level to CRITICAL, while increasing "input.gnu"'s verbosity by setting the level to DEBUG.

+ +

Handlers

+ +

The following handlers are implemented. I guess they could use more testing ;-) + +

    +
  • StreamHandler - logging to a stream, defaulting to sys.stderr.
  • +
  • FileHandler - logging to disk files.
  • +
  • RotatingFileHandler - logging to disk files with support for rollover, rotating files.
  • +
  • SocketHandler - logging to a streaming socket.
  • +
  • DatagramHandler - logging to a UDP socket.
  • +
  • SMTPHandler - logging to an email address.
  • +
  • SysLogHandler - logging to Unix syslog. Contributed by Nicolas Untz, based on Sam Rushing's syslog module.
  • +
  • MemoryHandler - buffering records in memory until a specific trigger occurs (or until the buffer gets full).
  • +
  • NTEventLogHandler - writes events to the NT event log. For this to work, you need to have Mark Hammond's Win32 extensions installed. (Though of course you can still log to NT from other platforms - just use SocketHandler to redirect to an NT machine).
  • +
  • HTTPHandler - sends events to a Web server using either GET or POST semantics.
  • +
+

All of these except the first two are defined in a sub-module, handlers.py. (To use these handlers, you'll need to import logging.handlers. In addition to the above list, there are example implementations of XMLHandler (see log_test9.py), BufferingSMTPHandler (see log_test11.py) and DBHandler (see log_test14.py) in the test harnesses, on which you can base more specific classes. There is also a class called SLHandler (see log_test1.py) which implements an alternative SysLogHandler - one which uses the syslog module in the standard library (and which is therefore only available on Unix).

+

SOAPHandler, which sends events to a SOAP server, has moved (as of release 0.4.4) from the core to an example script (log_test13.py). The SOAP message is packaged as a function call to a single log() function on the remote server, which takes each relevant member of the LogRecord as a positional parameter. This is perhaps not ideal - but then this SOAPHandler is just a proof-of-concept example to get you started ;-)

+

Note that the handlers are specifically intended not to raise exceptions when errors occur at runtime. This is to avoid error messages from the logging infrastructure polluting logging messages from the application being logged. If, for example, a SocketHandler sees a connection reset by the remote endpoint, it will silently drop all records passed to it (but it will try to connect each time). It may be that due to bugs there are some exceptions incorrectly raised by the logging system, I will try to rectify this kind of problem as soon as it is found and reported!

+ +

Formatters

+ +

A basic Formatter has been implemented, which should cater for most immediate +requirements. You basically initialize the Formatter with a format string which knows how the attribute dictionary of a LogRecord looks. For example, the output in the example above was produced +with a format string of "%(asctime)s %(name)-19s %(levelname)-5s - +%(message)s". Note that the "message" attribute of the LogRecord +is derived from "msg % args" where msg and args +are passed by the the user in a logging call.

+ +

Filters

+ +

Filters are used to refine logging output at either logger or handler level with a finer control than is available by just using logging levels. The basic Filter class takes an optional name argument and passes all logging records from loggers which are at or below the specified name.

+

For example, a Filter initialized with "A.B" will allow events logged by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" but not "A.BB", "B.A.B". If no name is specified, all events are passed by the filter.

+ +

Configuration

+ +

A basic configuration is provided via a module-level function, basicConfig(). +If you want to use very simple logging, you can just call the module-level +convenience functions and they will call basicConfig() for you if +necessary. It (basically) adds a StreamHandler (which writes to sys.stderr)to the root Logger.

+ +

There are numerous examples of configuration in the test/example scripts included in the distribution. For example, log_test8.py has an example of using a file-based logger.

+

An alternative using ConfigParser-based configuration files is also available (the older, dict-based function is no more). To use this functionality, you'll need to import logging.config. Here is an example of such a config file - it's a bit long, but a full working example so bear with me. I've annotated it as best I can :-):

+

+In the listing below, some values are used by both the logging configuration API +and the GUI configurator, while others are used only by the GUI configurator. To +make it clearer which values you absolutely need to have in the .ini file for it to be useful even if you hand-code it, the values used by the configuration API are shown +like this. (The other ones are used by the GUI configurator, but ignored by the configuration API.) +

+
+# --- logconf.ini -----------------------------------------------------------
+#The "loggers" section contains the key names for all the loggers in this
+#configuration. These are not the actual channel names, but values used to
+#identify where the parameters for each logger are found in this file.
+#The section for an individual logger is named "logger_xxx" where the "key"
+#for a logger is "xxx". So ... "logger_root", "logger_log02", etc. further
+#down the file, indicate how the root logger is set up, logger "log_02" is set
+#up, and so on.
+#Logger key names can be any identifier, except "root" which is reserved for
+#the root logger. (The names "lognn" are generated by the GUI configurator.)
+
+[loggers]
+keys=root,log02,log03,log04,log05,log06,log07
+
+#The "handlers" section contains the key names for all the handlers in this
+#configuration. Just as for loggers above, the key names are values used to
+#identify where the parameters for each handler are found in this file.
+#The section for an individual handler is named "handler_xxx" where the "key"
+#for a handler is "xxx". So sections "handler_hand01", "handler_hand02", etc.
+#further down the file, indicate how the handlers "hand01", "hand02" etc.
+#are set up.
+#Handler key names can be any identifier. (The names "handnn" are generated
+#by the GUI configurator.)
+
+[handlers]
+keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09
+
+#The "formatters" section contains the key names for all the formatters in
+#this configuration. Just as for loggers and handlers above, the key names
+#are values used to identify where the parameters for each formatter are found
+#in this file.
+#The section for an individual formatter is named "formatter_xxx" where the
+#"key" for a formatter is "xxx". So sections "formatter_form01",
+#"formatter_form02", etc. further down the file indicate how the formatters
+#"form01", "form02" etc. are set up.
+#Formatter key names can be any identifier. (The names "formnn" are generated
+#by the GUI configurator.)
+
+[formatters]
+keys=form01,form02,form03,form04,form05,form06,form07,form08,form09
+
+#The section below indicates the information relating to the root logger.
+#
+#The level value needs to be one of DEBUG, INFO, WARN, ERROR, CRITICAL or NOTSET.
+#In the root logger, NOTSET indicates that all messages will be logged.
+#Level values are eval()'d in the context of the logging package's namespace.
+#
+#The propagate value indicates whether or not parents of this loggers will
+#be traversed when looking for handlers. It doesn't really make sense in the
+#root logger - it's just there because a root logger is almost like any other
+#logger.
+#
+#The channel value indicates the lowest portion of the channel name of the
+#logger. For a logger called "a.b.c", this value would be "c".
+#
+#The parent value indicates the key name of the parent logger, except that
+#root is shown as "(root)" rather than "root".
+#
+#The qualname value is the fully qualified channel name of the logger. For a
+#logger called "a.b.c", this value would be "a.b.c".
+#
+#The handlers value is a comma-separated list of the key names of the handlers
+#attached to this logger.
+#
+[logger_root]
+level=NOTSET
+handlers=hand01
+qualname=(root) # note - this is used in non-root loggers
+propagate=1 # note - this is used in non-root loggers
+channel=
+parent=
+
+#
+#The explanation for the values in this section is analogous to the above. The
+#logger is named "log02" and coincidentally has a key name of "log02". It has
+#a level of DEBUG and handler with key name "hand02". (See section
+#"handler_hand02" for handler details.) If the level value were NOTSET, this tells
+#the logging package to consult the parent (as long as propagate is 1) for the
+#effective level of this logger. If propagate is 0, this level is treated as for
+#the root logger - a value of NOTSET means "pass everything", and other values are
+#interpreted at face value.
+#
+[logger_log02]
+level=DEBUG
+propagate=1
+qualname=log02
+handlers=hand02
+channel=log02
+parent=(root)
+
+#
+#The explanation for the values in this section is analogous to the above. The
+#logger is named "log02.log03" and has a key name of "log03".
+#It has a level of INFO and handler with key name "hand03".
+#
+[logger_log03]
+level=INFO
+propagate=1
+qualname=log02.log03
+handlers=hand03
+channel=log03
+parent=log02
+
+#
+#The explanations for the values in this section and subsequent logger sections
+#are analogous to the above.
+#
+[logger_log04]
+level=WARN
+propagate=0
+qualname=log02.log03.log04
+handlers=hand04
+channel=log04
+parent=log03
+
+[logger_log05]
+level=ERROR
+propagate=1
+qualname=log02.log03.log04.log05
+handlers=hand05
+channel=log05
+parent=log04
+
+[logger_log06]
+level=CRITICAL
+propagate=1
+qualname=log02.log03.log04.log05.log06
+handlers=hand06
+channel=log06
+parent=log05
+
+[logger_log07]
+level=WARN
+propagate=1
+qualname=log02.log03.log04.log05.log06.log07
+handlers=hand07
+channel=log07
+parent=log06
+
+#The section below indicates the information relating to handler "hand01".
+#The first three keys (class, level and formatter) are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The class value indicates the handler's class (as determined by eval() in
+#the logging package's namespace).
+#
+#The level value needs to be one of DEBUG, INFO, WARN, ERROR, CRITICAL or NOTSET.
+#NOTSET means "use the parent's level".
+#
+#The formatter value indicates the key name of the formatter for this handler.
+#If blank, a default formatter (logging._defaultFormatter) is used.
+#
+#The stream value indicates the stream for this StreamHandler. It is computed
+#by doing eval() on the string value in the context of the logging package's
+#namespace.
+#
+#The args value is a tuple of arguments which is passed to the constructor for
+#this handler's class in addition to the "self" argument.
+#
+[handler_hand01]
+class=StreamHandler
+level=NOTSET
+formatter=form01
+args=(sys.stdout,)
+stream=sys.stdout
+
+#The section below indicates the information relating to handler "hand02".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The filename value is the name of the file to write logging information to.
+#The mode value is the mode used to open() the file. The maxsize and backcount
+#values control rollover as described in the package's pydoc.
+#
+[handler_hand02]
+class=FileHandler
+level=DEBUG
+formatter=form02
+args=('python.log', 'w')
+filename=python.log
+mode=w
+
+#The section below indicates the information relating to handler "hand03".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The host value is the name of the host to send logging information to.
+#The port value is the port number to use for the socket connection.
+#
+[handler_hand03]
+class=handlers.SocketHandler
+level=INFO
+formatter=form03
+args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)
+host=localhost
+port=DEFAULT_TCP_LOGGING_PORT
+
+#The section below indicates the information relating to handler "hand04".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The host value is the name of the host to send logging information to.
+#The port value is the port number to use for the socket connection.
+#
+[handler_hand04]
+class=handlers.DatagramHandler
+level=WARN
+formatter=form04
+args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)
+host=localhost
+port=DEFAULT_UDP_LOGGING_PORT
+
+#The section below indicates the information relating to handler "hand05".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The host value is the name of the host to send logging information to.
+#The port value is the port number to use for the socket connection.
+#The facility is the syslog facility to use for logging.
+#
+[handler_hand05]
+class=handlers.SysLogHandler
+level=ERROR
+formatter=form05
+args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
+host=localhost
+port=SYSLOG_UDP_PORT
+facility=LOG_USER
+
+#The section below indicates the information relating to handler "hand06".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The appname value is the name of the application which appears in the
+#NT event log.
+#The dllname value is the pathname of a DLL to use for message definitions.
+#The logtype is the type of NT event log to write to - Application, Security
+#or System.
+#
+[handler_hand06]
+class=NTEventLogHandler
+level=CRITICAL
+formatter=form06
+args=('Python Application', '', 'Application')
+appname=Python Application
+dllname=
+logtype=Application
+
+#The section below indicates the information relating to handler "hand07".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The host value is the name of the SMTP server to connect to.
+#The port value is the port number to use for the SMTP connection.
+#The from value is the "From" value in emails.
+#The to value is a comma-separated list of email addresses.
+#The subject value is the subject of the email.
+#
+[handler_hand07]
+class=SMTPHandler
+level=WARN
+formatter=form07
+args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
+host=localhost
+port=25
+from=from@abc
+to=user1@abc,user2@xyz
+subject=Logger Subject
+
+#The section below indicates the information relating to handler "hand08".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The capacity value is the size of this handler's buffer.
+#The flushlevel value is the logging level at which the buffer is flushed.
+#The from value is the "From" value in emails.
+#The target value is the key name of the handler which messages are flushed
+#to (i.e. sent to when flushing).
+#
+[handler_hand08]
+class=MemoryHandler
+level=NOTSET
+formatter=form08
+target=
+args=(10, ERROR)
+capacity=10
+flushlevel=ERROR
+
+#The section below indicates the information relating to handler "hand09".
+#The first three keys are common to all handlers.
+#Any other values are handler-specific, except that "args", when eval()'ed,
+#is the list of arguments to the constructor for the handler class.
+#
+#The host value is the name of the HTTP server to connect to.
+#The port value is the port number to use for the HTTP connection.
+#The url value is the url to request from the server.
+#The method value is the HTTP request type (GET or POST).
+#
+[handler_hand09]
+class=HTTPHandler
+level=NOTSET
+formatter=form09
+args=('localhost:9022', '/log', 'GET')
+host=localhost
+port=9022
+url=/log
+method=GET
+
+#The sections below indicate the information relating to the various
+#formatters. The format value is the overall format string, and the
+#datefmt value is the strftime-compatible date/time format string. If
+#empty, the logging package substitutes ISO8601 format date/times where
+#needed. See the package pydoc for more details of the format string
+#structure.
+#
+[formatter_form01]
+format=F1 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form02]
+format=F2 %(asctime)s %(pathname)s(%(lineno)d): %(levelname)s %(message)s
+datefmt=
+
+[formatter_form03]
+format=F3 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form04]
+format=%(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form05]
+format=F5 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form06]
+format=F6 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form07]
+format=F7 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form08]
+format=F8 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+[formatter_form09]
+format=F9 %(asctime)s %(levelname)s %(message)s
+datefmt=
+
+# --- end of logconf.ini ----------------------------------------------------
+
+ +

To use a file like this, you would call logging.config.fileConfig("logconf.ini") +whereupon the file is read in and processed. Note that evaluation happens in the context of the logging package (hence the unqualified class names). + + +

The GUI Configurator

+ +To create a file like the above, you can use the new GUI configurator, logconf.py, which is invoked either with no arguments or with a single argument giving the name of a configuration file to read. (Or, if you're a masochist/don't have Tk, you can do it by hand. The configurator is a quick hack, which I hope is reasonably intuitive - have a play with it and see what you think. I've used it with 1.5.2 and 2.1.2 on Windows and 1.5.2 on Linux/x86. There's no validation, rudimentary error checking and the usability could be better, but it's something to build on, hey?)

+ +

Here's a screenshot of the configurator:

+ +

Here's a quick guide on how to use it: +

    +
  • The screen is laid out in three panels - for loggers, handlers and formatters (see the yellow rectangles in the screenshot).
  • +
  • Each panel consists of a listbox, New and Delete buttons and a "property editor" to set properties for each of the objects.
  • +
  • To create loggers, you need to first select the parent logger before clicking the New button. To create handlers and formatters, just click the appropriate New button.
  • +
  • To delete an item, just select it in the listbox and click the Delete button.
  • +
  • Whenever an item is selected in the listbox, its properties are displayed in the property editor section.
  • +
  • Whenever an item is deleted, the property editor section is cleared.
  • +
  • To edit a property, just click on the name or value. If the property is read-only, nothing happens. If it's a user-editable value, an entry field appears and you can type into it. If it's a user-selectable value (I mean selectable from a list), a button with ... appears. Clicking on it causes a pseudo-combobox to appear. All such boxes are single-selection, except for the "Handlers" property of loggers, for which several handlers can be selected.
  • +
  • To commit the changes, just click elsewhere in the property editor section, e.g. below the last property or on some other property.
  • +
  • Formatters are referred to by handlers, and handlers are referred to by loggers. Hence formatters and handlers have names generated for them automatically, for use in cross-referencing. The names of these cannot be changed. The name for loggers is, however, editable (except for the root logger), as it represents the position of the logger in the hierarchy.
  • +
  • The Load, Save, Save As and Reset buttons should be reasonably self-explanatory (except perhaps Reset, which just deletes all non-root loggers, handlers and formatters and starts with a clean slate).
  • +
  • Filters are not supported in this release, but will be as soon as time permits and if there is enough demand.
  • +

+

Case Scenarios

+ +

With reference to the PEP, +here are my comments on the current state of play. + +

    +
  1. A short simple script. See the example app.py + above.
  2. +
  3. Medium sized app with C extension module. I have not specifically considered C extension + modules but I assume they can just use the standard Python C API to make logging calls.
  4. +
  5. Distutils. I would welcome more specific comments on what kind of configuration people + think would be useful. To a certain extent, controlling verbosity levels through setup.py + options is, I think, the domain of the app developer rather than the logging package.
  6. +
  7. Large applications. If users can restart a system after changing the logging settings + (via some user-friendly or support-desk-friendly interface) then present functionality + should cater for this. In the case where the logging behaviour of a (long-)running system needs + to be changed, then the functionality (new in 0.4.3) described below can be used.
  8. +
+ +

Thread Safety

+ +

The package is intended to be threadsafe. Although it makes no use of threads to provide its functionality (except for on-the-fly reconfiguration), shared data in the package is protected by a thread lock which is acquired before, and released after, modifications to shared data. In addition, the Handler class creates a per-handler instance I/O lock which is acquired before, and released after, calling emit(). If you define your own handlers, in most situations you should not need to take any special precautions, as long as your I/O is called only from emit(). The thread locks impose a slight performance penalty, but it's no worse than for any other use of thread locks in Python applications.

+ +

On-The-Fly Reconfiguration

+ +

The package also allows a program to permit changing of the logging configuration on the fly, i.e. while the program is still running. This should be a help for developers of long-running programs such as servers (e.g. Zope, Webware). At this stage, the on-the-fly configurability is fairly basic - to use it, two new module-level functions are provided (in the logging.config module). +

    +
  • listen([port]) is used to create a thread which, when started, opens a socket server which listens on the specified port for configuration requests. The socket protocol is very basic - a two-byte length followed by a string of that length is sent to the listener. The string should be in the same format as logging configuration files, i.e. ConfigParser files conforming to the scheme described above.
  • +
  • stopListening() tells the thread created by listening to terminate.
  • +
+Currently, you can't just change part of the logging configuration - the sent configuration completely replaces the existing configuration, and if previously existing loggers are not in the new configurations, they will be disabled after the new configuration takes effect. The script log_test17.py in the distribution illustrates the on-the-fly configuration feature. +

+ +

Module-Level Convenience Functions

+ +

For the casual user, there are module-level convenience functions which operate on the +root logger. Here is the pydoc for them.

+ +

Performance

+ +

The implementation has not been optimized for performance. This is planned to be done in a later phase, following feature stabilization and benchmarking.

+ +

Implementation Status

+ +

The implementation is what I have termed alpha - mainly because it has not had very wide exposure or extensive testing in many environments. Please try to use it/break it and give me +any feedback you can! There is no reason I can see why this package should not be ready in time for the Python 2.3 release :-)

+ + +

Acknowledgements

+

The biggest thank you goes to the log4j developers, whom I am attempting to flatter sincerely by imitation ;-) Thanks also to Trent Mick for PEP 282, which prompted me to offer this implementation.

+

I'd also like to thank all of the people who have given me feedback, patches and encouragement. In particular (but in no particular order):

+
    +
  • Ollie Rutherfurd - patches and suggestions.
  • +
  • Greg Ward - for nudging me in the direction of distutils.
  • +
  • Hunter Matthews - questions about user-defined logging levels.
  • +
  • Nicolas Untz - SysLogHandler implementation.
  • +
  • Jeremy Hylton - discussions about logging exceptions.
  • +
  • Kevin Butler - discussions about logging exceptions.
  • +
  • Richard Jones - lots of positive feedback and ideas.
  • +
  • David Goodger - suggestion about using CRITICAL rather than FATAL.
  • +
  • Denis S. Otkidach - suggestions on filters and feedback on performance.
  • +
+

Still To Do

+

No rest for the wicked...

+
    +
  • Improvements to the GUI configurator. Feedback, anyone?
  • +
  • Overview-type documentation? The pydoc is reasonably comprehensive (I like to think). Perhaps a slightly formalized version of the information on this page?
  • +
  • Testing, and more testing (you could help with this, too ...)
  • +
+

If you can help with any of this, please email me.

+

Download and Installation

+

The current version is 0.4.7. Here is the latest tarball (also in zip format or Windows executable - the latter includes the logging package only). The distribution contains the following files: +


+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Filename     Contents
 
README.txt Brief description and change history.
__init__.py The core logging package itself, including StreamHandler and FileHandler.
handlers.py The other handlers provided as part of the package.
config.py The code for configuring the package.
setup.py The distutils setup script.
logrecv.py A test server used for testing SocketHandler, DatagramHandler, HTTPHandler and SOAPHandler. Run it with an argument of one of "TCP", "UDP", "HTTP" or "SOAP" before running a test harness which logs to one of these handlers. Note that to use the SOAP handler, you need to have installed PyXML-0.6.6 and the Zolera Soap Infrastructure. This is needed for logrecv.py only, and not for the logging module itself. (Note that ZSI requires Python 2.x)
app.py The minimal example described above.
mymodule.py Another example described above.
myapp.py From the second example described above.
log_test.py A test script intended to work as a regression test harness. Runs a number of the other scripts and generates output to stdout.log and stderr.log.
log_test0.py A simple test script using basicConfig() only.
log_test1.py An example showing slightly more involved configuration and + exception handling, as well as a Unix syslog handler which uses the standard library's syslog module.
log_test2.py A version of log_test0.py which only logs to + a SocketHandler.
log_test3.py An example showing use of fileConfig() and + logging to various loggers.
log_test4.py An example showing use of bespoke levels, filtering by level at logger and handler, and use of filter classes (descendants of Filter).
log_test5.py An example showing use of SMTPHandler. Before running this script, be sure to change the bogus addresses it contains to real ones which you have access to.
log_test6.py An example showing use of NTEventLogHandler. This script needs to be run on an NT system.
log_test7.py An example showing use of MemoryHandler.
log_test8.py An example showing use of FileHandler with rollover across multiple files.
log_test9.py An example showing use of BufferingHandler and BufferingFormatter through implementing simple XMLFormatter and XMLHandler classes.
log_test10.py An example showing how to get the logging module to create loggers of your own class (though it needs to be a subclass of Logger).
log_test11.py An example SMTP handler, called BufferingSMTPHandler, which buffers events and sends them via email in batches.
log_test12.py An example showing the use of HTTPHandler, for use with logrecv.py.
log_test13.py An example showing the use of SOAPHandler, for use with logrecv.py.
log_test14.py An example showing an implementation of DBHandler, showing how to log requests to RDBMS tables using the Python Database API 2.0.
log_test15.py An example showing the use of the Filter class with a string initializer.
log_test16.py An example showing the use of logging in a multi-threaded program.
log_test17.py An example showing the use of logging in a multi-threaded program, together with reconfiguring logging on the fly through the use of listen() and stopListening(). This script serves as both server and client, depending on the arguments it's called with.
log_test18.py An example showing the use of an example filter, MatchFilter, which offers flexible match-based +filtering of LogRecords.
log_test19.py A basic test of logger parents.
log_test20.py Demonstrates the use of custom class instances for messages and filtering based on classes.
log_test21.py Demonstrates the use of a wildcard name-space filter with and without custom message classes.
log_test22.py Demonstrates the use of either localtime or gmtime to do date/time formatting.
debug.ini An example configuration for use with log_test17.py.
warn.ini An example configuration for use with log_test17.py.
error.ini An example configuration for use with log_test17.py.
critical.ini An example configuration for use with log_test17.py.
log_test3.ini An example configuration for use with log_test3.py.
stdout.exp The expected results of stdout.log after running log_test.py.
stderr.exp The expected results of stderr.log after running log_test.py.
logconf.py A Tkinter-based GUI configurator.
logconf.ini Example configuration file, in ConfigParser format, for use with logconf.py and log_test3.py.
logging.dtd A simple example DTD for use with log_test9.py.
logging.xml An example XML file for use with log_test9.py. It references events.xml as external data.
events.xml An example XML file for use with log_test9.py. It holds the actual events in XML format.
python_logging.html The page you're reading now.
default.css Stylesheet for use with the HTML pages.
 
+
+

To install, unpack the archive into any directory, and in that directory invoke the script "setup.py install" to install the module in the default location used by distutils.

+

To use, just put logging.py in your Python path, "import logging" and go. (The installation procedure described above will normally put the logging module in your Python path. If you want to use file-based configuration API, you'll also need to import logging.config. To use the more esoteric handlers, you'll also need to import logging.handlers.)

+ +

Change History

+ +

The change history is as follows.

+ +
+Version   Date        Description
+=============================================================================
+0.4.7     15 Nov 2002 Made into a package with three modules: __init__ (the
+                      core code), handlers (all handlers other than
+                      FileHandler and its bases) and config (all the config
+                      stuff). Before doing this:
+                      Updated docstrings to include a short line, then a
+                      blank line, then more descriptive text.
+                      Renamed 'lvl' to 'level' in various functions.
+                      Changed FileHandler to use "a" and "w" instead of "a+"
+                      and "w+".
+                      Moved log file rotation functionality from FileHandler
+                      to a new class RotatingFileHandler.
+                      Improved docstring describing rollover.
+                      Updated makePickle to use 4-byte length and struct
+                      module, likewise logrecv.py. Also updated on-the-fly
+                      config reader to use 4-byte length/struct module.
+                      Altered ConfigParser test to look at 'readline' rather
+                      than 'read'.
+                      Added optional "defaults" argument to fileConfig, to
+                      be passed to ConfigParser.
+                      Renamed ALL to NOTSET to avoid confusion.
+                      Commented out getRootLogger(), as obsolete.
+                      To do regression testing, run log_test.py and compare
+                      the created files stdout.log and stderr.log against
+                      the files stdout.exp and stderr.exp. They should match
+                      except fir a couple of exception messages which give
+                      absolute file paths.
+                      Updated python_logging.html to remove links to
+                      logging_pydoc.html, which has been removed from the
+                      distribution.
+                      Changed default for raiseExceptions to 1.
+-----------------------------------------------------------------------------
+0.4.6     08 Jul 2002 Added raiseExceptions to allow conditional propagation
+                      of exceptions which occur during handling.
+                      Added converter to Formatter to allow use of any
+                      function to convert time from seconds to a tuple. It
+                      still defaults to time.localtime but now you can also
+                      use time.gmtime.
+                      Added log_test22.py to test the conversion feature.
+                      Changed rootlogger default level to WARN - was DEBUG.
+                      Updated some docstrings.
+                      Moved import of threading to where thread is imported.
+                      If either is unavailable, threading support is off.
+                      Updated minor defects in python_logging.html.
+                      Check to see if ConfigParser has readfp method; if it
+                      does and an object with a 'read' method is passed in,
+                      assumes a file-like object and uses readfp to read it
+                      in.
+-----------------------------------------------------------------------------
+0.4.5     04 Jun 2002 Fixed bug which caused problem if no args to message
+                      (suggested by Hye-Shik Chang).
+                      Fixed bug in _fixupParents (thanks to Nicholas Veeser)
+                      and added log_test19.py as a test case for this bug.
+                      Added getMessage to LogRecord (code was moved here from
+                      Formatter.format)
+                      Applied str() to record.msg to allow arbitrary classes
+                      to determine the formatting (as msg can now be a class
+                      instance).
+                      Table of Contents added to python_logging.html, the
+                      section on Loggers updated, and the logconf.ini file
+                      section annotated.
+                      Added log_test20.py which demonstrates how to use
+                      class instances to provide alternatives to numeric
+                      severities as mechanisms for control of logging.
+                      Added log_test21.py which builds on log_test20.py to
+                      show how you can use a regular expression-based Filter
+                      for flexible matching similar to e.g. Protomatter
+                      Syslog, where you can filter on e.g. "a.*" or "*.b" or
+                      "a.*.c".
+                      _levelNames changed to contain reverse mappings as well
+                      as forward mappings (leveltext->level as well as level
+                      -> leveltext). The reverse mappings are used by
+                      fileConfig().
+                      fileConfig() now more forgiving of missing options in
+                      .ini file - sensible defaults now used when some
+                      options are absent. Also, eval() is used less when
+                      interpreting .ini file contents - int() and dict lookup
+                      are used in more places. Altered log_test3.py and added
+                      log_test3.ini to show a hand-coded configuration file.
+-----------------------------------------------------------------------------
+0.4.4     02 May 2002 getEffectiveLevel() returns ALL instead of None when
+                      nothing found. Modified references to level=0 to
+                      level=ALL in a couple of places.
+                      SocketHandler now inherits from Handler (it used to
+                      inherit from StreamHandler, for no good reason).
+                      getLock() renamed to createLock().
+                      Docstring tidy-ups, and some tidying up of
+                      DatagramHandler.
+                      Factored out unpickling in logrecv.py.
+                      Added log_test18.py to illustrate MatchFilter, which is
+                      a general matching filter.
+                      Improved FileHandler.doRollover() so that the base
+                      file name is always the most recent, then .1, then .2
+                      etc. up to the maximum backup count. Renamed formal
+                      args and attributes used in rollover.
+                      Changed LogRecord attributes lvl -> levelno, level ->
+                      levelname (less ambiguity)
+                      Formatter.format searches for "%(asctime)" rather than
+                      "(asctime)"
+                      Renamed _start_time to _startTime
+                      Formatter.formatTime now returns the time
+                      Altered logrecv.py to support stopping servers
+                      programmatically
+                      Added log_test.py as overall test harness
+                      basicConfig() can now be safely called more than once
+                      Modified test scripts to make it easier to call them
+                      from log_test.py
+                      Moved SOAPHandler from core to log_test13.py. It's not
+                      general enough to be in the core; most production use
+                      will have differing RPC signatures.
+-----------------------------------------------------------------------------
+0.4.3     14 Apr 2002 Bug fix one-off error message to go to sys.stderr
+                      rather than sys.stdout.
+                      logrecv.py fix TCP for busy network.
+                      Thread safety - added locking to Handler and for shared
+                      data in module, and log_test16.py to test it.
+                      Added socket listener to allow on-the-fly configuration
+                      and added log_test17.py to test it.
+-----------------------------------------------------------------------------
+0.4.2     11 Apr 2002 Bug fix fileConfig() - setup of MemoryHandler target
+                      and errors when loggers have no handlers set or
+                      handlers have no formatters set
+                      logconf.py - seems to hang if window closed when combo
+                      dropdown is showing - added code to close popup on exit
+                      Some tweaks to _srcfile computation (normpath added)
+                      findCaller() optimized, now a lot faster!
+                      Logger.removeHandler now closes the handler before
+                      removing it
+                      fileConfig() removes existing handlers before adding
+                      the new set, to avoid memory leakage when repeated
+                      calls are made
+                      Fixed logrecv.py bug which hogged CPU time when TCP
+                      connection was closed from the client
+                      Added log_test14.py to demonstrate/test a DBHandler
+                      which writes logging records into an RDBMS using the
+                      Python Database API 2.0 (to run, you need something
+                      which supports this already installed - I tested with
+                      mxODBC)
+                      Made getLogger name argument optional - returns root
+                      logger if omitted
+                      Altered Filter to take a string initializer, filtering
+                      a sub-hierarchy rooted at a particular point (idea from
+                      Denis S. Otkidach).
+                      Added log_test15.py to test Filter initializer
+-----------------------------------------------------------------------------
+0.4.1     03 Apr 2002 Bug fix SMTPHandler - extra \r\n needed (Oleg Orlov)
+                      Added BufferingHandler, BufferingFormatter
+                      Renamed getChainedPriority to getEffectiveLevel
+                      Removed Logger.getRoot as it is redundant
+                      Added log_test9.py to test Buffering classes and
+                      to show an XMLFormatter example.
+                      Added setLoggerClass.
+                      Added log_test10.py to test setLoggerClass, using an
+                      example Logger-derived class which outputs exception
+                      info even for DEBUG level logging calls
+                      Added log_test11.py to test a buffering implementation
+                      of SMTPHandler
+                      Changed logging call implementation to allow keyword
+                      arguments (Kevin Butler and others)
+                      Changed default SysLogHandler implementation.
+                      Renamed "additive" to "propagate" as it better
+                      describes the attribute.
+                      Added HTTPHandler.
+                      Modified logrecv.py to remove "both" option and to add
+                      "HTTP" and "SOAP" options (SOAP option needs you to
+                      have PyXML-0.6.6 and ZSI installed - for logrecv.py
+                      only, and not for the core logging module itself).
+                      Added log_test12.py to test HTTPHandler.
+                      Added log_test13.py to test SOAPHandler.
+                      Formatted to Python source guidelines (spaces, indent
+                      of 4, within 80 columns).
+                      More method renamings (result of feedback) - _handle()
+                      renamed to emit(), _logRecord() renamed to handle().
+                      Renamed FATAL to CRITICAL (David Goodger), but left
+                      fatal() and FATAL in (until PEP is changed)
+                      Changed configuration file format to ConfigParser
+                      format.
+                      Factored filter application functionality out to a new
+                      Filterer class. The isLoggable() method is renamed to
+                      filter() in both Filter and Filterer classes.
+                      Altered SMTPHandler __init__ to accept (host, port)
+                      for the mail internet address.
+                      Added GUI configurator which uses Tkinter and the new
+                      configuration file format. (See logconf.py and an
+                      example configuration file in logconf.ini)
+                      Altered log_test3.py to test with the new file format.
+-----------------------------------------------------------------------------
+0.4       21 Mar 2002 Incorporated comments/patches from Ollie Rutherfurd:
+                      -Added level filtering for handlers.
+                      -Return root logger if no name specified in getLogger.
+                      Incorporated comments from Greg Ward:
+                      -Added distutils setup.py script.
+                      Added formatter initialization in Handler.__init__.
+                      Tidied up docstrings.
+                      Added removeHandler to Logger.
+                      Added removeFilter to Logger and Handler.
+                      logrecv.py modified to keep connection alive until
+                      client closes it.
+                      SocketHandler modified to not reset connection after
+                      each logging event.
+                      Added shutdown function which closes open sockets
+                      Renamed DEFAULT_LOGGING_PORT->DEFAULT_TCP_LOGGING_PORT
+                      Added DEFAULT_UDP_LOGGING_PORT
+                      Added log_test4.py (example of arbitrary levels)
+                      Added addLevelName, changed behaviour of getLevelName
+                      Fixed bugs in DatagramHandler
+                      Added SMTPHandler implementation
+                      Added log_test5.py to test SMTPHandler
+                      Added SysLogHandler (contribution from Nicolas Untz
+                      based on Sam Rushing's syslog.py)
+                      Modified log_test1.py to add a SysLogHandler
+                      Added rollover functionality to FileHandler
+                      Added NTEventLogHandler (based on Win32 extensions)
+                      Added MemoryHandler implementation
+                      Added log_test7.py to test MemoryHandler
+                      Added log_test8.py to test FileHandler rollover
+                      Added logException method to Logger
+                      Added formatException method to Formatter
+                      Added log_test6.py to test NTEventHandler and
+                      logException
+                      Numerous internal method renamings (sorry - but better
+                      to do this now, rather than when we enter beta status).
+-----------------------------------------------------------------------------
+0.3       14 Mar 2002 First public release, for early feedback
+-----------------------------------------------------------------------------
+0.2                   Consolidated into single file (for internal use only)
+-----------------------------------------------------------------------------
+0.1                   Initial implementation (for internal use only)
+-----------------------------------------------------------------------------
+
+ +

Copyright and License

+ +

The copyright statement follows.

+ +
+Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Vinay Sajip
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/tools/python/logging/logging-0.4.9.2/setup.py b/tools/python/logging/logging-0.4.9.2/setup.py new file mode 100644 index 0000000..5bca563 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/setup.py @@ -0,0 +1,29 @@ +from distutils.core import setup + +setup(name = "logging", + description="A logging module for Python", + long_description = """This module is intended to provide a standard error logging mechanism in Python as per PEP 282.""", + license="""Copyright (C) 2001-2004 by Vinay Sajip. All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Vinay Sajip +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.""", + version = "0.4.9.2", + author = "Vinay Sajip", + author_email = "vinay_sajip@red-dove.com", + maintainer = "Vinay Sajip", + maintainer_email = "vinay_sajip@red-dove.com", + url = "http://www.red-dove.com/python_logging.html", + packages = ["logging"], + ) diff --git a/tools/python/logging/logging-0.4.9.2/test/app.py b/tools/python/logging/logging-0.4.9.2/test/app.py new file mode 100644 index 0000000..e550a19 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/app.py @@ -0,0 +1,5 @@ +import logging + +logging.warning("Hello") +logging.error("Still here...") +logging.warning("Goodbye") \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/critical.ini b/tools/python/logging/logging-0.4.9.2/test/critical.ini new file mode 100644 index 0000000..8cbaa8a --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/critical.ini @@ -0,0 +1,60 @@ +[loggers] +keys=root,log02,log03,log04,log05 + +[handlers] +keys=hand01 + +[formatters] +keys=form01 + +[logger_root] +level=WARN +propagate=1 +channel= +parent= +qualname=(root) +handlers=hand01 + +[logger_log02] +level=CRITICAL +propagate=1 +channel=A +parent=(root) +qualname=A +handlers= + +[logger_log03] +level=CRITICAL +propagate=1 +channel=B +parent=log02 +qualname=A.B +handlers= + +[logger_log04] +level=CRITICAL +propagate=1 +channel=C +parent=log03 +qualname=A.B.C +handlers= + +[logger_log05] +level=CRITICAL +propagate=1 +channel=D +parent=log04 +qualname=A.B.C.D +handlers= + +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +stream=sys.stderr +args=(sys.stderr,) + +[formatter_form01] +format=critical.ini %(name)s %(levelname)s %(message)s +datefmt= + diff --git a/tools/python/logging/logging-0.4.9.2/test/debug.ini b/tools/python/logging/logging-0.4.9.2/test/debug.ini new file mode 100644 index 0000000..6c8622f --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/debug.ini @@ -0,0 +1,60 @@ +[loggers] +keys=root,log02,log03,log04,log05 + +[handlers] +keys=hand01 + +[formatters] +keys=form01 + +[logger_root] +level=WARN +propagate=1 +channel= +parent= +qualname=(root) +handlers=hand01 + +[logger_log02] +level=DEBUG +propagate=1 +channel=A +parent=(root) +qualname=A +handlers= + +[logger_log03] +level=DEBUG +propagate=1 +channel=B +parent=log02 +qualname=A.B +handlers= + +[logger_log04] +level=DEBUG +propagate=1 +channel=C +parent=log03 +qualname=A.B.C +handlers= + +[logger_log05] +level=DEBUG +propagate=1 +channel=D +parent=log04 +qualname=A.B.C.D +handlers= + +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +stream=sys.stderr +args=(sys.stderr,) + +[formatter_form01] +format=debug.ini %(name)s %(levelname)s %(message)s +datefmt= + diff --git a/tools/python/logging/logging-0.4.9.2/test/error.ini b/tools/python/logging/logging-0.4.9.2/test/error.ini new file mode 100644 index 0000000..49beea7 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/error.ini @@ -0,0 +1,60 @@ +[loggers] +keys=root,log02,log03,log04,log05 + +[handlers] +keys=hand01 + +[formatters] +keys=form01 + +[logger_root] +level=WARN +propagate=1 +channel= +parent= +qualname=(root) +handlers=hand01 + +[logger_log02] +level=ERROR +propagate=1 +channel=A +parent=(root) +qualname=A +handlers= + +[logger_log03] +level=ERROR +propagate=1 +channel=B +parent=log02 +qualname=A.B +handlers= + +[logger_log04] +level=ERROR +propagate=1 +channel=C +parent=log03 +qualname=A.B.C +handlers= + +[logger_log05] +level=ERROR +propagate=1 +channel=D +parent=log04 +qualname=A.B.C.D +handlers= + +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +stream=sys.stderr +args=(sys.stderr,) + +[formatter_form01] +format=error.ini %(name)s %(levelname)s %(message)s +datefmt= + diff --git a/tools/python/logging/logging-0.4.9.2/test/events.xml b/tools/python/logging/logging-0.4.9.2/test/events.xml new file mode 100644 index 0000000..f7f1092 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/events.xml @@ -0,0 +1,31 @@ + + + Info index = 90 + + + Info index = 91 + + + Info index = 92 + + + Info index = 93 + + + Info index = 94 + + + Info index = 95 + + + Info index = 96 + + + Info index = 97 + + + Info index = 98 + + + Info index = 99 + \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test.py b/tools/python/logging/logging-0.4.9.2/test/log_test.py new file mode 100755 index 0000000..5d54b9b --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Run all tests. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import os, sys, logging, threading, time + +BANNER = "-- %-10s %-6s --------------------------------------------------------\n" + +def banner(nm, typ): + sep = BANNER % (nm, typ) + sys.stdout.write(sep) + sys.stdout.flush() + sys.stderr.write(sep) + sys.stderr.flush() + +def main(): + oldout = sys.stdout + olderr = sys.stderr + sys.stdout = open("stdout.log", "w") + sys.stderr = open("stderr.log", "w") + logging.basicConfig() + root = logging.getLogger("") + hdlr0 = root.handlers[0] + #Set up servers + import logrecv + threads = [] + tcpserver = logrecv.LogRecordSocketReceiver() + tcpserver.logname = "" + threads.append(threading.Thread(target=logrecv.runTCP, args=(tcpserver,))) + udpserver = logrecv.LogRecordDatagramReceiver() + udpserver.logname = "" + threads.append(threading.Thread(target=logrecv.runUDP, args=(udpserver,))) + httpserver = logrecv.LogRecordHTTPReceiver() + httpserver.logname = "" + threads.append(threading.Thread(target=logrecv.runHTTP, args=(httpserver,))) + soapserver = None + if logrecv.SOAPServer: + soapserver = logrecv.SOAPServer() + soapserver.modules = (sys.modules["logrecv"],) + soapserver.logname = "" + threads.append(threading.Thread(target=logrecv.runSOAP, args=(soapserver,))) + + for thread in threads: + thread.start() + try: + import log_test0 + banner("log_test0", "begin") + log_test0.main() + banner("log_test0", "end") + try: + import log_test1 + banner("log_test1", "begin") + log_test1.main() + banner("log_test1", "end") + except ImportError: + pass + + import log_test2 + banner("log_test2", "begin") + log_test2.main() + banner("log_test2", "end") + time.sleep(3) + + #Skip 3 as it tests fileConfig + + banner("log_test4", "begin") + import log_test4 + banner("log_test4", "end") + + #Skip 5 as it tests SMTPHandler, can't easily check results automatically + + #Skip 6 as it tests NTEventLogHandler, can't easily check results automatically + + banner("log_test7", "begin") + root.removeHandler(hdlr0) + import log_test7 + root.addHandler(hdlr0) + banner("log_test7", "end") + + banner("log_test8", "begin") + import log_test8 + root.removeHandler(hdlr0) + log_test8.main() + root.addHandler(hdlr0) + banner("log_test8", "end") + + banner("log_test9", "begin") + import log_test9 + root.removeHandler(hdlr0) + log_test9.main() + root.addHandler(hdlr0) + banner("log_test9", "end") + + banner("log_test10", "begin") + import log_test10 + log_test10.main() + banner("log_test10", "end") + + #Skip 11 as it tests SMTPHandler, can't easily check results automatically + + import log_test12 + banner("log_test12", "begin") + log_test12.main() + banner("log_test12", "end") + + import log_test13 + banner("log_test13", "begin") + log_test13.main() + banner("log_test13", "end") + + import log_test15 + banner("log_test15", "begin") + log_test15.test(log_test15.FILTER) + banner("log_test15", "end") + + time.sleep(3) + + finally: + #shut down servers + olderr.write("Tidying up...") + tcpserver.abort = 1 + udpserver.abort = 1 + httpserver.abort = 1 + if soapserver: + soapserver.abort = 1 + for thread in threads: + thread.join() + sys.stdout.close() + sys.stdout = oldout + #don't close this, as hdlr0 references it. hdlr0 will be closed at application exit + #sys.stderr.close() + sys.stderr = olderr + print "Test run completed." + +if __name__ == "__main__": + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test0.py b/tools/python/logging/logging-0.4.9.2/test/log_test0.py new file mode 100755 index 0000000..3799841 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test0.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. A basic test of levels. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging +msgcount = 0 + +def nextmessage(): + global msgcount + rv = "Message %d" % msgcount + msgcount = msgcount + 1 + return rv + +def main(): + logging.basicConfig() + logging.getLogger("").setLevel(logging.DEBUG) + ERR = logging.getLogger("ERR") + ERR.setLevel(logging.ERROR) + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + INF_ERR = logging.getLogger("INF.ERR") + INF_ERR.setLevel(logging.ERROR) + DEB = logging.getLogger("DEB") + DEB.setLevel(logging.DEBUG) + + INF_UNDEF = logging.getLogger("INF.UNDEF") + INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") + UNDEF = logging.getLogger("UNDEF") + + GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") + CHILD = logging.getLogger("INF.BADPARENT") + + #These should log + ERR.log(logging.CRITICAL, nextmessage()) + ERR.error(nextmessage()) + + INF.log(logging.CRITICAL, nextmessage()) + INF.error(nextmessage()) + INF.warning(nextmessage()) + INF.info(nextmessage()) + + INF_UNDEF.log(logging.CRITICAL, nextmessage()) + INF_UNDEF.error(nextmessage()) + INF_UNDEF.warning(nextmessage()) + INF_UNDEF.info(nextmessage()) + + INF_ERR.log(logging.CRITICAL, nextmessage()) + INF_ERR.error(nextmessage()) + + INF_ERR_UNDEF.log(logging.CRITICAL, nextmessage()) + INF_ERR_UNDEF.error(nextmessage()) + + DEB.log(logging.CRITICAL, nextmessage()) + DEB.error(nextmessage()) + DEB.warning(nextmessage()) + DEB.info(nextmessage()) + DEB.debug(nextmessage()) + + UNDEF.log(logging.CRITICAL, nextmessage()) + UNDEF.error(nextmessage()) + UNDEF.warning(nextmessage()) + UNDEF.info(nextmessage()) + + GRANDCHILD.log(logging.CRITICAL, nextmessage()) + CHILD.log(logging.CRITICAL, nextmessage()) + + #These should not log + ERR.warning(nextmessage()) + ERR.info(nextmessage()) + ERR.debug(nextmessage()) + + INF.debug(nextmessage()) + INF_UNDEF.debug(nextmessage()) + + INF_ERR.warning(nextmessage()) + INF_ERR.info(nextmessage()) + INF_ERR.debug(nextmessage()) + INF_ERR_UNDEF.warning(nextmessage()) + INF_ERR_UNDEF.info(nextmessage()) + INF_ERR_UNDEF.debug(nextmessage()) + + INF.info("Messages should bear numbers 0 through 24.") + +if __name__ == "__main__": + import sys + #print sys.argv[0] + args = sys.argv[1:] + if "-profile" in args: + import profile, pstats + args.remove("-profile") + statf = "log_test0.pro" + profile.run("main()", statf) + stats = pstats.Stats(statf) + stats.strip_dirs().sort_stats('time').print_stats() + else: + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test1.py b/tools/python/logging/logging-0.4.9.2/test/log_test1.py new file mode 100755 index 0000000..98b9874 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test1.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. A test showing exception handling and use of SysLogHandler. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import sys, logging +import syslog + +class SLHandler(logging.Handler): + def __init__(self, ident, logopt=0, facility=syslog.LOG_USER): + logging.Handler.__init__(self) + self.ident = ident + self.logopt = logopt + self.facility = facility + self.mappings = { + logging.DEBUG: syslog.LOG_DEBUG, + logging.INFO: syslog.LOG_INFO, + logging.WARNING: syslog.LOG_WARNING, + logging.ERROR: syslog.LOG_ERR, + logging.CRITICAL: syslog.LOG_CRIT, + } + + def encodeLevel(self, level): + return self.mappings.get(level, syslog.LOG_INFO) + + def emit(self, record): + syslog.openlog(self.ident, self.logopt, self.facility) + msg = self.format(record) + prio = self.encodeLevel(record.levelno) + syslog.syslog(prio, msg) + syslog.closelog() + +def config(): + logging.basicConfig() + logging.getLogger("").setLevel(logging.DEBUG) + if __name__ == "__main__": + fmt = logging.Formatter("%(asctime)s %(filename)s:%(lineno)d %(levelname)-5s - %(message)s") + hdlr = logging.FileHandler("tmp.tmp") + hdlr.setFormatter(fmt) + logging.getLogger("").addHandler(hdlr) + else: + fmt = None + hdlr = SLHandler("log_test1") + if fmt: + hdlr.setFormatter(fmt) + logging.getLogger("").addHandler(hdlr) + return hdlr + +def run(): + logging.info("Starting...") + try: + print "7" + 4 + except Exception, e: + logging.error("Problem %s (%d)", "ERROR", logging.ERROR, exc_info=1) + logging.debug("Problem %s (%d)", "DEBUG", logging.DEBUG, exc_info=1) + logging.info("Done.") + + +def main(): + hdlr = config() + run() + logging.getLogger("").removeHandler(hdlr) + +if __name__ == "__main__": + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test10.py b/tools/python/logging/logging-0.4.9.2/test/log_test10.py new file mode 100755 index 0000000..839c08c --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test10.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Shows use of a user-defined Logger subclass. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import sys +import locale + +locale.setlocale(locale.LC_ALL, '') + +from logging import * + +LOG_FORMAT = "%(asctime)s %(levelname)-5s %(message)s" +DATE_FORMAT = "%x %X" + +class MyLogger(Logger): + """ + A simple example of a logger extension. + """ + def debug(self, msg, *args, **kwargs): + """ + This overridden method passes exception information for DEBUG level calls + """ + if self.manager.disable >= DEBUG: + return + if DEBUG >= self.getEffectiveLevel(): + exc_info = kwargs.get("exc_info", 0) + ei = None + if exc_info: + ei = sys.exc_info() + if not ei[1]: + ei = None + self._log(DEBUG, msg, args, ei) + del ei + +class NotALogger: + pass + +def config(): + try: + setLoggerClass(NotALogger) + except Exception, e: + sys.stderr.write("%s\n" % e) + setLoggerClass(MyLogger) + if __name__ == "__main__": + basicConfig() + if __name__ == "__main__": + getLogger("").handlers[0].setFormatter(Formatter(LOG_FORMAT, DATE_FORMAT)) + +def run(): + getLogger("").setLevel(DEBUG) + logger = getLogger("mylogger") + logger.info("Starting...") + logger.debug("Debug message not in exception handler (no traceback)") + logger.info("About to throw exception...") + try: + print "7" + 4 + except Exception, e: + logger.debug("Debug message inside exception handler (traceback)",exc_info=1) + logger.info("Done.") + +def main(): + config() + run() + +if __name__ == "__main__": + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test11.py b/tools/python/logging/logging-0.4.9.2/test/log_test11.py new file mode 100755 index 0000000..0aae955 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test11.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Tests BufferingSMTPHandler, an alternative implementation +of SMTPHandler. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import string, logging, logging.handlers + +MAILHOST = 'beta' +FROM = 'log_test11@red-dove.com' +TO = ['arkadi_renko'] +SUBJECT = 'Test Logging email from Python logging module (buffering)' + +class BufferingSMTPHandler(logging.handlers.BufferingHandler): + def __init__(self, mailhost, fromaddr, toaddrs, subject, capacity): + logging.handlers.BufferingHandler.__init__(self, capacity) + self.mailhost = mailhost + self.mailport = None + self.fromaddr = fromaddr + self.toaddrs = toaddrs + self.subject = subject + self.setFormatter(logging.Formatter("%(asctime)s %(levelname)-5s %(message)s")) + + def flush(self): + if len(self.buffer) > 0: + try: + import smtplib + port = self.mailport + if not port: + port = smtplib.SMTP_PORT + smtp = smtplib.SMTP(self.mailhost, port) + msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (self.fromaddr, string.join(self.toaddrs, ","), self.subject) + for record in self.buffer: + s = self.format(record) + print s + msg = msg + s + "\r\n" + smtp.sendmail(self.fromaddr, self.toaddrs, msg) + smtp.quit() + except: + self.handleError(None) # no particular record + self.buffer = [] + +def test(): + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + logger.addHandler(BufferingSMTPHandler(MAILHOST, FROM, TO, SUBJECT, 10)) + for i in xrange(102): + logger.info("Info index = %d", i) + logging.shutdown() + +if __name__ == "__main__": + test() \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test12.py b/tools/python/logging/logging-0.4.9.2/test/log_test12.py new file mode 100755 index 0000000..e5e9be3 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test12.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests HTTPHandler. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import sys, string, logging, logging.handlers + +def main(): + import pdb + host = "localhost:%d" % logging.handlers.DEFAULT_HTTP_LOGGING_PORT + gh = logging.handlers.HTTPHandler(host, '/log', 'GET') + ph = logging.handlers.HTTPHandler(host, '/log', 'POST') + logger = logging.getLogger("log_test12") + logger.propagate = 0 + logger.addHandler(gh) + logger.addHandler(ph) + logging.getLogger("").setLevel(logging.DEBUG) + logger.info("Jackdaws love my big %s of %s", "sphinx", "quartz") + logger.debug("Pack my %s with twelve dozen %s", "box", "liquor jugs") + gh.close() + ph.close() + logger.removeHandler(gh) + logger.removeHandler(ph) + +if __name__ == "__main__": + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test13.py b/tools/python/logging/logging-0.4.9.2/test/log_test13.py new file mode 100755 index 0000000..c054f29 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test13.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Implements a SOAPHandler class which +can be used to form the basis of extended SOAP functionality. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import string, logging, logging.handlers, types + +SOAP_MESSAGE = """ + + +%s + + + +""" + +class SOAPHandler(logging.Handler): + """ + A class which sends records to a SOAP server. + """ + def __init__(self, host, url): + """ + Initialize the instance with the host and the request URL + """ + logging.Handler.__init__(self) + self.host = host + self.url = url + + def emit(self, record): + """ + Send the record to the Web server as a SOAP message + """ + try: + import httplib + h = httplib.HTTP(self.host) + h.putrequest("POST", self.url) + keys = record.__dict__.keys() + keys.sort() + args = "" + for key in keys: + v = record.__dict__[key] + if type(v) == types.StringType: + t = "string" + elif (type(v) == types.IntType) or (type(v) == types.LongType): + t = "integer" + elif type(v) == types.FloatType: + t = "float" + else: + t = "string" + args = args + "%12s%s\n" % ("", + key, t, str(v), key) + data = SOAP_MESSAGE % args[:-1] + h.putheader("Content-type", "text/plain; charset=\"utf-8\"") + h.putheader("Content-length", str(len(data))) + h.endheaders() + #print data + h.send(data) + r = h.getreply() #can't do anything with the result + f = h.getfile() + if f: + #print f.read() + f.close() + except: + self.handleError(record) + +def main(): + sh = SOAPHandler('localhost:%d' % logging.handlers.DEFAULT_SOAP_LOGGING_PORT, '/log') + logger = logging.getLogger("log_test13") + logging.getLogger("").setLevel(logging.DEBUG) + logger.propagate = 0 + logger.addHandler(sh) + logger.info("Jackdaws love my big %s of %s", "sphinx", "quartz") + logger.debug("Pack my %s with five dozen %s", "box", "liquor jugs") + logger.removeHandler(sh) + +if __name__ == "__main__": + main() + diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test14.py b/tools/python/logging/logging-0.4.9.2/test/log_test14.py new file mode 100755 index 0000000..c00121b --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test14.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. An example handler - DBHandler - +which writes to an Python DB API 2.0 data source. You'll need to set this +source up before you run the test. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import sys, string, time, logging + +class DBHandler(logging.Handler): + def __init__(self, dsn, uid='', pwd=''): + logging.Handler.__init__(self) + import mx.ODBC.Windows + self.dsn = dsn + self.uid = uid + self.pwd = pwd + self.conn = mx.ODBC.Windows.connect(self.dsn, self.uid, self.pwd) + self.SQL = """INSERT INTO Events ( + Created, + RelativeCreated, + Name, + LogLevel, + LevelText, + Message, + Filename, + Pathname, + Lineno, + Milliseconds, + Exception, + Thread + ) + VALUES ( + %(dbtime)s, + %(relativeCreated)d, + '%(name)s', + %(levelno)d, + '%(levelname)s', + '%(message)s', + '%(filename)s', + '%(pathname)s', + %(lineno)d, + %(msecs)d, + '%(exc_text)s', + '%(thread)s' + ); + """ + self.cursor = self.conn.cursor() + + def formatDBTime(self, record): + record.dbtime = time.strftime("#%m/%d/%Y#", time.localtime(record.created)) + + def emit(self, record): + try: + #use default formatting + self.format(record) + #now set the database time up + self.formatDBTime(record) + if record.exc_info: + record.exc_text = logging._defaultFormatter.formatException(record.exc_info) + else: + record.exc_text = "" + sql = self.SQL % record.__dict__ + self.cursor.execute(sql) + self.conn.commit() + except: + import traceback + ei = sys.exc_info() + traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) + del ei + + def close(self): + self.cursor.close() + self.conn.close() + logging.Handler.close(self) + +dh = DBHandler('Logging') +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +logger.addHandler(dh) +logger.info("Jackdaws love my big %s of %s", "sphinx", "quartz") +logger.debug("Pack my %s with five dozen %s", "box", "liquor jugs") +try: + import math + math.exp(1000) +except: + logger.exception("Problem with %s", "math.exp") +logging.shutdown() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test15.py b/tools/python/logging/logging-0.4.9.2/test/log_test15.py new file mode 100755 index 0000000..cebb9d8 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test15.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests Filter. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import sys, logging + +FILTER = "a.b" + +def message(s): + sys.stderr.write("%s\n" % s) + +def doLog(): + logging.getLogger("a").info("Info 1") + logging.getLogger("a.b").info("Info 2") + logging.getLogger("a.c").info("Info 3") + logging.getLogger("a.b.c").info("Info 4") + logging.getLogger("a.b.c.d").info("Info 5") + logging.getLogger("a.bb.c").info("Info 6") + logging.getLogger("b").info("Info 7") + logging.getLogger("b.a").info("Info 8") + logging.getLogger("c.a.b").info("Info 9") + logging.getLogger("a.bb").info("Info 10") + +def test(fs): + root = logging.getLogger() + root.setLevel(logging.DEBUG) + if __name__ == "__main__": + hand = logging.StreamHandler() + hand.setFormatter(logging.Formatter("%(name)-10s %(message)s")) + root.addHandler(hand) + else: + hand = root.handlers[0] + message("Unfiltered...") + doLog() + message("Filtered with '%s'..." % fs) + filt = logging.Filter(fs) + hand.addFilter(filt) + doLog() + hand.removeFilter(filt) + +if __name__ == "__main__": + import sys + if len(sys.argv) > 1: + fs = sys.argv[1] + else: + fs = FILTER + test(fs) diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test16.py b/tools/python/logging/logging-0.4.9.2/test/log_test16.py new file mode 100755 index 0000000..f98b627 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test16.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests thread safety. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging, logging.handlers, thread, threading, random + +logging.raiseExceptions = 1 + +NUM_THREADS = 10 +LOOP_COUNT = 10000 + +LOG_MESSAGES = [ + (logging.DEBUG, "%3d This is a %s message", "debug"), + (logging.INFO, "%3d This is an %s message", "informational"), + (logging.WARNING, "%3d This is a %s message", "warning"), + (logging.ERROR, "%3d This is an %s message", "error"), + (logging.CRITICAL, "%3d This is a %s message", "critical"), +] + +LOG_NAMES = ["A", "A.B", "A.B.C", "A.B.C.D"] + +def doLog(num): + logger = logging.getLogger('') + logger.info("*** thread %s started (%d)", thread.get_ident(), num) + for i in xrange(LOOP_COUNT): + logger = logging.getLogger(random.choice(LOG_NAMES)) + a = random.choice(LOG_MESSAGES) + args = a[0:2] + (num,) + a[2:] + apply(logger.log, args) + +def test(): + f = logging.Formatter("%(asctime)s %(levelname)-9s %(name)-8s %(thread)5s %(message)s") + root = logging.getLogger('') + root.setLevel(logging.DEBUG) + h = logging.FileHandler('thread.log', 'w') + root.addHandler(h) + h.setFormatter(f) + h = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT) + #h = logging.handlers.DatagramHandler('localhost', logging.handlers.DEFAULT_UDP_LOGGING_PORT) + root.addHandler(h) + threads = [] + for i in xrange(NUM_THREADS): + threads.append(threading.Thread(target=doLog, args=(len(threads),))) + for t in threads: + t.start() + for t in threads: + t.join() + +if __name__ == "__main__": + test() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test17.py b/tools/python/logging/logging-0.4.9.2/test/log_test17.py new file mode 100755 index 0000000..3a72d5e --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test17.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests thread safety. + +To use as a server, run with no arguments in one process. +To use as a client, run with arguments "-client " where +is the name of a file containing a logging configuration. +The example files debug.ini, warn.ini, error.ini and critical.ini are +provided to use in the test. They each have a customized message format +(prefixed with their name) and the loggers have their levels set to the +value implied by their name. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import sys, logging, logging.config, thread, threading, random, time, struct + +NUM_THREADS = 10 +LOOP_COUNT = 10 + +CONFIG_PORT = 9077 + +logging.raiseExceptions = 1 + +LOG_MESSAGES = [ + (logging.DEBUG, "%3d This is a %s message", "debug"), + (logging.INFO, "%3d This is an %s message", "informational"), + (logging.WARNING, "%3d This is a %s message", "warning"), + (logging.ERROR, "%3d This is an %s message", "error"), + (logging.CRITICAL, "%3d This is a %s message", "critical"), +] + +LOG_NAMES = ["A", "A.B", "A.B.C", "A.B.C.D"] + +def doLog(num): + logger = logging.getLogger('') + logger.setLevel(logging.DEBUG) + logger.info("*** thread %s started (%d)", thread.get_ident(), num) + for i in xrange(LOOP_COUNT): + logger = logging.getLogger(random.choice(LOG_NAMES)) + a = random.choice(LOG_MESSAGES) + args = a[0:2] + (num,) + a[2:] + time.sleep(random.random() * 3) + apply(logger.log, args) + +def runserver(): + f = logging.Formatter("%(asctime)s %(levelname)-9s %(name)-8s %(thread)5s %(message)s") + root = logging.getLogger('') + h = logging.StreamHandler() + root.addHandler(h) + h.setFormatter(f) + threads = [] + for i in xrange(NUM_THREADS): + threads.append(threading.Thread(target=doLog, args=(len(threads),))) + threads.append(logging.config.listen(CONFIG_PORT)) #don't use default port + for t in threads: + t.start() + for t in threads[:-1]: + t.join() + logging.config.stopListening() + threads[-1].join() + +def runclient(fname): + import socket + + print "configuring with '%s'" % fname + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', CONFIG_PORT)) + f = open(fname, "r") + s = f.read() + f.close() + slen = struct.pack(">L", len(s)) + s = slen + s + sentsofar = 0 + left = len(s) + while left > 0: + sent = sock.send(s[sentsofar:]) + sentsofar = sentsofar + sent + left = left - sent + sock.close() + +if __name__ == "__main__": + if "-client" not in sys.argv: + runserver() + else: + sys.argv.remove("-client") + if len(sys.argv) > 1: + fname = sys.argv[1] + else: + fname = "warn.ini" + runclient(fname) \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test18.py b/tools/python/logging/logging-0.4.9.2/test/log_test18.py new file mode 100755 index 0000000..071ff8c --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test18.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests MatchFilter. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging, types, string + +class MatchFilter(logging.Filter): + def __init__(self, **kwargs): + self.dict = kwargs + + def matchOne(self, key, value, record): + rv = getattr(record, key) + if key != "name": + if key not in ["message", "msg"]: + return rv == value + else: + return string.find(str(rv), value) >= 0 + else: + if rv == value: + return 1 + nlen = len(value) + if string.find(rv, value, 0, nlen) != 0: + return 0 + if rv[nlen] == ".": + return 1 + + def matchValue(self, key, record): + vl = self.dict [key] + if type(vl) != types.ListType: + rv = self.matchOne(key, vl, record) + else: + for v in vl: + rv = self.matchOne(key, v, record) + if rv: + break + return rv + + def filter(self, record): + rv = 1 + for k in self.dict.keys(): + if self.matchValue(k, record): + rv = 0 + break + return rv + +def doLog(logger, n): + logger.debug("Debug %d" % n) + logger.info("Info %d" % n) + logger.warning("Warning %d" % n) + logger.error("Error %d" % n) + logger.critical("Critical %d" % n) + +def test(): + fmt = logging.Formatter("%(name)-10s %(levelname)-9s %(message)s") + hand = logging.StreamHandler() + hand.setFormatter(fmt) + root = logging.getLogger("") + root.setLevel(logging.DEBUG) + root.addHandler(hand) + loggers = ['A', + 'A.B', + 'A.BB', + 'A.C', + 'AA.B', + 'A.B.C', + 'A.B.C.D', + 'A.B.C.D.E', + 'Z.A.B', + ] + filt = MatchFilter(name = ['A.C', 'A.B.C'], #reject these loggers and their children + levelno = [logging.WARNING, logging.CRITICAL], #reject these levels, + msg = 'bug 2' #reject if this in message + ) + hand.addFilter(filt) + for log in loggers: + doLog(logging.getLogger(log), loggers.index(log)) + +if __name__ == "__main__": + test() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test19.py b/tools/python/logging/logging-0.4.9.2/test/log_test19.py new file mode 100755 index 0000000..cdfa1a0 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test19.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. A basic test of parents. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging + +def main(): + logging.basicConfig() + root = logging.getLogger("") + ab = logging.getLogger("a.b") + abc = logging.getLogger("a.b.c") + root.setLevel(logging.ERROR) + ab.setLevel(logging.INFO) + abc.info("Info") + abc.warning("Warning") + abc.error("Error") + print "abc = %s" % abc + print "abc.parent = %s" % abc.parent + print "ab = %s" % ab + print "ab.parent = %s" % ab.parent + print "root = %s" % root + +if __name__ == "__main__": + import sys + print sys.argv[0] + args = sys.argv[1:] + if "-profile" in args: + import profile, pstats + args.remove("-profile") + statf = "log_test19.pro" + profile.run("main()", statf) + stats = pstats.Stats(statf) + stats.strip_dirs().sort_stats('time').print_stats() + else: + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test2.py b/tools/python/logging/logging-0.4.9.2/test/log_test2.py new file mode 100755 index 0000000..2ba0ee1 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test2.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests logger levels and basic Formatter, and logging to +sockets. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging, logging.handlers, socket + +msgcount = 0 + +def nextmessage(): + global msgcount + rv = "Message %d" % msgcount + msgcount = msgcount + 1 + return rv + +def main(): + logging.basicConfig() + logging.getLogger("").setLevel(logging.DEBUG) + hdlr = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT) + if __name__ == "__main__": + hdlr.setFormatter(logging.Formatter("%(asctime)s %(name)-19s %(levelname)-5s - %(message)s")) + logging.getLogger("").addHandler(hdlr) + ERR = logging.getLogger("ERR") + ERR.setLevel(logging.ERROR) + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + INF_ERR = logging.getLogger("INF.ERR") + INF_ERR.setLevel(logging.ERROR) + DEB = logging.getLogger("DEB") + DEB.setLevel(logging.DEBUG) + + INF_UNDEF = logging.getLogger("INF.UNDEF") + INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") + UNDEF = logging.getLogger("UNDEF") + + GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") + CHILD = logging.getLogger("INF.BADPARENT") + + #These should log + ERR.log(logging.CRITICAL, nextmessage()) + ERR.error(nextmessage()) + + INF.log(logging.CRITICAL, nextmessage()) + INF.error(nextmessage()) + INF.warning(nextmessage()) + INF.info(nextmessage()) + + INF_UNDEF.log(logging.CRITICAL, nextmessage()) + INF_UNDEF.error(nextmessage()) + INF_UNDEF.warning(nextmessage()) + INF_UNDEF.info(nextmessage()) + + INF_ERR.log(logging.CRITICAL, nextmessage()) + INF_ERR.error(nextmessage()) + + INF_ERR_UNDEF.log(logging.CRITICAL, nextmessage()) + INF_ERR_UNDEF.error(nextmessage()) + + DEB.log(logging.CRITICAL, nextmessage()) + DEB.error(nextmessage()) + DEB.warning(nextmessage()) + DEB.info(nextmessage()) + DEB.debug(nextmessage()) + + UNDEF.log(logging.CRITICAL, nextmessage()) + UNDEF.error(nextmessage()) + UNDEF.warning(nextmessage()) + UNDEF.info(nextmessage()) + + GRANDCHILD.log(logging.CRITICAL, nextmessage()) + CHILD.log(logging.CRITICAL, nextmessage()) + + #These should not log + ERR.warning(nextmessage()) + ERR.info(nextmessage()) + ERR.debug(nextmessage()) + + INF.debug(nextmessage()) + INF_UNDEF.debug(nextmessage()) + + INF_ERR.warning(nextmessage()) + INF_ERR.info(nextmessage()) + INF_ERR.debug(nextmessage()) + INF_ERR_UNDEF.warning(nextmessage()) + INF_ERR_UNDEF.info(nextmessage()) + INF_ERR_UNDEF.debug(nextmessage()) + + INF.info("Messages should bear numbers 0 through 24.") + hdlr.close() + logging.getLogger("").removeHandler(hdlr) + +if __name__ == "__main__": + try: + main() + except socket.error: + print "\nA socket error occurred. Ensure that logrecv.py is running to receive logging requests from this script." diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test20.py b/tools/python/logging/logging-0.4.9.2/test/log_test20.py new file mode 100755 index 0000000..d3701f3 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test20.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Demonstrates the use of custom class +instances for messages and filtering based on classes. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging + +class MyClass: + def __init__(self, arg1, arg2): + self.arg1 = arg1 + self.arg2 = arg2 + + def __str__(self): + return "%s, %s" % (self.arg1, self.arg2) + +class MyChildClass(MyClass): + pass + +class ClassFilter(logging.Filter): + def __init__(self, klass): + self.klass = klass + + def filter(self, record): + return isinstance(record.msg, self.klass) + +class MyClassFilter(ClassFilter): + def __init__(self, arg): + ClassFilter.__init__(self, MyClass) + self.arg = arg + + def filter(self, record): + return ClassFilter.filter(self, record) and (record.msg.arg2 == self.arg) + +def main(): + handler = logging.StreamHandler() + root = logging.getLogger("") + root.setLevel(logging.DEBUG) + root.addHandler(handler) + root.addFilter(MyClassFilter("world")) + #Not logged, as it's not a MyClass instance + root.info("%s, %s", "Hello", "world") + #Logged, as it's an appropriate instance which matches the filter criteria + root.info(MyClass("Hello", "world")) + #Not logged, as it's an appropriate class but doesn't match the filter criteria + root.info(MyClass("Hello", "world!")) + #Logged, as it's an appropriate instance which matches the filter criteria + root.info(MyClass("Goodbye", "world")) + #Logged, as it's an appropriate class which matches the filter criteria + root.info(MyChildClass("Hello again", "world")) + +if __name__ == "__main__": + import sys + args = sys.argv[1:] + if "-profile" in args: + import profile, pstats + args.remove("-profile") + statf = "log_test20.pro" + profile.run("main()", statf) + stats = pstats.Stats(statf) + stats.strip_dirs().sort_stats('time').print_stats() + else: + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test21.py b/tools/python/logging/logging-0.4.9.2/test/log_test21.py new file mode 100755 index 0000000..1626beb --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test21.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Demonstrates the use of a wildcard +name-space filter with and without custom message classes. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging, re, string, types + +class TaggedEvent: + def __init__(self, tag, msg): + self.tag = tag + self.msg = msg + + def __str__(self): + return "%s: %s" % (self.tag, self.msg) + +class WildcardFilter(logging.Filter): + def __init__(self, wildcards): + self.setWildcards(wildcards) + + def setWildcard(self, wildcard): + arr = string.split(wildcard, ".") + for i in xrange(len(arr)): + s = arr[i] + if s == "*": + arr[i] = r'[\w.]*' + elif string.find(s, "*") > 0: + arr[i] = string.replace(s, "*", r'[\w]*') + s = "^%s$" % string.join(arr, r'\.') + self.patterns.append(re.compile(s)) + + def setWildcards(self, wildcards): + if type(wildcards) != types.ListType: + wildcards = [wildcards] + self.patterns = [] + for wildcard in wildcards: + self.setWildcard(wildcard) + + def filter(self, record): + rv = 0 + for pat in self.patterns: + m = pat.match(record.name) + if m is not None: + rv = 1 + break + return rv + +class TagFilter(WildcardFilter): + def filter(self, record): + rv = 0 + if isinstance(record.msg, TaggedEvent): + tag = record.msg.tag + else: + tag = record.name + for pat in self.patterns: + m = pat.match(tag) + if m is not None: + rv = 1 + break + return rv + +def main(): + handler = logging.StreamHandler() + root = logging.getLogger("") + root.setLevel(logging.DEBUG) + ab = logging.getLogger("a.b") + abc = logging.getLogger("a.b.c") + + root.addHandler(handler) + filter = WildcardFilter("*.b") + handler.addFilter(filter) + + ab.info("#1 from a.b") #logged + abc.info("#1 from a.b.c") #not logged + filter.setWildcards("*.b.c") + ab.info("#2 from a.b") #not logged + abc.info("#2 from a.b.c") #logged + filter.setWildcards("*.b*") + ab.info("#3 from a.b") #logged + abc.info("#3 from a.b.c") #not logged + filter.setWildcards("*") + ab.info("#4 from a.b") #logged + abc.info("#4 from a.b.c") #logged + filter.setWildcards("a*") + ab.info("#5 from a.b") #not logged + abc.info("#5 from a.b.c") #not logged + filter.setWildcards("a.*") + ab.info("#6 from a.b") #logged + abc.info("#6 from a.b.c") #logged + filter.setWildcards("*.b.*") + ab.info("#7 from a.b") #not logged + abc.info("#7 from a.b.c") #logged + filter.setWildcards(["*.b", "*.b.*"]) + ab.info("#8 from a.b") #logged + abc.info("#8 from a.b.c") #logged + filter.setWildcards(["a.*.c"]) + ab.info("#9 from a.b") #not logged + abc.info("#9 from a.b.c") #logged + + #Now test filtering with a tagged class + handler.removeFilter(filter) + tagfilter = TagFilter(["*.b", "*.b.*"]) + root.addFilter(tagfilter) + root.info(TaggedEvent("a.b", "#10")) #logged + root.info(TaggedEvent("a.c", "#10")) #not logged + root.info(TaggedEvent("a.b.c", "#10")) #logged + root.info(TaggedEvent("a.b.d", "#10")) #logged + +if __name__ == "__main__": + import sys + args = sys.argv[1:] + if "-profile" in args: + import profile, pstats + args.remove("-profile") + statf = "log_test21.pro" + profile.run("main()", statf) + stats = pstats.Stats(statf) + stats.strip_dirs().sort_stats('time').print_stats() + else: + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test22.py b/tools/python/logging/logging-0.4.9.2/test/log_test22.py new file mode 100755 index 0000000..427ae40 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test22.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Demonstrates the use of different +converters for time(secs) -> time(tuple). + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging, time + +def main(): + handler = logging.StreamHandler() + f1 = logging.Formatter("%(asctime)s %(message)s", "%m/%d %H:%M:%S") + f2 = logging.Formatter("%(asctime)s %(message)s", "%m/%d %H:%M:%S") + f2.converter = time.gmtime + handler.setFormatter(f1) + root = logging.getLogger("") + root.setLevel(logging.DEBUG) + root.addHandler(handler) + root.info("Something happened! [should be in local time]") + handler.setFormatter(f2) + root.info("Something else happened! [should be in GMT]") + handler.setFormatter(f1) + root.info("Something happened again! [should be in local time]") + logging.Formatter.converter = time.gmtime + root.info("Something else happened again! [should be in GMT]") + logging.Formatter.converter = time.localtime + root.info("Something else happened yet again! [should be in local time]") + +if __name__ == "__main__": + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test3.ini b/tools/python/logging/logging-0.4.9.2/test/log_test3.ini new file mode 100644 index 0000000..15b7518 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test3.ini @@ -0,0 +1,95 @@ +[loggers] +keys=root,area1,area11,area111,area1111,area11111,area111111 + +[handlers] +keys=console,file,socket,datagram,syslog,NT,SMTP + +[formatters] +keys=short,medium,long + +[logger_root] +handlers=console +level=DEBUG + +[logger_area1] +level=DEBUG +qualname=log02 +handlers=file + +[logger_area11] +level=INFO +qualname=log02.log03 +handlers=socket + +[logger_area111] +level=WARN +propagate=0 +qualname=log02.log03.log04 +handlers=datagram + +[logger_area1111] +level=ERROR +qualname=log02.log03.log04.log05 +handlers=syslog + +[logger_area11111] +level=CRITICAL +qualname=log02.log03.log04.log05.log06 +handlers=NT + +[logger_area111111] +level=WARN +qualname=log02.log03.log04.log05.log06.log07 +handlers=SMTP + +[handler_console] +class=StreamHandler +formatter=medium +stream=sys.stdout +args=(sys.stdout,) + +[handler_file] +class=FileHandler +level=DEBUG +formatter=long +args=('python.log', 'w') + +[handler_socket] +class=handlers.SocketHandler +level=INFO +formatter=medium +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT) + +[handler_datagram] +class=handlers.DatagramHandler +level=WARN +formatter=medium +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT) + +[handler_syslog] +class=handlers.SysLogHandler +level=ERROR +formatter=short +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) + +[handler_NT] +class=handlers.NTEventLogHandler +level=CRITICAL +formatter=medium +args=('Python Application', '', 'Application') + +[handler_SMTP] +class=handlers.SMTPHandler +level=WARN +formatter=long +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') + +[formatter_short] +format=%(message)s + +[formatter_long] +format=%(asctime)s %(pathname)s(%(lineno)d): %(levelname)s %(message)s + +[formatter_medium] +format=%(asctime)s %(levelname)s %(message)s +datefmt=%m-%d-%Y diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test3.py b/tools/python/logging/logging-0.4.9.2/test/log_test3.py new file mode 100755 index 0000000..09ba9ac --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test3.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests new fileConfig (not yet a complete test). + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import logging, logging.config + +def doLog(logger): + logger.debug("Debug") + logger.info("Info") + logger.warning("Warning") + logger.error("Error") + logger.critical("Critical") + +def main(): + logging.config.fileConfig("log_test3.ini") + logger = logging.getLogger(None) + print "---------------------------------------------------" + print "-- Logging to root; messages appear on console only" + print "---------------------------------------------------" + doLog(logger) + print "----------------------------------------------------------------------" + print "-- Logging to log02; messages appear on console and in file python.log" + print "----------------------------------------------------------------------" + logger = logging.getLogger("log02") + doLog(logger) + print "--------------------------------------------------------------------------" + print "-- Logging to log02.log03; messages appear on console, in file python.log," + print "-- and at logrecv.py tcp (if running. <= DEBUG messages will not appear)." + print "--------------------------------------------------------------------------" + logger = logging.getLogger("log02.log03") + doLog(logger) + print "-----------------------------------------------------------------------" + print "-- Logging to log02.log03.log04; messages appear only at logrecv.py udp" + print "-- (if running. <= INFO messages will not appear)." + print "-----------------------------------------------------------------------" + logger = logging.getLogger("log02.log03.log04") + doLog(logger) + print "--------------------------------------------------------------------" + print "-- Logging to log02.log03.log04.log05.log06; messages appear at" + print "-- logrecv.py udp (if running. < CRITICAL messages will not appear)." + print "--------------------------------------------------------------------" + logger = logging.getLogger("log02.log03.log04.log05.log06") + doLog(logger) + print "-- All done." + logging.shutdown() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test4.py b/tools/python/logging/logging-0.4.9.2/test/log_test4.py new file mode 100755 index 0000000..5b59777 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test4.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests arbitrary logging levels, filtering, and +use of strftime formatting. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +import logging +import locale, sys + +locale.setlocale(locale.LC_ALL, '') + +def message(s): + sys.stderr.write("%s\n" % s) + +# +# First, we define our levels. There can be as many as you want - the only limitations are that +# they should be integers, the lowest should be > 0 and larger values mean less information being +# logged. If you need specific level values which do not fit into these limitations, you can use +# a mapping dictionary to convert between your application levels and the logging system. +# +SILENT = 10 +TACITURN = 9 +TERSE = 8 +EFFUSIVE = 7 +SOCIABLE = 6 +VERBOSE = 5 +TALKATIVE = 4 +GARRULOUS = 3 +CHATTERBOX = 2 +BORING = 1 + +LEVEL_RANGE = range(BORING, SILENT + 1) + +# +# Next, we define names for our levels. You don't need to do this - in which case the system will +# use "Level n" to denote the text for the level. +# +my_logging_levels = { + SILENT : 'Silent', + TACITURN : 'Taciturn', + TERSE : 'Terse', + EFFUSIVE : 'Effusive', + SOCIABLE : 'Sociable', + VERBOSE : 'Verbose', + TALKATIVE : 'Talkative', + GARRULOUS : 'Garrulous', + CHATTERBOX: 'Chatterbox', + BORING : 'Boring', +} + +# +# Now, tell the logging system to associate names with our levels. +# +for lvl in my_logging_levels.keys(): + logging.addLevelName(lvl, my_logging_levels[lvl]) + +# +# Now, define a test function which logs an event at each of our levels. +# +def doLog(log): + for lvl in LEVEL_RANGE: + log.log(lvl, "This should only be seen at the '%s' logging level (or lower)", logging.getLevelName(lvl)) + +# +# Get the root logger and add a console hander to it, when run as a script. +# +log = logging.getLogger("") + +if __name__ == "__main__": + hdlr = logging.StreamHandler() + hdlr.setFormatter(logging.Formatter("%(asctime)s %(message)s", "%X")) #date format is as per the locale + log.addHandler(hdlr) +else: + hdlr = log.handlers[0] +# +# Set the logging level to each different value and call the utility function to log events. +# In the output, you should see that each time round the loop, the logging events actually output +# decreases. +# +for lvl in LEVEL_RANGE: + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) + log.setLevel(lvl) + doLog(log) +# +# Now, we demonstrate level filtering at the handler level. Tell the handler defined above +# to filter at level 'SOCIABLE', and repeat the above loop. Compare the output from the two runs. +# +hdlr.setLevel(SOCIABLE) +message("-- Filtering at handler level to SOCIABLE --") +for lvl in LEVEL_RANGE: + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) + log.setLevel(lvl) + doLog(log) + +hdlr.setLevel(0) #turn off level filtering at the handler + +# +# Now, let's demonstrate filtering. Suppose for some perverse reason we only want to print out +# all except GARRULOUS messages. Let's create a filter for this purpose... +# +class SpecificLevelFilter(logging.Filter): + def __init__(self, lvl): + self.level = lvl + + def filter(self, record): + return self.level != record.levelno + +class GarrulousFilter(SpecificLevelFilter): + def __init__(self): + SpecificLevelFilter.__init__(self, GARRULOUS) + +garr = GarrulousFilter() +hdlr.addFilter(garr) +message("-- Filtering using GARRULOUS filter --") +for lvl in LEVEL_RANGE: + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) + log.setLevel(lvl) + doLog(log) +# +# Now, let's demonstrate filtering at the logger. This time, use a filter which excludes SOCIABLE +# and TACITURN messages. Note that GARRULOUS events are still excluded. +# +class VerySpecificFilter(logging.Filter): + def filter(self, record): + return record.levelno not in [SOCIABLE, TACITURN] + +spec = VerySpecificFilter() +log.addFilter(spec) +message("-- Filtering using specific filter for SOCIABLE, TACITURN --") +for lvl in LEVEL_RANGE: + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) + log.setLevel(lvl) + doLog(log) + +log.removeFilter(spec) +hdlr.removeFilter(garr) +#Undo the one level which clashes...for regression tests +logging.addLevelName(logging.DEBUG, "DEBUG") + +# +# Er...that's it for now +# +if __name__ == "__main__": + print "All done." + logging.shutdown() + diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test5.py b/tools/python/logging/logging-0.4.9.2/test/log_test5.py new file mode 100755 index 0000000..fd8d246 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test5.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests SMTPHandler. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import logging, logging.handlers + +MAILHOST = 'beta' +FROM = 'log_test5@yourdomain.com' +TO = ['arkadi_renko'] +SUBJECT = 'Test Logging email from Python logging module (non-buffering)' + +def main(): + log = logging.getLogger("") + log.setLevel(logging.DEBUG) + hdlr = logging.handlers.SMTPHandler(MAILHOST, FROM, TO, SUBJECT) + hdlr.setFormatter(logging.Formatter("%(asctime)s %(levelname)-5s %(message)s")) + log.addHandler(hdlr) + log.info("Test email contents") + log.removeHandler(hdlr) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test6.py b/tools/python/logging/logging-0.4.9.2/test/log_test6.py new file mode 100755 index 0000000..53ad805 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test6.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests NTEventLogHandler. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import logging, logging.handlers + +def main(): + ntl = logging.handlers.NTEventLogHandler("Python Logging Test") + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + logger.addHandler(ntl) + logger.debug("This is a '%s' message", "Debug") + logger.info("This is a '%s' message", "Info") + logger.warning("This is a '%s' message", "Warning") + logger.error("This is a '%s' message", "Error") + logger.critical("This is a '%s' message", "Critical") + try: + x = 4 / 0 + except: + logger.info("This is an %s (or should that be %s?)", "informational exception", "exceptional information", exc_info=1) + logger.exception("This is the same stuff, via a %s", "exception() call") + logger.removeHandler(ntl) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test7.py b/tools/python/logging/logging-0.4.9.2/test/log_test7.py new file mode 100755 index 0000000..82845ba --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test7.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests MemoryHandler. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import sys, logging, logging.handlers + +def message(s): + sys.stderr.write("%s\n" % s) + +sh = logging.StreamHandler() +mh = logging.handlers.MemoryHandler(10,logging.WARNING, sh) +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +logger.addHandler(mh) +message("-- logging at DEBUG, nothing should be seen yet --") +logger.debug("Debug message") +message("-- logging at INFO, nothing should be seen yet --") +logger.info("Info message") +message("-- logging at WARNING, 3 messages should be seen --") +logger.warning("Warning message") +for i in xrange(102): + message("-- logging %d at level INFO, messages should be seen every 10 events --" % i) + logger.info("Info index = %d", i) +sh.close() +mh.close() +logger.removeHandler(mh) \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test8.py b/tools/python/logging/logging-0.4.9.2/test/log_test8.py new file mode 100755 index 0000000..774b0dd --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test8.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests FileHandler rollover. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import logging, logging.handlers +import locale + +locale.setlocale(locale.LC_ALL, '') + +sequence = 0 + +def doLog(logger): + global sequence + sequence = sequence + 1 + logger.debug("%6d This message should be at level %d - %s", sequence,\ + logging.DEBUG, logging.getLevelName(logging.DEBUG)) + sequence = sequence + 1 + logger.info("%6d This message should be at level %d - %s", sequence, + logging.INFO, logging.getLevelName(logging.INFO)) + sequence = sequence + 1 + logger.warning("%6d This message should be at level %d - %s", sequence,\ + logging.WARNING, logging.getLevelName(logging.WARNING)) + sequence = sequence + 1 + logger.error("%6d This message should be at level %d - %s", sequence,\ + logging.ERROR, logging.getLevelName(logging.ERROR)) + sequence = sequence + 1 + logger.critical("%6d This message should be at level %d - %s", sequence,\ + logging.CRITICAL, logging.getLevelName(logging.CRITICAL)) + +def main(): + logger = logging.getLogger("") #root logger + logger.setLevel(logging.DEBUG) + if __name__ == "__main__": + logname = "rollover.log" + else: + logname = "log_test_rollover.log" + hdlr = logging.handlers.RotatingFileHandler(logname, "a", 5000, 3) + if __name__ == "__main__": + fmt = logging.Formatter("%(asctime)s %(levelname)-5s %(message)s", "%x %X") + hdlr.setFormatter(fmt) + logger.addHandler(hdlr) + for i in xrange(100): + doLog(logger) + logger.removeHandler(hdlr) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/log_test9.py b/tools/python/logging/logging-0.4.9.2/test/log_test9.py new file mode 100755 index 0000000..75653af --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/log_test9.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +""" +A test harness for the logging module. Tests BufferingHandler, BufferingFormatter. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" +import logging, logging.handlers + +class XMLFormatter(logging.BufferingFormatter): + """ + A formatter which formats a set of records using XML, using an example DTD called "logging.dtd". + """ + def __init__(self): + fmtstr = """ + + %(message)s + """ + logging.BufferingFormatter.__init__(self, logging.Formatter(fmtstr)) + +# def formatHeader(self, records): +# return """ +# +#""" +# +# def formatFooter(self, records): +# return "" + +class XMLHandler(logging.handlers.BufferingHandler): + def __init__(self, capacity): + logging.handlers.BufferingHandler.__init__(self, capacity) + self.setFormatter(XMLFormatter()) + + def flush(self): + if len(self.buffer) > 0: + file = open("events.xml","w") + file.write(self.formatter.format(self.buffer)) + file.close() + self.buffer = [] + +def main(): + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + xh = XMLHandler(10) + logger.addHandler(xh) + for i in xrange(100): + logger.info("Info index = %d", i) + xh.close() + logger.removeHandler(xh) + +if __name__ == "__main__": + main() diff --git a/tools/python/logging/logging-0.4.9.2/test/logconf.ini b/tools/python/logging/logging-0.4.9.2/test/logconf.ini new file mode 100644 index 0000000..b74e10c --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/logconf.ini @@ -0,0 +1,180 @@ +[loggers] +keys=root,log02,log03,log04,log05,log06,log07 + +[handlers] +keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09 + +[formatters] +keys=form01,form02,form03,form04,form05,form06,form07,form08,form09 + +[logger_root] +level=NOTSET +propagate=1 +channel= +parent= +qualname=(root) +handlers=hand01 + +[logger_log02] +level=DEBUG +propagate=1 +channel=log02 +parent=(root) +qualname=log02 +handlers=hand02 + +[logger_log03] +level=INFO +propagate=1 +channel=log03 +parent=log02 +qualname=log02.log03 +handlers=hand03 + +[logger_log04] +level=WARN +propagate=0 +channel=log04 +parent=log03 +qualname=log02.log03.log04 +handlers=hand04 + +[logger_log05] +level=ERROR +propagate=1 +channel=log05 +parent=log04 +qualname=log02.log03.log04.log05 +handlers=hand05 + +[logger_log06] +level=CRITICAL +propagate=1 +channel=log06 +parent=log05 +qualname=log02.log03.log04.log05.log06 +handlers=hand06 + +[logger_log07] +level=WARN +propagate=1 +channel=log07 +parent=log06 +qualname=log02.log03.log04.log05.log06.log07 +handlers=hand07 + +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +stream=sys.stdout +args=(sys.stdout,) + +[handler_hand02] +class=FileHandler +level=DEBUG +formatter=form02 +filename=python.log +mode=w +args=('python.log', 'w') + +[handler_hand03] +class=handlers.SocketHandler +level=INFO +formatter=form03 +host=localhost +port=handlers.DEFAULT_TCP_LOGGING_PORT +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT) + +[handler_hand04] +class=handlers.DatagramHandler +level=WARN +formatter=form04 +host=localhost +port=handlers.DEFAULT_UDP_LOGGING_PORT +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT) + +[handler_hand05] +class=handlers.SysLogHandler +level=ERROR +formatter=form05 +host=localhost +port=handlers.SYSLOG_UDP_PORT +facility=LOG_USER +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) + +[handler_hand06] +class=handlers.NTEventLogHandler +level=CRITICAL +formatter=form06 +appname=Python Application +dllname= +logtype=Application +args=('Python Application', '', 'Application') + +[handler_hand07] +class=handlers.SMTPHandler +level=WARN +formatter=form07 +host=localhost +port=25 +from=from@abc +to=user1@abc,user2@xyz +subject=Logger Subject +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') + +[handler_hand08] +class=handlers.MemoryHandler +level=NOTSET +formatter=form08 +capacity=10 +flushlevel=ERROR +target= +args=(10, ERROR) + +[handler_hand09] +class=handlers.HTTPHandler +level=NOTSET +formatter=form09 +host=localhost +port=9022 +url=/log +method=GET +args=('localhost:9022', '/log', 'GET') + +[formatter_form01] +format=F1 %(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form02] +format=F2 %(asctime)s %(pathname)s(%(lineno)d): %(levelname)s %(message)s +datefmt= + +[formatter_form03] +format=F3 %(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form04] +format=%(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form05] +format=F5 %(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form06] +format=F6 %(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form07] +format=F7 %(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form08] +format=F8 %(asctime)s %(levelname)s %(message)s +datefmt= + +[formatter_form09] +format=F9 %(asctime)s %(levelname)s %(message)s +datefmt= + diff --git a/tools/python/logging/logging-0.4.9.2/test/logconf.py b/tools/python/logging/logging-0.4.9.2/test/logconf.py new file mode 100755 index 0000000..f54792f --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/logconf.py @@ -0,0 +1,1738 @@ +#!/usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# + +""" +A simple-minded GUI configurator for the logging module, using Tkinter. + +Should work under Python versions >= 1.5.2. + +Copyright (C) 2002 Vinay Sajip. All Rights Reserved. + +Configuration files are read/written using ConfigParser. +""" +""" + +(C) 2002 Vinay Sajip. All rights reserved. +""" +from Tkinter import * +from tkFileDialog import * +from tkMessageBox import * + +import os, sys, string, types +import ConfigParser + +active = None + +__version__ = "0.4.1" + +DEFAULT_FILENAME = "logconf.ini" + +LOGGING_LEVELS = ( + ("NOTSET", "NOTSET"), + ("DEBUG", "DEBUG"), + ("INFO", "INFO"), + ("WARNING", "WARNING"), + ("ERROR", "ERROR"), + ("CRITICAL", "CRITICAL") +) + +HANDLER_TYPES = ( + ("StreamHandlerProxy", "StreamHandler"), + ("FileHandlerProxy", "FileHandler"), + ("RotatingFileHandlerProxy", "RotatingFileHandler"), + ("SocketHandlerProxy", "SocketHandler"), + ("DatagramHandlerProxy", "DatagramHandler"), + ("SysLogHandlerProxy", "SysLogHandler"), + ("NTEventLogHandlerProxy", "NTEventLogHandler"), + ("SMTPHandlerProxy", "SMTPHandler"), + ("MemoryHandlerProxy", "MemoryHandler"), + ("HTTPHandlerProxy", "HTTPHandler"), +# ("SOAPHandlerProxy", "SOAPHandler"), +) + +OUTPUT_STREAMS = ( + ("sys.stdout", "sys.stdout"), + ("sys.stderr", "sys.stderr") +) + +FILE_MODES = ( + ("a", "a"), + ("w", "w") + ) + +HTTP_METHODS = ( + ("GET", "GET"), + ("POST", "POST") +) + +SYSLOG_FACILITIES = ( + ("LOG_AUTH", "auth"), + ("LOG_AUTHPRIV", "authpriv"), + ("LOG_CRON", "cron"), + ("LOG_DAEMON", "daemon"), + ("LOG_KERN", "kern"), + ("LOG_LPR", "lpr"), + ("LOG_MAIL", "mail"), + ("LOG_NEWS", "news"), + ("LOG_AUTH", "security"), + ("LOG_SYSLOG", "syslog"), + ("LOG_USER", "user"), + ("LOG_UUCP", "uucp"), + ("LOG_LOCAL0", "local0"), + ("LOG_LOCAL1", "local1"), + ("LOG_LOCAL2", "local2"), + ("LOG_LOCAL3", "local3"), + ("LOG_LOCAL4", "local4"), + ("LOG_LOCAL5", "local5"), + ("LOG_LOCAL6", "local6"), + ("LOG_LOCAL7", "local7"), +) + +LOG_TYPES = ( + ("Application", "Application"), + ("System", "System"), + ("Security", "Security") +) + +BOOLEAN_VALUES = ( + ("0", "False"), + ("1", "True") +) + +class Property: + def __init__(self, name, caption, value=None, choices=None): + self.name = name + self.caption = caption + self.value = value + self.choices = choices + + def getChoices(self): + return self.choices + + def isvalid(self, s): + return 0 + + def getCaption(self): + return self.caption + + def getValue(self): + return self.value + + def getChoiceText(self, val): + rv = "" + choices = self.getChoices() + if choices: + for choice in choices: + if choice[0] == val: + rv = choice[1] + break + return rv + + def setValue(self, val): + self.value = val + + def getValueText(self): + if type(self.value) in [types.ListType, types.TupleType]: + v = list(self.value) + else: + v = [self.value] + choices = self.getChoices() + if choices: + v = map(self.getChoiceText, v) + return string.join(v, ',') + +class PropertyHolder: + def __init__(self, dict): + self.dict = dict + self.propnames = [] + self.onPropListChanged = None + + def getPropNames(self): + """ + Return the property names in the order in which they are to + be listed. + """ + return self.propnames + + def getProp(self, name): + return self.dict[name] + + def isReadonly(self, name): + return 0 + + #convenience methods + def getPropValue(self, name): + return self.dict[name].value + + def setPropValue(self, name, value): + self.dict[name].setValue(value) + +LINE_COLOUR = '#999999' + +class ScrollingList(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent) + self.parent = parent + self.listener = self.parent + self.sb = Scrollbar(self, orient=VERTICAL) + kwargs["yscrollcommand"] = self.sb.set + self.list = apply(Listbox, (self,) + args, kwargs) + self.sb.config(command=self.list.yview) + self.sb.pack(side=RIGHT, fill=Y) + self.list.pack(side=LEFT, fill=BOTH,expand=1) + self.list.bind('', self.onListChange) + self.choices = None + + def setContents(self, choices, value): + self.choices = choices + self.value = value + self.list.delete(0, END) + if type(value) == types.ListType: + sm = EXTENDED + else: + sm = BROWSE + self.list.configure(selectmode=sm) + i = 0 + for choice in choices: + self.list.insert(END, choice[1]) + if sm == EXTENDED: + if choice[0] in value: + self.list.select_set(i) + else: + if choice[0] == value: + self.list.select_set(i) + i = i + 1 + + def getValue(self): + if type(self.value) == types.ListType: + multi = 1 + rv = [] + else: + multi = 0 + for i in xrange(len(self.choices)): + if self.list.select_includes(i): + if not multi: + rv = self.choices[i][0] + break + else: + rv.append(self.choices[i][0]) + return rv + + def onListChange(self, event): + self.value = self.getValue() + self.listener.onListChange(self.value) + +class PropertyHeader(Canvas): + def __init__(self, parent, *args, **kwargs): + self.namewidth = 120 + if kwargs.has_key("namewidth"): + self.namewidth = kwargs["namewidth"] + del kwargs["namewidth"] + self.rowheight = 16 + if kwargs.has_key("rowheight"): + self.rowheight = kwargs["rowheight"] + del kwargs["rowheight"] + apply(Canvas.__init__, (self, parent)+args, kwargs) + self.bind('', self.onConfigure) + x = 5 + y = 0 + wid = int(self.cget('width')) + self.create_text(x, y, text='Property', anchor='nw') + self.create_text(x + self.namewidth, y, text='Value', anchor='nw') + self.create_line(self.namewidth, 0, self.namewidth, self.rowheight, fill=LINE_COLOUR) + self.tline = self.create_line(0, 0, wid, 0, fill=LINE_COLOUR) + #self.create_line(0, 0, 0, self.rowheight, fill=LINE_COLOUR) + #self.create_line(wid - 1, 0, wid - 1, self.rowheight, fill=LINE_COLOUR) + + def onConfigure(self, event): + self.delete(self.tline) + self.tline = self.create_line(0, 0, event.width, 0, fill=LINE_COLOUR) + +_popup = None + +class PropertyCanvas(Canvas): + def __init__(self, parent, *args, **kwargs): + self.namewidth = 120 + if kwargs.has_key("namewidth"): + self.namewidth = kwargs["namewidth"] + del kwargs["namewidth"] + self.rowheight = 16 + if kwargs.has_key("rowheight"): + self.rowheight = kwargs["rowheight"] + del kwargs["rowheight"] + apply(Canvas.__init__, (self, parent)+args, kwargs) + self.namitems = [] + self.valitems = [] + self.lines = [] + self.pnames = [] + #Event bindings... + self.bind('', self.onEnter) + self.bind('', self.onClick) + self.bind('', self.onConfigure) + self.button = Button(height=self.rowheight, width=self.rowheight, text='...', command=self.onEdit) + self.btnitem = None + self.editor = Entry() + self.edititem = None + self.popup = Toplevel() + self.popup.withdraw() + self.popup.overrideredirect(1) + self.list = ScrollingList(self.popup, background='white', relief=FLAT, borderwidth=0) + self.list.pack(fill=BOTH, expand=1) + self.list.listener = self + self.listvisible = 0 + + def clear(self): + for itm in self.namitems: + self.delete(itm) + self.namitems = [] + for itm in self.valitems: + self.delete(itm) + self.valitems = [] + for lin in self.lines: + self.delete(lin) + self.lines = [] + + def setPropertyHolder(self, ph): + self.ph = ph + self.pnames = ph.getPropNames() + wid = int(self.cget('width')) + hei = int(self.cget('height')) + self.clear() + x = 5 + y = 0 + i = 0 + self.props = [] + for n in self.pnames: + prop = self.ph.getProp(n) + self.props.append(prop) + tn = "n%d" % i + tv = "v%d" % i + self.namitems.append(self.create_text(x, y + 2, text=prop.getCaption(), anchor='nw', tags=tn)) + self.valitems.append(self.create_text(x + self.namewidth, y + 2, text=prop.getValueText(), anchor='nw', tags=tv)) + y = y + self.rowheight + i = i + 1 + self.drawLines(wid, hei) + #self.config(height=y) + + def drawLines(self, wid, hei): + for lin in self.lines: + self.delete(lin) + self.lines = [] + y = 0 + for i in xrange(len(self.pnames)): + self.lines.append(self.create_line(0, y, wid, y, fill=LINE_COLOUR)) + y = y + self.rowheight + self.lines.append(self.create_line(0, y, wid, y, fill=LINE_COLOUR)) + self.create_line(self.namewidth, 0, self.namewidth, hei, fill=LINE_COLOUR) + + def onEnter(self, event): + if not self.edititem and not self.listvisible: + self.focus_set() + + def hideControls(self): + if self.listvisible: + self.popup.withdraw() + global _popup + _popup = None + self.listvisible = 0 + if self.edititem: + self.ph.setPropValue(self.editprop.name, self.editor.get()) + self.itemconfig(self.valitems[self.editrow], text=self.editprop.getValueText()) + self.delete(self.edititem) + self.edititem = None + if self.btnitem: + self.delete(self.btnitem) + self.btnitem = None + + def onClick(self, event): + row = event.y / self.rowheight + self.hideControls() + if row < len(self.pnames): + wid = int(self.cget('width')) + hei = self.rowheight + prop = self.props[row] + if not self.ph.isReadonly(self.pnames[row]): + self.editrow = row + self.editprop = prop + choices = prop.getChoices() + if choices != None: + val = prop.getValue() + self.list.setContents(choices, val) + self.listy = row * hei + self.rowheight + self.btnitem = self.create_window(wid - hei, row * hei, width=hei, height=hei, window=self.button, anchor='nw', tags='button') + else: + self.editor.delete(0, END) + self.editor.insert(0, prop.getValueText()) + self.editor.select_range(0, END) + self.edititem = self.create_window(self.namewidth + 1, row * hei, width=wid - self.namewidth, height = hei + 1, window=self.editor, anchor='nw', tags='editor') + self.editor.focus_set() + + def onConfigure(self, event): + self.hideControls() + self.drawLines(event.width, event.height) + self.configure(width=event.width, height=event.height) + + def onEdit(self): + wid = int(self.cget('width')) + #self.listitem = self.create_window(self.namewidth + 1, self.listy, width=wid - self.namewidth - 1, height = self.rowheight * 3, window=self.list, anchor='nw', tags='list') + w = wid - self.namewidth - 1 + h = self.rowheight * 5 + x = self.winfo_rootx() + self.namewidth + 1 + y = self.winfo_rooty() + self.listy + s = "%dx%d+%d+%d" % (w, h, x, y) + self.popup.deiconify() + self.popup.lift() + self.popup.focus_set() + self.listvisible = 1 + self.list.focus_set() + #For some reason with 1.5.2 (Windows), making the geometry call + #immediately following the assignment to s doesn't work. So we + #do it here + self.popup.geometry(s) + global _popup + _popup = self.popup + + def onListChange(self, val): + self.ph.setPropValue(self.editprop.name, val) + self.itemconfig(self.valitems[self.editrow], text=self.editprop.getValueText()) + if type(val) != types.ListType: + self.hideControls() + +class PropertyEditor(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent) + self.parent = parent + nw = kwargs.get("namewidth", 120) + rh = kwargs.get("rowheight", 16) + wid = kwargs.get("width", 300) + hei = kwargs.get("height", 60) + self.header = PropertyHeader(self, namewidth=nw, rowheight=rh, height=14, highlightthickness=0) + self.body = PropertyCanvas(self, namewidth=nw, rowheight=rh, width=wid, height=hei, background='white', highlightthickness=0) + self.header.pack(side=TOP, fill=X) + self.body.pack(side=BOTTOM, fill=BOTH, expand=1) + + def setPropertyHolder(self, ph): + self.body.setPropertyHolder(ph) + +class ADUPanel(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.add = Button(self, text="New", command=parent.onAdd) + self.add.pack(side=LEFT) #, fill=X, expand=1) + self.rmv = Button(self, text="Delete", command=parent.onDelete) + self.rmv.pack(side=LEFT) #, fill=X, expand=1) + #self.upd = Button(self, text="Update", command=parent.onUpdate) + #self.upd.pack(side=RIGHT, fill=X, expand=1) + +class ScrollList(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent) + self.parent = parent + self.sb = Scrollbar(self, orient=VERTICAL) + kwargs["yscrollcommand"] = self.sb.set + self.list = apply(Listbox, (self,) + args, kwargs) + self.sb.config(command=self.list.yview) + self.sb.pack(side=RIGHT, fill=Y) + self.list.pack(side=LEFT, fill=BOTH,expand=1) + +def sortqn(log1, log2): + qn1 = log1.getQualifiedName() + qn2 = log2.getQualifiedName() + if qn1 == "(root)": + rv = -1 + elif qn2 == "(root)": + rv = 1 + else: + rv = cmp(qn1, qn2) + return rv + +def sortn(obj1, obj2): + return cmp(obj1.getPropValue("name"), obj2.getPropValue("name")) + +class LoggerPanel(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + label = Label(self, text="Loggers:") + label.grid(row=0, column=0, sticky='w') + self.slist = ScrollList(self, height=15, background='white') + self.slist.list.bind('', self.onListChange) + self.slist.grid(row=1, column=0, sticky="nsew") + self.adu = ADUPanel(self) + self.adu.grid(row=2, column=0, sticky="we") + label = Label(self, text="Properties of selected logger:") + label.grid(row=3, column=0, sticky='w') + self.pe = PropertyEditor(self, height=120, borderwidth=1) + self.pe.grid(row=4, column=0, sticky='nsew') + self.columnconfigure(0, weight=1) + self.rowconfigure(1, weight=3) + self.rowconfigure(4, weight=1) + + def setConfig(self, config): + self.config = config + #populate list of loggers + llist = config.getLoggers() + llist.sort(sortqn) + self.slist.list.delete(0, END) + self.pe.body.clear() + self.names = [] + for logger in llist: + self.names.append(logger.getPropValue("name")) + self.slist.list.insert(END, logger.getQualifiedName()) + + def onAdd(self): + items = self.slist.list.curselection() + if not len(items): + showerror("No Parent Selected", "You haven't selected a parent logger.") + else: + idx = int(items[0]) + parent = self.config.getLogger(self.names[idx]) + log = self.config.getLogger(None) + log.onChannelChanged = self.onChannelChanged + log.setPropValue("parent", parent.getPropValue("name")) + self.names.insert(1 + idx, log.getPropValue("name")) + self.slist.list.insert(1 + idx, log.getQualifiedName()) + self.slist.list.select_clear(0, END) + self.slist.list.select_set(1 + idx) + self.pe.setPropertyHolder(log) + + def onDelete(self): + items = self.slist.list.curselection() + if not len(items): + showerror("No Item Selected", "You haven't selected anything to delete.") + else: + idx = int(items[0]) + name = self.slist.list.get(idx) + if name == "(root)": + showerror("Root Item Selected", "You cannot delete the root logger.") + else: + resp = askyesno("Logger Deletion", "Are you sure you want to delete logger '%s'?" % name) + if resp: + #self.config.removeLogger(self.names[idx]) + log = self.config.getLogger(self.names[idx]) + log.deleted = 1 + self.slist.list.delete(idx) + del self.names[idx] + self.pe.body.clear() + + def onChannelChanged(self, nm, chname): + i = self.names.index(nm) + sel = i + while i < len(self.names): + log = self.config.getLogger(self.names[i]) + self.slist.list.delete(i) + self.slist.list.insert(i, log.getQualifiedName()) + i = i + 1 + self.slist.list.select_clear(0, END) + self.slist.list.select_set(sel) + + def onListChange(self, event): + self.pe.body.hideControls() + items = self.slist.list.curselection() + idx = int(items[0]) + name = self.names[idx] + log = self.config.getLogger(name) + self.pe.setPropertyHolder(log) + +class HandlerPanel(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + label = Label(self, text="Handlers:") + label.grid(row=0, column=0, sticky='w') + self.slist = ScrollList(self, height=6, background='white') + self.slist.list.bind('', self.onListChange) + self.slist.grid(row=1, column=0, sticky="nsew") + self.adu = ADUPanel(self) + self.adu.grid(row=2, column=0, sticky="we") + label = Label(self, text="Properties of selected handler:") + label.grid(row=3, column=0, sticky='w') + self.pe = PropertyEditor(self, height=90, borderwidth=1) + self.pe.grid(row=4, column=0, sticky='nsew') + self.columnconfigure(0, weight=1) + self.rowconfigure(1, weight=1) + self.rowconfigure(4, weight=1) + + def setConfig(self, config): + self.config = config + #populate list of handlers + hlist = config.getHandlers() + hlist.sort(sortn) + self.slist.list.delete(0, END) + self.pe.body.clear() + for hand in hlist: + hand.onPropListChanged = self.onPropListChanged + self.slist.list.insert(END, hand.getPropValue("name")) + + def onAdd(self): + self.pe.body.hideControls() + hand = self.config.getHandler(None) + self.slist.list.insert(END, hand.getProp("name").getValueText()) + self.slist.list.select_clear(0, END) + self.slist.list.select_set(END) + hand.onPropListChanged = self.onPropListChanged + self.pe.setPropertyHolder(hand) + + def onDelete(self): + items = self.slist.list.curselection() + if not len(items): + showerror("No Item Selected", "You haven't selected anything to delete") + else: + name = self.slist.list.get(int(items[0])) + log = self.config.handlerIsUsed(name) + if log: + showerror("Handler in use", + "The handler '%s' is being used by logger '%s'"\ + ", so it cannot be deleted." % ( + name, log)) + else: + self.config.removeHandler(name) + self.slist.list.delete(items) + self.pe.body.clear() + + def onUpdate(self): + print "handler update" + + def onListChange(self, event): + self.pe.body.hideControls() + items = self.slist.list.curselection() + name = self.slist.list.get(int(items[0])) + hand = self.config.getHandler(name) + self.pe.setPropertyHolder(hand) + + def onPropListChanged(self, newhand): + newhand.onPropListChanged = self.onPropListChanged + self.pe.setPropertyHolder(newhand) + +class FormatterPanel(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + label = Label(self, text="Formatters:") + label.grid(row=0, column=0, sticky='w') + self.slist = ScrollList(self, height=4, background='white') + self.slist.list.bind('', self.onListChange) + self.slist.grid(row=1, column=0, sticky="nsew") + self.adu = ADUPanel(self) + self.adu.grid(row=2, column=0, sticky="ew") + label = Label(self, text="Properties of selected formatter:") + label.grid(row=3, column=0, sticky='w') + self.pe = PropertyEditor(self, height=60, borderwidth=1) + self.pe.grid(row=4, column=0, sticky='nsew') + self.columnconfigure(0, weight=1) + self.rowconfigure(1, weight=1) + self.rowconfigure(4, weight=1) + + def setConfig(self, config): + self.config = config + #populate list of formatters + flist = config.getFormatters() + flist.sort(sortn) + self.slist.list.delete(0, END) + self.pe.body.clear() + for form in flist: + self.slist.list.insert(END, form.getPropValue("name")) + + def onAdd(self): + self.pe.body.hideControls() + fmt = self.config.getFormatter(None) + self.slist.list.insert(END, fmt.getProp("name").getValueText()) + self.slist.list.select_clear(0, END) + i = self.slist.list.size() + self.slist.list.select_set(i - 1) + self.pe.setPropertyHolder(fmt) + + def onDelete(self): + self.pe.body.hideControls() + items = self.slist.list.curselection() + if not len(items): + showerror("No Item Selected", "You haven't selected anything to delete") + else: + name = self.slist.list.get(int(items[0])) + h = self.config.formatterIsUsed(name) + if h: + showerror("Formatter in use", + "The formatter '%s' is being used by handler '%s'"\ + ", so it cannot be deleted." % ( + name, h)) + else: + self.config.removeFormatter(name) + self.slist.list.delete(items) + self.pe.body.clear() + + def onUpdate(self): + self.pe.body.hideControls() + + def onListChange(self, event): + self.pe.body.hideControls() + items = self.slist.list.curselection() + name = self.slist.list.get(int(items[0])) + fmt = self.config.getFormatter(name) + self.pe.setPropertyHolder(fmt) + +class FilterPanel(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + label = Label(self, text="Filters:") + label.grid(row=0, column=0, sticky='w') + self.slist = ScrollList(self, height=4, background='white') + self.slist.list.bind('', self.onListChange) + self.slist.grid(row=1, column=0, sticky="nsew") + self.adu = ADUPanel(self) + self.adu.grid(row=2, column=0, sticky="ew") + label = Label(self, text="Properties of selected filter:") + label.grid(row=3, column=0, sticky='w') + self.pe = PropertyEditor(self, height=60, borderwidth=1) + self.pe.grid(row=4, column=0, sticky='nsew') + self.columnconfigure(0, weight=1) + self.rowconfigure(1, weight=1) + self.rowconfigure(4, weight=1) + + def setConfig(self, config): + self.config = config + #populate list of filters + flist = config.getFilters() + flist.sort(sortn) + self.slist.list.delete(0, END) + self.pe.body.clear() + for filt in flist: + self.slist.list.insert(END, filt.getPropValue("name")) + + def onAdd(self): + self.pe.body.hideControls() + filt = self.config.getFilter(None) + self.slist.list.insert(END, filt.getProp("name").getValueText()) + self.slist.list.select_clear(0, END) + i = self.slist.list.size() + self.slist.list.select_set(i - 1) + self.pe.setPropertyHolder(filt) + + def onDelete(self): + self.pe.body.hideControls() + items = self.slist.list.curselection() + if not len(items): + showerror("No Item Selected", "You haven't selected anything to delete") + else: + name = self.slist.list.get(int(items[0])) + h = self.config.filterIsUsed(name) + if h: + showerror("Filter in use", + "The filter '%s' is being used by '%s'"\ + ", so it cannot be deleted." % ( + name, h)) + else: + self.config.removeFilter(name) + self.slist.list.delete(items) + self.pe.body.clear() + + def onUpdate(self): + self.pe.body.hideControls() + + def onListChange(self, event): + self.pe.body.hideControls() + items = self.slist.list.curselection() + name = self.slist.list.get(int(items[0])) + filt = self.config.getFilter(name) + self.pe.setPropertyHolder(filt) + +class ConfigPanel(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.load = Button(self, text="Load...", command=parent.onLoad) + self.load.pack(side=LEFT) + self.save = Button(self, text="Save", command=parent.onSave) + self.save.pack(side=LEFT) + self.save = Button(self, text="Save as...", command=parent.onSaveAs) + self.save.pack(side=LEFT) + self.reset = Button(self, text="Reset", command=parent.onReset) + self.reset.pack(side=RIGHT) + +class Configurator(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.llist = LoggerPanel(self) + self.llist.grid(row=0, column=0, rowspan=2, sticky='nsew') + spacer = Canvas(self, width=2, highlightthickness=0) + spacer.grid(row=0, column=1, rowspan=2, sticky='ns') + self.hlist = HandlerPanel(self) + self.hlist.grid(row=0, column=2, sticky='nsew') + self.flist = FormatterPanel(self) + self.flist.grid(row=1, column=2, sticky='nsew') + self.cfg = ConfigPanel(self) + self.cfg.grid(row=2, column=0, columnspan=2, sticky='w') + self.filename = None + + self.rowconfigure(0, weight=1) + self.columnconfigure(0, weight=1) + self.columnconfigure(2, weight=1) + + label = Label(self, text="Copyright (C) 2002 Vinay Sajip. All rights reserved.", foreground='brown') + label.grid(row=3, column=0, columnspan=2, sticky='w') + + if len(sys.argv) > 1: + fn = sys.argv[1] + try: + self.loadFile(fn) + except Exception, e: + print e + raise + else: + self.onReset(0) + self.setTitle() + self.focus_set() + + def setTitle(self): + if self.filename: + s = os.path.split(self.filename)[1] + else: + s = "untitled" + self.winfo_toplevel().title("%s - Python Logging Configurator V%s" % (s, __version__)) + + def loadFile(self, fn): + self.config = LoggingConfig() + self.config.read(fn) + self.filename = fn + self.llist.setConfig(self.config) + self.hlist.setConfig(self.config) + self.flist.setConfig(self.config) + self.setTitle() + + def onLoad(self): + fn = askopenfilename(title="Choose configuration file", filetypes=[("Logging configurations", "*.ini"), ("All files", "*.*")]) + if fn: + self.loadFile(fn) + + def onSaveAs(self): + if self.filename: + fn = os.path.split(self.filename)[1] + else: + fn = DEFAULT_FILENAME + fn = asksaveasfilename(title="Save configuration as", initialfile=fn, filetypes=[("Logging configurations", "*.ini"), ("All files", "*.*")]) + if fn: + self.config.save(fn) + self.filename = fn + self.setTitle() + + def onSave(self): + if not self.filename: + self.onSaveAs() + else: + self.config.save(self.filename) + + def onReset(self, confirm=1): + if not confirm: + doit = 1 + else: + doit = askyesno("Reset", "Are you sure you want to reset?") + if doit: + self.config = LoggingConfig() + self.llist.setConfig(self.config) + self.hlist.setConfig(self.config) + self.flist.setConfig(self.config) + self.setTitle() + +# -- general properties + +class NameProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "name", "Name", value) + +class LevelProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "level", "Level", value) + + def getChoices(self): + return LOGGING_LEVELS + +# -- formatter properties + +class FormatProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "format", "Format", value) + +class DateFormatProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "datefmt", "Date Format", value) + +class FormatterProxy(PropertyHolder): + def __init__(self, config, dict): + self.config = config + PropertyHolder.__init__(self, dict) + prop = NameProperty(dict.get("name", "")) + self.dict["name"] = prop + prop = FormatProperty(dict.get("format", "%(asctime)s %(levelname)s %(message)s")) + self.dict["format"] = prop + prop = DateFormatProperty(dict.get("datefmt", "")) + self.dict["datefmt"] = prop + self.propnames = ["name", "format", "datefmt"] + + def isReadonly(self, name): + return name == "name" + + def writeConfig(self, file): + file.write("[formatter_%s]\n" % self.getPropValue("name")) + file.write("format=%s\n" % self.getPropValue("format")) + file.write("datefmt=%s\n\n" % self.getPropValue("datefmt")) + +# -- filter properties + +class LoggerNameProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "lname", "Name", value) + +class FilterProxy(PropertyHolder): + def __init__(self, config, dict): + self.config = config + PropertyHolder.__init__(self, dict) + prop = NameProperty(dict.get("name", "")) + self.dict["name"] = prop + prop = LoggerNameProperty(dict.get("lname", "")) + self.dict["lname"] = prop + self.propnames = ["name", "lname"] + + def isReadonly(self, name): + return name == "name" + + def writeConfig(self, file): + file.write("[filter_%s]\n" % self.getPropValue("name")) + file.write("lname=%s\n" % self.getPropValue("lname")) + +# -- handler properties and proxies + +class HandlerTypeProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "class", "Type", value) + + def getChoices(self): + return HANDLER_TYPES + +class FormatterProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "formatter", "Formatter", value) + + def getChoices(self): + return self.config.getFormatterChoice() + +class HandlerProxy(PropertyHolder): + def __init__(self, config, dict): + self.config = config + PropertyHolder.__init__(self, dict) + prop = NameProperty(dict.get("name", "")) + self.dict["name"] = prop + prop = HandlerTypeProperty(dict.get("class", "StreamHandlerProxy")) + self.dict["class"] = prop + prop = FormatterProperty(self.config, dict.get("formatter", "")) + self.dict["formatter"] = prop + prop = LevelProperty(dict.get("level", "NOTSET")) + self.dict["level"] = prop + self.propnames = ["name", "class", "level", "formatter"] + + def isReadonly(self, name): + return (name == "name") + + def setPropValue(self, name, value): + PropertyHolder.setPropValue(self, name, value) + if (name == "class"): #morph type of handler + #print "try morph -> %s" % value + try: + klass = eval(value) + except Exception, e: + print e + klass = None + if klass: + n = self.getPropValue("name") + d = { + "name": n, + "class": value, + "formatter": self.getPropValue("formatter"), + "level": self.getPropValue("level"), + } + newhand = klass(self.config, d) + self.config.handlers[n] = newhand #FIXME encapsulation + if self.onPropListChanged: + self.onPropListChanged(newhand) + + def writeConfig(self, file): + file.write("[handler_%s]\n" % self.getPropValue("name")) + s = self.getProp("class").getValueText() + if not s in ["StreamHandler", "FileHandler"]: + s = "handlers." + s + file.write("class=%s\n" % s) + file.write("level=%s\n" % self.getPropValue("level")) + file.write("formatter=%s\n" % self.getPropValue("formatter")) + +class StreamProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "stream", "Stream", value) + + def getChoices(self): + return OUTPUT_STREAMS + +class StreamHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = StreamProperty(self.config, dict.get("stream", "sys.stderr")) + self.dict["stream"] = prop + self.propnames.append("stream") + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + file.write("stream=%s\n" % self.getPropValue("stream")) + file.write("args=(%s,)\n\n" % self.getPropValue("stream")) + + def readConfig(self, sectname): + prop = StreamProperty(self.config, self.config.get(sectname, "stream")) + self.dict["stream"] = prop + self.propnames.append("stream") + +class FilenameProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "filename", "File name", value) + +class ModeProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "mode", "Mode", value) + + def getChoices(self): + return FILE_MODES + +class MaxSizeProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "maxsize", "Maximum Size (bytes)", value) + +class BackupCountProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "backcount", "Backup Count", value) + +class FileHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = FilenameProperty(dict.get("filename", "python.log")) + self.dict["filename"] = prop + prop = ModeProperty(dict.get("mode", "a")) + self.dict["mode"] = prop + self.propnames.extend(["filename", "mode"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + fn = self.getPropValue("filename") + file.write("filename=%s\n" % fn) + mode = self.getPropValue("mode") + file.write("mode=%s\n" % mode) + file.write("args=('%s', '%s')\n\n" % (fn, mode)) + + def readConfig(self, sectname): + prop = FilenameProperty(self.config.get(sectname, "filename")) + self.dict["filename"] = prop + prop = ModeProperty(self.config.get(sectname, "mode")) + self.dict["mode"] = prop + self.propnames.extend(["filename", "mode"]) + +class RotatingFileHandlerProxy(FileHandlerProxy): + def __init__(self, config, dict): + FileHandlerProxy.__init__(self, config, dict) + prop = MaxSizeProperty(dict.get("maxsize", "0")) + self.dict["maxsize"] = prop + prop = BackupCountProperty(dict.get("backcount", "1")) + self.dict["backcount"] = prop + self.propnames.extend(["maxsize", "backcount"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + fn = self.getPropValue("filename") + file.write("filename=%s\n" % fn) + mode = self.getPropValue("mode") + file.write("mode=%s\n" % mode) + ms = self.getPropValue("maxsize") + file.write("maxsize=%s\n" % ms) + bc = self.getPropValue("backcount") + file.write("backcount=%s\n" % bc) + file.write("args=('%s', '%s', %s, %s)\n\n" % (fn, mode, ms, bc)) + + def readConfig(self, sectname): + FileHandlerProxy.readConfig(self, sectname) + prop = MaxSizeProperty(self.config.get(sectname, "maxsize")) + self.dict["maxsize"] = prop + prop = BackupCountProperty(self.config.get(sectname, "backcount")) + self.dict["backcount"] = prop + self.propnames.extend(["maxsize", "backcount"]) + + +class HostProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "host", "Host", value) + +class PortProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "port", "Port", value) + +class SocketHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = HostProperty(dict.get("host", "localhost")) + self.dict["host"] = prop + prop = PortProperty(dict.get("port", "handlers.DEFAULT_TCP_LOGGING_PORT")) + self.dict["port"] = prop + self.propnames.extend(["host", "port"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + host = self.getPropValue("host") + file.write("host=%s\n" % host) + port = self.getPropValue("port") + file.write("port=%s\n" % port) + file.write("args=('%s', %s)\n\n" % (host, port)) + + def readConfig(self, sectname): + prop = HostProperty(self.config.get(sectname, "host")) + self.dict["host"] = prop + prop = PortProperty(self.config.get(sectname, "port")) + self.dict["port"] = prop + self.propnames.extend(["host", "port"]) + +class DatagramHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = HostProperty(dict.get("host", "localhost")) + self.dict["host"] = prop + prop = PortProperty(dict.get("port", "handlers.DEFAULT_UDP_LOGGING_PORT")) + self.dict["port"] = prop + self.propnames.extend(["host", "port"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + host = self.getPropValue("host") + file.write("host=%s\n" % host) + port = self.getPropValue("port") + file.write("port=%s\n" % port) + file.write("args=('%s', %s)\n\n" % (host, port)) + + def readConfig(self, sectname): + prop = HostProperty(self.config.get(sectname, "host")) + self.dict["host"] = prop + prop = PortProperty(self.config.get(sectname, "port")) + self.dict["port"] = prop + self.propnames.extend(["host", "port"]) + +class URLProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "url", "URL", value) + +class MethodProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "method", "HTTP Method", value) + + def getChoices(self): + return HTTP_METHODS + +class HTTPHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = HostProperty(dict.get("host", "localhost")) + self.dict["host"] = prop + prop = PortProperty(dict.get("port", "80")) + self.dict["port"] = prop + prop = URLProperty(dict.get("url", "")) + self.dict["url"] = prop + prop = MethodProperty(dict.get("method", "GET")) + self.dict["method"] = prop + self.propnames.extend(["host", "port", "url", "method"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + host = self.getPropValue("host") + file.write("host=%s\n" % host) + port = self.getPropValue("port") + file.write("port=%s\n" % port) + url = self.getPropValue("url") + file.write("url=%s\n" % url) + meth = self.getPropValue("method") + file.write("method=%s\n" % meth) + file.write("args=('%s:%s', '%s', '%s')\n\n" % (host, port, url, meth)) + + def readConfig(self, sectname): + prop = HostProperty(self.config.get(sectname, "host")) + self.dict["host"] = prop + prop = PortProperty(self.config.get(sectname, "port")) + self.dict["port"] = prop + prop = URLProperty(self.config.get(sectname, "url")) + self.dict["url"] = prop + prop = MethodProperty(self.config.get(sectname, "method")) + self.dict["method"] = prop + self.propnames.extend(["host", "port", "url", "method"]) + +class SOAPHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = HostProperty(dict.get("host", "localhost")) + self.dict["host"] = prop + prop = PortProperty(dict.get("port", "80")) + self.dict["port"] = prop + prop = URLProperty(dict.get("url", "")) + self.dict["url"] = prop + self.propnames.extend(["host", "port", "url"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + host = self.getPropValue("host") + file.write("host=%s\n" % host) + port = self.getPropValue("port") + file.write("port=%s\n" % port) + url = self.getPropValue("url") + file.write("url=%s\n" % url) + file.write("args=('%s:%s', '%s')\n\n" % (host, port, url)) + + def readConfig(self, sectname): + prop = HostProperty(self.config.get(sectname, "host")) + self.dict["host"] = prop + prop = PortProperty(self.config.get(sectname, "port")) + self.dict["port"] = prop + prop = URLProperty(self.config.get(sectname, "url")) + self.dict["url"] = prop + self.propnames.extend(["host", "port", "url"]) + +class FacilityProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "facility", "Facility", value) + + def getChoices(self): + return SYSLOG_FACILITIES + +class SysLogHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = HostProperty(dict.get("host", "localhost")) + self.dict["host"] = prop + prop = PortProperty(dict.get("port", "handlers.SYSLOG_UDP_PORT")) + self.dict["port"] = prop + prop = FacilityProperty(dict.get("facility", "handlers.SysLogHandler.LOG_USER")) + self.dict["facility"] = prop + self.propnames.extend(["host", "port", "facility"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + host = self.getPropValue("host") + file.write("host=%s\n" % host) + port = self.getPropValue("port") + file.write("port=%s\n" % port) + fac = self.getPropValue("facility") + file.write("facility=%s\n" % fac) + file.write("args=(('%s', %s), handlers.SysLogHandler.%s)\n\n" % (host, port, fac)) + + def readConfig(self, sectname): + prop = HostProperty(self.config.get(sectname, "host")) + self.dict["host"] = prop + prop = PortProperty(self.config.get(sectname, "port")) + self.dict["port"] = prop + prop = FacilityProperty(self.config.get(sectname, "facility")) + self.dict["facility"] = prop + self.propnames.extend(["host", "port", "facility"]) + +class FromProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "from", "From", value) + +class ToProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "to", "To", value) + +class SubjectProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "subject", "Subject", value) + +class SMTPHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = HostProperty(dict.get("host", "localhost")) + self.dict["host"] = prop + prop = PortProperty(dict.get("port", "25")) + self.dict["port"] = prop + prop = FromProperty(dict.get("from", "")) + self.dict["from"] = prop + prop = ToProperty(dict.get("to", "")) + self.dict["to"] = prop + prop = SubjectProperty(dict.get("subject", "")) + self.dict["subject"] = prop + self.propnames.extend(["host", "port", "from", "to", "subject"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + host = self.getPropValue("host") + file.write("host=%s\n" % host) + port = self.getPropValue("port") + file.write("port=%s\n" % port) + frm = self.getPropValue("from") + file.write("from=%s\n" % frm) + to = self.getPropValue("to") + file.write("to=%s\n" % to) + subj = self.getPropValue("subject") + file.write("subject=%s\n" % subj) + to = string.split(to, ",") + file.write("args=('%s', '%s', %s, '%s')\n\n" % (host, frm, repr(to), subj)) + + def readConfig(self, sectname): + prop = HostProperty(self.config.get(sectname, "host")) + self.dict["host"] = prop + prop = PortProperty(self.config.get(sectname, "port")) + self.dict["port"] = prop + prop = FromProperty(self.config.get(sectname, "from")) + self.dict["from"] = prop + prop = ToProperty(self.config.get(sectname, "to")) + self.dict["to"] = prop + prop = SubjectProperty(self.config.get(sectname, "subject")) + self.dict["subject"] = prop + self.propnames.extend(["host", "port", "from", "to", "subject"]) + +class CapacityProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "capacity", "Capacity", value) + +class FlushLevelProperty(LevelProperty): + def __init__(self, value=None): + Property.__init__(self, "flushlevel", "Flush Level", value) + +class TargetProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "target", "Target", value) + + def getChoices(self): + handlers = self.config.getHandlerChoice() + nm = self.dict["name"].getValueText() + #can't be own target... + return filter(lambda x,nm=nm: x[0] != nm, handlers) + +class MemoryHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = CapacityProperty(dict.get("capacity", "10")) + self.dict["capacity"] = prop + prop = FlushLevelProperty(dict.get("flushlevel", "ERROR")) + self.dict["flushlevel"] = prop + prop = TargetProperty(config, dict.get("target", "")) + prop.dict = self.dict + self.dict["target"] = prop + self.propnames.extend(["capacity", "flushlevel", "target"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + cap = self.getPropValue("capacity") + file.write("capacity=%s\n" % cap) + flvl = self.getPropValue("flushlevel") + file.write("flushlevel=%s\n" % flvl) + file.write("target=%s\n" % self.getPropValue("target")) + file.write("args=(%s, %s)\n\n" % (cap, flvl)) + + def readConfig(self, sectname): + prop = CapacityProperty(self.config.get(sectname, "capacity")) + self.dict["capacity"] = prop + prop = FlushLevelProperty(self.config.get(sectname, "flushlevel")) + self.dict["flushlevel"] = prop + prop = TargetProperty(self.config, self.config.get(sectname, "target")) + prop.dict = self.dict + self.dict["target"] = prop + self.propnames.extend(["capacity", "flushlevel", "target"]) + +class AppNameProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "appname", "Application Name", value) + +class DLLNameProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "dllname", "Message DLL name", value) + +class LogTypeProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "logtype", "Log Type", value) + + def getChoices(self): + return LOG_TYPES + +class NTEventLogHandlerProxy(HandlerProxy): + def __init__(self, config, dict): + HandlerProxy.__init__(self, config, dict) + prop = AppNameProperty(dict.get("appname", "Python Application")) + self.dict["appname"] = prop + prop = DLLNameProperty(dict.get("dllname", "")) + self.dict["dllname"] = prop + prop = LogTypeProperty(dict.get("logtype", "Application")) + self.dict["logtype"] = prop + self.propnames.extend(["appname", "dllname", "logtype"]) + + def writeConfig(self, file): + HandlerProxy.writeConfig(self, file) + app = self.getPropValue("appname") + file.write("appname=%s\n" % app) + dll = self.getPropValue("dllname") + file.write("dllname=%s\n" % dll) + ltype = self.getPropValue("logtype") + file.write("logtype=%s\n" % ltype) + file.write("args=('%s', '%s', '%s')\n\n" % (app, dll, ltype)) + + def readConfig(self, sectname): + prop = AppNameProperty(self.config.get(sectname, "appname")) + self.dict["appname"] = prop + prop = DLLNameProperty(self.config.get(sectname, "dllname")) + self.dict["dllname"] = prop + prop = LogTypeProperty(self.config.get(sectname, "logtype")) + self.dict["logtype"] = prop + self.propnames.extend(["appname", "dllname", "logtype"]) + +# -- logger properties and proxies + +class ChannelProperty(Property): + def __init__(self, value=None): + Property.__init__(self, "channel", "Name", value) + +class HandlerProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "handler", "Handlers", value) + + def getChoices(self): + return self.config.getHandlerChoice() + +class FilterProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "filter", "Filters", value) + + def getChoices(self): + return self.config.getFilterChoice() + +class ParentProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "parent", "Parent", value) + + def getChoices(self): + loggers = self.config.getLoggerChoice() + nm = self.dict["name"].getValueText() + #can't be own parent... + return filter(lambda x,nm=nm: x[0] != nm, loggers) + + def getValueText(self): + if self.dict.has_key("root"): + return "" + pn = Property.getValueText(self) + rv = "" + while pn != "(root)": + parent = self.config.getLogger(pn) + rv = parent.getPropValue("channel") + "." + rv + pn = parent.getProp("parent").value + return rv[:-1] + +class PropagateProperty(Property): + def __init__(self, config, value=None): + self.config = config + Property.__init__(self, "propagate", "Propagate", value) + + def getChoices(self): + return BOOLEAN_VALUES + +class LoggerProxy(PropertyHolder): + def __init__(self, config, dict): + self.config = config + PropertyHolder.__init__(self, dict) + prop = ChannelProperty(dict.get("channel", "")) + self.dict["channel"] = prop + prop = NameProperty(dict.get("name", "")) + self.dict["name"] = prop + prop = HandlerProperty(config, dict.get("handler", [])) + self.dict["handler"] = prop + prop = LevelProperty(dict.get("level", "NOTSET")) + self.dict["level"] = prop + prop = PropagateProperty(self.config, dict.get("propagate", "1")) + self.dict["propagate"] = prop + prop = ParentProperty(config, dict.get("parent", "(root)")) + prop.dict = self.dict + self.dict["parent"] = prop + self.propnames = ["parent", "channel", "level", "propagate", "handler"] + self.onChannelChanged = None + self.deleted = 0 + + def isReadonly(self, name): + return (name in ["channel", "parent", "propagate"]) and self.dict.has_key("root") + + def getQualifiedName(self): + pt = self.getProp("parent").getValueText() + nm = self.getPropValue("channel") + if pt: + pn = pt + "." + nm + else: + pn = nm + if pn == "": + pn = "(root)" + return pn + + def setPropValue(self, name, value): + PropertyHolder.setPropValue(self, name, value) + if (name == "channel"): + nm = self.getPropValue("name") + if self.onChannelChanged: + self.onChannelChanged(nm, value) + + def writeConfig(self, file): + if self.dict.has_key("root"): + name = "root" + else: + name = self.getPropValue("name") + file.write("[logger_%s]\n" % name) + file.write("level=%s\n" % self.getPropValue("level")) + file.write("propagate=%s\n" % self.getPropValue("propagate")) + file.write("channel=%s\n" % self.getPropValue("channel")) + file.write("parent=%s\n" % self.getPropValue("parent")) + file.write("qualname=%s\n" % self.getQualifiedName()) + file.write("handlers=%s\n\n" % string.join(self.getPropValue("handler"), ",")) + +# -- logging configuration + +class LoggingConfig(ConfigParser.ConfigParser): + def __init__(self, defaults=None): + ConfigParser.ConfigParser.__init__(self, defaults) + self.formatters = {} + self.handlers = {} + self.loggers = {} +# self.filters = {} + #create root logger + d = { "name": "(root)", "root": 1, "parent": "" } + self.loggers["(root)"] = LoggerProxy(self, d) + + def read(self, fn): + ConfigParser.ConfigParser.read(self, fn) + llist = self.get("loggers", "keys") + llist = string.split(llist, ",") + llist.remove("root") + sectname = "logger_root" + log = self.loggers["(root)"] + log.setPropValue("level", self.get(sectname, "level")) + hlist = self.get(sectname, "handlers") + hlist = string.split(hlist, ",") + log.setPropValue("handler", hlist) + for log in llist: + sectname = "logger_%s" % log + hlist = self.get(sectname, "handlers") + hlist = string.split(hlist, ",") + d = { + "name" : log, + "level" : self.get(sectname, "level"), + "channel" : self.get(sectname, "channel"), + "parent" : self.get(sectname, "parent"), + "propagate" : self.get(sectname, "propagate"), + "handler" : hlist, + } + self.loggers[log] = LoggerProxy(self, d) + hlist = self.get("handlers", "keys") + if len(hlist): + hlist = string.split(hlist, ",") + for hand in hlist: + sectname = "handler_%s" % hand + klass = self.get(sectname, "class") + if klass[:9] == "handlers.": + klass = klass[9:] + d = { + "name" : hand, + "class" : "%sProxy" % klass, + "level" : self.get(sectname, "level"), + "formatter" : self.get(sectname, "formatter"), + } + hobj = HandlerProxy(self, d) + hobj.__class__ = eval("%sProxy" % klass) + hobj.readConfig(sectname) + self.handlers[hand] = hobj + + flist = self.get("formatters", "keys") + if len(flist): + flist = string.split(flist, ",") + for form in flist: + sectname = "formatter_%s" % form + d = { + "name" : form, + "format" : self.get(sectname, "format", 1), + "datefmt" : self.get(sectname, "datefmt", 1), + } + self.formatters[form] = FormatterProxy(self, d) + +# flist = self.get("filters", "keys") +# if len(flist): +# flist = string.split(flist, ",") +# for filt in flist: +# sectname = "filter_%s" % filt +# d = { +# "name" : filt, +# "lname" : self.get(sectname, "lname", 1), +# } +# self.filters[filt] = FilterProxy(self, d) + + def getFormatter(self, name): + if name: + fmt = self.formatters[name] + else: + n = len(self.formatters.keys()) + 1 + name = "form%02d" % n + fmt = FormatterProxy(self, {"name": name}) + self.formatters[name] = fmt + return fmt + + def getHandler(self, name): + if name: + hand = self.handlers[name] + else: + n = len(self.handlers.keys()) + 1 + name = "hand%02d" % n + hand = StreamHandlerProxy(self, {"name": name}) + self.handlers[name] = hand + return hand + + def getLogger(self, name): + if name: + log = self.loggers[name] + else: + n = len(self.loggers.keys()) + 1 + name = "log%02d" % n + log = LoggerProxy(self, {"name": name, "channel": name}) + self.loggers[name] = log + return log + + def getFormatterChoice(self): + values = [] + keys = self.formatters.keys() + keys.sort() + for f in keys: + values.append((f, f)) + return tuple(values) + + def getHandlerChoice(self): + values = [] + keys = self.handlers.keys() + keys.sort() + for f in keys: + values.append((f, f)) + return tuple(values) + + def getFilterChoice(self): + values = [] + keys = self.filters.keys() + keys.sort() + for f in keys: + values.append((f, f)) + return tuple(values) + + def getLoggerChoice(self): + values = [] + keys = self.loggers.keys() + keys.sort() + for f in keys: + values.append((f, f)) + return tuple(values) + + def getLoggers(self): + return self.loggers.values() + + def getHandlers(self): + return self.handlers.values() + + def getFormatters(self): + return self.formatters.values() + + def formatterIsUsed(self, name): + rv = None + for h in self.handlers.keys(): + if self.handlers[h].getPropValue("formatter") == name: + rv = h + break + return rv + + def handlerIsUsed(self, name): + rv = None + for log in self.loggers.keys(): + if name in self.loggers[log].getPropValue("handler"): + rv = log + break + return rv + + def removeFormatter(self, name): + del self.formatters[name] + + def removeHandler(self, name): + del self.handlers[name] + + def removeLogger(self, name): + del self.loggers[name] + + def save(self, fn): + #needed because 1.5.2 ConfigParser should be supported + file = open(fn, "w") + #Write out the keys + loggers = self.loggers.keys() + loggers.remove("(root)") + loggers = filter(lambda x, d=self.loggers: not d[x].deleted, loggers) + loggers.sort() + list = ["root"] + list.extend(loggers) + file.write("[loggers]\nkeys=%s\n\n" % string.join(list, ",")) + handlers = self.handlers.keys() + handlers.sort() + file.write("[handlers]\nkeys=%s\n\n" % string.join(handlers, ",")) + formatters = self.formatters.keys() + formatters.sort() + file.write("[formatters]\nkeys=%s\n\n" % string.join(formatters, ",")) + #write out the root logger properties + log = self.loggers["(root)"] + log.writeConfig(file) + #write out other logger properties + for log in loggers: + log = self.loggers[log] + log.writeConfig(file) + #write out handler properties + for hand in handlers: + hand = self.handlers[hand] + hand.writeConfig(file) + #write out formatter properties + for form in formatters: + form = self.formatters[form] + form.writeConfig(file) + file.close() + +root = None + +def onClose(): + if _popup: + _popup.withdraw() + root.destroy() + +def main(): + global root + root=Tk() + cfg = Configurator(root) + cfg.pack(side=LEFT, fill=BOTH, expand=1) + root.protocol("WM_DELETE_WINDOW", onClose) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/logging.dtd b/tools/python/logging/logging-0.4.9.2/test/logging.dtd new file mode 100644 index 0000000..b2275ab --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/logging.dtd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/logging.xml b/tools/python/logging/logging-0.4.9.2/test/logging.xml new file mode 100644 index 0000000..6dabaff --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/logging.xml @@ -0,0 +1,5 @@ + +]> + +&data; + \ No newline at end of file diff --git a/tools/python/logging/logging-0.4.9.2/test/logrecv.ini b/tools/python/logging/logging-0.4.9.2/test/logrecv.ini new file mode 100644 index 0000000..eede0d3 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/logrecv.ini @@ -0,0 +1,36 @@ +[loggers] +keys=root + +[handlers] +keys=hand01,hand02 + +[formatters] +keys=form01 + +[logger_root] +level=NOTSET +propagate=1 +channel= +parent= +qualname=(root) +handlers=hand01,hand02 + +[handler_hand01] +class=FileHandler +level=NOTSET +formatter=form01 +filename=logrecv.log +mode=w +args=('logrecv.log', 'w') + +[handler_hand02] +class=StreamHandler +level=NOTSET +formatter=form01 +stream=sys.stderr +args=(sys.stderr,) + +[formatter_form01] +format=%(asctime)s %(levelname)-9s %(name)-8s %(thread)5s %(message)s +datefmt= + diff --git a/tools/python/logging/logging-0.4.9.2/test/logrecv.py b/tools/python/logging/logging-0.4.9.2/test/logrecv.py new file mode 100755 index 0000000..4c8007e --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/logrecv.py @@ -0,0 +1,443 @@ +#! /usr/bin/env python +# +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +""" +Simple socket-based logging event receiver for use with "logging.py" logging +module. + +Should work under Python versions >= 1.5.2, except that source line information +is not available unless 'inspect' is. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. +""" + +from select import select +import sys, string, struct, types, cPickle, socket +import logging, logging.handlers, logging.config + +TIMEOUT = 10 +if sys.platform == "win32": + RESET_ERROR = 10054 +else: + RESET_ERROR = 0 #FIXME get correct value for Unix... + +logging.raiseExceptions = 1 + +# +# TCP receiver +# + +from SocketServer import ThreadingTCPServer, StreamRequestHandler + +class LogRecordStreamHandler(StreamRequestHandler): + """ + Handler for a streaming logging request. It basically logs the record + using whatever logging policy is configured locally. + """ + + def handle(self): + """ + Handle multiple requests - each expected to be a 4-byte length, + followed by the LogRecord in pickle format. Logs the record + according to whatever policy is configured locally. + """ + while 1: + try: + chunk = self.connection.recv(4) + if len(chunk) < 4: + break + slen = struct.unpack(">L", chunk)[0] + chunk = self.connection.recv(slen) + while len(chunk) < slen: + chunk = chunk + self.connection.recv(slen - len(chunk)) + obj = self.unPickle(chunk) + record = logging.makeLogRecord(obj) + self.handleLogRecord(record) + except socket.error, e: + if type(e.args) != types.TupleType: + raise + else: + errcode = e.args[0] + if errcode != RESET_ERROR: + raise + break + + def unPickle(self, data): + return cPickle.loads(data) + + def handleLogRecord(self, record): + #if a name is specified, we use the named logger rather than the one + #implied by the record. This is so test harnesses don't get into + #endless loops (particularly log_test.py, which has this code and the + #client code in the same Python instance) + if self.server.logname is not None: + name = self.server.logname + else: + name = record.name + logger = logging.getLogger(name) + logger.handle(record) + +class LogRecordSocketReceiver(ThreadingTCPServer): + """ + A simple-minded TCP socket-based logging receiver suitable for test + purposes. + """ + + allow_reuse_address = 1 + + def __init__(self, host='localhost', port=logging.handlers.DEFAULT_TCP_LOGGING_PORT, + handler=LogRecordStreamHandler): + ThreadingTCPServer.__init__(self, (host, port), handler) + self.abort = 0 + self.timeout = 1 + self.logname = None + + def serve_until_stopped(self): + import select + abort = 0 + while not abort: + rd, wr, ex = select.select([self.socket.fileno()], + [], [], + self.timeout) + if rd: + self.handle_request() + abort = self.abort + +# +# UDP receiver +# + +from SocketServer import ThreadingUDPServer, DatagramRequestHandler + +class LogRecordDatagramHandler(DatagramRequestHandler): + """ + Handler for a datagram logging request. It basically logs the record using + whatever logging policy is configured locally. + """ + def handle(self): + chunk = self.packet + slen = struct.unpack(">L", chunk[:4])[0] + chunk = chunk[4:] + assert len(chunk) == slen + obj = self.unPickle(chunk) + record = logging.LogRecord(None, None, "", 0, "", (), None) + record.__dict__.update(obj) + self.handleLogRecord(record) + + def unPickle(self, data): + return cPickle.loads(data) + + def handleLogRecord(self, record): + #if a name is specified, we use the named logger rather than the one + #implied by the record. This is so test harnesses don't get into + #endless loops (particularly log_test.py, which has this code and the + #client code in the same Python instance) + if self.server.logname is not None: + name = self.server.logname + else: + name = record.name + logger = logging.getLogger(name) + logger.handle(record) + + def finish(self): + pass + +class LogRecordDatagramReceiver(ThreadingUDPServer): + """ + A simple-minded UDP datagram-based logging receiver suitable for test + purposes. + """ + + allow_reuse_address = 1 + + def __init__(self, host='localhost', port=logging.handlers.DEFAULT_UDP_LOGGING_PORT, + handler=LogRecordDatagramHandler): + ThreadingUDPServer.__init__(self, (host, port), handler) + self.abort = 0 + self.timeout = 1 + self.logname = None + + def serve_until_stopped(self): + import select + abort = 0 + while not abort: + rd, wr, ex = select.select([self.socket.fileno()], + [], [], + self.timeout) + if rd: + self.handle_request() + abort = self.abort + +# +# HTTP receiver +# + +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler + +import cgi + +class LogRecordHTTPHandler(BaseHTTPRequestHandler): + def makeDict(self, fs): + dict = {} + for mfs in fs.list: + dict[mfs.name] = mfs.value + for key in ["args", "exc_info", "exc_text", "lineno", "msecs", "created", + "thread", "levelno", "relativeCreated"]: + if dict.has_key(key): + dict[key] = eval(dict[key]) + return dict + + def do_GET(self): + """Serve a GET request.""" + sts = "OK" + env = { 'REQUEST_METHOD' : 'GET'} + try: + i = string.find(self.path, '?') + if i >= 0: + env['QUERY_STRING'] = self.path[i + 1:] + fs = cgi.FieldStorage(environ=env) + dict = self.makeDict(fs) + record = logging.LogRecord(None, None, "", 0, "", (), None) + record.__dict__.update(dict) + self.handleLogRecord(record) + except Exception, e: + sts = "ERROR" + raise + self.send_head() + self.wfile.write("GET %s" % sts) + + def handleLogRecord(self, record): + #if a name is specified, we use the named logger rather than the one + #implied by the record. This is so test harnesses don't get into + #endless loops (particularly log_test.py, which has this code and the + #client code in the same Python instance) + if self.server.logname is not None: + name = self.server.logname + else: + name = record.name + logger = logging.getLogger(name) + logger.handle(record) + + def do_HEAD(self): + """Serve a HEAD request.""" + self.send_head() + + def do_POST(self): + """Serve a POST request.""" + sts = "OK" + env = { 'REQUEST_METHOD' : 'POST'} + try: + length = self.headers.getheader('content-length') + if length: + env['CONTENT_LENGTH'] = length + #print self.headers + i = string.find(self.path, '?') + if i >= 0: + env['QUERY_STRING'] = self.path[i + 1:] + fs = cgi.FieldStorage(fp=self.rfile, environ=env) + dict = self.makeDict(fs) + record = logging.LogRecord(None, None, "", 0, "", (), None) + record.__dict__.update(dict) + self.handleLogRecord(record) + except Exception, e: + print e + sys.stdout.flush() + sts = "ERROR" + raise + self.send_head() + self.wfile.write("POST %s" % sts) + + def send_head(self): + """Common code for GET and HEAD commands. + + This sends the response code and MIME headers. + + Return value is either a file object (which has to be copied + to the outputfile by the caller unless the command was HEAD, + and must be closed by the caller under all circumstances), or + None, in which case the caller has nothing further to do. + + """ + self.send_response(200) + self.send_header("Content-type", "text/plain") + self.end_headers() + + def log_message(self, *args): + #comment out the following line if you don't want to show requests + #apply(BaseHTTPRequestHandler.log_message, (self,) + args) + pass + +class LogRecordHTTPReceiver(HTTPServer): + def __init__(self, host='localhost', port=logging.handlers.DEFAULT_HTTP_LOGGING_PORT, + handler=LogRecordHTTPHandler): + HTTPServer.__init__(self, (host, port), handler) + self.abort = 0 + self.timeout = 1 + self.logname = None + + def serve_until_stopped(self): + import select + abort = 0 + while not abort: + rd, wr, ex = select.select([self.socket.fileno()], + [], [], + self.timeout) + if rd: + self.handle_request() + abort = self.abort + + +# +# SOAP receiver +# + +try: + from ZSI import dispatch + + logname = None + + def log(args, created, exc_info, exc_text, filename, levelname, levelno, lineno, module, msecs, msg, name, pathname, process, relativeCreated, thread): + record = logging.LogRecord(None, None, "", 0, "", (), None) + record.args = eval(args) + record.exc_info = eval(exc_info) + record.exc_text = eval(exc_text) + record.created = created + record.filename = filename + record.module = module + record.levelname = levelname + record.lineno = lineno + record.levelno = levelno + record.msecs = msecs + record.msg = msg + record.name = name + record.pathname = pathname + record.process = process + record.relativeCreated = relativeCreated + record.thread = thread + #if a name is specified, we use the named logger rather than the one + #implied by the record. This is so test harnesses don't get into + #endless loops (particularly log_test.py, which has this code and the + #client code in the same Python instance) + if logname is not None: + lname = logname + else: + lname = name + logger = logging.getLogger(lname) + logger.handle(record) + + class MySOAPRequestHandler(dispatch.SOAPRequestHandler): + def log_message(self, *args): + #comment out the following line if you don't want to show requests + #apply(BaseHTTPRequestHandler.log_message, (self,) + args) + pass + + class SOAPServer(HTTPServer): + def __init__(self, port=logging.handlers.DEFAULT_SOAP_LOGGING_PORT): + address = ('', port) + HTTPServer.__init__(self, address, MySOAPRequestHandler) + self.abort = 0 + self.timeout = 1 + self.logname = None + self.docstyle = 0 + self.nsdict = {} + self.typesmodule = None + self.rpc = 1 + self.modules = (sys.modules["__main__"],) + + def serve_until_stopped(self): + import select + abort = 0 + while not abort: + rd, wr, ex = select.select([self.socket.fileno()], + [], [], + self.timeout) + if rd: + global logname + logname = self.logname + self.handle_request() + abort = self.abort + +except ImportError: + "Import failed" + SOAPServer = None + +def runTCP(tcpserver=None): + if not tcpserver: + tcpserver = LogRecordSocketReceiver() + print "About to start TCP server..." + tcpserver.serve_until_stopped() + +def runUDP(udpserver=None): + if not udpserver: + udpserver = LogRecordDatagramReceiver() + print "About to start UDP server..." + udpserver.serve_until_stopped() + +def runHTTP(httpserver=None): + if not httpserver: + httpserver = LogRecordHTTPReceiver() + print "About to start HTTP server..." + httpserver.serve_until_stopped() + +def runSOAP(soapserver=None): + if not SOAPServer: + print "Sorry, ZSI is not available. Install PyXML-0.6.6 and ZSI first." + print "See README.txt and python_logging.html for more information." + else: + if not soapserver: + soapserver = SOAPServer() + print "About to start SOAP server..." + soapserver.serve_until_stopped() + +FORMAT_STR = "%(asctime)s %(name)-19s %(levelname)-5s - %(message)s" + +if __name__ == "__main__": + if (len(sys.argv) < 2) or not (string.lower(sys.argv[1]) in \ + ["udp", "tcp", "http", "soap"]): + print "usage: logrecv.py [UDP|TCP|HTTP|SOAP]" + else: + #logging.basicConfig() + logging.config.fileConfig("logrecv.ini") +# both = string.lower(sys.argv[1]) == "both" +# hdlr = logging.FileHandler("test.log") +# hdlr.setFormatter(logging.Formatter(FORMAT_STR)) +# logging.getLogger("").addHandler(hdlr) +# if both: +# import threading +# tcpthread = threading.Thread(target=runTCP) +# udpthread = threading.Thread(target=runUDP) +# tcpthread.start() +# udpthread.start() +# tcpthread.join() +# udpthread.join() +# else: +# tcp = string.lower(sys.argv[1]) == "tcp" +# if tcp: +# runTCP() +# else: +# runUDP() + arg = string.lower(sys.argv[1]) + if arg == "tcp": + runTCP() + elif arg == "udp": + runUDP() + elif arg == "http": + runHTTP() + elif arg == "soap": + runSOAP() diff --git a/tools/python/logging/logging-0.4.9.2/test/myapp.py b/tools/python/logging/logging-0.4.9.2/test/myapp.py new file mode 100644 index 0000000..d1da1c2 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/myapp.py @@ -0,0 +1,13 @@ +import logging, mymodule + +logging.basicConfig() + +log = logging.getLogger("MyApp") +log.setLevel(logging.DEBUG) #set verbosity to show all messages of severity >= DEBUG +log.info("Starting my app") +try: + mymodule.doIt() +except Exception, e: + log.exception("There was a problem.") +log.info("Ending my app") + diff --git a/tools/python/logging/logging-0.4.9.2/test/mymodule.py b/tools/python/logging/logging-0.4.9.2/test/mymodule.py new file mode 100644 index 0000000..ac824df --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/mymodule.py @@ -0,0 +1,8 @@ +import logging +log = logging.getLogger("MyModule") + +def doIt(): + log.debug("Doin' stuff...") + #do stuff... + raise TypeError, "Bogus type error for testing" + diff --git a/tools/python/logging/logging-0.4.9.2/test/stderr.exp b/tools/python/logging/logging-0.4.9.2/test/stderr.exp new file mode 100644 index 0000000..7426e86 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/stderr.exp @@ -0,0 +1,566 @@ +-- log_test0 begin -------------------------------------------------------- +CRITICAL:ERR:Message 0 +ERROR:ERR:Message 1 +CRITICAL:INF:Message 2 +ERROR:INF:Message 3 +WARNING:INF:Message 4 +INFO:INF:Message 5 +CRITICAL:INF.UNDEF:Message 6 +ERROR:INF.UNDEF:Message 7 +WARNING:INF.UNDEF:Message 8 +INFO:INF.UNDEF:Message 9 +CRITICAL:INF.ERR:Message 10 +ERROR:INF.ERR:Message 11 +CRITICAL:INF.ERR.UNDEF:Message 12 +ERROR:INF.ERR.UNDEF:Message 13 +CRITICAL:DEB:Message 14 +ERROR:DEB:Message 15 +WARNING:DEB:Message 16 +INFO:DEB:Message 17 +DEBUG:DEB:Message 18 +CRITICAL:UNDEF:Message 19 +ERROR:UNDEF:Message 20 +WARNING:UNDEF:Message 21 +INFO:UNDEF:Message 22 +CRITICAL:INF.BADPARENT.UNDEF:Message 23 +CRITICAL:INF.BADPARENT:Message 24 +INFO:INF:Messages should bear numbers 0 through 24. +-- log_test0 end -------------------------------------------------------- +-- log_test2 begin -------------------------------------------------------- +CRITICAL:ERR:Message 0 +ERROR:ERR:Message 1 +CRITICAL:INF:Message 2 +ERROR:INF:Message 3 +WARNING:INF:Message 4 +INFO:INF:Message 5 +CRITICAL:INF.UNDEF:Message 6 +ERROR:INF.UNDEF:Message 7 +WARNING:INF.UNDEF:Message 8 +INFO:INF.UNDEF:Message 9 +CRITICAL:INF.ERR:Message 10 +ERROR:INF.ERR:Message 11 +CRITICAL:INF.ERR.UNDEF:Message 12 +ERROR:INF.ERR.UNDEF:Message 13 +CRITICAL:DEB:Message 14 +ERROR:DEB:Message 15 +WARNING:DEB:Message 16 +INFO:DEB:Message 17 +DEBUG:DEB:Message 18 +CRITICAL:UNDEF:Message 19 +ERROR:UNDEF:Message 20 +WARNING:UNDEF:Message 21 +INFO:UNDEF:Message 22 +CRITICAL:INF.BADPARENT.UNDEF:Message 23 +CRITICAL:INF.BADPARENT:Message 24 +INFO:INF:Messages should bear numbers 0 through 24. +-- log_test2 end -------------------------------------------------------- +CRITICAL:ERR:Message 0 +ERROR:ERR:Message 1 +CRITICAL:INF:Message 2 +ERROR:INF:Message 3 +WARNING:INF:Message 4 +INFO:INF:Message 5 +CRITICAL:INF.UNDEF:Message 6 +ERROR:INF.UNDEF:Message 7 +WARNING:INF.UNDEF:Message 8 +INFO:INF.UNDEF:Message 9 +CRITICAL:INF.ERR:Message 10 +ERROR:INF.ERR:Message 11 +CRITICAL:INF.ERR.UNDEF:Message 12 +ERROR:INF.ERR.UNDEF:Message 13 +CRITICAL:DEB:Message 14 +ERROR:DEB:Message 15 +WARNING:DEB:Message 16 +INFO:DEB:Message 17 +DEBUG:DEB:Message 18 +CRITICAL:UNDEF:Message 19 +ERROR:UNDEF:Message 20 +WARNING:UNDEF:Message 21 +INFO:UNDEF:Message 22 +CRITICAL:INF.BADPARENT.UNDEF:Message 23 +CRITICAL:INF.BADPARENT:Message 24 +INFO:INF:Messages should bear numbers 0 through 24. +-- log_test4 begin -------------------------------------------------------- +-- setting logging level to 'Boring' ----- +Boring:root:This should only be seen at the 'Boring' logging level (or lower) +Chatterbox:root:This should only be seen at the 'Chatterbox' logging level (or lower) +Garrulous:root:This should only be seen at the 'Garrulous' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Chatterbox' ----- +Chatterbox:root:This should only be seen at the 'Chatterbox' logging level (or lower) +Garrulous:root:This should only be seen at the 'Garrulous' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Garrulous' ----- +Garrulous:root:This should only be seen at the 'Garrulous' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Talkative' ----- +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Verbose' ----- +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Sociable' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Effusive' ----- +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Terse' ----- +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Taciturn' ----- +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Silent' ----- +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- Filtering at handler level to SOCIABLE -- +-- setting logging level to 'Boring' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Chatterbox' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Garrulous' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Talkative' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Verbose' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Sociable' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Effusive' ----- +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Terse' ----- +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Taciturn' ----- +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Silent' ----- +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- Filtering using GARRULOUS filter -- +-- setting logging level to 'Boring' ----- +Boring:root:This should only be seen at the 'Boring' logging level (or lower) +Chatterbox:root:This should only be seen at the 'Chatterbox' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Chatterbox' ----- +Chatterbox:root:This should only be seen at the 'Chatterbox' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Garrulous' ----- +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Talkative' ----- +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Verbose' ----- +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Sociable' ----- +Sociable:root:This should only be seen at the 'Sociable' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Effusive' ----- +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Terse' ----- +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Taciturn' ----- +Taciturn:root:This should only be seen at the 'Taciturn' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Silent' ----- +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- Filtering using specific filter for SOCIABLE, TACITURN -- +-- setting logging level to 'Boring' ----- +Boring:root:This should only be seen at the 'Boring' logging level (or lower) +Chatterbox:root:This should only be seen at the 'Chatterbox' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Chatterbox' ----- +Chatterbox:root:This should only be seen at the 'Chatterbox' logging level (or lower) +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Garrulous' ----- +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Talkative' ----- +Talkative:root:This should only be seen at the 'Talkative' logging level (or lower) +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Verbose' ----- +Verbose:root:This should only be seen at the 'Verbose' logging level (or lower) +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Sociable' ----- +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Effusive' ----- +Effusive:root:This should only be seen at the 'Effusive' logging level (or lower) +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Terse' ----- +Terse:root:This should only be seen at the 'Terse' logging level (or lower) +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Taciturn' ----- +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- setting logging level to 'Silent' ----- +Silent:root:This should only be seen at the 'Silent' logging level (or lower) +-- log_test4 end -------------------------------------------------------- +-- log_test7 begin -------------------------------------------------------- +-- logging at DEBUG, nothing should be seen yet -- +-- logging at INFO, nothing should be seen yet -- +-- logging at WARNING, 3 messages should be seen -- +Debug message +Info message +Warning message +-- logging 0 at level INFO, messages should be seen every 10 events -- +-- logging 1 at level INFO, messages should be seen every 10 events -- +-- logging 2 at level INFO, messages should be seen every 10 events -- +-- logging 3 at level INFO, messages should be seen every 10 events -- +-- logging 4 at level INFO, messages should be seen every 10 events -- +-- logging 5 at level INFO, messages should be seen every 10 events -- +-- logging 6 at level INFO, messages should be seen every 10 events -- +-- logging 7 at level INFO, messages should be seen every 10 events -- +-- logging 8 at level INFO, messages should be seen every 10 events -- +-- logging 9 at level INFO, messages should be seen every 10 events -- +Info index = 0 +Info index = 1 +Info index = 2 +Info index = 3 +Info index = 4 +Info index = 5 +Info index = 6 +Info index = 7 +Info index = 8 +Info index = 9 +-- logging 10 at level INFO, messages should be seen every 10 events -- +-- logging 11 at level INFO, messages should be seen every 10 events -- +-- logging 12 at level INFO, messages should be seen every 10 events -- +-- logging 13 at level INFO, messages should be seen every 10 events -- +-- logging 14 at level INFO, messages should be seen every 10 events -- +-- logging 15 at level INFO, messages should be seen every 10 events -- +-- logging 16 at level INFO, messages should be seen every 10 events -- +-- logging 17 at level INFO, messages should be seen every 10 events -- +-- logging 18 at level INFO, messages should be seen every 10 events -- +-- logging 19 at level INFO, messages should be seen every 10 events -- +Info index = 10 +Info index = 11 +Info index = 12 +Info index = 13 +Info index = 14 +Info index = 15 +Info index = 16 +Info index = 17 +Info index = 18 +Info index = 19 +-- logging 20 at level INFO, messages should be seen every 10 events -- +-- logging 21 at level INFO, messages should be seen every 10 events -- +-- logging 22 at level INFO, messages should be seen every 10 events -- +-- logging 23 at level INFO, messages should be seen every 10 events -- +-- logging 24 at level INFO, messages should be seen every 10 events -- +-- logging 25 at level INFO, messages should be seen every 10 events -- +-- logging 26 at level INFO, messages should be seen every 10 events -- +-- logging 27 at level INFO, messages should be seen every 10 events -- +-- logging 28 at level INFO, messages should be seen every 10 events -- +-- logging 29 at level INFO, messages should be seen every 10 events -- +Info index = 20 +Info index = 21 +Info index = 22 +Info index = 23 +Info index = 24 +Info index = 25 +Info index = 26 +Info index = 27 +Info index = 28 +Info index = 29 +-- logging 30 at level INFO, messages should be seen every 10 events -- +-- logging 31 at level INFO, messages should be seen every 10 events -- +-- logging 32 at level INFO, messages should be seen every 10 events -- +-- logging 33 at level INFO, messages should be seen every 10 events -- +-- logging 34 at level INFO, messages should be seen every 10 events -- +-- logging 35 at level INFO, messages should be seen every 10 events -- +-- logging 36 at level INFO, messages should be seen every 10 events -- +-- logging 37 at level INFO, messages should be seen every 10 events -- +-- logging 38 at level INFO, messages should be seen every 10 events -- +-- logging 39 at level INFO, messages should be seen every 10 events -- +Info index = 30 +Info index = 31 +Info index = 32 +Info index = 33 +Info index = 34 +Info index = 35 +Info index = 36 +Info index = 37 +Info index = 38 +Info index = 39 +-- logging 40 at level INFO, messages should be seen every 10 events -- +-- logging 41 at level INFO, messages should be seen every 10 events -- +-- logging 42 at level INFO, messages should be seen every 10 events -- +-- logging 43 at level INFO, messages should be seen every 10 events -- +-- logging 44 at level INFO, messages should be seen every 10 events -- +-- logging 45 at level INFO, messages should be seen every 10 events -- +-- logging 46 at level INFO, messages should be seen every 10 events -- +-- logging 47 at level INFO, messages should be seen every 10 events -- +-- logging 48 at level INFO, messages should be seen every 10 events -- +-- logging 49 at level INFO, messages should be seen every 10 events -- +Info index = 40 +Info index = 41 +Info index = 42 +Info index = 43 +Info index = 44 +Info index = 45 +Info index = 46 +Info index = 47 +Info index = 48 +Info index = 49 +-- logging 50 at level INFO, messages should be seen every 10 events -- +-- logging 51 at level INFO, messages should be seen every 10 events -- +-- logging 52 at level INFO, messages should be seen every 10 events -- +-- logging 53 at level INFO, messages should be seen every 10 events -- +-- logging 54 at level INFO, messages should be seen every 10 events -- +-- logging 55 at level INFO, messages should be seen every 10 events -- +-- logging 56 at level INFO, messages should be seen every 10 events -- +-- logging 57 at level INFO, messages should be seen every 10 events -- +-- logging 58 at level INFO, messages should be seen every 10 events -- +-- logging 59 at level INFO, messages should be seen every 10 events -- +Info index = 50 +Info index = 51 +Info index = 52 +Info index = 53 +Info index = 54 +Info index = 55 +Info index = 56 +Info index = 57 +Info index = 58 +Info index = 59 +-- logging 60 at level INFO, messages should be seen every 10 events -- +-- logging 61 at level INFO, messages should be seen every 10 events -- +-- logging 62 at level INFO, messages should be seen every 10 events -- +-- logging 63 at level INFO, messages should be seen every 10 events -- +-- logging 64 at level INFO, messages should be seen every 10 events -- +-- logging 65 at level INFO, messages should be seen every 10 events -- +-- logging 66 at level INFO, messages should be seen every 10 events -- +-- logging 67 at level INFO, messages should be seen every 10 events -- +-- logging 68 at level INFO, messages should be seen every 10 events -- +-- logging 69 at level INFO, messages should be seen every 10 events -- +Info index = 60 +Info index = 61 +Info index = 62 +Info index = 63 +Info index = 64 +Info index = 65 +Info index = 66 +Info index = 67 +Info index = 68 +Info index = 69 +-- logging 70 at level INFO, messages should be seen every 10 events -- +-- logging 71 at level INFO, messages should be seen every 10 events -- +-- logging 72 at level INFO, messages should be seen every 10 events -- +-- logging 73 at level INFO, messages should be seen every 10 events -- +-- logging 74 at level INFO, messages should be seen every 10 events -- +-- logging 75 at level INFO, messages should be seen every 10 events -- +-- logging 76 at level INFO, messages should be seen every 10 events -- +-- logging 77 at level INFO, messages should be seen every 10 events -- +-- logging 78 at level INFO, messages should be seen every 10 events -- +-- logging 79 at level INFO, messages should be seen every 10 events -- +Info index = 70 +Info index = 71 +Info index = 72 +Info index = 73 +Info index = 74 +Info index = 75 +Info index = 76 +Info index = 77 +Info index = 78 +Info index = 79 +-- logging 80 at level INFO, messages should be seen every 10 events -- +-- logging 81 at level INFO, messages should be seen every 10 events -- +-- logging 82 at level INFO, messages should be seen every 10 events -- +-- logging 83 at level INFO, messages should be seen every 10 events -- +-- logging 84 at level INFO, messages should be seen every 10 events -- +-- logging 85 at level INFO, messages should be seen every 10 events -- +-- logging 86 at level INFO, messages should be seen every 10 events -- +-- logging 87 at level INFO, messages should be seen every 10 events -- +-- logging 88 at level INFO, messages should be seen every 10 events -- +-- logging 89 at level INFO, messages should be seen every 10 events -- +Info index = 80 +Info index = 81 +Info index = 82 +Info index = 83 +Info index = 84 +Info index = 85 +Info index = 86 +Info index = 87 +Info index = 88 +Info index = 89 +-- logging 90 at level INFO, messages should be seen every 10 events -- +-- logging 91 at level INFO, messages should be seen every 10 events -- +-- logging 92 at level INFO, messages should be seen every 10 events -- +-- logging 93 at level INFO, messages should be seen every 10 events -- +-- logging 94 at level INFO, messages should be seen every 10 events -- +-- logging 95 at level INFO, messages should be seen every 10 events -- +-- logging 96 at level INFO, messages should be seen every 10 events -- +-- logging 97 at level INFO, messages should be seen every 10 events -- +-- logging 98 at level INFO, messages should be seen every 10 events -- +-- logging 99 at level INFO, messages should be seen every 10 events -- +Info index = 90 +Info index = 91 +Info index = 92 +Info index = 93 +Info index = 94 +Info index = 95 +Info index = 96 +Info index = 97 +Info index = 98 +Info index = 99 +-- logging 100 at level INFO, messages should be seen every 10 events -- +-- logging 101 at level INFO, messages should be seen every 10 events -- +Info index = 100 +Info index = 101 +-- log_test7 end -------------------------------------------------------- +-- log_test8 begin -------------------------------------------------------- +-- log_test8 end -------------------------------------------------------- +-- log_test9 begin -------------------------------------------------------- +-- log_test9 end -------------------------------------------------------- +-- log_test10 begin -------------------------------------------------------- +logger not derived from logging.Logger: NotALogger +INFO:mylogger:Starting... +DEBUG:mylogger:Debug message not in exception handler (no traceback) +INFO:mylogger:About to throw exception... +DEBUG:mylogger:Debug message inside exception handler (traceback) +Traceback (most recent call last): + File "C:\Projects\RDC\Python\packages\logging\test\log_test10.py", line 77, in run + print "7" + 4 +TypeError: cannot concatenate 'str' and 'int' objects +INFO:mylogger:Done. +-- log_test10 end -------------------------------------------------------- +-- log_test12 begin -------------------------------------------------------- +INFO:log_test12:Jackdaws love my big sphinx of quartz +INFO:log_test12:Jackdaws love my big sphinx of quartz +DEBUG:log_test12:Pack my box with twelve dozen liquor jugs +DEBUG:log_test12:Pack my box with twelve dozen liquor jugs +-- log_test12 end -------------------------------------------------------- +-- log_test13 begin -------------------------------------------------------- +INFO:log_test13:Jackdaws love my big sphinx of quartz +DEBUG:log_test13:Pack my box with five dozen liquor jugs +-- log_test13 end -------------------------------------------------------- +-- log_test15 begin -------------------------------------------------------- +Unfiltered... +INFO:a:Info 1 +INFO:a.b:Info 2 +INFO:a.c:Info 3 +INFO:a.b.c:Info 4 +INFO:a.b.c.d:Info 5 +INFO:a.bb.c:Info 6 +INFO:b:Info 7 +INFO:b.a:Info 8 +INFO:c.a.b:Info 9 +INFO:a.bb:Info 10 +Filtered with 'a.b'... +INFO:a.b:Info 2 +INFO:a.b.c:Info 4 +INFO:a.b.c.d:Info 5 +-- log_test15 end -------------------------------------------------------- diff --git a/tools/python/logging/logging-0.4.9.2/test/stdout.exp b/tools/python/logging/logging-0.4.9.2/test/stdout.exp new file mode 100644 index 0000000..bf3d150 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/stdout.exp @@ -0,0 +1,24 @@ +About to start TCP server... +About to start UDP server... +About to start HTTP server... +About to start SOAP server... +-- log_test0 begin -------------------------------------------------------- +-- log_test0 end -------------------------------------------------------- +-- log_test2 begin -------------------------------------------------------- +-- log_test2 end -------------------------------------------------------- +-- log_test4 begin -------------------------------------------------------- +-- log_test4 end -------------------------------------------------------- +-- log_test7 begin -------------------------------------------------------- +-- log_test7 end -------------------------------------------------------- +-- log_test8 begin -------------------------------------------------------- +-- log_test8 end -------------------------------------------------------- +-- log_test9 begin -------------------------------------------------------- +-- log_test9 end -------------------------------------------------------- +-- log_test10 begin -------------------------------------------------------- +-- log_test10 end -------------------------------------------------------- +-- log_test12 begin -------------------------------------------------------- +-- log_test12 end -------------------------------------------------------- +-- log_test13 begin -------------------------------------------------------- +-- log_test13 end -------------------------------------------------------- +-- log_test15 begin -------------------------------------------------------- +-- log_test15 end -------------------------------------------------------- diff --git a/tools/python/logging/logging-0.4.9.2/test/warn.ini b/tools/python/logging/logging-0.4.9.2/test/warn.ini new file mode 100644 index 0000000..3a7b374 --- /dev/null +++ b/tools/python/logging/logging-0.4.9.2/test/warn.ini @@ -0,0 +1,60 @@ +[loggers] +keys=root,log02,log03,log04,log05 + +[handlers] +keys=hand01 + +[formatters] +keys=form01 + +[logger_root] +level=WARN +propagate=1 +channel= +parent= +qualname=(root) +handlers=hand01 + +[logger_log02] +level=WARN +propagate=1 +channel=A +parent=(root) +qualname=A +handlers= + +[logger_log03] +level=WARN +propagate=1 +channel=B +parent=log02 +qualname=A.B +handlers= + +[logger_log04] +level=WARN +propagate=1 +channel=C +parent=log03 +qualname=A.B.C +handlers= + +[logger_log05] +level=WARN +propagate=1 +channel=D +parent=log04 +qualname=A.B.C.D +handlers= + +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +stream=sys.stderr +args=(sys.stderr,) + +[formatter_form01] +format=warn.ini %(name)s %(levelname)s %(message)s +datefmt= + diff --git a/tools/python/logging/setup.py b/tools/python/logging/setup.py new file mode 100644 index 0000000..724e17e --- /dev/null +++ b/tools/python/logging/setup.py @@ -0,0 +1,11 @@ +#============================================================================ +# Install the logging package from python 2.3 if not present. +#============================================================================ +import os + +try: + import logging +except ImportError: + print 'logging package not found: installing' + os.chdir('logging-0.4.9.2') + execfile('setup.py') diff --git a/tools/python/ptsname/ptsname.c b/tools/python/ptsname/ptsname.c new file mode 100644 index 0000000..3849bb8 --- /dev/null +++ b/tools/python/ptsname/ptsname.c @@ -0,0 +1,44 @@ +/****************************************************************************** + * ptsname.c + * + * A python extension to expose the POSIX ptsname() function. + * + * Copyright (C) 2007 XenSource Ltd + */ + +#include +#include + +/* Needed for Python versions earlier than 2.3. */ +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC DL_EXPORT(void) +#endif + +static PyObject *do_ptsname(PyObject *self, PyObject *args) +{ + int fd; + char *path; + + if (!PyArg_ParseTuple(args, "i", &fd)) + return NULL; + + path = ptsname(fd); + + if (!path) + { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + + return PyString_FromString(path); +} + +static PyMethodDef ptsname_methods[] = { + { "ptsname", do_ptsname, METH_VARARGS }, + { NULL } +}; + +PyMODINIT_FUNC initptsname(void) +{ + Py_InitModule("ptsname", ptsname_methods); +} diff --git a/tools/python/pylintrc b/tools/python/pylintrc new file mode 100644 index 0000000..efc4b0b --- /dev/null +++ b/tools/python/pylintrc @@ -0,0 +1,307 @@ +# 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 + + + diff --git a/tools/python/remove-potcdate.sed b/tools/python/remove-potcdate.sed new file mode 100644 index 0000000..2436c49 --- /dev/null +++ b/tools/python/remove-potcdate.sed @@ -0,0 +1,19 @@ +# Sed script that remove the POT-Creation-Date line in the header entry +# from a POT file. +# +# The distinction between the first and the following occurrences of the +# pattern is achieved by looking at the hold space. +/^"POT-Creation-Date: .*"$/{ +x +# Test if the hold space is empty. +s/P/P/ +ta +# Yes it was empty. First occurrence. Remove the line. +g +d +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/tools/python/scripts/README b/tools/python/scripts/README new file mode 100644 index 0000000..a5d8759 --- /dev/null +++ b/tools/python/scripts/README @@ -0,0 +1,49 @@ +Xen API Test +============ + +xapi.py is a simple command line tool to test the functionality of a +domain lifecycle supporting, Xen API talking version of Xend. + +Creating a VM is slightly more work under the Xen API. The differences +with this and xm is: + +1. None of the devices are created during vm-create. You must use + vbd-create and vif-create to attach a new device to the VM. + +2. VM's that are created using vm-create will not start by + default. You must use vm-start to "start" the domain. + +3. VM's that are created using vm-create will not be removed on + shutdown. You must remove it using vm-delete. + +Example Configuration Files +--------------------------- + +xapi.py uses a simple python configuration file similar to xm in the +face of the lack of any other reasonable format. + +All the fields are directly mapped to the arguments that are in the +Xen API constructore for the respective classes. + +xapi.domcfg.py: example configuration for a paravirtualised domain. +xapi.vbdcfg.py: example configuration for a file based block device. +xapi.vifcfg.py: example configuration for a simple bridged network + device. + +Example Session +--------------- + +xapi.py vm-list +xapi.py vm-create xapi.domcfg.py +xapi.py vbd-create xapi.vbdcfg.py +xapi.py vif-create xapi.vifcfg.py + +Notes +----- + +Currently lacking: + +1. Any real authentication. XendAuthSessions need to be filled in with + a proper authentication implementation either using PAM or other + means. + diff --git a/tools/python/scripts/README.lifecycle b/tools/python/scripts/README.lifecycle new file mode 100644 index 0000000..1e24cc0 --- /dev/null +++ b/tools/python/scripts/README.lifecycle @@ -0,0 +1,136 @@ +Xend Lifecycle/XenAPI Implementation Changes +============================================ + +Summary of what has changed in this branch of Xend: + +Managed Domains +--------------- + +The concept of managed domains is that Xend now has the ability to +manage the lifecycle of a domain from when it is created to being +shutdown. + +XendDomain +~~~~~~~~~~ + +In order to support managed domains, XendDomain has been modified to +keep the configuration in /var/lib/xend/domains/. + +The configuration is stored in SXP format so that it can be easily +loaded by the current Xend. In the future, we may switch to an XML +format similar to how XenAPI defines a VM configuration. + +TODO: There are still places where the device configuration or VM +configuration can be altered but the managed domain does not save it. + +XendDomainInfo +~~~~~~~~~~~~~~ + +XendDomainInfo has changed to support this mode of operation, +especially with domain construction and assumptions about the domain +when it shuts down. + +All configuration option parsing and validation has been moved from +XendDomainInfo to XendConfig. The purpose is so that we can abstract +away the knowledge of SXP in XendDomainInfo. The goal is to do away +with the bulky way of accessing SXP in Xend and moving that all to a +more pythonic interface. + +The DevController stuff at the end of XendDomainInfo has also been +moved to XendDevices because now it is needed in both XendConfig and +XendDomainInfo. + +Many of the constants are moved to XendConstants which reduces the +amount of recursive or scoped imports that occur in the code. + +XendConfig +~~~~~~~~~~ + +XendConfig is the beginnings of an interface for configuration options +so that other parts of Xend do not need to know what format the +configuration in. It can accept configuration passed in as parsed SXP +format, python filename or a Xen API struct. + +It is a subclass of a python dictionary, and hence access to its +functions are via the __getitem__ accessor. + +TODO: Define a proper interface to the XendConfig which is based on +either the Xen API or some other flexible format. + +XMLRPCServer +~~~~~~~~~~~~ + +Changes to the busy loop in here and SrvServer so that the daemon +shuts down cleanly. This also allows us to catch the shutdown and +perform maintanence tasks on the domains. + +Replacing xendomains init.d script +================================== + +Some work has gone into catching Xend's shutdown so that we can do the +same tasks that xendomains init.d script does but natively in Xend. + +For instance, a new configuration option, 'on_xend_start' and +'on_xend_stop' will allow domains that are managed by Xend to start up +when Xend starts, and correspondingly stop when Xend stops. + +Xen API +======= + +The new Xen API gives a standard interface to creating, configuring, +controlling and destroying VMs and the virtual devices that belong to +it. + +It also introduces the concept of Storage Repositories (SR) which are +factories for creating disk images. + +XendDomain +~~~~~~~~~~ + +XendDomain has now separated the section for the Legacy XM XMLRPC API +and the new Xen API. + +Since many things have a UUID, these are stored and represented as +close to the existing configuration. + +XendDomainInfo +~~~~~~~~~~~~~~ + +XendDomainInfo now supports UUIDs being assigned to devices and the +domain itself. It will preserve the UUID for managed domains. + +A number of new functions are now in XendDomainInfo to provide an +interface to devices. + +XendNode +~~~~~~~~ + +Represents the Host class in the Xen API and also contains an +incomplete representation of the physical CPUs availabel for the host. + +XendAuthSessions +~~~~~~~~~~~~~~~~ + +An abstract authenticator for the Xen API. Currently it is an empty +implementation with rudimentary support for users. The plan is the add +PAM based authentication. + +XendAPI +~~~~~~~ + +The guts of the Xen API implementation. Implements all the supported +functionality of the Xen API by placing calls to the relevent objects +like XendDomain and XendDomanInfo. + +The initialisation of the XendAPI object will actually install a +number of validation decorators in order to ensure the input is +correct. It is using some features of introspection and +metaprogramming in Python to reduce the amount of replication in the +code. + +XMLRPCServer +~~~~~~~~~~~~ + +The XMLRPC Server will support both the new Xen API and the old XM +XMLRPC API. The support is clearly marked in the code. + diff --git a/tools/python/scripts/test_hvm_create.py b/tools/python/scripts/test_hvm_create.py new file mode 100644 index 0000000..35abfe0 --- /dev/null +++ b/tools/python/scripts/test_hvm_create.py @@ -0,0 +1,179 @@ +#!/usr/bin/python + +vm_cfg = { + 'name_label': 'API_HVM', + 'user_version': 1, + 'is_a_template': False, + 'auto_power_on': False, # TODO + + 'memory_static_min': 64, + 'memory_static_max': 128, + #'memory_dynamic_min': 64, + #'memory_dynamic_max': 128, + + + 'VCPUs_policy': 'credit', + 'VCPUs_params': {}, + 'VCPUs_number': 2, + + 'actions_after_shutdown': 'destroy', + 'actions_after_reboot': 'restart', + 'actions_after_crash': 'destroy', + + 'PV_bootloader': '', + 'PV_bootloader_args': '', + + 'PV_kernel': '', + 'PV_ramdisk': '', + 'PV_args': '', + + 'HVM_boot': 'cda', + 'platform_std_VGA': False, + 'platform_serial': '', + 'platform_localtime': False, + 'platform_clock_offset': False, + 'platform_enable_audio': False, + 'PCI_bus': '' +} + +local_vdi_cfg = { + 'name_label': 'gentoo.hvm', + 'name_description': '', + 'virtual_size': 0, + 'type': 'system', + 'parent': '', + 'SR_name': 'Local', + 'sharable': False, + 'read_only': False, + 'other_config': {'location': 'file:/root/gentoo.amd64.hvm.img'}, +} + +local_vbd_cfg = { + 'VDI': '', + 'VM': '', + 'device': 'hda', + 'mode': 'RW', + 'type': 'disk', + 'driver': 'ioemu', +} + +vif_cfg = { + 'name': 'API_VIF', + 'type': 'ioemu', + 'device': '', + 'network': '', + 'MAC': '', + 'MTU': 1500, +} + +console_cfg = { + 'protocol': 'rfb', + 'other_config': {'vncunused': 1, 'vncpasswd': 'testing'}, +} + + +import sys +import time +sys.path.append('/usr/lib/python') + +from xapi import connect, execute + +def test_vm_create(): + server, session = connect() + vm_uuid = None + local_vdi_uuid = None + local_vbd_uuid = None + vif_uuid = None + + # List all VMs + vm_list = execute(server, 'VM.get_all', (session,)) + vm_names = [] + for vm_uuid in vm_list: + vm_record = execute(server, 'VM.get_record', (session, vm_uuid)) + vm_names.append(vm_record['name_label']) + + # Get default SR + local_sr_list = execute(server, 'SR.get_by_name_label', + (session, local_vdi_cfg['SR_name'])) + local_sr_uuid = local_sr_list[0] + + # Get default network + net_list = execute(server, 'network.get_all', (session,)) + net_uuid = net_list[0] + + try: + # Create a new VM + print 'Create VM' + vm_uuid = execute(server, 'VM.create', (session, vm_cfg)) + + print 'Create VDI' + # Create a new VDI (Local) + local_vdi_cfg['SR'] = local_sr_uuid + local_vdi_uuid = execute(server, 'VDI.create', + (session, local_vdi_cfg)) + + print 'Create VBD' + # Create a new VBD (Local) + local_vbd_cfg['VM'] = vm_uuid + local_vbd_cfg['VDI'] = local_vdi_uuid + local_vbd_uuid = execute(server, 'VBD.create', + (session, local_vbd_cfg)) + + print 'Craete VIF' + # Create a new VIF + vif_cfg['network'] = net_uuid + vif_cfg['VM'] = vm_uuid + vif_uuid = execute(server, 'VIF.create', (session, vif_cfg)) + + # Create a console + console_cfg['VM'] = vm_uuid + console_uuid = execute(server, 'console.create', + (session, console_cfg)) + print console_uuid + + # Start the VM + execute(server, 'VM.start', (session, vm_uuid, False)) + + time.sleep(30) + + test_suspend = False + if test_suspend: + print 'Suspending VM..' + execute(server, 'VM.suspend', (session, vm_uuid)) + print 'Suspended VM.' + time.sleep(5) + print 'Resuming VM ...' + execute(server, 'VM.resume', (session, vm_uuid, False)) + print 'Resumed VM.' + + # Wait for user to say we're good to shut it down + while True: + destroy = raw_input('destroy VM? ') + if destroy[0] in ('y', 'Y'): + break + + finally: + # Clean up + if vif_uuid: + execute(server, 'VIF.destroy', (session, vif_uuid)) + + if local_vbd_uuid: + execute(server, 'VBD.destroy', (session, local_vbd_uuid)) + if local_vdi_uuid: + execute(server, 'VDI.destroy', (session, local_vdi_uuid)) + + if vm_uuid: + try: + execute(server, 'VM.hard_shutdown', (session, vm_uuid)) + time.sleep(2) + except: + pass + try: + execute(server, 'VM.destroy', (session, vm_uuid)) + except: + pass + + +if __name__ == "__main__": + test_vm_create() + diff --git a/tools/python/scripts/test_vm_create.py b/tools/python/scripts/test_vm_create.py new file mode 100644 index 0000000..6575f15 --- /dev/null +++ b/tools/python/scripts/test_vm_create.py @@ -0,0 +1,212 @@ +#!/usr/bin/python + +vm_cfg = { + 'name_label': 'APIVM', + 'user_version': 1, + 'is_a_template': False, + 'auto_power_on': False, # TODO + + 'memory_static_min': 64, + 'memory_static_max': 128, + #'memory_dynamic_min': 64, + #'memory_dynamic_max': 128, + + + 'VCPUs_policy': 'credit', + 'VCPUs_params': '', + 'VCPUs_number': 2, + + 'actions_after_shutdown': 'destroy', + 'actions_after_reboot': 'restart', + 'actions_after_crash': 'destroy', + + 'PV_bootloader': '', + 'PV_bootloader_args': '', + + 'PV_kernel': '/boot/vmlinuz-2.6.18-xenU', + 'PV_ramdisk': '', + 'PV_args': 'root=/dev/sda1 ro', + + #'HVM_boot': '', + 'platform_std_VGA': False, + 'platform_serial': '', + 'platform_localtime': False, + 'platform_clock_offset': False, + 'platform_enable_audio': False, + 'PCI_bus': '' +} + +vdi_cfg = { + 'name_label': 'API_VDI', + 'name_description': '', + 'virtual_size': 100 * 1024 * 1024 * 1024, + 'type': 'system', + 'parent': '', + 'SR_name': 'QCoW', + 'sharable': False, + 'read_only': False, +} + +vbd_cfg = { + 'VDI': '', + 'VM': '', + 'device': 'sda2', + 'mode': 'RW', + 'type': 'disk', + 'driver': 'paravirtualised', +} + +local_vdi_cfg = { + 'name_label': 'gentoo.amd64.img', + 'name_description': '', + 'virtual_size': 0, + 'type': 'system', + 'parent': '', + 'SR_name': 'Local', + 'sharable': False, + 'read_only': False, + 'other_config': {'location': 'file:/root/gentoo.amd64.img'}, +} + +local_vbd_cfg = { + 'VDI': '', + 'VM': '', + 'device': 'sda1', + 'mode': 'RW', + 'type': 'disk', + 'driver': 'paravirtualised', +} + +vif_cfg = { + 'name': 'API_VIF', + 'type': 'paravirtualised', + 'device': '', + 'network': '', + 'MAC': '', + 'MTU': 1500, +} + +console_cfg = { + 'protocol': 'rfb', + 'other_config': {'vncunused': 1, 'vncpasswd': 'testing'}, +} + +import sys +import time +sys.path.append('/usr/lib/python') + +from xapi import connect, execute + +def test_vm_create(): + server, session = connect() + vm_uuid = None + vdi_uuid = None + local_vdi_uuid = None + local_vbd_uuid = None + vbd_uuid = None + vif_uuid = None + + # List all VMs + vm_list = execute(server, 'VM.get_all', (session,)) + vm_names = [] + for vm_uuid in vm_list: + vm_record = execute(server, 'VM.get_record', (session, vm_uuid)) + vm_names.append(vm_record['name_label']) + + # Get default SR + sr_list = execute(server, 'SR.get_by_name_label', (session, + vdi_cfg['SR_name'])) + sr_uuid = sr_list[0] + + local_sr_list = execute(server, 'SR.get_by_name_label', + (session, local_vdi_cfg['SR_name'])) + local_sr_uuid = local_sr_list[0] + + # Get default network + net_list = execute(server, 'network.get_all', (session,)) + net_uuid = net_list[0] + + try: + # Create a new VM + vm_uuid = execute(server, 'VM.create', (session, vm_cfg)) + + # Create a new VDI + vdi_cfg['SR'] = sr_uuid + vdi_uuid = execute(server, 'VDI.create', (session, vdi_cfg)) + + # Create a VDI backed VBD + vbd_cfg['VM'] = vm_uuid + vbd_cfg['VDI'] = vdi_uuid + vbd_uuid = execute(server, 'VBD.create', (session, vbd_cfg)) + + # Create a new VDI (Local) + local_vdi_cfg['SR'] = local_sr_uuid + local_vdi_uuid = execute(server, 'VDI.create', + (session, local_vdi_cfg)) + + # Create a new VBD (Local) + local_vbd_cfg['VM'] = vm_uuid + local_vbd_cfg['VDI'] = local_vdi_uuid + local_vbd_uuid = execute(server, 'VBD.create', + (session, local_vbd_cfg)) + + # Create a new VIF + vif_cfg['network'] = net_uuid + vif_cfg['VM'] = vm_uuid + vif_uuid = execute(server, 'VIF.create', (session, vif_cfg)) + + # Create a console + console_cfg['VM'] = vm_uuid + console_uuid = execute(server, 'console.create', + (session, console_cfg)) + print console_uuid + + # Start the VM + execute(server, 'VM.start', (session, vm_uuid, False)) + + time.sleep(30) + + test_suspend = False + if test_suspend: + print 'Suspending VM..' + execute(server, 'VM.suspend', (session, vm_uuid)) + print 'Suspended VM.' + time.sleep(5) + print 'Resuming VM ...' + execute(server, 'VM.resume', (session, vm_uuid, False)) + print 'Resumed VM.' + + finally: + # Wait for user to say we're good to shut it down + while True: + destroy = raw_input('destroy VM? ') + if destroy[0] in ('y', 'Y'): + break + + # Clean up + if vif_uuid: + execute(server, 'VIF.destroy', (session, vif_uuid)) + + if local_vbd_uuid: + execute(server, 'VBD.destroy', (session, local_vbd_uuid)) + if local_vdi_uuid: + execute(server, 'VDI.destroy', (session, local_vdi_uuid)) + + if vbd_uuid: + execute(server, 'VBD.destroy', (session, vbd_uuid)) + if vdi_uuid: + execute(server, 'VDI.destroy', (session, vdi_uuid)) + + if vm_uuid: + try: + execute(server, 'VM.hard_shutdown', (session, vm_uuid)) + time.sleep(2) + except: + pass + + execute(server, 'VM.destroy', (session, vm_uuid)) + + +if __name__ == "__main__": + test_vm_create() + diff --git a/tools/python/scripts/xapi.domcfg.py b/tools/python/scripts/xapi.domcfg.py new file mode 100644 index 0000000..d7b8ae5 --- /dev/null +++ b/tools/python/scripts/xapi.domcfg.py @@ -0,0 +1,37 @@ +# +# VM Configuration for Xen API +# + +name_label = 'GentooAPI' +name_description = 'Gentoo VM via API' +user_version = 1 +is_a_template = False +memory_static_max = 32 +memory_dynamic_max = 32 +memory_dynamic_min = 32 +memory_static_min = 32 +VCPUs_policy = '' +VCPUs_params = '' +VCPUS_features_required = '' +VCPUs_features_can_use = '' +VCPUs_features_force_on = '' +VCPUs_features_force_off = '' +actions_after_shutdown = 'destroy' +actions_after_reboot = 'restart' +actions_after_suspend = 'destroy' +actions_after_crash = 'restart' +bios_boot = '' +platform_std_VGA = False +platform_serial = '' +platform_localtime = False +platform_clock_offset = False +platform_enable_audio = False +builder = 'linux' +boot_method = '' # this will remove the kernel/initrd ?? +kernel_kernel = '/boot/vmlinuz-2.6.16.29-xen' +kernel_initrd = '/root/initrd-2.6.16.29-xen.img' +kernel_args = 'root=/dev/sda1 ro' +grub_cmdline = '' +PCI_bus = '' +other_config = '' + diff --git a/tools/python/scripts/xapi.py b/tools/python/scripts/xapi.py new file mode 100644 index 0000000..1a07795 --- /dev/null +++ b/tools/python/scripts/xapi.py @@ -0,0 +1,875 @@ +#!/usr/bin/python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ + +import sys +import time +import re +import os +sys.path.append('/usr/lib/python') + +from xen.util.xmlrpclib2 import ServerProxy +from optparse import * +from pprint import pprint +from types import DictType +from getpass import getpass + +# Get default values from the environment +SERVER_URI = os.environ.get('XAPI_SERVER_URI', 'http://localhost:9363/') +SERVER_USER = os.environ.get('XAPI_SERVER_USER', '') +SERVER_PASS = os.environ.get('XAPI_SERVER_PASS', '') + +MB = 1024 * 1024 + +HOST_INFO_FORMAT = '%-20s: %-50s' +VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(VCPUs_number)-5s'\ + ' %(power_state)-10s %(uuid)-36s' +SR_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(physical_size)-10s' \ + '%(type)-10s' +VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s' +VBD_LIST_FORMAT = '%(device)-6s %(uuid)-36s %(VDI)-8s' +TASK_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(status)-8s %(progress)-4s' +VIF_LIST_FORMAT = '%(name)-8s %(device)-7s %(uuid)-36s %(MAC)-10s' +CONSOLE_LIST_FORMAT = '%(uuid)-36s %(protocol)-8s %(location)-32s' + +COMMANDS = { + 'host-info': ('', 'Get Xen Host Info'), + 'host-set-name': ('', 'Set host name'), + 'pif-list': ('', 'List all PIFs'), + 'sr-list': ('', 'List all SRs'), + 'vbd-list': ('', 'List all VBDs'), + 'vbd-create': (' [opts]', + 'Create VBD attached to domname'), + 'vdi-create': (' [opts]', 'Create a VDI'), + 'vdi-list' : ('', 'List all VDI'), + 'vdi-rename': (' ', 'Rename VDI'), + 'vdi-destroy': ('', 'Delete VDI'), + 'vif-create': (' ', 'Create VIF attached to domname'), + 'vtpm-create' : (' ', 'Create VTPM attached to domname'), + + 'vm-create': ('', 'Create VM with python config'), + 'vm-destroy': ('', 'Delete VM'), + + 'vm-list': ('[--long]', 'List all domains.'), + 'vm-name': ('', 'Name of UUID.'), + 'vm-shutdown': (' [opts]', 'Shutdown VM with name'), + 'vm-start': ('', 'Start VM with name'), + 'vm-uuid': ('', 'UUID of a domain by name.'), + 'async-vm-start': ('', 'Start VM asynchronously'), +} + +OPTIONS = { + 'sr-list': [(('-l', '--long'), + {'action':'store_true', + 'help':'List all properties of SR'}) + ], + + 'vdi-list': [(('-l', '--long'), + {'action':'store_true', + 'help':'List all properties of VDI'}) + ], + 'vif-list': [(('-l', '--long'), + {'action':'store_true', + 'help':'List all properties of VIF'}) + ], + 'vm-list': [(('-l', '--long'), + {'action':'store_true', + 'help':'List all properties of VMs'}) + ], + 'vm-shutdown': [(('-f', '--force'), {'help': 'Shutdown Forcefully', + 'action': 'store_true'})], + + 'vdi-create': [(('--name-label',), {'help': 'Name for VDI'}), + (('--name-description',), {'help': 'Description for VDI'}), + (('--virtual-size',), {'type': 'int', + 'default': 0, + 'help': 'Size of VDI in bytes'}), + (('--type',), {'choices': ['system', 'user', 'ephemeral'], + 'default': 'system', + 'help': 'VDI type'}), + (('--sharable',), {'action': 'store_true', + 'help': 'VDI sharable'}), + (('--read-only',), {'action': 'store_true', + 'help': 'Read only'}), + (('--sr',), {})], + + 'vbd-create': [(('--VDI',), {'help': 'UUID of VDI to attach to.'}), + (('--mode',), {'choices': ['RO', 'RW'], + 'help': 'device mount mode'}), + (('--driver',), {'choices':['paravirtualised', 'ioemu'], + 'help': 'Driver for VBD'}), + (('--device',), {'help': 'Device name on guest domain'})] + +} + +class OptionError(Exception): + pass + +class XenAPIError(Exception): + pass + +# +# Extra utility functions +# + +class IterableValues(Values): + """Better interface to the list of values from optparse.""" + + def __iter__(self): + for opt, val in self.__dict__.items(): + if opt[0] == '_' or callable(val): + continue + yield opt, val + + +def parse_args(cmd_name, args, set_defaults = False): + argstring, desc = COMMANDS[cmd_name] + parser = OptionParser(usage = 'xapi %s %s' % (cmd_name, argstring), + description = desc) + if cmd_name in OPTIONS: + for optargs, optkwds in OPTIONS[cmd_name]: + parser.add_option(*optargs, **optkwds) + + if set_defaults: + default_values = parser.get_default_values() + defaults = IterableValues(default_values.__dict__) + else: + defaults = IterableValues() + (opts, extraargs) = parser.parse_args(args = list(args), + values = defaults) + return opts, extraargs + +def execute(server, fn, args, async = False): + if async: + func = eval('server.Async.%s' % fn) + else: + func = eval('server.%s' % fn) + + result = func(*args) + if type(result) != DictType: + raise TypeError("Function returned object of type: %s" % + str(type(result))) + if 'Value' not in result: + raise XenAPIError(*result['ErrorDescription']) + return result['Value'] + +_initialised = False +_server = None +_session = None +def connect(*args): + global _server, _session, _initialised + + if not _initialised: + # try without password or default credentials + try: + _server = ServerProxy(SERVER_URI) + _session = execute(_server.session, 'login_with_password', + (SERVER_USER, SERVER_PASS)) + except: + login = raw_input("Login: ") + password = getpass() + creds = (login, password) + _server = ServerProxy(SERVER_URI) + _session = execute(_server.session, 'login_with_password', + creds) + + _initialised = True + return (_server, _session) + +def _stringify(adict): + return dict([(k, str(v)) for k, v in adict.items()]) + +def _read_python_cfg(filename): + cfg = {} + execfile(filename, {}, cfg) + return cfg + +def resolve_vm(server, session, vm_name): + vm_uuid = execute(server, 'VM.get_by_name_label', (session, vm_name)) + if not vm_uuid: + return None + else: + return vm_uuid[0] + +def resolve_vdi(server, session, vdi_name): + vdi_uuid = execute(server, 'VDI.get_by_name_label', (session, vdi_name)) + if not vdi_uuid: + return None + else: + return vdi_uuid[0] + +# +# Actual commands +# + +def xapi_host_info(args, async = False): + server, session = connect() + hosts = execute(server, 'host.get_all', (session,)) + for host in hosts: # there is only one, but .. + hostinfo = execute(server, 'host.get_record', (session, host)) + print HOST_INFO_FORMAT % ('Name', hostinfo['name_label']) + print HOST_INFO_FORMAT % ('Version', hostinfo['software_version']) + print HOST_INFO_FORMAT % ('CPUs', len(hostinfo['host_CPUs'])) + print HOST_INFO_FORMAT % ('VMs', len(hostinfo['resident_VMs'])) + print HOST_INFO_FORMAT % ('UUID', host) + + for host_cpu_uuid in hostinfo['host_CPUs']: + host_cpu = execute(server, 'host_cpu.get_record', + (session, host_cpu_uuid)) + print 'CPU %s Util: %.2f' % (host_cpu['number'], + float(host_cpu['utilisation'])) + +def xapi_host_set_name(args, async = False): + if len(args) < 1: + raise OptionError("No hostname specified") + + server, session = connect() + hosts = execute(server, 'host.get_all', (session,)) + if len(hosts) > 0: + execute(server, 'host.set_name_label', (session, hosts[0], args[0])) + print 'Hostname: %s' % execute(server, 'host.get_name_label', + (session, hosts[0])) + +def xapi_vm_uuid(args, async = False): + if len(args) < 1: + raise OptionError("No domain name specified") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print vm_uuid + +def xapi_vm_name(args, async = False): + if len(args) < 1: + raise OptionError("No UUID specified") + + server, session = connect() + vm_name = execute(server, 'VM.get_name_label', (session, args[0])) + print vm_name + +def xapi_vm_list(args, async = False): + opts, args = parse_args('vm-list', args, set_defaults = True) + is_long = opts and opts.long + + list_only = args + + server, session = connect() + vm_uuids = execute(server, 'VM.get_all', (session,)) + if not is_long: + print VM_LIST_FORMAT % {'name_label':'Name', + 'memory_actual':'Mem', + 'VCPUs_number': 'VCPUs', + 'power_state': 'State', + 'uuid': 'UUID'} + + for uuid in vm_uuids: + vm_info = execute(server, 'VM.get_record', (session, uuid)) + + # skip domain if we don't want + if list_only and vm_info['name_label'] not in list_only: + continue + + if is_long: + vbds = vm_info['VBDs'] + vifs = vm_info['VIFs'] + vtpms = vm_info['VTPMs'] + vif_infos = [] + vbd_infos = [] + vtpm_infos = [] + for vbd in vbds: + vbd_info = execute(server, 'VBD.get_record', (session, vbd)) + vbd_infos.append(vbd_info) + for vif in vifs: + vif_info = execute(server, 'VIF.get_record', (session, vif)) + vif_infos.append(vif_info) + for vtpm in vtpms: + vtpm_info = execute(server, 'VTPM.get_record', (session, vtpm)) + vtpm_infos.append(vtpm_info) + vm_info['VBDs'] = vbd_infos + vm_info['VIFs'] = vif_infos + vm_info['VTPMs'] = vtpm_infos + pprint(vm_info) + else: + print VM_LIST_FORMAT % _stringify(vm_info) + +def xapi_vm_create(args, async = False): + if len(args) < 1: + raise OptionError("Configuration file not specified") + + filename = args[0] + cfg = _read_python_cfg(filename) + + print 'Creating VM from %s ..' % filename + server, session = connect() + uuid = execute(server, 'VM.create', (session, cfg), async = async) + print 'Done. (%s)' % uuid + print uuid + +def xapi_vm_destroy(args, async = False): + if len(args) < 1: + raise OptionError("No domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print 'Destroying VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.destroy', (session, vm_uuid), async = async) + print 'Done.' + + +def xapi_vm_start(args, async = False): + if len(args) < 1: + raise OptionError("No Domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print 'Starting VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.start', (session, vm_uuid, False), async = async) + if async: + print 'Task started: %s' % success + else: + print 'Done.' + +def xapi_vm_suspend(args, async = False): + if len(args) < 1: + raise OptionError("No Domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print 'Suspending VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.suspend', (session, vm_uuid), async = async) + if async: + print 'Task started: %s' % success + else: + print 'Done.' + + +def xapi_vm_resume(args, async = False): + if len(args) < 1: + raise OptionError("No Domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print 'Resuming VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.resume', (session, vm_uuid, False), async = async) + if async: + print 'Task started: %s' % success + else: + print 'Done.' + +def xapi_vm_pause(args, async = False): + if len(args) < 1: + raise OptionError("No Domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print 'Pausing VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.pause', (session, vm_uuid), async = async) + if async: + print 'Task started: %s' % success + else: + print 'Done.' + +def xapi_vm_unpause(args, async = False): + if len(args) < 1: + raise OptionError("No Domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + print 'Pausing VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.unpause', (session, vm_uuid), async = async) + if async: + print 'Task started: %s' % success + else: + print 'Done.' + +def xapi_task_list(args, async = False): + server, session = connect() + all_tasks = execute(server, 'task.get_all', (session,)) + + print TASK_LIST_FORMAT % {'name_label': 'Task Name', + 'uuid': 'UUID', + 'status': 'Status', + 'progress': '%'} + + for task_uuid in all_tasks: + task = execute(server, 'task.get_record', (session, task_uuid)) + print TASK_LIST_FORMAT % task + +def xapi_task_clear(args, async = False): + server, session = connect() + all_tasks = execute(server, 'task.get_all', (session,)) + for task_uuid in all_tasks: + success = execute(server, 'task.destroy', (session, task_uuid)) + print 'Destroyed Task %s' % task_uuid + +def xapi_vm_shutdown(args, async = False): + opts, args = parse_args("vm-shutdown", args, set_defaults = True) + + if len(args) < 1: + raise OptionError("No Domain name specified.") + + server, session = connect() + vm_uuid = resolve_vm(server, session, args[0]) + if opts.force: + print 'Forcefully shutting down VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.hard_shutdown', (session, vm_uuid), async = async) + else: + print 'Shutting down VM %s (%s)' % (args[0], vm_uuid) + success = execute(server, 'VM.clean_shutdown', (session, vm_uuid), async = async) + + if async: + print 'Task started: %s' % success + else: + print 'Done.' + +def xapi_vbd_create(args, async = False): + opts, args = parse_args('vbd-create', args) + + if len(args) < 2: + raise OptionError("Configuration file and domain not specified") + + domname = args[0] + + if len(args) > 1: + filename = args[1] + cfg = _read_python_cfg(filename) + else: + cfg = {} + + for opt, val in opts: + cfg[opt] = val + + print 'Creating VBD ...', + server, session = connect() + vm_uuid = resolve_vm(server, session, domname) + cfg['VM'] = vm_uuid + vbd_uuid = execute(server, 'VBD.create', (session, cfg), async = async) + if async: + print 'Task started: %s' % vbd_uuid + else: + print 'Done. (%s)' % vbd_uuid + +def xapi_vif_create(args, async = False): + if len(args) < 2: + raise OptionError("Configuration file not specified") + + domname = args[0] + filename = args[1] + cfg = _read_python_cfg(filename) + + print 'Creating VIF from %s ..' % filename + server, session = connect() + vm_uuid = resolve_vm(server, session, domname) + cfg['VM'] = vm_uuid + vif_uuid = execute(server, 'VIF.create', (session, cfg), async = async) + if async: + print 'Task started: %s' % vif_uuid + else: + print 'Done. (%s)' % vif_uuid + +def xapi_vbd_list(args, async = False): + server, session = connect() + domname = args[0] + + dom_uuid = resolve_vm(server, session, domname) + vbds = execute(server, 'VM.get_VBDs', (session, dom_uuid)) + + print VBD_LIST_FORMAT % {'device': 'Device', + 'uuid' : 'UUID', + 'VDI': 'VDI'} + + for vbd in vbds: + vbd_struct = execute(server, 'VBD.get_record', (session, vbd)) + print VBD_LIST_FORMAT % vbd_struct + + +def xapi_vbd_stats(args, async = False): + server, session = connect() + domname = args[0] + dom_uuid = resolve_vm(server, session, domname) + + vbds = execute(server, 'VM.get_VBDs', (session, dom_uuid)) + for vbd_uuid in vbds: + print execute(server, 'VBD.get_io_read_kbs', (session, vbd_uuid)) + +def xapi_vif_list(args, async = False): + server, session = connect() + opts, args = parse_args('vdi-list', args, set_defaults = True) + is_long = opts and opts.long + + domname = args[0] + + dom_uuid = resolve_vm(server, session, domname) + vifs = execute(server, 'VM.get_VIFs', (session, dom_uuid)) + + if not is_long: + print VIF_LIST_FORMAT % {'name': 'Name', + 'device': 'Device', + 'uuid' : 'UUID', + 'MAC': 'MAC'} + + for vif in vifs: + vif_struct = execute(server, 'VIF.get_record', (session, vif)) + print VIF_LIST_FORMAT % vif_struct + else: + for vif in vifs: + vif_struct = execute(server, 'VIF.get_record', (session, vif)) + pprint(vif_struct) + +def xapi_console_list(args, async = False): + server, session = connect() + opts, args = parse_args('vdi-list', args, set_defaults = True) + is_long = opts and opts.long + + domname = args[0] + + dom_uuid = resolve_vm(server, session, domname) + consoles = execute(server, 'VM.get_consoles', (session, dom_uuid)) + + if not is_long: + print CONSOLE_LIST_FORMAT % {'protocol': 'Protocol', + 'location': 'Location', + 'uuid': 'UUID'} + + for console in consoles: + console_struct = execute(server, 'console.get_record', + (session, console)) + print CONSOLE_LIST_FORMAT % console_struct + else: + for console in consoles: + console_struct = execute(server, 'console.get_record', + (session, console)) + pprint(console_struct) + + +def xapi_vdi_list(args, async = False): + opts, args = parse_args('vdi-list', args, set_defaults = True) + is_long = opts and opts.long + + server, session = connect() + vdis = execute(server, 'VDI.get_all', (session,)) + + if not is_long: + print VDI_LIST_FORMAT % {'name_label': 'VDI Label', + 'uuid' : 'UUID', + 'virtual_size': 'Bytes'} + + for vdi in vdis: + vdi_struct = execute(server, 'VDI.get_record', (session, vdi)) + print VDI_LIST_FORMAT % vdi_struct + + else: + for vdi in vdis: + vdi_struct = execute(server, 'VDI.get_record', (session, vdi)) + pprint(vdi_struct) + +def xapi_sr_list(args, async = False): + opts, args = parse_args('sr-list', args, set_defaults = True) + is_long = opts and opts.long + + server, session = connect() + srs = execute(server, 'SR.get_all', (session,)) + if not is_long: + print SR_LIST_FORMAT % {'name_label': 'SR Label', + 'uuid' : 'UUID', + 'physical_size': 'Size (MB)', + 'type': 'Type'} + + for sr in srs: + sr_struct = execute(server, 'SR.get_record', (session, sr)) + sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB + print SR_LIST_FORMAT % sr_struct + else: + for sr in srs: + sr_struct = execute(server, 'SR.get_record', (session, sr)) + pprint(sr_struct) + +def xapi_sr_rename(args, async = False): + server, session = connect() + sr = execute(server, 'SR.get_by_name_label', (session, args[0])) + execute(server, 'SR.set_name_label', (session, sr[0], args[1])) + +def xapi_vdi_create(args, async = False): + opts, args = parse_args('vdi-create', args) + + if len(args) > 0: + cfg = _read_python_cfg(args[0]) + else: + cfg = {} + + for opt, val in opts: + cfg[opt] = val + + server, session = connect() + srs = [] + if cfg.get('SR'): + srs = execute(server, 'SR.get_by_name_label', (session, cfg['SR'])) + else: + srs = execute(server, 'SR.get_all', (session,)) + + sr = srs[0] + cfg['SR'] = sr + + size = cfg['virtual_size']/MB + print 'Creating VDI of size: %dMB ..' % size, + uuid = execute(server, 'VDI.create', (session, cfg), async = async) + if async: + print 'Task started: %s' % uuid + else: + print 'Done. (%s)' % uuid + + +def xapi_vdi_destroy(args, async = False): + server, session = connect() + if len(args) < 1: + raise OptionError('Not enough arguments') + + vdi_uuid = args[0] + print 'Deleting VDI %s' % vdi_uuid + result = execute(server, 'VDI.destroy', (session, vdi_uuid), async = async) + if async: + print 'Task started: %s' % result + else: + print 'Done.' + +def xapi_vdi_rename(args, async = False): + server, session = connect() + if len(args) < 2: + raise OptionError('Not enough arguments') + + vdi_uuid = execute(server, 'VDI.get_by_name_label', session, args[0]) + vdi_name = args[1] + + print 'Renaming VDI %s to %s' % (vdi_uuid[0], vdi_name) + result = execute(server, 'VDI.set_name_label', + (session, vdi_uuid[0], vdi_name), async = async) + if async: + print 'Task started: %s' % result + else: + print 'Done.' + + + +def xapi_vtpm_create(args, async = False): + server, session = connect() + domname = args[0] + cfg = _read_python_cfg(args[1]) + + vm_uuid = resolve_vm(server, session, domname) + cfg['VM'] = vm_uuid + print "Creating vTPM with cfg = %s" % cfg + vtpm_uuid = execute(server, 'VTPM.create', (session, cfg)) + print "Done. (%s)" % vtpm_uuid + + +def xapi_pif_list(args, async = False): + server, session = connect() + pif_uuids = execute(server, 'PIF.get_all', (session,)) + for pif_uuid in pif_uuids: + pif = execute(server, 'PIF.get_record', (session, pif_uuid)) + print pif + + +def xapi_debug_wait(args, async = False): + secs = 10 + if len(args) > 0: + secs = int(args[0]) + server, session = connect() + task_uuid = execute(server, 'debug.wait', (session, secs), async=async) + print 'Task UUID: %s' % task_uuid + +def xapi_vm_stat(args, async = False): + domname = args[0] + + server, session = connect() + vm_uuid = resolve_vm(server, session, domname) + vif_uuids = execute(server, 'VM.get_VIFs', (session, vm_uuid)) + vbd_uuids = execute(server, 'VM.get_VBDs', (session, vm_uuid)) + vcpus_utils = execute(server, 'VM.get_VCPUs_utilisation', + (session, vm_uuid)) + + for vcpu_num in sorted(vcpus_utils.keys()): + print 'CPU %s : %5.2f%%' % (vcpu_num, vcpus_utils[vcpu_num] * 100) + + for vif_uuid in vif_uuids: + vif = execute(server, 'VIF.get_record', (session, vif_uuid)) + print '%(device)s: rx: %(io_read_kbs)10.2f tx: %(io_write_kbs)10.2f' \ + % vif + for vbd_uuid in vbd_uuids: + vbd = execute(server, 'VBD.get_record', (session, vbd_uuid)) + print '%(device)s: rd: %(io_read_kbs)10.2f wr: %(io_write_kbs)10.2f' \ + % vbd + +# +# Command Line Utils +# +import cmd +import shlex + +class XenAPICmd(cmd.Cmd): + def __init__(self, server, session): + cmd.Cmd.__init__(self) + self.server = server + self.session = session + self.prompt = ">>> " + + def default(self, line): + words = shlex.split(line) + if len(words) > 0: + cmd_name = words[0].replace('-', '_') + is_async = 'async' in cmd_name + if is_async: + cmd_name = re.sub('async_', '', cmd_name) + + func_name = 'xapi_%s' % cmd_name + func = globals().get(func_name) + + if func: + try: + args = tuple(words[1:]) + func(args, async = is_async) + return True + except SystemExit: + return False + except OptionError, e: + print 'Error:', str(e) + return False + except Exception, e: + import traceback + traceback.print_exc() + return False + print '*** Unknown command: %s' % words[0] + return False + + def do_EOF(self, line): + print + sys.exit(0) + + def do_help(self, line): + usage(print_usage = False) + + def emptyline(self): + pass + + def postcmd(self, stop, line): + return False + + def precmd(self, line): + words = shlex.split(line) + if len(words) > 0: + words0 = words[0].replace('-', '_') + return ' '.join([words0] + words[1:]) + else: + return line + +def shell(): + server, session = connect() + x = XenAPICmd(server, session) + x.cmdloop('Xen API Prompt. Type "help" for a list of functions') + +def usage(command = None, print_usage = True): + if not command: + if print_usage: + print 'Usage: xapi [options] [args]' + print + print 'Subcommands:' + print + + for func in sorted(globals().keys()): + if func.startswith('xapi_'): + command = func[5:].replace('_', '-') + args, description = COMMANDS.get(command, ('', '')) + print '%-16s %-40s' % (command, description) + print + else: + parse_args(command, ['-h']) + +def main(args): + + # poor man's optparse that doesn't abort on unrecognised opts + + options = {} + remaining = [] + + arg_n = 0 + while args: + arg = args.pop(0) + + if arg in ('--help', '-h'): + options['help'] = True + elif arg in ('--server', '-s') and args: + options['server'] = args.pop(0) + elif arg in ('--user', '-u') and args: + options['user'] = args.pop(0) + elif arg in ('--password', '-p') and args: + options['password'] = args.pop(0) + else: + remaining.append(arg) + + # abort here if these conditions are true + + if options.get('help') and not remaining: + usage() + sys.exit(1) + + if options.get('help') and remaining: + usage(remaining[0]) + sys.exit(1) + + if not remaining: + usage() + sys.exit(1) + + if options.get('server'): + # it is ugly to use a global, but it is simple + global SERVER_URI + SERVER_URI = options['server'] + + if options.get('user'): + global SERVER_USER + SERVER_USER = options['user'] + + if options.get('password'): + global SERVER_PASS + SERVER_PASS = options['password'] + + subcmd = remaining[0].replace('-', '_') + is_async = 'async' in subcmd + if is_async: + subcmd = re.sub('async_', '', subcmd) + subcmd_func_name = 'xapi_' + subcmd + subcmd_func = globals().get(subcmd_func_name, None) + + if subcmd == 'shell': + shell() + elif not subcmd_func or not callable(subcmd_func): + print 'Error: Unable to find subcommand \'%s\'' % subcmd + usage() + sys.exit(1) + + try: + subcmd_func(remaining[1:], async = is_async) + except XenAPIError, e: + print 'Error: %s' % str(e.args[0]) + sys.exit(2) + except OptionError, e: + print 'Error: %s' % e + + sys.exit(0) + +if __name__ == "__main__": + import sys + main(sys.argv[1:]) diff --git a/tools/python/scripts/xapi.vbdcfg.py b/tools/python/scripts/xapi.vbdcfg.py new file mode 100644 index 0000000..82dcaf8 --- /dev/null +++ b/tools/python/scripts/xapi.vbdcfg.py @@ -0,0 +1,12 @@ +# +# Virtual Block Device (VBD) Xen API Configuration +# +# Note: There is a non-API field here called "image" which is a backwards +# compat addition so you can mount to old images. +# + +VDI = '' +device = 'sda1' +mode = 'RW' +driver = 'paravirtualised' +image = 'file:/root/gentoo.amd64.img' diff --git a/tools/python/scripts/xapi.vdicfg.py b/tools/python/scripts/xapi.vdicfg.py new file mode 100644 index 0000000..cb63653 --- /dev/null +++ b/tools/python/scripts/xapi.vdicfg.py @@ -0,0 +1,6 @@ +name_label = 'VDI 1' +name_description = '' +virtual_size = 10 * 1024 * 1024 * 1024 +type = 'system' +sharable = False +read_only = False diff --git a/tools/python/scripts/xapi.vifcfg.py b/tools/python/scripts/xapi.vifcfg.py new file mode 100644 index 0000000..f421224 --- /dev/null +++ b/tools/python/scripts/xapi.vifcfg.py @@ -0,0 +1,10 @@ +# +# Virtual Network Interface Configuration for the Xen API +# + +name = '' +type = 'paravirtualised' +#device = 'eth0' # this is the dom0 device, not domU! +network = '' # ignored +MAC = '' +MTU = '1500' diff --git a/tools/python/scripts/xapi.vtpmcfg.py b/tools/python/scripts/xapi.vtpmcfg.py new file mode 100644 index 0000000..7c419ba --- /dev/null +++ b/tools/python/scripts/xapi.vtpmcfg.py @@ -0,0 +1,3 @@ +type = 'paravirtualised' +backend = 'Domain-0' +instance = 1 diff --git a/tools/python/setup.py b/tools/python/setup.py new file mode 100644 index 0000000..0afc168 --- /dev/null +++ b/tools/python/setup.py @@ -0,0 +1,95 @@ + +from distutils.core import setup, Extension +import os + +XEN_ROOT = "../.." + +extra_compile_args = [ "-fno-strict-aliasing", "-Werror" ] + +include_dirs = [ XEN_ROOT + "/tools/libxc", + XEN_ROOT + "/tools/xenstore", + XEN_ROOT + "/tools/include", + ] + +library_dirs = [ XEN_ROOT + "/tools/libxc", + XEN_ROOT + "/tools/xenstore", + ] + +libraries = [ "xenctrl", "xenguest", "xenstore" ] + +xc = Extension("xc", + extra_compile_args = extra_compile_args, + include_dirs = include_dirs + [ "xen/lowlevel/xc" ], + library_dirs = library_dirs, + libraries = libraries, + sources = [ "xen/lowlevel/xc/xc.c" ]) + +xs = Extension("xs", + extra_compile_args = extra_compile_args, + include_dirs = include_dirs + [ "xen/lowlevel/xs" ], + library_dirs = library_dirs, + libraries = libraries, + sources = [ "xen/lowlevel/xs/xs.c" ]) + +scf = Extension("scf", + extra_compile_args = extra_compile_args, + include_dirs = include_dirs + [ "xen/lowlevel/scf" ], + library_dirs = library_dirs, + libraries = libraries, + sources = [ "xen/lowlevel/scf/scf.c" ]) + +acm = Extension("acm", + extra_compile_args = extra_compile_args, + include_dirs = include_dirs + [ "xen/lowlevel/acm" ], + library_dirs = library_dirs, + libraries = libraries, + sources = [ "xen/lowlevel/acm/acm.c" ]) + +flask = Extension("flask", + extra_compile_args = extra_compile_args, + include_dirs = include_dirs + [ "xen/lowlevel/flask" ] + + [ "../flask/libflask/include" ], + library_dirs = library_dirs + [ "../flask/libflask" ], + libraries = libraries + [ "flask" ], + sources = [ "xen/lowlevel/flask/flask.c" ]) + +ptsname = Extension("ptsname", + extra_compile_args = extra_compile_args, + include_dirs = include_dirs + [ "ptsname" ], + library_dirs = library_dirs, + libraries = libraries, + sources = [ "ptsname/ptsname.c" ]) + +modules = [ xc, xs, ptsname, acm, flask ] +if os.uname()[0] == 'SunOS': + modules.append(scf) + +setup(name = 'xen', + version = '3.0', + description = 'Xen', + packages = ['xen', + 'xen.lowlevel', + 'xen.util', + 'xen.util.xsm', + 'xen.util.xsm.dummy', + 'xen.util.xsm.flask', + 'xen.util.xsm.acm', + 'xen.xend', + 'xen.xend.server', + 'xen.xend.xenstore', + 'xen.xm', + 'xen.web', + 'xen.sv', + 'xen.xsview', + + 'xen.xend.tests', + 'xen.xend.server.tests', + 'xen.xend.xenstore.tests', + 'xen.xm.tests' + ], + ext_package = "xen.lowlevel", + ext_modules = modules + ) + +os.chdir('logging') +execfile('setup.py') diff --git a/tools/python/test.py b/tools/python/test.py new file mode 100644 index 0000000..13912f6 --- /dev/null +++ b/tools/python/test.py @@ -0,0 +1,1094 @@ +#! /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() diff --git a/tools/python/xen/__init__.py b/tools/python/xen/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/lowlevel/__init__.py b/tools/python/xen/lowlevel/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/lowlevel/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/lowlevel/acm/acm.c b/tools/python/xen/lowlevel/acm/acm.c new file mode 100644 index 0000000..662c7f7 --- /dev/null +++ b/tools/python/xen/lowlevel/acm/acm.c @@ -0,0 +1,364 @@ +/**************************************************************** + * acm.c + * + * Copyright (C) 2006,2007 IBM Corporation + * + * Authors: + * Reiner Sailer + * Stefan Berger + * + * 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. + * + * ACM low-level code that allows Python control code to leverage + * the ACM hypercall interface to retrieve real-time information + * from the Xen hypervisor security module. + * + * indent -i4 -kr -nut + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PERROR(_m, _a...) \ +fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \ + errno, strerror(errno)) + +static PyObject *acm_error_obj; + +/* generic shared function */ +static void *__getssid(int domid, uint32_t *buflen) +{ + struct acm_getssid getssid; + int xc_handle; + #define SSID_BUFFER_SIZE 4096 + void *buf = NULL; + + if ((xc_handle = xc_interface_open()) < 0) { + goto out1; + } + if ((buf = malloc(SSID_BUFFER_SIZE)) == NULL) { + PERROR("acm.policytype: Could not allocate ssid buffer!\n"); + goto out2; + } + memset(buf, 0, SSID_BUFFER_SIZE); + set_xen_guest_handle(getssid.ssidbuf, buf); + getssid.ssidbuf_size = SSID_BUFFER_SIZE; + getssid.get_ssid_by = ACM_GETBY_domainid; + getssid.id.domainid = domid; + + if (xc_acm_op(xc_handle, ACMOP_getssid, &getssid, sizeof(getssid)) < 0) { + if (errno == EACCES) + PERROR("ACM operation failed."); + free(buf); + buf = NULL; + goto out2; + } else { + *buflen = SSID_BUFFER_SIZE; + goto out2; + } + out2: + xc_interface_close(xc_handle); + out1: + return buf; +} + + +/* retrieve the policytype indirectly by retrieving the + * ssidref for domain 0 (always exists) */ +static PyObject *policy(PyObject * self, PyObject * args) +{ + /* out */ + char *policyreference; + PyObject *ret; + void *ssid_buffer; + uint32_t buf_len; + + if (!PyArg_ParseTuple(args, "", NULL)) { + return NULL; + } + ssid_buffer = __getssid(0, &buf_len); + if (ssid_buffer == NULL || buf_len < sizeof(struct acm_ssid_buffer)) { + free(ssid_buffer); + return PyErr_SetFromErrno(acm_error_obj); + } + else { + struct acm_ssid_buffer *ssid = (struct acm_ssid_buffer *)ssid_buffer; + policyreference = (char *)(ssid_buffer + ssid->policy_reference_offset + + sizeof (struct acm_policy_reference_buffer)); + ret = Py_BuildValue("s", policyreference); + free(ssid_buffer); + return ret; + } +} + + +/* retrieve ssid info for a domain domid*/ +static PyObject *getssid(PyObject * self, PyObject * args) +{ + /* in */ + uint32_t domid; + /* out */ + char *policytype, *policyreference; + uint32_t ssidref; + + void *ssid_buffer; + uint32_t buf_len; + + if (!PyArg_ParseTuple(args, "i", &domid)) { + return NULL; + } + ssid_buffer = __getssid(domid, &buf_len); + if (ssid_buffer == NULL) { + return NULL; + } else if (buf_len < sizeof(struct acm_ssid_buffer)) { + free(ssid_buffer); + return NULL; + } else { + struct acm_ssid_buffer *ssid = (struct acm_ssid_buffer *) ssid_buffer; + policytype = ACM_POLICY_NAME(ssid->secondary_policy_code << 4 | + ssid->primary_policy_code); + ssidref = ssid->ssidref; + policyreference = (char *)(ssid_buffer + ssid->policy_reference_offset + + sizeof (struct acm_policy_reference_buffer)); + } + free(ssid_buffer); + return Py_BuildValue("{s:s,s:s,s:i}", + "policyreference", policyreference, + "policytype", policytype, + "ssidref", ssidref); +} + + +/* retrieve access decision based on domain ids or ssidrefs */ +static PyObject *getdecision(PyObject * self, PyObject * args) +{ + char *arg1_name, *arg1, *arg2_name, *arg2, *decision = NULL; + struct acm_getdecision getdecision; + int xc_handle, rc; + uint32_t hooktype; + + if (!PyArg_ParseTuple(args, "ssssi", &arg1_name, + &arg1, &arg2_name, &arg2, &hooktype)) { + return NULL; + } + + if ((xc_handle = xc_interface_open()) <= 0) { + PERROR("Could not open xen privcmd device!\n"); + return NULL; + } + + if ((strcmp(arg1_name, "domid") && strcmp(arg1_name, "ssidref")) || + (strcmp(arg2_name, "domid") && strcmp(arg2_name, "ssidref"))) + return NULL; + + getdecision.hook = hooktype; + if (!strcmp(arg1_name, "domid")) { + getdecision.get_decision_by1 = ACM_GETBY_domainid; + getdecision.id1.domainid = atoi(arg1); + } else { + getdecision.get_decision_by1 = ACM_GETBY_ssidref; + getdecision.id1.ssidref = atol(arg1); + } + if (!strcmp(arg2_name, "domid")) { + getdecision.get_decision_by2 = ACM_GETBY_domainid; + getdecision.id2.domainid = atoi(arg2); + } else { + getdecision.get_decision_by2 = ACM_GETBY_ssidref; + getdecision.id2.ssidref = atol(arg2); + } + + rc = xc_acm_op(xc_handle, ACMOP_getdecision, + &getdecision, sizeof(getdecision)); + + xc_interface_close(xc_handle); + + if (rc < 0) { + if (errno == EACCES) + PERROR("ACM operation failed."); + return NULL; + } + + if (getdecision.acm_decision == ACM_ACCESS_PERMITTED) + decision = "PERMITTED"; + else if (getdecision.acm_decision == ACM_ACCESS_DENIED) + decision = "DENIED"; + + return Py_BuildValue("s", decision); +} + +/* error messages for exceptions */ +const char bad_arg[] = "Bad function argument."; +const char ctrlif_op[] = "Could not open control interface."; +const char hv_op_err[] = "Error from hypervisor operation."; + + +static PyObject *chgpolicy(PyObject *self, PyObject *args) +{ + struct acm_change_policy chgpolicy; + int xc_handle, rc; + char *bin_pol = NULL, *del_arr = NULL, *chg_arr = NULL; + int bin_pol_len = 0, del_arr_len = 0, chg_arr_len = 0; + uint errarray_mbrs = 20 * 2; + uint32_t error_array[errarray_mbrs]; + PyObject *result; + uint len; + + memset(&chgpolicy, 0x0, sizeof(chgpolicy)); + + if (!PyArg_ParseTuple(args, "s#s#s#" ,&bin_pol, &bin_pol_len, + &del_arr, &del_arr_len, + &chg_arr, &chg_arr_len)) { + PyErr_SetString(PyExc_TypeError, bad_arg); + return NULL; + } + + chgpolicy.policy_pushcache_size = bin_pol_len; + chgpolicy.delarray_size = del_arr_len; + chgpolicy.chgarray_size = chg_arr_len; + chgpolicy.errarray_size = sizeof(error_array); + + set_xen_guest_handle(chgpolicy.policy_pushcache, bin_pol); + set_xen_guest_handle(chgpolicy.del_array, del_arr); + set_xen_guest_handle(chgpolicy.chg_array, chg_arr); + set_xen_guest_handle(chgpolicy.err_array, error_array); + + if ((xc_handle = xc_interface_open()) <= 0) { + PyErr_SetString(PyExc_IOError, ctrlif_op); + return NULL; + } + + rc = xc_acm_op(xc_handle, ACMOP_chgpolicy, &chgpolicy, sizeof(chgpolicy)); + + xc_interface_close(xc_handle); + + /* only pass the filled error codes */ + for (len = 0; (len + 1) < errarray_mbrs; len += 2) { + if (error_array[len] == 0) { + len *= sizeof(error_array[0]); + break; + } + } + + result = Py_BuildValue("is#", rc, error_array, len); + return result; +} + + +static PyObject *getpolicy(PyObject *self, PyObject *args) +{ + struct acm_getpolicy getpolicy; + int xc_handle, rc; + uint8_t pull_buffer[8192]; + PyObject *result; + uint32_t len = sizeof(pull_buffer); + + memset(&getpolicy, 0x0, sizeof(getpolicy)); + set_xen_guest_handle(getpolicy.pullcache, pull_buffer); + getpolicy.pullcache_size = sizeof(pull_buffer); + + if ((xc_handle = xc_interface_open()) <= 0) { + PyErr_SetString(PyExc_IOError, ctrlif_op); + return NULL; + } + + rc = xc_acm_op(xc_handle, ACMOP_getpolicy, &getpolicy, sizeof(getpolicy)); + + xc_interface_close(xc_handle); + + if (rc == 0) { + struct acm_policy_buffer *header = + (struct acm_policy_buffer *)pull_buffer; + if (ntohl(header->len) < sizeof(pull_buffer)) + len = ntohl(header->len); + } else { + len = 0; + } + + result = Py_BuildValue("is#", rc, pull_buffer, len); + return result; +} + + +static PyObject *relabel_domains(PyObject *self, PyObject *args) +{ + struct acm_relabel_doms reldoms; + int xc_handle, rc; + char *relabel_rules = NULL; + int rel_rules_len = 0; + uint errarray_mbrs = 20 * 2; + uint32_t error_array[errarray_mbrs]; + PyObject *result; + uint len; + + memset(&reldoms, 0x0, sizeof(reldoms)); + + if (!PyArg_ParseTuple(args, "s#" ,&relabel_rules, &rel_rules_len)) { + PyErr_SetString(PyExc_TypeError, bad_arg); + return NULL; + } + + reldoms.relabel_map_size = rel_rules_len; + reldoms.errarray_size = sizeof(error_array); + + set_xen_guest_handle(reldoms.relabel_map, relabel_rules); + set_xen_guest_handle(reldoms.err_array, error_array); + + if ((xc_handle = xc_interface_open()) <= 0) { + PyErr_SetString(PyExc_IOError, ctrlif_op); + return NULL; + } + + rc = xc_acm_op(xc_handle, ACMOP_relabeldoms, &reldoms, sizeof(reldoms)); + + xc_interface_close(xc_handle); + + + /* only pass the filled error codes */ + for (len = 0; (len + 1) < errarray_mbrs; len += 2) { + if (error_array[len] == 0) { + len *= sizeof(error_array[0]); + break; + } + } + + result = Py_BuildValue("is#", rc, error_array, len); + return result; +} + + +/*=================General Python Extension Declarations=================*/ + +/* methods */ +static PyMethodDef acmMethods[] = { + {"policy", policy, METH_VARARGS, "Retrieve Active ACM Policy Reference Name"}, + {"getssid", getssid, METH_VARARGS, "Retrieve label information and ssidref for a domain"}, + {"getdecision", getdecision, METH_VARARGS, "Retrieve ACM access control decision"}, + {"chgpolicy", chgpolicy, METH_VARARGS, "Change the policy in one step"}, + {"getpolicy", getpolicy, METH_NOARGS , "Get the binary policy from the hypervisor"}, + {"relabel_domains", relabel_domains, METH_VARARGS, "Relabel domains"}, + /* end of list (extend list above this line) */ + {NULL, NULL, 0, NULL} +}; + +/* inits */ +PyMODINIT_FUNC initacm(void) +{ + PyObject *m = Py_InitModule("acm", acmMethods); + acm_error_obj = PyErr_NewException("acm.Error", PyExc_RuntimeError, NULL); + Py_INCREF(acm_error_obj); + PyModule_AddObject(m, "Error", acm_error_obj); +} diff --git a/tools/python/xen/lowlevel/flask/flask.c b/tools/python/xen/lowlevel/flask/flask.c new file mode 100644 index 0000000..b838761 --- /dev/null +++ b/tools/python/xen/lowlevel/flask/flask.c @@ -0,0 +1,138 @@ +/****************************************************************************** + * flask.c + * + * Authors: George Coker, + * Michael LeMay, + * + * + * 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 + +#define PKG "xen.lowlevel.flask" +#define CLS "flask" + +#define CTX_LEN 1024 + +static PyObject *xc_error_obj; + +typedef struct { + PyObject_HEAD; + int xc_handle; +} XcObject; + +static PyObject *pyflask_context_to_sid(PyObject *self, PyObject *args, + PyObject *kwds) +{ + int xc_handle; + char *ctx; + char *buf; + uint32_t len; + uint32_t sid; + int ret; + + static char *kwd_list[] = { "context", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "s", kwd_list, + &ctx) ) + return NULL; + + len = strlen(ctx); + + buf = malloc(len); + if (!buf) { + errno = -ENOMEM; + PyErr_SetFromErrno(xc_error_obj); + } + + memcpy(buf, ctx, len); + + xc_handle = xc_interface_open(); + if (xc_handle < 0) { + errno = xc_handle; + return PyErr_SetFromErrno(xc_error_obj); + } + + ret = flask_context_to_sid(xc_handle, buf, len, &sid); + + xc_interface_close(xc_handle); + + free(buf); + + if ( ret != 0 ) { + errno = -ret; + return PyErr_SetFromErrno(xc_error_obj); + } + + return PyInt_FromLong(sid); +} + +static PyObject *pyflask_sid_to_context(PyObject *self, PyObject *args, + PyObject *kwds) +{ + int xc_handle; + uint32_t sid; + char ctx[CTX_LEN]; + uint32_t ctx_len = 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(); + if (xc_handle < 0) { + errno = xc_handle; + return PyErr_SetFromErrno(xc_error_obj); + } + + ret = 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 PyMethodDef pyflask_methods[] = { + { "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" }, + + { NULL, NULL, 0, NULL } +}; + +PyMODINIT_FUNC initflask(void) +{ + Py_InitModule("flask", pyflask_methods); +} + + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/tools/python/xen/lowlevel/scf/scf.c b/tools/python/xen/lowlevel/scf/scf.c new file mode 100644 index 0000000..22a7960 --- /dev/null +++ b/tools/python/xen/lowlevel/scf/scf.c @@ -0,0 +1,156 @@ +/* + * 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 + +#define XEND_FMRI "svc:/system/xvm/xend:default" +#define XEND_PG "config" + +static PyObject *scf_exc; + +static void * +scf_exception(const char *err, const char *value) +{ + int scferr = scf_error(); + const char *scfstrerr = scf_strerror(scferr); + PyObject *obj = Py_BuildValue("(isss)", scferr, err, scfstrerr, value); + PyErr_SetObject(scf_exc, obj); + return (NULL); +} + +static PyObject * +pyscf_get_bool(PyObject *o, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", NULL }; + scf_simple_prop_t *prop; + uint8_t *val; + char *name; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) + return (NULL); + + prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name); + + if (prop == NULL) + return (scf_exception("scf_simple_prop_get() failed", name)); + + if ((val = scf_simple_prop_next_boolean(prop)) == NULL) + return (scf_exception("scf_simple_prop_next_boolean() failed", + name)); + + if (*val) { + scf_simple_prop_free(prop); + Py_INCREF(Py_True); + return (Py_True); + } + + scf_simple_prop_free(prop); + Py_INCREF(Py_False); + return (Py_False); +} + +static PyObject * +pyscf_get_int(PyObject *o, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", NULL }; + scf_simple_prop_t *prop; + PyObject *obj; + int64_t *val; + char *name; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) + return (NULL); + + prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name); + + if (prop == NULL) + return (scf_exception("scf_simple_prop_get() failed", name)); + + if ((val = scf_simple_prop_next_integer(prop)) == NULL) + return (scf_exception("scf_simple_prop_next_integer() failed", + name)); + + obj = PyInt_FromLong((long)*val); + scf_simple_prop_free(prop); + return (obj); +} + +static PyObject * +pyscf_get_string(PyObject *o, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", NULL }; + scf_simple_prop_t *prop; + PyObject *obj; + char *name; + char *str; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) + return (NULL); + + prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name); + + if (prop == NULL) + return (scf_exception("scf_simple_prop_get() failed", name)); + + if ((str = scf_simple_prop_next_astring(prop)) == NULL) { + scf_simple_prop_free(prop); + return (scf_exception("scf_simple_prop_next_astring() failed", + name)); + } + + obj = PyString_FromString(str); + scf_simple_prop_free(prop); + return (obj); +} + +PyDoc_STRVAR(pyscf_get_bool__doc__, + "get_bool(name) - get the value of the named boolean property"); +PyDoc_STRVAR(pyscf_get_int__doc__, + "get_int(name) - get the value of the named integer property"); +PyDoc_STRVAR(pyscf_get_string__doc__, + "get_string(name) - get the value of the named string property"); + +static struct PyMethodDef pyscf_module_methods[] = { + { "get_bool", (PyCFunction) pyscf_get_bool, + METH_VARARGS|METH_KEYWORDS, pyscf_get_bool__doc__ }, + { "get_int", (PyCFunction) pyscf_get_int, + METH_VARARGS|METH_KEYWORDS, pyscf_get_int__doc__ }, + { "get_string", (PyCFunction) pyscf_get_string, + METH_VARARGS|METH_KEYWORDS, pyscf_get_string__doc__ }, + { NULL, NULL, 0, NULL } +}; + +PyMODINIT_FUNC +initscf(void) +{ + PyObject *m; + m = Py_InitModule("scf", pyscf_module_methods); + + scf_exc = PyErr_NewException("scf.error", NULL, NULL); + Py_INCREF(scf_exc); + PyModule_AddObject(m, "error", scf_exc); + PyModule_AddIntConstant(m, "SCF_ERROR_NOT_FOUND", SCF_ERROR_NOT_FOUND); +} diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c new file mode 100644 index 0000000..59a16b3 --- /dev/null +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -0,0 +1,2060 @@ +/****************************************************************************** + * Xc.c + * + * Copyright (c) 2003-2004, K A Fraser (University of Cambridge) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenctrl.h" +#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" + +static PyObject *xc_error_obj, *zero; + +typedef struct { + PyObject_HEAD; + int xc_handle; +} XcObject; + + +static PyObject *dom_op(XcObject *self, PyObject *args, + int (*fn)(int, uint32_t)); + +static PyObject *pyxc_error_to_exception(void) +{ + PyObject *pyerr; + const xc_error *err = xc_get_last_error(); + const char *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); + + xc_clear_last_error(); + + 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(); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_handle(XcObject *self) +{ + return PyInt_FromLong(self->xc_handle); +} + +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 ( !PyInt_Check(p) ) + goto out_exception; + handle[i] = (uint8_t)PyInt_AsLong(p); + } + } + + if ( (ret = xc_domain_create(self->xc_handle, ssidref, + handle, flags, &dom)) < 0 ) + return pyxc_error_to_exception(); + + if ( target ) + if ( (ret = xc_domain_set_target(self->xc_handle, dom, target)) < 0 ) + return pyxc_error_to_exception(); + + + return PyInt_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(); + + 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) +{ +#ifdef __ia64__ + dom_op(self, args, xc_ia64_save_to_nvram); +#endif + + 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(); + + 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(); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_vcpu_setaffinity(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t dom; + int vcpu = 0, i; + uint64_t cpumap = ~0ULL; + PyObject *cpulist = NULL; + + static char *kwd_list[] = { "domid", "vcpu", "cpumap", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|iO", kwd_list, + &dom, &vcpu, &cpulist) ) + return NULL; + + if ( (cpulist != NULL) && PyList_Check(cpulist) ) + { + cpumap = 0ULL; + for ( i = 0; i < PyList_Size(cpulist); i++ ) + { + long cpu = PyInt_AsLong(PyList_GetItem(cpulist, i)); + if ( cpu >= 64 ) + { + errno = EINVAL; + PyErr_SetFromErrno(xc_error_obj); + return NULL; + } + cpumap |= (uint64_t)1 << cpu; + } + } + + if ( xc_vcpu_setaffinity(self->xc_handle, dom, vcpu, cpumap) != 0 ) + return pyxc_error_to_exception(); + + Py_INCREF(zero); + 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 ( !PyInt_Check(p) ) + goto out_exception; + handle[i] = (uint8_t)PyInt_AsLong(p); + } + + if (xc_domain_sethandle(self->xc_handle, dom, handle) < 0) + return pyxc_error_to_exception(); + + 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(); + } + + 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}", + "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); + 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, PyInt_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; + uint64_t cpumap; + + static char *kwd_list[] = { "domid", "vcpu", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list, + &dom, &vcpu) ) + return NULL; + + rc = xc_vcpu_getinfo(self->xc_handle, dom, vcpu, &info); + if ( rc < 0 ) + return pyxc_error_to_exception(); + rc = xc_vcpu_getaffinity(self->xc_handle, dom, vcpu, &cpumap); + if ( rc < 0 ) + return pyxc_error_to_exception(); + + 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; cpumap != 0; i++ ) + { + if ( cpumap & 1 ) + PyList_Append(cpulist, PyInt_FromLong(i)); + cpumap >>= 1; + } + PyDict_SetItemString(info_dict, "cpumap", cpulist); + Py_DECREF(cpulist); + return info_dict; +} + +static PyObject *pyxc_linux_build(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t domid; + struct xc_dom_image *dom; + char *image, *ramdisk = NULL, *cmdline = "", *features = NULL; + int flags = 0; + int store_evtchn, console_evtchn; + int vhpt = 0; + unsigned int mem_mb; + unsigned long store_mfn = 0; + unsigned long console_mfn = 0; + PyObject* elfnote_dict; + PyObject* elfnote = NULL; + PyObject* ret; + int i; + + static char *kwd_list[] = { "domid", "store_evtchn", "memsize", + "console_evtchn", "image", + /* optional */ + "ramdisk", "cmdline", "flags", + "features", "vhpt", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiis|ssisi", kwd_list, + &domid, &store_evtchn, &mem_mb, + &console_evtchn, &image, + /* optional */ + &ramdisk, &cmdline, &flags, + &features, &vhpt) ) + return NULL; + + xc_dom_loginit(); + if (!(dom = xc_dom_allocate(cmdline, features))) + return pyxc_error_to_exception(); + + /* for IA64 */ + dom->vhpt_size_log2 = vhpt; + + if ( xc_dom_linux_build(self->xc_handle, dom, domid, mem_mb, image, + ramdisk, flags, store_evtchn, &store_mfn, + console_evtchn, &console_mfn) != 0 ) { + goto out; + } + + if ( !(elfnote_dict = PyDict_New()) ) + goto out; + + for ( i = 0; i < ARRAY_SIZE(dom->parms.elf_notes); i++ ) + { + switch ( dom->parms.elf_notes[i].type ) + { + case XEN_ENT_NONE: + continue; + case XEN_ENT_LONG: + elfnote = Py_BuildValue("k", dom->parms.elf_notes[i].data.num); + break; + case XEN_ENT_STR: + elfnote = Py_BuildValue("s", dom->parms.elf_notes[i].data.str); + break; + } + PyDict_SetItemString(elfnote_dict, + dom->parms.elf_notes[i].name, + elfnote); + Py_DECREF(elfnote); + } + + ret = Py_BuildValue("{s:i,s:i,s:N}", + "store_mfn", store_mfn, + "console_mfn", console_mfn, + "notes", elfnote_dict); + + if ( dom->arch_hooks->native_protocol ) + { + PyObject *native_protocol = + Py_BuildValue("s", dom->arch_hooks->native_protocol); + PyDict_SetItemString(ret, "native_protocol", native_protocol); + Py_DECREF(native_protocol); + } + + xc_dom_release(dom); + + return ret; + + out: + xc_dom_release(dom); + return pyxc_error_to_exception(); +} + +static PyObject *pyxc_get_hvm_param(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t dom; + int param; + unsigned long value; + + static char *kwd_list[] = { "domid", "param", NULL }; + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list, + &dom, ¶m) ) + return NULL; + + if ( xc_get_hvm_param(self->xc_handle, dom, param, &value) != 0 ) + return pyxc_error_to_exception(); + + return PyLong_FromUnsignedLong(value); + +} + +static PyObject *pyxc_set_hvm_param(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_set_hvm_param(self->xc_handle, dom, param, value) != 0 ) + return pyxc_error_to_exception(); + + 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 bdf = 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) ) + { + bdf |= (bus & 0xff) << 16; + bdf |= (dev & 0x1f) << 11; + bdf |= (func & 0x7) << 8; + + if ( xc_test_assign_device(self->xc_handle, dom, bdf) != 0 ) + { + if (errno == ENOSYS) + bdf = -1; + break; + } + bdf = 0; + } + + return Py_BuildValue("i", bdf); +} + +static PyObject *pyxc_assign_device(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t dom; + char *pci_str; + int32_t bdf = 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) ) + { + bdf |= (bus & 0xff) << 16; + bdf |= (dev & 0x1f) << 11; + bdf |= (func & 0x7) << 8; + + if ( xc_assign_device(self->xc_handle, dom, bdf) != 0 ) + { + if (errno == ENOSYS) + bdf = -1; + break; + } + bdf = 0; + } + + return Py_BuildValue("i", bdf); +} + +static PyObject *pyxc_deassign_device(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t dom; + char *pci_str; + int32_t bdf = 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) ) + { + bdf |= (bus & 0xff) << 16; + bdf |= (dev & 0x1f) << 11; + bdf |= (func & 0x7) << 8; + + if ( xc_deassign_device(self->xc_handle, dom, bdf) != 0 ) + { + if (errno == ENOSYS) + bdf = -1; + break; + } + bdf = 0; + } + + return Py_BuildValue("i", bdf); +} + +static PyObject *pyxc_get_device_group(XcObject *self, + PyObject *args) +{ + domid_t domid; + uint32_t bdf = 0; + uint32_t max_sdevs, num_sdevs; + int 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(); + + bdf |= (bus & 0xff) << 16; + bdf |= (dev & 0x1f) << 11; + bdf |= (func & 0x7) << 8; + + rc = xc_get_device_group(self->xc_handle, + domid, bdf, max_sdevs, &num_sdevs, sdev_array); + + if ( rc < 0 ) + { + free(sdev_array); + return pyxc_error_to_exception(); + } + + if ( !num_sdevs ) + { + free(sdev_array); + return Py_BuildValue("s", ""); + } + + group_str = calloc(num_sdevs, sizeof(dev_str)); + if (group_str == NULL) + 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; +} + +#ifdef __ia64__ +static PyObject *pyxc_nvram_init(XcObject *self, + PyObject *args) +{ + char *dom_name; + uint32_t dom; + + if ( !PyArg_ParseTuple(args, "si", &dom_name, &dom) ) + return NULL; + + xc_ia64_nvram_init(self->xc_handle, dom_name, dom); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_set_os_type(XcObject *self, + PyObject *args) +{ + char *os_type; + uint32_t dom; + + if ( !PyArg_ParseTuple(args, "si", &os_type, &dom) ) + return NULL; + + xc_ia64_set_os_type(self->xc_handle, os_type, dom); + + Py_INCREF(zero); + return zero; +} +#endif /* __ia64__ */ + + +#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] = PyString_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], + PyString_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(); + + return pyxc_create_cpuid_dict(regs_transform); +} + +static PyObject *pyxc_dom_set_policy_cpuid(XcObject *self, + PyObject *args) +{ + domid_t domid; + + if ( !PyArg_ParseTuple(args, "i", &domid) ) + return NULL; + + if ( xc_cpuid_apply_policy(self->xc_handle, domid) ) + return pyxc_error_to_exception(); + + Py_INCREF(zero); + return zero; +} + + +static PyObject *pyxc_dom_set_cpuid(XcObject *self, + PyObject *args) +{ + domid_t domid; + PyObject *sub_input, *config; + unsigned int 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(); + + 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(); + + 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(); + + Py_INCREF(zero); + return zero; +} +#endif /* __i386__ || __x86_64__ */ + +static PyObject *pyxc_hvm_build(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t dom; +#if !defined(__ia64__) + struct hvm_info_table *va_hvm; + uint8_t *va_map, sum; + int i; +#endif + char *image; + int memsize, vcpus = 1, acpi = 0, apic = 1; + + static char *kwd_list[] = { "domid", + "memsize", "image", "vcpus", "acpi", + "apic", NULL }; + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|iii", kwd_list, + &dom, &memsize, + &image, &vcpus, &acpi, &apic) ) + return NULL; + + if ( xc_hvm_build(self->xc_handle, dom, memsize, image) != 0 ) + return pyxc_error_to_exception(); + +#if !defined(__ia64__) + /* Set up the HVM info table. */ + va_map = xc_map_foreign_range(self->xc_handle, dom, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + HVM_INFO_PFN); + if ( va_map == NULL ) + return PyErr_SetFromErrno(xc_error_obj); + va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET); + memset(va_hvm, 0, sizeof(*va_hvm)); + strncpy(va_hvm->signature, "HVM INFO", 8); + va_hvm->length = sizeof(struct hvm_info_table); + va_hvm->acpi_enabled = acpi; + va_hvm->apic_mode = apic; + va_hvm->nr_vcpus = vcpus; + 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); +#endif + + return Py_BuildValue("{}"); +} + +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(); + + return PyInt_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(); + + 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(); + 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(); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_readconsolering(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + unsigned int clear = 0, index = 0, incremental = 0; + char _str[32768], *str = _str; + unsigned int count = 32768; + int ret; + + static char *kwd_list[] = { "clear", "index", "incremental", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list, + &clear, &index, &incremental) ) + return NULL; + + ret = xc_readconsolering(self->xc_handle, &str, &count, clear, + incremental, &index); + if ( ret < 0 ) + return pyxc_error_to_exception(); + + return PyString_FromStringAndSize(str, count); +} + + +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) +{ +#define MAX_CPU_ID 255 + xc_physinfo_t info; + char cpu_cap[128], virt_caps[128], *p; + int i, j, max_cpu_id; + uint64_t free_heap; + PyObject *ret_obj, *node_to_cpu_obj, *node_to_memory_obj; + xc_cpu_to_node_t map[MAX_CPU_ID + 1]; + const char *virtcap_names[] = { "hvm", "hvm_directio" }; + + set_xen_guest_handle(info.cpu_to_node, map); + info.max_cpu_id = MAX_CPU_ID; + + if ( xc_physinfo(self->xc_handle, &info) != 0 ) + return pyxc_error_to_exception(); + + p = cpu_cap; + *p = '\0'; + for ( i = 0; i < sizeof(info.hw_cap)/4; i++ ) + p += sprintf(p, "%08x:", info.hw_cap[i]); + *(p-1) = 0; + + p = virt_caps; + *p = '\0'; + for ( i = 0; i < 2; i++ ) + if ( (info.capabilities >> i) & 1 ) + p += sprintf(p, "%s ", virtcap_names[i]); + if ( p != virt_caps ) + *(p-1) = '\0'; + + ret_obj = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:l,s:l,s:l,s:i,s:s:s:s}", + "nr_nodes", info.nr_nodes, + "max_cpu_id", info.max_cpu_id, + "threads_per_core", info.threads_per_core, + "cores_per_socket", info.cores_per_socket, + "nr_cpus", info.nr_cpus, + "total_memory", pages_to_kib(info.total_pages), + "free_memory", pages_to_kib(info.free_pages), + "scrub_memory", pages_to_kib(info.scrub_pages), + "cpu_khz", info.cpu_khz, + "hw_caps", cpu_cap, + "virt_caps", virt_caps); + + max_cpu_id = info.max_cpu_id; + if ( max_cpu_id > MAX_CPU_ID ) + max_cpu_id = MAX_CPU_ID; + + /* Construct node-to-cpu lists. */ + node_to_cpu_obj = PyList_New(0); + + /* Make a list for each node. */ + for ( i = 0; i < info.nr_nodes; i++ ) + { + PyObject *cpus = PyList_New(0); + for ( j = 0; j <= max_cpu_id; j++ ) + if ( i == map[j]) + PyList_Append(cpus, PyInt_FromLong(j)); + PyList_Append(node_to_cpu_obj, cpus); + } + + node_to_memory_obj = PyList_New(0); + + for ( i = 0; i < info.nr_nodes; i++ ) + { + xc_availheap(self->xc_handle, 0, 0, i, &free_heap); + PyList_Append(node_to_memory_obj, + PyInt_FromLong(free_heap / 1024)); + } + + PyDict_SetItemString(ret_obj, "node_to_cpu", node_to_cpu_obj); + PyDict_SetItemString(ret_obj, "node_to_memory", node_to_memory_obj); + + return ret_obj; +#undef MAX_CPU_ID +} + +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; + 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(); + + if ( xc_version(self->xc_handle, XENVER_compile_info, &xen_cc) != 0 ) + return pyxc_error_to_exception(); + + if ( xc_version(self->xc_handle, XENVER_changeset, &xen_chgset) != 0 ) + return pyxc_error_to_exception(); + + if ( xc_version(self->xc_handle, XENVER_capabilities, &xen_caps) != 0 ) + return pyxc_error_to_exception(); + + if ( xc_version(self->xc_handle, XENVER_platform_parameters, &p_parms) != 0 ) + return pyxc_error_to_exception(); + + snprintf(str, sizeof(str), "virt_start=0x%lx", p_parms.virt_start); + + xen_pagesize = xc_version(self->xc_handle, XENVER_pagesize, NULL); + if (xen_pagesize < 0 ) + return pyxc_error_to_exception(); + + 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}", + "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, + "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_sedf_domain_set(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + uint32_t domid; + uint64_t period, slice, latency; + uint16_t extratime, weight; + static char *kwd_list[] = { "domid", "period", "slice", + "latency", "extratime", "weight",NULL }; + + if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLLLhh", kwd_list, + &domid, &period, &slice, + &latency, &extratime, &weight) ) + return NULL; + if ( xc_sedf_domain_set(self->xc_handle, domid, period, + slice, latency, extratime,weight) != 0 ) + return pyxc_error_to_exception(); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_sedf_domain_get(XcObject *self, PyObject *args) +{ + uint32_t domid; + uint64_t period, slice,latency; + uint16_t weight, extratime; + + if(!PyArg_ParseTuple(args, "i", &domid)) + return NULL; + + if (xc_sedf_domain_get(self->xc_handle, domid, &period, + &slice,&latency,&extratime,&weight)) + return pyxc_error_to_exception(); + + return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i,s:i}", + "domid", domid, + "period", period, + "slice", slice, + "latency", latency, + "extratime", extratime, + "weight", weight); +} + +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(); + + 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(); + + 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(); + + 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(); + + return Py_BuildValue("{s:H,s:H}", + "weight", sdom.weight, + "cap", sdom.cap); +} + +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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + Py_INCREF(zero); + return zero; +} + +static PyObject *dom_op(XcObject *self, PyObject *args, + int (*fn)(int, 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(); + + Py_INCREF(zero); + return zero; +} + +static PyMethodDef pyxc_methods[] = { + { "handle", + (PyCFunction)pyxc_handle, + METH_NOARGS, "\n" + "Query the xc control interface file descriptor.\n\n" + "Returns: [int] file descriptor\n" }, + + { "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" }, + + { "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" }, + + { "linux_build", + (PyCFunction)pyxc_linux_build, + METH_VARARGS | METH_KEYWORDS, "\n" + "Build a new Linux guest OS.\n" + " dom [int]: Identifier of domain to build into.\n" + " image [str]: Name of kernel image file. May be gzipped.\n" + " ramdisk [str, n/a]: Name of ramdisk file, if any.\n" + " cmdline [str, n/a]: Kernel parameters, if any.\n\n" + " vcpus [int, 1]: Number of Virtual CPUS in domain.\n\n" + "Returns: [int] 0 on success; -1 on error.\n" }, + + { "hvm_build", + (PyCFunction)pyxc_hvm_build, + METH_VARARGS | METH_KEYWORDS, "\n" + "Build a new HVM guest OS.\n" + " dom [int]: Identifier of domain to build into.\n" + " image [str]: Name of HVM loader image file.\n" + " vcpus [int, 1]: Number of Virtual CPUS in domain.\n\n" + "Returns: [int] 0 on success; -1 on error.\n" }, + + { "hvm_get_param", + (PyCFunction)pyxc_get_hvm_param, + 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_set_hvm_param, + 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" }, + + { "sedf_domain_set", + (PyCFunction)pyxc_sedf_domain_set, + METH_KEYWORDS, "\n" + "Set the scheduling parameters for a domain when running with Atropos.\n" + " dom [int]: domain to set\n" + " period [long]: domain's scheduling period\n" + " slice [long]: domain's slice per period\n" + " latency [long]: domain's wakeup latency hint\n" + " extratime [int]: domain aware of extratime?\n" + "Returns: [int] 0 on success; -1 on error.\n" }, + + { "sedf_domain_get", + (PyCFunction)pyxc_sedf_domain_get, + METH_VARARGS, "\n" + "Get the current scheduling parameters for a domain when running with\n" + "the Atropos scheduler." + " dom [int]: domain to query\n" + "Returns: [dict]\n" + " domain [int]: domain ID\n" + " period [long]: scheduler period\n" + " slice [long]: CPU reservation per period\n" + " latency [long]: domain's wakeup latency hint\n" + " extratime [int]: domain aware of extratime?\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"}, + + { "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" }, + + { "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_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" }, + +#ifdef __ia64__ + { "nvram_init", + (PyCFunction)pyxc_nvram_init, + METH_VARARGS, "\n" + "Init nvram in IA64 platform\n" + "Returns: [int] 0 on success; -1 on error.\n" }, + { "set_os_type", + (PyCFunction)pyxc_set_os_type, + METH_VARARGS, "\n" + "Set guest OS type on IA64 platform\n" + "Returns: [int] 0 on success; -1 on error.\n" }, +#endif /* __ia64__ */ + { "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_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 + + { NULL, NULL, 0, NULL } +}; + + +static PyObject *PyXc_getattr(PyObject *obj, char *name) +{ + return Py_FindMethod(pyxc_methods, obj, name); +} + +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 = -1; + + return (PyObject *)self; +} + +static int +PyXc_init(XcObject *self, PyObject *args, PyObject *kwds) +{ + if ((self->xc_handle = xc_interface_open()) == -1) { + pyxc_error_to_exception(); + return -1; + } + + return 0; +} + +static void PyXc_dealloc(XcObject *self) +{ + if (self->xc_handle != -1) { + xc_interface_close(self->xc_handle); + self->xc_handle = -1; + } + + self->ob_type->tp_free((PyObject *)self); +} + +static PyTypeObject PyXcType = { + PyObject_HEAD_INIT(NULL) + 0, + PKG "." CLS, + sizeof(XcObject), + 0, + (destructor)PyXc_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + PyXc_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL, /* tp_hash */ + NULL, /* tp_call */ + NULL, /* tp_str */ + NULL, /* tp_getattro */ + NULL, /* tp_setattro */ + NULL, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Xen client connections", /* tp_doc */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + pyxc_methods, /* tp_methods */ + NULL, /* tp_members */ + NULL, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyXc_init, /* tp_init */ + NULL, /* tp_alloc */ + PyXc_new, /* tp_new */ +}; + +static PyMethodDef xc_methods[] = { { NULL } }; + +PyMODINIT_FUNC initxc(void) +{ + PyObject *m; + + if (PyType_Ready(&PyXcType) < 0) + return; + + m = Py_InitModule(PKG, xc_methods); + + if (m == NULL) + return; + + xc_error_obj = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); + zero = PyInt_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_SEDF", XEN_SCHEDULER_SEDF); + PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT", XEN_SCHEDULER_CREDIT); + +} + + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/tools/python/xen/lowlevel/xs/xs.c b/tools/python/xen/lowlevel/xs/xs.c new file mode 100644 index 0000000..6497126 --- /dev/null +++ b/tools/python/xen/lowlevel/xs/xs.c @@ -0,0 +1,989 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 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 +#include "xs.h" + +/** @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" + +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 = PyString_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++) + PyList_SetItem(val, i, PyString_FromString(xsval[i])); + free(xsval); + return val; + } + else { + return none(errno == ENOENT); + } +} + + +#define xspy_mkdir_doc "\n" \ + "Make a directory.\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; + + 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); + xsperms = calloc(xsperms_n, 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; + } + 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; + } + + free(xsperms); + Py_INCREF(Py_None); + return Py_None; + + exit: + Py_XDECREF(tuple0); + free(xsperms); + return NULL; +} + +#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); + return PyString_FromString(thstr); +} + +#define xspy_transaction_end_doc "\n" \ + "End the current transaction.\n" \ + "Attempts to commit the transaction unless abort is true.\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) { + PyObject *val = PyString_FromString(xsval); + 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_getattr(PyObject *self, char *name) +{ + return Py_FindMethod(xshandle_methods, self, name); +} + +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); + + self->ob_type->tp_free((PyObject *)self); +} + +static PyTypeObject xshandle_type = { + PyObject_HEAD_INIT(NULL) + 0, + PKG "." CLS, + sizeof(XsHandle), + 0, + (destructor)xshandle_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xshandle_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL, /* tp_hash */ + NULL, /* tp_call */ + NULL, /* tp_str */ + NULL, /* tp_getattro */ + NULL, /* tp_setattro */ + NULL, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Xenstore connections", /* tp_doc */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + xshandle_methods, /* tp_methods */ + NULL, /* tp_members */ + NULL, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)xshandle_init, /* tp_init */ + NULL, /* tp_alloc */ + xshandle_new, /* tp_new */ +}; + +static PyMethodDef xs_methods[] = { { NULL } }; + +PyMODINIT_FUNC initxs(void) +{ + PyObject* m; + + if (PyType_Ready(&xshandle_type) < 0) + return; + + m = Py_InitModule(PKG, xs_methods); + + if (m == NULL) + return; + + xs_error = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); + + Py_INCREF(&xshandle_type); + PyModule_AddObject(m, CLS, (PyObject *)&xshandle_type); + + Py_INCREF(xs_error); + PyModule_AddObject(m, "Error", xs_error); +} + + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/tools/python/xen/sv/CreateDomain.py b/tools/python/xen/sv/CreateDomain.py new file mode 100755 index 0000000..748d99b --- /dev/null +++ b/tools/python/xen/sv/CreateDomain.py @@ -0,0 +1,205 @@ +from xen.sv.Wizard import * +from xen.sv.util import * +from xen.sv.GenTabbed import PreTab + +from xen.xm.create import make_config, OptVals + +from xen.xend.XendClient import server + +class CreateDomain( Wizard ): + def __init__( self, urlWriter ): + + sheets = [ CreatePage0, + CreatePage1, + CreatePage2, + CreatePage3, + CreatePage4, + CreateFinish ] + + Wizard.__init__( self, urlWriter, "Create Domain", sheets ) + + def op_finish( self, request ): + pass + +class CreatePage0( Sheet ): + + title = "General" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "General", 0 ) + self.addControl( InputControl( 'name', 'VM Name', 'VM Name:', "[\\w|\\S]+", "You must enter a name in this field" ) ) + self.addControl( InputControl( 'memory', '64', 'Memory (Mb):', "[\\d]+", "You must enter a number in this field" ) ) + self.addControl( InputControl( 'cpu', '0', 'CPU:', "[\\d]+", "You must enter a number in this feild" ) ) + self.addControl( InputControl( 'cpu_weight', '1', 'CPU Weight:', "[\\d]+", "You must enter a number in this feild" ) ) + self.addControl( InputControl( 'vcpus', '1', 'Virtual CPUs:', '[\\d]+', "You must enter a number in this feild") ) + +class CreatePage1( Sheet ): + + title = "Setup Kernel Image" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Setup Kernel Image", 1 ) + self.addControl( ListControl( 'builder', [('linux', 'Linux'), ('netbsd', 'NetBSD')], 'Domain Builder:' ) ) + self.addControl( FileControl( 'kernel', '/boot/vmlinuz-2.6.12-xenU', 'Kernel Image:' ) ) + self.addControl( InputControl( 'extra', '', 'Kernel Command Line Parameters:' ) ) + self.addControl( ListControl( 'use-initrd', [('yes', 'Yes'), ('no', 'No')], 'Use an Initial Ram Disk?:' ) ) + self.addControl( FileControl( 'initrd', '/boot/initrd-2.6.12-xenU.img', 'Initial Ram Disk:' ) ) + + def validate( self, request ): + if not self.passback: self.parseForm( request ) + check = True + request.write( previous_values.get( '>>>>>use-initrd' ) ) + previous_values = ssxp2hash( string2sxp( self.passback ) ) #get the map for quick reference + if DEBUG: print previous_values + for (feild, control) in self.feilds: + if feild == 'initrd' and previous_values.get( 'use-initrd' ) != 'no': + request.write( previous_values.get( '>>>>>use-initrd' ) ) + if control.validate( previous_values.get( feild ) ): + check = False + elif not control.validate( previous_values.get( feild ) ): + check = False + + if DEBUG: print "> %s = %s" % (feild, previous_values.get( feild )) + + return check + + +class CreatePage2( Sheet ): + + title = "Choose number of VBDS" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Setup Virtual Block Device", 2 ) + self.addControl( InputControl( 'num_vbds', '1', 'Number of VBDs:', '[\\d]+', "You must enter a number in this field" ) ) + +class CreatePage3( Sheet ): + + title = "Setup VBDS" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Setup Virtual Block Device", 3 ) + + def write_BODY( self, request ): + if not self.passback: self.parseForm( request ) + + previous_values = sxp2hash( string2sxp( self.passback ) ) #get the hash for quick reference + + num_vbds = previous_values.get( 'num_vbds' ) + + for i in range( int( num_vbds ) ): + self.addControl( InputControl( 'vbd%s_dom0' % i, 'phy:sda%s' % str(i + 1), 'Device %s name:' % i ) ) + self.addControl( InputControl( 'vbd%s_domU' % i, 'sda%s' % str(i + 1), 'Virtualized device %s:' % i ) ) + self.addControl( ListControl( 'vbd%s_mode' % i, [('w', 'Read + Write'), ('r', 'Read Only')], 'Device %s mode:' % i ) ) + + self.addControl( InputControl( 'root', '/dev/sda1', 'Root device (in VM):' ) ) + + Sheet.write_BODY( self, request ) + +class CreatePage4( Sheet ): + + title = "Network Setting" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Network settings", 4 ) + self.addControl( ListControl( 'dhcp', [('off', 'No'), ('dhcp', 'Yes')], 'Use DHCP:' ) ) + self.addControl( InputControl( 'hostname', 'hostname', 'VM Hostname:' ) ) + self.addControl( InputControl( 'ip_addr', '192.168.1.1', 'VM IP Address:' ) ) + self.addControl( InputControl( 'ip_subnet', '255.255.255.0', 'VM Subnet Mask:' ) ) + self.addControl( InputControl( 'ip_gateway', '192.168.1.1', 'VM Gateway:' ) ) + self.addControl( InputControl( 'ip_nfs', '192.168.1.1', 'NFS Server:' ) ) + +class CreateFinish( Sheet ): + + title = "Finish" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "All Done", 5 ) + + def write_BODY( self, request ): + + if not self.passback: self.parseForm( request ) + + xend_sxp = self.translate_sxp( string2sxp( self.passback ) ) + + request.write( "
%s
" % sxp2prettystring( xend_sxp ) ) + + try: + server.xend_domain_create( xend_sxp ) + request.write( "

You domain had been successfully created.

" ) + except Exception, e: + request.write( "

There was an error creating your domain.
The configuration used is as follows:\n

" ) + request.write( "
%s
" % sxp2prettystring( xend_sxp ) ) + request.write( "

The error was:

" ) + request.write( "
%s
" % str( e ) ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) + + def translate_sxp( self, fin_sxp ): + fin_hash = ssxp2hash( fin_sxp ) + + def get( key ): + ret = fin_hash.get( key ) + if ret: + return ret + else: + return "" + + vals = OptVals() + + vals.name = get( 'name' ) + vals.memory = get( 'memory' ) + vals.maxmem = get( 'maxmem' ) + vals.cpu = get( 'cpu' ) + vals.cpu_weight = get( 'cpu_weight' ) + vals.vcpus = get( 'vcpus' ) + + vals.builder = get( 'builder' ) + vals.kernel = get( 'kernel' ) + vals.root = get( 'root' ) + vals.extra = get( 'extra' ) + + #setup vbds + + vbds = [] + + for i in range( int( get( 'num_vbds' ) ) ): + vbds.append( ( get( 'vbd%s_dom0' % i ), get('vbd%s_domU' % i ), get( 'vbd%s_mode' % i ), None ) ) + + vals.disk = vbds + + #misc + + vals.pci = [] + + vals.blkif = None + vals.netif = None + vals.restart = None + vals.console = None + vals.ramdisk = None + vals.ssidref = -1 + vals.bootloader = None + vals.usb = [] + vals.acpi = [] + + #setup vifs + + vals.vif = [] + vals.nics = 1 + + ip = get( 'ip_addr' ) + nfs = get( 'ip_nfs' ) + gate = get( 'ip_gateway' ) + mask = get( 'ip_subnet' ) + host = get( 'hostname' ) + dhcp = get( 'dhcp' ) + + vals.cmdline_ip = "%s:%s:%s:%s:%s:eth0:%s" % (ip, nfs, gate, mask, host, dhcp) + + opts = None + + try: + return make_config( opts, vals ) + except Exception, e: + return [["There was an error creating the domain config SXP. This is typically due to an interface change in xm/create.py:make_config", e]] + diff --git a/tools/python/xen/sv/DomInfo.py b/tools/python/xen/sv/DomInfo.py new file mode 100755 index 0000000..89feca0 --- /dev/null +++ b/tools/python/xen/sv/DomInfo.py @@ -0,0 +1,268 @@ +from xen.xend.XendClient import server +from xen.xend import PrettyPrint + +from xen.sv.HTMLBase import HTMLBase +from xen.sv.util import * +from xen.sv.GenTabbed import * +from xen.sv.Wizard import * + +DEBUG=1 + +class DomInfo( GenTabbed ): + + def __init__( self, urlWriter ): + + self.dom = 0; + + GenTabbed.__init__( self, "Domain Info", urlWriter, [ 'General', 'SXP', 'Devices', 'Migrate', 'Save' ], [ DomGeneralTab, DomSXPTab, DomDeviceTab, DomMigrateTab, DomSaveTab ] ) + + def write_BODY( self, request ): + try: + dom = int( getVar( 'dom', request ) ) + except: + request.write( "

Please Select a Domain

" ) + return None + + GenTabbed.write_BODY( self, request ) + + def write_MENU( self, request ): + domains = [] + + try: + domains = server.xend_domains() + domains.sort() + except: + pass + + request.write( "\n\n" ) + request.write( "" ) + request.write( "\n" ) + request.write( "\n" ) + request.write( "\n" ) + request.write( "\n" ) + request.write( "" ) + + odd = True + if not domains is None: + for domain in domains: + odd = not odd; + if odd: + request.write( "\n" ) + else: + request.write( "\n" ) + domInfo = getDomInfo( domain ) + request.write( "\n" % domInfo ) + url = self.urlWriter( "&dom=%(id)s" % domInfo ) + request.write( "\n" % ( url, domInfo['name'] ) ) + request.write( "\n" % domInfo ) + if domInfo[ 'id' ] != "0": + request.write( "" % domInfo ) + else: + request.write( "" ) + request.write( "\n" ) + else: + request.write( "

Error getting domain list
Perhaps XenD not running?

") + request.write( "
DomainNameState
%(id)s%s%(state)5s" ) + if domInfo[ 'state' ][ 2 ] == "-": + request.write( "" % domInfo ) + else: + request.write( "" % domInfo ) + request.write( " 
" ) + +class DomGeneralTab( CompositeTab ): + def __init__( self, urlWriter ): + CompositeTab.__init__( self, [ DomGenTab, DomActionTab ], urlWriter ) + +class DomGenTab( GeneralTab ): + + def __init__( self, _ ): + + titles = {} + + titles[ 'ID' ] = 'dom' + titles[ 'Name' ] = 'name' + titles[ 'CPU' ] = 'cpu' + titles[ 'Memory' ] = ( 'mem', memoryFormatter ) + titles[ 'State' ] = ( 'state', stateFormatter ) + titles[ 'Total CPU' ] = ( 'cpu_time', smallTimeFormatter ) + titles[ 'Up Time' ] = ( 'up_time', bigTimeFormatter ) + + GeneralTab.__init__( self, {}, titles ) + + def write_BODY( self, request ): + + self.dom = getVar('dom', request) + + if self.dom is None: + request.write( "

Please Select a Domain

" ) + return None + + self.dict = getDomInfo( self.dom ) + + GeneralTab.write_BODY( self, request ) + +class DomSXPTab( PreTab ): + + def __init__( self, _ ): + self.dom = 0 + PreTab.__init__( self, "" ) + + + def write_BODY( self, request ): + self.dom = getVar('dom', request) + + if self.dom is None: + request.write( "

Please Select a Domain

" ) + return None + + try: + domInfo = server.xend_domain( self.dom ) + except: + domInfo = [["Error getting domain details."]] + + self.source = sxp2prettystring( domInfo ) + + PreTab.write_BODY( self, request ) + +class DomActionTab( ActionTab ): + + def __init__( self, _ ): + actions = { "shutdown" : "Shutdown", + "reboot" : "Reboot", + "pause" : "Pause", + "unpause" : "Unpause", + "destroy" : "Destroy" } + ActionTab.__init__( self, actions ) + + def op_shutdown( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomShutDown %s" % dom + try: + server.xend_domain_shutdown( int( dom ), "poweroff" ) + except: + pass + + def op_reboot( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomReboot %s" % dom + try: + server.xend_domain_shutdown( int( dom ), "reboot" ) + except: + pass + + def op_pause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomPause %s" % dom + try: + server.xend_domain_pause( int( dom ) ) + except: + pass + + def op_unpause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomUnpause %s" % dom + try: + server.xend_domain_unpause( int( dom ) ) + except: + pass + + def op_destroy( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomDestroy %s" % dom + try: + server.xend_domain_destroy(int( dom )) + except: + pass + +class DomDeviceTab( CompositeTab ): + + def __init__( self, urlWriter ): + CompositeTab.__init__( self, [ DomDeviceListTab, DomDeviceOptionsTab, DomDeviceActionTab ], urlWriter ) + +class DomDeviceListTab( NullTab ): + + title = "Device List" + + def __init__( self, _ ): + pass + +class DomDeviceOptionsTab( NullTab ): + + title = "Device Options" + + def __init__( self, _ ): + pass + +class DomDeviceActionTab( ActionTab ): + + def __init__( self, _ ): + ActionTab.__init__( self, { "addvcpu" : "Add VCPU", "addvbd" : "Add VBD", "addvif" : "Add VIF" } ) + +class DomMigrateTab( CompositeTab ): + + def __init__( self, urlWriter ): + CompositeTab.__init__( self, [ DomMigrateExtraTab, DomMigrateActionTab ], urlWriter ) + +class DomMigrateExtraTab( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Configure Migration", 0) + self.addControl( TickControl('live', 'True', 'Live migrate:') ) + self.addControl( InputControl('rate', '0', 'Rate limit:') ) + self.addControl( InputControl( 'dest', 'host.domain', 'Name or IP address:', ".*") ) + +class DomMigrateActionTab( ActionTab ): + + def __init__( self, _ ): + actions = { "migrate" : "Migrate" } + ActionTab.__init__( self, actions ) + + def op_migrate( self, request ): + try: + domid = int( getVar( 'dom', request ) ) + live = getVar( 'live', request ) + rate = getVar( 'rate', request ) + dest = getVar( 'dest', request ) + dom_sxp = server.xend_domain_migrate( domid, dest, live == 'True', rate ) + success = "Your domain was successfully Migrated.\n" + except Exception, e: + success = "There was an error migrating your domain\n" + dom_sxp = str(e) + +class DomSaveTab( CompositeTab ): + + def __init__( self, urlWriter ): + CompositeTab.__init__( self, [ DomSaveExtraTab, DomSaveActionTab ], urlWriter ) + +class DomSaveExtraTab( Sheet ): + + title = "Save location" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Save Domain to file", 0 ) + self.addControl( InputControl( 'file', '', 'Suspend file name:', ".*") ) + +class DomSaveActionTab( ActionTab ): + + def __init__( self, _ ): + actions = { "save" : "Save" } + ActionTab.__init__( self, actions ) + + def op_save( self, request ): + + try: + dom_sxp = server.xend_domain_save( config['domid'], config['file'] ) + success = "Your domain was successfully saved.\n" + except Exception, e: + success = "There was an error saving your domain\n" + dom_sxp = str(e) + + try: + dom = int( getVar( 'dom', request ) ) + except: + pass diff --git a/tools/python/xen/sv/GenTabbed.py b/tools/python/xen/sv/GenTabbed.py new file mode 100755 index 0000000..6631663 --- /dev/null +++ b/tools/python/xen/sv/GenTabbed.py @@ -0,0 +1,147 @@ +import types + +from xen.sv.HTMLBase import HTMLBase +from xen.sv.util import getVar + +class GenTabbed( HTMLBase ): + + def __init__( self, title, urlWriter, tabStrings, tabObjects ): + HTMLBase.__init__(self) + self.tabStrings = tabStrings + self.tabObjects = tabObjects + self.urlWriter = urlWriter + self.title = title + + def write_BODY( self, request ): + if not self.__dict__.has_key( "tab" ): + try: + self.tab = int( getVar( 'tab', request, 0 ) ) + except: + self.tab = 0 + + request.write( "\n
%s
" % self.title ) + + TabView( self.tab, self.tabStrings, self.urlWriter ).write_BODY( request ) + + try: + request.write( "\n
" ) + render_tab = self.tabObjects[ self.tab ] + render_tab( self.urlWriter ).write_BODY( request ) + request.write( "\n
" ) + except Exception, e: + request.write( "\n

Error Rendering Tab

" ) + request.write( "\n

%s

" % str( e ) ) + + request.write( "\n" % self.tab ) + + def perform( self, request ): + request.write( "Tab> perform" ) + request.write( "
op: " + str( getVar( 'op', request ) ) ) + request.write( "
args: " + str( getVar( 'args', request ) ) ) + request.write( "
tab: " + str( getVar( 'tab', request ) ) ) + + try: + action = getVar( 'op', request, 0 ) + if action == "tab": + self.tab = int( getVar( 'args', request ) ) + else: + this.tab = int( getVar( 'tab', request, 0 ) ) + self.tabObjects[ self.tab ]( self.urlWriter ).perform( request ) + except: + pass + +class PreTab( HTMLBase ): + + def __init__( self, source ): + HTMLBase.__init__( self ) + self.source = source + + def write_BODY( self, request ): + request.write( "\n
" )
+        request.write( self.source )
+        request.write( "\n
" ) + +class GeneralTab( HTMLBase ): + + def __init__( self, dict, titles ): + HTMLBase.__init__( self ) + self.dict = dict + self.titles = titles + + def write_BODY( self, request ): + + request.write( "\n" ) + + def writeAttr( niceName, attr, formatter=None ): + if type( attr ) is types.TupleType: + ( attr, formatter ) = attr + + if attr in self.dict: + if formatter: + temp = formatter( self.dict[ attr ] ) + else: + temp = str( self.dict[ attr ] ) + request.write( "\n" % ( niceName, temp ) ) + + for niceName, attr in self.titles.items(): + writeAttr( niceName, attr ) + + request.write( "

%s:

%s

" ) + +class NullTab( HTMLBase ): + + def __init__( self, title="Null Tab" ): + HTMLBase.__init__( self ) + self.title = title + + def write_BODY( self, request ): + request.write( "\n

%s

" % self.title ) + +class ActionTab( HTMLBase ): + + def __init__( self, actions ): + self.actions = actions + HTMLBase.__init__( self ) + + def write_BODY( self, request ): + for item in self.actions.items(): + try: + ((op, attr), title) = item + except: + (op, title) = item + attr = "" + request.write( "\n
%s
" % (op, attr, title) ) + +class CompositeTab( HTMLBase ): + + def __init__( self, tabs, urlWriter ): + HTMLBase.__init__( self ) + self.tabs = tabs + self.urlWriter = urlWriter + + def write_BODY( self, request ): + for tab in self.tabs: + tab( self.urlWriter ).write_BODY( request ) + + def perform( self, request ): + for tab in self.tabs: + tab( self.urlWriter ).perform( request ) + +class TabView( HTMLBase ): + + # tab - int, id into tabs of selected tab + # tabs - list of strings, tab names + # urlWriter - + def __init__( self, tab, tabs, urlWriter ): + HTMLBase.__init__(self) + self.tab = tab + self.tabs = tabs + self.urlWriter = urlWriter + + def write_BODY( self, request ): + for i in range( len( self.tabs ) ): + if self.tab == i: + at = " id='activeTab'" + else: + at = "" + request.write( "\n%s" % ( at, i, self.tabs[ i ] ) ) diff --git a/tools/python/xen/sv/HTMLBase.py b/tools/python/xen/sv/HTMLBase.py new file mode 100755 index 0000000..d0fca13 --- /dev/null +++ b/tools/python/xen/sv/HTMLBase.py @@ -0,0 +1,53 @@ +from xen.sv.util import * + +class HTMLBase: + + isLeaf = True + + def __init__( self ): + pass + + def render_POST( self, request ): + self.perform( request ) + return self.render_GET( request ) + + def render_GET( self, request ): + pass + + def write_BODY( self, request ): + pass + + def write_TOP( self, request ): + pass + + def write_BOTTOM( self, request ): + pass + + def get_op_method(self, op): + """Get the method for an operation. + For operation 'foo' looks for 'op_foo'. + + op operation name + returns method or None + """ + op_method_name = 'op_' + op + return getattr(self, op_method_name, None) + + def perform(self, req): + """General operation handler for posted operations. + For operation 'foo' looks for a method op_foo and calls + it with op_foo(req). Replies with code 500 if op_foo + is not found. + + The method must return a list when req.use_sxp is true + and an HTML string otherwise (or list). + Methods may also return a Deferred (for incomplete processing). + + req request + """ + op = req.args.get('op') + if not op is None and len(op) == 1: + op = op[0] + op_method = self.get_op_method(op) + if op_method: + op_method( req ) diff --git a/tools/python/xen/sv/Main.py b/tools/python/xen/sv/Main.py new file mode 100755 index 0000000..ea62af1 --- /dev/null +++ b/tools/python/xen/sv/Main.py @@ -0,0 +1,82 @@ + +from xen.sv.NodeInfo import NodeInfo +from xen.sv.DomInfo import DomInfo +from xen.sv.CreateDomain import CreateDomain +from xen.sv.RestoreDomain import RestoreDomain + +from xen.sv.util import getVar + +# adapter to make this all work with mod_python +# as opposed to Twisted +# (c) Tom Wilkie 2005 + +class Args: + def __init__( self, req ): + from mod_python.util import FieldStorage + self.fieldStorage = FieldStorage( req, True ) + + # return a list of values for the given key, + # or None if key not there + def get( self, var ): + retVar = self.fieldStorage.getlist( var ) + if len( retVar ) == 0: + return None + else: + return retVar + + # return a list of tuples, + # (key, value) where value is a list of values + def items( self ): + result = []; + for key in self.fieldStorage.keys(): + result.append( (key, self.fieldStorage.getlist( key ) ) ) + return result + +# This is the Main class +# It pieces together all the modules + +class Main: + def __init__( self ): + self.modules = { "node": NodeInfo, + "create": CreateDomain, + "restore" : RestoreDomain, + "info": DomInfo } + + self.init_done = False + + def init_modules( self, request ): + for moduleName, module in self.modules.iteritems(): + self.modules[ moduleName ] = module( self.urlWriter( moduleName, request.url ) ) + + def render_menu( self, request ): + if not self.init_done: + self.init_modules( request ) + self.init_done = True + + for _, module in self.modules.iteritems(): + module.write_MENU( request ) + request.write( "\n" ) + + def render_main( self, request ): + if not self.init_done: + self.init_modules( request ) + self.init_done = True + + moduleName = getVar('mod', request) + if moduleName not in self.modules: + request.write( '

Please select a module

' ) + else: + module = self.modules[ moduleName ] + module.write_BODY( request ) + + def do_POST( self, request ): + if not self.init_done: + self.init_modules( request ) + self.init_done = True + + moduleName = getVar( 'mod', request ) + if moduleName in self.modules: + self.modules[ moduleName ].perform( request ) + + def urlWriter( self, module, url ): + return lambda x: "%s?mod=%s%s" % ( url, module, x ) diff --git a/tools/python/xen/sv/NodeInfo.py b/tools/python/xen/sv/NodeInfo.py new file mode 100755 index 0000000..f8b47b1 --- /dev/null +++ b/tools/python/xen/sv/NodeInfo.py @@ -0,0 +1,73 @@ +from xen.xend.XendClient import server + +from xen.sv.util import * +from xen.sv.GenTabbed import * + +class NodeInfo( GenTabbed ): + + def __init__( self, urlWriter ): + GenTabbed.__init__( self, "Node Details", urlWriter, [ 'General', 'Dmesg', 'SXP' ], [ NodeGeneralTab, NodeDmesgTab, NodeSXPTab ] ) + + def write_MENU( self, request ): + request.write( "

Node details

" % self.urlWriter( '' ) ) + +class NodeGeneralTab( CompositeTab ): + def __init__( self, urlWriter ): + CompositeTab.__init__( self, [ NodeInfoTab, NodeActionTab ], urlWriter ) + +class NodeInfoTab( GeneralTab ): + + def __init__( self, urlWriter ): + + nodeInfo = {} + try: + nodeInfo = sxp2hash( server.xend_node() ) + except: + nodeInfo[ 'system' ] = 'Error getting node info' + + dictTitles = {} + dictTitles[ 'System' ] = 'system' + dictTitles[ 'Hostname' ] = 'host' + dictTitles[ 'Release' ] = 'release' + dictTitles[ 'Version' ] ='version' + dictTitles[ 'Machine' ] = 'machine' + dictTitles[ 'Cores' ] = 'cores' + dictTitles[ 'Hyperthreading' ] = ( 'hyperthreads_per_core', hyperthreadFormatter ) + dictTitles[ 'CPU Speed' ] = ( 'cpu_mhz', cpuFormatter ) + dictTitles[ 'Memory' ] = ( 'memory', memoryFormatter ) + dictTitles[ 'Free Memory' ] = ( 'free_memory', memoryFormatter ) + + GeneralTab.__init__( self, dict=nodeInfo, titles=dictTitles ) + +class NodeDmesgTab( PreTab ): + + def __init__( self, urlWriter ): + try: + dmesg = server.xend_node_get_dmesg() + except: + dmesg = "Error getting node information: XenD not running?" + PreTab.__init__( self, dmesg ) + +class NodeActionTab( ActionTab ): + + def __init__( self, urlWriter ): + ActionTab.__init__( self, { "shutdown" : "shutdown", + "reboot" : "reboot" } ) + + def op_shutdown( self, request ): + if debug: print ">NodeShutDown" + server.xend_node_shutdown() + + def op_reboot( self, request ): + if debug: print ">NodeReboot" + server.xend_node_reboot() + +class NodeSXPTab( PreTab ): + + def __init__( self, urlWriter ): + try: + nodeSXP = sxp2string( server.xend_node() ) + except: + nodeSXP = 'Error getting node sxp' + + PreTab.__init__( self, nodeSXP ) diff --git a/tools/python/xen/sv/RestoreDomain.py b/tools/python/xen/sv/RestoreDomain.py new file mode 100755 index 0000000..b836a43 --- /dev/null +++ b/tools/python/xen/sv/RestoreDomain.py @@ -0,0 +1,50 @@ +from xen.sv.Wizard import * +from xen.sv.util import * +from xen.sv.GenTabbed import PreTab + +from xen.xm.create import make_config, OptVals + +from xen.xend.XendClient import server + +class RestoreDomain( Wizard ): + def __init__( self, urlWriter ): + + sheets = [ ChooseRestoreDomain, + DoRestore ] + + Wizard.__init__( self, urlWriter, "Restore Domain", sheets ) + + +class ChooseRestoreDomain( Sheet ): + title = "Configure Restore" + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Configure Restore", 0) + + self.addControl( InputControl( 'file', '', + 'Suspend file name:', + ".*") ) + +class DoRestore( Sheet ): + title = "Restore Done" + + def __init__(self, urlWriter ): + Sheet.__init__(self, urlWriter, "Restore Done", 1) + + def write_BODY( self, request, err ): + + if not self.passback: self.parseForm( request ) + config = ssxp2hash ( string2sxp( self.passback ) ) + + try: + dom_sxp = server.xend_domain_restore( config['file'] ) + success = "Your domain was successfully restored.\n" + except Exception, e: + success = "There was an error restoring your domain\n" + dom_sxp = str(e) + + pt = PreTab( success + sxp2prettystring( dom_sxp ) ) + pt.write_BODY( request ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) diff --git a/tools/python/xen/sv/Wizard.py b/tools/python/xen/sv/Wizard.py new file mode 100755 index 0000000..c4ac53b --- /dev/null +++ b/tools/python/xen/sv/Wizard.py @@ -0,0 +1,245 @@ +from xen.sv.util import * +from xen.sv.HTMLBase import HTMLBase +from xen.sv.GenTabbed import GenTabbed, ActionTab +from xen.xend import sxp + +import re + +DEBUG = 0 + +class Wizard( GenTabbed ): + + def __init__( self, urlWriter, title, sheets ): + self.title = title + self.sheets = sheets + self.urlWriter = urlWriter + self.offset = 0 + GenTabbed.__init__( self, title, urlWriter, map( lambda x: x.title, sheets ), sheets ) + + def write_MENU( self, request ): + request.write( "

%s

" % (self.urlWriter( '' ), self.title) ) + + def write_BODY( self, request ): + GenTabbed.write_BODY( self, request ) + actionTab = ActionTab( { ("tab", str(self.tab-1)) : "< Prev", ("tab", str(self.tab+1)) : "Next >", "finish" : "Finish" } ) + actionTab.write_BODY( request ) + + def perform( self, request ): + try: + action = getVar( 'op', request, 0 ) + if action == "tab": + self.tab = int( getVar( 'args', request ) ) + oldtab = int( getVar( 'tab', request ) ) + if not self.tabObjects[ oldtab ]( self.urlWriter ).validate( request ): + self.tab = oldtab + else: + self.tab = int( getVar( 'tab', request, 0 ) ) + self.tabObjects[ self.tab ]( self.urlWriter ).perform( request ) + getattr( self, "op_" + getVar( "op", request ), None )( request ) + except: + pass + + def op_finish( self, request ): + pass + +class Sheet( HTMLBase ): + + def __init__( self, urlWriter, title, location ): + HTMLBase.__init__( self ) + self.urlWriter = urlWriter + self.fields = [] + self.title = title + self.location = location + self.passback = None + + def parseForm( self, request ): + do_not_parse = [ 'mod', 'op', 'passback' ] + + passed_back = request.args + + temp_passback = passed_back.get( "passback" ) + + if temp_passback is not None and len( temp_passback ) > 0: + temp_passback = temp_passback[ len( temp_passback )-1 ] + else: + temp_passback = "( )" + + last_passback = ssxp2hash( string2sxp( temp_passback ) ) #use special function - will work with no head on sxp + + if DEBUG: print last_passback + + for (key, value) in passed_back.items(): + if key not in do_not_parse: + last_passback[ key ] = value[ len( value ) - 1 ] + + self.passback = sxp2string( hash2sxp( last_passback ) ) #store the sxp + + if DEBUG: print self.passback + + def write_BODY( self, request ): + + if not self.passback: self.parseForm( request ) + + request.write( "

%s

" % self.title ) + + previous_values = ssxp2hash( string2sxp( self.passback ) ) #get the hash for quick reference + + request.write( "" ) + + for (field, control) in self.fields: + control.write_Control( request, previous_values.get( field ) ) + if previous_values.get( field ) is not None and not control.validate( previous_values.get( field ) ): + control.write_Help( request ) + + request.write( "
" ) + + request.write( "

" % self.passback ) + #request.write( "

" % self.location ) + + def addControl( self, control ): + self.fields.append( [ control.getName(), control ] ) + + def validate( self, request ): + + if not self.passback: self.parseForm( request ) + + check = True + + previous_values = ssxp2hash( string2sxp( self.passback ) ) #get the map for quick reference + if DEBUG: print previous_values + + for (field, control) in self.fields: + if not control.validate( previous_values.get( field ) ): + check = False + if DEBUG: print "> %s = %s" % (field, previous_values.get( field )) + + return check + +class SheetControl( HTMLBase ): + + def __init__( self, reg_exp = ".*" ): + HTMLBase.__init__( self ) + self.name = "" + self.reg_exp = reg_exp + + def write_Control( self, request, persistedValue ): + request.write( "%s" % persistedValue ) + + def write_Help( self, request ): + request.write( "

Text must match pattern:" ) + request.write( " %s

" % self.reg_exp ) + + def validate( self, persistedValue ): + if persistedValue is None: + persistedValue = "" + + return not re.compile( self.reg_exp ).match( persistedValue ) is None + + def getName( self ): + return self.name + + def setName( self, name ): + self.name = name + +class InputControl( SheetControl ): + + def __init__( self, name, defaultValue, humanText, reg_exp = ".*", help_text = "You must enter the appropriate details in this field." ): + SheetControl.__init__( self, reg_exp ) + self.setName( name ) + + self.defaultValue = defaultValue + self.humanText = humanText + self.help_text = help_text + + def write_Control( self, request, persistedValue ): + if persistedValue is None: + persistedValue = self.defaultValue + + request.write( "

%s

" % (self.humanText, self.getName(), persistedValue) ) + + def write_Help( self, request ): + request.write( "

" ) + request.write( " %s

" % self.help_text ) + +class TextControl( SheetControl ): + + def __init__( self, text ): + SheetControl.__init__( self ) + self.text = text + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.text ) + +class SmallTextControl( SheetControl ): + + def __init__( self, text ): + SheetControl.__init__( self ) + self.text = text + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.text ) + +class ListControl( SheetControl ): + + def __init__( self, name, options, humanText ): + SheetControl.__init__( self ) + self.setName( name ) + self.options = options + self.humanText = humanText + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.humanText ) + request.write( "" ) + + def validate( self, persistedValue ): + for (value, text) in self.options: + if value == persistedValue: + return True + + return False + +class FileControl( InputControl ): + + def __init__( self, name, defaultValue, humanText, reg_exp = ".*", help_text = "You must enter the appropriate details in this field." ): + InputControl.__init__( self, name, defaultValue, humanText ) + + def validate( self, persistedValue ): + if persistedValue is None: return False + try: + open( persistedValue ) + return True + except IOError, TypeError: + return False + + def write_Help( self, request ): + request.write( "

File does not exist: you must enter a valid, absolute file path.

" ) + +class TickControl( SheetControl ): + + def __init__( self, name, defaultValue, humanText ): + SheetControl.__init__( self ) + self.setName( name ) + self.defaultValue = defaultValue + self.humanText = humanText + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.humanText ) + + #request.write( str( persistedValue ) ) + + #TODO: Theres a problem with this: it doesn't persist an untick, because the browsers don't pass it back. Need a fix... + + if persistedValue == 'True': + request.write( "" % self.getName() ) + else: + request.write( "" % self.getName() ) + + request.write( "" ) + + diff --git a/tools/python/xen/sv/__init__.py b/tools/python/xen/sv/__init__.py new file mode 100755 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/sv/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/sv/util.py b/tools/python/xen/sv/util.py new file mode 100755 index 0000000..cfed397 --- /dev/null +++ b/tools/python/xen/sv/util.py @@ -0,0 +1,126 @@ +from xen.xend.XendClient import server +from xen.xend import sxp +from xen.xend import PrettyPrint + +import types + +def getDomInfo( domain ): + domInfoHash = {} + try: + domInfoHash = sxp2hash( server.xend_domain( domain ) ) + domInfoHash['dom'] = domain + except: + domInfoHash['name'] = "Error getting domain details" + return domInfoHash + +def sxp2hash( s ): + sxphash = {} + + for child in sxp.children( s ): + if isinstance( child, types.ListType ) and len( child ) > 1: + if isinstance( child[1], types.ListType ) and len( child ) > 1: + sxphash[ child[0] ] = sxp2hash( child[1] ) + else: + sxphash[ child[0] ] = child[1] + + return sxphash + +def ssxp2hash( s ): + sxphash = {} + + for i in s: + if isinstance( i, types.ListType ) and len( i ) > 1: + sxphash[ i[0] ] = i[1] + + return sxphash + +def hash2sxp( h ): + hashsxp = [] + + for (key, item) in h.items(): + hashsxp.append( [key, item] ) + + return hashsxp + +def string2sxp( string ): + pin = sxp.Parser() + pin.input( string ) + return pin.get_val() + +def sxp2string( sexp ): + return sxp.to_string( sexp ) + +def sxp2prettystring( sxp ): + class tmp: + def __init__( self ): + self.str = "" + def write( self, str ): + self.str = self.str + str + temp = tmp() + PrettyPrint.prettyprint( sxp, out=temp ) + return temp.str + +def getVar( var, request, default=None ): + + arg = request.args.get( var ) + + if arg is None: + return default + else: + return arg[ len( arg )-1 ] + +def bigTimeFormatter( time ): + time = float( time ) + weeks = time // 604800 + remainder = time % 604800 + days = remainder // 86400 + + remainder = remainder % 86400 + + hms = smallTimeFormatter( remainder ) + + return "%d weeks, %d days, %s" % ( weeks, days, hms ) + +def smallTimeFormatter( time ): + time = float( time ) + hours = time // 3600 + remainder = time % 3600 + mins = remainder // 60 + secs = time % 60 + return "%02d:%02d:%04.1f (hh:mm:ss.s)" % ( hours, mins, secs ) + +def stateFormatter( state ): + states = [ 'Running', 'Blocked', 'Paused', 'Shutdown', 'Crashed' ] + + stateStr = "" + + for i in range( len( state ) ): + if state[i] != "-": + stateStr += "%s, " % states[ i ] + + return stateStr + " (%s)" % state + +def memoryFormatter( mem ): + mem = int( mem ) + if mem >= 1024: + mem = float( mem ) / 1024 + return "%3.2fGb" % mem + else: + return "%7dMb" % mem + +def cpuFormatter( mhz ): + mhz = int( mhz ) + if mhz > 1000: + ghz = float( mhz ) / 1000.0 + return "%4.2fGHz" % ghz + else: + return "%4dMHz" % mhz + +def hyperthreadFormatter( threads ): + try: + if int( threads ) > 1: + return "Yes" + else: + return "No" + except: + return "No" diff --git a/tools/python/xen/util/Brctl.py b/tools/python/xen/util/Brctl.py new file mode 100644 index 0000000..5dc9a7f --- /dev/null +++ b/tools/python/xen/util/Brctl.py @@ -0,0 +1,186 @@ +"""Bridge control utilities. +""" +import os +import os.path +import re +import sys + +CMD_IFCONFIG = 'ifconfig' +CMD_ROUTE = 'route' +CMD_BRCTL = 'brctl' +CMD_IPTABLES = "iptables" + +opts = None + +class Opts: + + def __init__(self, defaults): + for (k, v) in defaults.items(): + setattr(self, k, v) + pass + +def cmd(p, s): + """Print and execute command 'p' with args 's'. + """ + global opts + c = p + ' ' + s + if opts.verbose: print c + if not opts.dryrun: + os.system(c) + +bridgeRE = re.compile(r'([^\t]*)\t*[^\t]*\t*[^\t]*\t*([^\t]*)') +def get_state(): + fin = os.popen(CMD_BRCTL + ' show', 'r') + try: + bridges = {} + brlist = None + brname = None + first = True + for line in fin: + if first: + first = False + elif line[0] == '\t': + brlist.append(line.strip()) + else: + if brname: + bridges[brname] = brlist + m = bridgeRE.match(line) + brname = m.group(1) + brlist = [m.group(2).strip()] + if brname: + bridges[brname] = brlist + return bridges + finally: + fin.close() + +def vif_bridge_add(params): + """Add the network interface for vif on dom to a bridge. + """ + cmd(CMD_BRCTL, 'addif %(bridge)s %(vif)s' % params) + +def vif_bridge_rem(params): + """Remove the network interface for vif on dom from a bridge. + """ + cmd(CMD_BRCTL, 'delif %(bridge)s %(vif)s' % params) + +def vif_restrict_addr(vif, addr, delete=0): + d = { 'vif': vif, 'addr': addr} + if delete: + d['flag'] = '-D' + else: + d['flag'] = '-A' + cmd(CMD_IPTABLES, '-P FORWARD DROP') + cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-in %(vif)s -s %(addr)s -j ACCEPT' % d) + cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-out %(vif)s -d %(addr)s -j ACCEPT' % d) + +def bridge_create(bridge, **kwd): + """Create a bridge. + Defaults hello time to 0, forward delay to 0 and stp off. + """ + cmd(CMD_BRCTL, 'addbr %s' % bridge) + if kwd.get('hello', None) is None: + kwd['hello'] = 0 + if kwd.get('fd', None) is None: + kwd['fd'] = 0 + if kwd.get('stp', None) is None: + kwd['stp'] = 'off' + bridge_set(bridge, **kwd) + cmd(CMD_IFCONFIG, "%s up" % bridge) + +def bridge_set(bridge, hello=None, fd=None, stp=None): + """Set bridge parameters. + """ + if hello is not None: + cmd(CMD_BRCTL, 'sethello %s %d' % (bridge, hello)) + if fd is not None: + cmd(CMD_BRCTL, 'setfd %s %d' % (bridge, fd)) + if stp is not None: + cmd(CMD_BRCTL, 'stp %s %s' % (bridge, stp)) + +def bridge_del(bridge): + """Delete a bridge. + """ + cmd(CMD_IFCONFIG, '%s down' % bridge) + cmd(CMD_BRCTL, 'delbr %s' % bridge) + +def routes(): + """Return a list of the routes. + """ + fin = os.popen(CMD_ROUTE + ' -n', 'r') + routes = [] + for x in fin: + if x.startswith('Kernel'): continue + if x.startswith('Destination'): continue + x = x.strip() + y = x.split() + z = { 'destination': y[0], + 'gateway' : y[1], + 'mask' : y[2], + 'flags' : y[3], + 'metric' : y[4], + 'ref' : y[5], + 'use' : y[6], + 'interface' : y[7] } + routes.append(z) + return routes + +def ifconfig(interface): + """Return the ip config for an interface, + """ + fin = os.popen(CMD_IFCONFIG + ' %s' % interface, 'r') + inetre = re.compile('\s*inet\s*addr:(?P
\S*)\s*Bcast:(?P\S*)\s*Mask:(?P\S*)') + info = None + for x in fin: + m = inetre.match(x) + if not m: continue + info = m.groupdict() + info['interface'] = interface + break + return info + +def reconfigure(interface, bridge): + """Reconfigure an interface to be attached to a bridge, and give the bridge + the IP address etc. from interface. Move the default route to the interface + to the bridge. + + """ + global opts + intf_info = ifconfig(interface) + if not intf_info: + print >>sys.stderr, 'Interface not found:', interface + return + #bridge_info = ifconfig(bridge) + #if not bridge_info: + # print >>sys.stderr, 'Bridge not found:', bridge + # return + route_info = routes() + intf_info['bridge'] = bridge + intf_info['gateway'] = None + for r in route_info: + if (r['destination'] == '0.0.0.0' and + 'G' in r['flags'] and + r['interface'] == interface): + intf_info['gateway'] = r['gateway'] + if not intf_info['gateway']: + print >>sys.stderr, 'Gateway not found: ', interface + return + cmd(CMD_IFCONFIG, + '%(bridge)s %(address)s netmask %(mask)s broadcast %(broadcast)s up' + % intf_info) + cmd(CMD_ROUTE, + 'add default gateway %(gateway)s dev %(bridge)s' + % intf_info) + cmd(CMD_BRCTL, 'addif %(bridge)s %(interface)s' % intf_info) + cmd(CMD_IFCONFIG, '%(interface)s 0.0.0.0' % intf_info) + +defaults = { + 'verbose' : 1, + 'dryrun' : 0, + } + +opts = Opts(defaults) + +def set_opts(val): + global opts + opts = val + return opts diff --git a/tools/python/xen/util/SSHTransport.py b/tools/python/xen/util/SSHTransport.py new file mode 100644 index 0000000..fa975f3 --- /dev/null +++ b/tools/python/xen/util/SSHTransport.py @@ -0,0 +1,102 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 Anthony Liguori +# Copyright (C) 2006 XenSource Inc. +#============================================================================ + +""" +XML-RPC SSH transport. +""" + +from xmlrpclib import getparser, Fault +from subprocess import Popen, PIPE +from getpass import getuser +from fcntl import ioctl +import errno +import os +import termios + + +def getHTTPURI(uri): + (protocol, rest) = uri.split(':', 1) + if not rest.startswith('//'): + raise ValueError("Invalid ssh URL '%s'" % uri) + rest = rest[2:] + user = getuser() + path = 'RPC2' + if rest.find('@') != -1: + (user, rest) = rest.split('@', 1) + if rest.find('/') != -1: + (host, rest) = rest.split('/', 1) + if len(rest) > 0: + path = rest + else: + host = rest + transport = SSHTransport(host, user) + uri = 'http://%s/%s' % (host, path) + return transport, uri + + +class SSHTransport(object): + def __init__(self, host, user, askpass=None): + self.host = host + self.user = user + self.askpass = askpass + self.ssh = None + + def getssh(self): + if self.ssh == None: + if self.askpass: + f = open('/dev/tty', 'w') + try: + os.environ['SSH_ASKPASS'] = self.askpass + ioctl(f.fileno(), termios.TIOCNOTTY) + finally: + f.close() + + cmd = ['ssh', '%s@%s' % (self.user, self.host), 'xm serve'] + try: + self.ssh = Popen(cmd, bufsize=0, stdin=PIPE, stdout=PIPE) + except OSError, (err, msg): + if err == errno.ENOENT: + raise Fault(0, "ssh executable not found!") + raise + return self.ssh + + def request(self, host, handler, request_body, verbose=0): + p, u = getparser() + ssh = self.getssh() + ssh.stdin.write("""POST /%s HTTP/1.1 +User-Agent: Xen +Host: %s +Content-Type: text/xml +Content-Length: %d + +%s""" % (handler, host, len(request_body), request_body)) + ssh.stdin.flush() + + content_length = 0 + line = ssh.stdout.readline() + if line.split()[1] != '200': + raise Fault(0, 'Server returned %s' % (' '.join(line[1:]))) + + while line not in ['', '\r\n', '\n']: + if line.lower().startswith('content-length:'): + content_length = int(line[15:].strip()) + line = ssh.stdout.readline() + content = ssh.stdout.read(content_length) + p.feed(content) + p.close() + return u.close() diff --git a/tools/python/xen/util/__init__.py b/tools/python/xen/util/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/util/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/util/acmpolicy.py b/tools/python/xen/util/acmpolicy.py new file mode 100644 index 0000000..64978a6 --- /dev/null +++ b/tools/python/xen/util/acmpolicy.py @@ -0,0 +1,1615 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006,2007 International Business Machines Corp. +# Author: Stefan Berger +#============================================================================ + +import os +import sha +import stat +import array +import struct +import shutil +import commands +from xml.dom import minidom, Node +from xen.xend.XendLogging import log +from xen.util import xsconstants, bootloader, mkdir +from xen.util.xspolicy import XSPolicy +from xen.xend.XendError import SecurityError +import xen.util.xsm.acm.acm as security +from xen.util.xsm.xsm import XSMError +from xen.xend import XendOptions + +ACM_POLICIES_DIR = security.policy_dir_prefix + "/" + +# Constants needed for generating a binary policy from its XML +# representation +ACM_POLICY_VERSION = 4 # Latest one +ACM_CHWALL_VERSION = 1 + +ACM_STE_VERSION = 1 + +ACM_MAGIC = 0x001debc; + +ACM_NULL_POLICY = 0 +ACM_CHINESE_WALL_POLICY = 1 +ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY = 2 +ACM_POLICY_UNDEFINED = 15 + + +ACM_LABEL_UNLABELED = "__UNLABELED__" +ACM_LABEL_UNLABELED_DISPLAY = "unlabeled" + +""" + Error codes reported in when trying to test for a new policy + These error codes are reported in an array of tuples where + each error code is followed by a parameter describing the error + more closely, such as a domain id. +""" +ACM_EVTCHN_SHARING_VIOLATION = 0x100 +ACM_GNTTAB_SHARING_VIOLATION = 0x101 +ACM_DOMAIN_LOOKUP = 0x102 +ACM_CHWALL_CONFLICT = 0x103 +ACM_SSIDREF_IN_USE = 0x104 + + +DEFAULT_policy = \ +"\n" +\ +"\n" +\ +" \n" +\ +" DEFAULT\n" +\ +" 1.0\n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" SystemManagement\n" +\ +" __UNLABELED__\n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" SystemManagement\n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" SystemManagement\n" +\ +" \n" +\ +" SystemManagement\n" +\ +" __UNLABELED__\n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" __UNLABELED__\n" +\ +" \n" +\ +" __UNLABELED__\n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" __UNLABELED__\n" +\ +" \n" +\ +" __UNLABELED__\n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +" \n" +\ +"\n" + +ACM_SCHEMA=""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +def get_DEFAULT_policy(dom0label=""): + fromnode = "" + if dom0label != "": + fromnode = " from=\"%s\"" % dom0label + return DEFAULT_policy % fromnode + +def initialize(): + xoptions = XendOptions.instance() + basedir = xoptions.get_xend_security_path() + policiesdir = basedir + "/policies" + mkdir.parents(policiesdir, stat.S_IRWXU) + + instdir = security.install_policy_dir_prefix + DEF_policy_file = "DEFAULT-security_policy.xml" + + #Install default policy. + f = open(policiesdir + "/" + DEF_policy_file, 'w') + if f: + f.write(get_DEFAULT_policy()) + f.close() + else: + log.error("Could not write the default policy's file.") + defpol = ACMPolicy(xml=get_DEFAULT_policy()) + defpol.compile() + + +class ACMPolicy(XSPolicy): + """ + ACMPolicy class. Implements methods for getting information from + the XML representation of the policy as well as compilation and + loading of a policy into the HV. + """ + + def __init__(self, name=None, dom=None, ref=None, xml=None): + if name: + self.name = name + try: + self.dom = minidom.parse(self.path_from_policy_name(name)) + except Exception, e: + raise SecurityError(-xsconstants.XSERR_XML_PROCESSING, + str(e)) + elif dom: + self.dom = dom + self.name = self.get_name() + elif xml: + try: + self.dom = minidom.parseString(xml) + except Exception, e: + raise SecurityError(-xsconstants.XSERR_XML_PROCESSING, + str(e)) + self.name = self.get_name() + rc = self.validate() + if rc != xsconstants.XSERR_SUCCESS: + raise SecurityError(rc) + if ref: + from xen.xend.XendXSPolicy import XendACMPolicy + self.xendacmpolicy = XendACMPolicy(self, {}, ref) + else: + self.xendacmpolicy = None + XSPolicy.__init__(self, name=self.name, ref=ref) + + def get_dom(self): + return self.dom + + def get_name(self): + return self.policy_dom_get_hdr_item("PolicyName") + + def get_type(self): + return xsconstants.XS_POLICY_ACM + + def get_type_name(self): + return xsconstants.ACM_POLICY_ID + + def __str__(self): + return self.get_name() + + + def validate(self): + """ + validate against the policy's schema Does not fail if the + libxml2 python lib is not installed + """ + rc = xsconstants.XSERR_SUCCESS + try: + import libxml2 + except Exception, e: + log.warn("Libxml2 python-wrapper is not installed on the system.") + return xsconstants.XSERR_SUCCESS + try: + parserctxt = libxml2.schemaNewMemParserCtxt(ACM_SCHEMA, + len(ACM_SCHEMA)) + schemaparser = parserctxt.schemaParse() + valid = schemaparser.schemaNewValidCtxt() + doc = libxml2.parseDoc(self.toxml()) + if doc.schemaValidateDoc(valid) != 0: + rc = -xsconstants.XSERR_BAD_XML + except Exception, e: + log.warn("Problem with the schema: %s" % str(e)) + rc = -xsconstants.XSERR_GENERAL_FAILURE + if rc != xsconstants.XSERR_SUCCESS: + log.warn("XML did not validate against schema") + if rc == xsconstants.XSERR_SUCCESS: + rc = self.__validate_name_and_labels() + return rc + + def __validate_name_and_labels(self): + """ no ':' allowed in the policy name and the labels """ + if ':' in self.get_name(): + return -xsconstants.XSERR_BAD_POLICY_NAME + for s in self.policy_get_resourcelabel_names(): + if ':' in s: + return -xsconstants.XSERR_BAD_LABEL + for s in self.policy_get_virtualmachinelabel_names(): + if ':' in s: + return -xsconstants.XSERR_BAD_LABEL + return xsconstants.XSERR_SUCCESS + + + def is_default_policy(self): + """ + Determine whether this is the default policy + """ + default = ['SystemManagement', ACM_LABEL_UNLABELED ] + if self.policy_get_virtualmachinelabel_names() == default and \ + self.policy_get_bootstrap_vmlabel() == default[0] and \ + self.policy_get_stetypes_types() == default and \ + self.policy_get_stes_of_vmlabel(default[0]) == default and \ + self.policy_get_stes_of_vmlabel(default[1]) == [default[1]] and \ + self.policy_get_resourcelabel_names() == [default[1]] and \ + self.policy_get_chwall_types() == [ default[0] ] and \ + self.get_name() == "DEFAULT": + return True + return False + + def update(self, xml_new): + """ + Update the policy with the new XML. The hypervisor decides + whether the new policy can be applied. + """ + rc = -xsconstants.XSERR_XML_PROCESSING + errors = "" + acmpol_old = self + try: + acmpol_new = ACMPolicy(xml=xml_new) + except Exception: + return -xsconstants.XSERR_XML_PROCESSING, errors + + vmlabel_map = acmpol_new.policy_get_vmlabel_translation_map() + + # An update requires version information in the current + # and new policy. The version number of the current policy + # must be the same as what is in the FromPolicy/Version node + # in the new one and the current policy's name must be the + # same as in FromPolicy/PolicyName + # The default policy when it is set skips this step. + if not acmpol_new.is_default_policy() and \ + not acmpol_old.is_default_policy(): + irc = self.__do_update_version_check(acmpol_new) + if irc != xsconstants.XSERR_SUCCESS: + return irc, errors + + if self.isloaded(): + newvmnames = \ + acmpol_new.policy_get_virtualmachinelabel_names_sorted() + oldvmnames = \ + acmpol_old.policy_get_virtualmachinelabel_names_sorted() + del_array = "" + chg_array = "" + + for o in oldvmnames: + if o not in newvmnames: + old_idx = oldvmnames.index(o) + if vmlabel_map.has_key(o): + #not a deletion, but a renaming + new = vmlabel_map[o] + new_idx = newvmnames.index(new) + chg_array += struct.pack("ii", old_idx, new_idx) + else: + del_array += struct.pack("i", old_idx) + for v in newvmnames: + if v in oldvmnames: + old_idx = oldvmnames.index(v) + new_idx = newvmnames.index(v) + if old_idx != new_idx: + chg_array += struct.pack("ii", old_idx, new_idx) + + # VM labels indicated in the 'from' attribute of a VM or + # resource node but that did not exist in the old policy + # are considered bad labels. + bad_renamings = set(vmlabel_map.keys()) - set(oldvmnames) + if len(bad_renamings) > 0: + log.error("Bad VM label renamings: %s" % + list(bad_renamings)) + return -xsconstants.XSERR_BAD_LABEL, errors + + reslabel_map = acmpol_new.policy_get_reslabel_translation_map() + oldresnames = acmpol_old.policy_get_resourcelabel_names() + bad_renamings = set(reslabel_map.keys()) - set(oldresnames) + if len(bad_renamings) > 0: + log.error("Bad resource label renamings: %s" % + list(bad_renamings)) + return -xsconstants.XSERR_BAD_LABEL, errors + + #Get binary and map from the new policy + rc, pol_map, bin_pol = acmpol_new.policy_create_map_and_bin() + if rc != xsconstants.XSERR_SUCCESS: + log.error("Could not build the map and binary policy.") + return rc, errors + + #Need to do / check the following: + # - relabel all resources where there is a 'from' field in + # the policy and mark those as unlabeled where the label + # does not appear in the new policy anymore + # - relabel all VMs where there is a 'from' field in the + # policy and mark those as unlabeled where the label + # does not appear in the new policy anymore; no running + # or paused VM may be unlabeled through this + # - check that under the new labeling conditions the VMs + # still have access to their resources as before. Unlabeled + # resources are inaccessible. If this check fails, the + # update failed. + # - Attempt changes in the hypervisor; if this step fails, + # roll back the relabeling of resources and VMs + # - Commit the relabeling of resources + + + rc, errors = security.change_acm_policy(bin_pol, + del_array, chg_array, + vmlabel_map, reslabel_map, + self, acmpol_new, + acmpol_new.is_default_policy()) + + if rc == 0: + # Replace the old DOM with the new one and save it + self.dom = acmpol_new.dom + self.compile() + log.info("ACM policy update was successful") + else: + #Not loaded in HV + self.dom = acmpol_new.dom + rc = self.compile() + return rc, errors + + def force_default_policy(klass, policy_ref): + """ + Force the installation of the DEFAULT policy if for + example no XML of the current policy is available and + the update path with comparisons of old and new policy + cannot be taken. + This only succeeds if only Domain-0 is running or + all guest have the same ssidref as Domain-0. + """ + errors = "" + + acmpol_new = ACMPolicy(xml = get_DEFAULT_policy(), ref=policy_ref) + + from xen.lowlevel import acm + dom0_ssidref = acm.getssid(0) + del_array = "" + chg_array = struct.pack("ii", + dom0_ssidref['ssidref'] & 0xffff, + 0x1) + + rc, pol_map, bin_pol = acmpol_new.policy_create_map_and_bin() + if rc != xsconstants.XSERR_SUCCESS: + return rc, errors, acmpol_new + rc, errors = security.hv_chg_policy(bin_pol, del_array, chg_array) + return rc, errors, acmpol_new + + force_default_policy = classmethod(force_default_policy) + + def get_reset_policy_xml(klass): + dom0_label = security.get_ssid(0)[1] + return get_DEFAULT_policy(dom0_label) + + get_reset_policy_xml = classmethod(get_reset_policy_xml) + + def __do_update_version_check(self, acmpol_new): + acmpol_old = self + + now_vers = acmpol_old.policy_dom_get_hdr_item("Version") + now_name = acmpol_old.policy_dom_get_hdr_item("PolicyName") + req_oldvers = acmpol_new.policy_dom_get_frompol_item("Version") + req_oldname = acmpol_new.policy_dom_get_frompol_item("PolicyName") + + if now_vers == "" or \ + now_vers != req_oldvers or \ + now_name != req_oldname: + log.info("Policy rejected: %s != %s or %s != %s" % \ + (now_vers,req_oldvers,now_name,req_oldname)) + return -xsconstants.XSERR_VERSION_PREVENTS_UPDATE + + if not self.isVersionUpdate(acmpol_new): + log.info("Policy rejected since new version is not an update.") + return -xsconstants.XSERR_VERSION_PREVENTS_UPDATE + + return xsconstants.XSERR_SUCCESS + + + def compareVersions(self, v1, v2): + """ + Compare two policy versions given their tuples of major and + minor. + Return '0' if versions are equal, '>0' if v1 > v2 and + '<' if v1 < v2 + """ + rc = v1[0] - v2[0] + if rc == 0: + rc = v1[1] - v2[1] + return rc + + def getVersionTuple(self, item="Version"): + v_str = self.policy_dom_get_hdr_item(item) + return self.__convVersionToTuple(v_str) + + def get_version(self): + return self.policy_dom_get_hdr_item("Version") + + def isVersionUpdate(self, polnew): + if self.compareVersions(polnew.getVersionTuple(), + self.getVersionTuple()) > 0: + return True + return False + + def __convVersionToTuple(self, v_str): + """ Convert a version string, formatted according to the scheme + "%d.%d" into a tuple of (major, minor). Return (0,0) if the + string is empty. + """ + major = 0 + minor = 0 + if v_str != "": + tmp = v_str.split(".") + major = int(tmp[0]) + if len(tmp) > 1: + minor = int(tmp[1]) + return (major, minor) + + def get_policies_path(self): + xoptions = XendOptions.instance() + basedir = xoptions.get_xend_security_path() + return basedir + "/policies/" + + def policy_path(self, name): + prefix = self.get_policies_path() + path = prefix + name.replace('.','/') + _path = path.split("/") + del _path[-1] + mkdir.parents("/".join(_path), stat.S_IRWXU) + return path + + def path_from_policy_name(self, name): + return self.policy_path(name) + "-security_policy.xml" + + # + # Functions interacting with the bootloader + # + def vmlabel_to_ssidref(self, vm_label): + """ Convert a VMlabel into an ssidref given the current + policy + Return xsconstants.INVALID_SSIDREF if conversion failed. + """ + ssidref = xsconstants.INVALID_SSIDREF + names = self.policy_get_virtualmachinelabel_names_sorted() + try: + vmidx = names.index(vm_label) + ssidref = (vmidx << 16) | vmidx + except: + pass + return ssidref + + def set_vm_bootlabel(self, vm_label, remove=False): + parms="<>" + if vm_label != "": + ssidref = self.vmlabel_to_ssidref(vm_label) + if ssidref == xsconstants.INVALID_SSIDREF: + return -xsconstants.XSERR_BAD_LABEL + parms = "0x%08x:%s:%s:%s" % \ + (ssidref, xsconstants.ACM_POLICY_ID, \ + self.get_name(),vm_label) + else: + ssidref = 0 #Identifier for removal + + if remove == True: + parms = "<>" + + try: + def_title = bootloader.get_default_title() + bootloader.set_kernel_attval(def_title, "ssidref", parms) + except: + return -xsconstants.XSERR_GENERAL_FAILURE + return ssidref + + # + # Utility functions related to the policy's files + # + def get_filename(self, postfix, prefix=None, dotted=False): + """ + Create the filename for the policy. The prefix is prepended + to the path. If dotted is True, then a policy name like + 'a.b.c' will remain as is, otherwise it will become 'a/b/c' + """ + if prefix == None: + prefix = self.get_policies_path() + name = self.get_name() + if name: + p = name.split(".") + path = "" + if dotted: + sep = "." + else: + sep = "/" + if len(p) > 1: + path = sep.join(p[0:len(p)-1]) + if prefix != "" or path != "": + allpath = prefix + path + sep + p[-1] + postfix + else: + allpath = p[-1] + postfix + return allpath + return None + + def __readfile(self, name): + cont = "" + filename = self.get_filename(name) + f = open(filename, "r") + if f: + cont = f.read() + f.close() + return cont + + def get_map(self): + return self.__readfile(".map") + + def get_bin(self): + return self.__readfile(".bin") + + def copy_policy_file(self, suffix, destdir): + spolfile = self.get_filename(suffix) + dpolfile = destdir + "/" + self.get_filename(suffix,"",dotted=True) + try: + shutil.copyfile(spolfile, dpolfile) + except Exception, e: + log.error("Could not copy policy file %s to %s: %s" % + (spolfile, dpolfile, str(e))) + return -xsconstants.XSERR_FILE_ERROR + return xsconstants.XSERR_SUCCESS + + # + # DOM-related functions + # + + def policy_dom_get(self, parent, key, createit=False): + for node in parent.childNodes: + if node.nodeType == Node.ELEMENT_NODE: + if node.nodeName == key: + return node + if createit: + self.dom_create_node(parent, key) + return self.policy_dom_get(parent, key) + + def dom_create_node(self, parent, newname, value=" "): + xml = "<"+newname+">"+ value +"" + frag = minidom.parseString(xml) + frag.childNodes[0].nodeType = Node.DOCUMENT_FRAGMENT_NODE + parent.appendChild(frag.childNodes[0]) + return frag.childNodes[0] + + def dom_get_node(self, path, createit=False): + node = None + parts = path.split("/") + doc = self.get_dom() + if len(parts) > 0: + node = self.policy_dom_get(doc.documentElement, parts[0]) + if node: + i = 1 + while i < len(parts): + _node = self.policy_dom_get(node, parts[i], createit) + if not _node: + if not createit: + break + else: + self.dom_create_node(node, parts[i]) + _node = self.policy_dom_get(node, parts[i]) + node = _node + i += 1 + return node + + # + # Header-related functions + # + def policy_dom_get_header_subnode(self, nodename): + node = self.dom_get_node("PolicyHeader/%s" % nodename) + return node + + def policy_dom_get_hdr_item(self, name, default=""): + node = self.policy_dom_get_header_subnode(name) + if node and len(node.childNodes) > 0: + return node.childNodes[0].nodeValue + return default + + def policy_dom_get_frompol_item(self, name, default="", createit=False): + node = self.dom_get_node("PolicyHeader/FromPolicy",createit) + if node: + node = self.policy_dom_get(node, name, createit) + if node and len(node.childNodes) > 0: + return node.childNodes[0].nodeValue + return default + + def get_header_fields_map(self): + header = { + 'policyname' : self.policy_dom_get_hdr_item("PolicyName"), + 'policyurl' : self.policy_dom_get_hdr_item("PolicyUrl"), + 'reference' : self.policy_dom_get_hdr_item("Reference"), + 'date' : self.policy_dom_get_hdr_item("Date"), + 'namespaceurl' : self.policy_dom_get_hdr_item("NameSpaceUrl"), + 'version' : self.policy_dom_get_hdr_item("Version") + } + return header + + def set_frompolicy_name(self, name): + """ For tools to adapt the header of the policy """ + node = self.dom_get_node("PolicyHeader/FromPolicy/PolicyName", + createit=True) + node.childNodes[0].nodeValue = name + + def set_frompolicy_version(self, version): + """ For tools to adapt the header of the policy """ + node = self.dom_get_node("PolicyHeader/FromPolicy/Version", + createit=True) + node.childNodes[0].nodeValue = version + + def set_policy_name(self, name): + """ For tools to adapt the header of the policy """ + node = self.dom_get_node("PolicyHeader/PolicyName") + node.childNodes[0].nodeValue = name + + def set_policy_version(self, version): + """ For tools to adapt the header of the policy """ + node = self.dom_get_node("PolicyHeader/Version") + node.childNodes[0].nodeValue = version + + def update_frompolicy(self, curpol): + self.set_frompolicy_name(curpol.policy_dom_get_hdr_item("PolicyName")) + version = curpol.policy_dom_get_hdr_item("Version") + self.set_frompolicy_version(version) + (maj, minor) = self.__convVersionToTuple(version) + self.set_policy_version("%s.%s" % (maj, minor+1)) + + # + # Get all types that are part of a node + # + + def policy_get_types(self, node): + strings = [] + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == "Type" and \ + len(node.childNodes[i].childNodes) > 0: + strings.append(node.childNodes[i].childNodes[0].nodeValue) + i += 1 + return strings + + # + # Simple Type Enforcement-related functions + # + + def policy_get_stetypes_node(self): + node = self.dom_get_node("SimpleTypeEnforcement/SimpleTypeEnforcementTypes") + return node + + def policy_get_stetypes_types(self): + strings = [] + node = self.policy_get_stetypes_node() + if node: + strings = self.policy_get_types(node) + return strings + + # + # Chinese Wall Type-related functions + # + + def policy_get_chwall_types(self): + strings = [] + node = self.dom_get_node("ChineseWall/ChineseWallTypes") + if node: + strings = self.policy_get_types(node) + return strings + + def policy_get_chwall_cfses(self): + cfs = [] + node = self.dom_get_node("ChineseWall/ConflictSets") + if node: + i = 0 + while i < len(node.childNodes): + _cfs = {} + if node.childNodes[i].nodeName == "Conflict": + _cfs['name'] = node.childNodes[i].getAttribute('name') + _cfs['chws'] = self.policy_get_types(node.childNodes[i]) + cfs.append(_cfs) + i += 1 + return cfs + + def policy_get_chwall_cfses_names_sorted(self): + """ + Return the list of all conflict set names in alphabetical + order. + """ + cfs_names = [] + node = self.dom_get_node("ChineseWall/ConflictSets") + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == "Conflict": + n = node.childNodes[i].getAttribute('name') + #it better have a name! + if n: + cfs_names.append(n) + i += 1 + cfs_names.sort() + return cfs_names + + # + # Subject Label-related functions + # + + def policy_get_bootstrap_vmlabel(self): + node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels") + if node: + vmlabel = node.getAttribute("bootstrap") + return vmlabel + + # Get the names of all virtual machine labels; returns an array + def policy_get_virtualmachinelabel_names(self): + strings = [] + node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels") + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == "VirtualMachineLabel": + name = self.policy_dom_get(node.childNodes[i], "Name") + if len(name.childNodes) > 0: + strings.append(name.childNodes[0].nodeValue) + i += 1 + return strings + + def policy_sort_virtualmachinelabel_names(self, vmnames): + bootstrap = self.policy_get_bootstrap_vmlabel() + if bootstrap not in vmnames: + raise SecurityError(-xsconstants.XSERR_POLICY_INCONSISTENT) + vmnames.remove(bootstrap) + vmnames.sort() + vmnames.insert(0, bootstrap) + if ACM_LABEL_UNLABELED in vmnames: + vmnames.remove(ACM_LABEL_UNLABELED) + vmnames.insert(0, ACM_LABEL_UNLABELED) + return vmnames + + def policy_get_virtualmachinelabel_names_sorted(self): + """ Get a sorted list of VMlabel names. The bootstrap VM's + label will be the first one in that list, followed + by an alphabetically sorted list of VM label names """ + vmnames = self.policy_get_virtualmachinelabel_names() + res = self.policy_sort_virtualmachinelabel_names(vmnames) + if res[0] != ACM_LABEL_UNLABELED: + res.insert(0, ACM_LABEL_UNLABELED) + return res + + def policy_get_virtualmachinelabels(self): + """ Get a list of all virtual machine labels in this policy """ + res = [] + node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels") + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == "VirtualMachineLabel": + name = self.policy_dom_get(node.childNodes[i], "Name") + if len(name.childNodes) > 0: + _res = {} + _res['type'] = xsconstants.ACM_LABEL_VM + _res['name'] = name.childNodes[0].nodeValue + stes = self.policy_dom_get(node.childNodes[i], + "SimpleTypeEnforcementTypes") + if stes: + _res['stes'] = self.policy_get_types(stes) + else: + _res['stes'] = [] + chws = self.policy_dom_get(node.childNodes[i], + "ChineseWallTypes") + if chws: + _res['chws'] = self.policy_get_types(chws) + else: + _res['chws'] = [] + res.append(_res) + i += 1 + return res + + def policy_get_stes_of_vmlabel(self, vmlabel): + """ Get a list of all STEs of a given VMlabel """ + return self.__policy_get_stes_of_labeltype(vmlabel, + "/SubjectLabels", "VirtualMachineLabel") + + def policy_get_stes_of_resource(self, reslabel): + """ Get a list of all resources of a given VMlabel """ + return self.__policy_get_stes_of_labeltype(reslabel, + "/ObjectLabels", "ResourceLabel") + + def __policy_get_stes_of_labeltype(self, label, path, labeltype): + node = self.dom_get_node("SecurityLabelTemplate" + path) + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == labeltype: + name = self.policy_dom_get(node.childNodes[i], "Name") + if len(name.childNodes) > 0 and \ + name.childNodes[0].nodeValue == label: + stes = self.policy_dom_get(node.childNodes[i], + "SimpleTypeEnforcementTypes") + if not stes: + return [] + return self.policy_get_types(stes) + i += 1 + return [] + + def policy_check_vmlabel_against_reslabels(self, vmlabel, resources): + """ + Check whether the given vmlabel is compatible with the given + resource labels. Do this by getting all the STEs of the + vmlabel and the STEs of the resources. Any STE type of the + VM label must match an STE type of the resource. + """ + vm_stes = self.policy_get_stes_of_vmlabel(vmlabel) + if len(vm_stes) == 0: + return False + for res in resources: + res_stes = self.policy_get_stes_of_resource(res) + if len(res_stes) == 0 or \ + len( set(res_stes).intersection( set(vm_stes) ) ) == 0: + return False + return True + + def __policy_get_label_translation_map(self, path, labeltype): + res = {} + node = self.dom_get_node("SecurityLabelTemplate/" + path) + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == labeltype: + name = self.policy_dom_get(node.childNodes[i], "Name") + from_name = name.getAttribute("from") + if from_name and len(name.childNodes) > 0: + res.update({from_name : name.childNodes[0].nodeValue}) + i += 1 + return res + + def policy_get_vmlabel_translation_map(self): + """ + Get a dictionary of virtual machine mappings from their + old VMlabel name to the new VMlabel name. + """ + return self.__policy_get_label_translation_map("SubjectLabels", + "VirtualMachineLabel") + + def policy_get_reslabel_translation_map(self): + """ + Get a dictionary of resource mappings from their + old resource label name to the new resource label name. + """ + return self.__policy_get_label_translation_map("ObjectLabels", + "ResourceLabel") + + # + # Object Label-related functions + # + def policy_get_resourcelabel_names(self): + """ + Get the names of all resource labels in an array but + only those that actually have types + """ + strings = [] + node = self.dom_get_node("SecurityLabelTemplate/ObjectLabels") + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == "ResourceLabel": + name = self.policy_dom_get(node.childNodes[i], "Name") + stes = self.policy_dom_get(node.childNodes[i], + "SimpleTypeEnforcementTypes") + if stes and len(name.childNodes) > 0: + strings.append(name.childNodes[0].nodeValue) + i += 1 + return strings + + def policy_get_resourcelabels(self): + """ + Get all information about all resource labels of this policy. + """ + res = [] + node = self.dom_get_node("SecurityLabelTemplate/ObjectLabels") + if node: + i = 0 + while i < len(node.childNodes): + if node.childNodes[i].nodeName == "ResourceLabel": + name = self.policy_dom_get(node.childNodes[i], "Name") + if len(name.childNodes) > 0: + _res = {} + _res['type'] = xsconstants.ACM_LABEL_RES + _res['name'] = name.childNodes[0].nodeValue + stes = self.policy_dom_get(node.childNodes[i], + "SimpleTypeEnforcementTypes") + if stes: + _res['stes'] = self.policy_get_types(stes) + else: + _res['stes'] = [] + _res['chws'] = [] + res.append(_res) + i += 1 + return res + + + def policy_find_reslabels_with_stetype(self, stetype): + """ + Find those resource labels that hold a given STE type. + """ + res = [] + reslabels = self.policy_get_resourcelabels() + for resl in reslabels: + if stetype in resl['stes']: + res.append(resl['name']) + return res + + + def toxml(self): + dom = self.get_dom() + if dom: + return dom.toxml() + return None + + def hash(self): + """ Calculate a SAH1 hash of the XML policy """ + return sha.sha(self.toxml()) + + def save(self): + ### Save the XML policy into a file ### + rc = -xsconstants.XSERR_FILE_ERROR + name = self.get_name() + if name: + path = self.path_from_policy_name(name) + if path: + f = open(path, 'w') + if f: + try: + try: + f.write(self.toxml()) + rc = 0 + except: + pass + finally: + f.close() + return rc + + def __write_to_file(self, suffix, data): + #write the data into a file with the given suffix + f = open(self.get_filename(suffix),"w") + if f: + try: + try: + f.write(data) + except Exception, e: + log.error("Error writing file: %s" % str(e)) + return -xsconstants.XSERR_FILE_ERROR + finally: + f.close() + else: + return -xsconstants.XSERR_FILE_ERROR + return xsconstants.XSERR_SUCCESS + + + def compile(self): + rc = self.save() + if rc == 0: + rc, mapfile, bin_pol = self.policy_create_map_and_bin() + + if rc == 0: + try: + security.mapfile_lock() + + rc = self.__write_to_file(".map", mapfile) + if rc != 0: + log.error("Error writing map file") + + finally: + security.mapfile_unlock() + + if rc == 0: + rc = self.__write_to_file(".bin", bin_pol) + if rc != 0: + log.error("Error writing binary policy file") + return rc + + def loadintohv(self): + """ + load this policy into the hypervisor + if successful,the policy's flags will indicate that the + policy is the one loaded into the hypervisor + """ + if not self.isloaded(): + (ret, output) = commands.getstatusoutput( + security.xensec_tool + + " loadpolicy " + + self.get_filename(".bin")) + if ret != 0: + return -xsconstants.XSERR_POLICY_LOAD_FAILED + return xsconstants.XSERR_SUCCESS + + def isloaded(self): + """ + Determine whether this policy is the active one. + """ + if self.get_name() == security.get_active_policy_name(): + return True + return False + + def destroy(self): + """ + Destroy the policy including its binary, mapping and + XML files. + This only works if the policy is not the one that's loaded + """ + if self.isloaded(): + return -xsconstants.XSERR_POLICY_LOADED + files = [ self.get_filename(".map",""), + self.get_filename(".bin","") ] + for f in files: + try: + os.unlink(f) + except: + pass + if self.xendacmpolicy: + self.xendacmpolicy.destroy() + XSPolicy.destroy(self) + return xsconstants.XSERR_SUCCESS + + def policy_get_domain_label(self, domid): + """ + Given a domain's ID, retrieve the label it has using + its ssidref for reverse calculation. + """ + try: + mgmt_dom = security.get_ssid(domid) + except: + return "" + return self.policy_get_domain_label_by_ssidref(int(mgmt_dom[3])) + + def policy_get_domain_label_by_ssidref(self, ssidref): + """ Given an ssidref, find the corresponding VM label """ + chwall_ref = ssidref & 0xffff + try: + allvmtypes = self.policy_get_virtualmachinelabel_names_sorted() + except: + return None + return allvmtypes[chwall_ref] + + def policy_get_domain_label_formatted(self, domid): + label = self.policy_get_domain_label(domid) + if label == "": + label = ACM_LABEL_UNLABELED + return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, self.get_name(), label) + + def policy_get_domain_label_by_ssidref_formatted(self, ssidref): + label = self.policy_get_domain_label_by_ssidref(ssidref) + if label == "": + return "" + return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, self.get_name(), label) + + def policy_create_map_and_bin(self): + """ + Create the policy's map and binary files -- compile the policy. + """ + def roundup8(len): + return ((len + 7) & ~7) + + rc = xsconstants.XSERR_SUCCESS + mapfile = "" + primpolcode = ACM_POLICY_UNDEFINED + secpolcode = ACM_POLICY_UNDEFINED + unknown_ste = set() + unknown_chw = set() + unlabeled_ste = "__NULL_LABEL__" + unlabeled_chw = "__NULL_LABEL__" + + rc = self.validate() + if rc: + return rc, "", "" + + stes = self.policy_get_stetypes_types() + if stes: + stes.sort() + + chws = self.policy_get_chwall_types() + if chws: + chws.sort() + + vms = self.policy_get_virtualmachinelabels() + bootstrap = self.policy_get_bootstrap_vmlabel() + + vmlabels = self.policy_get_virtualmachinelabel_names_sorted() + if bootstrap not in vmlabels: + log.error("Bootstrap label '%s' not found among VM labels '%s'." \ + % (bootstrap, vmlabels)) + return -xsconstants.XSERR_POLICY_INCONSISTENT, "", "" + + vms_with_chws = [] + chws_by_vm = { ACM_LABEL_UNLABELED : [] } + for v in vms: + if v.has_key("chws"): + vms_with_chws.append(v["name"]) + chws_by_vm[v["name"]] = v["chws"] + + + if bootstrap in vms_with_chws: + vms_with_chws.remove(bootstrap) + vms_with_chws.sort() + vms_with_chws.insert(0, bootstrap) + else: + vms_with_chws.sort() + + if ACM_LABEL_UNLABELED in vms_with_chws: + unlabeled_chw = ACM_LABEL_UNLABELED + vms_with_chws.remove(ACM_LABEL_UNLABELED) ; # @1 + + vms_with_stes = [] + stes_by_vm = { ACM_LABEL_UNLABELED : [] } + for v in vms: + if v.has_key("stes"): + vms_with_stes.append(v["name"]) + stes_by_vm[v["name"]] = v["stes"] + + if bootstrap in vms_with_stes: + vms_with_stes.remove(bootstrap) + vms_with_stes.sort() + vms_with_stes.insert(0, bootstrap) + else: + vms_with_stes.sort() + + if ACM_LABEL_UNLABELED in vms_with_stes: + unlabeled_ste = ACM_LABEL_UNLABELED + vms_with_stes.remove(ACM_LABEL_UNLABELED) ; # @2 + + resnames = self.policy_get_resourcelabel_names() + resnames.sort() + stes_by_res = {} + res = self.policy_get_resourcelabels() + for r in res: + if r.has_key("stes"): + stes_by_res[r["name"]] = r["stes"] + + if ACM_LABEL_UNLABELED in resnames: + resnames.remove(ACM_LABEL_UNLABELED) + + # check for duplicate labels + if len(vmlabels) != len(set(vmlabels)) or \ + len(resnames) != len(set(resnames)) or \ + len(stes) != len(set(stes)) or \ + len(chws) != len(set(chws)): + return -xsconstants.XSERR_POLICY_HAS_DUPLICATES, "", "" + + max_chw_ssids = 1 + len(vms_with_chws) + max_chw_types = 1 + len(vms_with_chws) + max_ste_ssids = 1 + len(vms_with_stes) + len(resnames) + max_ste_types = 1 + len(vms_with_stes) + len(resnames) + + mapfile = "POLICYREFERENCENAME %s\n" % self.get_name() + mapfile += "MAGIC %08x\n" % ACM_MAGIC + mapfile += "POLICFILE %s\n" % \ + self.path_from_policy_name(self.get_name()) + mapfile += "BINARYFILE %s\n" % self.get_filename(".bin") + mapfile += "MAX-CHWALL-TYPES %08x\n" % len(chws) + mapfile += "MAX-CHWALL-SSIDS %08x\n" % max_chw_ssids + mapfile += "MAX-CHWALL-LABELS %08x\n" % max_chw_ssids + mapfile += "MAX-STE-TYPES %08x\n" % len(stes) + mapfile += "MAX-STE-SSIDS %08x\n" % max_ste_ssids + mapfile += "MAX-STE-LABELS %08x\n" % max_ste_ssids + mapfile += "\n" + + if chws: + mapfile += \ + "PRIMARY CHWALL\n" + primpolcode = ACM_CHINESE_WALL_POLICY + if stes: + mapfile += \ + "SECONDARY STE\n" + else: + mapfile += \ + "SECONDARY NULL\n" + secpolcode = ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY + else: + if stes: + mapfile += \ + "PRIMARY STE\n" + primpolcode = ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY + mapfile += \ + "SECONDARY NULL\n" + + mapfile += "\n" + + if len(vms_with_chws) > 0: + mapfile += \ + "LABEL->SSID ANY CHWALL %-20s %x\n" % \ + (unlabeled_chw, 0) + i = 0 + for v in vms_with_chws: + mapfile += \ + "LABEL->SSID VM CHWALL %-20s %x\n" % \ + (v, i+1) + i += 1 + mapfile += "\n" + + if len(vms_with_stes) > 0 or len(resnames) > 0: + mapfile += \ + "LABEL->SSID ANY STE %-20s %08x\n" % \ + (unlabeled_ste, 0) + i = 0 + for v in vms_with_stes: + mapfile += \ + "LABEL->SSID VM STE %-20s %x\n" % (v, i+1) + i += 1 + j = 0 + for r in resnames: + mapfile += \ + "LABEL->SSID RES STE %-20s %x\n" % (r, j+i+1) + j += 1 + mapfile += "\n" + + if vms_with_chws: + mapfile += \ + "SSID->TYPE CHWALL %08x\n" % 0 + i = 1 + for v in vms_with_chws: + mapfile += \ + "SSID->TYPE CHWALL %08x" % i + for c in chws_by_vm[v]: + mapfile += " %s" % c + mapfile += "\n" + i += 1 + mapfile += "\n" + + if len(vms_with_stes) > 0 or len(resnames) > 0: + mapfile += \ + "SSID->TYPE STE %08x\n" % 0 + i = 1 + for v in vms_with_stes: + mapfile += \ + "SSID->TYPE STE %08x" % i + for s in stes_by_vm[v]: + mapfile += " %s" % s + mapfile += "\n" + i += 1 + + for r in resnames: + mapfile += \ + "SSID->TYPE STE %08x" % i + for s in stes_by_res[r]: + mapfile += " %s" % s + mapfile += "\n" + i += 1 + mapfile += "\n" + + if chws: + i = 0 + while i < len(chws): + mapfile += \ + "TYPE CHWALL %-20s %d\n" % (chws[i], i) + i += 1 + mapfile += "\n" + if stes: + i = 0 + while i < len(stes): + mapfile += \ + "TYPE STE %-20s %d\n" % (stes[i], i) + i += 1 + mapfile += "\n" + + mapfile += "\n" + + # Build header with policy name + length = roundup8(4 + len(self.get_name()) + 1) + polname = self.get_name(); + pr_bin = struct.pack("!i", len(polname)+1) + pr_bin += polname; + while len(pr_bin) < length: + pr_bin += "\x00" + + # Build chinese wall part + vms_with_chws.insert(0, ACM_LABEL_UNLABELED) + + cfses_names = self.policy_get_chwall_cfses_names_sorted() + cfses = self.policy_get_chwall_cfses() + + chwformat = "!iiiiiiiii" + max_chw_cfs = len(cfses) + chw_ssid_offset = struct.calcsize(chwformat) + chw_confset_offset = chw_ssid_offset + \ + 2 * len(chws) * max_chw_types + chw_running_types_offset = 0 + chw_conf_agg_offset = 0 + + chw_bin = struct.pack(chwformat, + ACM_CHWALL_VERSION, + ACM_CHINESE_WALL_POLICY, + len(chws), + max_chw_ssids, + max_chw_cfs, + chw_ssid_offset, + chw_confset_offset, + chw_running_types_offset, + chw_conf_agg_offset) + chw_bin_body = "" + + # VMs that are listed and their chinese walls + for v in vms_with_chws: + for c in chws: + unknown_chw |= (set(chws_by_vm[v]) - set(chws)) + if c in chws_by_vm[v]: + chw_bin_body += struct.pack("!h",1) + else: + chw_bin_body += struct.pack("!h",0) + + # Conflict sets -- they need to be processed in alphabetical order + for cn in cfses_names: + if cn == "" or cn is None: + return -xsconstants.XSERR_BAD_CONFLICTSET, "", "" + i = 0 + while i < len(cfses): + if cfses[i]['name'] == cn: + conf = cfses[i]['chws'] + break + i += 1 + for c in chws: + if c in conf: + chw_bin_body += struct.pack("!h",1) + else: + chw_bin_body += struct.pack("!h",0) + del cfses[i] + + if len(cfses) != 0: + return -xsconstants.XSERR_BAD_CONFLICTSET, "", "" + + chw_bin += chw_bin_body + + while len(chw_bin) < roundup8(len(chw_bin)): + chw_bin += "\x00" + + # Build STE part + vms_with_stes.insert(0, ACM_LABEL_UNLABELED) # Took out in @2 + + steformat="!iiiii" + ste_bin = struct.pack(steformat, + ACM_STE_VERSION, + ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + len(stes), + max_ste_types, + struct.calcsize(steformat)) + ste_bin_body = "" + if stes: + # VMs that are listed and their STE types + for v in vms_with_stes: + unknown_ste |= (set(stes_by_vm[v]) - set(stes)) + for s in stes: + if s in stes_by_vm[v]: + ste_bin_body += struct.pack("!h",1) + else: + ste_bin_body += struct.pack("!h",0) + for r in resnames: + unknown_ste |= (set(stes_by_res[r]) - set(stes)) + for s in stes: + if s in stes_by_res[r]: + ste_bin_body += struct.pack("!h",1) + else: + ste_bin_body += struct.pack("!h",0) + + ste_bin += ste_bin_body; + + while len(ste_bin) < roundup8(len(ste_bin)): + ste_bin += "\x00" + + #Write binary header: + headerformat="!iiiiiiiiii20s" + totallen_bin = struct.calcsize(headerformat) + \ + len(pr_bin) + len(chw_bin) + len(ste_bin) + polref_offset = struct.calcsize(headerformat) + primpoloffset = polref_offset + len(pr_bin) + if primpolcode == ACM_CHINESE_WALL_POLICY: + secpoloffset = primpoloffset + len(chw_bin) + elif primpolcode == ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY: + secpoloffset = primpoloffset + len(ste_bin) + else: + secpoloffset = primpoloffset + + (major, minor) = self.getVersionTuple() + hdr_bin = struct.pack(headerformat, + ACM_MAGIC, + ACM_POLICY_VERSION, + totallen_bin, + polref_offset, + primpolcode, + primpoloffset, + secpolcode, + secpoloffset, + major, minor, + self.hash().digest()) + + all_bin = array.array('B') + for s in [ hdr_bin, pr_bin, chw_bin, ste_bin ]: + for c in s: + all_bin.append(ord(c)) + + log.info("Compiled policy: rc = %s" % hex(rc)) + if len(unknown_ste) > 0: + log.info("The following STEs in VM/res labels were unknown:" \ + " %s" % list(unknown_ste)) + rc = -xsconstants.XSERR_BAD_LABEL + if len(unknown_chw) > 0: + log.info("The following Ch. Wall types in labels were unknown:" \ + " %s" % list(unknown_chw)) + rc = -xsconstants.XSERR_BAD_LABEL + return rc, mapfile, all_bin.tostring() + + def validate_enforced_policy_hash(self): + """ verify that the policy hash embedded in the binary policy + that is currently enforce matches the one of the XML policy. + """ + if self.hash().digest() != self.get_enforced_policy_hash(): + raise Exception('Policy hashes do not match') + + def get_enforced_policy_hash(self): + binpol = self.get_enforced_binary() + headerformat="!iiiiiiiiii20s" + res = struct.unpack(headerformat, binpol[:60]) + if len(res) >= 11: + return res[10] + return None + + def get_enforced_binary(self): + rc, binpol = security.hv_get_policy() + if rc != 0: + raise SecurityError(-xsconstants.XSERR_HV_OP_FAILED) + return binpol + + get_enforced_binary = classmethod(get_enforced_binary) diff --git a/tools/python/xen/util/asserts.py b/tools/python/xen/util/asserts.py new file mode 100644 index 0000000..dd3cb7f --- /dev/null +++ b/tools/python/xen/util/asserts.py @@ -0,0 +1,27 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +def isCharConvertible(c): + """Assert that the given value is convertible to a character using the %c + conversion. This implies that c is either an integer, or a character + (i.e. a string of length 1). + """ + + assert (isinstance(c, int) or + (isinstance(c, str) and + len(c) == 1)), "%s is not convertible to a character" % c diff --git a/tools/python/xen/util/auxbin.py b/tools/python/xen/util/auxbin.py new file mode 100644 index 0000000..75e38a8 --- /dev/null +++ b/tools/python/xen/util/auxbin.py @@ -0,0 +1,62 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005-2006 XenSource Inc. +#============================================================================ + + +LIB_32 = "/usr/lib" +LIB_64 = "/usr/lib64" +LIB_BIN_SUFFIX = "xen/bin" + +## The architectures on which the LIB_64 directory is used. This +# deliberately excludes ia64 and ppc64, and Solaris. +LIB_64_ARCHS = [ 'x86_64', 's390x', 'sparc64'] + + +import os +import os.path +import sys + + +def execute(exe, args = None): + exepath = pathTo(exe) + a = [ exepath ] + if args: + a.extend(args) + os.execv(exepath, a) + + +def pathTo(exe): + return os.path.join(path(), exe) + + +def path(): + return os.path.join(libpath(), LIB_BIN_SUFFIX) + + +def libpath(): + machine = os.uname()[4] + if sys.argv[0] != '-c': + prefix = os.path.dirname(os.path.dirname(sys.argv[0])) + path = os.path.join(prefix, os.path.basename(LIB_64)) + if machine in LIB_64_ARCHS and os.path.exists(path): + return path + path = os.path.join(prefix, os.path.basename(LIB_32)) + if os.path.exists(path): + return path + if machine in LIB_64_ARCHS and os.path.exists(LIB_64): + return LIB_64 + else: + return LIB_32 diff --git a/tools/python/xen/util/blkif.py b/tools/python/xen/util/blkif.py new file mode 100644 index 0000000..7493577 --- /dev/null +++ b/tools/python/xen/util/blkif.py @@ -0,0 +1,107 @@ +import os +import re +import string + +def expand_dev_name(name): + if not name: + return name + if re.match( '^/', name ): + return name + else: + return '/dev/' + name + +def blkdev_name_to_number(name): + """Take the given textual block-device name (e.g., '/dev/sda1', + 'hda') and return the device number used by the OS. """ + + n = expand_dev_name(name) + + devname = 'virtual-device' + devnum = None + + try: + return (devname, os.stat(n).st_rdev) + except Exception, ex: + pass + + scsi_major = [ 8, 65, 66, 67, 68, 69, 70, 71, 128, 129, 130, 131, 132, 133, 134, 135 ] + if re.match( '/dev/sd[a-z]([1-9]|1[0-5])?$', n): + major = scsi_major[(ord(n[7:8]) - ord('a')) / 16] + minor = ((ord(n[7:8]) - ord('a')) % 16) * 16 + int(n[8:] or 0) + devnum = major * 256 + minor + elif re.match( '/dev/sd[a-i][a-z]([1-9]|1[0-5])?$', n): + major = scsi_major[((ord(n[7:8]) - ord('a') + 1) * 26 + (ord(n[8:9]) - ord('a'))) / 16 ] + minor = (((ord(n[7:8]) - ord('a') + 1 ) * 26 + (ord(n[8:9]) - ord('a'))) % 16) * 16 + int(n[9:] or 0) + devnum = major * 256 + minor + elif re.match( '/dev/hd[a-t]([1-9]|[1-5][0-9]|6[0-3])?', n): + ide_majors = [ 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 ] + major = ide_majors[(ord(n[7:8]) - ord('a')) / 2] + minor = ((ord(n[7:8]) - ord('a')) % 2) * 64 + int(n[8:] or 0) + devnum = major * 256 + minor + elif re.match( '/dev/xvd[a-p]([1-9]|1[0-5])?$', n): + devnum = (202 << 8) + ((ord(n[8:9]) - ord('a')) << 4) + int(n[9:] or 0) + elif re.match('/dev/xvd[q-z]([1-9]|1[0-5])?$', n): + devname = 'virtual-device-ext' + devnum = (1 << 28) + ((ord(n[8:9]) - ord('a')) << 8) + int(n[9:] or 0) + elif re.match('/dev/xvd[a-i][a-z]([1-9]|1[0-5])?$', n): + devname = 'virtual-device-ext' + devnum = (1 << 28) + (((ord(n[8:9]) - ord('a') + 1) * 26 + (ord(n[9:10]) - ord('a'))) << 8) + int(n[10:] or 0) + elif re.match( '^(0x)[0-9a-fA-F]+$', name ): + devnum = string.atoi(name, 16) + elif re.match('^[0-9]+$', name): + devnum = string.atoi(name, 10) + + return (devname, devnum) + +def blkdev_segment(name): + """Take the given block-device name (e.g. '/dev/sda1', 'hda') + and return a dictionary { device, start_sector, + nr_sectors, type } + device: Device number of the given partition + start_sector: Index of first sector of the partition + nr_sectors: Number of sectors comprising this partition + type: 'Disk' or identifying name for partition type + """ + val = None + (name, n) = blkdev_name_to_number(name) + if not n is None: + val = { 'device' : n, + 'start_sector' : long(0), + 'nr_sectors' : long(1L<<63), + 'type' : 'Disk' } + return val + +def _parse_uname(uname): + fn = taptype = None + if uname.find(":") != -1: + (typ, fn) = uname.split(":", 1) + if typ == "phy" and not fn.startswith("/"): + fn = "/dev/%s" %(fn,) + if typ == "tap": + (taptype, fn) = fn.split(":", 1) + return (fn, taptype) + +def blkdev_uname_to_file(uname): + """Take a blkdev uname and return the corresponding filename.""" + return _parse_uname(uname)[0] + +def blkdev_uname_to_taptype(uname): + """Take a blkdev uname and return the blktap type.""" + return _parse_uname(uname)[1] + +def mount_mode(name): + mode = None + name = expand_dev_name(name) + lines = os.popen('mount 2>/dev/null').readlines() + exp = re.compile('^' + name + ' .*[\(,]r(?P[ow])[,\)]') + for line in lines: + pm = exp.match(line) + if not pm: continue + mode = pm.group('mode') + break + if mode == 'w': + return mode + if mode == 'o': + mode = 'r' + return mode + diff --git a/tools/python/xen/util/bootloader.py b/tools/python/xen/util/bootloader.py new file mode 100644 index 0000000..d3b2de7 --- /dev/null +++ b/tools/python/xen/util/bootloader.py @@ -0,0 +1,626 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006,2007 International Business Machines Corp. +# Author: Stefan Berger +#============================================================================ + +import re +import os, stat +import tempfile +import shutil +import threading + +from xen.xend.XendLogging import log +from xen.util import mkdir +import xen.util.xsm.xsm as security + +# +# Functions for modifying entries in the bootloader, i.e. adding +# a module to boot the system with a policy. +# + +def get_default_title(): + """ See description in Bootloader class below """ + return __bootloader.get_default_title() + + +def get_boot_policies(): + """ See description in Bootloader class below """ + return __bootloader.get_boot_policies() + + +def add_boot_policy(index, binpolname): + """ See description in Bootloader class below """ + return __bootloader.add_boot_policy(index, binpolname) + + +def rm_policy_from_boottitle(index, unamelist): + """ See description in Bootloader class below """ + return __bootloader.rm_policy_from_boottitle(index, unamelist) + + +def set_kernel_attval(index, att, val): + """ See description in Bootloader class below """ + return __bootloader.set_kernel_attval(index, att, val) + + +def get_kernel_val(index, att): + """ See description in Bootloader class below """ + return __bootloader.get_kernel_val(index, att) + + +def set_boot_policy(title_idx, filename): + boottitles = get_boot_policies() + for key in boottitles.iterkeys(): + boottitles[key] += ".bin" + if boottitles.has_key(title_idx): + rm_policy_from_boottitle(title_idx, [ boottitles[title_idx] ]) + rc = add_boot_policy(title_idx, filename) + return rc + + +def loads_default_policy(filename): + """ Determine whether the given policy is loaded by the default boot title """ + policy = get_default_policy() + if policy: + polfile = policy + ".bin" + if polfile == filename or \ + "/"+polfile == filename: + return True + return False + + +def get_default_policy(): + """ Get the name of the policy loaded by the default boot title """ + title = get_default_title() + policies = get_boot_policies() + return policies.get(title) + + +def set_default_boot_policy(filename): + """ Set the boot policy in the default title to the given name. """ + title = get_default_title() + return set_boot_policy(title, filename) + + +def __is_bootdir_mounted(): + """ + Determine whether the boot partition /boot is mounted or not + """ + rc = False + file = open("/proc/mounts") + for line in file: + tmp = line.split(" ") + if tmp[1] == "/boot": + rc = True + break + return rc + +def get_prefix(): + if __is_bootdir_mounted(): + return "/" + else: + return "/boot/" + + + +class Bootloader: + """ Bootloader class that real bootloader implementations must overwrite """ + def __init__(self): + pass + + def probe(self): + """ Test whether this implementation of a bootloader is supported on the + local system """ + return True + + def get_default_title(self): + """ Get the index (starting with 0) of the default boot title + This number is read from the grub configuration file. + In case of an error '-1' is returned + @rtype: int + @return: the index of the default boot title + """ + return None + + def get_boot_policies(self): + """ Get a dictionary of policies that the system is booting with. + @rtype: dict + @return: dictionary of boot titles where the keys are the + indices of the boot titles + """ + return {} + + def add_boot_policy(self, index, binpolname): + """ Add the binary policy for automatic loading when + booting the system. Add it to the boot title at index + 'index'. + """ + return False + + def rm_policy_from_boottitle(self, index, unamelist): + """ Remove a policy from the given title. A list of possible policies + must be given to detect what module to remove + """ + return False + + def set_kernel_attval(self, index, att, val): + """ + Append an attribut/value pair to the kernel line. + @param index : The index of the title to modify + @param att : The attribute to add + @param val : The value to add. If no value or the special value + '<>' is given, then the attribute will be removed. + If an empty value is given, then only the attribute + is added in the format "att", otherwise "att=val" + is added. + """ + return False + + def get_kernel_val(self, index, att): + """ + Get an attribute's value from the kernel line. + @param index : The index of the title to get the attribute/value from + @param att : The attribute to read the value of + """ + return None + + +class Grub(Bootloader): + """ Implementation for manipulating bootloader entries in grub according + to the 'Bootloader' class interface """ + + def __init__(self): + self.__bootfile_lock = threading.RLock() + self.title_re = re.compile("\s*title\s", re.IGNORECASE) + self.module_re = re.compile("\s+module\s", re.IGNORECASE) + self.policy_re = re.compile(".*\.bin", re.IGNORECASE) + self.kernel_re = re.compile("\s*kernel\s", re.IGNORECASE) + Bootloader.__init__(self) + + def probe(self): + try: + boot_file = self.__get_bootfile() + except: + return False + return True + + + def __get_bootfile(self): + """ Get the name of the bootfile """ + boot_file = "/boot/grub/grub.conf" + alt_boot_file = "/boot/grub/menu.lst" + + if not os.path.isfile(boot_file): + #take alternate boot file instead + boot_file = alt_boot_file + + #follow symlink since menue.lst might be linked to grub.conf + if not os.path.exists(boot_file): + raise IOError("Boot file \'%s\' not found." % boot_file) + + if stat.S_ISLNK(os.lstat(boot_file)[stat.ST_MODE]): + new_name = os.readlink(boot_file) + if new_name[0] == "/": + boot_file = new_name + else: + path = boot_file.split('/') + path[len(path)-1] = new_name + boot_file = '/'.join(path) + if not os.path.exists(boot_file): + raise IOError("Boot file \'%s\' not found." % boot_file) + return boot_file + + + def get_default_title(self): + """ Get the index (starting with 0) of the default boot title + This number is read from the grub configuration file. + In case of an error '-1' is returned + @rtype: int + @return: the index of the default boot title + """ + def_re = re.compile("default", re.IGNORECASE) + default = None + try: + boot_file = self.__get_bootfile() + except: + return default + try: + self.__bootfile_lock.acquire() + grub_fd = open(boot_file) + for line in grub_fd: + line = line.rstrip() + if def_re.match(line): + #remove 'default=' + line = line.lstrip()[8:] + default = int(line) + break + finally: + self.__bootfile_lock.release() + return default + + + def get_boot_policies(self): + """ Get a dictionary of policies that the system is booting with. + @rtype: dict + @return: dictionary of boot titles where the keys are the + indices of the boot titles + """ + policies = {} + within_title = 0 + idx = -1 + try: + boot_file = self.__get_bootfile() + except: + return policies + try: + self.__bootfile_lock.acquire() + + grub_fd = open(boot_file) + for line in grub_fd: + if self.title_re.match(line): + within_title = 1 + idx = idx + 1 + if within_title and self.module_re.match(line): + if self.policy_re.match(line): + start = line.find("module") + pol = line[start+6:] + pol = pol.strip() + if pol[0] == '/': + pol = pol[1:] + if pol[0:5] == "boot/": + pol = pol[5:] + if pol.endswith(".bin"): + pol = pol[:-4] + policies[idx] = pol + finally: + self.__bootfile_lock.release() + return policies + + + def add_boot_policy(self, index, binpolname): + """ Add the binary policy for automatic loading when + booting the system. Add it to the boot title at index + 'index'. + """ + ctr = 0 + module_line = "" + within_title = 0 + found = False + try: + boot_file = self.__get_bootfile() + except: + return False + try: + self.__bootfile_lock.acquire() + grub_fd = open(boot_file) + (tmp_fd, tmp_grub) = tempfile.mkstemp() + for line in grub_fd: + if self.title_re.match(line): + if module_line != "" and not found: + os.write(tmp_fd, module_line) + found = True + + if ctr == index: + within_title = 1 + else: + within_title = 0 + ctr = ctr + 1 + elif within_title and self.module_re.match(line): + start = line.find("module") + l = line[start+6:len(line)] + l = l.lstrip() + if l[0] == '/': + prefix = "/" + else: + prefix = "" + prefix = get_prefix() + module_line = "\tmodule %s%s\n" % (prefix,binpolname) + else: + if module_line != "" and not found: + os.write(tmp_fd, module_line) + found = True + + os.write(tmp_fd, line) + + if module_line != "" and not found: + if ord(line[-1]) not in [ 10 ]: + os.write(tmp_fd, '\n') + os.write(tmp_fd, module_line) + found = True + + shutil.move(boot_file, boot_file+"_save") + shutil.copyfile(tmp_grub, boot_file) + os.close(tmp_fd) + try: + os.remove(tmp_grub) + except: + pass + finally: + self.__bootfile_lock.release() + return found + + + def rm_policy_from_boottitle(self, index, unamelist): + """ Remove a policy from the given title. A list of possible policies + must be given to detect what module to remove + """ + found = False + ctr = 0 + within_title = 0 + + prefix = get_prefix() + namelist = [prefix+name for name in unamelist] + + try: + boot_file = self.__get_bootfile() + except: + return False + try: + self.__bootfile_lock.acquire() + + grub_fd = open(boot_file) + (tmp_fd, tmp_grub) = tempfile.mkstemp() + for line in grub_fd: + omit_line = False + if self.title_re.match(line): + if ctr == index: + within_title = 1 + else: + within_title = 0 + ctr = ctr + 1 + if within_title and self.module_re.match(line): + if self.policy_re.match(line): + start = line.find("module") + pol = line[start+6:len(line)] + pol = pol.strip() + if pol in namelist: + omit_line = True + found = True + if not omit_line: + os.write(tmp_fd, line) + if found: + shutil.move(boot_file, boot_file+"_save") + shutil.copyfile(tmp_grub, boot_file) + os.close(tmp_fd) + try: + os.remove(tmp_grub) + except: + pass + finally: + self.__bootfile_lock.release() + return found + + + def set_kernel_attval(self, index, att, val): + """ + Append an attribut/value pair to the kernel line. + @param index : The index of the title to modify + @param att : The attribute to add + @param val : The value to add. If no value or the special value + '<>' is given, then the attribute will be removed. + If an empty value is given, then only the attribute + is added in the format "att", otherwise "att=val" + is added. + """ + found = False + ctr = 0 + within_title = 0 + try: + boot_file = self.__get_bootfile() + except: + False + try: + self.__bootfile_lock.acquire() + + grub_fd = open(boot_file) + (tmp_fd, tmp_grub) = tempfile.mkstemp() + for line in grub_fd: + if self.title_re.match(line): + if ctr == index: + within_title = 1 + else: + within_title = 0 + ctr = ctr + 1 + if within_title and self.kernel_re.match(line): + nitems = [] + items = line.split(" ") + i = 0 + while i < len(items): + el = items[i].split("=",1) + if el[0] != att: + nitems.append(items[i].rstrip("\n")) + i += 1 + if val == "": + nitems.append("%s" % (att)) + elif val != None and val != "<>": + nitems.append("%s=%s" % (att,val)) + line = " ".join(nitems) + "\n" + os.write(tmp_fd, line) + shutil.move(boot_file, boot_file+"_save") + shutil.copyfile(tmp_grub, boot_file) + os.close(tmp_fd) + try: + os.remove(tmp_grub) + except: + pass + finally: + self.__bootfile_lock.release() + return found + + + def get_kernel_val(self, index, att): + """ + Get an attribute's value from the kernel line. + @param index : The index of the title to get the attribute/value from + @param att : The attribute to read the value of + """ + ctr = 0 + within_title = 0 + try: + boot_file = self.__get_bootfile() + except: + return None + try: + self.__bootfile_lock.acquire() + + grub_fd = open(boot_file) + for line in grub_fd: + if self.title_re.match(line): + if ctr == index: + within_title = 1 + else: + within_title = 0 + ctr = ctr + 1 + if within_title and self.kernel_re.match(line): + line = line.strip() + items = line.split(" ") + i = 0 + while i < len(items): + el = items[i].split("=",1) + if el[0] == att: + if len(el) == 1: + return "<>" + return el[1] + i += 1 + finally: + self.__bootfile_lock.release() + return None # Not found + +class LatePolicyLoader(Bootloader): + """ A fake bootloader file that holds the policy to load automatically + once xend has started up and the Domain-0 label to set. """ + def __init__(self): + self.__bootfile_lock = threading.RLock() + self.PATH = security.security_dir_prefix + self.FILENAME = self.PATH + "/xen_boot_policy" + self.DEFAULT_TITLE = "ANY" + self.POLICY_ATTR = "POLICY" + Bootloader.__init__(self) + + def probe(self): + try: + _dir=os.path.dirname(self.FILENAME) + mkdir.parents(_dir, stat.S_IRWXU) + except: + return False + return True + + def get_default_title(self): + return self.DEFAULT_TITLE + + def get_boot_policies(self): + policies = {} + try: + self.__bootfile_lock.acquire() + + res = self.__loadcontent() + + pol = res.get( self.POLICY_ATTR ) + if pol: + policies.update({ self.DEFAULT_TITLE : pol }) + + finally: + self.__bootfile_lock.release() + + return policies + + def add_boot_policy(self, index, binpolname): + try: + self.__bootfile_lock.acquire() + + res = self.__loadcontent() + if binpolname.endswith(".bin"): + binpolname = binpolname[0:-4] + res[ self.POLICY_ATTR ] = binpolname + self.__writecontent(res) + finally: + self.__bootfile_lock.release() + + return True + + def rm_policy_from_boottitle(self, index, unamelist): + try: + self.__bootfile_lock.acquire() + + res = self.__loadcontent() + if self.POLICY_ATTR in res: + del(res[self.POLICY_ATTR]) + self.__writecontent(res) + finally: + self.__bootfile_lock.release() + + return True + + def set_kernel_attval(self, index, att, val): + try: + self.__bootfile_lock.acquire() + + res = self.__loadcontent() + res[att] = val + self.__writecontent(res) + finally: + self.__bootfile_lock.release() + + return True + + def get_kernel_val(self, index, att): + try: + self.__bootfile_lock.acquire() + + res = self.__loadcontent() + return res.get(att) + finally: + self.__bootfile_lock.release() + + def __loadcontent(self): + res={} + try: + file = open(self.FILENAME) + for line in file: + tmp = line.split("=",1) + if len(tmp) == 2: + res[tmp[0]] = tmp[1].strip() + file.close() + except: + pass + + return res + + def __writecontent(self, items): + rc = True + try: + file = open(self.FILENAME,"w") + if file: + for key, value in items.items(): + file.write("%s=%s\n" % (str(key),str(value))) + file.close() + except: + rc = False + + return rc + + +__bootloader = Bootloader() + +def init(): + global __bootloader + grub = Grub() + if grub.probe() == True: + __bootloader = grub + else: + late = LatePolicyLoader() + if late.probe() == True: + __bootloader = late diff --git a/tools/python/xen/util/bugtool.py b/tools/python/xen/util/bugtool.py new file mode 100644 index 0000000..18569dc --- /dev/null +++ b/tools/python/xen/util/bugtool.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python + +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Copyright (c) 2005, XenSource Ltd. + + +import errno +import getpass +import httplib +import re +import os +import os.path +import StringIO +import sys +import tarfile +import tempfile +import time +import urllib + +import xen.lowlevel.xc + +from xen.xend import encode + + +SERVER = 'bugzilla.xensource.com' +SHOW_BUG_PATTERN = 'http://%s/bugzilla/show_bug.cgi?id=%%d' % SERVER +ATTACH_PATTERN = \ + 'http://%s/bugzilla/attachment.cgi?bugid=%%d&action=enter' % SERVER + +TITLE_RE = re.compile(r'(.*)') + +FILES_TO_SEND = [ '/var/log/' + x for x in + [ 'syslog', 'messages', 'debug', + 'xen/xend-debug.log', 'xen/xenstored-trace.log', + 'xen/xen-hotplug.log', 'xen/xend.log' ] + + [ 'xen/xend.log.%d' % z for z in range(1,6) ] ] +#FILES_TO_SEND = [ ] + + +def main(argv = None): + if argv is None: + argv = sys.argv + + print ''' +This application will collate the Xen dmesg output, details of the hardware +configuration of your machine, information about the build of Xen that you are +using, plus, if you allow it, various logs. + +The information collated can either be posted to a Xen Bugzilla bug (this bug +must already exist in the system, and you must be a registered user there), or +it can be saved as a .tar.bz2 for sending or archiving. + +The collated logs may contain private information, and if you are at all +worried about that, you should exit now, or you should explicitly exclude +those logs from the archive. + +''' + + bugball = [] + + xc = xen.lowlevel.xc.xc() + + def do(n, f): + try: + s = f() + except Exception, exn: + s = str(exn) + bugball.append(string_iterator(n, s)) + + do('xen-dmesg', lambda: xc.readconsolering()) + do('physinfo', lambda: prettyDict(xc.physinfo())) + do('xeninfo', lambda: prettyDict(xc.xeninfo())) + + for filename in FILES_TO_SEND: + if not os.path.exists(filename): + continue + + if yes('Include %s? [Y/n] ' % filename): + bugball.append(file(filename)) + + maybeAttach(bugball) + + if (yes(''' +Do you wish to save these details as a tarball (.tar.bz2)? [Y/n] ''')): + tar(bugball) + + return 0 + + +def maybeAttach(bugball): + if not yes(''' +Do you wish to attach these details to a Bugzilla bug? [Y/n] '''): + return + + bug = int(raw_input('Bug number? ')) + + bug_title = getBugTitle(bug) + + if bug_title == 'Search by bug number' or bug_title == 'Invalid Bug ID': + print >>sys.stderr, 'Bug %d does not exist!' % bug + maybeAttach(bugball) + elif yes('Are you sure that you want to attach to %s? [Y/n] ' % + bug_title): + attach(bug, bugball) + else: + maybeAttach(bugball) + + +def attach(bug, bugball): + username = raw_input('Bugzilla username: ') + password = getpass.getpass('Bugzilla password: ') + + conn = httplib.HTTPConnection(SERVER) + try: + for f in bugball: + send(bug, conn, f, f.name, username, password) + finally: + conn.close() + + +def getBugTitle(bug): + f = urllib.urlopen(SHOW_BUG_PATTERN % bug) + + try: + for line in f: + m = TITLE_RE.search(line) + if m: + return m.group(1) + finally: + f.close() + + raise "Could not find title of bug %d!" % bug + + +def send(bug, conn, fd, filename, username, password): + + print "Attaching %s to bug %d." % (filename, bug) + + headers, data = encode.encode_data( + { 'bugid' : str(bug), + 'action' : 'insert', + 'data' : fd, + 'description' : '%s from %s' % (filename, username), + 'contenttypeselection' : 'text/plain', + 'contenttypemethod' : 'list', + 'ispatch' : '0', + 'GoAheadAndLogIn' : '1', + 'Bugzilla_login' : username, + 'Bugzilla_password' : password, + }) + + conn.request('POST',ATTACH_PATTERN % bug, data, headers) + response = conn.getresponse() + try: + body = response.read() + m = TITLE_RE.search(body) + + if response.status != 200: + print >>sys.stderr, ( + 'Attach failed: %s %s.' % (response.status, response.reason)) + elif not m or m.group(1) != 'Changes Submitted': + print >>sys.stderr, ( + 'Attach failed: got a page titled %s.' % m.group(1)) + else: + print "Attaching %s to bug %d succeeded." % (filename, bug) + finally: + response.close() + + +def tar(bugball): + filename = raw_input('Tarball destination filename? ') + + now = time.time() + + tf = tarfile.open(filename, 'w:bz2') + + try: + for f in bugball: + ti = tarfile.TarInfo(f.name.split('/')[-1]) + if hasattr(f, 'size'): + ti.size = f.size() + else: + ti.size = os.stat(f.name).st_size + + ti.mtime = now + ti.type = tarfile.REGTYPE + ti.uid = 0 + ti.gid = 0 + ti.uname = 'root' + ti.gname = 'root' + + f.seek(0) # If we've added this file to a bug, it will have been + # read once already, so reset it. + tf.addfile(ti, f) + finally: + tf.close() + + print 'Writing tarball %s successful.' % filename + + +def prettyDict(d): + format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()])) + return '\n'.join([format % i for i in d.items()]) + '\n' + + +class string_iterator(StringIO.StringIO): + def __init__(self, name, val): + StringIO.StringIO.__init__(self, val) + self.name = name + + def size(self): + return len(self.getvalue()) + + +def yes(prompt): + yn = raw_input(prompt) + + return len(yn) == 0 or yn.lower()[0] == 'y' + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/python/xen/util/diagnose.py b/tools/python/xen/util/diagnose.py new file mode 100644 index 0000000..cd43614 --- /dev/null +++ b/tools/python/xen/util/diagnose.py @@ -0,0 +1,185 @@ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Copyright (c) 2005-2006 XenSource Inc + + +import re +import socket +import sys + +from xen.xend import sxp + +from xen.xend.XendClient import server +from xen.xend.XendError import XendError +from xen.xend.xenstore.xstransact import xstransact +from xen.xend.server import DevController + +import xen.xend.XendProtocol + + +domain = None +domid = None +deviceClass = None +device = None +frontendPath = None +backendPath = None + + +def diagnose(dom): + global domain + global domid + global dompath + + try: + domain = server.xend.domain(dom) + state = sxp.child_value(domain, 'state') + domid = int(sxp.child_value(domain, 'domid')) + name = sxp.child_value(domain, 'name') + + print "Domain ID is %d." % domid + print "Domain name is %s." % name + + if not state: + raise XendError("Cannot find state") + + if state.find('c') != -1: + print "Domain has crashed." + except socket.error, exn: + print "Cannot contact Xend." + + try: + domid = int(dom) + name = dom + except ValueError: + print \ +"Without Xend, you will have to specify the domain ID, not the domain name." + sys.exit(1) + except xen.xend.XendProtocol.XendError, exn: + print exn + sys.exit(1) + + dompath = '/local/domain/%d' % domid + diagnose_console() + diagnose_devices() + + +def diagnose_console(): + port = xstransact.Read(dompath + '/console/port') + ringref = xstransact.Read(dompath + '/console/ring-ref') + tty = xstransact.Read(dompath + '/console/tty') + + if not port: + print "Console port is missing; Xend has failed." + if not ringref: + print "Console ring-ref is missing; Xend has failed." + if not tty: + print "Console tty is missing; Xenconsoled has failed." + + +def diagnose_devices(): + global deviceClass + global device + global frontendPath + global backendPath + + device_path = dompath + '/device' + + device_classes = xstransact.List(device_path) + + print "Found %d device classes in use." % len(device_classes) + + for dc in device_classes: + deviceClass = dc + device_class_path = device_path + '/' + deviceClass + + devices = xstransact.List(device_class_path) + + print "Found %d %s devices." % (len(devices), deviceClass) + + for d in devices: + device = d + + print "Found device %s, %s." % (deviceClass, device) + + frontendPath = device_class_path + '/' + device + backendPath = xstransact.Read(frontendPath, 'backend') + + if not backendPath: + print ("Cannot find backend path for device %s, %s." % + (deviceClass, device)) + else: + frontend_state = xstransact.Read(frontendPath, 'state') + backend_state = xstransact.Read(backendPath, 'state') + + print "Backend is in state %s." % stateString(backend_state) + print "Frontend is in state %s." % stateString(frontend_state) + + check_for_error(True) + check_for_error(False) + + diagnose_hotplugging() + + +def check_for_error(backend): + if backend: + path = backendPath.replace('backend/', 'error/backend/') + else: + path = frontendPath.replace('device/', 'error/device/') + + err = xstransact.Read(path, 'error') + + if err: + print ("%s for device %s, %s shows error %s." % + (backend and 'Backend' or 'Frontend', deviceClass, device, + err)) + + +def diagnose_hotplugging(): + if deviceClass == 'vbd': + phy = xstransact.Read(backendPath, 'physical-device') + + if phy: + print ('Device %s, %s hotplugging has completed successfully, ' + 'and is connected to physical device %s.' % + (deviceClass, device, phy)) + else: + print ('Device %s, %s hotplugging failed.' % + (deviceClass, device)) + elif deviceClass == 'vif': + handle = xstransact.Read(backendPath, 'handle') + + if handle: + print ('Device %s, %s hotplugging has completed successfully, ' + 'and is using handle %s.' % + (deviceClass, device, handle)) + else: + print ('Device %s, %s hotplugging failed.' % + (deviceClass, device)) + + +def stateString(state): + return state and DevController.xenbusState[int(state)] or '' + + +def main(argv = None): + if argv is None: + argv = sys.argv + + diagnose(argv[1]) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/python/xen/util/dictio.py b/tools/python/xen/util/dictio.py new file mode 100644 index 0000000..4fcebdb --- /dev/null +++ b/tools/python/xen/util/dictio.py @@ -0,0 +1,50 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 International Business Machines Corp. +# Author: Bryan D. Payne +#============================================================================ + + +def dict_read(dictname, filename): + """Loads and returns the dictionary named from + the file. + """ + dict = {} + + # read in the config file + globs = {} + locs = {} + execfile(filename, globs, locs) + + for (k, v) in locs.items(): + if k == dictname: + dict = v + break + + return dict + +def dict_write(dict, dictname, filename): + """Writes to using the name . If the file + contains any other data, it will be overwritten. + """ + prefix = dictname + " = {\n" + suffix = "}\n" + fd = open(filename, "wb") + fd.write(prefix) + for key in dict: + line = " '" + str(key) + "': " + str(dict[key]) + ",\n" + fd.write(line) + fd.write(suffix) + fd.close() diff --git a/tools/python/xen/util/ip.py b/tools/python/xen/util/ip.py new file mode 100644 index 0000000..9133e88 --- /dev/null +++ b/tools/python/xen/util/ip.py @@ -0,0 +1,121 @@ +import os +import re +import socket +import struct +import errno + +##### Networking-related functions + +def get_defaultroute(): + fd = os.popen('/sbin/ip route list 2>/dev/null') + for line in fd.readlines(): + m = re.search('^default via ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) dev ([^ ]*)', + line) + if m: + return [m.group(1), m.group(2)] + return [None, None] + +def get_current_ipaddr(dev='defaultroute'): + """Get the primary IP address for the given network interface. + + dev network interface (default: default route device) + + returns interface address as a string + """ + if dev == 'defaultroute': + dev = get_defaultroute()[1] + if not dev: + return + fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' ) + for line in fd.readlines(): + m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*', + line ) + if m: + return m.group(1) + return None + +def get_current_ipmask(dev='defaultroute'): + """Get the primary IP netmask for a network interface. + + dev network interface (default: default route device) + + returns interface netmask as a string + """ + if dev == 'defaultroute': + dev = get_defaultroute()[1] + if not dev: + return + fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' ) + for line in fd.readlines(): + m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*', + line ) + if m: + return m.group(1) + return None + +def get_current_ipgw(dev='defaultroute'): + """Get the IP gateway for a network interface. + + dev network interface (default: default route device) + + returns gateway address as a string + """ + if dev == 'defaultroute': + return get_defaultroute()[0] + if not dev: + return + fd = os.popen( '/sbin/route -n' ) + for line in fd.readlines(): + m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' + + '\s+\S+\s+\S*G.*' + dev + '.*', line ) + if m: + return m.group(1) + return None + +def inet_aton(addr): + """Convert an IP addr in IPv4 dot notation into an int. + + addr IP address as a string + + returns integer + """ + b = socket.inet_aton(addr) + return struct.unpack('!I', b)[0] + +def inet_ntoa(n): + """Convert an int into an IP addr in IPv4 dot notation. + + n IP address + + returns string + """ + b = struct.pack('!I', n) + return socket.inet_ntoa(b) + +def add_offset_to_ip(addr, offset): + """Add a numerical offset to an IP addr in IPv4 dot notation. + + addr IP address + offset offset to add + + returns new address + """ + n = inet_aton(addr) + n += offset + return inet_ntoa(n) + +def check_subnet( ip, network, netmask ): + """Check if an IP address is in the subnet defined by + a network address and mask'. + + ip IP adress + network network address + netmask network mask + + returns 1 if it is in the subnet, 0 if not + """ + n_ip = inet_aton(ip) + n_net = inet_aton(network) + n_mask = inet_aton(netmask) + return (n_ip & n_mask) == (n_net & n_mask) + diff --git a/tools/python/xen/util/mac.py b/tools/python/xen/util/mac.py new file mode 100644 index 0000000..47dffd8 --- /dev/null +++ b/tools/python/xen/util/mac.py @@ -0,0 +1,11 @@ + +from string import join, split + +def macToString(mac): + return ':'.join(map(lambda x: "%02x" % x, mac)) + +def macFromString(str): + mac = [ int(x, 16) for x in str.split(':') ] + if len(mac) != 6: + raise ValueError("invalid mac: %s" % str) + return mac diff --git a/tools/python/xen/util/mkdir.py b/tools/python/xen/util/mkdir.py new file mode 100644 index 0000000..121e498 --- /dev/null +++ b/tools/python/xen/util/mkdir.py @@ -0,0 +1,44 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006 XenSource Inc. +#============================================================================ + +import errno +import os +import os.path +import stat + + +def parents(dir, perms, enforcePermissions = False): + """ + Ensure that the given directory exists, creating it if necessary, but not + complaining if it's already there. + + @param dir The directory name. + @param perms One of the stat.S_ constants. + @param enforcePermissions Enforce our ownership and the given permissions, + even if the directory pre-existed with different ones. + """ + # Catch the exception here, rather than checking for the directory's + # existence first, to avoid races. + try: + os.makedirs(dir, perms) + except OSError, exn: + if exn.args[0] != errno.EEXIST or not os.path.isdir(dir): + raise + if enforcePermissions: + os.chown(dir, os.geteuid(), os.getegid()) + os.chmod(dir, stat.S_IRWXU) diff --git a/tools/python/xen/util/oshelp.py b/tools/python/xen/util/oshelp.py new file mode 100644 index 0000000..b5a0a2a --- /dev/null +++ b/tools/python/xen/util/oshelp.py @@ -0,0 +1,20 @@ +import fcntl +import os + +def fcntl_setfd_cloexec(file, bool): + f = fcntl.fcntl(file, fcntl.F_GETFD) + if bool: f |= fcntl.FD_CLOEXEC + else: f &= ~fcntl.FD_CLOEXEC + fcntl.fcntl(file, fcntl.F_SETFD) + +def waitstatus_description(st): + if os.WIFEXITED(st): + es = os.WEXITSTATUS(st) + if es: return "exited with nonzero status %i" % es + else: return "exited" + elif os.WIFSIGNALED(st): + s = "died due to signal %i" % os.WTERMSIG(st) + if os.WCOREDUMP(st): s += " (core dumped)" + return s + else: + return "failed with unexpected wait status %i" % st diff --git a/tools/python/xen/util/pci.py b/tools/python/xen/util/pci.py new file mode 100644 index 0000000..aeb25d8 --- /dev/null +++ b/tools/python/xen/util/pci.py @@ -0,0 +1,936 @@ +#!/usr/bin/env python +# +# PCI Device Information Class +# - Helps obtain information about which I/O resources a PCI device needs +# +# Author: Ryan Wilson + +import sys +import os, os.path +import resource +import re +import types +import struct +import time +from xen.util import utils + +PROC_PCI_PATH = '/proc/bus/pci/devices' +PROC_PCI_NUM_RESOURCES = 7 + +SYSFS_PCI_DEVS_PATH = '/bus/pci/devices' +SYSFS_PCI_DEV_RESOURCE_PATH = '/resource' +SYSFS_PCI_DEV_CONFIG_PATH = '/config' +SYSFS_PCI_DEV_IRQ_PATH = '/irq' +SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver' +SYSFS_PCI_DEV_VENDOR_PATH = '/vendor' +SYSFS_PCI_DEV_DEVICE_PATH = '/device' +SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor' +SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device' +SYSFS_PCI_DEV_CLASS_PATH = '/class' +SYSFS_PCIBACK_PATH = '/bus/pci/drivers/pciback/' + +LSPCI_CMD = 'lspci' + +PCI_DEV_REG_EXPRESS_STR = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}."+ \ + r"[0-9a-fA-F]{1}" +PCI_DEV_FORMAT_STR = '%04x:%02x:%02x.%01x' + +DEV_TYPE_PCIe_ENDPOINT = 0 +DEV_TYPE_PCIe_BRIDGE = 1 +DEV_TYPE_PCI_BRIDGE = 2 +DEV_TYPE_PCI = 3 + +PCI_VENDOR_ID = 0x0 +PCI_STATUS = 0x6 +PCI_CLASS_DEVICE = 0x0a +PCI_CLASS_BRIDGE_PCI = 0x0604 + +PCI_HEADER_TYPE = 0x0e +PCI_HEADER_TYPE_MASK = 0x7f +PCI_HEADER_TYPE_NORMAL = 0 +PCI_HEADER_TYPE_BRIDGE = 1 +PCI_HEADER_TYPE_CARDBUS = 2 + +PCI_CAPABILITY_LIST = 0x34 +PCI_CB_BRIDGE_CONTROL = 0x3e +PCI_BRIDGE_CTL_BUS_RESET= 0x40 + +PCI_CAP_ID_EXP = 0x10 +PCI_EXP_FLAGS = 0x2 +PCI_EXP_FLAGS_TYPE = 0x00f0 +PCI_EXP_TYPE_PCI_BRIDGE = 0x7 +PCI_EXP_DEVCAP = 0x4 +PCI_EXP_DEVCAP_FLR = (0x1 << 28) +PCI_EXP_DEVCTL = 0x8 +PCI_EXP_DEVCTL_FLR = (0x1 << 15) + +PCI_CAP_ID_PM = 0x01 +PCI_PM_CTRL = 4 +PCI_PM_CTRL_NO_SOFT_RESET = 0x0004 +PCI_PM_CTRL_STATE_MASK = 0x0003 +PCI_D3hot = 3 + +VENDOR_INTEL = 0x8086 +PCI_CAP_ID_VENDOR_SPECIFIC_CAP = 0x09 +PCI_CLASS_ID_USB = 0x0c03 +PCI_USB_FLRCTRL = 0x4 + +PCI_CAP_ID_AF = 0x13 +PCI_AF_CAPs = 0x3 +PCI_AF_CAPs_TP_FLR = 0x3 +PCI_AF_CTL = 0x4 +PCI_AF_CTL_FLR = 0x1 + +PCI_BAR_0 = 0x10 +PCI_BAR_5 = 0x24 +PCI_BAR_SPACE = 0x01 +PCI_BAR_IO = 0x01 +PCI_BAR_IO_MASK = ~0x03 +PCI_BAR_MEM = 0x00 +PCI_BAR_MEM_MASK = ~0x0f +PCI_STATUS_CAP_MASK = 0x10 +PCI_STATUS_OFFSET = 0x6 +PCI_CAP_OFFSET = 0x34 +MSIX_BIR_MASK = 0x7 +MSIX_SIZE_MASK = 0x7ff + +# Global variable to store information from lspci +lspci_info = None + +#Calculate PAGE_SHIFT: number of bits to shift an address to get the page number +PAGE_SIZE = resource.getpagesize() +PAGE_SHIFT = 0 +t = PAGE_SIZE +while not (t&1): + t>>=1 + PAGE_SHIFT+=1 + +PAGE_MASK=~(PAGE_SIZE - 1) +# Definitions from Linux: include/linux/pci.h +def PCI_DEVFN(slot, func): + return ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +def parse_hex(val): + try: + if isinstance(val, types.StringTypes): + return int(val, 16) + else: + return val + except ValueError: + return None + +def parse_pci_name(pci_name_string): + pci_match = re.match(r"((?P[0-9a-fA-F]{1,4})[:,])?" + \ + r"(?P[0-9a-fA-F]{1,2})[:,]" + \ + r"(?P[0-9a-fA-F]{1,2})[.,]" + \ + r"(?P[0-7])$", pci_name_string) + if pci_match is None: + raise PciDeviceParseError(('Failed to parse pci device name: %s' % + pci_name_string)) + pci_dev_info = pci_match.groupdict('0') + + domain = parse_hex(pci_dev_info['domain']) + bus = parse_hex(pci_dev_info['bus']) + slot = parse_hex(pci_dev_info['slot']) + func = parse_hex(pci_dev_info['func']) + + return (domain, bus, slot, func) + + +def find_sysfs_mnt(): + try: + return utils.find_sysfs_mount() + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'% + (PROC_PCI_PATH, strerr, errno))) + return None + +def get_all_pci_names(): + sysfs_mnt = find_sysfs_mnt() + pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split() + return pci_names + +def get_all_pci_devices(): + pci_devs = [] + for pci_name in get_all_pci_names(): + pci_match = re.match(r"((?P[0-9a-fA-F]{1,4})[:,])?" + \ + r"(?P[0-9a-fA-F]{1,2})[:,]" + \ + r"(?P[0-9a-fA-F]{1,2})[.,]" + \ + r"(?P[0-7])$", pci_name) + if pci_match is None: + raise PciDeviceParseError(('Failed to parse pci device name: %s' % + pci_name)) + pci_dev_info = pci_match.groupdict('0') + domain = parse_hex(pci_dev_info['domain']) + bus = parse_hex(pci_dev_info['bus']) + slot = parse_hex(pci_dev_info['slot']) + func = parse_hex(pci_dev_info['func']) + try: + pci_dev = PciDevice(domain, bus, slot, func) + except: + continue + pci_devs.append(pci_dev) + + return pci_devs + +def create_lspci_info(): + global lspci_info + lspci_info = {} + + # Execute 'lspci' command and parse the result. + # If the command does not exist, lspci_info will be kept blank ({}). + for paragraph in os.popen(LSPCI_CMD + ' -vmm').read().split('\n\n'): + device_name = None + device_info = {} + for line in paragraph.split('\n'): + try: + (opt, value) = line.split(':\t') + if opt == 'Slot': + device_name = PCI_DEV_FORMAT_STR % parse_pci_name(value) + else: + device_info[opt] = value + except: + pass + if device_name is not None: + lspci_info[device_name] = device_info + +def save_pci_conf_space(devs_string): + pci_list = [] + cfg_list = [] + sysfs_mnt = find_sysfs_mnt() + for pci_str in devs_string: + pci_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + pci_str + \ + SYSFS_PCI_DEV_CONFIG_PATH + fd = os.open(pci_path, os.O_RDONLY) + configs = [] + for i in range(0, 256, 4): + configs = configs + [os.read(fd,4)] + os.close(fd) + pci_list = pci_list + [pci_path] + cfg_list = cfg_list + [configs] + return (pci_list, cfg_list) + +def restore_pci_conf_space(pci_cfg_list): + pci_list = pci_cfg_list[0] + cfg_list = pci_cfg_list[1] + for i in range(0, len(pci_list)): + pci_path = pci_list[i] + configs = cfg_list[i] + fd = os.open(pci_path, os.O_WRONLY) + for dw in configs: + os.write(fd, dw) + os.close(fd) + +def find_all_devices_owned_by_pciback(): + sysfs_mnt = find_sysfs_mnt() + pciback_path = sysfs_mnt + SYSFS_PCIBACK_PATH + pci_names = os.popen('ls ' + pciback_path).read() + pci_list = re.findall(PCI_DEV_REG_EXPRESS_STR, pci_names) + dev_list = [] + for pci in pci_list: + (dom, b, d, f) = parse_pci_name(pci) + dev = PciDevice(dom, b, d, f) + dev_list = dev_list + [dev] + return dev_list + +def transform_list(target, src): + ''' src: its element is pci string (Format: xxxx:xx:xx:x). + target: its element is pci string, or a list of pci string. + + If all the elements in src are in target, we remove them from target + and add src into target; otherwise, we remove from target all the + elements that also appear in src. + ''' + result = [] + target_contains_src = True + for e in src: + if not e in target: + target_contains_src = False + break + + if target_contains_src: + result = result + [src] + for e in target: + if not e in src: + result = result + [e] + return result + +def check_FLR_capability(dev_list): + if len(dev_list) == 0: + return [] + + pci_list = [] + pci_dev_dict = {} + for dev in dev_list: + pci_list = pci_list + [dev.name] + pci_dev_dict[dev.name] = dev + + while True: + need_transform = False + for pci in pci_list: + if isinstance(pci, types.StringTypes): + dev = pci_dev_dict[pci] + if dev.bus == 0: + continue + if dev.dev_type == DEV_TYPE_PCIe_ENDPOINT and not dev.pcie_flr: + coassigned_pci_list = dev.find_all_the_multi_functions() + need_transform = True + elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr: + coassigned_pci_list = dev.find_coassigned_devices(True) + del coassigned_pci_list[0] + need_transform = True + + if need_transform: + pci_list = transform_list(pci_list, coassigned_pci_list) + if not need_transform: + break + + if len(pci_list) == 0: + return [] + + for i in range(0, len(pci_list)): + if isinstance(pci_list[i], types.StringTypes): + pci_list[i] = [pci_list[i]] + + # Now every element in pci_list is a list of pci string. + + result = [] + for pci_names in pci_list: + devs = [] + for pci in pci_names: + devs = devs + [pci_dev_dict[pci]] + result = result + [devs] + return result + +def check_mmio_bar(devs_list): + result = [] + + for dev_list in devs_list: + non_aligned_bar_found = False + for dev in dev_list: + if dev.has_non_page_aligned_bar: + non_aligned_bar_found = True + break + if not non_aligned_bar_found: + result = result + [dev_list] + + return result + +class PciDeviceNotFoundError(Exception): + def __init__(self,domain,bus,slot,func): + self.domain = domain + self.bus = bus + self.slot = slot + self.func = func + self.name = PCI_DEV_FORMAT_STR %(domain, bus, slot, func) + + def __str__(self): + return ('PCI Device %s Not Found' % (self.name)) + +class PciDeviceParseError(Exception): + def __init__(self,msg): + self.message = msg + def __str__(self): + return 'Error Parsing PCI Device Info: '+self.message + +class PciDeviceAssignmentError(Exception): + def __init__(self,msg): + self.message = msg + def __str__(self): + return 'pci: impproper device assignment spcified: ' + \ + self.message + +class PciDevice: + def __init__(self, domain, bus, slot, func): + self.domain = domain + self.bus = bus + self.slot = slot + self.func = func + self.name = PCI_DEV_FORMAT_STR % (domain, bus, slot, func) + self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name + SYSFS_PCI_DEV_CONFIG_PATH + self.irq = 0 + self.iomem = [] + self.ioports = [] + self.driver = None + self.vendor = None + self.device = None + self.subvendor = None + self.subdevice = None + self.msix = 0 + self.msix_iomem = [] + self.revision = 0 + self.classcode = None + self.vendorname = "" + self.devicename = "" + self.classname = "" + self.subvendorname = "" + self.subdevicename = "" + self.dev_type = None + self.has_non_page_aligned_bar = False + self.pcie_flr = False + self.pci_af_flr = False + self.detect_dev_info() + self.get_info_from_sysfs() + self.get_info_from_lspci() + + def find_parent(self): + # i.e., /sys/bus/pci/devices/0000:00:19.0 or + # /sys/bus/pci/devices/0000:03:04.0 + path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name + # i.e., ../../../devices/pci0000:00/0000:00:19.0 + # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0 + try: + target = os.readlink(path) + lst = target.split('/') + parent = lst[len(lst)-2] + if parent[0:3] == 'pci': + # We have reached the upmost one. + return None + else: + lst = parent.split(':') + dom = int(lst[0], 16) + bus = int(lst[1], 16) + lst = lst[2] + lst = lst.split('.') + dev = int(lst[0], 16) + func = int(lst[1], 16) + return (dom, bus, dev, func) + except OSError, (errno, strerr): + raise PciDeviceParseError('Can not locate the parent of %s', + self.name) + + def find_the_uppermost_pci_bridge(self): + # Find the uppermost PCI/PCI-X bridge + (dom, b, d, f) = self.find_parent() + dev = dev_parent = PciDevice(dom, b, d, f) + while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE: + parent = dev_parent.find_parent() + if parent is None: + break + (dom, b, d, f) = parent + dev = dev_parent + dev_parent = PciDevice(dom, b, d, f) + return dev + + def find_all_devices_behind_the_bridge(self, ignore_bridge): + sysfs_mnt = find_sysfs_mnt() + self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name + pci_names = os.popen('ls ' + self_path).read() + dev_list = re.findall(PCI_DEV_REG_EXPRESS_STR, pci_names) + + list = [self.name] + for pci_str in dev_list: + (dom, b, d, f) = parse_pci_name(pci_str) + dev = PciDevice(dom, b, d, f) + if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \ + dev.dev_type == DEV_TYPE_PCIe_BRIDGE: + sub_list_including_self = \ + dev.find_all_devices_behind_the_bridge(ignore_bridge) + if ignore_bridge: + del sub_list_including_self[0] + list = list + [sub_list_including_self] + else: + list = list + [dev.name] + return list + + def find_coassigned_devices(self, ignore_bridge = True): + ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X + bridge, and all devices behind it must be co-assigned to the same + guest. + + Parameter: + [ignore_bridge]: if set, the returned result doesn't include + any bridge behind the uppermost PCI/PCI-X bridge. + + Note: The first element of the return value is the uppermost + PCI/PCI-X bridge. If the caller doesn't need the first + element, the caller itself can remove it explicitly. + ''' + dev = self.find_the_uppermost_pci_bridge() + dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge) + dev_list = re.findall(PCI_DEV_REG_EXPRESS_STR, '%s' % dev_list) + return dev_list + + def do_secondary_bus_reset(self, target_bus, devs): + # Save the config spaces of all the devices behind the bus. + (pci_list, cfg_list) = save_pci_conf_space(devs) + + #Do the Secondary Bus Reset + sysfs_mnt = find_sysfs_mnt() + parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \ + target_bus + SYSFS_PCI_DEV_CONFIG_PATH + fd = os.open(parent_path, os.O_RDWR) + os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0) + br_cntl = (struct.unpack('H', os.read(fd, 2)))[0] + # Assert Secondary Bus Reset + os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0) + br_cntl |= PCI_BRIDGE_CTL_BUS_RESET + os.write(fd, struct.pack('H', br_cntl)) + time.sleep(0.200) + # De-assert Secondary Bus Reset + os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0) + br_cntl &= ~PCI_BRIDGE_CTL_BUS_RESET + os.write(fd, struct.pack('H', br_cntl)) + time.sleep(0.200) + os.close(fd) + + # Restore the config spaces + restore_pci_conf_space((pci_list, cfg_list)) + + def do_Dstate_transition(self): + pos = self.find_cap_offset(PCI_CAP_ID_PM) + if pos == 0: + return False + + (pci_list, cfg_list) = save_pci_conf_space([self.name]) + + # Enter D3hot without soft reset + pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL) + pm_ctl |= PCI_PM_CTRL_NO_SOFT_RESET + pm_ctl &= ~PCI_PM_CTRL_STATE_MASK + pm_ctl |= PCI_D3hot + self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl) + time.sleep(0.010) + + # From D3hot to D0 + self.pci_conf_write32(pos + PCI_PM_CTRL, 0) + time.sleep(0.010) + + restore_pci_conf_space((pci_list, cfg_list)) + return True + + def do_vendor_specific_FLR_method(self): + pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP) + if pos == 0: + return + + vendor_id = self.pci_conf_read16(PCI_VENDOR_ID) + if vendor_id != VENDOR_INTEL: + return + + class_id = self.pci_conf_read16(PCI_CLASS_DEVICE) + if class_id != PCI_CLASS_ID_USB: + return + + (pci_list, cfg_list) = save_pci_conf_space([self.name]) + + self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1) + time.sleep(0.010) + + restore_pci_conf_space((pci_list, cfg_list)) + + def do_FLR_for_integrated_device(self): + if not self.do_Dstate_transition(): + self.do_vendor_specific_FLR_method() + + def find_all_the_multi_functions(self): + sysfs_mnt = find_sysfs_mnt() + pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read() + p = self.name + p = p[0 : p.rfind('.')] + '.[0-7]' + funcs = re.findall(p, pci_names) + return funcs + + def find_cap_offset(self, cap): + path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_CONFIG_PATH + + pos = PCI_CAPABILITY_LIST + + try: + fd = os.open(path, os.O_RDONLY) + os.lseek(fd, PCI_STATUS, 0) + status = struct.unpack('H', os.read(fd, 2))[0] + if (status & 0x10) == 0: + # The device doesn't support PCI_STATUS_CAP_LIST + return 0 + + max_cap = 48 + while max_cap > 0: + os.lseek(fd, pos, 0) + pos = ord(os.read(fd, 1)) + if pos < 0x40: + pos = 0 + break; + os.lseek(fd, pos + 0, 0) + id = ord(os.read(fd, 1)) + if id == 0xff: + pos = 0 + break; + + # Found the capability + if id == cap: + break; + + # Test the next one + pos = pos + 1 + max_cap = max_cap - 1; + + os.close(fd) + except OSError, (errno, strerr): + raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' % + (strerr, errno))) + return pos + + def pci_conf_read8(self, pos): + fd = os.open(self.cfg_space_path, os.O_RDONLY) + os.lseek(fd, pos, 0) + str = os.read(fd, 1) + os.close(fd) + val = struct.unpack('B', str)[0] + return val + + def pci_conf_read16(self, pos): + fd = os.open(self.cfg_space_path, os.O_RDONLY) + os.lseek(fd, pos, 0) + str = os.read(fd, 2) + os.close(fd) + val = struct.unpack('H', str)[0] + return val + + def pci_conf_read32(self, pos): + fd = os.open(self.cfg_space_path, os.O_RDONLY) + os.lseek(fd, pos, 0) + str = os.read(fd, 4) + os.close(fd) + val = struct.unpack('I', str)[0] + return val + + def pci_conf_write8(self, pos, val): + str = struct.pack('B', val) + fd = os.open(self.cfg_space_path, os.O_WRONLY) + os.lseek(fd, pos, 0) + os.write(fd, str) + os.close(fd) + + def pci_conf_write16(self, pos, val): + str = struct.pack('H', val) + fd = os.open(self.cfg_space_path, os.O_WRONLY) + os.lseek(fd, pos, 0) + os.write(fd, str) + os.close(fd) + + def pci_conf_write32(self, pos, val): + str = struct.pack('I', val) + fd = os.open(self.cfg_space_path, os.O_WRONLY) + os.lseek(fd, pos, 0) + os.write(fd, str) + os.close(fd) + + def detect_dev_info(self): + class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE) + pos = self.find_cap_offset(PCI_CAP_ID_EXP) + if class_dev == PCI_CLASS_BRIDGE_PCI: + if pos == 0: + self.dev_type = DEV_TYPE_PCI_BRIDGE + else: + creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS) + if ((creg & PCI_EXP_TYPE_PCI_BRIDGE) >> 4) == \ + PCI_EXP_TYPE_PCI_BRIDGE: + self.dev_type = DEV_TYPE_PCI_BRIDGE + else: + self.dev_type = DEV_TYPE_PCIe_BRIDGE + else: + if pos != 0: + self.dev_type = DEV_TYPE_PCIe_ENDPOINT + else: + self.dev_type = DEV_TYPE_PCI + + # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE + if self.name == '0000:00:00.0': + self.dev_type = DEV_TYPE_PCIe_BRIDGE + + if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \ + (self.dev_type == DEV_TYPE_PCIe_BRIDGE): + return + + # Try to findthe PCIe FLR capability + if self.dev_type == DEV_TYPE_PCIe_ENDPOINT: + dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP) + if dev_cap & PCI_EXP_DEVCAP_FLR: + self.pcie_flr = True + elif self.dev_type == DEV_TYPE_PCI: + # Try to find the "PCI Advanced Capabilities" + pos = self.find_cap_offset(PCI_CAP_ID_AF) + if pos != 0: + af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs) + if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR: + self.pci_af_flr = True + + bar_addr = PCI_BAR_0 + while bar_addr <= PCI_BAR_5: + bar = self.pci_conf_read32(bar_addr) + if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM: + bar = bar & PCI_BAR_MEM_MASK + bar = bar & ~PAGE_MASK + if bar != 0: + self.has_non_page_aligned_bar = True + break + bar_addr = bar_addr + 4 + + def devs_check_driver(self, devs): + if len(devs) == 0: + return + for pci_dev in devs: + (dom, b, d, f) = parse_pci_name(pci_dev) + dev = PciDevice(dom, b, d, f) + if dev.driver == 'pciback': + continue + err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \ + ', but it is not owned by pciback.' + raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name)) + + def do_FLR(self): + """ Perform FLR (Functional Level Reset) for the device. + """ + if self.dev_type == DEV_TYPE_PCIe_ENDPOINT: + # If PCIe device supports FLR, we use it. + if self.pcie_flr: + (pci_list, cfg_list) = save_pci_conf_space([self.name]) + pos = self.find_cap_offset(PCI_CAP_ID_EXP) + self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR) + # We must sleep at least 100ms for the completion of FLR + time.sleep(0.200) + restore_pci_conf_space((pci_list, cfg_list)) + else: + if self.bus == 0: + self.do_FLR_for_integrated_device() + else: + funcs = self.find_all_the_multi_functions() + self.devs_check_driver(funcs) + + parent = '%04x:%02x:%02x.%01x' % self.find_parent() + + # Do Secondary Bus Reset. + self.do_secondary_bus_reset(parent, funcs) + # PCI devices + else: + # For PCI device on host bus, we test "PCI Advanced Capabilities". + if self.bus == 0 and self.pci_af_flr: + (pci_list, cfg_list) = save_pci_conf_space([self.name]) + # We use Advanced Capability to do FLR. + pos = self.find_cap_offset(PCI_CAP_ID_AF) + self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_FLR) + time.sleep(0.200) + restore_pci_conf_space((pci_list, cfg_list)) + else: + if self.bus == 0: + self.do_FLR_for_integrated_device() + else: + devs = self.find_coassigned_devices(False) + # Remove the element 0 which is a bridge + target_bus = devs[0] + del devs[0] + self.devs_check_driver(devs) + + # Do Secondary Bus Reset. + self.do_secondary_bus_reset(target_bus, devs) + + def find_capability(self, type): + sysfs_mnt = find_sysfs_mnt() + if sysfs_mnt == None: + return False + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_CONFIG_PATH + try: + conf_file = open(path, 'rb') + conf_file.seek(PCI_HEADER_TYPE) + header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK + if header_type == PCI_HEADER_TYPE_CARDBUS: + return + conf_file.seek(PCI_STATUS_OFFSET) + status = ord(conf_file.read(1)) + if status&PCI_STATUS_CAP_MASK: + conf_file.seek(PCI_CAP_OFFSET) + capa_pointer = ord(conf_file.read(1)) + capa_count = 0 + while capa_pointer: + if capa_pointer < 0x40: + raise PciDeviceParseError( + ('Broken capability chain: %s' % self.name)) + capa_count += 1 + if capa_count > 96: + raise PciDeviceParseError( + ('Looped capability chain: %s' % self.name)) + conf_file.seek(capa_pointer) + capa_id = ord(conf_file.read(1)) + capa_pointer = ord(conf_file.read(1)) + if capa_id == type: + # get the type + message_cont_lo = ord(conf_file.read(1)) + message_cont_hi = ord(conf_file.read(1)) + self.msix=1 + self.msix_entries = (message_cont_lo + \ + (message_cont_hi << 8)) \ + & MSIX_SIZE_MASK + t_off=conf_file.read(4) + p_off=conf_file.read(4) + self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \ + (ord(t_off[2])<<16)| \ + (ord(t_off[3])<<24) + self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \ + (ord(p_off[2])<<16) | \ + (ord(p_off[3])<<24) + self.table_index = self.table_offset & MSIX_BIR_MASK + self.table_offset = self.table_offset & ~MSIX_BIR_MASK + self.pba_index = self.pba_offset & MSIX_BIR_MASK + self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK + break + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' % + (PROC_PCI_PATH, strerr, errno))) + + def remove_msix_iomem(self, index, start, size): + if (index == self.table_index): + table_start = start+self.table_offset + table_end = table_start + self.msix_entries * 16 + table_start = table_start & PAGE_MASK + table_end = (table_end + PAGE_SIZE) & PAGE_MASK + self.msix_iomem.append((table_start, table_end-table_start)) + if (index==self.pba_index): + pba_start = start + self.pba_offset + pba_end = pba_start + self.msix_entries/8 + pba_start = pba_start & PAGE_MASK + pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK + self.msix_iomem.append((pba_start, pba_end-pba_start)) + + def get_info_from_sysfs(self): + self.find_capability(0x11) + sysfs_mnt = find_sysfs_mnt() + if sysfs_mnt == None: + return False + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_RESOURCE_PATH + try: + resource_file = open(path,'r') + + for i in range(PROC_PCI_NUM_RESOURCES): + line = resource_file.readline() + sline = line.split() + if len(sline)<3: + continue + + start = int(sline[0],16) + end = int(sline[1],16) + flags = int(sline[2],16) + size = end-start+1 + + if start!=0: + if flags&PCI_BAR_IO: + self.ioports.append( (start,size) ) + else: + self.iomem.append( (start,size) ) + if (self.msix): + self.remove_msix_iomem(i, start, size) + + + + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_IRQ_PATH + try: + self.irq = int(open(path,'r').readline()) + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH + try: + self.driver = os.path.basename(os.readlink(path)) + except OSError, (errno, strerr): + self.driver = "" + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_VENDOR_PATH + try: + self.vendor = int(open(path,'r').readline(), 16) + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_DEVICE_PATH + try: + self.device = int(open(path,'r').readline(), 16) + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH + try: + self.subvendor = int(open(path,'r').readline(), 16) + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH + try: + self.subdevice = int(open(path,'r').readline(), 16) + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \ + self.name+SYSFS_PCI_DEV_CLASS_PATH + try: + self.classcode = int(open(path,'r').readline(), 16) + except IOError, (errno, strerr): + raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' % + (path, strerr, errno))) + + return True + + def get_info_from_lspci(self): + """ Get information such as vendor name, device name, class name, etc. + Since we cannot obtain these data from sysfs, use 'lspci' command. + """ + global lspci_info + + if lspci_info is None: + create_lspci_info() + + try: + device_info = lspci_info[self.name] + self.revision = int(device_info['Rev'], 16) + self.vendorname = device_info['Vendor'] + self.devicename = device_info['Device'] + self.classname = device_info['Class'] + self.subvendorname = device_info['SVendor'] + self.subdevicename = device_info['SDevice'] + except KeyError: + pass + + return True + + def __str__(self): + str = "PCI Device %s\n" % (self.name) + for (start,size) in self.ioports: + str = str + "IO Port 0x%02x [size=%d]\n"%(start,size) + for (start,size) in self.iomem: + str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size) + str = str + "IRQ %d\n"%(self.irq) + str = str + "Vendor ID 0x%04x\n"%(self.vendor) + str = str + "Device ID 0x%04x\n"%(self.device) + str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor) + str = str + "Subsystem Device ID 0x%04x"%(self.subdevice) + return str + +def main(): + if len(sys.argv)<5: + print "Usage: %s \n" % sys.argv[0] + sys.exit(2) + + dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16), + int(sys.argv[3],16), int(sys.argv[4],16)) + print str(dev) + +if __name__=='__main__': + main() diff --git a/tools/python/xen/util/utils.py b/tools/python/xen/util/utils.py new file mode 100644 index 0000000..6d0c5ab --- /dev/null +++ b/tools/python/xen/util/utils.py @@ -0,0 +1,76 @@ +import traceback +import sys +import os + +def exception_string(e): + (ty,v,tb) = sys.exc_info() + return traceback.format_exception_only(ty,v) + +def daemonize(prog, args, stdin_tmpfile=None): + """Runs a program as a daemon with the list of arguments. Returns the PID + of the daemonized program, or returns 0 on error. + """ + r, w = os.pipe() + pid = os.fork() + + if pid == 0: + os.close(r) + w = os.fdopen(w, 'w') + os.setsid() + try: + pid2 = os.fork() + except: + pid2 = None + if pid2 == 0: + os.chdir("/") + null_fd = os.open("/dev/null", os.O_RDWR) + if stdin_tmpfile is not None: + os.dup2(stdin_tmpfile.fileno(), 0) + else: + os.dup2(null_fd, 0) + os.dup2(null_fd, 1) + os.dup2(null_fd, 2) + for fd in range(3, 256): + try: + os.close(fd) + except: + pass + os.execvp(prog, args) + os._exit(1) + else: + w.write(str(pid2 or 0)) + w.close() + os._exit(0) + os.close(w) + r = os.fdopen(r) + daemon_pid = int(r.read()) + r.close() + os.waitpid(pid, 0) + return daemon_pid + +# Global variable to store the sysfs mount point +sysfs_mount_point = None + +PROC_MOUNTS_PATH = '/proc/mounts' + +def find_sysfs_mount(): + global sysfs_mount_point + + if not sysfs_mount_point is None: + return sysfs_mount_point + + try: + mounts_file = open(PROC_MOUNTS_PATH, 'r') + + for line in mounts_file: + sline = line.split() + if len(sline) < 3: + continue + if sline[2] == 'sysfs': + sysfs_mount_point= sline[1] + return sysfs_mount_point + except IOError, (errno, strerr): + raise + + return None + diff --git a/tools/python/xen/util/vscsi_util.py b/tools/python/xen/util/vscsi_util.py new file mode 100644 index 0000000..42137bc --- /dev/null +++ b/tools/python/xen/util/vscsi_util.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# -*- mode: python; -*- + +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2008 FUJITSU Limited +# Based on the blkif.py +#============================================================================ + + +"""Support for VSCSI Devices. +""" +import os +import os.path +import sys +import re +import string +from xen.util import utils + +SYSFS_SCSI_PATH = "/bus/scsi/devices" +SYSFS_SCSI_DEV_VENDOR_PATH = '/vendor' +SYSFS_SCSI_DEV_MODEL_PATH = '/model' +SYSFS_SCSI_DEV_TYPEID_PATH = '/type' +SYSFS_SCSI_DEV_REVISION_PATH = '/rev' +SYSFS_SCSI_DEV_SCSILEVEL_PATH = '/scsi_level' + +def _vscsi_hctl_block(name, scsi_devices): + """ block-device name is convert into hctl. (e.g., '/dev/sda', + '0:0:0:0')""" + try: + search = re.compile(r'' + name + '$', re.DOTALL) + except Exception, e: + raise VmError("vscsi: invalid expression. " + str(e)) + chk = 0 + for hctl, block, sg, scsi_id in scsi_devices: + if search.match(hctl): + chk = 1 + break + + if chk: + return (hctl, block) + else: + return (None, None) + + +def _vscsi_block_scsiid_to_hctl(phyname, scsi_devices): + """ block-device name is convert into hctl. (e.g., '/dev/sda', + '0:0:0:0')""" + + if re.match('/dev/sd[a-z]+([1-9]|1[0-5])?$', phyname): + # sd driver + name = re.sub('(^/dev/)|([1-9]|1[0-5])?$', '', phyname) + elif re.match('/dev/sg[0-9]+$', phyname): + # sg driver + name = re.sub('^/dev/', '', phyname) + elif re.match('/dev/st[0-9]+$', phyname): + # st driver + name = re.sub('^/dev/', '', phyname) + else: + # scsi_id -gu + name = phyname + + chk = 0 + for hctl, block, sg, scsi_id in scsi_devices: + if block == name: + chk = 1 + break + elif sg == name: + chk = 1 + break + elif scsi_id == name: + chk = 1 + break + + if chk: + return (hctl, block) + else: + return (None, None) + + +def vscsi_get_scsidevices(): + """ get all scsi devices""" + + devices = [] + sysfs_mnt = utils.find_sysfs_mount() + + for dirpath, dirnames, files in os.walk(sysfs_mnt + SYSFS_SCSI_PATH): + for hctl in dirnames: + paths = os.path.join(dirpath, hctl) + block = "-" + for f in os.listdir(paths): + if re.match('^block', f): + os.chdir(os.path.join(paths, f)) + block = os.path.basename(os.getcwd()) + elif re.match('^tape', f): + os.chdir(os.path.join(paths, f)) + block = os.path.basename(os.getcwd()) + elif re.match('^scsi_changer', f): + os.chdir(os.path.join(paths, f)) + block = os.path.basename(os.getcwd()) + elif re.match('^onstream_tape', f): + os.chdir(os.path.join(paths, f)) + block = os.path.basename(os.getcwd()) + + if re.match('^scsi_generic', f): + os.chdir(os.path.join(paths, f)) + sg = os.path.basename(os.getcwd()) + lines = os.popen('/sbin/scsi_id -gu -s /class/scsi_generic/' + sg).read().split() + if len(lines) == 0: + scsi_id = '-' + else: + scsi_id = lines[0] + + devices.append([hctl, block, sg, scsi_id]) + + return devices + + +def vscsi_search_hctl_and_block(device): + + scsi_devices = vscsi_get_scsidevices() + + tmp = device.split(':') + if len(tmp) == 4: + (hctl, block) = _vscsi_hctl_block(device, scsi_devices) + else: + (hctl, block) = _vscsi_block_scsiid_to_hctl(device, scsi_devices) + + return (hctl, block) + + +def get_scsi_vendor(pHCTL): + try: + sysfs_mnt = utils.find_sysfs_mount() + sysfs_scsi_dev_path = \ + os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL) + scsi_vendor = \ + os.popen('cat ' + sysfs_scsi_dev_path + \ + SYSFS_SCSI_DEV_VENDOR_PATH).read() + return scsi_vendor.splitlines()[0] + except: + return None + +def get_scsi_model(pHCTL): + try: + sysfs_mnt = utils.find_sysfs_mount() + sysfs_scsi_dev_path = \ + os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL) + scsi_model = \ + os.popen('cat ' + sysfs_scsi_dev_path + \ + SYSFS_SCSI_DEV_MODEL_PATH).read() + return scsi_model.splitlines()[0] + except: + return None + +def get_scsi_typeid(pHCTL): + try: + sysfs_mnt = utils.find_sysfs_mount() + sysfs_scsi_dev_path = \ + os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL) + scsi_typeid = \ + os.popen('cat ' + sysfs_scsi_dev_path + \ + SYSFS_SCSI_DEV_TYPEID_PATH).read() + return int(scsi_typeid.splitlines()[0]) + except: + return None + +def get_scsi_revision(pHCTL): + try: + sysfs_mnt = utils.find_sysfs_mount() + sysfs_scsi_dev_path = \ + os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL) + scsi_revision = \ + os.popen('cat ' + sysfs_scsi_dev_path + \ + SYSFS_SCSI_DEV_REVISION_PATH).read() + return scsi_revision.splitlines()[0] + except: + return None + +def get_scsi_scsilevel(pHCTL): + try: + sysfs_mnt = utils.find_sysfs_mount() + sysfs_scsi_dev_path = \ + os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL) + scsi_scsilevel = \ + os.popen('cat ' + sysfs_scsi_dev_path + \ + SYSFS_SCSI_DEV_SCSILEVEL_PATH).read() + return int(scsi_scsilevel.splitlines()[0]) + except: + return None + +def get_all_scsi_devices(): + + scsi_devs = [] + + for scsi_info in vscsi_get_scsidevices(): + scsi_dev = { + 'physical_HCTL': scsi_info[0], + 'dev_name': None, + 'sg_name': scsi_info[2], + 'scsi_id': None + } + if scsi_info[1] != '-': + scsi_dev['dev_name'] = scsi_info[1] + if scsi_info[3] != '-': + scsi_dev['scsi_id'] = scsi_info[3] + + scsi_dev['vendor_name'] = \ + get_scsi_vendor(scsi_dev['physical_HCTL']) + scsi_dev['model'] = \ + get_scsi_model(scsi_dev['physical_HCTL']) + scsi_dev['type_id'] = \ + get_scsi_typeid(scsi_dev['physical_HCTL']) + scsi_dev['revision'] = \ + get_scsi_revision(scsi_dev['physical_HCTL']) + scsi_dev['scsi_level'] = \ + get_scsi_scsilevel(scsi_dev['physical_HCTL']) + + try: + lsscsi_info = os.popen('lsscsi ' + scsi_dev['physical_HCTL']).read().split() + scsi_dev['type'] = lsscsi_info[1] + except: + scsi_dev['type'] = None + + scsi_devs.append(scsi_dev) + + return scsi_devs + diff --git a/tools/python/xen/util/xmlrpcclient.py b/tools/python/xen/util/xmlrpcclient.py new file mode 100644 index 0000000..5b88317 --- /dev/null +++ b/tools/python/xen/util/xmlrpcclient.py @@ -0,0 +1,123 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 Anthony Liguori +# Copyright (C) 2007 XenSource Inc. +#============================================================================ + + +from httplib import FakeSocket, HTTPConnection, HTTP +import socket +import string +import xmlrpclib +from types import StringTypes + + +try: + import SSHTransport + ssh_enabled = True +except ImportError: + # SSHTransport is disabled on Python <2.4, because it uses the subprocess + # package. + ssh_enabled = False + + +# A new ServerProxy that also supports httpu urls. An http URL comes in the +# form: +# +# httpu:///absolute/path/to/socket.sock +# +# It assumes that the RPC handler is /RPC2. This probably needs to be improved + +class HTTPUnixConnection(HTTPConnection): + def connect(self): + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sock.connect(self.host) + +class HTTPUnix(HTTP): + _connection_class = HTTPUnixConnection + +class UnixTransport(xmlrpclib.Transport): + def request(self, host, handler, request_body, verbose=0): + self.__handler = handler + return xmlrpclib.Transport.request(self, host, '/RPC2', + request_body, verbose) + def make_connection(self, host): + return HTTPUnix(self.__handler) + + +# We need our own transport for HTTPS, because xmlrpclib.SafeTransport is +# broken -- it does not handle ERROR_ZERO_RETURN properly. +class HTTPSTransport(xmlrpclib.SafeTransport): + def _parse_response(self, file, sock): + p, u = self.getparser() + while 1: + try: + if sock: + response = sock.recv(1024) + else: + response = file.read(1024) + except socket.sslerror, exn: + if exn[0] == socket.SSL_ERROR_ZERO_RETURN: + break + raise + + if not response: + break + if self.verbose: + print 'body:', repr(response) + p.feed(response) + + file.close() + p.close() + return u.close() + + +# See xmlrpclib2.TCPXMLRPCServer._marshalled_dispatch. +def conv_string(x): + if isinstance(x, StringTypes): + s = string.replace(x, "'", r"\047") + exec "s = '" + s + "'" + return s + else: + return x + + +class ServerProxy(xmlrpclib.ServerProxy): + def __init__(self, uri, transport=None, encoding=None, verbose=0, + allow_none=1): + if transport == None: + (protocol, rest) = uri.split(':', 1) + if protocol == 'httpu': + uri = 'http:' + rest + transport = UnixTransport() + elif protocol == 'https': + transport = HTTPSTransport() + elif protocol == 'ssh': + global ssh_enabled + if ssh_enabled: + (transport, uri) = SSHTransport.getHTTPURI(uri) + else: + raise ValueError( + "SSH transport not supported on Python <2.4.") + xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, + verbose, allow_none) + + def __request(self, methodname, params): + response = xmlrpclib.ServerProxy.__request(self, methodname, params) + + if isinstance(response, tuple): + return tuple([conv_string(x) for x in response]) + else: + return conv_string(response) diff --git a/tools/python/xen/util/xmlrpclib2.py b/tools/python/xen/util/xmlrpclib2.py new file mode 100644 index 0000000..a503de1 --- /dev/null +++ b/tools/python/xen/util/xmlrpclib2.py @@ -0,0 +1,217 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 Anthony Liguori +# Copyright (C) 2006 XenSource Inc. +#============================================================================ + +""" +An enhanced XML-RPC client/server interface for Python. +""" + +import re +import fcntl +from types import * + + +from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler +import SocketServer +import xmlrpclib, socket, os, stat + +import mkdir + +from xen.web import connection +from xen.xend.XendLogging import log + +# +# Convert all integers to strings as described in the Xen API +# + + +def stringify(value): + if isinstance(value, long) or \ + (isinstance(value, int) and not isinstance(value, bool)): + return str(value) + elif isinstance(value, dict): + new_value = {} + for k, v in value.items(): + new_value[stringify(k)] = stringify(v) + return new_value + elif isinstance(value, (tuple, list)): + return [stringify(v) for v in value] + else: + return value + + +# We're forced to subclass the RequestHandler class so that we can work around +# some bugs in Keep-Alive handling and also enabled it by default +class XMLRPCRequestHandler(SimpleXMLRPCRequestHandler): + protocol_version = "HTTP/1.1" + + def __init__(self, hosts_allowed, request, client_address, server): + self.hosts_allowed = hosts_allowed + SimpleXMLRPCRequestHandler.__init__(self, request, client_address, + server) + + # this is inspired by SimpleXMLRPCRequestHandler's do_POST but differs + # in a few non-trivial ways + # 1) we never generate internal server errors. We let the exception + # propagate so that it shows up in the Xend debug logs + # 2) we don't bother checking for a _dispatch function since we don't + # use one + def do_POST(self): + addrport = self.client_address + if not connection.hostAllowed(addrport, self.hosts_allowed): + self.connection.shutdown(1) + return + + data = self.rfile.read(int(self.headers["content-length"])) + rsp = self.server._marshaled_dispatch(data) + + self.send_response(200) + self.send_header("Content-Type", "text/xml") + self.send_header("Content-Length", str(len(rsp))) + self.end_headers() + + self.wfile.write(rsp) + self.wfile.flush() + if self.close_connection == 1: + self.connection.shutdown(1) + +# This is a base XML-RPC server for TCP. It sets allow_reuse_address to +# true, and has an improved marshaller that logs and serializes exceptions. + +class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer): + allow_reuse_address = True + + def __init__(self, addr, allowed, xenapi, requestHandler=None, + logRequests = 1): + self.xenapi = xenapi + + if requestHandler is None: + requestHandler = XMLRPCRequestHandler + SimpleXMLRPCServer.__init__(self, addr, + (lambda x, y, z: + requestHandler(allowed, x, y, z)), + logRequests) + + flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) + + def get_request(self): + (client, addr) = SimpleXMLRPCServer.get_request(self) + flags = fcntl.fcntl(client.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(client.fileno(), fcntl.F_SETFD, flags) + return (client, addr) + + def _marshaled_dispatch(self, data, dispatch_method = None): + params, method = xmlrpclib.loads(data) + if False: + # Enable this block of code to exit immediately without sending + # a response. This allows you to test client-side crash handling. + import sys + sys.exit(1) + try: + if dispatch_method is not None: + response = dispatch_method(method, params) + else: + response = self._dispatch(method, params) + + if self.xenapi and \ + (response is None or + not isinstance(response, dict) or + 'Status' not in response): + log.exception('Internal error handling %s: Invalid result %s', + method, response) + response = { "Status": "Failure", + "ErrorDescription": + ['INTERNAL_ERROR', + 'Invalid result %s handling %s' % + (response, method)]} + + # With either Unicode or normal strings, we can only transmit + # \t, \n, \r, \u0020-\ud7ff, \ue000-\ufffd, and \u10000-\u10ffff + # in an XML document. xmlrpclib does not escape these values + # properly, and then breaks when it comes to parse the document. + # To hack around this problem, we use repr here and exec above + # to transmit the string using Python encoding. + # Thanks to David Mertz for the trick (buried + # in xml_pickle.py). + if isinstance(response, StringTypes): + response = repr(response)[1:-1] + + response = (response,) + response = xmlrpclib.dumps(response, + methodresponse=1, + allow_none=1) + except Exception, exn: + try: + if self.xenapi: + if _is_not_supported(exn): + errdesc = ['MESSAGE_METHOD_UNKNOWN', method] + else: + log.exception('Internal error handling %s', method) + errdesc = ['INTERNAL_ERROR', str(exn)] + + response = xmlrpclib.dumps( + ({ "Status": "Failure", + "ErrorDescription": errdesc },), + methodresponse = 1) + else: + import xen.xend.XendClient + if isinstance(exn, xmlrpclib.Fault): + response = xmlrpclib.dumps(exn) + else: + log.exception('Internal error handling %s', method) + response = xmlrpclib.dumps( + xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn))) + except: + log.exception('Internal error handling error') + + return response + + +notSupportedRE = re.compile(r'method "(.*)" is not supported') +def _is_not_supported(exn): + try: + m = notSupportedRE.search(exn[0]) + return m is not None + except: + return False + + +# This is a XML-RPC server that sits on a Unix domain socket. +# It implements proper support for allow_reuse_address by +# unlink()'ing an existing socket. + +class UnixXMLRPCRequestHandler(XMLRPCRequestHandler): + def address_string(self): + try: + return XMLRPCRequestHandler.address_string(self) + except ValueError, e: + return self.client_address[:2] + +class UnixXMLRPCServer(TCPXMLRPCServer): + address_family = socket.AF_UNIX + allow_address_reuse = True + + def __init__(self, addr, allowed, xenapi, logRequests = 1): + mkdir.parents(os.path.dirname(addr), stat.S_IRWXU, True) + if self.allow_reuse_address and os.path.exists(addr): + os.unlink(addr) + + TCPXMLRPCServer.__init__(self, addr, allowed, xenapi, + UnixXMLRPCRequestHandler, logRequests) diff --git a/tools/python/xen/util/xpopen.py b/tools/python/xen/util/xpopen.py new file mode 100644 index 0000000..b0c880f --- /dev/null +++ b/tools/python/xen/util/xpopen.py @@ -0,0 +1,169 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved +# +# PSF LICENSE AGREEMENT FOR PYTHON 2.3 +# ------------------------------------ +# +# 1. This LICENSE AGREEMENT is between the Python Software Foundation +# ("PSF"), and the Individual or Organization ("Licensee") accessing and +# otherwise using Python 2.3 software in source or binary form and its +# associated documentation. +# +# 2. Subject to the terms and conditions of this License Agreement, PSF +# hereby grants Licensee a nonexclusive, royalty-free, world-wide +# license to reproduce, analyze, test, perform and/or display publicly, +# prepare derivative works, distribute, and otherwise use Python 2.3 +# alone or in any derivative version, provided, however, that PSF's +# License Agreement and PSF's notice of copyright, i.e., "Copyright (c) +# 2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved" are +# retained in Python 2.3 alone or in any derivative version prepared by +# Licensee. +# +# 3. In the event Licensee prepares a derivative work that is based on +# or incorporates Python 2.3 or any part thereof, and wants to make +# the derivative work available to others as provided herein, then +# Licensee hereby agrees to include in any such work a brief summary of +# the changes made to Python 2.3. +# +# 4. PSF is making Python 2.3 available to Licensee on an "AS IS" +# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT +# INFRINGE ANY THIRD PARTY RIGHTS. +# +# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +# 2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3, +# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. +# +# 6. This License Agreement will automatically terminate upon a material +# breach of its terms and conditions. +# +# 7. Nothing in this License Agreement shall be deemed to create any +# relationship of agency, partnership, or joint venture between PSF and +# Licensee. This License Agreement does not grant permission to use PSF +# trademarks or trade name in a trademark sense to endorse or promote +# products or services of Licensee, or any third party. +# +# 8. By copying, installing or otherwise using Python 2.3, Licensee +# agrees to be bound by the terms and conditions of this License +# Agreement. +# +# Modifications: Copyright (c) 2005 Christian Limpach +# - add support for excluding a list of file descriptors from being +# closed, allowing access to those file descriptors from the command. +# + +"""Spawn a command with pipes to its stdin, stdout, and optionally stderr. + +The normal os.popen(cmd, mode) call spawns a shell command and provides a +file interface to just the input or output of the process depending on +whether mode is 'r' or 'w'. This module provides the functions xpopen2(cmd) +and xpopen3(cmd) which return two or three pipes to the spawned command. +Optionally exclude a list of file descriptors from being closed, allowing +access to those file descriptors from the command. +""" + +import os +import sys + +try: + MAXFD = os.sysconf('SC_OPEN_MAX') +except (AttributeError, ValueError): + MAXFD = 256 + +_active = [] + +def _cleanup(): + for inst in _active[:]: + inst.poll() + +class xPopen3: + """Class representing a child process. Normally instances are created + by the factory functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1, passfd=()): + """The parameter 'cmd' is the shell command to execute in a + sub-process. The 'capturestderr' flag, if true, specifies that + the object should capture standard error output of the child process. + The default is false. If the 'bufsize' parameter is specified, it + specifies the size of the I/O buffers to/from the child process.""" + _cleanup() + self.passfd = passfd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + for i in range(3, MAXFD): + if i in self.passfd: + continue + try: + os.close(i) + except OSError: + pass + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(127) + + def poll(self): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + if pid == self.pid: + self.sts = sts + _active.remove(self) + except os.error: + pass + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + if pid == self.pid: + self.sts = sts + _active.remove(self) + return self.sts + + +def xpopen2(cmd, bufsize=-1, mode='t', passfd=[]): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout, child_stdin) are returned.""" + inst = xPopen3(cmd, False, bufsize, passfd) + return inst.fromchild, inst.tochild + +def xpopen3(cmd, bufsize=-1, mode='t', passfd=[]): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout, child_stdin, child_stderr) are returned.""" + inst = xPopen3(cmd, True, bufsize, passfd) + return inst.fromchild, inst.tochild, inst.childerr diff --git a/tools/python/xen/util/xsconstants.py b/tools/python/xen/util/xsconstants.py new file mode 100644 index 0000000..58739eb --- /dev/null +++ b/tools/python/xen/util/xsconstants.py @@ -0,0 +1,114 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 International Business Machines Corp. +# Author: Stefan Berger +#============================================================================ + +XS_INST_NONE = 0 +XS_INST_BOOT = (1 << 0) +XS_INST_LOAD = (1 << 1) + +XS_POLICY_ACM = (1 << 0) +XS_POLICY_FLASK = (1 << 1) +XS_POLICY_DUMMY = (1 << 2) +XS_POLICY_USE = 0 + +# Some internal variables used by the Xen-API +ACM_LABEL_VM = (1 << 0) +ACM_LABEL_RES = (1 << 1) + +# Base for XS error codes for collision avoidance with other error codes +XSERR_BASE = 0x1000 + +# XS error codes as used by the Xen-API +XSERR_SUCCESS = 0 +XSERR_GENERAL_FAILURE = 1 + XSERR_BASE +XSERR_BAD_XML = 2 + XSERR_BASE # XML is wrong (not according to schema) +XSERR_XML_PROCESSING = 3 + XSERR_BASE +XSERR_POLICY_INCONSISTENT = 4 + XSERR_BASE # i.e., bootstrap name not a VM label +XSERR_FILE_ERROR = 5 + XSERR_BASE +XSERR_BAD_RESOURCE_FORMAT = 6 + XSERR_BASE # badly formatted resource +XSERR_BAD_LABEL_FORMAT = 7 + XSERR_BASE +XSERR_RESOURCE_NOT_LABELED = 8 + XSERR_BASE +XSERR_RESOURCE_ALREADY_LABELED = 9 + XSERR_BASE +XSERR_WRONG_POLICY_TYPE = 10 + XSERR_BASE +XSERR_BOOTPOLICY_INSTALLED = 11 + XSERR_BASE +XSERR_NO_DEFAULT_BOOT_TITLE = 12 + XSERR_BASE +XSERR_POLICY_LOAD_FAILED = 13 + XSERR_BASE +XSERR_POLICY_LOADED = 14 + XSERR_BASE +XSERR_POLICY_TYPE_UNSUPPORTED = 15 + XSERR_BASE +XSERR_BAD_CONFLICTSET = 16 + XSERR_BASE +XSERR_RESOURCE_IN_USE = 17 + XSERR_BASE +XSERR_BAD_POLICY_NAME = 18 + XSERR_BASE +XSERR_VERSION_PREVENTS_UPDATE = 19 + XSERR_BASE +XSERR_BAD_LABEL = 20 + XSERR_BASE +XSERR_VM_WRONG_STATE = 21 + XSERR_BASE +XSERR_POLICY_NOT_LOADED = 22 + XSERR_BASE +XSERR_RESOURCE_ACCESS = 23 + XSERR_BASE +XSERR_HV_OP_FAILED = 24 + XSERR_BASE +XSERR_BOOTPOLICY_INSTALL_ERROR = 25 + XSERR_BASE +XSERR_VM_NOT_AUTHORIZED = 26 + XSERR_BASE +XSERR_VM_IN_CONFLICT = 27 + XSERR_BASE +XSERR_POLICY_HAS_DUPLICATES = 28 + XSERR_BASE +XSERR_LAST = 28 + XSERR_BASE ## KEEP LAST + +XSERR_MESSAGES = [ + '', + 'General Failure', + 'XML is malformed', + 'Error while processing XML', + 'Policy has inconsistencies', + 'A file access error occurred', + 'The resource format is not valid', + 'The label format is not valid', + 'The resource is not labeld', + 'The resource is already labeld', + 'The policy type is wrong', + 'The system boot policy is installed', + 'Could not find the default boot title', + 'Loading of the policy failed', + 'The policy is loaded', + 'The policy type is unsupported', + 'There is a bad conflict set', + 'The resource is in use', + 'The policy has an invalid name', + 'The version of the policy prevents an update', + 'The label is bad', + 'Operation not premittend - the VM is in the wrong state', + 'The policy is not loaded', + 'Error accessing resource', + 'Operation failed in hypervisor', + 'Boot policy installation error', + 'VM is not authorized to run', + 'VM label conflicts with another VM', + 'Duplicate labels or types in policy' +] + +def xserr2string(err): + if err == XSERR_SUCCESS: + return "Success" + if err >= XSERR_GENERAL_FAILURE and \ + err <= XSERR_LAST: + return XSERR_MESSAGES[err - XSERR_BASE] + return "Unknown XSERR code '%s'." % (hex(err)) + +# Policy identifiers used in labels +ACM_POLICY_ID = 'ACM' + +INVALID_POLICY_PREFIX = 'INV_' + +INVALID_SSIDREF = 0xFFFFFFFFL + +XS_INACCESSIBLE_LABEL = '__INACCESSIBLE__' diff --git a/tools/python/xen/util/xsm/__init__.py b/tools/python/xen/util/xsm/__init__.py new file mode 100644 index 0000000..139597f --- /dev/null +++ b/tools/python/xen/util/xsm/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/tools/python/xen/util/xsm/acm/__init__.py b/tools/python/xen/util/xsm/acm/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/util/xsm/acm/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/util/xsm/acm/acm.py b/tools/python/xen/util/xsm/acm/acm.py new file mode 100644 index 0000000..c1efdfa --- /dev/null +++ b/tools/python/xen/util/xsm/acm/acm.py @@ -0,0 +1,1725 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 International Business Machines Corp. +# Author: Reiner Sailer +# Author: Bryan D. Payne +# Author: Stefan Berger +#============================================================================ + +import commands +import logging +import os, string, re +import threading +import struct +import stat +import base64 +from xen.lowlevel import acm +from xen.xend import sxp +from xen.xend import XendConstants +from xen.xend import XendOptions +from xen.xend.XendLogging import log +from xen.xend.XendError import VmError +from xen.util import dictio, xsconstants +from xen.xend.XendConstants import * + +#global directories and tools for security management +install_policy_dir_prefix = "/etc/xen/acm-security/policies" +security_dir_prefix = XendOptions.instance().get_xend_security_path() +policy_dir_prefix = security_dir_prefix + "/policies" +res_label_filename = policy_dir_prefix + "/resource_labels" +boot_filename = "/boot/grub/menu.lst" +altboot_filename = "/boot/grub/grub.conf" +xensec_tool = "/usr/sbin/xensec_tool" + +#global patterns for map file +#police_reference_tagname = "POLICYREFERENCENAME" +primary_entry_re = re.compile("\s*PRIMARY\s+.*", re.IGNORECASE) +secondary_entry_re = re.compile("\s*SECONDARY\s+.*", re.IGNORECASE) +label_template_re = re.compile(".*security_label_template.xml", re.IGNORECASE) +mapping_filename_re = re.compile(".*\.map", re.IGNORECASE) +policy_reference_entry_re = re.compile("\s*POLICYREFERENCENAME\s+.*", re.IGNORECASE) +vm_label_re = re.compile("\s*LABEL->SSID\s.+[VM|ANY]\s+.*", re.IGNORECASE) +res_label_re = re.compile("\s*LABEL->SSID\s+RES\s+.*", re.IGNORECASE) +all_label_re = re.compile("\s*LABEL->SSID\s+.*", re.IGNORECASE) +access_control_re = re.compile("\s*access_control\s*=", re.IGNORECASE) + +#global patterns for boot configuration file +xen_title_re = re.compile("\s*title\s+XEN", re.IGNORECASE) +any_title_re = re.compile("\s*title\s", re.IGNORECASE) +xen_kernel_re = re.compile("\s*kernel.*xen.*\.gz", re.IGNORECASE) +kernel_ver_re = re.compile("\s*module.*vmlinuz", re.IGNORECASE) +any_module_re = re.compile("\s*module\s", re.IGNORECASE) +empty_line_re = re.compile("^\s*$") +binary_name_re = re.compile(".*[chwall|ste|chwall_ste].*\.bin", re.IGNORECASE) +policy_name_re = re.compile(".*[chwall|ste|chwall_ste].*", re.IGNORECASE) + +#decision hooks known to the hypervisor +ACMHOOK_sharing = 1 +ACMHOOK_authorization = 2 +ACMHOOK_conflictset = 3 + +#other global variables +NULL_SSIDREF = 0 + +#general Rlock for map files; only one lock for all mapfiles +__mapfile_lock = threading.RLock() +__resfile_lock = threading.RLock() + +log = logging.getLogger("xend.util.security") + + +#Functions exported through XML-RPC +xmlrpc_exports = [ + 'set_resource_label', + 'get_resource_label', + 'list_labels', + 'get_labeled_resources', + 'set_policy', + 'reset_policy', + 'get_policy', + 'activate_policy', + 'rm_bootpolicy', + 'get_xstype', + 'get_domain_label', + 'set_domain_label' +] + +# Our own exception definition. It is masked (pass) if raised and +# whoever raises this exception must provide error information. +class XSMError(Exception): + def __init__(self,value): + self.value = value + def __str__(self): + return repr(self.value) + + + +def err(msg): + """Raise ACM exception. + """ + raise XSMError(msg) + + + +active_policy = None + + +def mapfile_lock(): + __mapfile_lock.acquire() + +def mapfile_unlock(): + __mapfile_lock.release() + + +def resfile_lock(): + __resfile_lock.acquire() + +def resfile_unlock(): + __resfile_lock.release() + + +def refresh_security_policy(): + """ + retrieves security policy + """ + global active_policy + + active_policy = 'INACCESSIBLE' + + if os.access("/proc/xen/privcmd", os.R_OK|os.W_OK): + try: + active_policy = acm.policy() + except: + active_policy = "INACTIVE" + +def get_active_policy_name(): + refresh_security_policy() + return active_policy + +# now set active_policy +refresh_security_policy() + +def on(): + """ + returns none if security policy is off (not compiled), + any string otherwise, use it: if not security.on() ... + """ + if get_active_policy_name() not in ['INACTIVE', 'NULL', '']: + return xsconstants.XS_POLICY_ACM + return 0 + + +def calc_dom_ssidref_from_info(info): + """ + Calculate a domain's ssidref from the security_label in its + info. + This function is called before the domain is started and + makes sure that: + - the type of the policy is the same as indicated in the label + - the name of the policy is the same as indicated in the label + - calculates an up-to-date ssidref for the domain + The latter is necessary since the domain's ssidref could have + changed due to changes to the policy. + """ + import xen.xend.XendConfig + if isinstance(info, xen.xend.XendConfig.XendConfig): + if info.has_key('security_label'): + seclab = info['security_label'] + tmp = seclab.split(":") + if len(tmp) != 3: + raise VmError("VM label '%s' in wrong format." % seclab) + typ, policyname, vmlabel = seclab.split(":") + if typ != xsconstants.ACM_POLICY_ID: + raise VmError("Policy type '%s' must be changed." % typ) + if get_active_policy_name() != policyname: + raise VmError("Active policy '%s' different than " + "what in VM's label ('%s')." % + (get_active_policy_name(), policyname)) + ssidref = label2ssidref(vmlabel, policyname, "dom") + return ssidref + else: + return 0x0 + raise VmError("security.calc_dom_ssidref_from_info: info of type '%s'" + "not supported." % type(info)) + + +def getmapfile(policyname): + """ + in: if policyname is None then the currently + active hypervisor policy is used + out: 1. primary policy, 2. secondary policy, + 3. open file descriptor for mapping file, and + 4. True if policy file is available, False otherwise + """ + if not policyname: + policyname = get_active_policy_name() + map_file_ok = False + primary = None + secondary = None + #strip last part of policy as file name part + policy_dir_list = string.split(policyname, ".") + policy_file = policy_dir_list.pop() + if len(policy_dir_list) > 0: + policy_dir = string.join(policy_dir_list, "/") + "/" + else: + policy_dir = "" + + map_filename = policy_dir_prefix + "/" + policy_dir + policy_file + ".map" + # check if it is there, if not check if policy file is there + if not os.path.isfile(map_filename): + policy_filename = policy_dir_prefix + "/" + policy_dir + policy_file + "-security_policy.xml" + if not os.path.isfile(policy_filename): + err("Policy file \'" + policy_filename + "\' not found.") + else: + err("Mapping file \'" + map_filename + "\' not found.") + + f = open(map_filename) + for line in f: + if policy_reference_entry_re.match(line): + l = line.split() + if (len(l) == 2) and (l[1] == policyname): + map_file_ok = True + elif primary_entry_re.match(line): + l = line.split() + if len(l) == 2: + primary = l[1] + elif secondary_entry_re.match(line): + l = line.split() + if len(l) == 2: + secondary = l[1] + f.close() + f = open(map_filename) + if map_file_ok and primary and secondary: + return (primary, secondary, f, True) + else: + err("Mapping file inconsistencies found.") + + + +def ssidref2label(ssidref_var): + """ + returns labelname corresponding to ssidref; + maps current policy to default directory + to find mapping file + """ + #1. translated permitted input formats + if isinstance(ssidref_var, str): + ssidref_var.strip() + if ssidref_var[0:2] == "0x": + ssidref = int(ssidref_var[2:], 16) + else: + ssidref = int(ssidref_var) + elif isinstance(ssidref_var, int): + ssidref = ssidref_var + else: + err("Instance type of ssidref not supported (must be of type 'str' or 'int')") + + if ssidref == 0: + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + return ACM_LABEL_UNLABELED + + try: + mapfile_lock() + + (primary, secondary, f, pol_exists) = getmapfile(None) + if not f: + if (pol_exists): + err("Mapping file for policy not found.") + else: + err("Policy file for \'" + get_active_policy_name() + + "\' not found.") + + #2. get labelnames for both ssidref parts + pri_ssid = ssidref & 0xffff + sec_ssid = ssidref >> 16 + pri_null_ssid = NULL_SSIDREF & 0xffff + sec_null_ssid = NULL_SSIDREF >> 16 + pri_labels = [] + sec_labels = [] + labels = [] + + for line in f: + l = line.split() + if (len(l) < 5) or (l[0] != "LABEL->SSID"): + continue + if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid): + pri_labels.append(l[3]) + if secondary and (l[2] == secondary) and (int(l[4], 16) == sec_ssid): + sec_labels.append(l[3]) + f.close() + finally: + mapfile_unlock() + + #3. get the label that is in both lists (combination must be a single label) + if (primary == "CHWALL") and (pri_ssid == pri_null_ssid) and (sec_ssid != sec_null_ssid): + labels = sec_labels + elif (secondary == "CHWALL") and (pri_ssid != pri_null_ssid) and (sec_ssid == sec_null_ssid): + labels = pri_labels + elif secondary == "NULL": + labels = pri_labels + else: + for i in pri_labels: + for j in sec_labels: + if (i==j): + labels.append(i) + if len(labels) != 1: + err("Label for ssidref \'" + str(ssidref) + + "\' unknown or not unique in policy \'" + active_policy + "\'") + + return labels[0] + + + +def label2ssidref(labelname, policyname, typ): + """ + returns ssidref corresponding to labelname; + maps current policy to default directory + to find mapping file """ + + if policyname in ['NULL', 'INACTIVE', 'INACCESSIBLE' ]: + err("Cannot translate labels for \'" + policyname + "\' policy.") + + allowed_types = ['ANY'] + if typ == 'dom': + allowed_types.append('VM') + elif typ == 'res': + allowed_types.append('RES') + else: + err("Invalid type. Must specify 'dom' or 'res'.") + + try: + mapfile_lock() + (primary, secondary, f, pol_exists) = getmapfile(policyname) + + #2. get labelnames for ssidref parts and find a common label + pri_ssid = [] + sec_ssid = [] + for line in f: + l = line.split() + if (len(l) < 5) or (l[0] != "LABEL->SSID"): + continue + if primary and (l[1] in allowed_types) and \ + (l[2] == primary) and \ + (l[3] == labelname): + pri_ssid.append(int(l[4], 16)) + if secondary and (l[1] in allowed_types) and \ + (l[2] == secondary) and \ + (l[3] == labelname): + sec_ssid.append(int(l[4], 16)) + f.close() + if (typ == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0): + pri_ssid.append(NULL_SSIDREF) + elif (typ == 'res') and (secondary == "CHWALL") and \ + (len(sec_ssid) == 0): + sec_ssid.append(NULL_SSIDREF) + + #3. sanity check and composition of ssidref + if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and \ + (secondary != "NULL")): + err("Label \'" + labelname + "\' not found.") + elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1): + err("Label \'" + labelname + "\' not unique in policy (policy error)") + if secondary == "NULL": + return pri_ssid[0] + else: + return (sec_ssid[0] << 16) | pri_ssid[0] + finally: + mapfile_unlock() + + +def refresh_ssidref(config): + """ + looks up ssidref from security field + and refreshes the value if label exists + """ + #called by dom0, policy could have changed after xen.utils.security was initialized + refresh_security_policy() + + security = None + if isinstance(config, dict): + security = config['security'] + elif isinstance(config, list): + security = sxp.child_value(config, 'security') + else: + err("Instance type of config parameter not supported.") + if not security: + #nothing to do (no security label attached) + return config + + policyname = None + labelname = None + # compose new security field + for idx in range(0, len(security)): + if security[idx][0] == 'ssidref': + security.pop(idx) + break + elif security[idx][0] == 'access_control': + for jdx in [1, 2]: + if security[idx][jdx][0] == 'label': + labelname = security[idx][jdx][1] + elif security[idx][jdx][0] == 'policy': + policyname = security[idx][jdx][1] + else: + err("Illegal field in access_control") + #verify policy is correct + if active_policy != policyname: + err("Policy \'" + str(policyname) + + "\' in label does not match active policy \'" + + str(active_policy) +"\'!") + + new_ssidref = label2ssidref(labelname, policyname, 'dom') + if not new_ssidref: + err("SSIDREF refresh failed!") + + security.append([ 'ssidref',str(new_ssidref)]) + security = ['security', security ] + + for idx in range(0,len(config)): + if config[idx][0] == 'security': + config.pop(idx) + break + config.append(security) + + + +def get_ssid(domain): + """ + enables domains to retrieve the label / ssidref of a running domain + """ + if not on(): + err("No policy active.") + + if isinstance(domain, str): + domain_int = int(domain) + elif isinstance(domain, int): + domain_int = domain + else: + err("Illegal parameter type.") + try: + ssid_info = acm.getssid(int(domain_int)) + except: + err("Cannot determine security information.") + + label = ssidref2label(ssid_info["ssidref"]) + + return(ssid_info["policyreference"], + label, + ssid_info["policytype"], + ssid_info["ssidref"]) + + + +def get_decision(arg1, arg2): + """ + enables domains to retrieve access control decisions from + the hypervisor Access Control Module. + IN: args format = ['domid', id] or ['ssidref', ssidref] + or ['access_control', ['policy', policy], ['label', label], ['type', type]] + """ + + if not on(): + err("No policy active.") + + #translate labels before calling low-level function + if arg1[0] == 'access_control': + if (arg1[1][0] != 'policy') or (arg1[2][0] != 'label') or (arg1[3][0] != 'type'): + err("Argument type not supported.") + ssidref = label2ssidref(arg1[2][1], arg1[1][1], arg1[3][1]) + arg1 = ['ssidref', str(ssidref)] + if arg2[0] == 'access_control': + if (arg2[1][0] != 'policy') or (arg2[2][0] != 'label') or (arg2[3][0] != 'type'): + err("Argument type not supported.") + ssidref = label2ssidref(arg2[2][1], arg2[1][1], arg2[3][1]) + arg2 = ['ssidref', str(ssidref)] + + # accept only int or string types for domid and ssidref + if isinstance(arg1[1], int): + arg1[1] = str(arg1[1]) + if isinstance(arg2[1], int): + arg2[1] = str(arg2[1]) + if not isinstance(arg1[1], str) or not isinstance(arg2[1], str): + err("Invalid id or ssidref type, string or int required") + + try: + decision = acm.getdecision(arg1[0], arg1[1], arg2[0], arg2[1], + ACMHOOK_sharing) + except: + err("Cannot determine decision.") + + if decision: + return decision + else: + err("Cannot determine decision (Invalid parameter).") + + +def has_authorization(ssidref): + """ Check if the domain with the given ssidref has authorization to + run on this system. To have authoriztion dom0's STE types must + be a superset of that of the domain's given through its ssidref. + """ + rc = True + dom0_ssidref = int(acm.getssid(0)['ssidref']) + decision = acm.getdecision('ssidref', str(dom0_ssidref), + 'ssidref', str(ssidref), + ACMHOOK_authorization) + if decision == "DENIED": + rc = False + return rc + + +def hv_chg_policy(bin_pol, del_array, chg_array): + """ + Change the binary policy in the hypervisor + The 'del_array' and 'chg_array' give hints about deleted ssidrefs + and changed ssidrefs which can be due to deleted VM labels + or reordered VM labels + """ + rc = -xsconstants.XSERR_GENERAL_FAILURE + errors = "" + if not on(): + err("No policy active.") + try: + rc, errors = acm.chgpolicy(bin_pol, del_array, chg_array) + except Exception, e: + pass + if len(errors) > 0: + rc = -xsconstants.XSERR_HV_OP_FAILED + return rc, errors + +def hv_get_policy(): + """ + Gte the binary policy enforced in the hypervisor + """ + rc = -xsconstants.XSERR_GENERAL_FAILURE + bin_pol = "" + if not on(): + err("No policy active.") + try: + rc, bin_pol = acm.getpolicy() + except Exception, e: + pass + if len(bin_pol) == 0: + bin_pol = None + return rc, bin_pol + + +def is_in_conflict(ssidref): + """ Check whether the given ssidref is in conflict with any running + domain. + """ + decision = acm.getdecision('ssidref', str(ssidref), + 'ssidref', str(ssidref), + ACMHOOK_conflictset) + if decision == "DENIED": + return True + return False + + +def set_policy(xs_type, xml, flags, overwrite): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + xspoladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + acmpol, rc, errors = \ + xspoladmin.add_acmpolicy_to_system(xml, + int(flags), + True) + return rc, base64.b64encode(errors) + except Exception, e: + err(str(e)) + + +def reset_policy(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + xspoladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + acmpol, rc, errors = \ + xspoladmin.reset_acmpolicy() + return rc, base64.b64encode(errors) + except Exception, e: + err(str(e)) + + +def get_policy(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + poladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + policy = poladmin.get_loaded_policy() + if policy != None: + return policy.toxml(), poladmin.get_policy_flags(policy) + except Exception, e: + err(str(e)) + return "", 0 + +def activate_policy(flags): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + poladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + policies = poladmin.get_policies() + if len(policies) > 0: + flags = int(flags) + irc = poladmin.activate_xspolicy(policies[0], flags) + return irc + except Exception, e: + err("Error while activating the policy: " % str(e)) + return 0 + + +def rm_bootpolicy(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + rc = XendXSPolicyAdmin.XSPolicyAdminInstance().rm_bootpolicy() + if rc != xsconstants.XSERR_SUCCESS: + err("Error while removing boot policy: %s" % \ + str(xsconstants.xserr2string(-rc))) + return rc + + +def get_xstype(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + return XendXSPolicyAdmin.XSPolicyAdminInstance().isXSEnabled() + + +def get_domain_label(domain): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendDomain + dom = XendDomain.instance().domain_lookup_nr(domain) + if dom: + seclab = dom.get_security_label() + return seclab + else: + err("Domain not found.") + + +def set_domain_label(domain, seclab, old_seclab): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendDomain + dom = XendDomain.instance().domain_lookup_nr(domain) + if dom: + results = dom.set_security_label(seclab, old_seclab) + rc, errors, old_label, new_ssidref = results + return rc, new_ssidref + else: + err("Domain not found.") + + +def dump_policy(): + if active_policy in ['NULL', 'INACTIVE', 'INACCESSIBLE' ]: + err("\'" + active_policy + "\' policy. Nothing to dump.") + + (ret, output) = commands.getstatusoutput(xensec_tool + " getpolicy") + if ret: + err("Dumping hypervisor policy failed:\n" + output) + + print output + + +def dump_policy_file(filename, ssidref=None): + ssid = "" + if ssidref: + ssid = " " + str(ssidref) + (ret, output) = commands.getstatusoutput(xensec_tool + " dumppolicy " + + filename + ssid) + if ret: + err("Dumping policy failed:\n" + output) + + print output + + +def list_labels(policy_name, ltype): + """ + Xend exports this function via XML-RPC + + List the VM,resource or any kind of labels contained in the + given policy. If no policy name is given, the currently + active policy's label will be returned if they exist. + """ + if not policy_name: + if active_policy in [ 'NULL', 'INACTIVE', "" ]: + err("Current policy \'" + active_policy + "\' " + "has no labels defined.\n") + + if not ltype or ltype == 'dom': + condition = vm_label_re + elif ltype == 'res': + condition = res_label_re + elif ltype == 'any': + condition = all_label_re + else: + err("Unknown label type \'" + ltype + "\'") + + try: + mapfile_lock() + + (primary, secondary, f, pol_exists) = getmapfile(policy_name) + if not f: + if pol_exists: + err("Cannot find mapfile for policy \'" + policy_name + "\'.\n") + else: + err("Unknown policy \'" + policy_name + "\'") + + labels = [] + for line in f: + if condition.match(line): + label = line.split()[3] + if label not in labels: + labels.append(label) + finally: + mapfile_unlock() + + if '__NULL_LABEL__' in labels: + labels.remove('__NULL_LABEL__') + + return labels + + +def get_res_label(resource): + """Returns resource label information (policytype, label, policy) if + it exists. Otherwise returns null label and policy. + """ + def default_res_label(): + ssidref = NULL_SSIDREF + if on(): + label = ssidref2label(ssidref) + else: + label = None + return (xsconstants.ACM_POLICY_ID, 'NULL', label) + + + tmp = get_resource_label(resource) + if len(tmp) == 2: + policytype = xsconstants.ACM_POLICY_ID + policy, label = tmp + elif len(tmp) == 3: + policytype, policy, label = tmp + else: + policytype, policy, label = default_res_label() + + return (policytype, label, policy) + + +def get_res_security_details(resource): + """Returns the (label, ssidref, policy) associated with a given + resource from the global resource label file. + """ + def default_security_details(): + ssidref = NULL_SSIDREF + if on(): + label = ssidref2label(ssidref) + else: + label = None + policy = active_policy + return (label, ssidref, policy) + + # find the entry associated with this resource + (policytype, label, policy) = get_res_label(resource) + if policy == 'NULL': + log.info("Resource label for "+resource+" not in file, using DEFAULT.") + return default_security_details() + + if policytype != xsconstants.ACM_POLICY_ID: + raise VmError("Unknown policy type '%s in label for resource '%s'" % + (policytype, resource)) + + # is this resource label for the running policy? + if policy == active_policy: + ssidref = label2ssidref(label, policy, 'res') + elif label == xsconstants.XS_INACCESSIBLE_LABEL: + ssidref = NULL_SSIDREF + else: + log.info("Resource label not for active policy, using DEFAULT.") + return default_security_details() + + return (label, ssidref, policy) + +def security_label_to_details(seclab): + """ Convert a Xen-API type of security label into details """ + def default_security_details(): + ssidref = NULL_SSIDREF + if on(): + label = ssidref2label(ssidref) + else: + label = None + policy = active_policy + return (label, ssidref, policy) + + (policytype, policy, label) = seclab.split(":") + + # is this resource label for the running policy? + if policy == active_policy: + ssidref = label2ssidref(label, policy, 'res') + else: + log.info("Resource label not for active policy, using DEFAULT.") + return default_security_details() + + return (label, ssidref, policy) + +def unify_resname(resource, mustexist=True): + """Makes all resource locations absolute. In case of physical + resources, '/dev/' is added to local file names""" + + if not resource: + return resource + + # sanity check on resource name + try: + (typ, resfile) = resource.split(":", 1) + except: + err("Resource spec '%s' contains no ':' delimiter" % resource) + + if typ == "tap": + try: + (subtype, resfile) = resfile.split(":") + except: + err("Resource spec '%s' contains no tap subtype" % resource) + + if typ in ["phy"]: + if not resfile.startswith("/"): + resfile = "/dev/" + resfile + if mustexist: + resfile = os.path.realpath(resfile) + try: + stats = os.lstat(resfile) + if not (stat.S_ISBLK(stats[stat.ST_MODE])): + err("Invalid resource") + except: + err("Invalid resource") + + if typ in [ "file", "tap" ]: + resfile = os.path.realpath(resfile) + if mustexist and not os.path.isfile(resfile): + err("Invalid resource") + + if typ == "vlan": + try: + vlan = int(resfile) + if vlan < 1 or vlan > 4095: + err("VLAN ID %d out of range." % vlan) + except Exception, e: + err("Invalid VLAN : %s" % resfile) + + + #file: resources must be specified with absolute path + #vlan resources don't start with '/' + if typ != "vlan": + if (not resfile.startswith("/")) or \ + (mustexist and not os.path.exists(resfile)): + err("Invalid resource.") + + # from here on absolute file names with resources + if typ == "tap": + typ = typ + ":" + subtype + resource = typ + ":" + resfile + return resource + + +def res_security_check(resource, domain_label): + """Checks if the given resource can be used by the given domain + label. Returns 1 if the resource can be used, otherwise 0. + """ + rtnval = 1 + + # if security is on, ask the hypervisor for a decision + if on(): + #build canonical resource name + resource = unify_resname(resource) + + (label, ssidref, policy) = get_res_security_details(resource) + domac = ['access_control'] + domac.append(['policy', active_policy]) + domac.append(['label', domain_label]) + domac.append(['type', 'dom']) + decision = get_decision(domac, ['ssidref', str(ssidref)]) + + # provide descriptive error messages + if decision == 'DENIED': + if label == ssidref2label(NULL_SSIDREF): + raise XSMError("Resource '"+resource+"' is not labeled") + rtnval = 0 + else: + raise XSMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed") + rtnval = 0 + + # security is off, make sure resource isn't labeled + else: + # Note, we can't canonicalise the resource here, because people using + # xm without ACM are free to use relative paths. + (policytype, label, policy) = get_res_label(resource) + if policy != 'NULL': + raise XSMError("Security is off, but '"+resource+"' is labeled") + rtnval = 0 + + return rtnval + +def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label): + """Checks if the given resource can be used by the given domain + label. Returns 1 if the resource can be used, otherwise 0. + """ + rtnval = 1 + # if security is on, ask the hypervisor for a decision + if on(): + if rlabel == xsconstants.XS_INACCESSIBLE_LABEL: + return 0 + typ, dpolicy, domain_label = xapi_dom_label.split(":") + if not dpolicy or not domain_label: + raise VmError("VM security label in wrong format.") + if active_policy != rpolicy: + raise VmError("Resource's policy '%s' != active policy '%s'" % + (rpolicy, active_policy)) + domac = ['access_control'] + domac.append(['policy', active_policy]) + domac.append(['label', domain_label]) + domac.append(['type', 'dom']) + decision = get_decision(domac, ['ssidref', str(rssidref)]) + + log.info("Access Control Decision : %s" % decision) + # provide descriptive error messages + if decision == 'DENIED': + if rlabel == ssidref2label(NULL_SSIDREF): + #raise XSMError("Resource is not labeled") + rtnval = 0 + else: + #raise XSMError("Permission denied for resource because label '"+rlabel+"' is not allowed") + rtnval = 0 + + # security is off, make sure resource isn't labeled + else: + # Note, we can't canonicalise the resource here, because people using + # xm without ACM are free to use relative paths. + if rpolicy != 'NULL': + #raise XSMError("Security is off, but resource is labeled") + rtnval = 0 + + return rtnval + + +def validate_label_xapi(xapi_label, dom_or_res): + """ + Make sure that this label is part of the currently enforced policy + and that it references the current policy. + dom_or_res defines whether this is a VM ('res') or resource label + ('res') + """ + tmp = xapi_label.split(":") + if len(tmp) != 3: + return -xsconstants.XSERR_BAD_LABEL_FORMAT + policytyp, policyref, label = tmp + return validate_label(policytyp, policyref, label, dom_or_res) + + +def validate_label(policytype, policyref, label, dom_or_res): + """ + Make sure that this label is part of the currently enforced policy + and that it reference the current policy. + """ + if policytype != xsconstants.ACM_POLICY_ID: + return -xsconstants.XSERR_WRONG_POLICY_TYPE + if not policytype or not label: + return -xsconstants.XSERR_BAD_LABEL_FORMAT + rc = xsconstants.XSERR_SUCCESS + if label == xsconstants.XS_INACCESSIBLE_LABEL: + return rc + from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + curpol = XSPolicyAdminInstance().get_loaded_policy() + if not curpol or curpol.get_name() != policyref: + rc = -xsconstants.XSERR_BAD_LABEL + else: + try: + label2ssidref(label, curpol.get_name() , dom_or_res) + except: + rc = -xsconstants.XSERR_BAD_LABEL + return rc + + +def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi): + """Assign a resource label to a resource + @param resource: The name of a resource, i.e., "phy:/dev/hda", or + "tap:qcow:/path/to/file.qcow" + + @param reslabel_xapi: A resource label foramtted as in all other parts of + the Xen-API, i.e., ACM:xm-test:blue" + @rtype: int + @return Success (0) or failure value (< 0) + """ + olabel = "" + if reslabel_xapi == "": + return rm_resource_label(resource, oldlabel_xapi) + + rc = validate_label_xapi(reslabel_xapi, 'res') + if rc != xsconstants.XSERR_SUCCESS: + return rc + + if oldlabel_xapi not in [ "" ]: + tmp = oldlabel_xapi.split(":") + if len(tmp) != 3: + return -xsconstants.XSERR_BAD_LABEL_FORMAT + otyp, opolicyref, olabel = tmp + # Only ACM is supported + if otyp != xsconstants.ACM_POLICY_ID and \ + otyp != xsconstants.INVALID_POLICY_PREFIX + \ + xsconstants.ACM_POLICY_ID: + return -xsconstants.XSERR_WRONG_POLICY_TYPE + typ, policyref, label = reslabel_xapi.split(":") + return set_resource_label(resource, typ, policyref, label, olabel) + + +def is_resource_in_use(resource): + """ + Domain-0 'owns' resources of type 'VLAN', the rest are owned by + the guests. + """ + from xen.xend import XendDomain + lst = [] + if resource.startswith('vlan'): + from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + curpol = XSPolicyAdminInstance().get_loaded_policy() + policytype, label, policy = get_res_label(resource) + if curpol and \ + policytype == xsconstants.ACM_POLICY_ID and \ + policy == curpol.get_name() and \ + label in curpol.policy_get_resourcelabel_names(): + # VLAN is in use. + lst.append(XendDomain.instance(). + get_vm_by_uuid(XendDomain.DOM0_UUID)) + else: + dominfos = XendDomain.instance().list('all') + for dominfo in dominfos: + if is_resource_in_use_by_dom(dominfo, resource): + lst.append(dominfo) + return lst + +def devices_equal(res1, res2, mustexist=True): + """ Determine whether two devices are equal """ + return (unify_resname(res1, mustexist) == + unify_resname(res2, mustexist)) + +def is_resource_in_use_by_dom(dominfo, resource): + """ Determine whether a resources is in use by a given domain + @return True or False + """ + if not dominfo.domid: + return False + if dominfo._stateGet() not in [ DOM_STATE_RUNNING ]: + return False + devs = dominfo.info['devices'] + uuids = devs.keys() + for uuid in uuids: + dev = devs[uuid] + if len(dev) >= 2 and dev[1].has_key('uname'): + # dev[0] is type, i.e. 'vbd' + if devices_equal(dev[1]['uname'], resource, mustexist=False): + log.info("RESOURCE IN USE: Domain %d uses %s." % + (dominfo.domid, resource)) + return True + return False + + +def get_domain_resources(dominfo): + """ Collect all resources of a domain in a map where each entry of + the map is a list. + Entries are strored in the following formats: + tap:qcow:/path/xyz.qcow + """ + resources = { 'vbd' : [], 'tap' : [], 'vif' : []} + devs = dominfo.info['devices'] + uuids = devs.keys() + for uuid in uuids: + dev = devs[uuid] + typ = dev[0] + if typ in [ 'vbd', 'tap' ]: + resources[typ].append(dev[1]['uname']) + if typ in [ 'vif' ]: + sec_lab = dev[1].get('security_label') + if sec_lab: + resources[typ].append(sec_lab) + else: + # !!! This should really get the label of the domain + # or at least a resource label that has the same STE type + # as the domain has + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + resources[typ].append("%s:%s:%s" % + (xsconstants.ACM_POLICY_ID, + active_policy, + ACM_LABEL_UNLABELED)) + + return resources + + +def resources_compatible_with_vmlabel(xspol, dominfo, vmlabel): + """ + Check whether the resources' labels are compatible with the + given VM label. This is a function to be used when for example + a running domain is to get the new label 'vmlabel' + """ + if not xspol: + return False + + try: + resfile_lock() + try: + access_control = dictio.dict_read("resources", + res_label_filename) + except: + # No labeled resources -> must be compatible + return True + return __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel, + access_control) + finally: + resfile_unlock() + return False + + +def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel, + access_control, + is_policy_update=False): + """ + Check whether the resources' labels are compatible with the + given VM label. The access_control parameter provides a + dictionary of the resource name to resource label mappings + under which the evaluation should be done. + Call this only for a paused or running domain. + """ + def collect_labels(reslabels, s_label, polname): + if len(s_label) != 3 or polname != s_label[1]: + return False + label = s_label[2] + if not label in reslabels: + reslabels.append(label) + return True + + resources = get_domain_resources(dominfo) + reslabels = [] # all resource labels + + polname = xspol.get_name() + for key, value in resources.items(): + if key in [ 'vbd', 'tap' ]: + for res in resources[key]: + if not res in access_control: + label = [xsconstants.ACM_POLICY_ID, + xspol.get_name(), + ACM_LABEL_UNLABELED] + else: + label = access_control[res] + if not collect_labels(reslabels, label, polname): + return False + elif key in [ 'vif' ]: + for xapi_label in value: + label = xapi_label.split(":") + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + if not (is_policy_update and \ + label[2] == ACM_LABEL_UNLABELED): + if not collect_labels(reslabels, label, polname): + return False + else: + log.error("Unhandled device type: %s" % key) + return False + + # Check that all resource labes have a common STE type with the + # vmlabel + if len(reslabels) > 0: + rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels) + else: + rc = True + log.info("vmlabel=%s, reslabels=%s, rc=%s" % + (vmlabel, reslabels, str(rc))) + return rc; + +def set_resource_label(resource, policytype, policyref, reslabel, \ + oreslabel = None): + """ + Xend exports this function via XML-RPC. + + Assign a label to a resource + If the old label (oreslabel) is given, then the resource must have + that old label. + A resource label may be changed if + - the resource is not in use + @param resource : The name of a resource, i.e., "phy:/dev/hda" + @param policyref : The name of the policy + @param reslabel : the resource label within the policy + @param oreslabel : optional current resource label + + @rtype: int + @return Success (0) or failure value (< 0) + """ + + + try: + resource = unify_resname(resource, mustexist=False) + except Exception: + return -xsconstants.XSERR_BAD_RESOURCE_FORMAT + + try: + resfile_lock() + mapfile_lock() + + if reslabel not in [ '', xsconstants.XS_INACCESSIBLE_LABEL ]: + ssidref = label2ssidref(reslabel, policyref, 'res') + + domains = is_resource_in_use(resource) + if len(domains) > 0: + return -xsconstants.XSERR_RESOURCE_IN_USE + + access_control = {} + try: + access_control = dictio.dict_read("resources", res_label_filename) + except: + pass + if oreslabel: + if not access_control.has_key(resource): + return -xsconstants.XSERR_BAD_LABEL + tmp = access_control[resource] + if len(tmp) != 3: + return -xsconstants.XSERR_BAD_LABEL + if tmp[2] != oreslabel: + return -xsconstants.XSERR_BAD_LABEL + if resource.startswith('vlan:'): + for key, value in access_control.items(): + if value == tuple([policytype, policyref, reslabel]) and \ + key.startswith('vlan:'): + return -xsconstants.XSERR_BAD_LABEL + + if reslabel == xsconstants.XS_INACCESSIBLE_LABEL: + policytype = xsconstants.ACM_POLICY_ID + policyref = '*' + + if reslabel != "": + new_entry = { resource : tuple([policytype, policyref, reslabel])} + access_control.update(new_entry) + command = "add" + reslbl = ":".join([policytype, policyref, reslabel]) + else: + if access_control.has_key(resource): + del access_control[resource] + command = "remove" + reslbl = "" + run_resource_label_change_script(resource, reslbl, command) + dictio.dict_write(access_control, "resources", res_label_filename) + finally: + resfile_unlock() + mapfile_unlock() + return xsconstants.XSERR_SUCCESS + +def rm_resource_label(resource, oldlabel_xapi): + """Remove a resource label from a physical resource + @param resource: The name of a resource, i.e., "phy:/dev/hda" + + @rtype: int + @return Success (0) or failure value (< 0) + """ + tmp = oldlabel_xapi.split(":") + if len(tmp) != 3: + return -xsconstants.XSERR_BAD_LABEL_FORMAT + otyp, opolicyref, olabel = tmp + # Only ACM is supported + if otyp != xsconstants.ACM_POLICY_ID and \ + otyp != xsconstants.INVALID_POLICY_PREFIX + xsconstants.ACM_POLICY_ID: + return -xsconstants.XSERR_WRONG_POLICY_TYPE + return set_resource_label(resource, "", "", "", olabel) + +def get_resource_label_xapi(resource): + """Get the assigned resource label of a physical resource + in the format used by then Xen-API, i.e., "ACM:xm-test:blue" + + @rtype: string + @return the string representing policy type, policy name and label of + the resource + """ + res = get_resource_label(resource) + return format_resource_label(res) + +def format_resource_label(res): + if res: + if len(res) == 2: + return xsconstants.ACM_POLICY_ID + ":" + res[0] + ":" + res[1] + if len(res) == 3: + return ":".join(res) + return "" + +def get_resource_label(resource): + """ + Xend exports this function via XML-RPC. + + Get the assigned resource label of a given resource + @param resource: The name of a resource, i.e., "phy:/dev/hda" + + @rtype: list + @return tuple of (policy name, resource label), i.e., (xm-test, blue) + """ + try: + resource = unify_resname(resource, mustexist=False) + except Exception: + return [] + + reslabel_map = get_labeled_resources() + + if reslabel_map.has_key(resource): + return list(reslabel_map[resource]) + else: + #Try to resolve each label entry + for key, value in reslabel_map.items(): + try: + if resource == unify_resname(key): + return list(value) + except: + pass + + return [] + + +def get_labeled_resources_xapi(): + """ Get a map of all labeled resource with the labels formatted in the + xen-api resource label format. + """ + reslabel_map = get_labeled_resources() + for key, labeldata in reslabel_map.items(): + reslabel_map[key] = format_resource_label(labeldata) + return reslabel_map + + +def get_labeled_resources(): + """ + Xend exports this function via XML-RPC + + Get a map of all labeled resources. + @rtype: list + @return list of labeled resources + """ + try: + resfile_lock() + try: + access_control = dictio.dict_read("resources", res_label_filename) + except: + return {} + finally: + resfile_unlock() + return access_control + + +def relabel_domains(relabel_list): + """ + Relabel the given domains to have a new ssidref. + @param relabel_list: a list containing tuples of domid, ssidref + example: [ [0, 0x00020002] ] + """ + rel_rules = "" + for r in relabel_list: + log.info("Relabeling domain with domid %d to new ssidref 0x%08x", + r[0], r[1]) + rel_rules += struct.pack("ii", r[0], r[1]) + try: + rc, errors = acm.relabel_domains(rel_rules) + except Exception, e: + log.info("Error after relabel_domains: %s" % str(e)) + rc = -xsconstants.XSERR_GENERAL_FAILURE + errors = "" + if (len(errors) > 0): + rc = -xsconstants.XSERR_HV_OP_FAILED + return rc, errors + + +def __update_label_policy_change(sec_lab, + cur_poltype, + cur_polname, + new_poltype, + new_polname, + polnew_labels, + label_map): + """ + Determine a new resource label given the new policy's type + and name and the new policy's (resource/VM) labels and the + (resource/VM) label map that indicates renaming rules for + labels. + """ + is_deleted = False + policytype, policy, label = sec_lab + + if cur_poltype != policytype or \ + cur_polname != policy: + return sec_lab, is_deleted + + if policytype != xsconstants.ACM_POLICY_ID: + return sec_lab, is_deleted + elif label_map.has_key(label) and policy == cur_polname: + # renaming of an active label; policy may have been renamed + label = label_map[label] + polname = new_polname + elif label not in polnew_labels: + # label been removed + policytype = xsconstants.INVALID_POLICY_PREFIX + policytype + polname = policy + is_deleted = True + else: + # no change to label + policytype = xsconstants.ACM_POLICY_ID + polname = new_polname + + return tuple( [ policytype, polname, label ] ), is_deleted + + +def change_acm_policy(bin_pol, del_array, chg_array, + vmlabel_map, reslabel_map, cur_acmpol, new_acmpol, + is_reset): + """ + Change the ACM policy of the system by relabeling + domains and resources first and doing some access checks. + Then update the policy in the hypervisor. If this is all successful, + relabel the domains permanently and commit the relabed resources. + + Need to do / check the following: + - relabel all resources where there is a 'from' field in + the policy. [ NOT DOING THIS: and mark those as unlabeled where the label + does not appear in the new policy anymore (deletion) ] + - relabel all VMs where there is a 'from' field in the + policy and mark those as unlabeled where the label + does not appear in the new policy anymore; no running + or paused VM may be unlabeled through this + - check that under the new labeling conditions the VMs + still have access to their resources as before. Unlabeled + resources are inaccessible. If this check fails, the + update failed. + - Attempt changes in the hypervisor; if this step fails, + roll back the relabeling of resources and VMs + - Make the relabeling of resources and VMs permanent + + This function should be called with the lock to the domains + held (XendDomain.instance().domains_lock) + """ + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + rc = xsconstants.XSERR_SUCCESS + + domain_label_map = {} + new_policyname = new_acmpol.get_name() + new_policytype = new_acmpol.get_type_name() + cur_policyname = cur_acmpol.get_name() + cur_policytype = cur_acmpol.get_type_name() + polnew_reslabels = new_acmpol.policy_get_resourcelabel_names() + errors="" + + try: + resfile_lock() + mapfile_lock() + + # Get all domains' dominfo. + from xen.xend import XendDomain + dominfos = XendDomain.instance().list('all') + + log.info("----------------------------------------------") + + label_changes = [] + # relabel resources + + access_control = {} + try: + access_control = dictio.dict_read("resources", res_label_filename) + except: + pass + + for key, labeldata in access_control.items(): + if len(labeldata) == 2: + policy, label = labeldata + policytype = xsconstants.ACM_POLICY_ID + elif len(labeldata) == 3: + policytype, policy, label = labeldata + else: + return -xsconstants.XSERR_BAD_LABEL_FORMAT, "" + + new_sec_lab, is_deleted = \ + __update_label_policy_change( tuple([policytype, + policy, + label]), + cur_policytype, + cur_policyname, + new_policytype, + new_policyname, + polnew_reslabels, + reslabel_map) + + if is_deleted: + label_changes.append(key) + # Update entry + access_control[key] = new_sec_lab + + # All resources have new labels in the access_control map + # There may still be labels in there that are invalid now. + + # Do this in memory without writing to disk: + # - Relabel all domains independent of whether they are running + # or not + # - later write back to config files + polnew_vmlabels = new_acmpol.policy_get_virtualmachinelabel_names() + + for dominfo in dominfos: + sec_lab = dominfo.get_security_label() + if not sec_lab: + continue + policytype, policy, vmlabel = sec_lab.split(":") + name = dominfo.getName() + + if policytype != cur_policytype or \ + policy != cur_policyname: + continue + + new_vmlabel = vmlabel + if vmlabel_map.has_key(vmlabel) and \ + (not is_reset or name == "Domain-0") : + # renaming of the label; this is only allowed if it's + # not a reset of the policy or if it is a reset, then + # only for Domain-0 + new_vmlabel = vmlabel_map[vmlabel] + polname = new_policyname + elif new_vmlabel not in polnew_vmlabels and \ + vmlabel != ACM_LABEL_UNLABELED: + # removal of VM label and not the 'unlabeled' label + policytype = xsconstants.INVALID_POLICY_PREFIX + policytype + polname = policy + else: + polname = new_policyname + + new_seclab = "%s:%s:%s" % \ + (policytype, polname, new_vmlabel) + + domain_label_map[dominfo] = [ sec_lab, new_seclab ] + + if dominfo._stateGet() in (DOM_STATE_PAUSED, DOM_STATE_RUNNING): + compatible = __resources_compatible_with_vmlabel(new_acmpol, + dominfo, + new_vmlabel, + access_control, + is_policy_update=True) + log.info("Domain %s with new label '%s' can access its " + "resources? : %s" % + (name, new_vmlabel, str(compatible))) + log.info("VM labels in new policy: %s" % + new_acmpol.policy_get_virtualmachinelabel_names()) + if not compatible: + return (-xsconstants.XSERR_RESOURCE_ACCESS, "") + + for dominfo in dominfos: + # relabel the VIF interfaces + changed = False + for vif_uuid in dominfo.get_vifs(): + sec_lab = dominfo.info['devices'][vif_uuid][1]\ + .get('security_label') + if sec_lab: + result, _ = \ + __update_label_policy_change(tuple(sec_lab.split(':')), + cur_policytype, + cur_policyname, + new_policytype, + new_policyname, + polnew_reslabels, + reslabel_map) + new_sec_lab = ':'.join(list(result)) + if new_sec_lab != sec_lab: + changed = True + dominfo.info['devices'][vif_uuid][1]\ + ['security_label'] = new_sec_lab + if changed: + XendDomain.instance().managed_config_save(dominfo) + + rc, errors = hv_chg_policy(bin_pol, del_array, chg_array) + if rc == 0: + for key in label_changes: + run_resource_label_change_script(key, "", "remove") + # Write the relabeled resources back into the file + dictio.dict_write(access_control, "resources", res_label_filename) + # Properly update all VMs to their new labels + for dominfo, labels in domain_label_map.items(): + sec_lab, new_seclab = labels + if sec_lab != new_seclab: + log.info("Updating domain %s to new label '%s'." % \ + (dominfo.getName(), new_seclab)) + # This better be working! + res = dominfo.set_security_label(new_seclab, + sec_lab, + new_acmpol, + cur_acmpol) + if res[0] != xsconstants.XSERR_SUCCESS: + log.info("ERROR: Could not chg label on domain %s: %s" % + (dominfo.getName(), + xsconstants.xserr2string(-int(res[0])))) + finally: + log.info("----------------------------------------------") + mapfile_unlock() + resfile_unlock() + + return rc, errors + +def parse_security_label(security_label): + tmp = security_label.split(":") + if len(tmp) != 3: + return "" + else: + return security_label + +def set_security_label(policy, label): + if label and policy and label != "" and policy != "": + return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, policy, label) + else: + return "" + +def ssidref2security_label(ssidref): + from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + return XSPolicyAdminInstance().ssidref_to_vmlabel(ssidref) + +def get_security_label(self, xspol=None): + """ + Get the security label of a domain + @param xspol The policy to use when converting the ssid into + a label; only to be passed during the updating + of the policy + """ + domid = self.getDomid() + + if not xspol: + from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + xspol = XSPolicyAdminInstance().get_loaded_policy() + + label = "" + if xspol: + label = xspol.policy_get_domain_label_formatted(domid) + if domid != 0: + label = self.info.get('security_label', label) + return label + + +def check_can_run(sec_label): + """ Check whether a VM could run, given its vm label. A VM can run if + - it is authorized + - is not in conflict with any running domain + """ + try: + mapfile_lock() + + if sec_label == None or sec_label == "": + vm_label = ACM_LABEL_UNLABELED + else: + poltype, policy, vm_label = sec_label.split(':') + if policy != get_active_policy_name(): + return -xsconstants.XSERR_BAD_POLICY_NAME + ssidref = label2ssidref(vm_label, policy, 'dom') + if ssidref != xsconstants.INVALID_SSIDREF: + if not has_authorization(ssidref): + return -xsconstants.XSERR_VM_NOT_AUTHORIZED + if is_in_conflict(ssidref): + return -xsconstants.XSERR_VM_IN_CONFLICT + return -xsconstants.XSERR_SUCCESS + else: + return -xsconstants.XSERR_BAD_LABEL + finally: + mapfile_unlock() + + +__cond = threading.Condition() +__script_runner = None +__orders = [] + +def run_resource_label_change_script(resource, label, command): + global __cond, __orders, __script_runner + + def __run_resource_label_change_script(): + global __cond, __orders + script = XendOptions.instance().get_resource_label_change_script() + if script: + parms = {} + while True: + __cond.acquire() + if len(__orders) == 0: + __cond.wait() + + parms['label'], \ + parms['command'], \ + parms['resource'] = __orders[0] + + __orders = __orders[1:] + __cond.release() + + log.info("Running resource label change script %s: %s" % + (script, parms)) + parms.update(os.environ) + os.spawnve(os.P_WAIT, script[0], script, parms) + else: + log.info("No script given for relabeling of resources.") + if not __script_runner: + __script_runner = \ + threading.Thread(target=__run_resource_label_change_script, + args=()) + __script_runner.start() + + __cond.acquire() + __orders.append((label,command,resource)) + __cond.notify() + __cond.release() diff --git a/tools/python/xen/util/xsm/dummy/__init__.py b/tools/python/xen/util/xsm/dummy/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/util/xsm/dummy/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/util/xsm/dummy/dummy.py b/tools/python/xen/util/xsm/dummy/dummy.py new file mode 100644 index 0000000..795d637 --- /dev/null +++ b/tools/python/xen/util/xsm/dummy/dummy.py @@ -0,0 +1,136 @@ +import sys +from xen.util import xsconstants +from xen.xend.XendLogging import log + +class XSMError(Exception): + def __init__(self,value): + self.value = value + def __str__(self): + return repr(self.value) + + +security_dir_prefix = ""; +policy_dir_prefix = ""; +active_policy = ""; +NULL_SSIDREF = 0; + +#Functions exported through XML-RPC +xmlrpc_exports = [ + 'set_resource_label', + 'get_resource_label', + 'list_labels', + 'get_labeled_resources', + 'set_policy', + 'reset_policy', + 'get_policy', + 'activate_policy', + 'rm_bootpolicy', + 'get_xstype', + 'get_domain_label', + 'set_domain_label' +] + +def err(msg): + """Raise XSM-dummy exception. + """ + raise XSMError(msg) + +def on(): + return 0 + +def ssidref2label(ssidref): + return 0 + +def label2ssidref(label, policy, type): + return 0 + +def res_security_check(resource, domain_label): + return 1 + +def get_res_security_details(resource): + return ("","","") + +def get_res_label(resource): + return ("","") + +def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label): + return 1 + +def parse_security_label(security_label): + return "" + +def calc_dom_ssidref_from_info(info): + return "" + +def set_security_label(policy, label): + return "" + +def ssidref2security_label(ssidref): + return "" + +def has_authorization(ssidref): + return True + +def get_security_label(self, xspol=None): + return "" + +def get_resource_label_xapi(resource): + return "" + +def get_labeled_resources_xapi(): + return {} + +def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi): + err("Command not supported under XSM 'dummy' module.") + +def format_resource_label(res): + return "" + +def set_resource_label(resource, policytype, policyref, reslabel, + oreslabel = None): + err("Command not supported under XSM 'dummy' module.") + +def get_resource_label(resource): + return "" + +def list_labels(policy_name, ltype): + return [] + +def get_labeled_resources(): + return {} + +def set_policy(xs_type, xml, flags, overwrite): + err("Command not supported under xsm 'dummy' module.") + +def reset_policy(): + err("Command not supported under xsm 'dummy' module.") + +def get_policy(): + return "", 0 + +def activate_policy(): + err("Command not supported under xsm 'dummy' module.") + +def rm_bootpolicy(): + err("Command not supported under xsm 'dummy' module.") + +def get_xstype(): + return 0 + +def get_domain_label(domain): + return "" + +def set_domain_label(): + err("Command not supported under xsm 'dummy' module.") + +def dump_policy(): + pass + +def dump_policy_file(): + pass + +def get_ssid(domain): + err("No ssid has been assigned to any domain under xsm dummy module.") + +def security_label_to_details(res_label): + return ("","","") diff --git a/tools/python/xen/util/xsm/flask/__init__.py b/tools/python/xen/util/xsm/flask/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/util/xsm/flask/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/util/xsm/flask/flask.py b/tools/python/xen/util/xsm/flask/flask.py new file mode 100644 index 0000000..04dc391 --- /dev/null +++ b/tools/python/xen/util/xsm/flask/flask.py @@ -0,0 +1,49 @@ +import sys +from xen.lowlevel import flask +from xen.util import xsconstants +from xen.xend import sxp + +#Functions exported through XML-RPC +xmlrpc_exports = [ ] + +def err(msg): + """Raise XSM-Flask exception. + """ + sys.stderr.write("XSM-FlaskError: " + msg + "\n") + raise XSMError(msg) + +def on(): + return xsconstants.XS_POLICY_FLASK + +def ssidref2label(ssidref): + try: + return flask.flask_sid_to_context(ssidref) + except: + return "" + +def label2ssidref(label, policy, type): + try: + return flask.flask_context_to_sid(label) + except: + return "" + +def parse_security_label(security_label): + return security_label + +def calc_dom_ssidref_from_info(info): + ssidref = label2ssidref(info['security_label'], "", "") + return ssidref + +def set_security_label(policy, label): + if label: + return label + else: + return "" + +def ssidref2security_label(ssidref): + label = ssidref2label(ssidref) + return label + +def get_security_label(self, xspol=None): + label = self.info['security_label'] + return label diff --git a/tools/python/xen/util/xsm/xsm.py b/tools/python/xen/util/xsm/xsm.py new file mode 100644 index 0000000..1c093c6 --- /dev/null +++ b/tools/python/xen/util/xsm/xsm.py @@ -0,0 +1,20 @@ +import sys +import string +from xen.xend import XendOptions +from xen.util import xsconstants +from xsm_core import xsm_init + +xoptions = XendOptions.instance() +xsm_module_name = xoptions.get_xsm_module_name() + +xsconstants.XS_POLICY_USE = eval("xsconstants.XS_POLICY_" + + string.upper(xsm_module_name)) + +xsm_module_path = "xen.util.xsm." + xsm_module_name + "." + xsm_module_name +xsm_module = __import__(xsm_module_path, globals(), locals(), ['*']) + +xsm_init(xsm_module) + +for op in dir(xsm_module): + if not hasattr(sys.modules[__name__], op): + setattr(sys.modules[__name__], op, getattr(xsm_module, op, None)) diff --git a/tools/python/xen/util/xsm/xsm_core.py b/tools/python/xen/util/xsm/xsm_core.py new file mode 100644 index 0000000..f40af12 --- /dev/null +++ b/tools/python/xen/util/xsm/xsm_core.py @@ -0,0 +1,7 @@ +import sys +import xen.util.xsm.dummy.dummy as dummy + +def xsm_init(self): + for op in dir(dummy): + if not hasattr(self, op): + setattr(self, op, getattr(dummy, op, None)) diff --git a/tools/python/xen/util/xspolicy.py b/tools/python/xen/util/xspolicy.py new file mode 100644 index 0000000..7cd5656 --- /dev/null +++ b/tools/python/xen/util/xspolicy.py @@ -0,0 +1,66 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006,2007 International Business Machines Corp. +# Author: Stefan Berger +#============================================================================ + +import threading +import xsconstants + +class XSPolicy: + """ + The base policy class for all policies administered through + XSPolicyAdmin. + """ + + def __init__(self, name=None, ref=None): + self.lock = threading.Lock() + self.ref = ref + self.name = name + if ref: + from xen.xend.XendXSPolicy import XendXSPolicy + self.xendxspolicy = XendXSPolicy(self, {}, ref) + else: + self.xendxspolicy = None + + def grab_lock(self): + self.lock.acquire() + + def unlock(self): + self.lock.release() + + def get_ref(self): + return self.ref + + def destroy(self): + if self.xendxspolicy: + self.xendxspolicy.destroy() + + # All methods below should be overwritten by the inheriting class + + def isloaded(self): + return False + + def loadintohv(self): + return xsconstants.XSERR_POLICY_LOAD_FAILED + + def get_type(self): + return xsconstants.XS_POLICY_NONE + + def get_type_name(self): + return "" + + def update(self, repr_new): + return -xsconstants.XSERR_GENERAL_FAILURE, "" diff --git a/tools/python/xen/web/SrvBase.py b/tools/python/xen/web/SrvBase.py new file mode 100644 index 0000000..bf59b55 --- /dev/null +++ b/tools/python/xen/web/SrvBase.py @@ -0,0 +1,98 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +import types + + +from xen.xend import sxp +from xen.xend import PrettyPrint +from xen.xend.Args import ArgError +from xen.xend.XendError import XendError +from xen.xend.XendLogging import log + +import resource +import http +import httpserver + +def uri_pathlist(p): + """Split a path into a list. + p path + return list of path elements + """ + l = [] + for x in p.split('/'): + if x == '': continue + l.append(x) + return l + +class SrvBase(resource.Resource): + """Base class for services. + """ + + + def use_sxp(self, req): + return req.useSxp() + + def get_op_method(self, op): + """Get the method for an operation. + For operation 'foo' looks for 'op_foo'. + + op operation name + returns method or None + """ + op_method_name = 'op_' + op + return getattr(self, op_method_name, None) + + def perform(self, req): + """General operation handler for posted operations. + For operation 'foo' looks for a method op_foo and calls + it with op_foo(op, req). Replies with code 500 if op_foo + is not found. + + The method must return a list when req.use_sxp is true + and an HTML string otherwise (or list). + Methods may also return a ThreadRequest (for incomplete processing). + + req request + """ + op = req.args.get('op') + if op is None or len(op) != 1: + req.setResponseCode(http.NOT_ACCEPTABLE, "Invalid request") + return '' + op = op[0] + op_method = self.get_op_method(op) + if op_method is None: + req.setResponseCode(http.NOT_IMPLEMENTED, "Operation not implemented: " + op) + req.setHeader("Content-Type", "text/plain") + req.write("Operation not implemented: " + op) + return '' + else: + try: + return op_method(op, req) + except Exception, exn: + req.setResponseCode(http.INTERNAL_SERVER_ERROR, "Request failed: " + op) + log.exception("Request %s failed.", op) + if req.useSxp(): + return ['xend.err', str(exn)] + else: + return "

%s

" % str(exn) + + def print_path(self, req): + """Print the path with hyperlinks. + """ + req.printPath() + diff --git a/tools/python/xen/web/SrvDir.py b/tools/python/xen/web/SrvDir.py new file mode 100644 index 0000000..7d8001a --- /dev/null +++ b/tools/python/xen/web/SrvDir.py @@ -0,0 +1,124 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +import types + +from xen.xend import sxp +from xen.xend import PrettyPrint +from xen.xend.Args import ArgError +from xen.xend.XendError import XendError +#from xen.xend.XendLogging import log + +import resource +import http + +from xen.web.SrvBase import SrvBase + +class SrvConstructor: + """Delayed constructor for sub-servers. + Does not import the sub-server class or create the object until needed. + """ + + def __init__(self, klass): + """Create a constructor. It is assumed that the class + should be imported as 'from xen.xend.server.klass import klass'. + + klass name of its class + """ + self.klass = klass + self.obj = None + + def getobj(self): + """Get the sub-server object, importing its class and instantiating it if + necessary. + """ + if not self.obj: + exec 'from xen.xend.server.%s import %s' % (self.klass, self.klass) + klassobj = eval(self.klass) + self.obj = klassobj() + return self.obj + +class SrvDir(SrvBase): + """Base class for directory servlets. + """ + isLeaf = False + + def __init__(self): + SrvBase.__init__(self) + self.table = {} + self.order = [] + + def noChild(self, msg): + return resource.ErrorPage(http.NOT_FOUND, msg=msg) + + def getChild(self, x, req): + if x == '': return self + try: + val = self.get(x) + except XendError, ex: + return self.noChild(str(ex)) + if val is None: + return self.noChild('Not found: ' + str(x)) + else: + return val + + def get(self, x): + val = self.table.get(x) + if isinstance(val, SrvConstructor): + val = val.getobj() + return val + + def add(self, x, v=None): + if v is None: + v = 'SrvDir' + if isinstance(v, types.StringType): + v = SrvConstructor(v) + self.table[x] = v + self.order.append(x) + return v + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-type", sxp.mime_type) + self.ls(req, 1) + else: + req.write('') + self.print_path(req) + self.ls(req) + self.form(req) + req.write('') + return '' + + def ls(self, req, use_sxp=0): + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + if use_sxp: + req.write('(ls ') + for k in self.order: + req.write(' ' + k) + req.write(')') + else: + req.write('
    ') + for k in self.order: + v = self.get(k) + req.write('
  • %s
  • ' + % (url, k, k)) + req.write('
') + + def form(self, req): + pass diff --git a/tools/python/xen/web/__init__.py b/tools/python/xen/web/__init__.py new file mode 100644 index 0000000..718ac55 --- /dev/null +++ b/tools/python/xen/web/__init__.py @@ -0,0 +1,17 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +#============================================================================ + diff --git a/tools/python/xen/web/connection.py b/tools/python/xen/web/connection.py new file mode 100644 index 0000000..7d5702f --- /dev/null +++ b/tools/python/xen/web/connection.py @@ -0,0 +1,294 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd. +#============================================================================ + +import sys +import os +import threading +import socket +import fcntl + +from errno import EAGAIN, EINTR, EWOULDBLOCK + +try: + from OpenSSL import SSL +except ImportError: + pass + +from xen.xend.XendLogging import log + +"""General classes to support server and client sockets, without +specifying what kind of socket they are. There are subclasses +for TCP and unix-domain sockets (see tcp.py and unix.py). +""" + +BUFFER_SIZE = 1024 +BACKLOG = 5 + + +class SocketServerConnection: + """An accepted connection to a server. + """ + + def __init__(self, sock, protocol_class): + self.sock = sock + self.protocol = protocol_class() + self.protocol.setTransport(self) + threading.Thread(target=self.main).start() + + + def main(self): + try: + while True: + try: + data = self.sock.recv(BUFFER_SIZE) + if data == '': + break + if self.protocol.dataReceived(data): + break + except socket.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + break + finally: + try: + self.sock.close() + except: + pass + + + def close(self): + self.sock.close() + + + def write(self, data): + self.sock.send(data) + + +class SocketListener: + """A server socket, running listen in a thread. + Accepts connections and runs a thread for each one. + """ + + def __init__(self, protocol_class): + self.protocol_class = protocol_class + self.sock = self.createSocket() + threading.Thread(target=self.main).start() + + + def close(self): + try: + self.sock.close() + except: + pass + + + def createSocket(self): + raise NotImplementedError() + + + def acceptConnection(self, sock, protocol, addr): + raise NotImplementedError() + + + def main(self): + try: + fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC) + self.sock.listen(BACKLOG) + + while True: + try: + (sock, addr) = self.sock.accept() + self.acceptConnection(sock, addr) + except socket.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + break + finally: + self.close() + + +class SSLSocketServerConnection(SocketServerConnection): + """An SSL aware accepted connection to a server. + + As pyOpenSSL SSL.Connection fileno() method just retrieve the file + descriptor number for the underlying socket, direct read/write to the file + descriptor will result no data encrypted. + + recv2fd() and fd2send() are simple wrappers for functions who need direct + read/write to a file descriptor rather than a socket like object. + + To use recv2fd(), you can create a pipe and start a thread to transfer all + received data to one end of the pipe, then read from the other end: + + p2cread, p2cwrite = os.pipe() + threading.Thread(target=connection.SSLSocketServerConnection.recv2fd, + args=(sock, p2cwrite)).start() + os.read(p2cread, 1024) + + To use fd2send(): + + p2cread, p2cwrite = os.pipe() + threading.Thread(target=connection.SSLSocketServerConnection.fd2send, + args=(sock, p2cread)).start() + os.write(p2cwrite, "data") + """ + + def __init__(self, sock, protocol_class): + SocketServerConnection.__init__(self, sock, protocol_class) + + + def main(self): + try: + while True: + try: + data = self.sock.recv(BUFFER_SIZE) + if data == "": + break + if self.protocol.dataReceived(data): + break + except socket.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + break + except (SSL.WantReadError, SSL.WantWriteError, \ + SSL.WantX509LookupError): + # The operation did not complete; the same I/O method + # should be called again. + continue + except SSL.ZeroReturnError: + # The SSL Connection has been closed. + break + except SSL.SysCallError, (retval, desc): + if ((retval == -1 and desc == "Unexpected EOF") + or retval > 0): + # The SSL Connection is lost. + break + log.debug("SSL SysCallError:%d:%s" % (retval, desc)) + break + except SSL.Error, e: + # other SSL errors + log.debug("SSL Error:%s" % e) + break + finally: + try: + self.sock.close() + except: + pass + + + def recv2fd(sock, fd): + try: + while True: + try: + data = sock.recv(BUFFER_SIZE) + if data == "": + break + count = 0 + while count < len(data): + try: + nbytes = os.write(fd, data[count:]) + count += nbytes + except os.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + raise + except socket.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + break + except (SSL.WantReadError, SSL.WantWriteError, \ + SSL.WantX509LookupError): + # The operation did not complete; the same I/O method + # should be called again. + continue + except SSL.ZeroReturnError: + # The SSL Connection has been closed. + break + except SSL.SysCallError, (retval, desc): + if ((retval == -1 and desc == "Unexpected EOF") + or retval > 0): + # The SSL Connection is lost. + break + log.debug("SSL SysCallError:%d:%s" % (retval, desc)) + break + except SSL.Error, e: + # other SSL errors + log.debug("SSL Error:%s" % e) + break + finally: + try: + sock.close() + os.close(fd) + except: + pass + + recv2fd = staticmethod(recv2fd) + + + def fd2send(sock, fd): + try: + while True: + try: + data = os.read(fd, BUFFER_SIZE) + if data == "": + break + count = 0 + while count < len(data): + try: + nbytes = sock.send(data[count:]) + count += nbytes + except socket.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + raise + except (SSL.WantReadError, SSL.WantWriteError, \ + SSL.WantX509LookupError): + # The operation did not complete; the same I/O method + # should be called again. + continue + except SSL.ZeroReturnError: + # The SSL Connection has been closed. + raise + except SSL.SysCallError, (retval, desc): + if not (retval == -1 and data == ""): + # errors when writing empty strings are expected + # and can be ignored + log.debug("SSL SysCallError:%d:%s" % (retval, desc)) + raise + except SSL.Error, e: + # other SSL errors + log.debug("SSL Error:%s" % e) + raise + except os.error, ex: + if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR): + break + finally: + try: + sock.close() + os.close(fd) + except: + pass + + fd2send = staticmethod(fd2send) + + +def hostAllowed(addrport, hosts_allowed): + if hosts_allowed is None: + return True + else: + fqdn = socket.getfqdn(addrport[0]) + for h in hosts_allowed: + if h.match(fqdn) or h.match(addrport[0]): + return True + log.warn("Rejected connection from %s (%s).", addrport[0], fqdn) + return False diff --git a/tools/python/xen/web/http.py b/tools/python/xen/web/http.py new file mode 100644 index 0000000..e11d8d2 --- /dev/null +++ b/tools/python/xen/web/http.py @@ -0,0 +1,518 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#============================================================================ +# Parts of this library are derived from Twisted: +# Copyright (C) 2001 Matthew W. Lefkowitz +# +# Copyright (C) 2005 Mike Wray +#============================================================================ + +from mimetools import Message +from cStringIO import StringIO +import math +import socket +import time +import cgi + +CONTINUE = 100 +SWITCHING_PROTOCOLS = 101 + +OK = 200 +CREATED = 201 +ACCEPTED = 202 +NON_AUTHORITATIVE_INFORMATION = 203 +NO_CONTENT = 204 +RESET_CONTENT = 205 +PARTIAL_CONTENT = 206 +MULTI_STATUS = 207 + +MULTIPLE_CHOICE = 300 +MOVED_PERMANENTLY = 301 +FOUND = 302 +SEE_OTHER = 303 +NOT_MODIFIED = 304 +USE_PROXY = 305 +TEMPORARY_REDIRECT = 307 + +BAD_REQUEST = 400 +UNAUTHORIZED = 401 +PAYMENT_REQUIRED = 402 +FORBIDDEN = 403 +NOT_FOUND = 404 +NOT_ALLOWED = 405 +NOT_ACCEPTABLE = 406 +PROXY_AUTH_REQUIRED = 407 +REQUEST_TIMEOUT = 408 +CONFLICT = 409 +GONE = 410 +LENGTH_REQUIRED = 411 +PRECONDITION_FAILED = 412 +REQUEST_ENTITY_TOO_LARGE = 413 +REQUEST_URI_TOO_LONG = 414 +UNSUPPORTED_MEDIA_TYPE = 415 +REQUESTED_RANGE_NOT_SATISFIABLE = 416 +EXPECTATION_FAILED = 417 + +INTERNAL_SERVER_ERROR = 500 +NOT_IMPLEMENTED = 501 +BAD_GATEWAY = 502 +SERVICE_UNAVAILABLE = 503 +GATEWAY_TIMEOUT = 504 +VERSION_NOT_SUPPORTED = 505 +INSUFFICIENT_STORAGE_SPACE = 507 +NOT_EXTENDED = 510 + +NO_BODY_CODES = [ NO_CONTENT, NOT_MODIFIED ] + + +STATUS = { + CONTINUE : "Continue", + SWITCHING_PROTOCOLS : "Switching protocols", + + OK : "OK", + CREATED : "Created", + ACCEPTED : "Accepted", + NON_AUTHORITATIVE_INFORMATION : "Non-authoritative information", + NO_CONTENT : "No content", + RESET_CONTENT : "Reset content", + PARTIAL_CONTENT : "Partial content", + MULTI_STATUS : "Multi-status", + + MULTIPLE_CHOICE : "Multiple choice", + MOVED_PERMANENTLY : "Moved permanently", + FOUND : "Found", + SEE_OTHER : "See other", + NOT_MODIFIED : "Not modified", + USE_PROXY : "Use proxy", + TEMPORARY_REDIRECT : "Temporary redirect", + + BAD_REQUEST : "Bad request", + UNAUTHORIZED : "Unauthorized", + PAYMENT_REQUIRED : "Payment required", + FORBIDDEN : "Forbidden", + NOT_FOUND : "Not found", + NOT_ALLOWED : "Not allowed", + NOT_ACCEPTABLE : "Not acceptable", + PROXY_AUTH_REQUIRED : "Proxy authentication required", + REQUEST_TIMEOUT : "Request timeout", + CONFLICT : "Conflict", + GONE : "Gone", + LENGTH_REQUIRED : "Length required", + PRECONDITION_FAILED : "Precondition failed", + REQUEST_ENTITY_TOO_LARGE : "Request entity too large", + REQUEST_URI_TOO_LONG : "Request URI too long", + UNSUPPORTED_MEDIA_TYPE : "Unsupported media type", + REQUESTED_RANGE_NOT_SATISFIABLE : "Requested range not satisfiable", + EXPECTATION_FAILED : "Expectation failed", + + INTERNAL_SERVER_ERROR : "Internal server error", + NOT_IMPLEMENTED : "Not implemented", + BAD_GATEWAY : "Bad gateway", + SERVICE_UNAVAILABLE : "Service unavailable", + GATEWAY_TIMEOUT : "Gateway timeout", + VERSION_NOT_SUPPORTED : "HTTP version not supported", + INSUFFICIENT_STORAGE_SPACE : "Insufficient storage space", + NOT_EXTENDED : "Not extended", + } + +def getStatus(code): + return STATUS.get(code, "unknown") + +MULTIPART_FORM_DATA = 'multipart/form-data' +URLENCODED = 'application/x-www-form-urlencoded' + +parseQueryArgs = cgi.parse_qs + +def timegm(year, month, day, hour, minute, second): + """Convert time tuple in GMT to seconds since epoch, GMT""" + EPOCH = 1970 + assert year >= EPOCH + assert 1 <= month <= 12 + days = 365*(year-EPOCH) + calendar.leapdays(EPOCH, year) + for i in range(1, month): + days = days + calendar.mdays[i] + if month > 2 and calendar.isleap(year): + days = days + 1 + days = days + day - 1 + hours = days*24 + hour + minutes = hours*60 + minute + seconds = minutes*60 + second + return seconds + +def stringToDatetime(dateString): + """Convert an HTTP date string to seconds since epoch.""" + parts = dateString.split(' ') + day = int(parts[1]) + month = int(monthname.index(parts[2])) + year = int(parts[3]) + hour, min, sec = map(int, parts[4].split(':')) + return int(timegm(year, month, day, hour, min, sec)) + +class HttpRequest: + + http_version = (1, 1) + + http_version_string = ("HTTP/%d.%d" % http_version) + + max_content_length = 10000 + max_headers = 500 + + request_line = None + request_method = None + request_uri = None + request_path = None + request_query = None + request_version = None + content_length = 0 + content = None + etag = None + close_connection = True + response_code = 200 + response_status = "OK" + response_sent = False + cached = False + last_modified = None + + forceSSL = False + + def __init__(self, host, rin, out): + self.host = host + self.rin = rin + self.out = out + self.request_args = {} + self.args = self.request_args + self.request_headers = {} + self.request_cookies = {} + self.response_headers = {} + self.response_cookies = {} + self.output = StringIO() + self.parseRequest() + + def isSecure(self): + return self.forceSSL + + def getRequestMethod(self): + return self.request_method + + def trim(self, str, ends): + for end in ends: + if str.endswith(end): + str = str[ : -len(end) ] + break + return str + + def requestError(self, code, msg=None): + self.sendError(code, msg) + raise ValueError(self.response_status) + + def sendError(self, code, msg=None): + self.setResponseCode(code, msg=msg) + self.sendResponse() + + def parseRequestVersion(self, version): + try: + if not version.startswith('HTTP/'): + raise ValueError + version_string = version.split('/', 1)[1] + version_codes = version_string.split('.') + if len(version_codes) != 2: + raise ValueError + request_version = (int(version_codes[0]), int(version_codes[1])) + except (ValueError, IndexError): + self.requestError(400, "Bad request version (%s)" % `version`) + + def parseRequestLine(self): + line = self.trim(self.request_line, ['\r\n', '\n']) + line_fields = line.split() + n = len(line_fields) + if n == 3: + [method, uri, version] = line_fields + elif n == 2: + [method, uri] = line_fields + version = 'HTTP/0.9' + else: + self.requestError(BAD_REQUEST, + "Bad request (%s)" % `line`) + + request_version = self.parseRequestVersion(version) + + if request_version > (2, 0): + self.requestError(VERSION_NOT_SUPPORTED, + "HTTP version not supported (%s)" % `version`) + #if request_version >= (1, 1) and self.http_version >= (1, 1): + # self.close_connection = False + #else: + # self.close_connection = True + + self.request_method = method + self.method = method + self.request_uri = uri + self.request_version = version + + uri_query = uri.split('?') + if len(uri_query) == 1: + self.request_path = uri + else: + self.request_path = uri_query[0] + self.request_query = uri_query[1] + self.request_args = parseQueryArgs(self.request_query) + self.args = self.request_args + + + def parseRequestHeaders(self): + header_bytes = "" + header_count = 0 + while True: + if header_count >= self.max_headers: + self.requestError(BAD_REQUEST, + "Bad request (too many headers)") + line = self.rin.readline() + header_bytes += line + header_count += 1 + if line == '\r\n' or line == '\n' or line == '': + break + header_input = StringIO(header_bytes) + self.request_headers = Message(header_input) + + def parseRequestCookies(self): + cookie_hdr = self.getHeader("cookie") + if not cookie_hdr: return + for cookie in cookie_hdr.split(';'): + try: + cookie = cookie.lstrip() + (k, v) = cookie.split('=', 1) + self.request_cookies[k] = v + except ValueError: + pass + + def parseRequestArgs(self): + if ((self.content is None) or + (self.request_method != "POST")): + return + content_type = self.getHeader('content-type') + if not content_type: + return + (encoding, params) = cgi.parse_header(content_type) + if encoding == URLENCODED: + xargs = cgi.parse_qs(self.content.getvalue(), + keep_blank_values=True) + elif encoding == MULTIPART_FORM_DATA: + xargs = cgi.parse_multipart(self.content, params) + else: + xargs = {} + self.request_args.update(xargs) + + def getCookie(self, k): + return self.request_cookies[k] + + def readContent(self): + try: + self.content_length = int(self.getHeader("Content-Length")) + except: + return + if self.content_length > self.max_content_length: + self.requestError(REQUEST_ENTITY_TOO_LARGE) + self.content = self.rin.read(self.content_length) + self.content = StringIO(self.content) + self.content.seek(0,0) + + def parseRequest(self): + self.request_line = self.rin.readline() + self.parseRequestLine() + self.parseRequestHeaders() + self.parseRequestCookies() + connection_mode = self.getHeader('Connection') + self.setCloseConnection(connection_mode) + self.readContent() + self.parseRequestArgs() + + def setCloseConnection(self, mode): + if not mode: return + mode = mode.lower() + if mode == 'close': + self.close_connection = True + elif (mode == 'keep-alive') and (self.http_version >= (1, 1)): + self.close_connection = False + + def getCloseConnection(self): + return self.close_connection + + def getHeader(self, k, v=None): + return self.request_headers.get(k, v) + + def getRequestMethod(self): + return self.request_method + + def getRequestPath(self): + return self.request_path + + def setResponseCode(self, code, status=None, msg=None): + self.response_code = code + if not status: + status = getStatus(code) + self.response_status = status + + def setResponseHeader(self, k, v): + k = k.lower() + self.response_headers[k] = v + if k == 'connection': + self.setCloseConnection(v) + + setHeader = setResponseHeader + + def setLastModified(self, when): + # time.time() may be a float, but the HTTP-date strings are + # only good for whole seconds. + when = long(math.ceil(when)) + if (not self.last_modified) or (self.last_modified < when): + self.lastModified = when + + modified_since = self.getHeader('if-modified-since') + if modified_since: + modified_since = stringToDatetime(modified_since) + if modified_since >= when: + self.setResponseCode(NOT_MODIFIED) + self.cached = True + + def setContentType(self, ty): + self.setResponseHeader("Content-Type", ty) + + def setEtag(self, etag): + if etag: + self.etag = etag + + tags = self.getHeader("if-none-match") + if tags: + tags = tags.split() + if (etag in tags) or ('*' in tags): + if self.request_method in ("HEAD", "GET"): + code = NOT_MODIFIED + else: + code = PRECONDITION_FAILED + self.setResponseCode(code) + self.cached = True + + def addCookie(self, k, v, expires=None, domain=None, path=None, + max_age=None, comment=None, secure=None): + cookie = v + if expires != None: + cookie += "; Expires=%s" % expires + if domain != None: + cookie += "; Domain=%s" % domain + if path != None: + cookie += "; Path=%s" % path + if max_age != None: + cookie += "; Max-Age=%s" % max_age + if comment != None: + cookie += "; Comment=%s" % comment + if secure: + cookie += "; Secure" + self.response_cookies[k] = cookie + + def sendResponseHeaders(self): + if self.etag: + self.setResponseHeader("ETag", self.etag) + for (k, v) in self.response_headers.items(): + self.send("%s: %s\r\n" % (k.capitalize(), v)) + for (k, v) in self.response_cookies.items(): + self.send("Set-Cookie: %s=%s\r\n" % (k, v)) + self.send("\r\n") + + def sendResponse(self): + if self.response_sent: + return + self.response_sent = True + send_body = self.hasBody() + if not self.close_connection: + self.setResponseHeader("Connection", "keep-alive") + self.setResponseHeader("Pragma", "no-cache") + self.setResponseHeader("Cache-Control", "no-cache") + self.setResponseHeader("Expires", "-1") + if send_body: + self.output.seek(0, 0) + body = self.output.getvalue() + body_length = len(body) + self.setResponseHeader("Content-Length", body_length) + if self.http_version > (0, 9): + self.send("%s %d %s\r\n" % (self.http_version_string, + self.response_code, + self.response_status)) + self.sendResponseHeaders() + if send_body: + self.send(body) + self.flush() + + def write(self, data): + self.output.write(data) + + def send(self, data): + #print 'send>', data + self.out.write(data) + + def flush(self): + self.out.flush() + + def hasNoBody(self): + return ((self.request_method == "HEAD") or + (self.response_code in NO_BODY_CODES) or + self.cached) + + def hasBody(self): + return not self.hasNoBody() + + def process(self): + pass + return self.close_connection + + def getRequestHostname(self): + """Get the hostname that the user passed in to the request. + + Uses the 'Host:' header if it is available, and the + host we are listening on otherwise. + """ + return (self.getHeader('host') or + socket.gethostbyaddr(self.getHostAddr())[0] + ).split(':')[0] + + def getHost(self): + return self.host + + def getHostAddr(self): + return self.host[0] + + def getPort(self): + return self.host[1] + + def setHost(self, host, port, ssl=0): + """Change the host and port the request thinks it's using. + + This method is useful for working with reverse HTTP proxies (e.g. + both Squid and Apache's mod_proxy can do this), when the address + the HTTP client is using is different than the one we're listening on. + + For example, Apache may be listening on https://www.example.com, and then + forwarding requests to http://localhost:8080, but we don't want HTML produced + to say 'http://localhost:8080', they should say 'https://www.example.com', + so we do:: + + request.setHost('www.example.com', 443, ssl=1) + + """ + self.forceSSL = ssl + self.received_headers["host"] = host + self.host = (host, port) + + + diff --git a/tools/python/xen/web/httpserver.py b/tools/python/xen/web/httpserver.py new file mode 100644 index 0000000..d8a1f78 --- /dev/null +++ b/tools/python/xen/web/httpserver.py @@ -0,0 +1,367 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ + +import threading + +import string +import socket +import types +from urllib import quote, unquote +import os +import os.path +import fcntl + +from xen.xend import sxp +from xen.xend.Args import ArgError +from xen.xend.XendError import XendError + +import http +import unix +from resource import Resource, ErrorPage +from SrvDir import SrvDir + +class ThreadRequest: + """A request to complete processing using a thread. + """ + + def __init__(self, processor, req, fn, args, kwds): + self.processor = processor + self.req = req + self.fn = fn + self.args = args + self.kwds = kwds + + def run(self): + self.processor.setInThread() + thread = threading.Thread(target=self.main) + thread.setDaemon(True) + thread.start() + + def call(self): + try: + self.fn(*self.args, **self.kwds) + except SystemExit: + raise + except Exception, ex: + self.req.resultErr(ex) + self.req.finish() + + def main(self): + self.call() + self.processor.process() + + +class RequestProcessor: + """Processor for requests on a connection to an http server. + Requests are executed synchonously unless they ask for a thread by returning + a ThreadRequest. + """ + + done = False + + inThread = False + + def __init__(self, server, sock, addr): + self.server = server + self.sock = sock + self.srd = sock.makefile('rb') + self.srw = sock.makefile('wb') + self.srvaddr = server.getServerAddr() + + def isInThread(self): + return self.inThread + + def setInThread(self): + self.inThread = True + + def getServer(self): + return self.server + + def getRequest(self): + return HttpServerRequest(self, self.srvaddr, self.srd, self.srw) + + def close(self): + try: + self.sock.close() + except: + pass + + def finish(self): + self.done = True + self.close() + + def process(self): + while not self.done: + req = self.getRequest() + res = req.process() + if isinstance(res, ThreadRequest): + if self.isInThread(): + res.call() + else: + res.run() + break + else: + req.finish() + +class HttpServerRequest(http.HttpRequest): + """A single request to an http server. + """ + + def __init__(self, processor, addr, srd, srw): + self.processor = processor + self.prepath = '' + http.HttpRequest.__init__(self, addr, srd, srw) + + def getServer(self): + return self.processor.getServer() + + def process(self): + """Process the request. If the return value is a ThreadRequest + it is evaluated in a thread. + """ + try: + self.prepath = [] + self.postpath = map(unquote, string.split(self.request_path[1:], '/')) + resource = self.getResource() + return self.render(resource) + except SystemExit: + raise + except Exception, ex: + self.processError(ex) + + def processError(self, ex): + import traceback; traceback.print_exc() + self.sendError(http.INTERNAL_SERVER_ERROR, msg=str(ex)) + self.setCloseConnection('close') + + def finish(self): + self.sendResponse() + if self.close_connection: + self.processor.finish() + + def prePathURL(self): + url_host = self.getRequestHostname() + port = self.getPort() + if self.isSecure(): + url_proto = "https" + default_port = 443 + else: + url_proto = "http" + default_port = 80 + if port != default_port: + url_host += (':%d' % port) + url_path = quote(string.join(self.prepath, '/')) + return ('%s://%s/%s' % (url_proto, url_host, url_path)) + + def getResource(self): + return self.getServer().getResource(self) + + def render(self, resource): + val = None + if resource is None: + self.sendError(http.NOT_FOUND) + else: + try: + while True: + val = resource.render(self) + if not isinstance(val, Resource): + break + val = self.result(val) + except SystemExit: + raise + except Exception, ex: + self.resultErr(ex) + return val + + def threadRequest(self, _fn, *_args, **_kwds): + """Create a request to finish request processing in a thread. + Use this to create a ThreadRequest to return from rendering a + resource if you need a thread to complete processing. + """ + return ThreadRequest(self.processor, self, _fn, _args, _kwds) + + def result(self, val): + if isinstance(val, Exception): + return self.resultErr(val) + else: + return self.resultVal(val) + + def resultVal(self, val): + """Callback to complete the request. + + @param val: the value + """ + if val is None: + return val + elif isinstance(val, ThreadRequest): + return val + elif self.useSxp(): + self.setHeader("Content-Type", sxp.mime_type) + sxp.show(val, out=self) + else: + self.write('') + self.printPath() + if isinstance(val, types.ListType): + self.write('
')
+                PrettyPrint.prettyprint(val, out=self)
+                self.write('
') + else: + self.write(str(val)) + self.write('') + return None + + def resultErr(self, err): + """Error callback to complete a request. + + @param err: the error + """ + if not isinstance(err, (ArgError, sxp.ParseError, XendError)): + raise + #log.exception("op=%s: %s", op, str(err)) + if self.useSxp(): + self.setHeader("Content-Type", sxp.mime_type) + sxp.show(['xend.err', str(err)], out=self) + else: + self.setHeader("Content-Type", "text/plain") + self.write('Error ') + self.write(': ') + self.write(str(err)) + return None + + def useSxp(self): + """Determine whether to send an SXP response to a request. + Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept. + + returns 1 for SXP, 0 otherwise + """ + ok = 0 + user_agent = self.getHeader('User-Agent') + accept = self.getHeader('Accept') + if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0): + ok = 1 + return ok + + def printPath(self): + pathlist = [x for x in self.prepath if x != '' ] + s = "/" + self.write('

/') + for x in pathlist: + s += x + "/" + self.write(' %s/' % (s, x)) + self.write("

") + +class HttpServerClient: + + def __init__(self, server, sock, addr): + self.server = server + self.sock = sock + self.addr = addr + + def process(self): + thread = threading.Thread(target=self.doProcess) + thread.setDaemon(True) + thread.start() + + def doProcess(self): + try: + rp = RequestProcessor(self.server, self.sock, self.addr) + rp.process() + except SystemExit: + raise + except Exception, ex: + print 'HttpServer>processRequest> exception: ', ex + try: + self.sock.close() + except: + pass + +class HttpServer: + + backlog = 5 + + def __init__(self, root, interface, port=8080): + self.root = root + self.interface = interface + self.port = port + # ready indicates when we are ready to begin accept connections + # it should be set after a successful bind + self.ready = False + self.closed = False + + def run(self): + self.bind() + self.listen() + self.ready = True + + while not self.closed: + (sock, addr) = self.accept() + cl = HttpServerClient(self, sock, addr) + cl.process() + + def stop(self): + self.close() + + def bind(self): + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(self.socket.fileno(), fcntl.F_SETFD, flags) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.bind((self.interface, self.port)) + + def listen(self): + self.socket.listen(self.backlog) + + def accept(self): + return self.socket.accept() + + def close(self): + self.closed = True + self.ready = False + # shutdown socket explicitly to allow reuse + try: + self.socket.shutdown(2) + except socket.error: + pass + + try: + self.socket.close() + except socket.error: + pass + + def getServerAddr(self): + return (socket.gethostname(), self.port) + + def getResource(self, req): + return self.root.getRequestResource(req) + + def shutdown(self): + self.close() + + +class UnixHttpServer(HttpServer): + + def __init__(self, root, path): + HttpServer.__init__(self, root, 'localhost') + self.path = path + + def bind(self): + self.socket = unix.bind(self.path) + flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(self.socket.fileno(), fcntl.F_SETFD, flags) diff --git a/tools/python/xen/web/protocol.py b/tools/python/xen/web/protocol.py new file mode 100644 index 0000000..603973a --- /dev/null +++ b/tools/python/xen/web/protocol.py @@ -0,0 +1,40 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd. +#============================================================================ + +class Protocol: + + def __init__(self): + self.transport = None + + def setTransport(self, transport): + self.transport = transport + + def dataReceived(self, data): + raise NotImplementedError() + + def write(self, data): + if self.transport: + return self.transport.write(data) + else: + return 0 + + def read(self): + if self.transport: + return self.transport.read() + else: + return None diff --git a/tools/python/xen/web/resource.py b/tools/python/xen/web/resource.py new file mode 100644 index 0000000..6c226bb --- /dev/null +++ b/tools/python/xen/web/resource.py @@ -0,0 +1,108 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +#============================================================================ + +import http + +def findResource(resource, request): + """Traverse resource tree to find who will handle the request.""" + while request.postpath and not resource.isLeaf: + #print 'findResource:', resource, request.postpath + pathElement = request.postpath.pop(0) + request.prepath.append(pathElement) + next = resource.getPathResource(pathElement, request) + if not next: break + resource = next + return resource + +class Resource: + + isLeaf = False + + def __init__(self): + self.children = {} + + def getRequestResource(self, req): + return findResource(self, req) + + def getChild(self, path, request): + return None + + def getPathResource(self, path, request): + #print 'getPathResource>', self, path + if self.children.has_key(path): + val = self.children[path] + else: + val = self.getChild(path, request) + #print 'getPathResource<', val + return val + + def putChild(self, path, child): + self.children[path] = child + #child.server = self.server + + def render(self, req): + meth = getattr(self, 'render_' + req.getRequestMethod(), self.unsupported) + return meth(req) + + def supportedMethods(self): + l = [] + s = 'render_' + for x in dir(self): + if x.startswith(s): + l.append(x[len(s):]) + return l + + def render_HEAD(self, req): + return self.render_GET(req) + + def render_GET(self, req): + req.setContentType("text/plain") + req.write("GET") + + def render_POST(self, req): + req.setContentType("text/plain") + req.write("POST") + + def unsupported(self, req): + req.setHeader("Accept", ",".join(self.supportedMethods())) + req.setResponseCode(http.NOT_IMPLEMENTED) + req.setContentType("text/plain") + req.write("Request method not supported (%s)" % req.getRequestMethod()) + +class ErrorPage(Resource): + + isLeaf = True + + def __init__(self, code, status=None, msg=None): + Resource.__init__(self) + if status is None: + status = http.getStatus(code) + if msg is None: + msg = status + self.code = code + self.status = status + self.msg = msg + + def render(self, req): + req.setResponseCode(self.code, self.status) + req.setContentType("text/plain") + req.write(self.msg) + + + + + diff --git a/tools/python/xen/web/static.py b/tools/python/xen/web/static.py new file mode 100644 index 0000000..70cb7d6 --- /dev/null +++ b/tools/python/xen/web/static.py @@ -0,0 +1,61 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +#============================================================================ +import os + +from resource import Resource + +class File(Resource): + + isLeaf = True + + def __init__(self, filename, defaultType=None): + if defaultType is None: + defaultType = "text/plain" + self.filename = filename + self.type = defaultType + self.encoding = None + + def getFileSize(self): + try: + info = os.stat(self.filename) + return info.st_size + except: + return 0 + + def render(self, req): + if self.type: + req.setHeader('Content-Type', self.type) + if self.encoding: + req.setHeader('Content-Encoding', self.encoding) + req.setHeader('Content-Length', self.getFileSize()) + try: + io = file(self.filename, "r") + while True: + buf = io.read(1024) + if not buf: + break + req.write(buf) + except IOError: + pass + try: + if io: + io.close() + except: + pass + + + diff --git a/tools/python/xen/web/tcp.py b/tools/python/xen/web/tcp.py new file mode 100644 index 0000000..c4436d5 --- /dev/null +++ b/tools/python/xen/web/tcp.py @@ -0,0 +1,118 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd. +#============================================================================ + + +import errno +import re +import socket +import time + +import connection + +from xen.xend.XendLogging import log + + +class TCPListener(connection.SocketListener): + + def __init__(self, protocol_class, port, interface, hosts_allow): + self.port = port + self.interface = interface + self.hosts_allow = hosts_allow + connection.SocketListener.__init__(self, protocol_class) + + + def createSocket(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + # SO_REUSEADDR does not always ensure that we do not get an address + # in use error when restarted quickly + # we implement a timeout to try and avoid failing unnecessarily + timeout = time.time() + 30 + while True: + try: + sock.bind((self.interface, self.port)) + return sock + except socket.error, (_errno, strerrno): + if _errno == errno.EADDRINUSE and time.time() < timeout: + time.sleep(0.5) + else: + raise + + + def acceptConnection(self, sock, addrport): + addr = addrport[0] + if connection.hostAllowed(addrport, self.hosts_allow): + connection.SocketServerConnection(sock, self.protocol_class) + else: + try: + sock.close() + except: + pass + +class SSLTCPListener(TCPListener): + + def __init__(self, protocol_class, port, interface, hosts_allow, + ssl_key_file = None, ssl_cert_file = None): + if not ssl_key_file or not ssl_cert_file: + raise ValueError("SSLXMLRPCServer requires ssl_key_file " + "and ssl_cert_file to be set.") + + self.ssl_key_file = ssl_key_file + self.ssl_cert_file = ssl_cert_file + + TCPListener.__init__(self, protocol_class, port, interface, hosts_allow) + + + def createSocket(self): + from OpenSSL import SSL + # make a SSL socket + ctx = SSL.Context(SSL.SSLv23_METHOD) + ctx.set_options(SSL.OP_NO_SSLv2) + ctx.use_privatekey_file (self.ssl_key_file) + ctx.use_certificate_file(self.ssl_cert_file) + sock = SSL.Connection(ctx, + socket.socket(socket.AF_INET, socket.SOCK_STREAM)) + sock.set_accept_state() + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + # SO_REUSEADDR does not always ensure that we do not get an address + # in use error when restarted quickly + # we implement a timeout to try and avoid failing unnecessarily + timeout = time.time() + 30 + while True: + try: + sock.bind((self.interface, self.port)) + return sock + except socket.error, (_errno, strerrno): + if _errno == errno.EADDRINUSE and time.time() < timeout: + time.sleep(0.5) + else: + raise + + + def acceptConnection(self, sock, addrport): + addr = addrport[0] + if connection.hostAllowed(addrport, self.hosts_allow): + connection.SSLSocketServerConnection(sock, self.protocol_class) + else: + try: + sock.close() + except: + pass + diff --git a/tools/python/xen/web/unix.py b/tools/python/xen/web/unix.py new file mode 100644 index 0000000..12b6e96 --- /dev/null +++ b/tools/python/xen/web/unix.py @@ -0,0 +1,55 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2005-2006 XenSource Ltd. +#============================================================================ + + +import os +import os.path +import socket +import stat + +from xen.util import mkdir + +import connection + + +def bind(path): + """Create a Unix socket, and bind it to the given path. The socket is +created such that only the current user may access it.""" + + parent = os.path.dirname(path) + mkdir.parents(parent, stat.S_IRWXU, True) + if os.path.exists(path): + os.unlink(path) + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.bind(path) + return sock + + +class UnixListener(connection.SocketListener): + def __init__(self, path, protocol_class): + self.path = path + connection.SocketListener.__init__(self, protocol_class) + + + def createSocket(self): + return bind(self.path) + + + def acceptConnection(self, sock, _): + connection.SocketServerConnection(sock, self.protocol_class) diff --git a/tools/python/xen/xend/Args.py b/tools/python/xen/xend/Args.py new file mode 100644 index 0000000..e2193b6 --- /dev/null +++ b/tools/python/xen/xend/Args.py @@ -0,0 +1,166 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +import types +import StringIO + +from xen.xend import sxp + +class ArgError(StandardError): + pass + +class Args: + """Argument encoding support for HTTP. + """ + + def __init__(self, paramspec, keyspec): + self.arg_ord = [] + self.arg_dict = {} + self.key_ord = [] + self.key_dict = {} + for (name, typ) in paramspec: + self.arg_ord.append(name) + self.arg_dict[name] = typ + for (name, typ) in keyspec: + self.key_ord.append(name) + self.key_dict[name] = typ + + def get_args(self, d, xargs=None): + args = {} + keys = {} + params = [] + if xargs: + self.split_args(xargs, args, keys) + self.split_args(d, args, keys) + for a in self.arg_ord: + if a in args: + params.append(args[a]) + else: + raise ArgError('Missing parameter: %s' % a) + return (params, keys) + + def split_args(self, d, args, keys): + for (k, v) in d.items(): + if k in self.arg_dict: + typ = self.arg_dict[k] + val = self.coerce(typ, v) + args[k] = val + elif k in self.key_dict: + typ = self.key_dict[k] + val = self.coerce(typ, v) + keys[k] = val + else: + raise ArgError('Invalid parameter: %s' % k) + + def get_form_args(self, f, xargs=None): + d = {} + for (k, v) in f.items(): + if ((k not in self.arg_dict) and + (k not in self.key_dict)): + continue + if isinstance(v, types.ListType): + n = len(v) + if n == 0: + continue + elif n == 1: + val = v[0] + else: + raise ArgError('Too many values for %s' % k) + else: + val = v + d[k] = val + return self.get_args(d, xargs=xargs) + + def coerce(self, typ, v): + try: + if typ == 'int': + val = int(v) + elif typ == 'long': + val = long(v) + elif typ == 'str': + val = str(v) + elif typ == 'sxpr': + val = self.sxpr(v) + elif typ == 'bool': + val = self.bool(v) + else: + raise ArgError('invalid type:' + str(typ)) + return val + except ArgError: + raise + except StandardError, ex: + raise ArgError(str(ex)) + + def bool(self, v): + return (v.lower() in ['on', 'yes', '1', 'true']) + + def sxpr(self, v): + if isinstance(v, types.ListType): + val = v + elif isinstance(v, types.FileType) or hasattr(v, 'readline'): + val = self.sxpr_file(v) + elif isinstance(v, types.StringType): + val = self.sxpr_file(StringIO.StringIO(v)) + else: + val = str(v) + return val + + def sxpr_file(self, fin): + try: + vals = sxp.parse(fin) + except: + raise ArgError('Coercion to sxpr failed') + if len(vals) == 1: + return vals[0] + else: + raise ArgError('Too many sxprs') + + def call_with_args(self, fn, args, xargs=None): + (params, keys) = self.get_args(args, xargs=xargs) + return fn(*params, **keys) + + def call_with_form_args(self, fn, fargs, xargs=None): + (params, keys) = self.get_form_args(fargs, xargs=xargs) + return fn(*params, **keys) + +class ArgFn(Args): + """Represent a remote HTTP operation as a function. + Used on the client. + """ + + def __init__(self, fn, paramspec, keyspec = None): + if keyspec == None: + keyspec = {} + Args.__init__(self, paramspec, keyspec) + self.fn = fn + + def __call__(self, fargs, xargs=None): + return self.call_with_args(self.fn, fargs, xargs=xargs) + +class FormFn(Args): + """Represent an operation as a function over a form. + Used in the HTTP server. + """ + + def __init__(self, fn, paramspec, keyspec = None): + if keyspec == None: + keyspec = {} + Args.__init__(self, paramspec, keyspec) + self.fn = fn + + def __call__(self, fargs, xargs=None): + return self.call_with_form_args(self.fn, fargs, xargs=xargs) diff --git a/tools/python/xen/xend/PrettyPrint.py b/tools/python/xen/xend/PrettyPrint.py new file mode 100644 index 0000000..4813577 --- /dev/null +++ b/tools/python/xen/xend/PrettyPrint.py @@ -0,0 +1,323 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +"""General pretty-printer, including support for SXP. + +""" +import sys +import types +import StringIO +from xen.xend import sxp + +class PrettyItem: + + def __init__(self, width): + self.width = width + + def insert(self, block): + block.addtoline(self) + + def get_width(self): + return self.width + + def output(self, _): + print '***PrettyItem>output>', self + pass + + def prettyprint(self, _): + print '***PrettyItem>prettyprint>', self + return self.width + +class PrettyString(PrettyItem): + + def __init__(self, x): + PrettyItem.__init__(self, len(x)) + self.value = x + + def output(self, out): + out.write(self.value) + + def prettyprint(self, line): + line.output(self) + + def show(self, out): + print >> out, ("(string (width %d) '%s')" % (self.width, self.value)) + +class PrettySpace(PrettyItem): + + def output(self, out): + out.write(' ' * self.width) + + def prettyprint(self, line): + line.output(self) + + def show(self, out): + print >> out, ("(space (width %d))" % self.width) + +class PrettyBreak(PrettyItem): + + def __init__(self, width, indent): + PrettyItem.__init__(self, width) + self.indent = indent + self.space = 0 + self.active = 0 + + def output(self, out): + out.write(' ' * self.width) + + def prettyprint(self, line): + if line.breaks(self.space): + self.active = 1 + line.newline(self.indent) + else: + line.output(self) + + def show(self, out): + print >> out, ("(break (width %d) (indent %d) (space %d) (active %d))" + % (self.width, self.indent, self.space, self.active)) + +class PrettyNewline(PrettySpace): + + def insert(self, block): + block.newline() + block.addtoline(self) + + def prettyprint(self, line): + line.newline(0) + line.output(self) + + def show(self, out): + print >> out, ("(nl (width %d))" % self.width) + +class PrettyLine(PrettyItem): + def __init__(self): + PrettyItem.__init__(self, 0) + self.content = [] + + def write(self, x): + self.content.append(x) + + def end(self): + width = 0 + lastwidth = 0 + lastbreak = None + for x in self.content: + if isinstance(x, PrettyBreak): + if lastbreak: + lastbreak.space = (width - lastwidth) + lastbreak = x + lastwidth = width + width += x.get_width() + if lastbreak: + lastbreak.space = (width - lastwidth) + self.width = width + + def prettyprint(self, line): + for x in self.content: + x.prettyprint(line) + + def show(self, out): + print >> out, '(LINE (width %d)' % self.width + for x in self.content: + x.show(out) + print >> out, ')' + +class PrettyBlock(PrettyItem): + + def __init__(self, all=0, parent=None): + PrettyItem.__init__(self, 0) + + self.lines = [] + self.parent = parent + self.indent = 0 + self.all = all + self.broken = 0 + self.newline() + + def add(self, item): + item.insert(self) + + def end(self): + self.width = 0 + for l in self.lines: + l.end() + if self.width < l.width: + self.width = l.width + + def breaks(self, _): + return self.all and self.broken + + def newline(self): + self.lines.append(PrettyLine()) + + def addtoline(self, x): + self.lines[-1].write(x) + + def prettyprint(self, line): + self.indent = line.used + line.block = self + if not line.fits(self.width): + self.broken = 1 + for l in self.lines: + l.prettyprint(line) + line.block = self.parent + + def show(self, out): + print >> out, ('(BLOCK (width %d) (indent %d) (all %d) (broken %d)' % + (self.width, self.indent, self.all, self.broken)) + for l in self.lines: + l.show(out) + print >> out, ')' + +class Line: + + def __init__(self, out, width): + self.block = None + self.out = out + self.width = width + self.used = 0 + self.space = self.width + + def newline(self, indent): + indent += self.block.indent + self.out.write('\n') + self.out.write(' ' * indent) + self.used = indent + self.space = self.width - self.used + + def fits(self, n): + return self.space - n >= 0 + + def breaks(self, n): + return self.block.breaks(n) or not self.fits(n) + + def output(self, x): + n = x.get_width() + self.space -= n + self.used += n + if self.space < 0: + self.space = 0 + x.output(self.out) + +class PrettyPrinter: + """A prettyprinter based on what I remember of Derek Oppen's + prettyprint algorithm from TOPLAS way back. + """ + + def __init__(self, width=40): + self.width = width + self.block = None + self.top = None + + def write(self, x): + self.block.add(PrettyString(x)) + + def add(self, item): + self.block.add(item) + + def addbreak(self, width=1, indent=4): + self.add(PrettyBreak(width, indent)) + + def addspace(self, width=1): + self.add(PrettySpace(width)) + + def addnl(self, indent=0): + self.add(PrettyNewline(indent)) + + def begin(self, all=0): + block = PrettyBlock(all=all, parent=self.block) + self.block = block + + def end(self): + self.block.end() + if self.block.parent: + self.block.parent.add(self.block) + else: + self.top = self.block + self.block = self.block.parent + + def prettyprint(self, out=sys.stdout): + self.top.prettyprint(Line(out, self.width)) + +class SXPPrettyPrinter(PrettyPrinter): + """An SXP prettyprinter. + """ + + def pstring(self, x): + io = StringIO.StringIO() + sxp.show(x, out=io) + io.seek(0) + val = io.getvalue() + io.close() + return val + + def pprint(self, l): + if isinstance(l, types.ListType): + self.begin(all=1) + self.write('(') + i = 0 + for x in l: + if(i): self.addbreak() + self.pprint(x) + i += 1 + self.addbreak(width=0, indent=0) + self.write(')') + self.end() + else: + self.write(self.pstring(l)) + +def prettyprint(sxpr, out=sys.stdout, width=80): + """Prettyprint an SXP form. + + sxpr s-expression + out destination + width maximum output width + """ + if isinstance(sxpr, types.ListType): + pp = SXPPrettyPrinter(width=width) + pp.pprint(sxpr) + pp.prettyprint(out=out) + else: + sxp.show(sxpr, out=out) + print >> out + +def prettyprintstring(sxpr, width=80): + """Prettyprint an SXP form to a string. + + sxpr s-expression + width maximum output width + """ + io = StringIO.StringIO() + prettyprint(sxpr, out=io, width=width) + io.seek(0) + val = io.getvalue() + io.close() + return val + +def main(): + pin = sxp.Parser() + while 1: + buf = sys.stdin.read(100) + pin.input(buf) + if buf == '': break + l = pin.get_val() + prettyprint(l, width=80) + +if __name__ == "__main__": + main() + diff --git a/tools/python/xen/xend/Vifctl.py b/tools/python/xen/xend/Vifctl.py new file mode 100644 index 0000000..ba21050 --- /dev/null +++ b/tools/python/xen/xend/Vifctl.py @@ -0,0 +1,36 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +"""Xend interface to networking control scripts. +""" +import os + +import XendOptions + + +def network(op): + """Call a network control script. + + @param op: operation (start, stop) + """ + if op not in ['start', 'stop']: + raise ValueError('Invalid operation: ' + op) + script = XendOptions.instance().get_network_script() + if script: + script.insert(1, op) + os.spawnv(os.P_WAIT, script[0], script) diff --git a/tools/python/xen/xend/XendAPI.py b/tools/python/xen/xend/XendAPI.py new file mode 100644 index 0000000..42e131b --- /dev/null +++ b/tools/python/xen/xend/XendAPI.py @@ -0,0 +1,2719 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006-2007 XenSource Ltd. +#============================================================================ + +import inspect +import os +import Queue +import sets +import string +import sys +import traceback +import threading +import time +import xmlrpclib + +import XendDomain, XendDomainInfo, XendNode, XendDmesg +import XendLogging, XendTaskManager, XendAPIStore + +from XendAPIVersion import * +from XendAuthSessions import instance as auth_manager +from XendError import * +from XendClient import ERROR_INVALID_DOMAIN +from XendLogging import log +from XendNetwork import XendNetwork +from XendTask import XendTask +from XendPIFMetrics import XendPIFMetrics +from XendVMMetrics import XendVMMetrics +from XendPIF import XendPIF +from XendPBD import XendPBD +from XendPPCI import XendPPCI +from XendDPCI import XendDPCI +from XendPSCSI import XendPSCSI +from XendDSCSI import XendDSCSI +from XendXSPolicy import XendXSPolicy, XendACMPolicy + +from XendAPIConstants import * +from xen.util.xmlrpclib2 import stringify + +from xen.util.blkif import blkdev_name_to_number +from xen.util import xsconstants + + +AUTH_NONE = 'none' +AUTH_PAM = 'pam' + +argcounts = {} + +# ------------------------------------------ +# Utility Methods for Xen API Implementation +# ------------------------------------------ + +def xen_api_success(value): + """Wraps a return value in XenAPI format.""" + if value is None: + s = '' + else: + s = stringify(value) + return {"Status": "Success", "Value": s} + +def xen_api_success_void(): + """Return success, but caller expects no return value.""" + return xen_api_success("") + +def xen_api_error(error): + """Wraps an error value in XenAPI format.""" + if type(error) == tuple: + error = list(error) + if type(error) != list: + error = [error] + if len(error) == 0: + error = ['INTERNAL_ERROR', 'Empty list given to xen_api_error'] + + return { "Status": "Failure", + "ErrorDescription": [str(x) for x in error] } + + +def xen_api_todo(): + """Temporary method to make sure we track down all the TODOs""" + return {"Status": "Error", "ErrorDescription": XEND_ERROR_TODO} + + +def now(): + return datetime() + + +def datetime(when = None): + """Marshall the given time as a Xen-API DateTime. + + @param when The time in question, given as seconds since the epoch, UTC. + May be None, in which case the current time is used. + """ + if when is None: + return xmlrpclib.DateTime(time.gmtime()) + else: + return xmlrpclib.DateTime(time.gmtime(when)) + + +# --------------------------------------------------- +# Event dispatch +# --------------------------------------------------- + +EVENT_QUEUE_LENGTH = 50 +event_registrations = {} + +def event_register(session, reg_classes): + if session not in event_registrations: + event_registrations[session] = { + 'classes' : sets.Set(), + 'queue' : Queue.Queue(EVENT_QUEUE_LENGTH), + 'next-id' : 1 + } + if not reg_classes: + reg_classes = classes + event_registrations[session]['classes'].union_update(reg_classes) + + +def event_unregister(session, unreg_classes): + if session not in event_registrations: + return + + if unreg_classes: + event_registrations[session]['classes'].intersection_update( + unreg_classes) + if len(event_registrations[session]['classes']) == 0: + del event_registrations[session] + else: + del event_registrations[session] + + +def event_next(session): + if session not in event_registrations: + return xen_api_error(['SESSION_NOT_REGISTERED', session]) + queue = event_registrations[session]['queue'] + events = [queue.get()] + try: + while True: + events.append(queue.get(False)) + except Queue.Empty: + pass + + return xen_api_success(events) + + +def _ctor_event_dispatch(xenapi, ctor, api_cls, session, args): + result = ctor(xenapi, session, *args) + if result['Status'] == 'Success': + ref = result['Value'] + event_dispatch('add', api_cls, ref, '') + return result + + +def _dtor_event_dispatch(xenapi, dtor, api_cls, session, ref, args): + result = dtor(xenapi, session, ref, *args) + if result['Status'] == 'Success': + event_dispatch('del', api_cls, ref, '') + return result + + +def _setter_event_dispatch(xenapi, setter, api_cls, attr_name, session, ref, + args): + result = setter(xenapi, session, ref, *args) + if result['Status'] == 'Success': + event_dispatch('mod', api_cls, ref, attr_name) + return result + + +def event_dispatch(operation, api_cls, ref, attr_name): + assert operation in ['add', 'del', 'mod'] + event = { + 'timestamp' : now(), + 'class' : api_cls, + 'operation' : operation, + 'ref' : ref, + 'obj_uuid' : ref, + 'field' : attr_name, + } + for reg in event_registrations.values(): + if api_cls in reg['classes']: + event['id'] = reg['next-id'] + reg['next-id'] += 1 + reg['queue'].put(event) + + +# --------------------------------------------------- +# Python Method Decorators for input value validation +# --------------------------------------------------- + +def trace(func, api_name = ''): + """Decorator to trace XMLRPC Xen API methods. + + @param func: function with any parameters + @param api_name: name of the api call for debugging. + """ + if hasattr(func, 'api'): + api_name = func.api + def trace_func(self, *args, **kwargs): + log.debug('%s: %s' % (api_name, args)) + return func(self, *args, **kwargs) + trace_func.api = api_name + return trace_func + + +def catch_typeerror(func): + """Decorator to catch any TypeErrors and translate them into Xen-API + errors. + + @param func: function with params: (self, ...) + @rtype: callable object + """ + def f(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except TypeError, exn: + #log.exception('catch_typeerror') + if hasattr(func, 'api') and func.api in argcounts: + # Assume that if the argument count was wrong and if the + # exception was thrown inside this file, then it is due to an + # invalid call from the client, otherwise it's an internal + # error (which will be handled further up). + expected = argcounts[func.api] + actual = len(args) + len(kwargs) + if expected != actual: + tb = sys.exc_info()[2] + try: + sourcefile = traceback.extract_tb(tb)[-1][0] + if sourcefile == inspect.getsourcefile(XendAPI): + return xen_api_error( + ['MESSAGE_PARAMETER_COUNT_MISMATCH', + func.api, expected, actual]) + finally: + del tb + raise + except XendAPIError, exn: + return xen_api_error(exn.get_api_error()) + + return f + + +def session_required(func): + """Decorator to verify if session is valid before calling method. + + @param func: function with params: (self, session, ...) + @rtype: callable object + """ + def check_session(self, session, *args, **kwargs): + if auth_manager().is_session_valid(session): + return func(self, session, *args, **kwargs) + else: + return xen_api_error(['SESSION_INVALID', session]) + + return check_session + + +def _is_valid_ref(ref, validator): + return type(ref) == str and validator(ref) + +def _check_ref(validator, clas, func, api, session, ref, *args, **kwargs): + if _is_valid_ref(ref, validator): + return func(api, session, ref, *args, **kwargs) + else: + return xen_api_error(['HANDLE_INVALID', clas, ref]) + + +def valid_host(func): + """Decorator to verify if host_ref is valid before calling method. + + @param func: function with params: (self, session, host_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(XendNode.instance().is_valid_host, + 'host', func, *args, **kwargs) + +def valid_host_metrics(func): + """Decorator to verify if host_metrics_ref is valid before calling + method. + + @param func: function with params: (self, session, host_metrics_ref) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: r == XendNode.instance().host_metrics_uuid, + 'host_metrics', func, *args, **kwargs) + +def valid_host_cpu(func): + """Decorator to verify if host_cpu_ref is valid before calling method. + + @param func: function with params: (self, session, host_cpu_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(XendNode.instance().is_valid_cpu, + 'host_cpu', func, *args, **kwargs) + +def valid_vm(func): + """Decorator to verify if vm_ref is valid before calling method. + + @param func: function with params: (self, session, vm_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(XendDomain.instance().is_valid_vm, + 'VM', func, *args, **kwargs) + +def valid_vbd(func): + """Decorator to verify if vbd_ref is valid before calling method. + + @param func: function with params: (self, session, vbd_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendDomain.instance().is_valid_dev('vbd', r), + 'VBD', func, *args, **kwargs) + +def valid_vbd_metrics(func): + """Decorator to verify if ref is valid before calling method. + + @param func: function with params: (self, session, ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendDomain.instance().is_valid_dev('vbd', r), + 'VBD_metrics', func, *args, **kwargs) + +def valid_vif(func): + """Decorator to verify if vif_ref is valid before calling method. + + @param func: function with params: (self, session, vif_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendDomain.instance().is_valid_dev('vif', r), + 'VIF', func, *args, **kwargs) + +def valid_vif_metrics(func): + """Decorator to verify if ref is valid before calling method. + + @param func: function with params: (self, session, ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendDomain.instance().is_valid_dev('vif', r), + 'VIF_metrics', func, *args, **kwargs) + +def valid_vdi(func): + """Decorator to verify if vdi_ref is valid before calling method. + + @param func: function with params: (self, session, vdi_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(XendNode.instance().is_valid_vdi, + 'VDI', func, *args, **kwargs) + +def valid_vtpm(func): + """Decorator to verify if vtpm_ref is valid before calling method. + + @param func: function with params: (self, session, vtpm_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendDomain.instance().is_valid_dev('vtpm', r), + 'VTPM', func, *args, **kwargs) + + +def valid_console(func): + """Decorator to verify if console_ref is valid before calling method. + + @param func: function with params: (self, session, console_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendDomain.instance().is_valid_dev('console', + r), + 'console', func, *args, **kwargs) + +def valid_sr(func): + """Decorator to verify if sr_ref is valid before calling method. + + @param func: function with params: (self, session, sr_ref, ...) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: XendNode.instance().is_valid_sr, + 'SR', func, *args, **kwargs) + +def valid_task(func): + """Decorator to verify if task_ref is valid before calling + method. + + @param func: function with params: (self, session, task_ref) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(XendTaskManager.get_task, + 'task', func, *args, **kwargs) + +def valid_debug(func): + """Decorator to verify if task_ref is valid before calling + method. + + @param func: function with params: (self, session, task_ref) + @rtype: callable object + """ + return lambda *args, **kwargs: \ + _check_ref(lambda r: r in XendAPI._debug, + 'debug', func, *args, **kwargs) + + +def valid_object(class_name): + """Decorator to verify if object is valid before calling + method. + + @param func: function with params: (self, session, pif_ref) + @rtype: callable object + """ + return lambda func: \ + lambda *args, **kwargs: \ + _check_ref(lambda r: \ + XendAPIStore.get(r, class_name) is not None, + 'PIF', func, *args, **kwargs) + +# ----------------------------- +# Bridge to Legacy XM API calls +# ----------------------------- + +def do_vm_func(fn_name, vm_ref, *args, **kwargs): + """Helper wrapper func to abstract away from repetitive code. + + @param fn_name: function name for XendDomain instance + @type fn_name: string + @param vm_ref: vm_ref + @type vm_ref: string + @param *args: more arguments + @type *args: tuple + """ + try: + xendom = XendDomain.instance() + fn = getattr(xendom, fn_name) + xendom.do_legacy_api_with_uuid(fn, vm_ref, *args, **kwargs) + return xen_api_success_void() + except VMBadState, exn: + return xen_api_error(['VM_BAD_POWER_STATE', vm_ref, exn.expected, + exn.actual]) + + +classes = { + 'session' : None, + 'event' : None, + 'host' : valid_host, + 'host_cpu' : valid_host_cpu, + 'host_metrics' : valid_host_metrics, + 'VM' : valid_vm, + 'VBD' : valid_vbd, + 'VBD_metrics' : valid_vbd_metrics, + 'VIF' : valid_vif, + 'VIF_metrics' : valid_vif_metrics, + 'VDI' : valid_vdi, + 'VTPM' : valid_vtpm, + 'console' : valid_console, + 'SR' : valid_sr, + 'task' : valid_task, + 'XSPolicy' : valid_object("XSPolicy"), + 'ACMPolicy' : valid_object("ACMPolicy"), + 'debug' : valid_debug, + 'network' : valid_object("network"), + 'PIF' : valid_object("PIF"), + 'VM_metrics' : valid_object("VM_metrics"), + 'PBD' : valid_object("PBD"), + 'PIF_metrics' : valid_object("PIF_metrics"), + 'PPCI' : valid_object("PPCI"), + 'DPCI' : valid_object("DPCI"), + 'PSCSI' : valid_object("PSCSI"), + 'DSCSI' : valid_object("DSCSI") +} + +autoplug_classes = { + 'network' : XendNetwork, + 'PIF' : XendPIF, + 'VM_metrics' : XendVMMetrics, + 'PBD' : XendPBD, + 'PIF_metrics' : XendPIFMetrics, + 'PPCI' : XendPPCI, + 'DPCI' : XendDPCI, + 'PSCSI' : XendPSCSI, + 'DSCSI' : XendDSCSI, + 'XSPolicy' : XendXSPolicy, + 'ACMPolicy' : XendACMPolicy, +} + +class XendAPI(object): + """Implementation of the Xen-API in Xend. Expects to be + used via XMLRPCServer. + + All methods that need a valid session are marked with + a L{session_required} decorator that will + transparently perform the required session authentication. + + We need to support Python <2.4, so we use the old decorator syntax. + + All XMLRPC accessible methods require an 'api' attribute and + is set to the XMLRPC function name which the method implements. + """ + + __decorated__ = False + __init_lock__ = threading.Lock() + _debug = {} + + def __new__(cls, *args, **kwds): + """ Override __new__ to decorate the class only once. + + Lock to make sure the classes are not decorated twice. + """ + cls.__init_lock__.acquire() + try: + if not cls.__decorated__: + cls._decorate() + cls.__decorated__ = True + + return object.__new__(cls, *args, **kwds) + finally: + cls.__init_lock__.release() + + def _decorate(cls): + """ Decorate all the object methods to have validators + and appropriate function attributes. + + This should only be executed once for the duration of the + server. + """ + global_validators = [session_required, catch_typeerror] + + + # Cheat methods + # ------------- + # Methods that have a trivial implementation for all classes. + # 1. get_by_uuid == getting by ref, so just return uuid for + # all get_by_uuid() methods. + + for api_cls in classes.keys(): + # We'll let the autoplug classes implement these functions + # themselves - its much cleaner to do it in the base class + if api_cls == 'session' or api_cls in autoplug_classes.keys(): + continue + + get_by_uuid = '%s_get_by_uuid' % api_cls + get_uuid = '%s_get_uuid' % api_cls + get_all_records = '%s_get_all_records' % api_cls + + def _get_by_uuid(_1, _2, ref): + return xen_api_success(ref) + + def _get_uuid(_1, _2, ref): + return xen_api_success(ref) + + def unpack(v): + return v.get('Value') + + def _get_all_records(_api_cls): + return lambda s, session: \ + xen_api_success(dict([(ref, unpack(getattr(cls, '%s_get_record' % _api_cls)(s, session, ref)))\ + for ref in unpack(getattr(cls, '%s_get_all' % _api_cls)(s, session))])) + + setattr(cls, get_by_uuid, _get_by_uuid) + setattr(cls, get_uuid, _get_uuid) + setattr(cls, get_all_records, _get_all_records(api_cls)) + + # Autoplugging classes + # -------------------- + # These have all of their methods grabbed out from the implementation + # class, and wrapped up to be compatible with the Xen-API. + + def getter(ref, type): + return XendAPIStore.get(ref, type) + + for api_cls, impl_cls in autoplug_classes.items(): + def doit(n): + dot_n = '%s.%s' % (api_cls, n) + full_n = '%s_%s' % (api_cls, n) + if not hasattr(cls, full_n): + f = getattr(impl_cls, n) + argcounts[dot_n] = f.func_code.co_argcount + 1 + g = lambda api_cls: \ + setattr(cls, full_n, \ + lambda s, session, ref, *args: \ + xen_api_success( \ + f(getter(ref, api_cls), *args))) + g(api_cls) # Force api_cls to be captured + + def doit_func(n): + dot_n = '%s.%s' % (api_cls, n) + full_n = '%s_%s' % (api_cls, n) + if not hasattr(cls, full_n): + f = getattr(impl_cls, n) + argcounts[dot_n] = f.func_code.co_argcount + setattr(cls, full_n, \ + lambda s, session, *args: \ + xen_api_success( \ + f(*args))) + + ro_attrs = impl_cls.getAttrRO() + rw_attrs = impl_cls.getAttrRW() + methods = impl_cls.getMethods() + funcs = impl_cls.getFuncs() + + for attr_name in ro_attrs + rw_attrs: + doit('get_%s' % attr_name) + for attr_name in rw_attrs: + doit('set_%s' % attr_name) + for method in methods: + doit('%s' % method) + for func in funcs: + doit_func('%s' % func) + + def wrap_method(name, new_f): + try: + f = getattr(cls, name) + wrapped_f = (lambda *args: new_f(f, *args)) + wrapped_f.api = f.api + wrapped_f.async = f.async + setattr(cls, name, wrapped_f) + except AttributeError: + # Logged below (API call: %s not found) + pass + + + def setter_event_wrapper(api_cls, attr_name): + setter_name = '%s_set_%s' % (api_cls, attr_name) + wrap_method( + setter_name, + lambda setter, s, session, ref, *args: + _setter_event_dispatch(s, setter, api_cls, attr_name, + session, ref, args)) + + + def ctor_event_wrapper(api_cls): + ctor_name = '%s_create' % api_cls + wrap_method( + ctor_name, + lambda ctor, s, session, *args: + _ctor_event_dispatch(s, ctor, api_cls, session, args)) + + + def dtor_event_wrapper(api_cls): + dtor_name = '%s_destroy' % api_cls + wrap_method( + dtor_name, + lambda dtor, s, session, ref, *args: + _dtor_event_dispatch(s, dtor, api_cls, session, ref, args)) + + + # Wrapping validators around XMLRPC calls + # --------------------------------------- + + for api_cls, validator in classes.items(): + def doit(n, takes_instance, async_support = False, + return_type = None): + n_ = n.replace('.', '_') + try: + f = getattr(cls, n_) + if n not in argcounts: + argcounts[n] = f.func_code.co_argcount - 1 + + validators = takes_instance and validator and \ + [validator] or [] + + validators += global_validators + for v in validators: + f = v(f) + f.api = n + f.async = async_support + if return_type: + f.return_type = return_type + + setattr(cls, n_, f) + except AttributeError: + log.warn("API call: %s not found" % n) + + if api_cls in autoplug_classes.keys(): + impl_cls = autoplug_classes[api_cls] + ro_attrs = impl_cls.getAttrRO() + rw_attrs = impl_cls.getAttrRW() + methods = map(lambda x: (x, ""), impl_cls.getMethods()) + funcs = map(lambda x: (x, ""), impl_cls.getFuncs()) + else: + ro_attrs = getattr(cls, '%s_attr_ro' % api_cls, []) \ + + cls.Base_attr_ro + rw_attrs = getattr(cls, '%s_attr_rw' % api_cls, []) \ + + cls.Base_attr_rw + methods = getattr(cls, '%s_methods' % api_cls, []) \ + + cls.Base_methods + funcs = getattr(cls, '%s_funcs' % api_cls, []) \ + + cls.Base_funcs + + # wrap validators around readable class attributes + for attr_name in ro_attrs + rw_attrs: + doit('%s.get_%s' % (api_cls, attr_name), True, + async_support = False) + + # wrap validators around writable class attrributes + for attr_name in rw_attrs: + doit('%s.set_%s' % (api_cls, attr_name), True, + async_support = False) + setter_event_wrapper(api_cls, attr_name) + + # wrap validators around methods + for method_name, return_type in methods: + doit('%s.%s' % (api_cls, method_name), True, + async_support = True) + + # wrap validators around class functions + for func_name, return_type in funcs: + doit('%s.%s' % (api_cls, func_name), False, + async_support = True, + return_type = return_type) + + ctor_event_wrapper(api_cls) + dtor_event_wrapper(api_cls) + + + _decorate = classmethod(_decorate) + + def __init__(self, auth): + self.auth = auth + + Base_attr_ro = ['uuid'] + Base_attr_rw = [] + Base_methods = [('get_record', 'Struct')] + Base_funcs = [('get_all', 'Set'), ('get_by_uuid', None), ('get_all_records', 'Set')] + + # Xen API: Class Session + # ---------------------------------------------------------------- + # NOTE: Left unwrapped by __init__ + + session_attr_ro = ['this_host', 'this_user', 'last_active'] + session_methods = [('logout', None)] + + def session_get_all(self, session): + return xen_api_success([session]) + + def session_login_with_password(self, *args): + if len(args) != 2: + return xen_api_error( + ['MESSAGE_PARAMETER_COUNT_MISMATCH', + 'session.login_with_password', 2, len(args)]) + username = args[0] + password = args[1] + try: + session = ((self.auth == AUTH_NONE and + auth_manager().login_unconditionally(username)) or + auth_manager().login_with_password(username, password)) + return xen_api_success(session) + except XendError, e: + return xen_api_error(['SESSION_AUTHENTICATION_FAILED']) + session_login_with_password.api = 'session.login_with_password' + + # object methods + def session_logout(self, session): + auth_manager().logout(session) + return xen_api_success_void() + + def session_get_record(self, session, self_session): + if self_session != session: + return xen_api_error(['PERMISSION_DENIED']) + record = {'uuid' : session, + 'this_host' : XendNode.instance().uuid, + 'this_user' : auth_manager().get_user(session), + 'last_active': now()} + return xen_api_success(record) + + def session_get_uuid(self, session, self_session): + return xen_api_success(self_session) + + def session_get_by_uuid(self, session, self_session): + return xen_api_success(self_session) + + # attributes (ro) + def session_get_this_host(self, session, self_session): + if self_session != session: + return xen_api_error(['PERMISSION_DENIED']) + return xen_api_success(XendNode.instance().uuid) + + def session_get_this_user(self, session, self_session): + if self_session != session: + return xen_api_error(['PERMISSION_DENIED']) + user = auth_manager().get_user(session) + if user is not None: + return xen_api_success(user) + return xen_api_error(['SESSION_INVALID', session]) + + def session_get_last_active(self, session, self_session): + if self_session != session: + return xen_api_error(['PERMISSION_DENIED']) + return xen_api_success(now()) + + + # Xen API: Class User + # ---------------------------------------------------------------- + # TODO: NOT IMPLEMENTED YET + + # Xen API: Class Tasks + # ---------------------------------------------------------------- + + task_attr_ro = ['name_label', + 'name_description', + 'status', + 'progress', + 'type', + 'result', + 'error_info', + 'allowed_operations', + 'session' + ] + + task_attr_rw = [] + + task_funcs = [('get_by_name_label', 'Set(task)'), + ('cancel', None)] + + def task_get_name_label(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.name_label) + + def task_get_name_description(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.name_description) + + def task_get_status(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.get_status()) + + def task_get_progress(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.progress) + + def task_get_type(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.type) + + def task_get_result(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.result) + + def task_get_error_info(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.error_info) + + def task_get_allowed_operations(self, session, task_ref): + return xen_api_success({}) + + def task_get_session(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.session) + + def task_get_all(self, session): + tasks = XendTaskManager.get_all_tasks() + return xen_api_success(tasks) + + def task_get_record(self, session, task_ref): + task = XendTaskManager.get_task(task_ref) + return xen_api_success(task.get_record()) + + def task_cancel(self, session, task_ref): + return xen_api_error('OPERATION_NOT_ALLOWED') + + def task_get_by_name_label(self, session, name): + return xen_api_success(XendTaskManager.get_task_by_name(name)) + + # Xen API: Class host + # ---------------------------------------------------------------- + + host_attr_ro = ['software_version', + 'resident_VMs', + 'PBDs', + 'PIFs', + 'PPCIs', + 'PSCSIs', + 'host_CPUs', + 'cpu_configuration', + 'metrics', + 'capabilities', + 'supported_bootloaders', + 'sched_policy', + 'API_version_major', + 'API_version_minor', + 'API_version_vendor', + 'API_version_vendor_implementation', + 'enabled'] + + host_attr_rw = ['name_label', + 'name_description', + 'other_config', + 'logging'] + + host_methods = [('disable', None), + ('enable', None), + ('reboot', None), + ('shutdown', None), + ('add_to_other_config', None), + ('remove_from_other_config', None), + ('dmesg', 'String'), + ('dmesg_clear', 'String'), + ('get_log', 'String'), + ('send_debug_keys', None)] + + host_funcs = [('get_by_name_label', None), + ('list_methods', None)] + + # attributes + def host_get_name_label(self, session, host_ref): + return xen_api_success(XendNode.instance().name) + def host_set_name_label(self, session, host_ref, new_name): + XendNode.instance().set_name(new_name) + return xen_api_success_void() + def host_get_name_description(self, session, host_ref): + return xen_api_success(XendNode.instance().get_description()) + def host_set_name_description(self, session, host_ref, new_desc): + XendNode.instance().set_description(new_desc) + return xen_api_success_void() + def host_get_other_config(self, session, host_ref): + return xen_api_success(XendNode.instance().other_config) + def host_set_other_config(self, session, host_ref, other_config): + node = XendNode.instance() + node.other_config = dict(other_config) + node.save() + return xen_api_success_void() + def host_add_to_other_config(self, session, host_ref, key, value): + node = XendNode.instance() + node.other_config[key] = value + node.save() + return xen_api_success_void() + def host_remove_from_other_config(self, session, host_ref, key): + node = XendNode.instance() + if key in node.other_config: + del node.other_config[key] + node.save() + return xen_api_success_void() + def host_get_API_version_major(self, _, ref): + return xen_api_success(XEN_API_VERSION_MAJOR) + def host_get_API_version_minor(self, _, ref): + return xen_api_success(XEN_API_VERSION_MINOR) + def host_get_API_version_vendor(self, _, ref): + return xen_api_success(XEN_API_VERSION_VENDOR) + def host_get_API_version_vendor_implementation(self, _, ref): + return xen_api_success(XEN_API_VERSION_VENDOR_IMPLEMENTATION) + def host_get_software_version(self, session, host_ref): + return xen_api_success(XendNode.instance().xen_version()) + def host_get_enabled(self, _1, _2): + return xen_api_success(XendDomain.instance().allow_new_domains()) + def host_get_resident_VMs(self, session, host_ref): + return xen_api_success(XendDomain.instance().get_domain_refs()) + def host_get_PBDs(self, _, ref): + return xen_api_success(XendPBD.get_all()) + def host_get_PIFs(self, session, ref): + return xen_api_success(XendNode.instance().get_PIF_refs()) + def host_get_PPCIs(self, session, ref): + return xen_api_success(XendNode.instance().get_PPCI_refs()) + def host_get_PSCSIs(self, session, ref): + return xen_api_success(XendNode.instance().get_PSCSI_refs()) + def host_get_host_CPUs(self, session, host_ref): + return xen_api_success(XendNode.instance().get_host_cpu_refs()) + def host_get_metrics(self, _, ref): + return xen_api_success(XendNode.instance().host_metrics_uuid) + def host_get_capabilities(self, session, host_ref): + return xen_api_success(XendNode.instance().get_capabilities()) + def host_get_supported_bootloaders(self, session, host_ref): + return xen_api_success(['pygrub']) + def host_get_sched_policy(self, _, host_ref): + return xen_api_success(XendNode.instance().get_vcpus_policy()) + def host_get_cpu_configuration(self, _, host_ref): + return xen_api_success(XendNode.instance().get_cpu_configuration()) + def host_set_logging(self, _, host_ref, logging): + return xen_api_todo() + def host_get_logging(self, _, host_ref): + return xen_api_todo() + + # object methods + def host_disable(self, session, host_ref): + XendDomain.instance().set_allow_new_domains(False) + return xen_api_success_void() + def host_enable(self, session, host_ref): + XendDomain.instance().set_allow_new_domains(True) + return xen_api_success_void() + def host_reboot(self, session, host_ref): + if not XendDomain.instance().allow_new_domains(): + return xen_api_error(XEND_ERROR_HOST_RUNNING) + return xen_api_error(XEND_ERROR_UNSUPPORTED) + def host_shutdown(self, session, host_ref): + if not XendDomain.instance().allow_new_domains(): + return xen_api_error(XEND_ERROR_HOST_RUNNING) + return xen_api_error(XEND_ERROR_UNSUPPORTED) + + def host_dmesg(self, session, host_ref): + return xen_api_success(XendDmesg.instance().info()) + + def host_dmesg_clear(self, session, host_ref): + return xen_api_success(XendDmesg.instance().clear()) + + def host_get_log(self, session, host_ref): + log_file = open(XendLogging.getLogFilename()) + log_buffer = log_file.read() + log_buffer = log_buffer.replace('\b', ' ') + log_buffer = log_buffer.replace('\f', '\n') + log_file.close() + return xen_api_success(log_buffer) + + def host_send_debug_keys(self, _, host_ref, keys): + node = XendNode.instance() + node.send_debug_keys(keys) + return xen_api_success_void() + + def host_get_record(self, session, host_ref): + node = XendNode.instance() + dom = XendDomain.instance() + record = {'uuid': node.uuid, + 'name_label': node.name, + 'name_description': '', + 'API_version_major': XEN_API_VERSION_MAJOR, + 'API_version_minor': XEN_API_VERSION_MINOR, + 'API_version_vendor': XEN_API_VERSION_VENDOR, + 'API_version_vendor_implementation': + XEN_API_VERSION_VENDOR_IMPLEMENTATION, + 'software_version': node.xen_version(), + 'enabled': XendDomain.instance().allow_new_domains(), + 'other_config': node.other_config, + 'resident_VMs': dom.get_domain_refs(), + 'host_CPUs': node.get_host_cpu_refs(), + 'cpu_configuration': node.get_cpu_configuration(), + 'metrics': node.host_metrics_uuid, + 'capabilities': node.get_capabilities(), + 'supported_bootloaders': ['pygrub'], + 'sched_policy': node.get_vcpus_policy(), + 'logging': {}, + 'PIFs': XendPIF.get_all(), + 'PBDs': XendPBD.get_all(), + 'PPCIs': XendPPCI.get_all(), + 'PSCSIs': XendPSCSI.get_all()} + return xen_api_success(record) + + # class methods + def host_get_all(self, session): + return xen_api_success((XendNode.instance().uuid,)) + def host_get_by_name_label(self, session, name): + if XendNode.instance().name == name: + return xen_api_success((XendNode.instance().uuid,)) + return xen_api_success([]) + + def host_list_methods(self, _): + def _funcs(): + return [getattr(XendAPI, x) for x in XendAPI.__dict__] + + return xen_api_success([x.api for x in _funcs() + if hasattr(x, 'api')]) + + # Xen API: Class host_CPU + # ---------------------------------------------------------------- + + host_cpu_attr_ro = ['host', + 'number', + 'vendor', + 'speed', + 'modelname', + 'stepping', + 'flags', + 'utilisation', + 'features'] + + # attributes + def _host_cpu_get(self, ref, field): + return xen_api_success( + XendNode.instance().get_host_cpu_field(ref, field)) + + def host_cpu_get_host(self, _, ref): + return xen_api_success(XendNode.instance().uuid) + def host_cpu_get_features(self, _, ref): + return self._host_cpu_get(ref, 'features') + def host_cpu_get_number(self, _, ref): + return self._host_cpu_get(ref, 'number') + def host_cpu_get_vendor(self, _, ref): + return self._host_cpu_get(ref, 'vendor') + def host_cpu_get_speed(self, _, ref): + return self._host_cpu_get(ref, 'speed') + def host_cpu_get_modelname(self, _, ref): + return self._host_cpu_get(ref, 'modelname') + def host_cpu_get_stepping(self, _, ref): + return self._host_cpu_get(ref, 'stepping') + def host_cpu_get_flags(self, _, ref): + return self._host_cpu_get(ref, 'flags') + def host_cpu_get_utilisation(self, _, ref): + return xen_api_success(XendNode.instance().get_host_cpu_load(ref)) + + # object methods + def host_cpu_get_record(self, _, ref): + node = XendNode.instance() + record = dict([(f, node.get_host_cpu_field(ref, f)) + for f in self.host_cpu_attr_ro + if f not in ['uuid', 'host', 'utilisation']]) + record['uuid'] = ref + record['host'] = node.uuid + record['utilisation'] = node.get_host_cpu_load(ref) + return xen_api_success(record) + + # class methods + def host_cpu_get_all(self, session): + return xen_api_success(XendNode.instance().get_host_cpu_refs()) + + + # Xen API: Class host_metrics + # ---------------------------------------------------------------- + + host_metrics_attr_ro = ['memory_total', + 'memory_free', + 'last_updated'] + host_metrics_attr_rw = [] + host_metrics_methods = [] + + def host_metrics_get_all(self, _): + return xen_api_success([XendNode.instance().host_metrics_uuid]) + + def _host_metrics_get(self, ref, f): + return xen_api_success(getattr(node, f)()) + + def host_metrics_get_record(self, _, ref): + return xen_api_success({ + 'uuid' : ref, + 'memory_total' : self._host_metrics_get_memory_total(), + 'memory_free' : self._host_metrics_get_memory_free(), + 'last_updated' : now(), + }) + + def host_metrics_get_memory_total(self, _1, _2): + return xen_api_success(self._host_metrics_get_memory_total()) + + def host_metrics_get_memory_free(self, _1, _2): + return xen_api_success(self._host_metrics_get_memory_free()) + + def host_metrics_get_last_updated(self, _1, _2): + return xen_api_success(now()) + + def _host_metrics_get_memory_total(self): + node = XendNode.instance() + return node.xc.physinfo()['total_memory'] * 1024 + + def _host_metrics_get_memory_free(self): + node = XendNode.instance() + return node.xc.physinfo()['free_memory'] * 1024 + + # Xen API: Class VM + # ---------------------------------------------------------------- + + VM_attr_ro = ['power_state', + 'resident_on', + 'consoles', + 'VIFs', + 'VBDs', + 'VTPMs', + 'DPCIs', + 'DSCSIs', + 'tools_version', + 'domid', + 'is_control_domain', + 'metrics', + 'crash_dumps', + ] + + VM_attr_rw = ['name_label', + 'name_description', + 'user_version', + 'is_a_template', + 'auto_power_on', + 'memory_dynamic_max', + 'memory_dynamic_min', + 'memory_static_max', + 'memory_static_min', + 'VCPUs_max', + 'VCPUs_at_startup', + 'VCPUs_params', + 'actions_after_shutdown', + 'actions_after_reboot', + 'actions_after_suspend', + 'actions_after_crash', + 'PV_bootloader', + 'PV_kernel', + 'PV_ramdisk', + 'PV_args', + 'PV_bootloader_args', + 'HVM_boot_policy', + 'HVM_boot_params', + 'platform', + 'PCI_bus', + 'other_config', + 'security_label'] + + VM_methods = [('clone', 'VM'), + ('start', None), + ('pause', None), + ('unpause', None), + ('clean_shutdown', None), + ('clean_reboot', None), + ('hard_shutdown', None), + ('hard_reboot', None), + ('suspend', None), + ('resume', None), + ('send_sysrq', None), + ('set_VCPUs_number_live', None), + ('add_to_HVM_boot_params', None), + ('remove_from_HVM_boot_params', None), + ('add_to_VCPUs_params', None), + ('add_to_VCPUs_params_live', None), + ('remove_from_VCPUs_params', None), + ('add_to_platform', None), + ('remove_from_platform', None), + ('add_to_other_config', None), + ('remove_from_other_config', None), + ('save', None), + ('set_memory_dynamic_max_live', None), + ('set_memory_dynamic_min_live', None), + ('send_trigger', None), + ('migrate', None), + ('destroy', None)] + + VM_funcs = [('create', 'VM'), + ('restore', None), + ('get_by_name_label', 'Set(VM)')] + + # parameters required for _create() + VM_attr_inst = [ + 'name_label', + 'name_description', + 'user_version', + 'is_a_template', + 'memory_static_max', + 'memory_dynamic_max', + 'memory_dynamic_min', + 'memory_static_min', + 'VCPUs_max', + 'VCPUs_at_startup', + 'VCPUs_params', + 'actions_after_shutdown', + 'actions_after_reboot', + 'actions_after_suspend', + 'actions_after_crash', + 'PV_bootloader', + 'PV_kernel', + 'PV_ramdisk', + 'PV_args', + 'PV_bootloader_args', + 'HVM_boot_policy', + 'HVM_boot_params', + 'platform', + 'PCI_bus', + 'other_config', + 'security_label'] + + def VM_get(self, name, session, vm_ref): + return xen_api_success( + XendDomain.instance().get_vm_by_uuid(vm_ref).info[name]) + + def VM_set(self, name, session, vm_ref, value): + xd = XendDomain.instance() + dominfo = xd.get_vm_by_uuid(vm_ref) + dominfo.info[name] = value + return self._VM_save(dominfo) + + def _VM_save(self, dominfo): + XendDomain.instance().managed_config_save(dominfo) + return xen_api_success_void() + + # attributes (ro) + def VM_get_power_state(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_power_state()) + + def VM_get_resident_on(self, session, vm_ref): + return xen_api_success(XendNode.instance().uuid) + + def VM_get_memory_static_max(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_memory_static_max()) + + def VM_get_memory_static_min(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_memory_static_min()) + + def VM_get_VIFs(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_vifs()) + + def VM_get_VBDs(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_vbds()) + + def VM_get_VTPMs(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_vtpms()) + + def VM_get_consoles(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_consoles()) + + def VM_get_DPCIs(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_dpcis()) + + def VM_get_DSCSIs(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_dscsis()) + + def VM_get_tools_version(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return dom.get_tools_version() + + def VM_get_metrics(self, _, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_metrics()) + + def VM_get_VCPUs_max(self, _, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + def VM_get_VCPUs_at_startup(self, _, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + # attributes (rw) + def VM_get_name_label(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.getName()) + + def VM_get_name_description(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + def VM_get_user_version(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + def VM_get_is_a_template(self, session, ref): + return self.VM_get('is_a_template', session, ref) + + def VM_get_memory_dynamic_max(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_memory_dynamic_max()) + + def VM_get_memory_dynamic_min(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_memory_dynamic_min()) + + def VM_get_VCPUs_params(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_vcpus_params()) + + def VM_get_actions_after_shutdown(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_on_shutdown()) + + def VM_get_actions_after_reboot(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_on_reboot()) + + def VM_get_actions_after_suspend(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_on_suspend()) + + def VM_get_actions_after_crash(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_on_crash()) + + def VM_get_PV_bootloader(self, session, vm_ref): + return self.VM_get('PV_bootloader', session, vm_ref) + + def VM_get_PV_kernel(self, session, vm_ref): + return self.VM_get('PV_kernel', session, vm_ref) + + def VM_get_PV_ramdisk(self, session, vm_ref): + return self.VM_get('PV_ramdisk', session, vm_ref) + + def VM_get_PV_args(self, session, vm_ref): + return self.VM_get('PV_args', session, vm_ref) + + def VM_get_PV_bootloader_args(self, session, vm_ref): + return self.VM_get('PV_bootloader_args', session, vm_ref) + + def VM_get_HVM_boot_policy(self, session, vm_ref): + return self.VM_get('HVM_boot_policy', session, vm_ref) + + def VM_get_HVM_boot_params(self, session, vm_ref): + return self.VM_get('HVM_boot_params', session, vm_ref) + + def VM_get_platform(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_platform()) + + def VM_get_PCI_bus(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return dom.get_pci_bus() + + def VM_set_PCI_bus(self, session, vm_ref, val): + return self.VM_set('PCI_bus', session, vm_ref, val) + + def VM_get_other_config(self, session, vm_ref): + return self.VM_get('other_config', session, vm_ref) + + def VM_get_domid(self, _, ref): + domid = XendDomain.instance().get_vm_by_uuid(ref).getDomid() + return xen_api_success(domid is None and -1 or domid) + + def VM_get_is_control_domain(self, session, vm_ref): + xd = XendDomain.instance() + return xen_api_success( + xd.get_vm_by_uuid(vm_ref) == xd.privilegedDomain()) + + def VM_set_name_label(self, session, vm_ref, label): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.setName(label) + return self._VM_save(dom) + + def VM_set_name_description(self, session, vm_ref, desc): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + def VM_set_user_version(self, session, vm_ref, ver): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + def VM_set_is_a_template(self, session, vm_ref, is_template): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_todo() + + def VM_set_memory_dynamic_max(self, session, vm_ref, mem): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.set_memory_dynamic_max(int(mem)) + return self._VM_save(dom) + + def VM_set_memory_dynamic_min(self, session, vm_ref, mem): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.set_memory_dynamic_min(int(mem)) + return self._VM_save(dom) + + def VM_set_memory_static_max(self, session, vm_ref, mem): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.set_memory_static_max(int(mem)) + return self._VM_save(dom) + + def VM_set_memory_static_min(self, session, vm_ref, mem): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.set_memory_static_min(int(mem)) + return self._VM_save(dom) + + def VM_set_memory_dynamic_max_live(self, session, vm_ref, mem): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.set_memory_dynamic_max(int(mem)) + # need to pass target as MiB + dom.setMemoryTarget(int(mem)/1024/1024) + return xen_api_success_void() + + def VM_set_memory_dynamic_min_live(self, session, vm_ref, mem): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.set_memory_dynamic_min(int(mem)) + # need to pass target as MiB + dom.setMemoryTarget(int(mem)/1024/1024) + return xen_api_success_void() + + def VM_set_VCPUs_params(self, session, vm_ref, value): + return self.VM_set('vcpus_params', session, vm_ref, value) + + def VM_add_to_VCPUs_params(self, session, vm_ref, key, value): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if 'vcpus_params' not in dom.info: + dom.info['vcpus_params'] = {} + dom.info['vcpus_params'][key] = value + return self._VM_save(dom) + + def VM_add_to_VCPUs_params_live(self, session, vm_ref, key, value): + self.VM_add_to_VCPUs_params(session, vm_ref, key, value) + self._VM_VCPUs_params_refresh(vm_ref) + return xen_api_success_void() + + def _VM_VCPUs_params_refresh(self, vm_ref): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + + #update the cpumaps + for key, value in xeninfo.info['vcpus_params'].items(): + if key.startswith("cpumap"): + vcpu = int(key[6:]) + try: + cpus = map(int, value.split(",")) + xendom.domain_pincpu(xeninfo.getDomid(), vcpu, cpus) + except Exception, ex: + log.exception(ex) + + #need to update sched params aswell + if 'weight' in xeninfo.info['vcpus_params'] \ + and 'cap' in xeninfo.info['vcpus_params']: + weight = xeninfo.info['vcpus_params']['weight'] + cap = xeninfo.info['vcpus_params']['cap'] + xendom.domain_sched_credit_set(xeninfo.getDomid(), weight, cap) + + def VM_set_VCPUs_number_live(self, _, vm_ref, num): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.setVCpuCount(int(num)) + return xen_api_success_void() + + def VM_remove_from_VCPUs_params(self, session, vm_ref, key): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if 'vcpus_params' in dom.info \ + and key in dom.info['vcpus_params']: + del dom.info['vcpus_params'][key] + return self._VM_save(dom) + else: + return xen_api_success_void() + + def VM_set_VCPUs_at_startup(self, session, vm_ref, num): + return self.VM_set('VCPUs_at_startup', session, vm_ref, num) + + def VM_set_VCPUs_max(self, session, vm_ref, num): + return self.VM_set('VCPUs_max', session, vm_ref, num) + + def VM_set_actions_after_shutdown(self, session, vm_ref, action): + if action not in XEN_API_ON_NORMAL_EXIT: + return xen_api_error(['VM_ON_NORMAL_EXIT_INVALID', vm_ref]) + return self.VM_set('actions_after_shutdown', session, vm_ref, action) + + def VM_set_actions_after_reboot(self, session, vm_ref, action): + if action not in XEN_API_ON_NORMAL_EXIT: + return xen_api_error(['VM_ON_NORMAL_EXIT_INVALID', vm_ref]) + return self.VM_set('actions_after_reboot', session, vm_ref, action) + + def VM_set_actions_after_suspend(self, session, vm_ref, action): + if action not in XEN_API_ON_NORMAL_EXIT: + return xen_api_error(['VM_ON_NORMAL_EXIT_INVALID', vm_ref]) + return self.VM_set('actions_after_suspend', session, vm_ref, action) + + def VM_set_actions_after_crash(self, session, vm_ref, action): + if action not in XEN_API_ON_CRASH_BEHAVIOUR: + return xen_api_error(['VM_ON_CRASH_BEHAVIOUR_INVALID', vm_ref]) + return self.VM_set('actions_after_crash', session, vm_ref, action) + + def VM_set_HVM_boot_policy(self, session, vm_ref, value): + if value != "" and value != "BIOS order": + return xen_api_error( + ['VALUE_NOT_SUPPORTED', 'VM.HVM_boot_policy', value, + 'Xend supports only the "BIOS order" boot policy.']) + else: + return self.VM_set('HVM_boot_policy', session, vm_ref, value) + + def VM_set_HVM_boot_params(self, session, vm_ref, value): + return self.VM_set('HVM_boot_params', session, vm_ref, value) + + def VM_add_to_HVM_boot_params(self, session, vm_ref, key, value): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if 'HVM_boot_params' not in dom.info: + dom.info['HVM_boot_params'] = {} + dom.info['HVM_boot_params'][key] = value + return self._VM_save(dom) + + def VM_remove_from_HVM_boot_params(self, session, vm_ref, key): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if 'HVM_boot_params' in dom.info \ + and key in dom.info['HVM_boot_params']: + del dom.info['HVM_boot_params'][key] + return self._VM_save(dom) + else: + return xen_api_success_void() + + def VM_set_PV_bootloader(self, session, vm_ref, value): + return self.VM_set('PV_bootloader', session, vm_ref, value) + + def VM_set_PV_kernel(self, session, vm_ref, value): + return self.VM_set('PV_kernel', session, vm_ref, value) + + def VM_set_PV_ramdisk(self, session, vm_ref, value): + return self.VM_set('PV_ramdisk', session, vm_ref, value) + + def VM_set_PV_args(self, session, vm_ref, value): + return self.VM_set('PV_args', session, vm_ref, value) + + def VM_set_PV_bootloader_args(self, session, vm_ref, value): + return self.VM_set('PV_bootloader_args', session, vm_ref, value) + + def VM_set_platform(self, session, vm_ref, value): + return self.VM_set('platform', session, vm_ref, value) + + def VM_add_to_platform(self, session, vm_ref, key, value): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + plat = dom.get_platform() + plat[key] = value + return self.VM_set_platform(session, vm_ref, plat) + + def VM_remove_from_platform(self, session, vm_ref, key): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + plat = dom.get_platform() + if key in plat: + del plat[key] + return self.VM_set_platform(session, vm_ref, plat) + else: + return xen_api_success_void() + + def VM_set_other_config(self, session, vm_ref, value): + return self.VM_set('other_config', session, vm_ref, value) + + def VM_add_to_other_config(self, session, vm_ref, key, value): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if dom and 'other_config' in dom.info: + dom.info['other_config'][key] = value + return self._VM_save(dom) + + def VM_remove_from_other_config(self, session, vm_ref, key): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if dom and 'other_config' in dom.info \ + and key in dom.info['other_config']: + del dom.info['other_config'][key] + return self._VM_save(dom) + else: + return xen_api_success_void() + + def VM_get_crash_dumps(self, _, vm_ref): + return xen_api_todo() + + # class methods + def VM_get_all(self, session): + refs = [d.get_uuid() for d in XendDomain.instance().list('all')] + return xen_api_success(refs) + + def VM_get_by_name_label(self, session, label): + xendom = XendDomain.instance() + dom = xendom.domain_lookup_nr(label) + if dom: + return xen_api_success([dom.get_uuid()]) + return xen_api_success([]) + + def VM_get_security_label(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + label = dom.get_security_label() + return xen_api_success(label) + + def VM_set_security_label(self, session, vm_ref, sec_label, old_label): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + (rc, errors, oldlabel, new_ssidref) = \ + dom.set_security_label(sec_label, old_label) + if rc != xsconstants.XSERR_SUCCESS: + return xen_api_error(['SECURITY_ERROR', rc, + xsconstants.xserr2string(-rc)]) + if rc == 0: + rc = new_ssidref + return xen_api_success(rc) + + def VM_create(self, session, vm_struct): + xendom = XendDomain.instance() + domuuid = XendTask.log_progress(0, 100, + xendom.create_domain, vm_struct) + return xen_api_success(domuuid) + + # object methods + def VM_get_record(self, session, vm_ref): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + if not xeninfo: + return xen_api_error(['HANDLE_INVALID', 'VM', vm_ref]) + + domid = xeninfo.getDomid() + + record = { + 'uuid': xeninfo.get_uuid(), + 'power_state': xeninfo.get_power_state(), + 'name_label': xeninfo.getName(), + 'name_description': xeninfo.getName(), + 'user_version': 1, + 'is_a_template': xeninfo.info['is_a_template'], + 'auto_power_on': False, + 'resident_on': XendNode.instance().uuid, + 'memory_static_min': xeninfo.get_memory_static_min(), + 'memory_static_max': xeninfo.get_memory_static_max(), + 'memory_dynamic_min': xeninfo.get_memory_dynamic_min(), + 'memory_dynamic_max': xeninfo.get_memory_dynamic_max(), + 'VCPUs_params': xeninfo.get_vcpus_params(), + 'VCPUs_at_startup': xeninfo.getVCpuCount(), + 'VCPUs_max': xeninfo.getVCpuCount(), + 'actions_after_shutdown': xeninfo.get_on_shutdown(), + 'actions_after_reboot': xeninfo.get_on_reboot(), + 'actions_after_suspend': xeninfo.get_on_suspend(), + 'actions_after_crash': xeninfo.get_on_crash(), + 'consoles': xeninfo.get_consoles(), + 'VIFs': xeninfo.get_vifs(), + 'VBDs': xeninfo.get_vbds(), + 'VTPMs': xeninfo.get_vtpms(), + 'DPCIs': xeninfo.get_dpcis(), + 'DSCSIs': xeninfo.get_dscsis(), + 'PV_bootloader': xeninfo.info.get('PV_bootloader'), + 'PV_kernel': xeninfo.info.get('PV_kernel'), + 'PV_ramdisk': xeninfo.info.get('PV_ramdisk'), + 'PV_args': xeninfo.info.get('PV_args'), + 'PV_bootloader_args': xeninfo.info.get('PV_bootloader_args'), + 'HVM_boot_policy': xeninfo.info.get('HVM_boot_policy'), + 'HVM_boot_params': xeninfo.info.get('HVM_boot_params'), + 'platform': xeninfo.get_platform(), + 'PCI_bus': xeninfo.get_pci_bus(), + 'tools_version': xeninfo.get_tools_version(), + 'other_config': xeninfo.info.get('other_config', {}), + 'domid': domid is None and -1 or domid, + 'is_control_domain': xeninfo.info['is_control_domain'], + 'metrics': xeninfo.get_metrics(), + 'security_label': xeninfo.get_security_label(), + 'crash_dumps': [] + } + return xen_api_success(record) + + def VM_clean_reboot(self, session, vm_ref): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + XendTask.log_progress(0, 100, xeninfo.shutdown, "reboot") + return xen_api_success_void() + + def VM_clean_shutdown(self, session, vm_ref): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + XendTask.log_progress(0, 100, xeninfo.shutdown, "poweroff") + return xen_api_success_void() + + def VM_clone(self, session, vm_ref): + return xen_api_error(XEND_ERROR_UNSUPPORTED) + + def VM_destroy(self, session, vm_ref): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_delete", vm_ref) + + def VM_hard_reboot(self, session, vm_ref): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_reset", vm_ref) + + def VM_hard_shutdown(self, session, vm_ref): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_destroy", vm_ref) + + def VM_pause(self, session, vm_ref): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_pause", vm_ref) + + def VM_resume(self, session, vm_ref, start_paused): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_resume", vm_ref, + start_paused = start_paused) + + def VM_start(self, session, vm_ref, start_paused): + try: + return XendTask.log_progress(0, 100, do_vm_func, + "domain_start", vm_ref, + start_paused = start_paused) + except HVMRequired, exn: + return xen_api_error(['VM_HVM_REQUIRED', vm_ref]) + + def VM_suspend(self, session, vm_ref): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_suspend", vm_ref) + + def VM_unpause(self, session, vm_ref): + return XendTask.log_progress(0, 100, do_vm_func, + "domain_unpause", vm_ref) + + def VM_send_sysrq(self, _, vm_ref, req): + xeninfo = XendDomain.instance().get_vm_by_uuid(vm_ref) + if xeninfo.state == XEN_API_VM_POWER_STATE_RUNNING \ + or xeninfo.state == XEN_API_VM_POWER_STATE_PAUSED: + xeninfo.send_sysrq(req) + return xen_api_success_void() + else: + return xen_api_error( + ['VM_BAD_POWER_STATE', vm_ref, + XendDomain.POWER_STATE_NAMES[XEN_API_VM_POWER_STATE_RUNNING], + XendDomain.POWER_STATE_NAMES[xeninfo.state]]) + + def VM_send_trigger(self, _, vm_ref, trigger, vcpu): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + xendom.domain_send_trigger(xeninfo.getDomid(), trigger, vcpu) + return xen_api_success_void() + + def VM_migrate(self, _, vm_ref, destination_url, live, other_config): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + + port = other_config.get("port", 0) + node = other_config.get("node", -1) + ssl = other_config.get("ssl", None) + + xendom.domain_migrate(xeninfo.getDomid(), destination_url, + bool(live), port, node, ssl) + return xen_api_success_void() + + def VM_save(self, _, vm_ref, dest, checkpoint): + xendom = XendDomain.instance() + xeninfo = xendom.get_vm_by_uuid(vm_ref) + xendom.domain_save(xeninfo.getDomid(), dest, checkpoint) + return xen_api_success_void() + + def VM_restore(self, _, src, paused): + xendom = XendDomain.instance() + xendom.domain_restore(src, bool(paused)) + return xen_api_success_void() + + + # Xen API: Class VBD + # ---------------------------------------------------------------- + + VBD_attr_ro = ['VM', + 'VDI', + 'metrics', + 'runtime_properties'] + VBD_attr_rw = ['device', + 'bootable', + 'mode', + 'type'] + + VBD_attr_inst = VBD_attr_rw + + VBD_methods = [('media_change', None), ('destroy', None)] + VBD_funcs = [('create', 'VBD')] + + # object methods + def VBD_get_record(self, session, vbd_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vbd', vbd_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VBD', vbd_ref]) + cfg = vm.get_dev_xenapi_config('vbd', vbd_ref) + if not cfg: + return xen_api_error(['HANDLE_INVALID', 'VBD', vbd_ref]) + + valid_vbd_keys = self.VBD_attr_ro + self.VBD_attr_rw + \ + self.Base_attr_ro + self.Base_attr_rw + + return_cfg = {} + for k in cfg.keys(): + if k in valid_vbd_keys: + return_cfg[k] = cfg[k] + + return_cfg['metrics'] = vbd_ref + return_cfg['runtime_properties'] = {} #todo + + return xen_api_success(return_cfg) + + def VBD_media_change(self, session, vbd_ref, vdi_ref): + return xen_api_error(XEND_ERROR_UNSUPPORTED) + + # class methods + def VBD_create(self, session, vbd_struct): + xendom = XendDomain.instance() + xennode = XendNode.instance() + + if not xendom.is_valid_vm(vbd_struct['VM']): + return xen_api_error(['HANDLE_INVALID', 'VM', vbd_struct['VM']]) + + dom = xendom.get_vm_by_uuid(vbd_struct['VM']) + vdi = xennode.get_vdi_by_uuid(vbd_struct['VDI']) + if not vdi: + return xen_api_error(['HANDLE_INVALID', 'VDI', vdi_ref]) + + # new VBD via VDI/SR + vdi_image = vdi.get_location() + + try: + vbd_ref = XendTask.log_progress(0, 100, + dom.create_vbd, + vbd_struct, vdi_image) + except XendError, e: + log.exception("Error in VBD_create") + return xen_api_error(['INTERNAL_ERROR', str(e)]) + + vdi.addVBD(vbd_ref) + + xendom.managed_config_save(dom) + return xen_api_success(vbd_ref) + + + def VBD_destroy(self, session, vbd_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vbd', vbd_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VBD', vbd_ref]) + + vdi_ref = XendDomain.instance()\ + .get_dev_property_by_uuid('vbd', vbd_ref, "VDI") + vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref) + + XendTask.log_progress(0, 100, vm.destroy_vbd, vbd_ref) + + vdi.removeVBD(vbd_ref) + + return xen_api_success_void() + + def _VBD_get(self, vbd_ref, prop): + return xen_api_success( + XendDomain.instance().get_dev_property_by_uuid( + 'vbd', vbd_ref, prop)) + + # attributes (ro) + def VBD_get_metrics(self, _, vbd_ref): + return xen_api_success(vbd_ref) + + def VBD_get_runtime_properties(self, _, vbd_ref): + xendom = XendDomain.instance() + dominfo = xendom.get_vm_with_dev_uuid('vbd', vbd_ref) + device = dominfo.get_dev_config_by_uuid('vbd', vbd_ref) + + try: + devid = int(device['id']) + device_sxps = dominfo.getDeviceSxprs('vbd') + device_dicts = [dict(device_sxp[1][0:]) for device_sxp in device_sxps] + device_dict = [device_dict + for device_dict in device_dicts + if int(device_dict['virtual-device']) == devid][0] + + return xen_api_success(device_dict) + except Exception, exn: + log.exception(exn) + return xen_api_success({}) + + # attributes (rw) + def VBD_get_VM(self, session, vbd_ref): + return self._VBD_get(vbd_ref, 'VM') + + def VBD_get_VDI(self, session, vbd_ref): + return self._VBD_get(vbd_ref, 'VDI') + + def VBD_get_device(self, session, vbd_ref): + return self._VBD_get(vbd_ref, 'device') + + def VBD_get_bootable(self, session, vbd_ref): + return self._VBD_get(vbd_ref, 'bootable') + + def VBD_get_mode(self, session, vbd_ref): + return self._VBD_get(vbd_ref, 'mode') + + def VBD_get_type(self, session, vbd_ref): + return self._VBD_get(vbd_ref, 'type') + + def VBD_set_bootable(self, session, vbd_ref, bootable): + bootable = bool(bootable) + xd = XendDomain.instance() + vm = xd.get_vm_with_dev_uuid('vbd', vbd_ref) + vm.set_dev_property('vbd', vbd_ref, 'bootable', bootable) + xd.managed_config_save(vm) + return xen_api_success_void() + + def VBD_set_mode(self, session, vbd_ref, mode): + if mode == 'RW': + mode = 'w' + else: + mode = 'r' + xd = XendDomain.instance() + vm = xd.get_vm_with_dev_uuid('vbd', vbd_ref) + vm.set_dev_property('vbd', vbd_ref, 'mode', mode) + xd.managed_config_save(vm) + return xen_api_success_void() + + def VBD_get_all(self, session): + xendom = XendDomain.instance() + vbds = [d.get_vbds() for d in XendDomain.instance().list('all')] + vbds = reduce(lambda x, y: x + y, vbds) + return xen_api_success(vbds) + + + # Xen API: Class VBD_metrics + # ---------------------------------------------------------------- + + VBD_metrics_attr_ro = ['io_read_kbs', + 'io_write_kbs', + 'last_updated'] + VBD_metrics_attr_rw = [] + VBD_metrics_methods = [] + + def VBD_metrics_get_all(self, session): + return self.VBD_get_all(session) + + def VBD_metrics_get_record(self, _, ref): + vm = XendDomain.instance().get_vm_with_dev_uuid('vbd', ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VBD_metrics', ref]) + return xen_api_success( + { 'io_read_kbs' : vm.get_dev_property('vbd', ref, 'io_read_kbs'), + 'io_write_kbs' : vm.get_dev_property('vbd', ref, 'io_write_kbs'), + 'last_updated' : now() + }) + + def VBD_metrics_get_io_read_kbs(self, _, ref): + return self._VBD_get(ref, 'io_read_kbs') + + def VBD_metrics_get_io_write_kbs(self, session, ref): + return self._VBD_get(ref, 'io_write_kbs') + + def VBD_metrics_get_last_updated(self, _1, _2): + return xen_api_success(now()) + + + # Xen API: Class VIF + # ---------------------------------------------------------------- + + VIF_attr_ro = ['network', + 'VM', + 'metrics', + 'runtime_properties'] + VIF_attr_rw = ['device', + 'MAC', + 'MTU', + 'security_label'] + + VIF_attr_inst = VIF_attr_rw + + VIF_methods = [('destroy', None)] + VIF_funcs = [('create', 'VIF')] + + + # object methods + def VIF_get_record(self, session, vif_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vif', vif_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VIF', vif_ref]) + cfg = vm.get_dev_xenapi_config('vif', vif_ref) + if not cfg: + return xen_api_error(['HANDLE_INVALID', 'VIF', vif_ref]) + + valid_vif_keys = self.VIF_attr_ro + self.VIF_attr_rw + \ + self.Base_attr_ro + self.Base_attr_rw + + return_cfg = {} + for k in cfg.keys(): + if k in valid_vif_keys: + return_cfg[k] = cfg[k] + + return_cfg['metrics'] = vif_ref + + return xen_api_success(return_cfg) + + # class methods + def VIF_create(self, session, vif_struct): + xendom = XendDomain.instance() + if not xendom.is_valid_vm(vif_struct['VM']): + return xen_api_error(['HANDLE_INVALID', 'VM', vif_struct['VM']]) + + dom = xendom.get_vm_by_uuid(vif_struct['VM']) + try: + vif_ref = dom.create_vif(vif_struct) + xendom.managed_config_save(dom) + return xen_api_success(vif_ref) + except XendError, exn: + return xen_api_error(['INTERNAL_ERROR', str(exn)]) + + def VIF_destroy(self, session, vif_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vif', vif_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VIF', vif_ref]) + + vm.destroy_vif(vif_ref) + return xen_api_success_void() + + def _VIF_get(self, ref, prop): + return xen_api_success( + XendDomain.instance().get_dev_property_by_uuid('vif', ref, prop)) + + # getters/setters + def VIF_get_metrics(self, _, vif_ref): + return xen_api_success(vif_ref) + + def VIF_get_VM(self, session, vif_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vif', vif_ref) + return xen_api_success(vm.get_uuid()) + + def VIF_get_MTU(self, session, vif_ref): + return self._VIF_get(vif_ref, 'MTU') + + def VIF_get_MAC(self, session, vif_ref): + return self._VIF_get(vif_ref, 'MAC') + + def VIF_get_device(self, session, vif_ref): + return self._VIF_get(vif_ref, 'device') + + def VIF_get_all(self, session): + xendom = XendDomain.instance() + vifs = [d.get_vifs() for d in XendDomain.instance().list('all')] + vifs = reduce(lambda x, y: x + y, vifs) + return xen_api_success(vifs) + + def VIF_get_runtime_properties(self, _, vif_ref): + xendom = XendDomain.instance() + dominfo = xendom.get_vm_with_dev_uuid('vif', vif_ref) + device = dominfo.get_dev_config_by_uuid('vif', vif_ref) + + try: + devid = int(device['id']) + + device_sxps = dominfo.getDeviceSxprs('vif') + device_dicts = [dict(device_sxp[1][1:]) + for device_sxp in device_sxps] + + device_dict = [device_dict + for device_dict in device_dicts + if int(device_dict['handle']) == devid][0] + + return xen_api_success(device_dict) + + except Exception, exn: + log.exception(exn) + return xen_api_success({}) + + def VIF_get_security_label(self, session, vif_ref): + return self._VIF_get(vif_ref, 'security_label') + + def _VIF_set(self, ref, prop, val, old_val): + return XendDomain.instance().set_dev_property_by_uuid( + 'vif', ref, prop, val, old_val) + + def VIF_set_security_label(self, session, vif_ref, sec_lab, old_lab): + xendom = XendDomain.instance() + dom = xendom.get_vm_with_dev_uuid('vif', vif_ref) + if not dom: + return xen_api_error(['HANDLE_INVALID', 'VIF', vif_ref]) + + if dom._stateGet() == XEN_API_VM_POWER_STATE_RUNNING: + raise SecurityError(-xsconstants.XSERR_RESOURCE_IN_USE) + + rc = self._VIF_set(vif_ref, 'security_label', sec_lab, old_lab) + if rc == False: + raise SecurityError(-xsconstants.XSERR_BAD_LABEL) + return xen_api_success(xsconstants.XSERR_SUCCESS) + + + # Xen API: Class VIF_metrics + # ---------------------------------------------------------------- + + VIF_metrics_attr_ro = ['io_read_kbs', + 'io_write_kbs', + 'io_total_read_kbs', + 'io_total_write_kbs', + 'last_updated'] + VIF_metrics_attr_rw = [] + VIF_metrics_methods = [] + + def VIF_metrics_get_all(self, session): + return self.VIF_get_all(session) + + def VIF_metrics_get_record(self, _, ref): + vm = XendDomain.instance().get_vm_with_dev_uuid('vif', ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VIF_metrics', ref]) + return xen_api_success( + { 'io_read_kbs' : vm.get_dev_property('vif', ref, 'io_read_kbs'), + 'io_write_kbs' : vm.get_dev_property('vif', ref, 'io_write_kbs'), + 'io_total_read_kbs' : vm.get_dev_property('vif', ref, 'io_total_read_kbs'), + 'io_total_write_kbs' : vm.get_dev_property('vif', ref, 'io_total_write_kbs'), + 'last_updated' : now() + }) + + def VIF_metrics_get_io_read_kbs(self, _, ref): + return self._VIF_get(ref, 'io_read_kbs') + + def VIF_metrics_get_io_write_kbs(self, session, ref): + return self._VIF_get(ref, 'io_write_kbs') + + def VIF_metrics_get_io_total_read_kbs(self, _, ref): + return self._VIF_get(ref, 'io_total_read_kbs') + + def VIF_metrics_get_io_total_write_kbs(self, session, ref): + return self._VIF_get(ref, 'io_total_write_kbs') + + def VIF_metrics_get_last_updated(self, _1, _2): + return xen_api_success(now()) + + + # Xen API: Class VDI + # ---------------------------------------------------------------- + VDI_attr_ro = ['SR', + 'VBDs', + 'physical_utilisation', + 'type'] + VDI_attr_rw = ['name_label', + 'name_description', + 'virtual_size', + 'sharable', + 'read_only', + 'other_config', + 'security_label'] + VDI_attr_inst = VDI_attr_ro + VDI_attr_rw + + VDI_methods = [('destroy', None)] + VDI_funcs = [('create', 'VDI'), + ('get_by_name_label', 'Set(VDI)')] + + def _get_VDI(self, ref): + return XendNode.instance().get_vdi_by_uuid(ref) + + def VDI_get_VBDs(self, session, vdi_ref): + vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref) + return xen_api_success(vdi.getVBDs()) + + def VDI_get_physical_utilisation(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref). + get_physical_utilisation()) + + def VDI_get_type(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).type) + + def VDI_get_name_label(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).name_label) + + def VDI_get_name_description(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).name_description) + + def VDI_get_SR(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).sr_uuid) + + def VDI_get_virtual_size(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).virtual_size) + + def VDI_get_sharable(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).sharable) + + def VDI_get_read_only(self, session, vdi_ref): + return xen_api_success(self._get_VDI(vdi_ref).read_only) + + def VDI_set_name_label(self, session, vdi_ref, value): + self._get_VDI(vdi_ref).name_label = value + return xen_api_success_void() + + def VDI_set_name_description(self, session, vdi_ref, value): + self._get_VDI(vdi_ref).name_description = value + return xen_api_success_void() + + def VDI_set_virtual_size(self, session, vdi_ref, value): + return xen_api_error(XEND_ERROR_UNSUPPORTED) + + def VDI_set_sharable(self, session, vdi_ref, value): + self._get_VDI(vdi_ref).sharable = bool(value) + return xen_api_success_void() + + def VDI_set_read_only(self, session, vdi_ref, value): + self._get_VDI(vdi_ref).read_only = bool(value) + return xen_api_success_void() + + def VDI_get_other_config(self, session, vdi_ref): + return xen_api_success( + self._get_VDI(vdi_ref).other_config) + + def VDI_set_other_config(self, session, vdi_ref, other_config): + self._get_VDI(vdi_ref).other_config = other_config + return xen_api_success_void() + + # Object Methods + + def VDI_destroy(self, session, vdi_ref): + sr = XendNode.instance().get_sr_containing_vdi(vdi_ref) + sr.destroy_vdi(vdi_ref) + return xen_api_success_void() + + def VDI_get_record(self, session, vdi_ref): + image = XendNode.instance().get_vdi_by_uuid(vdi_ref) + return xen_api_success({ + 'uuid': vdi_ref, + 'name_label': image.name_label, + 'name_description': image.name_description, + 'SR': image.sr_uuid, + 'VBDs': image.getVBDs(), + 'virtual_size': image.virtual_size, + 'physical_utilisation': image.physical_utilisation, + 'type': image.type, + 'sharable': image.sharable, + 'read_only': image.read_only, + 'other_config': image.other_config, + 'security_label' : image.get_security_label() + }) + + # Class Functions + def VDI_create(self, session, vdi_struct): + sr_ref = vdi_struct.get('SR') + xennode = XendNode.instance() + if not xennode.is_valid_sr(sr_ref): + return xen_api_error(['HANDLE_INVALID', 'SR', sr_ref]) + + vdi_uuid = xennode.srs[sr_ref].create_vdi(vdi_struct) + return xen_api_success(vdi_uuid) + + def VDI_get_all(self, session): + xennode = XendNode.instance() + vdis = [sr.get_vdis() for sr in xennode.srs.values()] + return xen_api_success(reduce(lambda x, y: x + y, vdis)) + + def VDI_get_by_name_label(self, session, name): + xennode = XendNode.instance() + return xen_api_success(xennode.get_vdi_by_name_label(name)) + + def VDI_set_security_label(self, session, vdi_ref, sec_lab, old_lab): + vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref) + rc = vdi.set_security_label(sec_lab, old_lab) + if rc < 0: + return xen_api_error(['SECURITY_ERROR', rc, + xsconstants.xserr2string(-rc)]) + return xen_api_success(rc) + + def VDI_get_security_label(self, session, vdi_ref): + vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref) + return xen_api_success(vdi.get_security_label()) + + # Xen API: Class VTPM + # ---------------------------------------------------------------- + + VTPM_attr_rw = ['other_config'] + VTPM_attr_ro = ['VM', + 'backend', + 'runtime_properties' ] + + VTPM_attr_inst = VTPM_attr_rw + + VTPM_methods = [('destroy', None)] + VTPM_funcs = [('create', 'VTPM')] + + def VTPM_get_other_config(self, session, vtpm_ref): + xendom = XendDomain.instance() + return xen_api_success(xendom.get_dev_property_by_uuid('vtpm', + vtpm_ref, + 'other_config')) + + def VTPM_set_other_config(self, session, vtpm_ref, other_config): + xendom = XendDomain.instance() + xendom.set_dev_property_by_uuid('vtpm', + vtpm_ref, + 'other_config', + other_config) + return xen_api_success_void() + + # object methods + def VTPM_get_record(self, session, vtpm_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VTPM', vtpm_ref]) + cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref) + if not cfg: + return xen_api_error(['HANDLE_INVALID', 'VTPM', vtpm_ref]) + valid_vtpm_keys = self.VTPM_attr_ro + self.VTPM_attr_rw + \ + self.Base_attr_ro + self.Base_attr_rw + return_cfg = {} + for k in cfg.keys(): + if k in valid_vtpm_keys: + return_cfg[k] = cfg[k] + + return xen_api_success(return_cfg) + + # Class Functions + def VTPM_get_backend(self, session, vtpm_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'VTPM', vtpm_ref]) + cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref) + if not cfg: + return xen_api_error(['HANDLE_INVALID', 'VTPM', vtpm_ref]) + if not cfg.has_key('backend'): + return xen_api_error(['INTERNAL_ERROR', 'VTPM backend not set']) + return xen_api_success(cfg['backend']) + + def VTPM_get_VM(self, session, vtpm_ref): + xendom = XendDomain.instance() + return xen_api_success(xendom.get_dev_property_by_uuid('vtpm', + vtpm_ref, 'VM')) + + def VTPM_destroy(self, session, vtpm_ref): + xendom = XendDomain.instance() + dom = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) + if dom: + if dom.state != XEN_API_VM_POWER_STATE_HALTED: + vm_ref = dom.get_dev_property('vtpm', vtpm_ref, 'VM') + return xen_api_error(['VM_BAD_POWER_STATE', vm_ref, + XendDomain.POWER_STATE_NAMES[XEN_API_VM_POWER_STATE_HALTED], + XendDomain.POWER_STATE_NAMES[dom.state]]) + from xen.xend.server import tpmif + tpmif.destroy_vtpmstate(dom.getName()) + return xen_api_success_void() + else: + return xen_api_error(['HANDLE_INVALID', 'VM', vtpm_struct['VM']]) + + # class methods + def VTPM_create(self, session, vtpm_struct): + xendom = XendDomain.instance() + if xendom.is_valid_vm(vtpm_struct['VM']): + dom = xendom.get_vm_by_uuid(vtpm_struct['VM']) + try: + vtpm_ref = dom.create_vtpm(vtpm_struct) + xendom.managed_config_save(dom) + return xen_api_success(vtpm_ref) + except XendError, exn: + return xen_api_error(['INTERNAL_ERROR', str(exn)]) + else: + return xen_api_error(['HANDLE_INVALID', 'VM', vtpm_struct['VM']]) + + def VTPM_get_all(self, session): + xendom = XendDomain.instance() + vtpms = [d.get_vtpms() for d in XendDomain.instance().list('all')] + vtpms = reduce(lambda x, y: x + y, vtpms) + return xen_api_success(vtpms) + + def VTPM_get_runtime_properties(self, _, vtpm_ref): + xendom = XendDomain.instance() + dominfo = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) + device = dominfo.get_dev_config_by_uuid('vtpm', vtpm_ref) + + try: + device_sxps = dominfo.getDeviceSxprs('vtpm') + device_dict = dict(device_sxps[0][1]) + return xen_api_success(device_dict) + except: + return xen_api_success({}) + + # Xen API: Class console + # ---------------------------------------------------------------- + + + console_attr_ro = ['location', 'protocol', 'VM'] + console_attr_rw = ['other_config'] + console_funcs = [('create', 'console')] + + def console_get_all(self, session): + xendom = XendDomain.instance() + cons = [d.get_consoles() for d in XendDomain.instance().list('all')] + cons = reduce(lambda x, y: x + y, cons) + return xen_api_success(cons) + + def console_get_location(self, session, console_ref): + xendom = XendDomain.instance() + return xen_api_success(xendom.get_dev_property_by_uuid('console', + console_ref, + 'location')) + + def console_get_protocol(self, session, console_ref): + xendom = XendDomain.instance() + return xen_api_success(xendom.get_dev_property_by_uuid('console', + console_ref, + 'protocol')) + + def console_get_VM(self, session, console_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('console', console_ref) + return xen_api_success(vm.get_uuid()) + + def console_get_other_config(self, session, console_ref): + xendom = XendDomain.instance() + return xen_api_success(xendom.get_dev_property_by_uuid('console', + console_ref, + 'other_config')) + + # object methods + def console_get_record(self, session, console_ref): + xendom = XendDomain.instance() + vm = xendom.get_vm_with_dev_uuid('console', console_ref) + if not vm: + return xen_api_error(['HANDLE_INVALID', 'console', console_ref]) + cfg = vm.get_dev_xenapi_config('console', console_ref) + if not cfg: + return xen_api_error(['HANDLE_INVALID', 'console', console_ref]) + + valid_console_keys = self.console_attr_ro + self.console_attr_rw + \ + self.Base_attr_ro + self.Base_attr_rw + + return_cfg = {} + for k in cfg.keys(): + if k in valid_console_keys: + return_cfg[k] = cfg[k] + + return xen_api_success(return_cfg) + + def console_create(self, session, console_struct): + xendom = XendDomain.instance() + if not xendom.is_valid_vm(console_struct['VM']): + return xen_api_error(['HANDLE_INVALID', 'VM', + console_struct['VM']]) + + dom = xendom.get_vm_by_uuid(console_struct['VM']) + try: + if 'protocol' not in console_struct: + return xen_api_error(['CONSOLE_PROTOCOL_INVALID', + 'No protocol specified']) + + console_ref = dom.create_console(console_struct) + xendom.managed_config_save(dom) + return xen_api_success(console_ref) + except XendError, exn: + return xen_api_error(['INTERNAL_ERROR', str(exn)]) + + def console_set_other_config(self, session, console_ref, other_config): + xd = XendDomain.instance() + vm = xd.get_vm_with_dev_uuid('console', console_ref) + vm.set_console_other_config(console_ref, other_config) + xd.managed_config_save(vm) + return xen_api_success_void() + + # Xen API: Class SR + # ---------------------------------------------------------------- + SR_attr_ro = ['VDIs', + 'PBDs', + 'virtual_allocation', + 'physical_utilisation', + 'physical_size', + 'type', + 'content_type'] + + SR_attr_rw = ['name_label', + 'name_description'] + + SR_attr_inst = ['physical_size', + 'type', + 'name_label', + 'name_description'] + + SR_methods = [] + SR_funcs = [('get_by_name_label', 'Set(SR)'), + ('get_by_uuid', 'SR')] + + # Class Functions + def SR_get_all(self, session): + return xen_api_success(XendNode.instance().get_all_sr_uuid()) + + def SR_get_by_name_label(self, session, label): + return xen_api_success(XendNode.instance().get_sr_by_name(label)) + + def SR_get_supported_types(self, _): + return xen_api_success(['local', 'qcow_file']) + + # Class Methods + + def SR_get_record(self, session, sr_ref): + sr = XendNode.instance().get_sr(sr_ref) + if sr: + return xen_api_success(sr.get_record()) + return xen_api_error(['HANDLE_INVALID', 'SR', sr_ref]) + + # Attribute acceess + + def _get_SR_func(self, sr_ref, func): + return xen_api_success(getattr(XendNode.instance().get_sr(sr_ref), + func)()) + + def _get_SR_attr(self, sr_ref, attr): + return xen_api_success(getattr(XendNode.instance().get_sr(sr_ref), + attr)) + + def SR_get_VDIs(self, _, ref): + return self._get_SR_func(ref, 'list_images') + + def SR_get_PBDs(self, _, ref): + return xen_api_success(XendPBD.get_by_SR(ref)) + + def SR_get_virtual_allocation(self, _, ref): + return self._get_SR_func(ref, 'virtual_allocation') + + def SR_get_physical_utilisation(self, _, ref): + return self._get_SR_func(ref, 'physical_utilisation') + + def SR_get_physical_size(self, _, ref): + return self._get_SR_attr(ref, 'physical_size') + + def SR_get_type(self, _, ref): + return self._get_SR_attr(ref, 'type') + + def SR_get_content_type(self, _, ref): + return self._get_SR_attr(ref, 'content_type') + + def SR_get_name_label(self, _, ref): + return self._get_SR_attr(ref, 'name_label') + + def SR_get_name_description(self, _, ref): + return self._get_SR_attr(ref, 'name_description') + + def SR_set_name_label(self, session, sr_ref, value): + sr = XendNode.instance.get_sr(sr_ref) + if sr: + sr.name_label = value + XendNode.instance().save() + return xen_api_success_void() + + def SR_set_name_description(self, session, sr_ref, value): + sr = XendNode.instance.get_sr(sr_ref) + if sr: + sr.name_description = value + XendNode.instance().save() + return xen_api_success_void() + + + # Xen API: Class event + # ---------------------------------------------------------------- + + event_attr_ro = [] + event_attr_rw = [] + event_funcs = [('register', None), + ('unregister', None), + ('next', None)] + + def event_register(self, session, reg_classes): + event_register(session, reg_classes) + return xen_api_success_void() + + def event_unregister(self, session, unreg_classes): + event_unregister(session, reg_classes) + return xen_api_success_void() + + def event_next(self, session): + return event_next(session) + + # Xen API: Class debug + # ---------------------------------------------------------------- + + debug_methods = [('destroy', None), + ('get_record', 'debug')] + debug_funcs = [('wait', None), + ('return_failure', None)] + + def debug_wait(self, session, wait_secs): + import time + prog_units = 100/float(wait_secs) + for i in range(int(wait_secs)): + XendTask.log_progress(prog_units * i, prog_units * (i + 1), + time.sleep, 1) + return xen_api_success_void() + + + def debug_return_failure(self, session): + return xen_api_error(['DEBUG_FAIL', session]) + + def debug_create(self, session): + debug_uuid = uuid.createString() + self._debug[debug_uuid] = None + return xen_api_success(debug_uuid) + + def debug_destroy(self, session, debug_ref): + del self._debug[debug_ref] + return xen_api_success_void() + + def debug_get_record(self, session, debug_ref): + return xen_api_success({'uuid': debug_ref}) + + +class XendAPIAsyncProxy: + """ A redirector for Async.Class.function calls to XendAPI + but wraps the call for use with the XendTaskManager. + + @ivar xenapi: Xen API instance + @ivar method_map: Mapping from XMLRPC method name to callable objects. + """ + + method_prefix = 'Async.' + + def __init__(self, xenapi): + """Initialises the Async Proxy by making a map of all + implemented Xen API methods for use with XendTaskManager. + + @param xenapi: XendAPI instance + """ + self.xenapi = xenapi + self.method_map = {} + for method_name in dir(self.xenapi): + method = getattr(self.xenapi, method_name) + if method_name[0] != '_' and hasattr(method, 'async') \ + and method.async == True: + self.method_map[method.api] = method + + def _dispatch(self, method, args): + """Overridden method so that SimpleXMLRPCServer will + resolve methods through this method rather than through + inspection. + + @param method: marshalled method name from XMLRPC. + @param args: marshalled arguments from XMLRPC. + """ + + # Only deal with method names that start with "Async." + if not method.startswith(self.method_prefix): + return xen_api_error(['MESSAGE_METHOD_UNKNOWN', method]) + + # Lookup synchronous version of the method + synchronous_method_name = method[len(self.method_prefix):] + if synchronous_method_name not in self.method_map: + return xen_api_error(['MESSAGE_METHOD_UNKNOWN', method]) + + method = self.method_map[synchronous_method_name] + + # Check that we've got enough arguments before issuing a task ID. + needed = argcounts[method.api] + if len(args) != needed: + return xen_api_error(['MESSAGE_PARAMETER_COUNT_MISMATCH', + self.method_prefix + method.api, needed, + len(args)]) + + # Validate the session before proceeding + session = args[0] + if not auth_manager().is_session_valid(session): + return xen_api_error(['SESSION_INVALID', session]) + + # create and execute the task, and return task_uuid + return_type = getattr(method, 'return_type', None) + task_uuid = XendTaskManager.create_task(method, args, + synchronous_method_name, + return_type, + synchronous_method_name, + session) + return xen_api_success(task_uuid) diff --git a/tools/python/xen/xend/XendAPIConstants.py b/tools/python/xen/xend/XendAPIConstants.py new file mode 100644 index 0000000..60c8511 --- /dev/null +++ b/tools/python/xen/xend/XendAPIConstants.py @@ -0,0 +1,70 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006-2007 XenSource Ltd. +#============================================================================ + +# +# Xen API Enums +# + +XEN_API_VM_POWER_STATE = [ + 'Halted', + 'Paused', + 'Running', + 'Suspended', + 'Halted', + 'Crashed', + 'Unknown' +] + +XEN_API_VM_POWER_STATE_HALTED = 0 +XEN_API_VM_POWER_STATE_PAUSED = 1 +XEN_API_VM_POWER_STATE_RUNNING = 2 +XEN_API_VM_POWER_STATE_SUSPENDED = 3 +XEN_API_VM_POWER_STATE_SHUTTINGDOWN = 4 +XEN_API_VM_POWER_STATE_CRASHED = 5 +XEN_API_VM_POWER_STATE_UNKNOWN = 6 + +XEN_API_ON_NORMAL_EXIT = [ + 'destroy', + 'restart', +] + +XEN_API_ON_CRASH_BEHAVIOUR = [ + 'destroy', + 'coredump_and_destroy', + 'restart', + 'coredump_and_restart', + 'preserve', + 'rename_restart' +] + +XEN_API_ON_CRASH_BEHAVIOUR_FILTER = { + 'destroy' : 'destroy', + 'coredump-destroy' : 'coredump_and_destroy', + 'coredump_and_destroy' : 'coredump_and_destroy', + 'restart' : 'restart', + 'coredump-restart' : 'coredump_and_restart', + 'coredump_and_restart' : 'coredump_and_restart', + 'preserve' : 'preserve', + 'rename-restart' : 'rename_restart', + 'rename_restart' : 'rename_restart', +} + +XEN_API_VBD_MODE = ['RO', 'RW'] +XEN_API_VDI_TYPE = ['system', 'user', 'ephemeral'] +XEN_API_VBD_TYPE = ['CD', 'Disk'] +XEN_API_TASK_STATUS_TYPE = ['pending', 'success', 'failure'] +XEN_API_CONSOLE_PROTOCOL = ['vt100', 'rfb', 'rdp'] diff --git a/tools/python/xen/xend/XendAPIStore.py b/tools/python/xen/xend/XendAPIStore.py new file mode 100644 index 0000000..372509d --- /dev/null +++ b/tools/python/xen/xend/XendAPIStore.py @@ -0,0 +1,59 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 Tom Wilkie +#============================================================================ +""" +This is a place to put instances of XenAPI objects, +instead of just holding them in arbitrary places. + +All objects which subclass XendBase should use this +mechanism. + +You must register both the uuid and type, and get objects +by type, to ensure safety +""" + +__classes = {} + +def register(uuid, type, inst): + __classes[(uuid, type)] = inst + return inst + +def deregister(uuid, type): + old = get(uuid, type) + del __classes[(uuid, type)] + return old + +def get(uuid, type): + """ + Get the instances by uuid and type + """ + return __classes.get((uuid, type), None) + +def get_all(all_type): + """ + Get all instances by type + """ + return [inst + for ((uuid, t), inst) in __classes.items() + if t == all_type] + +def get_all_uuid(all_type): + """ + Get all uuids by type + """ + return [uuid + for (uuid, t) in __classes.keys() + if t == all_type] diff --git a/tools/python/xen/xend/XendAPIVersion.py b/tools/python/xen/xend/XendAPIVersion.py new file mode 100644 index 0000000..2659108 --- /dev/null +++ b/tools/python/xen/xend/XendAPIVersion.py @@ -0,0 +1,22 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2007 XenSource Inc. +#============================================================================ + + +XEN_API_VERSION_MAJOR = 1 +XEN_API_VERSION_MINOR = 0 +XEN_API_VERSION_VENDOR = 'xenbits' +XEN_API_VERSION_VENDOR_IMPLEMENTATION = {} diff --git a/tools/python/xen/xend/XendAuthSessions.py b/tools/python/xen/xend/XendAuthSessions.py new file mode 100644 index 0000000..fb28e3f --- /dev/null +++ b/tools/python/xen/xend/XendAuthSessions.py @@ -0,0 +1,131 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ + +import time + +from xen.xend import uuid +from xen.xend.XendError import * +from xen.xend.XendLogging import log + +class XendAuthSessions: + """Keeps track of Xen API Login Sessions using PAM. + + Note: Login sessions are not valid across instances of Xend. + """ + def __init__(self): + self.sessions = {} + + def init(self): + pass + + def login_unconditionally(self, username): + """Returns a session UUID if valid. + + @rtype: string + @return: Session UUID + """ + new_session = uuid.createString() + self.sessions[new_session] = (username, time.time()) + return new_session + + def login_with_password(self, username, password): + """Returns a session UUID if valid, otherwise raises an error. + + @raises XendError: If login fails. + @rtype: string + @return: Session UUID + """ + if self.is_authorized(username, password): + return self.login_unconditionally(username) + + raise XendError("Login failed") + + def logout(self, session): + """Delete session of it exists.""" + if self.is_session_valid(session): + del self.sessions[session] + + def is_session_valid(self, session): + """Returns true is session is valid.""" + if type(session) == type(str()): + return (session in self.sessions) + return False + + def is_authorized(self, username, password): + """Returns true is a user is authorised via PAM. + + Note: We use the 'login' PAM stack rather than inventing + our own. + + @rtype: boolean + """ + pam_auth = None + try: + import PAM + pam_auth = PAM.pam() + except ImportError: + log.warn("python-pam is required for XenAPI support.") + return False + except NameError: + # if PAM doesn't exist, let's ignore it + return False + + pam_auth.start("login") + pam_auth.set_item(PAM.PAM_USER, username) + + def _pam_conv(auth, query_list, user_data = None): + resp = [] + for i in range(len(query_list)): + query, qtype = query_list[i] + if qtype == PAM.PAM_PROMPT_ECHO_ON: + resp.append((username, 0)) + elif qtype == PAM.PAM_PROMPT_ECHO_OFF: + resp.append((password, 0)) + else: + return None + return resp + + pam_auth.set_item(PAM.PAM_CONV, _pam_conv) + + try: + pam_auth.authenticate() + pam_auth.acct_mgmt() + except PAM.error, resp: + return False + except Exception, e: + log.warn("Error with PAM: %s" % str(e)) + return False + else: + return True + + def get_user(self, session): + try: + return self.sessions[session][0] + except (KeyError, IndexError): + return None + + +def instance(): + """Singleton constructor. Use this instead of the class constructor. + """ + global inst + try: + inst + except: + inst = XendAuthSessions() + inst.init() + return inst diff --git a/tools/python/xen/xend/XendBase.py b/tools/python/xen/xend/XendBase.py new file mode 100644 index 0000000..73fed47 --- /dev/null +++ b/tools/python/xen/xend/XendBase.py @@ -0,0 +1,126 @@ +#!/usr/bin/python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 Tom Wilkie +#============================================================================ +""" +Base class for all XenAPI classes +""" + +from xen.xend.XendError import * +from xen.xend import XendAPIStore + +class XendBase: + # + # These functions describe the object, and what is exposed via the API + # + def getClass(self): + return "Base" + + def getAttrRO(self): + return ['uuid'] + + def getAttrRW(self): + return [] + + def getAttrInst(self): + return [] + + def getMethods(self): + return ["get_record"] + + def getFuncs(self): + return ["get_all", "get_by_uuid", "get_all_records"] + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def __init__(self, uuid, record): + self.__uuid = uuid + + # First check this class implements all the correct methods: + for attr_ro in self.getAttrRO() + self.getAttrRW(): + if not hasattr(self, "get_%s" % attr_ro): + raise ImplementationError(self.getClass(), + "get_%s" % attr_ro) + + for attr_rw in self.getAttrRW(): + if not hasattr(self, "set_%s" % attr_rw): + raise ImplementationError(self.getClass(), + "set_%s" % attr_rw) + + for method in self.getMethods(): + if not hasattr(self, method): + raise ImplementationError(self.getClass(), + method) + + for func in self.getFuncs(): + if not hasattr(self.__class__, func): + raise ImplementationError(self.getClass(), + func) + + # Next check that the class is being created with the correct + # parameters + if not isinstance(record, dict): + raise CreateUnspecifiedAttributeError( + "record" , self.getClass()) + + for attr_inst in self.getAttrInst(): + if attr_inst not in record: + raise CreateUnspecifiedAttributeError( + attr_inst, self.getClass()) + setattr(self, attr_inst, record[attr_inst]) + + # Finally register it + XendAPIStore.register(uuid, self.getClass(), self) + + def destroy(self): + XendAPIStore.deregister(self.get_uuid(), self.getClass()) + + def get_uuid(self): + return self.__uuid + + def get_record(self): + keys = self.getAttrRO() + self.getAttrRW() + return dict([(key, getattr(self, "get_%s" % key)()) + for key in keys]) + + # + # Class methods + # + + def get_all(cls): + return XendAPIStore.get_all_uuid(cls.getClass()) + + def get_by_uuid(cls, uuid): + # Sanity check the uuid is one of us + me = XendAPIStore.get(uuid, cls.getClass()) + if me is not None and me.getClass() == cls.getClass(): + # In OSS, ref == uuid + return uuid + else: + raise "Big Error.. TODO!" + + def get_all_records(cls): + return dict([(inst.get_uuid(), inst.get_record()) + for inst in XendAPIStore.get_all(cls.getClass())]) + + get_all = classmethod(get_all) + get_by_uuid = classmethod(get_by_uuid) + get_all_records = classmethod(get_all_records) diff --git a/tools/python/xen/xend/XendBootloader.py b/tools/python/xen/xend/XendBootloader.py new file mode 100644 index 0000000..60e8761 --- /dev/null +++ b/tools/python/xen/xend/XendBootloader.py @@ -0,0 +1,186 @@ +# +# XendBootloader.py - Framework to run a boot loader for picking the kernel +# +# 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, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, select, errno, stat, signal, tty +import random +import shlex +from xen.xend import sxp + +from xen.util import mkdir +from XendLogging import log +from XendError import VmError + +import pty, termios, fcntl +from xen.lowlevel import ptsname + +def bootloader(blexec, disk, dom, quiet = False, blargs = '', kernel = '', + ramdisk = '', kernel_args = ''): + """Run the boot loader executable on the given disk and return a + config image. + @param blexec Binary to use as the boot loader + @param disk Disk to run the boot loader on. + @param dom DomainInfo representing the domain being booted. + @param quiet Run in non-interactive mode, just booting the default. + @param blargs Arguments to pass to the bootloader.""" + + if not os.access(blexec, os.X_OK): + msg = "Bootloader isn't executable" + log.error(msg) + raise VmError(msg) + if not os.access(disk, os.R_OK): + msg = "Disk isn't accessible" + log.error(msg) + raise VmError(msg) + + if os.uname()[0] == "NetBSD" and disk.startswith('/dev/'): + disk = disk.replace("/dev/", "/dev/r") + + mkdir.parents("/var/run/xend/boot/", stat.S_IRWXU) + + while True: + fifo = "/var/run/xend/boot/xenbl.%s" %(random.randint(0, 32000),) + try: + os.mkfifo(fifo, 0600) + except OSError, e: + if (e.errno != errno.EEXIST): + raise + break + + # 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. + + (m1, s1) = pty.openpty() + tty.setraw(m1); + fcntl.fcntl(m1, fcntl.F_SETFL, os.O_NDELAY); + os.close(s1) + slavename = ptsname.ptsname(m1) + dom.storeDom("console/tty", slavename) + + # Release the domain lock here, because we definitely don't want + # a stuck bootloader to deny service to other xend clients. + from xen.xend import XendDomain + domains = XendDomain.instance() + domains.domains_lock.release() + + (child, m2) = pty.fork() + if (not child): + args = [ blexec ] + if kernel: + args.append("--kernel=%s" % kernel) + if ramdisk: + args.append("--ramdisk=%s" % ramdisk) + if kernel_args: + args.append("--args=%s" % kernel_args) + if quiet: + args.append("-q") + args.append("--output=%s" % fifo) + if blargs: + args.extend(shlex.split(blargs)) + args.append(disk) + + try: + log.debug("Launching bootloader as %s." % str(args)) + env = os.environ.copy() + env['TERM'] = 'vt100' + os.execvpe(args[0], args, env) + except OSError, e: + print e + pass + os._exit(1) + + # record that this domain is bootloading + dom.bootloader_pid = child + + tty.setraw(m2); + fcntl.fcntl(m2, fcntl.F_SETFL, os.O_NDELAY); + while True: + try: + r = os.open(fifo, os.O_RDONLY) + except OSError, e: + if e.errno == errno.EINTR: + continue + break + ret = "" + inbuf=""; outbuf=""; + while True: + sel = select.select([r, m1, m2], [m1, m2], []) + try: + if m1 in sel[0]: + s = os.read(m1, 1) + inbuf += s + if m2 in sel[1] and len(inbuf) != 0: + os.write(m2, inbuf[0]) + inbuf = inbuf[1:] + except OSError, e: + if e.errno == errno.EIO: + pass + try: + if m2 in sel[0]: + s = os.read(m2, 1) + outbuf += s + if m1 in sel[1] and len(outbuf) != 0: + os.write(m1, outbuf[0]) + outbuf = outbuf[1:] + except OSError, e: + if e.errno == errno.EIO: + pass + if r in sel[0]: + s = os.read(r, 1) + ret = ret + s + if len(s) == 0: + break + del inbuf + del outbuf + os.waitpid(child, 0) + os.close(r) + os.close(m2) + os.close(m1) + os.unlink(fifo) + + # Re-acquire the lock to cover the changes we're about to make + # when we return to domain creation. + domains.domains_lock.acquire() + + if dom.bootloader_pid is None: + msg = "Domain was died while the bootloader was running." + log.error(msg) + raise VmError, msg + + dom.bootloader_pid = None + + if len(ret) == 0: + msg = "Boot loader didn't return any data!" + log.error(msg) + raise VmError, msg + + pin = sxp.Parser() + pin.input(ret) + pin.input_eof() + blcfg = pin.val + return blcfg + + +def bootloader_tidy(dom): + if hasattr(dom, "bootloader_pid") and dom.bootloader_pid is not None: + pid = dom.bootloader_pid + dom.bootloader_pid = None + os.kill(pid, signal.SIGKILL) + + diff --git a/tools/python/xen/xend/XendCheckpoint.py b/tools/python/xen/xend/XendCheckpoint.py new file mode 100644 index 0000000..af76bdc --- /dev/null +++ b/tools/python/xen/xend/XendCheckpoint.py @@ -0,0 +1,405 @@ +# Copyright (C) 2005 Christian Limpach +# Copyright (C) 2005 XenSource Ltd + +# 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. + +import os +import os.path +import re +import string +import threading +import fcntl +from struct import pack, unpack, calcsize + +from xen.util.xpopen import xPopen3 +import xen.util.auxbin +import xen.lowlevel.xc + +from xen.xend import balloon, sxp, image +from xen.xend.XendError import XendError, VmError +from xen.xend.XendLogging import log +from xen.xend.XendConfig import XendConfig +from xen.xend.XendConstants import * +from xen.xend import XendNode + +SIGNATURE = "LinuxGuestRecord" +QEMU_SIGNATURE = "QemuDeviceModelRecord" +dm_batch = 512 +XC_SAVE = "xc_save" +XC_RESTORE = "xc_restore" + + +sizeof_int = calcsize("i") +sizeof_unsigned_int = calcsize("I") +sizeof_unsigned_long = calcsize("L") + + +xc = xen.lowlevel.xc.xc() + + +def write_exact(fd, buf, errmsg): + if os.write(fd, buf) != len(buf): + raise XendError(errmsg) + + +def read_exact(fd, size, errmsg): + buf = '' + while size != 0: + readstr = os.read(fd, size) + if not len(readstr): + log.error("read_exact: EOF trying to read %d (buf='%s')" % \ + (size, buf)) + raise XendError(errmsg) + size = size - len(readstr) + buf = buf + readstr + return buf + + +def insert_after(list, pred, value): + for i,k in enumerate(list): + if type(k) == type([]): + if k[0] == pred: + list.insert (i+1, value) + return + + +def save(fd, dominfo, network, live, dst, checkpoint=False, node=-1): + write_exact(fd, SIGNATURE, "could not write guest state file: signature") + + sxprep = dominfo.sxpr() + + if node > -1: + insert_after(sxprep,'vcpus',['node', str(node)]) + + config = sxp.to_string(sxprep) + + domain_name = dominfo.getName() + # Rename the domain temporarily, so that we don't get a name clash if this + # domain is migrating (live or non-live) to the local host. Doing such a + # thing is useful for debugging. + dominfo.setName('migrating-' + domain_name) + + try: + dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name) + + write_exact(fd, pack("!i", len(config)), + "could not write guest state file: config len") + write_exact(fd, config, "could not write guest state file: config") + + image_cfg = dominfo.info.get('image', {}) + hvm = dominfo.info.is_hvm() + + # xc_save takes three customization parameters: maxit, max_f, and + # flags the last controls whether or not save is 'live', while the + # first two further customize behaviour when 'live' save is + # enabled. Passing "0" simply uses the defaults compiled into + # libxenguest; see the comments and/or code in xc_linux_save() for + # more information. + cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd), + str(dominfo.getDomid()), "0", "0", + str(int(live) | (int(hvm) << 2)) ] + log.debug("[xc_save]: %s", string.join(cmd)) + + def saveInputHandler(line, tochild): + log.debug("In saveInputHandler %s", line) + if line == "suspend": + log.debug("Suspending %d ...", dominfo.getDomid()) + dominfo.shutdown('suspend') + dominfo.waitForShutdown() + if line in ('suspend', 'suspended'): + dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2, + domain_name) + log.info("Domain %d suspended.", dominfo.getDomid()) + dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3, + domain_name) + if hvm: + dominfo.image.saveDeviceModel() + + if line == "suspend": + tochild.write("done\n") + tochild.flush() + log.debug('Written done') + + forkHelper(cmd, fd, saveInputHandler, False) + + # put qemu device model state + if os.path.exists("/var/lib/xen/qemu-save.%d" % dominfo.getDomid()): + write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature") + qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(), + os.O_RDONLY) + while True: + buf = os.read(qemu_fd, dm_batch) + if len(buf): + write_exact(fd, buf, "could not write device model state") + else: + break + os.close(qemu_fd) + os.remove("/var/lib/xen/qemu-save.%d" % dominfo.getDomid()) + + if checkpoint: + dominfo.resumeDomain() + else: + dominfo.destroy() + dominfo.testDeviceComplete() + try: + dominfo.setName(domain_name, False) + except VmError: + # Ignore this. The name conflict (hopefully) arises because we + # are doing localhost migration; if we are doing a suspend of a + # persistent VM, we need the rename, and don't expect the + # conflict. This needs more thought. + pass + + except Exception, exn: + log.exception("Save failed on domain %s (%s) - resuming.", domain_name, + dominfo.getDomid()) + dominfo.resumeDomain() + + try: + dominfo.setName(domain_name) + except: + log.exception("Failed to reset the migrating domain's name") + + raise exn + + +def restore(xd, fd, dominfo = None, paused = False, relocating = False): + signature = read_exact(fd, len(SIGNATURE), + "not a valid guest state file: signature read") + if signature != SIGNATURE: + raise XendError("not a valid guest state file: found '%s'" % + signature) + + l = read_exact(fd, sizeof_int, + "not a valid guest state file: config size read") + vmconfig_size = unpack("!i", l)[0] + vmconfig_buf = read_exact(fd, vmconfig_size, + "not a valid guest state file: config read") + + p = sxp.Parser() + p.input(vmconfig_buf) + if not p.ready: + raise XendError("not a valid guest state file: config parse") + + vmconfig = p.get_val() + + if not relocating: + domconfig = XendConfig(sxp_obj = vmconfig) + othervm = xd.domain_lookup_nr(domconfig["name_label"]) + if othervm is None or othervm.domid is None: + othervm = xd.domain_lookup_nr(domconfig["uuid"]) + if othervm is not None and othervm.domid is not None: + raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid)) + + if dominfo: + dominfo.resume() + else: + dominfo = xd.restore_(vmconfig) + + # repin domain vcpus if a target node number was specified + # this is done prior to memory allocation to aide in memory + # distribution for NUMA systems. + nodenr = -1 + for i,l in enumerate(vmconfig): + if type(l) == type([]): + if l[0] == 'node': + nodenr = int(l[1]) + + if nodenr >= 0: + node_to_cpu = XendNode.instance().xc.physinfo()['node_to_cpu'] + if nodenr < len(node_to_cpu): + for v in range(0, dominfo.info['VCPUs_max']): + xc.vcpu_setaffinity(dominfo.domid, v, node_to_cpu[nodenr]) + + store_port = dominfo.getStorePort() + console_port = dominfo.getConsolePort() + + assert store_port + assert console_port + + # if hvm, pass mem size to calculate the store_mfn + image_cfg = dominfo.info.get('image', {}) + is_hvm = dominfo.info.is_hvm() + if is_hvm: + apic = int(dominfo.info['platform'].get('apic', 0)) + pae = int(dominfo.info['platform'].get('pae', 0)) + log.info("restore hvm domain %d, apic=%d, pae=%d", + dominfo.domid, apic, pae) + else: + apic = 0 + pae = 0 + + try: + restore_image = image.create(dominfo, dominfo.info) + memory = restore_image.getRequiredAvailableMemory( + dominfo.info['memory_dynamic_max'] / 1024) + maxmem = restore_image.getRequiredAvailableMemory( + dominfo.info['memory_static_max'] / 1024) + shadow = restore_image.getRequiredShadowMemory( + dominfo.info['shadow_memory'] * 1024, + dominfo.info['memory_static_max'] / 1024) + + log.debug("restore:shadow=0x%x, _static_max=0x%x, _static_min=0x%x, ", + dominfo.info['shadow_memory'], + dominfo.info['memory_static_max'], + dominfo.info['memory_static_min']) + + # Round shadow up to a multiple of a MiB, as shadow_mem_control + # takes MiB and we must not round down and end up under-providing. + shadow = ((shadow + 1023) / 1024) * 1024 + + # set memory limit + xc.domain_setmaxmem(dominfo.getDomid(), maxmem) + + balloon.free(memory + shadow) + + shadow_cur = xc.shadow_mem_control(dominfo.getDomid(), shadow / 1024) + dominfo.info['shadow_memory'] = shadow_cur + + cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE), + fd, dominfo.getDomid(), + store_port, console_port, int(is_hvm), pae, apic]) + log.debug("[xc_restore]: %s", string.join(cmd)) + + handler = RestoreInputHandler() + + forkHelper(cmd, fd, handler.handler, True) + + # We don't want to pass this fd to any other children -- we + # might need to recover the disk space that backs it. + try: + flags = fcntl.fcntl(fd, fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(fd, fcntl.F_SETFD, flags) + except: + pass + + if handler.store_mfn is None: + raise XendError('Could not read store MFN') + + if not is_hvm and handler.console_mfn is None: + raise XendError('Could not read console MFN') + + # get qemu state and create a tmp file for dm restore + # Even PV guests may have QEMU stat, but its not currently + # used so only bother with HVM currently. + if is_hvm: + qemu_signature = read_exact(fd, len(QEMU_SIGNATURE), + "invalid device model signature read") + if qemu_signature != QEMU_SIGNATURE: + raise XendError("not a valid device model state: found '%s'" % + qemu_signature) + qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(), + os.O_WRONLY | os.O_CREAT | os.O_TRUNC) + while True: + buf = os.read(fd, dm_batch) + if len(buf): + write_exact(qemu_fd, buf, + "could not write dm state to tmp file") + else: + break + os.close(qemu_fd) + restore_image.setCpuid() + + + os.read(fd, 1) # Wait for source to close connection + + dominfo.completeRestore(handler.store_mfn, handler.console_mfn) + + # + # We shouldn't hold the domains_lock over a waitForDevices + # As this function sometime gets called holding this lock, + # we must release it and re-acquire it appropriately + # + from xen.xend import XendDomain + + lock = True; + try: + XendDomain.instance().domains_lock.release() + except: + lock = False; + + try: + dominfo.waitForDevices() # Wait for backends to set up + except Exception, exn: + log.exception(exn) + + if lock: + XendDomain.instance().domains_lock.acquire() + + if not paused: + dominfo.unpause() + + return dominfo + except: + dominfo.destroy() + raise + + +class RestoreInputHandler: + def __init__(self): + self.store_mfn = None + self.console_mfn = None + + + def handler(self, line, _): + m = re.match(r"^(store-mfn) (\d+)$", line) + if m: + self.store_mfn = int(m.group(2)) + else: + m = re.match(r"^(console-mfn) (\d+)$", line) + if m: + self.console_mfn = int(m.group(2)) + + +def forkHelper(cmd, fd, inputHandler, closeToChild): + child = xPopen3(cmd, True, -1, [fd, xc.handle()]) + + if closeToChild: + child.tochild.close() + + thread = threading.Thread(target = slurp, args = (child.childerr,)) + thread.start() + + try: + try: + while 1: + line = child.fromchild.readline() + if line == "": + break + else: + line = line.rstrip() + log.debug('%s', line) + inputHandler(line, child.tochild) + + except IOError, exn: + raise XendError('Error reading from child process for %s: %s' % + (cmd, exn)) + finally: + child.fromchild.close() + if not closeToChild: + child.tochild.close() + thread.join() + child.childerr.close() + status = child.wait() + + if status >> 8 == 127: + raise XendError("%s failed: popen failed" % string.join(cmd)) + elif status != 0: + raise XendError("%s failed" % string.join(cmd)) + + +def slurp(infile): + while 1: + line = infile.readline() + if line == "": + break + else: + line = line.strip() + m = re.match(r"^ERROR: (.*)", line) + if m is None: + log.info('%s', line) + else: + log.error('%s', m.group(1)) diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py new file mode 100644 index 0000000..ef16699 --- /dev/null +++ b/tools/python/xen/xend/XendClient.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2006 Anthony Liguori +#============================================================================ + +from xen.util.xmlrpcclient import ServerProxy +import os +import sys + +XML_RPC_SOCKET = "/var/run/xend/xmlrpc.sock" +XEN_API_SOCKET = "/var/run/xend/xen-api.sock" + +ERROR_INTERNAL = 1 +ERROR_GENERIC = 2 +ERROR_INVALID_DOMAIN = 3 + +uri = 'httpu:///var/run/xend/xmlrpc.sock' +if os.environ.has_key('XM_SERVER'): + uri = os.environ['XM_SERVER'] + +try: + server = ServerProxy(uri) +except ValueError, exn: + print >>sys.stderr, exn + sys.exit(1) + diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py new file mode 100644 index 0000000..b16a907 --- /dev/null +++ b/tools/python/xen/xend/XendConfig.py @@ -0,0 +1,1995 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006-2007 XenSource Ltd +#============================================================================ + +import logging +import re +import time +import types + +from xen.xend import sxp +from xen.xend import uuid +from xen.xend import XendOptions +from xen.xend import XendAPIStore +from xen.xend.XendPPCI import XendPPCI +from xen.xend.XendDPCI import XendDPCI +from xen.xend.XendPSCSI import XendPSCSI +from xen.xend.XendDSCSI import XendDSCSI +from xen.xend.XendError import VmError +from xen.xend.XendDevices import XendDevices +from xen.xend.PrettyPrint import prettyprintstring +from xen.xend.XendConstants import DOM_STATE_HALTED +from xen.xend.xenstore.xstransact import xstransact +from xen.xend.server.BlktapController import blktap_disk_types +from xen.xend.server.netif import randomMAC +from xen.util.blkif import blkdev_name_to_number, blkdev_uname_to_file +from xen.util import xsconstants +import xen.util.auxbin + +log = logging.getLogger("xend.XendConfig") +log.setLevel(logging.WARN) + + +""" +XendConfig API + + XendConfig will try to mirror as closely the Xen API VM Struct + with extra parameters for those options that are not supported. + +""" + +def reverse_dict(adict): + """Return the reverse mapping of a dictionary.""" + return dict([(v, k) for k, v in adict.items()]) + +def bool0(v): + return v != '0' and v != 'False' and bool(v) + +# Recursively copy a data struct, scrubbing out VNC passwords. +# Will scrub any dict entry with a key of 'vncpasswd' or any +# 2-element list whose first member is 'vncpasswd'. It will +# also scrub a string matching '(vncpasswd XYZ)'. Everything +# else is no-op passthrough +def scrub_password(data): + if type(data) == dict or type(data) == XendConfig: + scrubbed = {} + for key in data.keys(): + if key == "vncpasswd": + scrubbed[key] = "XXXXXXXX" + else: + scrubbed[key] = scrub_password(data[key]) + return scrubbed + elif type(data) == list: + if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd': + return ['vncpasswd', 'XXXXXXXX'] + else: + scrubbed = [] + for entry in data: + scrubbed.append(scrub_password(entry)) + return scrubbed + elif type(data) == tuple: + scrubbed = [] + for entry in data: + scrubbed.append(scrub_password(entry)) + return tuple(scrubbed) + elif type(data) == str: + return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data) + else: + return data + +# +# CPU fields: +# +# VCPUs_max -- the maximum number of vcpus that this domain may ever have. +# aka XendDomainInfo.getVCpuCount(). +# vcpus -- the legacy configuration name for above. +# max_vcpu_id -- vcpus_number - 1. This is given to us by Xen. +# +# cpus -- the list of pCPUs available to each vCPU. +# +# vcpu_avail -- a bitmap telling the guest domain whether it may use each of +# its VCPUs. This is translated to +# /cpu//availability = {online,offline} for use +# by the guest domain. +# VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This +# is changed by changing vcpu_avail, and waiting for the +# domain to respond. +# + + +# Mapping from XendConfig configuration keys to the old +# legacy configuration keys that map directly. + +XENAPI_CFG_TO_LEGACY_CFG = { + 'uuid': 'uuid', + 'VCPUs_max': 'vcpus', + 'cpus': 'cpus', + 'name_label': 'name', + 'actions_after_shutdown': 'on_poweroff', + 'actions_after_reboot': 'on_reboot', + 'actions_after_crash': 'on_crash', + 'PV_bootloader': 'bootloader', + 'PV_bootloader_args': 'bootloader_args', +} + +LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG) + +# Platform configuration keys and their types. +XENAPI_PLATFORM_CFG_TYPES = { + 'acpi': int, + 'apic': int, + 'boot': str, + 'device_model': str, + 'loader': str, + 'display' : str, + 'fda': str, + 'fdb': str, + 'keymap': str, + 'isa' : int, + 'localtime': int, + 'monitor': int, + 'nographic': int, + 'pae' : int, + 'rtc_timeoffset': int, + 'serial': str, + 'sdl': int, + 'opengl': int, + 'soundhw': str, + 'stdvga': int, + 'usb': int, + 'usbdevice': str, + 'hpet': int, + 'vnc': int, + 'vncconsole': int, + 'vncdisplay': int, + 'vnclisten': str, + 'timer_mode': int, + 'viridian': int, + 'vncpasswd': str, + 'vncunused': int, + 'xauthority': str, + 'pci': str, + 'vhpt': int, + 'guest_os_type': str, + 'hap': int, + 'xen_extended_power_mgmt': int, +} + +# Xen API console 'other_config' keys. +XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten', + 'vncpasswd', 'type', 'display', 'xauthority', + 'keymap', 'opengl'] + +# List of XendConfig configuration keys that have no direct equivalent +# in the old world. + +XENAPI_CFG_TYPES = { + 'uuid': str, + 'name_label': str, + 'name_description': str, + 'user_version': str, + 'is_a_template': bool0, + 'resident_on': str, + 'memory_static_min': int, # note these are stored in bytes, not KB! + 'memory_static_max': int, + 'memory_dynamic_min': int, + 'memory_dynamic_max': int, + 'cpus': list, + 'vcpus_params': dict, + 'VCPUs_max': int, + 'VCPUs_at_startup': int, + 'VCPUs_live': int, + 'actions_after_shutdown': str, + 'actions_after_reboot': str, + 'actions_after_crash': str, + 'PV_bootloader': str, + 'PV_kernel': str, + 'PV_ramdisk': str, + 'PV_args': str, + 'PV_bootloader_args': str, + 'HVM_boot_policy': str, + 'HVM_boot_params': dict, + 'PCI_bus': str, + 'platform': dict, + 'tools_version': dict, + 'other_config': dict, + 'target': int, + 'security_label': str, + 'pci': str, + 'cpuid' : dict, + 'cpuid_check' : dict, + 'machine_address_size': int, + 'suppress_spurious_page_faults': bool0, +} + +# List of legacy configuration keys that have no equivalent in the +# Xen API, but are still stored in XendConfig. + +LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [ + # roundtripped (dynamic, unmodified) + 'shadow_memory', + 'vcpu_avail', + 'features', + # read/write + 'on_xend_start', + 'on_xend_stop', + # read-only + 'domid', + 'start_time', + 'cpu_time', + 'online_vcpus', + # write-once + 'cpu', + 'cpus', +] + +LEGACY_CFG_TYPES = { + 'uuid': str, + 'name': str, + 'vcpus': int, + 'vcpu_avail': long, + 'memory': int, + 'shadow_memory': int, + 'maxmem': int, + 'start_time': float, + 'cpu_time': float, + 'features': str, + 'localtime': int, + 'name': str, + 'on_poweroff': str, + 'on_reboot': str, + 'on_crash': str, + 'on_xend_stop': str, + 'on_xend_start': str, + 'online_vcpus': int, + 'rtc/timeoffset': str, +} + +# Values that should be stored in xenstore's /vm/ that is used +# by Xend. Used in XendDomainInfo to restore running VM state from +# xenstore. +LEGACY_XENSTORE_VM_PARAMS = [ + 'uuid', + 'name', + 'vcpus', + 'vcpu_avail', + 'memory', + 'shadow_memory', + 'maxmem', + 'start_time', + 'name', + 'on_poweroff', + 'on_crash', + 'on_reboot', + 'on_xend_start', + 'on_xend_stop', +] + +## +## Config Choices +## + +CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart', + 'coredump-destroy', 'coredump-restart') +CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown', + 'crashed', 'dying') + +class XendConfigError(VmError): + def __str__(self): + return 'Invalid Configuration: %s' % str(self.value) + +## +## XendConfig Class (an extended dictionary) +## + +class XendConfig(dict): + """ The new Xend VM Configuration. + + Stores the configuration in xenapi compatible format but retains + import and export functions for SXP. + """ + def __init__(self, filename = None, sxp_obj = None, + xapi = None, dominfo = None): + + dict.__init__(self) + self.update(self._defaults()) + + if filename: + try: + sxp_obj = sxp.parse(open(filename,'r')) + sxp_obj = sxp_obj[0] + except IOError, e: + raise XendConfigError("Unable to read file: %s" % filename) + + if sxp_obj: + self._sxp_to_xapi(sxp_obj) + self._sxp_to_xapi_unsupported(sxp_obj) + elif xapi: + self.update_with_xenapi_config(xapi) + elif dominfo: + # output from xc.domain_getinfo + self._dominfo_to_xapi(dominfo, update_mem = True) + + log.debug('XendConfig.init: %s' % scrub_password(self)) + + # validators go here + self.validate() + + """ In time, we should enable this type checking addition. It is great + also for tracking bugs and unintended writes to XendDomainInfo.info + def __setitem__(self, key, value): + type_conv = XENAPI_CFG_TYPES.get(key) + if callable(type_conv): + try: + dict.__setitem__(self, key, type_conv(value)) + except (ValueError, TypeError): + raise XendConfigError("Wrong type for configuration value " + + "%s. Expected %s" % + (key, type_conv.__name__)) + else: + dict.__setitem__(self, key, value) + """ + + def _defaults(self): + defaults = { + 'name_label': 'Domain-Unnamed', + 'actions_after_shutdown': 'destroy', + 'actions_after_reboot': 'restart', + 'actions_after_crash': 'restart', + 'actions_after_suspend': '', + 'is_a_template': False, + 'is_control_domain': False, + 'features': '', + 'PV_bootloader': '', + 'PV_kernel': '', + 'PV_ramdisk': '', + 'PV_args': '', + 'PV_bootloader_args': '', + 'HVM_boot_policy': '', + 'HVM_boot_params': {}, + 'memory_static_min': 0, + 'memory_dynamic_min': 0, + 'shadow_memory': 0, + 'memory_static_max': 0, + 'memory_dynamic_max': 0, + 'devices': {}, + 'on_xend_start': 'ignore', + 'on_xend_stop': 'ignore', + 'cpus': [], + 'VCPUs_max': 1, + 'VCPUs_live': 1, + 'VCPUs_at_startup': 1, + 'vcpus_params': {}, + 'console_refs': [], + 'vif_refs': [], + 'vbd_refs': [], + 'vtpm_refs': [], + 'other_config': {}, + 'platform': {}, + 'target': 0, + } + + return defaults + + # + # Here we assume these values exist in the dict. + # If they don't we have a bigger problem, lets not + # try and 'fix it up' but acutually fix the cause ;-) + # + def _memory_sanity_check(self): + log.trace("_memory_sanity_check memory_static_min: %s, " + "memory_static_max: %i, " + "memory_dynamic_min: %i, " + "memory_dynamic_max: %i", + self["memory_static_min"], + self["memory_static_max"], + self["memory_dynamic_min"], + self["memory_dynamic_max"]) + + if not self["memory_static_min"] <= self["memory_static_max"]: + raise XendConfigError("memory_static_min must be less " \ + "than or equal to memory_static_max") + if not self["memory_static_min"] <= self["memory_dynamic_min"]: + raise XendConfigError("memory_static_min must be less " \ + "than or equal to memory_dynamic_min") + if not self["memory_dynamic_max"] <= self["memory_static_max"]: + raise XendConfigError("memory_dynamic_max must be less " \ + "than or equal to memory_static_max") + if not self["memory_dynamic_max"] > 0: + raise XendConfigError("memory_dynamic_max must be greater " \ + "than zero") + if not self["memory_static_max"] > 0: + raise XendConfigError("memory_static_max must be greater " \ + "than zero") + + def _actions_sanity_check(self): + for event in ['shutdown', 'reboot', 'crash']: + if self['actions_after_' + event] not in CONFIG_RESTART_MODES: + raise XendConfigError('Invalid event handling mode: ' + + event) + + def _vcpus_sanity_check(self): + if 'VCPUs_max' in self and 'vcpu_avail' not in self: + self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1 + + def _uuid_sanity_check(self): + """Make sure UUID is in proper string format with hyphens.""" + if 'uuid' not in self or not self['uuid']: + self['uuid'] = uuid.createString() + else: + self['uuid'] = uuid.toString(uuid.fromString(self['uuid'])) + + def _name_sanity_check(self): + if 'name_label' not in self: + self['name_label'] = 'Domain-' + self['uuid'] + + def _platform_sanity_check(self): + if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap(): + self['platform']['keymap'] = XendOptions.instance().get_keymap() + + if self.is_hvm() or self.has_rfb(): + if 'device_model' not in self['platform']: + self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm") + + if self.is_hvm(): + if 'timer_mode' not in self['platform']: + self['platform']['timer_mode'] = 1 + if 'viridian' not in self['platform']: + self['platform']['viridian'] = 0 + if 'rtc_timeoffset' not in self['platform']: + self['platform']['rtc_timeoffset'] = 0 + if 'hpet' not in self['platform']: + self['platform']['hpet'] = 0 + if 'loader' not in self['platform']: + # Old configs may have hvmloader set as PV_kernel param + if self.has_key('PV_kernel') and self['PV_kernel'] != '': + self['platform']['loader'] = self['PV_kernel'] + self['PV_kernel'] = '' + else: + self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader" + log.debug("Loader is %s" % str(self['platform']['loader'])) + + # Compatibility hack, can go away soon. + if 'soundhw' not in self['platform'] and \ + self['platform'].get('enable_audio'): + self['platform']['soundhw'] = 'sb16' + + def validate(self): + self._uuid_sanity_check() + self._name_sanity_check() + self._memory_sanity_check() + self._actions_sanity_check() + self._vcpus_sanity_check() + self._platform_sanity_check() + + def _dominfo_to_xapi(self, dominfo, update_mem = False): + self['domid'] = dominfo['domid'] + self['online_vcpus'] = dominfo['online_vcpus'] + self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1 + + if update_mem: + self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024 + self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024 + self['memory_static_min'] = 0 + self['memory_static_max'] = dominfo['maxmem_kb'] * 1024 + self._memory_sanity_check() + + self['cpu_time'] = dominfo['cpu_time']/1e9 + if dominfo.get('ssidref'): + ssidref = int(dominfo.get('ssidref')) + import xen.util.xsm.xsm as security + self['security_label'] = security.ssidref2security_label(ssidref) + + self['shutdown_reason'] = dominfo['shutdown_reason'] + + # parse state into Xen API states + self['running'] = dominfo['running'] + self['crashed'] = dominfo['crashed'] + self['dying'] = dominfo['dying'] + self['shutdown'] = dominfo['shutdown'] + self['paused'] = dominfo['paused'] + self['blocked'] = dominfo['blocked'] + + if 'name' in dominfo: + self['name_label'] = dominfo['name'] + + if 'handle' in dominfo: + self['uuid'] = uuid.toString(dominfo['handle']) + + def parse_cpuid(self, cfg, field): + def int2bin(n, count=32): + return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)]) + + for input, regs in cfg[field].iteritems(): + if not regs is dict: + cfg[field][input] = dict(regs) + + cpuid = {} + for input in cfg[field]: + inputs = input.split(',') + if inputs[0][0:2] == '0x': + inputs[0] = str(int(inputs[0], 16)) + if len(inputs) == 2: + if inputs[1][0:2] == '0x': + inputs[1] = str(int(inputs[1], 16)) + new_input = ','.join(inputs) + cpuid[new_input] = {} # new input + for reg in cfg[field][input]: + val = cfg[field][input][reg] + if val[0:2] == '0x': + cpuid[new_input][reg] = int2bin(int(val, 16)) + else: + cpuid[new_input][reg] = val + cfg[field] = cpuid + + def _parse_sxp(self, sxp_cfg): + """ Populate this XendConfig using the parsed SXP. + + @param sxp_cfg: Parsed SXP Configuration + @type sxp_cfg: list of lists + @rtype: dictionary + @return: A dictionary containing the parsed options of the SXP. + """ + cfg = {} + + for key, typ in XENAPI_CFG_TYPES.items(): + val = sxp.child_value(sxp_cfg, key) + if val is not None: + try: + cfg[key] = typ(val) + except (ValueError, TypeError), e: + log.warn('Unable to convert type value for key: %s' % key) + + # Convert deprecated options to current equivalents. + + restart = sxp.child_value(sxp_cfg, 'restart') + if restart: + if restart == 'onreboot': + cfg['on_poweroff'] = 'destroy' + cfg['on_reboot'] = 'restart' + cfg['on_crash'] = 'destroy' + elif restart == 'always': + for opt in ('on_poweroff', 'on_reboot', 'on_crash'): + cfg[opt] = 'restart' + elif restart == 'never': + for opt in ('on_poweroff', 'on_reboot', 'on_crash'): + cfg[opt] = 'never' + else: + log.warn('Ignoring unrecognised value for deprecated option:' + 'restart = \'%s\'', restart) + + # Handle memory, passed in as MiB + + if sxp.child_value(sxp_cfg, "memory") != None: + cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory")) + if sxp.child_value(sxp_cfg, "maxmem") != None: + cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem")) + + # Convert scheduling parameters to vcpus_params + if 'vcpus_params' not in cfg: + cfg['vcpus_params'] = {} + cfg["vcpus_params"]["weight"] = \ + int(sxp.child_value(sxp_cfg, "cpu_weight", 256)) + cfg["vcpus_params"]["cap"] = \ + int(sxp.child_value(sxp_cfg, "cpu_cap", 0)) + + # Only extract options we know about. + extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \ + XENAPI_CFG_TO_LEGACY_CFG.values() + + for key in extract_keys: + val = sxp.child_value(sxp_cfg, key) + if val != None: + try: + cfg[key] = LEGACY_CFG_TYPES[key](val) + except KeyError: + cfg[key] = val + except (TypeError, ValueError), e: + log.warn("Unable to parse key %s: %s: %s" % + (key, str(val), e)) + + if 'platform' not in cfg: + cfg['platform'] = {} + localtime = sxp.child_value(sxp_cfg, 'localtime') + if localtime is not None: + cfg['platform']['localtime'] = localtime + + # Compatibility hack -- can go soon. + for key in XENAPI_PLATFORM_CFG_TYPES.keys(): + val = sxp.child_value(sxp_cfg, "platform_" + key, None) + if val is not None: + self['platform'][key] = val + + # Compatibility hack -- can go soon. + boot_order = sxp.child_value(sxp_cfg, 'HVM_boot') + if boot_order: + cfg['HVM_boot_policy'] = 'BIOS order' + cfg['HVM_boot_params'] = { 'order' : boot_order } + + + # Parsing the device SXP's. + cfg['devices'] = {} + for dev in sxp.children(sxp_cfg, 'device'): + config = sxp.child0(dev) + dev_type = sxp.name(config) + self.device_add(dev_type, cfg_sxp = config, target = cfg) + + # Extract missing data from configuration entries + image_sxp = sxp.child_value(sxp_cfg, 'image', []) + if image_sxp: + image_vcpus = sxp.child_value(image_sxp, 'vcpus') + if image_vcpus != None: + try: + if 'VCPUs_max' not in cfg: + cfg['VCPUs_max'] = int(image_vcpus) + elif cfg['VCPUs_max'] != int(image_vcpus): + cfg['VCPUs_max'] = int(image_vcpus) + log.warn('Overriding vcpus from %d to %d using image' + 'vcpus value.', cfg['VCPUs_max']) + except ValueError, e: + raise XendConfigError('integer expeceted: %s: %s' % + image_sxp, e) + + # Deprecated cpu configuration + if 'cpu' in cfg: + if 'cpus' in cfg: + cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus']) + else: + cfg['cpus'] = str(cfg['cpu']) + + # Convert 'cpus' to list of list of ints + cpus_list = [] + if 'cpus' in cfg: + # Convert the following string to list of ints. + # The string supports a list of ranges (0-3), + # seperated by commas, and negation (^1). + # Precedence is settled by order of the string: + # "0-3,^1" -> [0,2,3] + # "0-3,^1,1" -> [0,1,2,3] + def cnv(s): + l = [] + for c in s.split(','): + if c.find('-') != -1: + (x, y) = c.split('-') + for i in range(int(x), int(y)+1): + l.append(int(i)) + else: + # remove this element from the list + if c[0] == '^': + l = [x for x in l if x != int(c[1:])] + else: + l.append(int(c)) + return l + + if type(cfg['cpus']) == list: + if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list: + # If sxp_cfg was created from config.sxp, + # the form of 'cpus' is list of list of string. + # Convert 'cpus' to list of list of ints. + # Conversion examples: + # [['1']] -> [[1]] + # [['0','2'],['1','3']] -> [[0,2],[1,3]] + try: + for c1 in cfg['cpus']: + cpus = [] + for c2 in c1: + cpus.append(int(c2)) + cpus_list.append(cpus) + except ValueError, e: + raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e)) + else: + # Conversion examples: + # ["1"] -> [[1]] + # ["0,2","1,3"] -> [[0,2],[1,3]] + # ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]] + try: + for c in cfg['cpus']: + cpus = cnv(c) + cpus_list.append(cpus) + except ValueError, e: + raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e)) + + if len(cpus_list) != cfg['vcpus']: + raise XendConfigError('vcpus and the item number of cpus are not same') + else: + # Conversion examples: + # vcpus=1: + # "1" -> [[1]] + # "0-3,^1" -> [[0,2,3]] + # vcpus=2: + # "1" -> [[1],[1]] + # "0-3,^1" -> [[0,2,3],[0,2,3]] + try: + cpus = cnv(cfg['cpus']) + for v in range(0, cfg['vcpus']): + cpus_list.append(cpus) + except ValueError, e: + raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e)) + else: + # Generation examples: + # vcpus=1: + # -> [[]] + # vcpus=2: + # -> [[],[]] + for v in range(0, cfg['vcpus']): + cpus_list.append(list()) + + cfg['cpus'] = cpus_list + + # Parse cpuid + if 'cpuid' in cfg: + self.parse_cpuid(cfg, 'cpuid') + if 'cpuid_check' in cfg: + self.parse_cpuid(cfg, 'cpuid_check') + + import xen.util.xsm.xsm as security + if security.on() == xsconstants.XS_POLICY_USE: + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'): + cfg['security'] = sxp.child_value(sxp_cfg, 'security') + elif not cfg.get('security_label'): + cfg['security'] = [['access_control', + ['policy', security.get_active_policy_name() ], + ['label', ACM_LABEL_UNLABELED ]]] + + if 'security' in cfg and not cfg.get('security_label'): + secinfo = cfg['security'] + # The xm command sends a list formatted like this: + # [['access_control', ['policy', 'xm-test'],['label', 'red']], + # ['ssidref', 196611]] + policy = "" + label = "" + for idx in range(0, len(secinfo)): + if secinfo[idx][0] == "access_control": + for aidx in range(1, len(secinfo[idx])): + if secinfo[idx][aidx][0] == "policy": + policy = secinfo[idx][aidx][1] + if secinfo[idx][aidx][0] == "label": + label = secinfo[idx][aidx][1] + cfg['security_label'] = \ + security.set_security_label(policy, label) + if not sxp.child_value(sxp_cfg, 'security_label'): + del cfg['security'] + + sec_lab = cfg['security_label'].split(":") + if len(sec_lab) != 3: + raise XendConfigError("Badly formatted security label: %s" + % cfg['security_label']) + + old_state = sxp.child_value(sxp_cfg, 'state') + if old_state: + for i in range(len(CONFIG_OLD_DOM_STATES)): + cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-') + + return cfg + + + def _sxp_to_xapi(self, sxp_cfg): + """Read in an SXP Configuration object and + populate at much of the Xen API with valid values. + """ + log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg)) + + # _parse_sxp() below will call device_add() and construct devices. + # Some devices may require VM's uuid, so setup self['uuid'] + # beforehand. + self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString()) + + cfg = self._parse_sxp(sxp_cfg) + + for key, typ in XENAPI_CFG_TYPES.items(): + val = cfg.get(key) + if val is not None: + self[key] = typ(val) + + # Convert parameters that can be directly mapped from + # the Legacy Config to Xen API Config + + for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items(): + try: + type_conv = XENAPI_CFG_TYPES.get(apikey) + if callable(type_conv): + self[apikey] = type_conv(cfg[cfgkey]) + else: + log.warn("Unconverted key: " + apikey) + self[apikey] = cfg[cfgkey] + except KeyError: + pass + + # Lets try and handle memory correctly + + MiB = 1024 * 1024 + + if "memory" in cfg: + self["memory_static_min"] = 0 + self["memory_static_max"] = int(cfg["memory"]) * MiB + self["memory_dynamic_min"] = int(cfg["memory"]) * MiB + self["memory_dynamic_max"] = int(cfg["memory"]) * MiB + + if "maxmem" in cfg: + self["memory_static_max"] = int(cfg["maxmem"]) * MiB + + self._memory_sanity_check() + + def update_with(n, o): + if not self.get(n): + self[n] = cfg.get(o, '') + + update_with('PV_bootloader', 'bootloader') + update_with('PV_bootloader_args', 'bootloader_args') + + image_sxp = sxp.child_value(sxp_cfg, 'image', []) + if image_sxp: + self.update_with_image_sxp(image_sxp) + + # Convert Legacy HVM parameters to Xen API configuration + for key in XENAPI_PLATFORM_CFG_TYPES.keys(): + if key in cfg: + self['platform'][key] = cfg[key] + + # set device references in the configuration + self['devices'] = cfg.get('devices', {}) + self['console_refs'] = cfg.get('console_refs', []) + self['vif_refs'] = cfg.get('vif_refs', []) + self['vbd_refs'] = cfg.get('vbd_refs', []) + self['vtpm_refs'] = cfg.get('vtpm_refs', []) + + # coalesce hvm vnc frame buffer with vfb config + if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0: + # add vfb device if it isn't there already + if not self.has_rfb(): + dev_config = ['vfb'] + dev_config.append(['type', 'vnc']) + # copy VNC related params from platform config to vfb dev conf + for key in ['vncpasswd', 'vncunused', 'vncdisplay', + 'vnclisten']: + if key in self['platform']: + dev_config.append([key, self['platform'][key]]) + + self.device_add('vfb', cfg_sxp = dev_config) + + + def has_rfb(self): + for console_uuid in self['console_refs']: + if self['devices'][console_uuid][1].get('protocol') == 'rfb': + return True + if self['devices'][console_uuid][0] == 'vfb': + return True + return False + + def _sxp_to_xapi_unsupported(self, sxp_cfg): + """Read in an SXP configuration object and populate + values are that not related directly supported in + the Xen API. + """ + + log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg)) + + # Parse and convert parameters used to configure + # the image (as well as HVM images) + image_sxp = sxp.child_value(sxp_cfg, 'image', []) + if image_sxp: + image_type = sxp.name(image_sxp) + if image_type != 'hvm' and image_type != 'linux': + self['platform']['image_type'] = image_type + + for key in XENAPI_PLATFORM_CFG_TYPES.keys(): + val = sxp.child_value(image_sxp, key, None) + if val is not None and val != '': + self['platform'][key] = val + + notes = sxp.children(image_sxp, 'notes') + if notes: + self['notes'] = self.notes_from_sxp(notes[0]) + + self._hvm_boot_params_from_sxp(image_sxp) + + # extract backend value + + backend = [] + for c in sxp.children(sxp_cfg, 'backend'): + backend.append(sxp.name(sxp.child0(c))) + if backend: + self['backend'] = backend + + # Parse and convert other Non Xen API parameters. + def _set_cfg_if_exists(sxp_arg): + val = sxp.child_value(sxp_cfg, sxp_arg) + if val != None: + if LEGACY_CFG_TYPES.get(sxp_arg): + self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val) + else: + self[sxp_arg] = val + + _set_cfg_if_exists('shadow_memory') + _set_cfg_if_exists('features') + _set_cfg_if_exists('on_xend_stop') + _set_cfg_if_exists('on_xend_start') + _set_cfg_if_exists('vcpu_avail') + + # Parse and store runtime configuration + _set_cfg_if_exists('start_time') + _set_cfg_if_exists('cpu_time') + _set_cfg_if_exists('shutdown_reason') + _set_cfg_if_exists('up_time') + _set_cfg_if_exists('status') # TODO, deprecated + + def _get_old_state_string(self): + """Returns the old xm state string. + @rtype: string + @return: old state string + """ + state_string = '' + for state_name in CONFIG_OLD_DOM_STATES: + on_off = self.get(state_name, 0) + if on_off: + state_string += state_name[0] + else: + state_string += '-' + + return state_string + + + def update_config(self, dominfo): + """Update configuration with the output from xc.domain_getinfo(). + + @param dominfo: Domain information via xc.domain_getinfo() + @type dominfo: dict + """ + self._dominfo_to_xapi(dominfo) + self.validate() + + def update_with_xenapi_config(self, xapi): + """Update configuration with a Xen API VM struct + + @param xapi: Xen API VM Struct + @type xapi: dict + """ + + log.debug('update_with_xenapi_config: %s' % scrub_password(xapi)) + + for key, val in xapi.items(): + type_conv = XENAPI_CFG_TYPES.get(key) + if type_conv is None: + key = key.lower() + type_conv = XENAPI_CFG_TYPES.get(key) + if callable(type_conv): + self[key] = type_conv(val) + else: + self[key] = val + + # XenAPI defines platform as a string-string map. If platform + # configuration exists, convert values to appropriate type. + if 'platform' in xapi: + for key, val in xapi['platform'].items(): + type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key) + if type_conv is None: + key = key.lower() + type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key) + if callable(type_conv): + self['platform'][key] = type_conv(val) + else: + self['platform'][key] = val + + self['vcpus_params']['weight'] = \ + int(self['vcpus_params'].get('weight', 256)) + self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0)) + + def cpuid_to_sxp(self, sxpr, field): + regs_list = [] + for input, regs in self[field].iteritems(): + reg_list = [] + for reg, val in regs.iteritems(): + reg_list.append([reg, val]) + regs_list.append([input, reg_list]) + sxpr.append([field, regs_list]) + + + def to_sxp(self, domain = None, ignore_devices = False, ignore = [], + legacy_only = True): + """ Get SXP representation of this config object. + + Incompat: removed store_mfn, console_mfn + + @keyword domain: (optional) XendDomainInfo to get extra information + from such as domid and running devices. + @type domain: XendDomainInfo + @keyword ignore: (optional) list of 'keys' that we do not want + to export. + @type ignore: list of strings + @rtype: list of list (SXP representation) + """ + sxpr = ['domain'] + + # TODO: domid/dom is the same thing but called differently + # depending if it is from xenstore or sxpr. + + if domain.getDomid() is not None: + sxpr.append(['domid', domain.getDomid()]) + + if not legacy_only: + for name, typ in XENAPI_CFG_TYPES.items(): + if name in self and self[name] not in (None, []): + if typ == dict: + s = self[name].items() + elif typ == list: + s = self[name] + else: + s = str(self[name]) + sxpr.append([name, s]) + + for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items(): + if legacy in ('cpus'): # skip this + continue + if self.has_key(xenapi) and self[xenapi] not in (None, []): + if type(self[xenapi]) == bool: + # convert booleans to ints before making an sxp item + sxpr.append([legacy, int(self[xenapi])]) + else: + sxpr.append([legacy, self[xenapi]]) + + MiB = 1024*1024 + + sxpr.append(["maxmem", int(self["memory_static_max"])/MiB]) + sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB]) + + for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG: + if legacy in ('domid', 'uuid', 'cpus'): # skip these + continue + if self.has_key(legacy) and self[legacy] not in (None, []): + sxpr.append([legacy, self[legacy]]) + + if self.has_key('security_label'): + sxpr.append(['security_label', self['security_label']]) + + sxpr.append(['image', self.image_sxpr()]) + sxpr.append(['status', domain._stateGet()]) + + if domain.getDomid() is not None: + sxpr.append(['state', self._get_old_state_string()]) + + if domain: + if domain.store_mfn: + sxpr.append(['store_mfn', domain.store_mfn]) + if domain.console_mfn: + sxpr.append(['console_mfn', domain.console_mfn]) + + + # Marshall devices (running or from configuration) + if not ignore_devices: + txn = xstransact() + try: + for cls in XendDevices.valid_devices(): + found = False + + # figure if there is a dev controller is valid and running + if domain and domain.getDomid() != None: + try: + controller = domain.getDeviceController(cls) + configs = controller.configurations(txn) + for config in configs: + if sxp.name(config) in ('vbd', 'tap'): + # The bootable flag is never written to the + # store as part of the device config. + dev_uuid = sxp.child_value(config, 'uuid') + dev_type, dev_cfg = self['devices'][dev_uuid] + is_bootable = dev_cfg.get('bootable', 0) + config.append(['bootable', int(is_bootable)]) + config.append(['VDI', dev_cfg.get('VDI', '')]) + + sxpr.append(['device', config]) + + found = True + except: + log.exception("dumping sxp from device controllers") + pass + + # if we didn't find that device, check the existing config + # for a device in the same class + if not found: + for dev_type, dev_info in self.all_devices_sxpr(): + if dev_type == cls: + sxpr.append(['device', dev_info]) + + txn.commit() + except: + txn.abort() + raise + + if 'cpuid' in self: + self.cpuid_to_sxp(sxpr, 'cpuid') + if 'cpuid_check' in self: + self.cpuid_to_sxp(sxpr, 'cpuid_check') + + log.debug(sxpr) + + return sxpr + + def _blkdev_name_to_number(self, dev): + if 'ioemu:' in dev: + _, dev = dev.split(':', 1) + try: + dev, _ = dev.split(':', 1) + except ValueError: + pass + + try: + devid = int(dev) + except ValueError: + # devid is not a number but a string containing either device + # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728) + dev2 = type(dev) is str and dev.split('/')[-1] or None + if dev2 == None: + log.debug("Could not check the device %s", dev) + return None + try: + devid = int(dev2) + except ValueError: + (xenbus, devid) = blkdev_name_to_number(dev2) + if devid == None: + log.debug("The device %s is not device name", dev2) + return None + return devid + + def device_duplicate_check(self, dev_type, dev_info, defined_config): + defined_devices_sxpr = self.all_devices_sxpr(target = defined_config) + + if dev_type == 'vbd' or dev_type == 'tap': + dev_uname = dev_info.get('uname') + blkdev_name = dev_info.get('dev') + devid = self._blkdev_name_to_number(blkdev_name) + if devid == None or dev_uname == None: + return + + for o_dev_type, o_dev_info in defined_devices_sxpr: + if o_dev_type == 'vbd' or o_dev_type == 'tap': + blkdev_file = blkdev_uname_to_file(dev_uname) + o_dev_uname = sxp.child_value(o_dev_info, 'uname') + if o_dev_uname != None: + o_blkdev_file = blkdev_uname_to_file(o_dev_uname) + if blkdev_file == o_blkdev_file: + raise XendConfigError('The file "%s" is already used' % + blkdev_file) + o_blkdev_name = sxp.child_value(o_dev_info, 'dev') + o_devid = self._blkdev_name_to_number(o_blkdev_name) + if o_devid != None and devid == o_devid: + raise XendConfigError('The device "%s" is already defined' % + blkdev_name) + + elif dev_type == 'vif': + dev_mac = dev_info.get('mac') + + for o_dev_type, o_dev_info in defined_devices_sxpr: + if dev_type == o_dev_type: + if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower(): + raise XendConfigError('The mac "%s" is already defined' % + dev_mac) + + def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None, + target = None): + """Add a device configuration in SXP format or XenAPI struct format. + + For SXP, it could be either: + + [device, [vbd, [uname ...]] + + or: + + [vbd, [uname ..]] + + @type cfg_sxp: list of lists (parsed sxp object) + @param cfg_sxp: SXP configuration object + @type cfg_xenapi: dict + @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif) + @param target: write device information to + @type target: None or a dictionary + @rtype: string + @return: Assigned UUID of the device. + """ + if target == None: + target = self + + if dev_type not in XendDevices.valid_devices(): + raise XendConfigError("XendConfig: %s not a valid device type" % + dev_type) + + if cfg_sxp == None and cfg_xenapi == None: + raise XendConfigError("XendConfig: device_add requires some " + "config.") + + #if cfg_sxp: + # log.debug("XendConfig.device_add: %s" % str(cfg_sxp)) + #if cfg_xenapi: + # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi)) + + if cfg_sxp: + if sxp.child0(cfg_sxp) == 'device': + config = sxp.child0(cfg_sxp) + else: + config = cfg_sxp + + dev_type = sxp.name(config) + dev_info = {} + + if dev_type == 'pci': + pci_devs_uuid = sxp.child_value(config, 'uuid', + uuid.createString()) + + pci_dict = self.pci_convert_sxp_to_dict(config) + pci_devs = pci_dict['devs'] + + # create XenAPI DPCI objects. + for pci_dev in pci_devs: + dpci_uuid = pci_dev.get('uuid') + ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'], + pci_dev['bus'], + pci_dev['slot'], + pci_dev['func']) + if ppci_uuid is None: + continue + dpci_record = { + 'VM': self['uuid'], + 'PPCI': ppci_uuid, + 'hotplug_slot': pci_dev.get('vslot', 0) + } + XendDPCI(dpci_uuid, dpci_record) + + target['devices'][pci_devs_uuid] = (dev_type, + {'devs': pci_devs, + 'uuid': pci_devs_uuid}) + + log.debug("XendConfig: reading device: %s" % pci_devs) + + return pci_devs_uuid + + if dev_type == 'vscsi': + vscsi_devs_uuid = sxp.child_value(config, 'uuid', + uuid.createString()) + vscsi_dict = self.vscsi_convert_sxp_to_dict(config) + vscsi_devs = vscsi_dict['devs'] + + # create XenAPI DSCSI objects. + for vscsi_dev in vscsi_devs: + dscsi_uuid = vscsi_dev.get('uuid') + pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev']) + if pscsi_uuid is None: + continue + dscsi_record = { + 'VM': self['uuid'], + 'PSCSI': pscsi_uuid, + 'virtual_HCTL': vscsi_dev.get('v-dev') + } + XendDSCSI(dscsi_uuid, dscsi_record) + + target['devices'][vscsi_devs_uuid] = \ + (dev_type, {'devs': vscsi_devs, 'uuid': vscsi_devs_uuid} ) + log.debug("XendConfig: reading device: %s" % vscsi_devs) + return vscsi_devs_uuid + + for opt_val in config[1:]: + try: + opt, val = opt_val + dev_info[opt] = val + except (TypeError, ValueError): # unpack error + pass + + if dev_type == 'vbd': + dev_info['bootable'] = 0 + if dev_info.get('dev', '').startswith('ioemu:'): + dev_info['driver'] = 'ioemu' + else: + dev_info['driver'] = 'paravirtualised' + + if dev_type == 'tap': + if dev_info['uname'].split(':')[1] not in blktap_disk_types: + raise XendConfigError("tap:%s not a valid disk type" % + dev_info['uname'].split(':')[1]) + + if dev_type == 'vif': + if not dev_info.get('mac'): + dev_info['mac'] = randomMAC() + + self.device_duplicate_check(dev_type, dev_info, target) + + if dev_type == 'vif': + if dev_info.get('policy') and dev_info.get('label'): + dev_info['security_label'] = "%s:%s:%s" % \ + (xsconstants.ACM_POLICY_ID, + dev_info['policy'],dev_info['label']) + + # create uuid if it doesn't exist + dev_uuid = dev_info.get('uuid', None) + if not dev_uuid: + dev_uuid = uuid.createString() + dev_info['uuid'] = dev_uuid + + # store dev references by uuid for certain device types + target['devices'][dev_uuid] = (dev_type, dev_info) + if dev_type in ('vif', 'vbd', 'vtpm'): + param = '%s_refs' % dev_type + if param not in target: + target[param] = [] + if dev_uuid not in target[param]: + if dev_type == 'vbd': + # Compat hack -- mark first disk bootable + dev_info['bootable'] = int(not target[param]) + target[param].append(dev_uuid) + elif dev_type == 'tap': + if 'vbd_refs' not in target: + target['vbd_refs'] = [] + if dev_uuid not in target['vbd_refs']: + # Compat hack -- mark first disk bootable + dev_info['bootable'] = int(not target['vbd_refs']) + target['vbd_refs'].append(dev_uuid) + + elif dev_type == 'vfb': + # Populate other config with aux data that is associated + # with vfb + + other_config = {} + for key in XENAPI_CONSOLE_OTHER_CFG: + if key in dev_info: + other_config[key] = dev_info[key] + target['devices'][dev_uuid][1]['other_config'] = other_config + + + if 'console_refs' not in target: + target['console_refs'] = [] + + # Treat VFB devices as console devices so they are found + # through Xen API + if dev_uuid not in target['console_refs']: + target['console_refs'].append(dev_uuid) + + elif dev_type == 'console': + if 'console_refs' not in target: + target['console_refs'] = [] + if dev_uuid not in target['console_refs']: + target['console_refs'].append(dev_uuid) + + log.debug("XendConfig: reading device: %s" % scrub_password(dev_info)) + return dev_uuid + + if cfg_xenapi: + dev_info = {} + dev_uuid = '' + if dev_type == 'vif': + dev_info['mac'] = cfg_xenapi.get('MAC') + if not dev_info['mac']: + dev_info['mac'] = randomMAC() + # vifname is the name on the guest, not dom0 + # TODO: we don't have the ability to find that out or + # change it from dom0 + #if cfg_xenapi.get('device'): # don't add if blank + # dev_info['vifname'] = cfg_xenapi.get('device') + if cfg_xenapi.get('type'): + dev_info['type'] = cfg_xenapi.get('type') + if cfg_xenapi.get('name'): + dev_info['name'] = cfg_xenapi.get('name') + if cfg_xenapi.get('network'): + network = XendAPIStore.get( + cfg_xenapi.get('network'), 'network') + dev_info['bridge'] = network.get_name_label() + + if cfg_xenapi.get('security_label'): + dev_info['security_label'] = \ + cfg_xenapi.get('security_label') + + dev_uuid = cfg_xenapi.get('uuid', None) + if not dev_uuid: + dev_uuid = uuid.createString() + dev_info['uuid'] = dev_uuid + target['devices'][dev_uuid] = (dev_type, dev_info) + target['vif_refs'].append(dev_uuid) + + elif dev_type in ('vbd', 'tap'): + dev_info['type'] = cfg_xenapi.get('type', 'Disk') + if dev_info['type'] == 'CD': + old_vbd_type = 'cdrom' + else: + old_vbd_type = 'disk' + + dev_info['uname'] = cfg_xenapi.get('image', '') + dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'), + old_vbd_type) + dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0)) + dev_info['driver'] = cfg_xenapi.get('driver', '') + dev_info['VDI'] = cfg_xenapi.get('VDI', '') + + if cfg_xenapi.get('mode') == 'RW': + dev_info['mode'] = 'w' + else: + dev_info['mode'] = 'r' + + dev_uuid = cfg_xenapi.get('uuid', None) + if not dev_uuid: + dev_uuid = uuid.createString() + dev_info['uuid'] = dev_uuid + target['devices'][dev_uuid] = (dev_type, dev_info) + target['vbd_refs'].append(dev_uuid) + + elif dev_type == 'vtpm': + if cfg_xenapi.get('type'): + dev_info['type'] = cfg_xenapi.get('type') + + dev_uuid = cfg_xenapi.get('uuid', None) + if not dev_uuid: + dev_uuid = uuid.createString() + dev_info['uuid'] = dev_uuid + dev_info['other_config'] = cfg_xenapi.get('other_config', {}) + target['devices'][dev_uuid] = (dev_type, dev_info) + target['vtpm_refs'].append(dev_uuid) + + elif dev_type == 'console': + dev_uuid = cfg_xenapi.get('uuid', None) + if not dev_uuid: + dev_uuid = uuid.createString() + dev_info['uuid'] = dev_uuid + dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb') + console_other_config = cfg_xenapi.get('other_config', {}) + dev_info['other_config'] = console_other_config + if dev_info['protocol'] == 'rfb': + # collapse other config into devinfo for things + # such as vncpasswd, vncunused, etc. + dev_info.update(console_other_config) + dev_info['type'] = console_other_config.get('type', 'vnc') + target['devices'][dev_uuid] = ('vfb', dev_info) + target['console_refs'].append(dev_uuid) + + # if console is rfb, set device_model ensuring qemu + # is invoked for pvfb services + if 'device_model' not in target['platform']: + target['platform']['device_model'] = \ + xen.util.auxbin.pathTo("qemu-dm") + + # Finally, if we are a pvfb, we need to make a vkbd + # as well that is not really exposed to Xen API + vkbd_uuid = uuid.createString() + target['devices'][vkbd_uuid] = ('vkbd', {}) + + elif dev_info['protocol'] == 'vt100': + # if someone tries to create a VT100 console + # via the Xen API, we'll have to ignore it + # because we create one automatically in + # XendDomainInfo._update_consoles + raise XendConfigError('Creating vt100 consoles via ' + 'Xen API is unsupported') + + return dev_uuid + + # no valid device to add + return '' + + def phantom_device_add(self, dev_type, cfg_xenapi = None, + target = None): + """Add a phantom tap device configuration in XenAPI struct format. + """ + + if target == None: + target = self + + if dev_type not in XendDevices.valid_devices() and \ + dev_type not in XendDevices.pseudo_devices(): + raise XendConfigError("XendConfig: %s not a valid device type" % + dev_type) + + if cfg_xenapi == None: + raise XendConfigError("XendConfig: device_add requires some " + "config.") + + if cfg_xenapi: + log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi)) + + if cfg_xenapi: + dev_info = {} + if dev_type in ('vbd', 'tap'): + if dev_type == 'vbd': + dev_info['uname'] = cfg_xenapi.get('image', '') + dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device') + elif dev_type == 'tap': + if cfg_xenapi.get('image').find('tap:') == -1: + dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image') + dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device') + dev_info['uname'] = cfg_xenapi.get('image') + dev_info['mode'] = cfg_xenapi.get('mode') + dev_info['backend'] = '0' + dev_uuid = cfg_xenapi.get('uuid', uuid.createString()) + dev_info['uuid'] = dev_uuid + self['devices'][dev_uuid] = (dev_type, dev_info) + self['vbd_refs'].append(dev_uuid) + return dev_uuid + + return '' + + def pci_convert_sxp_to_dict(self, dev_sxp): + """Convert pci device sxp to dict + @param dev_sxp: device configuration + @type dev_sxp: SXP object (parsed config) + @return: dev_config + @rtype: dictionary + """ + # Parsing the device SXP's. In most cases, the SXP looks + # like this: + # + # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]] + # + # However, for PCI devices it looks like this: + # + # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]] + # + # It seems the reasoning for this difference is because + # pciif.py needs all the PCI device configurations at + # the same time when creating the devices. + # + # To further complicate matters, Xen 2.0 configuration format + # uses the following for pci device configuration: + # + # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]] + + # For PCI device hotplug support, the SXP of PCI devices is + # extendend like this: + # + # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2], + # [vslt, 0]], + # [state, 'Initialising']]] + # + # 'vslt' shows the virtual hotplug slot number which the PCI device + # is inserted in. This is only effective for HVM domains. + # + # state 'Initialising' indicates that the device is being attached, + # while state 'Closing' indicates that the device is being detached. + # + # The Dict looks like this: + # + # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslt: 0}], + # states: ['Initialising'] } + + dev_config = {} + + pci_devs = [] + for pci_dev in sxp.children(dev_sxp, 'dev'): + pci_dev_info = {} + for opt_val in pci_dev[1:]: + try: + opt, val = opt_val + pci_dev_info[opt] = val + except TypeError: + pass + # append uuid for each pci device. + dpci_uuid = pci_dev_info.get('uuid', uuid.createString()) + pci_dev_info['uuid'] = dpci_uuid + pci_devs.append(pci_dev_info) + dev_config['devs'] = pci_devs + + pci_states = [] + for pci_state in sxp.children(dev_sxp, 'state'): + try: + pci_states.append(pci_state[1]) + except IndexError: + raise XendError("Error reading state while parsing pci sxp") + dev_config['states'] = pci_states + + return dev_config + + def vscsi_convert_sxp_to_dict(self, dev_sxp): + """Convert vscsi device sxp to dict + @param dev_sxp: device configuration + @type dev_sxp: SXP object (parsed config) + @return: dev_config + @rtype: dictionary + """ + # Parsing the device SXP's. In most cases, the SXP looks + # like this: + # + # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]] + # + # However, for SCSI devices it looks like this: + # + # [device, + # [vscsi, + # [dev, + # [devid, 0], [p-devname, sdb], [p-dev, 1:0:0:1], + # [v-dev, 0:0:0:0], [state, Initialising] + # ], + # [dev, + # [devid, 0], [p-devname, sdc], [p-dev, 1:0:0:2], + # [v-dev, 0:0:0:1], [satet, Initialising] + # ] + # ], + # [vscsi, + # [dev, + # [devid, 1], [p-devname, sdg], [p-dev, 2:0:0:0], + # [v-dev, 1:0:0:0], [state, Initialising] + # ], + # [dev, + # [devid, 1], [p-devname, sdh], [p-dev, 2:0:0:1], + # [v-dev, 1:0:0:1], [satet, Initialising] + # ] + # ] + # ] + # + # It seems the reasoning for this difference is because + # vscsiif.py needs all the SCSI device configurations with + # same host number at the same time when creating the devices. + + # For SCSI device hotplug support, the SXP of SCSI devices is + # extendend like this: + # + # [device, + # [vscsi, + # [dev, + # [devid, 0], [p-devname, sdd], [p-dev, 1:0:0:3], + # [v-dev, 0:0:0:2], [state, Initialising] + # ] + # ] + # ] + # + # state 'Initialising' indicates that the device is being attached, + # while state 'Closing' indicates that the device is being detached. + # + # The Dict looks like this: + # + # { devs: [ {devid: 0, p-devname: sdd, p-dev: 1:0:0:3, + # v-dev: 0:0:0:2, state: Initialising} ] } + + dev_config = {} + + vscsi_devs = [] + for vscsi_dev in sxp.children(dev_sxp, 'dev'): + vscsi_dev_info = {} + for opt_val in vscsi_dev[1:]: + try: + opt, val = opt_val + vscsi_dev_info[opt] = val + except TypeError: + pass + # append uuid for each vscsi device. + vscsi_uuid = vscsi_dev_info.get('uuid', uuid.createString()) + vscsi_dev_info['uuid'] = vscsi_uuid + vscsi_devs.append(vscsi_dev_info) + dev_config['devs'] = vscsi_devs + + return dev_config + + def console_add(self, protocol, location, other_config = {}): + dev_uuid = uuid.createString() + if protocol == 'vt100': + dev_info = { + 'uuid': dev_uuid, + 'protocol': protocol, + 'location': location, + 'other_config': other_config, + } + + if 'devices' not in self: + self['devices'] = {} + + self['devices'][dev_uuid] = ('console', dev_info) + self['console_refs'].append(dev_uuid) + return dev_info + + return {} + + def console_update(self, console_uuid, key, value): + for dev_uuid, (dev_type, dev_info) in self['devices'].items(): + if dev_uuid == console_uuid: + dev_info[key] = value + # collapse other_config into dev_info for things + # such as vncpasswd, vncunused, etc. + if key == 'other_config': + for k in XENAPI_CONSOLE_OTHER_CFG: + if k in dev_info and k not in value: + del dev_info[k] + dev_info.update(value) + break + + def console_get_all(self, protocol): + if protocol == 'vt100': + consoles = [dinfo for dtype, dinfo in self['devices'].values() + if dtype == 'console'] + return [c for c in consoles if c.get('protocol') == protocol] + + elif protocol == 'rfb': + vfbs = [dinfo for dtype, dinfo in self['devices'].values() + if dtype == 'vfb'] + + # move all non-console key values to other_config before + # returning console config + valid_keys = ['uuid', 'location'] + for vfb in vfbs: + other_config = {} + for key, val in vfb.items(): + if key not in valid_keys: + other_config[key] = vfb[key] + del vfb[key] + vfb['other_config'] = other_config + vfb['protocol'] = 'rfb' + + return vfbs + + else: + return [] + + def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}): + """Update an existing device with the new configuration. + + @rtype: boolean + @return: Returns True if succesfully found and updated a device conf + """ + if dev_uuid in self['devices'] and cfg_sxp: + if sxp.child0(cfg_sxp) == 'device': + config = sxp.child0(cfg_sxp) + else: + config = cfg_sxp + + dev_type, dev_info = self['devices'][dev_uuid] + + if dev_type == 'pci': # Special case for pci + pci_dict = self.pci_convert_sxp_to_dict(config) + pci_devs = pci_dict['devs'] + + # destroy existing XenAPI DPCI objects + for dpci_uuid in XendDPCI.get_by_VM(self['uuid']): + XendAPIStore.deregister(dpci_uuid, "DPCI") + + # create XenAPI DPCI objects. + for pci_dev in pci_devs: + dpci_uuid = pci_dev.get('uuid') + ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'], + pci_dev['bus'], + pci_dev['slot'], + pci_dev['func']) + if ppci_uuid is None: + continue + dpci_record = { + 'VM': self['uuid'], + 'PPCI': ppci_uuid, + 'hotplug_slot': pci_dev.get('vslot', 0) + } + XendDPCI(dpci_uuid, dpci_record) + + self['devices'][dev_uuid] = (dev_type, + {'devs': pci_devs, + 'uuid': dev_uuid}) + return True + + if dev_type == 'vscsi': # Special case for vscsi + vscsi_dict = self.vscsi_convert_sxp_to_dict(config) + vscsi_devs = vscsi_dict['devs'] + + # destroy existing XenAPI DSCSI objects + for dscsi_uuid in XendDSCSI.get_by_VM(self['uuid']): + XendAPIStore.deregister(dscsi_uuid, "DSCSI") + + # create XenAPI DSCSI objects. + for vscsi_dev in vscsi_devs: + dscsi_uuid = vscsi_dev.get('uuid') + pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev']) + if pscsi_uuid is None: + continue + dscsi_record = { + 'VM': self['uuid'], + 'PSCSI': pscsi_uuid, + 'virtual_HCTL': vscsi_dev.get('v-dev') + } + XendDSCSI(dscsi_uuid, dscsi_record) + + self['devices'][dev_uuid] = \ + (dev_type, {'devs': vscsi_devs, 'uuid': dev_uuid} ) + return True + + for opt_val in config[1:]: + try: + opt, val = opt_val + dev_info[opt] = val + except (TypeError, ValueError): + pass # no value for this config option + + self['devices'][dev_uuid] = (dev_type, dev_info) + return True + + elif dev_uuid in self['devices'] and cfg_xenapi: + dev_type, dev_info = self['devices'][dev_uuid] + for key, val in cfg_xenapi.items(): + dev_info[key] = val + self['devices'][dev_uuid] = (dev_type, dev_info) + + return False + + + def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None): + """Get Device SXPR by either giving the device UUID or (type, config). + + @rtype: list of lists + @return: device config sxpr + """ + sxpr = [] + + if target == None: + target = self + + if dev_uuid != None and dev_uuid in target['devices']: + dev_type, dev_info = target['devices'][dev_uuid] + + if dev_type == None or dev_info == None: + raise XendConfigError("Required either UUID or device type and " + "configuration dictionary.") + + sxpr.append(dev_type) + if dev_type in ('console', 'vfb'): + config = [(opt, val) for opt, val in dev_info.items() + if opt != 'other_config'] + else: + config = [(opt, val) for opt, val in dev_info.items()] + + sxpr += config + + return sxpr + + def ordered_device_refs(self, target = None): + result = [] + + if target == None: + target = self + + # vkbd devices *must* be before vfb devices, otherwise + # there is a race condition when setting up devices + # where the daemon spawned for the vfb may write stuff + # into xenstore vkbd backend, before DevController has + # setup permissions on the vkbd backend path. This race + # results in domain creation failing with 'device already + # connected' messages + result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd']) + + result.extend(target.get('console_refs', []) + + target.get('vbd_refs', []) + + target.get('vif_refs', []) + + target.get('vtpm_refs', [])) + + result.extend([u for u in target['devices'].keys() if u not in result]) + return result + + def all_devices_sxpr(self, target = None): + """Returns the SXPR for all devices in the current configuration.""" + sxprs = [] + pci_devs = [] + + if target == None: + target = self + + if 'devices' not in target: + return sxprs + + ordered_refs = self.ordered_device_refs(target = target) + for dev_uuid in ordered_refs: + dev_type, dev_info = target['devices'][dev_uuid] + if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices + if dev_type == 'pci': + sxpr = ['pci', ['uuid', dev_info['uuid']]] + elif dev_type == 'vscsi': + sxpr = ['vscsi', ['uuid', dev_info['uuid']]] + for pci_dev_info in dev_info['devs']: + pci_dev_sxpr = ['dev'] + for opt, val in pci_dev_info.items(): + pci_dev_sxpr.append([opt, val]) + sxpr.append(pci_dev_sxpr) + sxprs.append((dev_type, sxpr)) + else: + sxpr = self.device_sxpr(dev_type = dev_type, + dev_info = dev_info, + target = target) + sxprs.append((dev_type, sxpr)) + + return sxprs + + def image_sxpr(self): + """Returns a backwards compatible image SXP expression that is + used in xenstore's /vm//image value and xm list.""" + image = [self.image_type()] + if self.has_key('PV_kernel'): + image.append(['kernel', self['PV_kernel']]) + if self.has_key('PV_ramdisk') and self['PV_ramdisk']: + image.append(['ramdisk', self['PV_ramdisk']]) + if self.has_key('PV_args') and self['PV_args']: + image.append(['args', self['PV_args']]) + + for key in XENAPI_PLATFORM_CFG_TYPES.keys(): + if key in self['platform']: + image.append([key, self['platform'][key]]) + + if 'notes' in self: + image.append(self.notes_sxp(self['notes'])) + + return image + + def update_with_image_sxp(self, image_sxp, bootloader = False): + # Convert Legacy "image" config to Xen API PV_* + # configuration + log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp)) + + # user-specified args must come last: previous releases did this and + # some domU kernels rely upon the ordering. + kernel_args = sxp.child_value(image_sxp, 'args', '') + + # attempt to extract extra arguments from SXP config + arg_ip = sxp.child_value(image_sxp, 'ip') + if arg_ip and not re.search(r'ip=[^ ]+', kernel_args): + kernel_args = 'ip=%s ' % arg_ip + kernel_args + arg_root = sxp.child_value(image_sxp, 'root') + if arg_root and not re.search(r'root=', kernel_args): + kernel_args = 'root=%s ' % arg_root + kernel_args + + if bootloader: + self['_temp_using_bootloader'] = '1' + self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','') + self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','') + self['_temp_args'] = kernel_args + else: + self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','') + self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','') + self['PV_args'] = kernel_args + + for key in XENAPI_PLATFORM_CFG_TYPES.keys(): + val = sxp.child_value(image_sxp, key, None) + if val is not None and val != '': + self['platform'][key] = val + + notes = sxp.children(image_sxp, 'notes') + if notes: + self['notes'] = self.notes_from_sxp(notes[0]) + + self._hvm_boot_params_from_sxp(image_sxp) + + def set_notes(self, notes): + 'Add parsed elfnotes to image' + self['notes'] = notes + + def get_notes(self): + try: + return self['notes'] or {} + except KeyError: + return {} + + def notes_from_sxp(self, nsxp): + notes = {} + for note in sxp.children(nsxp): + notes[note[0]] = note[1] + return notes + + def notes_sxp(self, notes): + nsxp = ['notes'] + for k, v in notes.iteritems(): + nsxp.append([k, str(v)]) + return nsxp + + def _hvm_boot_params_from_sxp(self, image_sxp): + boot = sxp.child_value(image_sxp, 'boot', None) + if boot is not None: + self['HVM_boot_policy'] = 'BIOS order' + self['HVM_boot_params'] = { 'order' : boot } + + def is_hvm(self): + return self['HVM_boot_policy'] != '' + + def target(self): + return self['target'] + + def image_type(self): + stored_type = self['platform'].get('image_type') + return stored_type or (self.is_hvm() and 'hvm' or 'linux') + + def is_hap(self): + return self['platform'].get('hap', 0) diff --git a/tools/python/xen/xend/XendConstants.py b/tools/python/xen/xend/XendConstants.py new file mode 100644 index 0000000..13e046a --- /dev/null +++ b/tools/python/xen/xend/XendConstants.py @@ -0,0 +1,136 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ + +from xen.xend.XendAPIConstants import * + +# +# Shutdown codes and reasons. +# + +DOMAIN_POWEROFF = 0 +DOMAIN_REBOOT = 1 +DOMAIN_SUSPEND = 2 +DOMAIN_CRASH = 3 +DOMAIN_HALT = 4 + +DOMAIN_SHUTDOWN_REASONS = { + DOMAIN_POWEROFF: "poweroff", + DOMAIN_REBOOT : "reboot", + DOMAIN_SUSPEND : "suspend", + DOMAIN_CRASH : "crash", + DOMAIN_HALT : "halt" +} +REVERSE_DOMAIN_SHUTDOWN_REASONS = \ + dict([(y, x) for x, y in DOMAIN_SHUTDOWN_REASONS.items()]) + +HVM_PARAM_CALLBACK_IRQ = 0 +HVM_PARAM_STORE_PFN = 1 +HVM_PARAM_STORE_EVTCHN = 2 +HVM_PARAM_PAE_ENABLED = 4 +HVM_PARAM_IOREQ_PFN = 5 +HVM_PARAM_BUFIOREQ_PFN = 6 +HVM_PARAM_NVRAM_FD = 7 # ia64 +HVM_PARAM_VHPT_SIZE = 8 # ia64 +HVM_PARAM_BUFPIOREQ_PFN = 9 # ia64 +HVM_PARAM_VIRIDIAN = 9 # x86 +HVM_PARAM_TIMER_MODE = 10 +HVM_PARAM_HPET_ENABLED = 11 +HVM_PARAM_ACPI_S_STATE = 14 + +restart_modes = [ + "restart", + "destroy", + "preserve", + "rename-restart", + "coredump-destroy", + "coredump-restart" + ] + +DOM_STATES = [ + 'halted', + 'paused', + 'running', + 'suspended', + 'shutdown', + 'crashed', + 'unknown', +] + +DOM_STATE_HALTED = XEN_API_VM_POWER_STATE_HALTED +DOM_STATE_PAUSED = XEN_API_VM_POWER_STATE_PAUSED +DOM_STATE_RUNNING = XEN_API_VM_POWER_STATE_RUNNING +DOM_STATE_SUSPENDED = XEN_API_VM_POWER_STATE_SUSPENDED +DOM_STATE_SHUTDOWN = XEN_API_VM_POWER_STATE_SHUTTINGDOWN +DOM_STATE_CRASHED = XEN_API_VM_POWER_STATE_CRASHED +DOM_STATE_UNKNOWN = XEN_API_VM_POWER_STATE_UNKNOWN + +DOM_STATES_OLD = [ + 'running', + 'blocked', + 'paused', + 'shutdown', + 'crashed', + 'dying' + ] + +STATE_DOM_OK = 1 +STATE_DOM_SHUTDOWN = 2 + +SHUTDOWN_TIMEOUT = (60.0 * 5) + +ZOMBIE_PREFIX = 'Zombie-' + +"""Minimum time between domain restarts in seconds.""" +MINIMUM_RESTART_TIME = 20 + +RESTART_IN_PROGRESS = 'xend/restart_in_progress' +DUMPCORE_IN_PROGRESS = 'xend/dumpcore_in_progress' +LAST_SHUTDOWN_REASON = 'xend/last_shutdown_reason' + +TRIGGER_NMI = 0 +TRIGGER_RESET = 1 +TRIGGER_INIT = 2 +TRIGGER_S3RESUME = 3 + +TRIGGER_TYPE = { + "nmi" : TRIGGER_NMI, + "reset" : TRIGGER_RESET, + "init" : TRIGGER_INIT, + "s3resume": TRIGGER_S3RESUME +} + +# +# Device migration stages (eg. XendDomainInfo, XendCheckpoint, server.tpmif) +# + +DEV_MIGRATE_TEST = 0 +DEV_MIGRATE_STEP1 = 1 +DEV_MIGRATE_STEP2 = 2 +DEV_MIGRATE_STEP3 = 3 + +# +# VTPM-related constants +# + +VTPM_DELETE_SCRIPT = '/etc/xen/scripts/vtpm-delete' + +# +# Xenstore Constants +# + +XS_VMROOT = "/vm/" + diff --git a/tools/python/xen/xend/XendDPCI.py b/tools/python/xen/xend/XendDPCI.py new file mode 100644 index 0000000..c5dc920 --- /dev/null +++ b/tools/python/xen/xend/XendDPCI.py @@ -0,0 +1,154 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2008 NEC Corporation +# Yosuke Iwamatsu +#============================================================================ + +from xen.xend.XendBase import XendBase +from xen.xend.XendPPCI import XendPPCI +from xen.xend import XendAPIStore +from xen.xend import uuid as genuuid + +import XendDomain, XendNode + +from XendError import * +from XendTask import XendTask +from XendLogging import log + +class XendDPCI(XendBase): + """Representation of a passthrough PCI device.""" + + def getClass(self): + return "DPCI" + + def getAttrRO(self): + attrRO = ['virtual_domain', + 'virtual_bus', + 'virtual_slot', + 'virtual_func', + 'virtual_name', + 'VM', + 'PPCI', + 'hotplug_slot'] + return XendBase.getAttrRO() + attrRO + + def getAttrRW(self): + attrRW = [] + return XendBase.getAttrRW() + attrRW + + def getAttrInst(self): + attrInst = ['VM', + 'PPCI', + 'hotplug_slot'] + return XendBase.getAttrInst() + attrInst + + def getMethods(self): + methods = ['destroy'] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = ['create'] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def create(self, dpci_struct): + + # Check if VM is valid + xendom = XendDomain.instance() + if not xendom.is_valid_vm(dpci_struct['VM']): + raise InvalidHandleError('VM', dpci_struct['VM']) + dom = xendom.get_vm_by_uuid(dpci_struct['VM']) + + # Check if PPCI is valid + xennode = XendNode.instance() + ppci_uuid = xennode.get_ppci_by_uuid(dpci_struct['PPCI']) + if not ppci_uuid: + raise InvalidHandleError('PPCI', dpci_struct['PPCI']) + for existing_dpci in XendAPIStore.get_all('DPCI'): + if ppci_uuid == existing_dpci.get_PPCI(): + raise DirectPCIError("Device is in use") + + # Assign PPCI to VM + try: + dpci_ref = XendTask.log_progress(0, 100, dom.create_dpci, + dpci_struct) + except XendError, e: + raise DirectPCIError("Failed to assign device") + + # TODO: Retrive virtual pci device infomation. + + return dpci_ref + + create = classmethod(create) + + def get_by_VM(cls, VM_ref): + result = [] + for dpci in XendAPIStore.get_all("DPCI"): + if dpci.get_VM() == VM_ref: + result.append(dpci.get_uuid()) + return result + + get_by_VM = classmethod(get_by_VM) + + def __init__(self, uuid, record): + XendBase.__init__(self, uuid, record) + + self.virtual_domain = -1 + self.virtual_bus = -1 + self.virtual_slot = -1 + self.virtual_func = -1 + + self.VM = record['VM'] + self.PPCI = record['PPCI'] + self.hotplug_slot = record['hotplug_slot'] + + def destroy(self): + xendom = XendDomain.instance() + dom = xendom.get_vm_by_uuid(self.get_VM()) + if not dom: + raise InvalidHandleError("VM", self.get_VM()) + XendTask.log_progress(0, 100, dom.destroy_dpci, self.get_uuid()) + + def get_virtual_domain(self): + return self.virtual_domain + + def get_virtual_bus(self): + return self.virtual_bus + + def get_virtual_slot(self): + return self.virtual_slot + + def get_virtual_func(self): + return self.virtual_func + + def get_virtual_name(self): + return "%04x:%02x:%02x.%01x" % (self.virtual_domain, self.virtual_bus, + self.virtual_slot, self.virtual_func) + + def get_VM(self): + return self.VM + + def get_PPCI(self): + return self.PPCI + + def get_hotplug_slot(self): + return self.hotplug_slot + diff --git a/tools/python/xen/xend/XendDSCSI.py b/tools/python/xen/xend/XendDSCSI.py new file mode 100644 index 0000000..5926325 --- /dev/null +++ b/tools/python/xen/xend/XendDSCSI.py @@ -0,0 +1,174 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright FUJITSU LIMITED 2008 +# Masaki Kanno +#============================================================================ + +from xen.xend.XendBase import XendBase +from xen.xend.XendPSCSI import XendPSCSI +from xen.xend import XendAPIStore +from xen.xend import sxp +from xen.xend import uuid as genuuid + +import XendDomain, XendNode + +from XendError import * +from XendTask import XendTask +from XendLogging import log + +class XendDSCSI(XendBase): + """Representation of a half-virtualized SCSI device.""" + + def getClass(self): + return "DSCSI" + + def getAttrRO(self): + attrRO = ['VM', + 'PSCSI', + 'virtual_host', + 'virtual_channel', + 'virtual_target', + 'virtual_lun', + 'virtual_HCTL', + 'runtime_properties'] + return XendBase.getAttrRO() + attrRO + + def getAttrRW(self): + attrRW = [] + return XendBase.getAttrRW() + attrRW + + def getAttrInst(self): + attrInst = ['VM', + 'PSCSI', + 'virtual_HCTL'] + return XendBase.getAttrInst() + attrInst + + def getMethods(self): + methods = ['destroy'] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = ['create'] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def create(self, dscsi_struct): + + # Check if VM is valid + xendom = XendDomain.instance() + if not xendom.is_valid_vm(dscsi_struct['VM']): + raise InvalidHandleError('VM', dscsi_struct['VM']) + dom = xendom.get_vm_by_uuid(dscsi_struct['VM']) + + # Check if PSCSI is valid + xennode = XendNode.instance() + pscsi_uuid = xennode.get_pscsi_by_uuid(dscsi_struct['PSCSI']) + if not pscsi_uuid: + raise InvalidHandleError('PSCSI', dscsi_struct['PSCSI']) + + # Assign PSCSI to VM + try: + dscsi_ref = XendTask.log_progress(0, 100, \ + dom.create_dscsi, \ + dscsi_struct) + except XendError, e: + log.exception("Error in create_dscsi") + raise + + return dscsi_ref + + create = classmethod(create) + + def get_by_VM(cls, VM_ref): + result = [] + for dscsi in XendAPIStore.get_all("DSCSI"): + if dscsi.get_VM() == VM_ref: + result.append(dscsi.get_uuid()) + return result + + get_by_VM = classmethod(get_by_VM) + + def __init__(self, uuid, record): + XendBase.__init__(self, uuid, record) + v_hctl = self.virtual_HCTL.split(':') + self.virtual_host = int(v_hctl[0]) + self.virtual_channel = int(v_hctl[1]) + self.virtual_target = int(v_hctl[2]) + self.virtual_lun = int(v_hctl[3]) + + def get_VM(self): + return self.VM + + def get_PSCSI(self): + return self.PSCSI + + def get_virtual_host(self): + return self.virtual_host + + def get_virtual_channel(self): + return self.virtual_channel + + def get_virtual_target(self): + return self.virtual_target + + def get_virtual_lun(self): + return self.virtual_lun + + def get_virtual_HCTL(self): + return self.virtual_HCTL + + def get_runtime_properties(self): + xendom = XendDomain.instance() + dominfo = xendom.get_vm_by_uuid(self.VM) + + try: + device_dict = {} + for device_sxp in dominfo.getDeviceSxprs('vscsi'): + target_dev = None + for dev in device_sxp[1][0][1]: + vdev = sxp.child_value(dev, 'v-dev') + if vdev == self.virtual_HCTL: + target_dev = dev + break + if target_dev is None: + continue + + dev_dict = {} + for info in target_dev[1:]: + dev_dict[info[0]] = info[1] + device_dict['dev'] = dev_dict + for info in device_sxp[1][1:]: + device_dict[info[0]] = info[1] + + return device_dict + except Exception, exn: + log.exception(exn) + return {} + + def destroy(self): + xendom = XendDomain.instance() + dom = xendom.get_vm_by_uuid(self.get_VM()) + if not dom: + raise InvalidHandleError("VM", self.get_VM()) + XendTask.log_progress(0, 100, \ + dom.destroy_dscsi, \ + self.get_uuid()) + diff --git a/tools/python/xen/xend/XendDevices.py b/tools/python/xen/xend/XendDevices.py new file mode 100644 index 0000000..4463842 --- /dev/null +++ b/tools/python/xen/xend/XendDevices.py @@ -0,0 +1,84 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd +#============================================================================ + +# +# A collection of DevControllers +# + +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, vfbif, vscsiif +from xen.xend.server.BlktapController import BlktapController +from xen.xend.server.ConsoleController import ConsoleController + + +class XendDevices: + """ An ugly halfway point between the module local device name + to class map we used to have in XendDomainInfo and something + slightly more managable. + + This class should contain all the functions that have to do + with managing devices in Xend. Right now it is only a factory + function. + """ + + controllers = { + 'vbd': blkif.BlkifController, + 'vif': netif.NetifController, + 'vtpm': tpmif.TPMifController, + 'pci': pciif.PciController, + 'ioports': iopif.IOPortsController, + 'irq': irqif.IRQController, + 'tap': BlktapController, + 'vfb': vfbif.VfbifController, + 'vkbd': vfbif.VkbdifController, + 'console': ConsoleController, + 'vscsi': vscsiif.VSCSIController, + } + + #@classmethod + def valid_devices(cls): + return cls.controllers.keys() + valid_devices = classmethod(valid_devices) + + #@classmethod + def make_controller(cls, name, domain): + """Factory function to make device controllers per domain. + + @param name: device class name in L{VALID_DEVICES} + @type name: String + @param domain: domain this controller is handling devices for. + @type domain: XendDomainInfo + @return: DevController of class 'name' or None + @rtype: subclass of DevController + """ + if name in cls.controllers.keys(): + cls.controllers[name].deviceClass = name + return cls.controllers[name](domain) + return None + + make_controller = classmethod(make_controller) + + def destroy_device_state(cls, domain): + """Destroy the state of (external) devices. This is necessary + to do when a VM's configuration is destroyed. + + @param domain: domain this controller is handling devices for. + @type domain: XendDomainInfo + """ + from xen.xend.XendLogging import log + tpmif.destroy_vtpmstate(domain.info.get('vtpm_refs')) + + destroy_device_state = classmethod(destroy_device_state) diff --git a/tools/python/xen/xend/XendDmesg.py b/tools/python/xen/xend/XendDmesg.py new file mode 100644 index 0000000..f4d0ff6 --- /dev/null +++ b/tools/python/xen/xend/XendDmesg.py @@ -0,0 +1,41 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2007 XenSource Inc. +#============================================================================ + +"""Get dmesg output for this node. +""" + +import xen.lowlevel.xc + +class XendDmesg: + def __init__(self): + self.xc = xen.lowlevel.xc.xc() + + def info(self): + return self.xc.readconsolering() + + def clear(self): + return self.xc.readconsolering(True) + +def instance(): + global inst + try: + inst + except: + inst = XendDmesg() + return inst + diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py new file mode 100644 index 0000000..9faebe9 --- /dev/null +++ b/tools/python/xen/xend/XendDomain.py @@ -0,0 +1,1717 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 Christian Limpach +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +"""Handler for domain operations. + Nothing here is persistent (across reboots). + Needs to be persistent for one uptime. +""" + +import os +import stat +import shutil +import socket +import tempfile +import threading + +import xen.lowlevel.xc + + +from xen.xend import XendOptions, XendCheckpoint, XendDomainInfo +from xen.xend.PrettyPrint import prettyprint +from xen.xend import XendConfig, image +from xen.xend.XendError import XendError, XendInvalidDomain, VmError +from xen.xend.XendError import VMBadState +from xen.xend.XendLogging import log +from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE +from xen.xend.XendConstants import XS_VMROOT +from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_PAUSED +from xen.xend.XendConstants import DOM_STATE_RUNNING, DOM_STATE_SUSPENDED +from xen.xend.XendConstants import DOM_STATE_SHUTDOWN, DOM_STATE_UNKNOWN +from xen.xend.XendConstants import DOM_STATE_CRASHED, HVM_PARAM_ACPI_S_STATE +from xen.xend.XendConstants import TRIGGER_TYPE, TRIGGER_S3RESUME +from xen.xend.XendDevices import XendDevices +from xen.xend.XendAPIConstants import * + +from xen.xend.xenstore.xstransact import xstransact +from xen.xend.xenstore.xswatch import xswatch +from xen.util import mkdir +from xen.xend import uuid + +xc = xen.lowlevel.xc.xc() +xoptions = XendOptions.instance() + +__all__ = [ "XendDomain" ] + +CACHED_CONFIG_FILE = 'config.sxp' +CHECK_POINT_FILE = 'checkpoint.chk' +DOM0_UUID = "00000000-0000-0000-0000-000000000000" +DOM0_NAME = "Domain-0" +DOM0_ID = 0 + +POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x]) + for x in [DOM_STATE_HALTED, + DOM_STATE_PAUSED, + DOM_STATE_RUNNING, + DOM_STATE_SUSPENDED, + DOM_STATE_SHUTDOWN, + DOM_STATE_CRASHED, + DOM_STATE_UNKNOWN]]) +POWER_STATE_ALL = 'all' + + +class XendDomain: + """Index of all domains. Singleton. + + @ivar domains: map of domains indexed by domid + @type domains: dict of XendDomainInfo + @ivar managed_domains: domains that are not running and managed by Xend + @type managed_domains: dict of XendDomainInfo indexed by uuid + @ivar domains_lock: lock that must be held when manipulating self.domains + @type domains_lock: threaading.RLock + @ivar _allow_new_domains: Flag to set that allows creating of new domains. + @type _allow_new_domains: boolean + """ + + def __init__(self): + self.domains = {} + self.managed_domains = {} + self.domains_lock = threading.RLock() + + # xen api instance vars + # TODO: nothing uses this at the moment + self._allow_new_domains = True + + # This must be called only the once, by instance() below. It is separate + # from the constructor because XendDomainInfo calls back into this class + # in order to check the uniqueness of domain names. This means that + # instance() must be able to return a valid instance of this class even + # during this initialisation. + def init(self): + """Singleton initialisation function.""" + + dom_path = self._managed_path() + mkdir.parents(dom_path, stat.S_IRWXU) + + xstransact.Mkdir(XS_VMROOT) + xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID}) + + self.domains_lock.acquire() + try: + try: + dom0info = [d for d in self._running_domains() \ + if d.get('domid') == DOM0_ID][0] + + dom0info['name'] = DOM0_NAME + dom0 = XendDomainInfo.recreate(dom0info, True) + except IndexError: + raise XendError('Unable to find Domain 0') + + self._setDom0CPUCount() + + # This watch registration needs to be before the refresh call, so + # that we're sure that we haven't missed any releases, but inside + # the domains_lock, as we don't want the watch to fire until after + # the refresh call has completed. + xswatch("@introduceDomain", self._on_domains_changed) + xswatch("@releaseDomain", self._on_domains_changed) + + self._init_domains() + finally: + self.domains_lock.release() + + + def _on_domains_changed(self, _): + """ Callback method when xenstore changes. + + Calls refresh which will keep the local cache of domains + in sync. + + @rtype: int + @return: 1 + """ + self.domains_lock.acquire() + try: + self._refresh() + finally: + self.domains_lock.release() + return 1 + + def _init_domains(self): + """Does the initial scan of managed and active domains to + populate self.domains. + + Note: L{XendDomainInfo._checkName} will call back into XendDomain + to make sure domain name is not a duplicate. + + """ + self.domains_lock.acquire() + try: + running = self._running_domains() + managed = self._managed_domains() + + # add all active domains + for dom in running: + if dom['dying'] == 1: + log.warn('Ignoring dying domain %d from now on' % + dom['domid']) + continue + + if dom['domid'] != DOM0_ID: + try: + new_dom = XendDomainInfo.recreate(dom, False) + except Exception: + log.exception("Failed to create reference to running " + "domain id: %d" % dom['domid']) + + image.cleanup_stale_sentinel_fifos() + + # add all managed domains as dormant domains. + for dom in managed: + dom_uuid = dom.get('uuid') + if not dom_uuid: + continue + + dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid) + try: + running_dom = self.domain_lookup_nr(dom_name) + if not running_dom: + # instantiate domain if not started. + new_dom = XendDomainInfo.createDormant(dom) + self._managed_domain_register(new_dom) + else: + self._managed_domain_register(running_dom) + for key in XendConfig.XENAPI_CFG_TYPES.keys(): + if key not in XendConfig.LEGACY_XENSTORE_VM_PARAMS and \ + key in dom: + running_dom.info[key] = dom[key] + except Exception: + log.exception("Failed to create reference to managed " + "domain: %s" % dom_name) + + finally: + self.domains_lock.release() + + + # ----------------------------------------------------------------- + # Getting managed domains storage path names + + def _managed_path(self, domuuid = None): + """Returns the path of the directory where managed domain + information is stored. + + @keyword domuuid: If not None, will return the path to the domain + otherwise, will return the path containing + the directories which represent each domain. + @type: None or String. + @rtype: String + @return: Path. + """ + dom_path = xoptions.get_xend_domains_path() + if domuuid: + dom_path = os.path.join(dom_path, domuuid) + return dom_path + + def _managed_config_path(self, domuuid): + """Returns the path to the configuration file of a managed domain. + + @param domname: Domain uuid + @type domname: String + @rtype: String + @return: path to config file. + """ + return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE) + + def _managed_check_point_path(self, domuuid): + """Returns absolute path to check point file for managed domain. + + @param domuuid: Name of managed domain + @type domname: String + @rtype: String + @return: Path + """ + return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE) + + def _managed_config_remove(self, domuuid): + """Removes a domain configuration from managed list + + @param domuuid: Name of managed domain + @type domname: String + @raise XendError: fails to remove the domain. + """ + config_path = self._managed_path(domuuid) + try: + if os.path.exists(config_path) and os.path.isdir(config_path): + shutil.rmtree(config_path) + except IOError: + log.exception('managed_config_remove failed removing conf') + raise XendError("Unable to remove managed configuration" + " for domain: %s" % domuuid) + + def managed_config_save(self, dominfo): + """Save a domain's configuration to disk + + @param domninfo: Managed domain to save. + @type dominfo: XendDomainInfo + @raise XendError: fails to save configuration. + @rtype: None + """ + if not self.is_domain_managed(dominfo): + return # refuse to save configuration this domain isn't managed + + if dominfo: + domains_dir = self._managed_path() + dom_uuid = dominfo.get_uuid() + domain_config_dir = self._managed_path(dom_uuid) + + def make_or_raise(path): + try: + mkdir.parents(path, stat.S_IRWXU) + except: + log.exception("%s could not be created." % path) + raise XendError("%s could not be created." % path) + + make_or_raise(domains_dir) + make_or_raise(domain_config_dir) + + try: + fd, fn = tempfile.mkstemp() + f = os.fdopen(fd, 'w+b') + try: + prettyprint(dominfo.sxpr(legacy_only = False), f, + width = 78) + finally: + f.close() + + try: + shutil.move(fn, self._managed_config_path(dom_uuid)) + except: + log.exception("Renaming %s to %s", fn, + self._managed_config_path(dom_uuid)) + os.remove(fn) + except: + log.exception("Error occurred saving configuration file " + + "to %s" % domain_config_dir) + raise XendError("Failed to save configuration file to: %s" % + domain_config_dir) + else: + log.warn("Trying to save configuration for invalid domain") + + + def _managed_domains(self): + """ Returns list of domains that are managed. + + Expects to be protected by domains_lock. + + @rtype: list of XendConfig + @return: List of domain configurations that are managed. + """ + dom_path = self._managed_path() + dom_uuids = os.listdir(dom_path) + doms = [] + for dom_uuid in dom_uuids: + try: + cfg_file = self._managed_config_path(dom_uuid) + cfg = XendConfig.XendConfig(filename = cfg_file) + if cfg.get('uuid') != dom_uuid: + # something is wrong with the SXP + log.error("UUID mismatch in stored configuration: %s" % + cfg_file) + continue + doms.append(cfg) + except Exception: + log.exception('Unable to open or parse config.sxp: %s' % \ + cfg_file) + return doms + + def _managed_domain_unregister(self, dom): + try: + if self.is_domain_managed(dom): + self._managed_config_remove(dom.get_uuid()) + del self.managed_domains[dom.get_uuid()] + dom.destroy_xapi_instances() + except ValueError: + log.warn("Domain is not registered: %s" % dom.get_uuid()) + + def _managed_domain_register(self, dom): + self.managed_domains[dom.get_uuid()] = dom + + def is_domain_managed(self, dom = None): + return (dom.get_uuid() in self.managed_domains) + + # End of Managed Domain Access + # -------------------------------------------------------------------- + + def _running_domains(self): + """Get table of domains indexed by id from xc. + + @requires: Expects to be protected by domains_lock. + @rtype: list of dicts + @return: A list of dicts representing the running domains. + """ + try: + return xc.domain_getinfo() + except RuntimeError, e: + log.exception("Unable to get domain information.") + return {} + + def _setDom0CPUCount(self): + """Sets the number of VCPUs dom0 has. Retreived from the + Xend configuration, L{XendOptions}. + + @requires: Expects to be protected by domains_lock. + @rtype: None + """ + dom0 = self.privilegedDomain() + + # get max number of vcpus to use for dom0 from config + target = int(xoptions.get_dom0_vcpus()) + log.debug("number of vcpus to use is %d", target) + + # target == 0 means use all processors + if target > 0: + dom0.setVCpuCount(target) + + + def _refresh(self, refresh_shutdown = True): + """Refresh the domain list. Needs to be called when + either xenstore has changed or when a method requires + up to date information (like uptime, cputime stats). + + Expects to be protected by the domains_lock. + + @rtype: None + """ + + txn = xstransact() + try: + self._refreshTxn(txn, refresh_shutdown) + txn.commit() + except: + txn.abort() + raise + + def _refreshTxn(self, transaction, refresh_shutdown): + running = self._running_domains() + # Add domains that are not already tracked but running in Xen, + # and update domain state for those that are running and tracked. + for dom in running: + domid = dom['domid'] + if domid in self.domains: + self.domains[domid].update(dom, refresh_shutdown, transaction) + elif domid not in self.domains and dom['dying'] != 1: + try: + new_dom = XendDomainInfo.recreate(dom, False) + except VmError: + log.exception("Unable to recreate domain") + try: + xc.domain_pause(domid) + do_FLR(domid) + xc.domain_destroy(domid) + except: + log.exception("Hard destruction of domain failed: %d" % + domid) + + # update information for all running domains + # - like cpu_time, status, dying, etc. + # remove domains that are not running from active domain list. + # The list might have changed by now, because the update call may + # cause new domains to be added, if the domain has rebooted. We get + # the list again. + running = self._running_domains() + running_domids = [d['domid'] for d in running if d['dying'] != 1] + for domid, dom in self.domains.items(): + if domid not in running_domids and domid != DOM0_ID: + self._remove_domain(dom, domid) + + + def add_domain(self, info): + """Add a domain to the list of running domains + + @requires: Expects to be protected by the domains_lock. + @param info: XendDomainInfo of a domain to be added. + @type info: XendDomainInfo + """ + log.debug("Adding Domain: %s" % info.getDomid()) + self.domains[info.getDomid()] = info + + # update the managed domains with a new XendDomainInfo object + # if we are keeping track of it. + if info.get_uuid() in self.managed_domains: + self._managed_domain_register(info) + + def remove_domain(self, info, domid = None): + """Remove the domain from the list of running domains, taking the + domains_lock first. + """ + self.domains_lock.acquire() + try: + self._remove_domain(info, domid) + finally: + self.domains_lock.release() + + def _remove_domain(self, info, domid = None): + """Remove the domain from the list of running domains + + @requires: Expects to be protected by the domains_lock. + @param info: XendDomainInfo of a domain to be removed. + @type info: XendDomainInfo + """ + if info: + if domid == None: + domid = info.getDomid() + + if info._stateGet() != DOM_STATE_HALTED: + info.cleanupDomain() + + if domid in self.domains: + del self.domains[domid] + + info.destroy_xapi_instances() + else: + log.warning("Attempted to remove non-existent domain.") + + def restore_(self, config): + """Create a domain as part of the restore process. This is called + only from L{XendCheckpoint}. + + A restore request comes into XendDomain through L{domain_restore} + or L{domain_restore_fd}. That request is + forwarded immediately to XendCheckpoint which, when it is ready, will + call this method. It is necessary to come through here rather than go + directly to L{XendDomainInfo.restore} because we need to + serialise the domain creation process, but cannot lock + domain_restore_fd as a whole, otherwise we will deadlock waiting for + the old domain to die. + + @param config: Configuration of domain to restore + @type config: SXP Object (eg. list of lists) + """ + self.domains_lock.acquire() + try: + dominfo = XendDomainInfo.restore(config) + return dominfo + finally: + self.domains_lock.release() + + + def domain_lookup(self, domid): + """Look up given I{domid} in the list of managed and running + domains. + + @note: Will cause a refresh before lookup up domains, for + a version that does not need to re-read xenstore + use L{domain_lookup_nr}. + + @param domid: Domain ID or Domain Name. + @type domid: int or string + @return: Found domain. + @rtype: XendDomainInfo + @raise XendInvalidDomain: If domain is not found. + """ + self.domains_lock.acquire() + try: + self._refresh(refresh_shutdown = False) + dom = self.domain_lookup_nr(domid) + if not dom: + raise XendInvalidDomain(str(domid)) + return dom + finally: + self.domains_lock.release() + + + def domain_lookup_nr(self, domid): + """Look up given I{domid} in the list of managed and running + domains. + + @param domid: Domain ID or Domain Name. + @type domid: int or string + @return: Found domain. + @rtype: XendDomainInfo or None + """ + self.domains_lock.acquire() + try: + # lookup by name + match = [dom for dom in self.domains.values() \ + if dom.getName() == domid] + if match: + return match[0] + + match = [dom for dom in self.managed_domains.values() \ + if dom.getName() == domid] + if match: + return match[0] + + # lookup by id + try: + if int(domid) in self.domains: + return self.domains[int(domid)] + except ValueError: + pass + + # lookup by uuid for running domains + match = [dom for dom in self.domains.values() \ + if dom.get_uuid() == domid] + if match: + return match[0] + + # lookup by uuid for inactive managed domains + if domid in self.managed_domains: + return self.managed_domains[domid] + + return None + finally: + self.domains_lock.release() + + def privilegedDomain(self): + """ Get the XendDomainInfo of a dom0 + + @rtype: XendDomainInfo + """ + self.domains_lock.acquire() + try: + return self.domains[DOM0_ID] + finally: + self.domains_lock.release() + + def autostart_domains(self): + """ Autostart managed domains that are marked as such. """ + + need_starting = [] + + self.domains_lock.acquire() + try: + for dom_uuid, dom in self.managed_domains.items(): + if dom and dom._stateGet() == DOM_STATE_HALTED: + on_xend_start = dom.info.get('on_xend_start', 'ignore') + auto_power_on = dom.info.get('auto_power_on', False) + should_start = (on_xend_start == 'start') or auto_power_on + if should_start: + need_starting.append(dom_uuid) + finally: + self.domains_lock.release() + + for dom_uuid in need_starting: + self.domain_start(dom_uuid, False) + + def cleanup_domains(self): + """Clean up domains that are marked as autostop. + Should be called when Xend goes down. This is currently + called from L{xen.xend.servers.XMLRPCServer}. + + """ + log.debug('cleanup_domains') + self.domains_lock.acquire() + try: + for dom in self.domains.values(): + if dom.getName() == DOM0_NAME: + continue + + try: + if dom._stateGet() == DOM_STATE_RUNNING: + shutdownAction = dom.info.get('on_xend_stop', 'ignore') + if shutdownAction == 'shutdown': + log.debug('Shutting down domain: %s' % dom.getName()) + dom.shutdown("poweroff") + elif shutdownAction == 'suspend': + self.domain_suspend(dom.getName()) + else: + log.debug('Domain %s continues to run.' % dom.getName()) + except: + log.exception('Domain %s failed to %s.' % \ + (dom.getName(), shutdownAction)) + finally: + self.domains_lock.release() + + + + # ---------------------------------------------------------------- + # Xen API + + + def set_allow_new_domains(self, allow_new_domains): + self._allow_new_domains = allow_new_domains + + def allow_new_domains(self): + return self._allow_new_domains + + def get_domain_refs(self): + result = [] + try: + self.domains_lock.acquire() + result = [d.get_uuid() for d in self.domains.values()] + for d in self.managed_domains.keys(): + if d not in result: + result.append(d) + return result + finally: + self.domains_lock.release() + + def get_all_vms(self): + self.domains_lock.acquire() + try: + result = self.domains.values() + result += [x for x in self.managed_domains.values() if + x not in result] + return result + finally: + self.domains_lock.release() + + def get_vm_by_uuid(self, vm_uuid): + self.domains_lock.acquire() + try: + for dom in self.domains.values(): + if dom.get_uuid() == vm_uuid: + return dom + + if vm_uuid in self.managed_domains: + return self.managed_domains[vm_uuid] + + return None + finally: + self.domains_lock.release() + + def get_vm_with_dev_uuid(self, klass, dev_uuid): + self.domains_lock.acquire() + try: + for dom in self.domains.values() + self.managed_domains.values(): + if dom.has_device(klass, dev_uuid): + return dom + return None + finally: + self.domains_lock.release() + + def get_dev_property_by_uuid(self, klass, dev_uuid, field): + value = None + self.domains_lock.acquire() + + try: + try: + dom = self.get_vm_with_dev_uuid(klass, dev_uuid) + if dom: + value = dom.get_dev_property(klass, dev_uuid, field) + except ValueError, e: + pass + finally: + self.domains_lock.release() + + return value + + def set_dev_property_by_uuid(self, klass, dev_uuid, field, value, + old_val = None): + rc = True + self.domains_lock.acquire() + + try: + try: + dom = self.get_vm_with_dev_uuid(klass, dev_uuid) + if dom: + o_val = dom.get_dev_property(klass, dev_uuid, field) + log.info("o_val=%s, old_val=%s" % (o_val, old_val)) + if old_val and old_val != o_val: + return False + + dom.set_dev_property(klass, dev_uuid, field, value) + self.managed_config_save(dom) + except ValueError, e: + pass + finally: + self.domains_lock.release() + + return rc + + def is_valid_vm(self, vm_ref): + return (self.get_vm_by_uuid(vm_ref) != None) + + def is_valid_dev(self, klass, dev_uuid): + return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None) + + def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs): + dom = self.uuid_to_dom(vm_uuid) + fn(dom, *args, **kwargs) + + def uuid_to_dom(self, vm_uuid): + self.domains_lock.acquire() + try: + for domid, dom in self.domains.items(): + if dom.get_uuid() == vm_uuid: + return domid + + if vm_uuid in self.managed_domains: + domid = self.managed_domains[vm_uuid].getDomid() + if domid is None: + return self.managed_domains[vm_uuid].getName() + else: + return domid + + raise XendInvalidDomain(vm_uuid) + finally: + self.domains_lock.release() + + + def create_domain(self, xenapi_vm): + self.domains_lock.acquire() + try: + try: + xeninfo = XendConfig.XendConfig(xapi = xenapi_vm) + dominfo = XendDomainInfo.createDormant(xeninfo) + log.debug("Creating new managed domain: %s: %s" % + (dominfo.getName(), dominfo.get_uuid())) + self._managed_domain_register(dominfo) + self.managed_config_save(dominfo) + return dominfo.get_uuid() + except XendError, e: + raise + except Exception, e: + raise XendError(str(e)) + finally: + self.domains_lock.release() + + def rename_domain(self, dom, new_name): + self.domains_lock.acquire() + try: + old_name = dom.getName() + dom.setName(new_name) + + finally: + self.domains_lock.release() + + + # + # End of Xen API + # ---------------------------------------------------------------- + + # ------------------------------------------------------------ + # Xen Legacy API + + def list(self, state = DOM_STATE_RUNNING): + """Get list of domain objects. + + @param: the state in which the VMs should be -- one of the + DOM_STATE_XYZ constants, or the corresponding name, or 'all'. + @return: domains + @rtype: list of XendDomainInfo + """ + if type(state) == int: + state = POWER_STATE_NAMES[state] + state = state.lower() + + self.domains_lock.acquire() + try: + self._refresh(refresh_shutdown = False) + + # active domains + active_domains = self.domains.values() + active_uuids = [d.get_uuid() for d in active_domains] + + # inactive domains + inactive_domains = [] + for dom_uuid, dom in self.managed_domains.items(): + if dom_uuid not in active_uuids: + inactive_domains.append(dom) + + if state == POWER_STATE_ALL: + return active_domains + inactive_domains + else: + return filter(lambda x: + POWER_STATE_NAMES[x._stateGet()].lower() == state, + active_domains + inactive_domains) + finally: + self.domains_lock.release() + + + def list_sorted(self, state = DOM_STATE_RUNNING): + """Get list of domain objects, sorted by name. + + @param: the state in which the VMs should be -- one of the + DOM_STATE_XYZ constants, or the corresponding name, or 'all'. + @return: domain objects + @rtype: list of XendDomainInfo + """ + doms = self.list(state) + doms.sort(lambda x, y: cmp(x.getName(), y.getName())) + return doms + + def list_names(self, state = DOM_STATE_RUNNING): + """Get list of domain names. + + @param: the state in which the VMs should be -- one of the + DOM_STATE_XYZ constants, or the corresponding name, or 'all'. + @return: domain names + @rtype: list of strings. + """ + return [d.getName() for d in self.list_sorted(state)] + + def domain_suspend(self, domname): + """Suspends a domain that is persistently managed by Xend + + @param domname: Domain Name + @type domname: string + @rtype: None + @raise XendError: Failure during checkpointing. + """ + + try: + dominfo = self.domain_lookup_nr(domname) + if not dominfo: + raise XendInvalidDomain(domname) + + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot suspend privileged domain %s" % domname) + + if dominfo._stateGet() != DOM_STATE_RUNNING: + raise VMBadState("Domain is not running", + POWER_STATE_NAMES[DOM_STATE_RUNNING], + POWER_STATE_NAMES[dominfo._stateGet()]) + + dom_uuid = dominfo.get_uuid() + + if not os.path.exists(self._managed_config_path(dom_uuid)): + raise XendError("Domain is not managed by Xend lifecycle " + + "support.") + + path = self._managed_check_point_path(dom_uuid) + oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC + if hasattr(os, "O_LARGEFILE"): + oflags |= os.O_LARGEFILE + fd = os.open(path, oflags) + try: + # For now we don't support 'live checkpoint' + XendCheckpoint.save(fd, dominfo, False, False, path) + finally: + os.close(fd) + except OSError, ex: + raise XendError("can't write guest state file %s: %s" % + (path, ex[1])) + + def domain_resume(self, domname, start_paused = False): + """Resumes a domain that is persistently managed by Xend. + + @param domname: Domain Name + @type domname: string + @rtype: None + @raise XendError: If failed to restore. + """ + self.domains_lock.acquire() + try: + try: + fd = None + dominfo = self.domain_lookup_nr(domname) + + if not dominfo: + raise XendInvalidDomain(domname) + + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot resume privileged domain %s" % domname) + + if dominfo._stateGet() != XEN_API_VM_POWER_STATE_SUSPENDED: + raise XendError("Cannot resume domain that is not suspended.") + + dominfo.setResume(True) + + dom_uuid = dominfo.get_uuid() + chkpath = self._managed_check_point_path(dom_uuid) + if not os.path.exists(chkpath): + raise XendError("Domain was not suspended by Xend") + + # Restore that replaces the existing XendDomainInfo + try: + log.debug('Current DomainInfo state: %d' % dominfo._stateGet()) + oflags = os.O_RDONLY + if hasattr(os, "O_LARGEFILE"): + oflags |= os.O_LARGEFILE + fd = os.open(chkpath, oflags) + XendCheckpoint.restore(self, + fd, + dominfo, + paused = start_paused) + os.unlink(chkpath) + except OSError, ex: + raise XendError("Failed to read stored checkpoint file") + except IOError, ex: + raise XendError("Failed to delete checkpoint file") + except Exception, ex: + log.exception("Exception occurred when resuming") + raise XendError("Error occurred when resuming: %s" % str(ex)) + finally: + if fd is not None: + os.close(fd) + self.domains_lock.release() + + + def domain_create(self, config): + """Create a domain from a configuration. + + @param config: configuration + @type config: SXP Object (list of lists) + @rtype: XendDomainInfo + """ + self.domains_lock.acquire() + try: + self._refresh() + + dominfo = XendDomainInfo.create(config) + return dominfo + finally: + self.domains_lock.release() + + + def domain_create_from_dict(self, config_dict): + """Create a domain from a configuration dictionary. + + @param config_dict: configuration + @rtype: XendDomainInfo + """ + self.domains_lock.acquire() + try: + self._refresh() + + dominfo = XendDomainInfo.create_from_dict(config_dict) + return dominfo + finally: + self.domains_lock.release() + + + def domain_new(self, config): + """Create a domain from a configuration but do not start it. + + @param config: configuration + @type config: SXP Object (list of lists) + @rtype: XendDomainInfo + """ + self.domains_lock.acquire() + try: + try: + domconfig = XendConfig.XendConfig(sxp_obj = config) + dominfo = XendDomainInfo.createDormant(domconfig) + log.debug("Creating new managed domain: %s" % + dominfo.getName()) + self._managed_domain_register(dominfo) + self.managed_config_save(dominfo) + # no return value because it isn't meaningful for client + except XendError, e: + raise + except Exception, e: + raise XendError(str(e)) + finally: + self.domains_lock.release() + + def domain_start(self, domid, start_paused = True): + """Start a managed domain + + @require: Domain must not be running. + @param domid: Domain name or domain ID. + @type domid: string or int + @rtype: None + @raise XendError: If domain is still running + @rtype: None + """ + self.domains_lock.acquire() + try: + self._refresh() + + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo._stateGet() != DOM_STATE_HALTED: + raise VMBadState("Domain is already running", + POWER_STATE_NAMES[DOM_STATE_HALTED], + POWER_STATE_NAMES[dominfo._stateGet()]) + + dominfo.start(is_managed = True) + finally: + self.domains_lock.release() + + try: + dominfo.waitForDevices() + except Exception, ex: + log.warn("Failed to setup devices for " + str(dominfo) + ": " + str(ex)) + dominfo.destroy() + raise + + if not start_paused: + dominfo.unpause() + + def domain_delete(self, domid): + """Remove a managed domain from database + + @require: Domain must not be running. + @param domid: Domain name or domain ID. + @type domid: string or int + @rtype: None + @raise XendError: If domain is still running + """ + self.domains_lock.acquire() + try: + try: + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo._stateGet() != XEN_API_VM_POWER_STATE_HALTED: + raise VMBadState("Domain is not halted.", + POWER_STATE_NAMES[DOM_STATE_HALTED], + POWER_STATE_NAMES[dominfo._stateGet()]) + + self._domain_delete_by_info(dominfo) + except Exception, ex: + raise XendError(str(ex)) + finally: + self.domains_lock.release() + + + def domain_delete_by_dominfo(self, dominfo): + """Only for use by XendDomainInfo. + """ + self.domains_lock.acquire() + try: + self._domain_delete_by_info(dominfo) + finally: + self.domains_lock.release() + + + def _domain_delete_by_info(self, dominfo): + """Expects to be protected by domains_lock. + """ + log.info("Domain %s (%s) deleted." % + (dominfo.getName(), dominfo.info.get('uuid'))) + + self._managed_domain_unregister(dominfo) + self._remove_domain(dominfo) + XendDevices.destroy_device_state(dominfo) + + + def domain_configure(self, config): + """Configure an existing domain. + + @param vmconfig: vm configuration + @type vmconfig: SXP Object (list of lists) + @todo: Not implemented + """ + # !!! + raise XendError("Unsupported") + + def domain_restore(self, src, paused=False): + """Restore a domain from file. + + @param src: filename of checkpoint file to restore from + @type src: string + @return: Restored domain + @rtype: XendDomainInfo + @raise XendError: Failure to restore domain + """ + try: + oflags = os.O_RDONLY + if hasattr(os, "O_LARGEFILE"): + oflags |= os.O_LARGEFILE + fd = os.open(src, oflags) + try: + return self.domain_restore_fd(fd, paused=paused) + finally: + os.close(fd) + except OSError, ex: + raise XendError("can't read guest state file %s: %s" % + (src, ex[1])) + + def domain_restore_fd(self, fd, paused=False, relocating=False): + """Restore a domain from the given file descriptor. + + @param fd: file descriptor of the checkpoint file + @type fd: File object + @rtype: XendDomainInfo + @raise XendError: if failed to restore + """ + + try: + return XendCheckpoint.restore(self, fd, paused=paused, relocating=relocating) + except XendError, e: + log.exception("Restore failed") + raise + except: + # I don't really want to log this exception here, but the error + # handling in the relocation-socket handling code (relocate.py) is + # poor, so we need to log this for debugging. + log.exception("Restore failed") + raise XendError("Restore failed") + + def domain_unpause(self, domid): + """Unpause domain execution. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: None + @raise XendError: Failed to unpause + @raise XendInvalidDomain: Domain is not valid + """ + try: + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot unpause privileged domain %s" % domid) + if dominfo._stateGet() not in (DOM_STATE_PAUSED, DOM_STATE_RUNNING): + raise VMBadState("Domain '%s' is not started" % domid, + POWER_STATE_NAMES[DOM_STATE_PAUSED], + POWER_STATE_NAMES[dominfo._stateGet()]) + log.info("Domain %s (%d) unpaused.", dominfo.getName(), + int(dominfo.getDomid())) + dominfo.unpause() + except XendInvalidDomain: + log.exception("domain_unpause") + raise + except Exception, ex: + log.exception("domain_unpause") + raise XendError(str(ex)) + + def domain_pause(self, domid, state=False): + """Pause domain execution. + + @param domid: Domain ID or Name + @type domid: int or string. + @keyword state: If True, will return the domain state before pause + @type state: bool + @rtype: int if state is True + @return: Domain state (DOM_STATE_*) + @rtype: None if state is False + @raise XendError: Failed to pause + @raise XendInvalidDomain: Domain is not valid + """ + try: + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot pause privileged domain %s" % domid) + ds = dominfo._stateGet() + if ds not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED): + raise VMBadState("Domain '%s' is not started" % domid, + POWER_STATE_NAMES[DOM_STATE_RUNNING], + POWER_STATE_NAMES[ds]) + log.info("Domain %s (%d) paused.", dominfo.getName(), + int(dominfo.getDomid())) + if ds == DOM_STATE_RUNNING: + dominfo.pause() + if state: + return ds + except XendInvalidDomain: + log.exception("domain_pause") + raise + except Exception, ex: + log.exception("domain_pause") + raise XendError(str(ex)) + + def domain_dump(self, domid, filename, live, crash): + """Dump domain core.""" + + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot dump core for privileged domain %s" % domid) + if dominfo._stateGet() not in (DOM_STATE_PAUSED, DOM_STATE_RUNNING, DOM_STATE_CRASHED): + raise VMBadState("Domain '%s' is not started" % domid, + POWER_STATE_NAMES[DOM_STATE_PAUSED], + POWER_STATE_NAMES[dominfo._stateGet()]) + + try: + log.info("Domain core dump requested for domain %s (%d) " + "live=%d crash=%d.", + dominfo.getName(), dominfo.getDomid(), live, crash) + return dominfo.dumpCore(filename) + except Exception, ex: + raise XendError(str(ex)) + + def domain_destroy(self, domid): + """Terminate domain immediately. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: None + @raise XendError: Failed to destroy + @raise XendInvalidDomain: Domain is not valid + """ + + dominfo = self.domain_lookup_nr(domid) + if dominfo and dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot destroy privileged domain %s" % domid) + + if dominfo: + val = dominfo.destroy() + else: + try: + xc.domain_pause(int(domid)) + do_FLR(int(domid)) + val = xc.domain_destroy(int(domid)) + except ValueError: + raise XendInvalidDomain(domid) + except Exception, e: + raise XendError(str(e)) + + return val + + def domain_migrate(self, domid, dst, live=False, port=0, node=-1, ssl=None): + """Start domain migration. + + @param domid: Domain ID or Name + @type domid: int or string. + @param dst: Destination IP address + @type dst: string + @keyword live: Live migration + @type live: bool + @keyword port: relocation port on destination + @type port: int + @keyword node: use node number for target + @type node: int + @keyword ssl: use ssl connection + @type ssl: bool + @rtype: None + @raise XendError: Failed to migrate + @raise XendInvalidDomain: Domain is not valid + """ + + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot migrate privileged domain %s" % domid) + if dominfo._stateGet() != DOM_STATE_RUNNING: + raise VMBadState("Domain is not running", + POWER_STATE_NAMES[DOM_STATE_RUNNING], + POWER_STATE_NAMES[dominfo._stateGet()]) + + """ The following call may raise a XendError exception """ + dominfo.testMigrateDevices(True, dst) + + if live: + """ Make sure there's memory free for enabling shadow mode """ + dominfo.checkLiveMigrateMemory() + + if ssl is None: + ssl = xoptions.get_xend_relocation_ssl() + + if ssl: + from OpenSSL import SSL + from xen.web import connection + if port == 0: + port = xoptions.get_xend_relocation_ssl_port() + try: + ctx = SSL.Context(SSL.SSLv23_METHOD) + sock = SSL.Connection(ctx, + socket.socket(socket.AF_INET, socket.SOCK_STREAM)) + sock.set_connect_state() + sock.connect((dst, port)) + sock.send("sslreceive\n") + sock.recv(80) + except SSL.Error, err: + raise XendError("SSL error: %s" % err) + except socket.error, err: + raise XendError("can't connect: %s" % err) + + p2cread, p2cwrite = os.pipe() + threading.Thread(target=connection.SSLSocketServerConnection.fd2send, + args=(sock, p2cread)).start() + + try: + XendCheckpoint.save(p2cwrite, dominfo, True, live, dst, + node=node) + finally: + sock.shutdown() + sock.close() + + os.close(p2cread) + os.close(p2cwrite) + else: + if port == 0: + port = xoptions.get_xend_relocation_port() + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # When connecting to our ssl enabled relocation server using a + # plain socket, send will success but recv will block. Add a + # 30 seconds timeout to raise a socket.timeout exception to + # inform the client. + sock.settimeout(30.0) + sock.connect((dst, port)) + sock.send("receive\n") + sock.recv(80) + sock.settimeout(None) + except socket.error, err: + raise XendError("can't connect: %s" % err) + + try: + XendCheckpoint.save(sock.fileno(), dominfo, True, live, + dst, node=node) + finally: + sock.close() + + def domain_save(self, domid, dst, checkpoint=False): + """Start saving a domain to file. + + @param domid: Domain ID or Name + @type domid: int or string. + @param dst: Destination filename + @type dst: string + @rtype: None + @raise XendError: Failed to save domain + @raise XendInvalidDomain: Domain is not valid + """ + try: + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot save privileged domain %s" % str(domid)) + if dominfo._stateGet() != DOM_STATE_RUNNING: + raise VMBadState("Domain is not running", + POWER_STATE_NAMES[DOM_STATE_RUNNING], + POWER_STATE_NAMES[dominfo._stateGet()]) + + oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC + if hasattr(os, "O_LARGEFILE"): + oflags |= os.O_LARGEFILE + fd = os.open(dst, oflags) + try: + XendCheckpoint.save(fd, dominfo, False, False, dst, + checkpoint=checkpoint) + except Exception, e: + os.close(fd) + raise e + os.close(fd) + except OSError, ex: + raise XendError("can't write guest state file %s: %s" % + (dst, ex[1])) + + def domain_pincpu(self, domid, vcpu, cpumap): + """Set which cpus vcpu can use + + @param domid: Domain ID or Name + @type domid: int or string. + @param vcpu: vcpu to pin to + @type vcpu: int + @param cpumap: string repr of usable cpus + @type cpumap: string + @rtype: 0 + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + # if vcpu is keyword 'all', apply the cpumap to all vcpus + if str(vcpu).lower() == "all": + vcpus = range(0, int(dominfo.getVCpuCount())) + else: + vcpus = [ int(vcpu) ] + + # set the same cpumask for all vcpus + rc = 0 + cpus = dominfo.getCpus() + for v in vcpus: + try: + if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): + rc = xc.vcpu_setaffinity(dominfo.getDomid(), v, cpumap) + cpus[v] = cpumap + except Exception, ex: + log.exception(ex) + raise XendError("Cannot pin vcpu: %d to cpu: %s - %s" % \ + (v, cpumap, str(ex))) + dominfo.setCpus(cpus) + self.managed_config_save(dominfo) + + return rc + + def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime, + weight): + """Set Simple EDF scheduler parameters for a domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: 0 + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + try: + return xc.sedf_domain_set(dominfo.getDomid(), period, slice_, + latency, extratime, weight) + except Exception, ex: + raise XendError(str(ex)) + + def domain_cpu_sedf_get(self, domid): + """Get Simple EDF scheduler parameters for a domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: SXP object + @return: The parameters for Simple EDF schedule for a domain. + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + try: + sedf_info = xc.sedf_domain_get(dominfo.getDomid()) + # return sxpr + return ['sedf', + ['domid', sedf_info['domid']], + ['period', sedf_info['period']], + ['slice', sedf_info['slice']], + ['latency', sedf_info['latency']], + ['extratime', sedf_info['extratime']], + ['weight', sedf_info['weight']]] + + except Exception, ex: + raise XendError(str(ex)) + + def domain_shadow_control(self, domid, op): + """Shadow page control. + + @param domid: Domain ID or Name + @type domid: int or string. + @param op: operation + @type op: int + @rtype: 0 + """ + dominfo = self.domain_lookup(domid) + try: + return xc.shadow_control(dominfo.getDomid(), op) + except Exception, ex: + raise XendError(str(ex)) + + def domain_shadow_mem_get(self, domid): + """Get shadow pagetable memory allocation. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: int + @return: shadow memory in MB + """ + dominfo = self.domain_lookup(domid) + try: + return xc.shadow_mem_control(dominfo.getDomid()) + except Exception, ex: + raise XendError(str(ex)) + + def domain_shadow_mem_set(self, domid, mb): + """Set shadow pagetable memory allocation. + + @param domid: Domain ID or Name + @type domid: int or string. + @param mb: shadow memory to set in MB + @type: mb: int + @rtype: int + @return: shadow memory in MB + """ + dominfo = self.domain_lookup(domid) + try: + return xc.shadow_mem_control(dominfo.getDomid(), mb=mb) + except Exception, ex: + raise XendError(str(ex)) + + def domain_sched_credit_get(self, domid): + """Get credit scheduler parameters for a domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: dict with keys 'weight' and 'cap' + @return: credit scheduler parameters + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): + try: + return xc.sched_credit_domain_get(dominfo.getDomid()) + except Exception, ex: + raise XendError(str(ex)) + else: + return {'weight' : dominfo.getWeight(), + 'cap' : dominfo.getCap()} + + def domain_sched_credit_set(self, domid, weight = None, cap = None): + """Set credit scheduler parameters for a domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @type weight: int + @type cap: int + @rtype: 0 + """ + set_weight = False + set_cap = False + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + try: + if weight is None: + weight = int(0) + elif weight < 1 or weight > 65535: + raise XendError("weight is out of range") + else: + set_weight = True + + if cap is None: + cap = int(~0) + elif cap < 0 or cap > dominfo.getVCpuCount() * 100: + raise XendError("cap is out of range") + else: + set_cap = True + + assert type(weight) == int + assert type(cap) == int + + rc = 0 + if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): + rc = xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap) + if rc == 0: + if set_weight: + dominfo.setWeight(weight) + if set_cap: + dominfo.setCap(cap) + self.managed_config_save(dominfo) + return rc + except Exception, ex: + log.exception(ex) + raise XendError(str(ex)) + + def domain_maxmem_set(self, domid, mem): + """Set the memory limit for a domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @param mem: memory limit (in MiB) + @type mem: int + @raise XendError: fail to set memory + @rtype: 0 + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + dominfo.setMemoryMaximum(mem) + + def domain_ioport_range_enable(self, domid, first, last): + """Enable access to a range of IO ports for a domain + + @param first: first IO port + @param last: last IO port + @raise XendError: failed to set range + @rtype: 0 + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + nr_ports = last - first + 1 + try: + return xc.domain_ioport_permission(dominfo.getDomid(), + first_port = first, + nr_ports = nr_ports, + allow_access = 1) + except Exception, ex: + raise XendError(str(ex)) + + def domain_ioport_range_disable(self, domid, first, last): + """Disable access to a range of IO ports for a domain + + @param first: first IO port + @param last: last IO port + @raise XendError: failed to set range + @rtype: 0 + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + nr_ports = last - first + 1 + try: + return xc.domain_ioport_permission(dominfo.getDomid(), + first_port = first, + nr_ports = nr_ports, + allow_access = 0) + except Exception, ex: + raise XendError(str(ex)) + + def domain_send_trigger(self, domid, trigger_name, vcpu = 0): + """Send trigger to a domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @param trigger_name: trigger type name + @type trigger_name: string + @param vcpu: VCPU to send trigger (default is 0) + @type vcpu: int + @raise XendError: failed to send trigger + @raise XendInvalidDomain: Domain is not valid + @rtype: 0 + """ + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + if dominfo._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): + raise VMBadState("Domain '%s' is not started" % domid, + POWER_STATE_NAMES[DOM_STATE_RUNNING], + POWER_STATE_NAMES[dominfo._stateGet()]) + if trigger_name.lower() in TRIGGER_TYPE.keys(): + trigger = TRIGGER_TYPE[trigger_name.lower()] + else: + raise XendError("Invalid trigger: %s" % trigger_name) + if trigger == TRIGGER_S3RESUME: + xc.hvm_set_param(dominfo.getDomid(), HVM_PARAM_ACPI_S_STATE, 0) + return None + try: + return xc.domain_send_trigger(dominfo.getDomid(), + trigger, + vcpu) + except Exception, ex: + raise XendError(str(ex)) + + def domain_reset(self, domid): + """Terminate domain immediately, and then create domain. + + @param domid: Domain ID or Name + @type domid: int or string. + @rtype: None + @raise XendError: Failed to destroy or create + @raise XendInvalidDomain: Domain is not valid + """ + + dominfo = self.domain_lookup_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + if dominfo and dominfo.getDomid() == DOM0_ID: + raise XendError("Cannot reset privileged domain %s" % domid) + if dominfo._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): + raise VMBadState("Domain '%s' is not started" % domid, + POWER_STATE_NAMES[DOM_STATE_RUNNING], + POWER_STATE_NAMES[dominfo._stateGet()]) + try: + dominfo.resetDomain() + except Exception, ex: + raise XendError(str(ex)) + + +def instance(): + """Singleton constructor. Use this instead of the class constructor. + """ + global inst + try: + inst + except: + inst = XendDomain() + inst.init() + return inst diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py new file mode 100644 index 0000000..9784f47 --- /dev/null +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -0,0 +1,3620 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005-2007 XenSource Ltd +#============================================================================ + +"""Representation of a single domain. +Includes support for domain construction, using +open-ended configurations. + +Author: Mike Wray + +""" + +import logging +import time +import threading +import re +import copy +import os +import traceback +from types import StringTypes + +import xen.lowlevel.xc +from xen.util import asserts +from xen.util.blkif import blkdev_uname_to_file, blkdev_uname_to_taptype +import xen.util.xsm.xsm as security +from xen.util import xsconstants + +from xen.xend import balloon, sxp, uuid, image, arch, osdep +from xen.xend import XendOptions, XendNode, XendConfig + +from xen.xend.XendConfig import scrub_password +from xen.xend.XendBootloader import bootloader, bootloader_tidy +from xen.xend.XendError import XendError, VmError +from xen.xend.XendDevices import XendDevices +from xen.xend.XendTask import XendTask +from xen.xend.xenstore.xstransact import xstransact, complete +from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, SetTarget, ResumeDomain +from xen.xend.xenstore.xswatch import xswatch +from xen.xend.XendConstants import * +from xen.xend.XendAPIConstants import * + +from xen.xend.XendVMMetrics import XendVMMetrics + +from xen.xend import XendAPIStore +from xen.xend.XendPPCI import XendPPCI +from xen.xend.XendDPCI import XendDPCI +from xen.xend.XendPSCSI import XendPSCSI +from xen.xend.XendDSCSI import XendDSCSI + +MIGRATE_TIMEOUT = 30.0 +BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp' + +xc = xen.lowlevel.xc.xc() +xoptions = XendOptions.instance() + +log = logging.getLogger("xend.XendDomainInfo") +#log.setLevel(logging.TRACE) + + +def create(config): + """Creates and start a VM using the supplied configuration. + + @param config: A configuration object involving lists of tuples. + @type config: list of lists, eg ['vm', ['image', 'xen.gz']] + + @rtype: XendDomainInfo + @return: An up and running XendDomainInfo instance + @raise VmError: Invalid configuration or failure to start. + """ + from xen.xend import XendDomain + domconfig = XendConfig.XendConfig(sxp_obj = config) + othervm = XendDomain.instance().domain_lookup_nr(domconfig["name_label"]) + if othervm is None or othervm.domid is None: + othervm = XendDomain.instance().domain_lookup_nr(domconfig["uuid"]) + if othervm is not None and othervm.domid is not None: + raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid)) + log.debug("XendDomainInfo.create(%s)", scrub_password(config)) + vm = XendDomainInfo(domconfig) + try: + vm.start() + except: + log.exception('Domain construction failed') + vm.destroy() + raise + + return vm + +def create_from_dict(config_dict): + """Creates and start a VM using the supplied configuration. + + @param config_dict: An configuration dictionary. + + @rtype: XendDomainInfo + @return: An up and running XendDomainInfo instance + @raise VmError: Invalid configuration or failure to start. + """ + + log.debug("XendDomainInfo.create_from_dict(%s)", + scrub_password(config_dict)) + vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict)) + try: + vm.start() + except: + log.exception('Domain construction failed') + vm.destroy() + raise + return vm + +def recreate(info, priv): + """Create the VM object for an existing domain. The domain must not + be dying, as the paths in the store should already have been removed, + and asking us to recreate them causes problems. + + @param xeninfo: Parsed configuration + @type xeninfo: Dictionary + @param priv: Is a privileged domain (Dom 0) + @type priv: bool + + @rtype: XendDomainInfo + @return: A up and running XendDomainInfo instance + @raise VmError: Invalid configuration. + @raise XendError: Errors with configuration. + """ + + log.debug("XendDomainInfo.recreate(%s)", scrub_password(info)) + + assert not info['dying'] + + xeninfo = XendConfig.XendConfig(dominfo = info) + xeninfo['is_control_domain'] = priv + xeninfo['is_a_template'] = False + domid = xeninfo['domid'] + uuid1 = uuid.fromString(xeninfo['uuid']) + needs_reinitialising = False + + dompath = GetDomainPath(domid) + if not dompath: + raise XendError('No domain path in store for existing ' + 'domain %d' % domid) + + log.info("Recreating domain %d, UUID %s. at %s" % + (domid, xeninfo['uuid'], dompath)) + + # need to verify the path and uuid if not Domain-0 + # if the required uuid and vm aren't set, then that means + # we need to recreate the dom with our own values + # + # NOTE: this is probably not desirable, really we should just + # abort or ignore, but there may be cases where xenstore's + # entry disappears (eg. xenstore-rm /) + # + try: + vmpath = xstransact.Read(dompath, "vm") + if not vmpath: + if not priv: + log.warn('/local/domain/%d/vm is missing. recreate is ' + 'confused, trying our best to recover' % domid) + needs_reinitialising = True + raise XendError('reinit') + + uuid2_str = xstransact.Read(vmpath, "uuid") + if not uuid2_str: + log.warn('%s/uuid/ is missing. recreate is confused, ' + 'trying our best to recover' % vmpath) + needs_reinitialising = True + raise XendError('reinit') + + uuid2 = uuid.fromString(uuid2_str) + if uuid1 != uuid2: + log.warn('UUID in /vm does not match the UUID in /dom/%d.' + 'Trying out best to recover' % domid) + needs_reinitialising = True + except XendError: + pass # our best shot at 'goto' in python :) + + vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv, + vmpath = vmpath) + + if needs_reinitialising: + vm._recreateDom() + vm._removeVm() + vm._storeVmDetails() + vm._storeDomDetails() + + vm.image = image.create(vm, vm.info) + vm.image.recreate() + + vm._registerWatches() + vm.refreshShutdown(xeninfo) + + # register the domain in the list + from xen.xend import XendDomain + XendDomain.instance().add_domain(vm) + + return vm + + +def restore(config): + """Create a domain and a VM object to do a restore. + + @param config: Domain SXP configuration + @type config: list of lists. (see C{create}) + + @rtype: XendDomainInfo + @return: A up and running XendDomainInfo instance + @raise VmError: Invalid configuration or failure to start. + @raise XendError: Errors with configuration. + """ + + log.debug("XendDomainInfo.restore(%s)", scrub_password(config)) + vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config), + resume = True) + try: + vm.resume() + return vm + except: + vm.destroy() + raise + +def createDormant(domconfig): + """Create a dormant/inactive XenDomainInfo without creating VM. + This is for creating instances of persistent domains that are not + yet start. + + @param domconfig: Parsed configuration + @type domconfig: XendConfig object + + @rtype: XendDomainInfo + @return: A up and running XendDomainInfo instance + @raise XendError: Errors with configuration. + """ + + log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig)) + + # domid does not make sense for non-running domains. + domconfig.pop('domid', None) + vm = XendDomainInfo(domconfig) + return vm + +def domain_by_name(name): + """Get domain by name + + @params name: Name of the domain + @type name: string + @return: XendDomainInfo or None + """ + from xen.xend import XendDomain + return XendDomain.instance().domain_lookup_by_name_nr(name) + + +def shutdown_reason(code): + """Get a shutdown reason from a code. + + @param code: shutdown code + @type code: int + @return: shutdown reason + @rtype: string + """ + return DOMAIN_SHUTDOWN_REASONS.get(code, "?") + +def dom_get(dom): + """Get info from xen for an existing domain. + + @param dom: domain id + @type dom: int + @return: info or None + @rtype: dictionary + """ + try: + domlist = xc.domain_getinfo(dom, 1) + if domlist and dom == domlist[0]['domid']: + return domlist[0] + except Exception, err: + # ignore missing domain + log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err)) + return None + +def do_FLR(domid): + from xen.xend.server.pciif import parse_pci_name, PciDevice + path = '/local/domain/0/backend/pci/%u/0/' % domid + num_devs = xstransact.Read(path + 'num_devs'); + if num_devs is None or num_devs == "": + return; + + num_devs = int(xstransact.Read(path + 'num_devs')); + + dev_str_list = [] + for i in range(num_devs): + dev_str = xstransact.Read(path + 'dev-%i' % i) + dev_str_list = dev_str_list + [dev_str] + + for dev_str in dev_str_list: + (dom, b, d, f) = parse_pci_name(dev_str) + try: + dev = PciDevice(dom, b, d, f) + except Exception, e: + raise VmError("pci: failed to locate device and "+ + "parse it's resources - "+str(e)) + dev.do_FLR() + +class XendDomainInfo: + """An object represents a domain. + + @TODO: try to unify dom and domid, they mean the same thing, but + xc refers to it as dom, and everywhere else, including + xenstore it is domid. The best way is to change xc's + python interface. + + @ivar info: Parsed configuration + @type info: dictionary + @ivar domid: Domain ID (if VM has started) + @type domid: int or None + @ivar vmpath: XenStore path to this VM. + @type vmpath: string + @ivar dompath: XenStore path to this Domain. + @type dompath: string + @ivar image: Reference to the VM Image. + @type image: xen.xend.image.ImageHandler + @ivar store_port: event channel to xenstored + @type store_port: int + @ivar console_port: event channel to xenconsoled + @type console_port: int + @ivar store_mfn: xenstored mfn + @type store_mfn: int + @ivar console_mfn: xenconsoled mfn + @type console_mfn: int + @ivar notes: OS image notes + @type notes: dictionary + @ivar vmWatch: reference to a watch on the xenstored vmpath + @type vmWatch: xen.xend.xenstore.xswatch + @ivar shutdownWatch: reference to watch on the xenstored domain shutdown + @type shutdownWatch: xen.xend.xenstore.xswatch + @ivar shutdownStartTime: UNIX Time when domain started shutting down. + @type shutdownStartTime: float or None +# @ivar state: Domain state +# @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...) + @ivar state_updated: lock for self.state + @type state_updated: threading.Condition + @ivar refresh_shutdown_lock: lock for polling shutdown state + @type refresh_shutdown_lock: threading.Condition + @ivar _deviceControllers: device controller cache for this domain + @type _deviceControllers: dict 'string' to DevControllers + """ + + def __init__(self, info, domid = None, dompath = None, augment = False, + priv = False, resume = False, vmpath = None): + """Constructor for a domain + + @param info: parsed configuration + @type info: dictionary + @keyword domid: Set initial domain id (if any) + @type domid: int + @keyword dompath: Set initial dompath (if any) + @type dompath: string + @keyword augment: Augment given info with xenstored VM info + @type augment: bool + @keyword priv: Is a privileged domain (Dom 0) + @type priv: bool + @keyword resume: Is this domain being resumed? + @type resume: bool + """ + + self.info = info + if domid == None: + self.domid = self.info.get('domid') + else: + self.domid = domid + + #REMOVE: uuid is now generated in XendConfig + #if not self._infoIsSet('uuid'): + # self.info['uuid'] = uuid.toString(uuid.create()) + + # Find a unique /vm// path if not specified. + # This avoids conflict between pre-/post-migrate domains when doing + # localhost relocation. + self.vmpath = vmpath + i = 0 + while self.vmpath == None: + self.vmpath = XS_VMROOT + self.info['uuid'] + if i != 0: + self.vmpath = self.vmpath + '-' + str(i) + try: + if self._readVm("uuid"): + self.vmpath = None + i = i + 1 + except: + pass + + self.dompath = dompath + + self.image = None + self.store_port = None + self.store_mfn = None + self.console_port = None + self.console_mfn = None + + self.native_protocol = None + + self.vmWatch = None + self.shutdownWatch = None + self.shutdownStartTime = None + self._resume = resume + + self.state_updated = threading.Condition() + self.refresh_shutdown_lock = threading.Condition() + self._stateSet(DOM_STATE_HALTED) + + self._deviceControllers = {} + + for state in DOM_STATES_OLD: + self.info[state] = 0 + + if augment: + self._augmentInfo(priv) + + self._checkName(self.info['name_label']) + + self.metrics = XendVMMetrics(uuid.createString(), self) + + + # + # Public functions available through XMLRPC + # + + + def start(self, is_managed = False): + """Attempts to start the VM by do the appropriate + initialisation if it not started. + """ + from xen.xend import XendDomain + + if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED, XEN_API_VM_POWER_STATE_CRASHED): + try: + XendTask.log_progress(0, 30, self._constructDomain) + XendTask.log_progress(31, 60, self._initDomain) + + XendTask.log_progress(61, 70, self._storeVmDetails) + XendTask.log_progress(71, 80, self._storeDomDetails) + XendTask.log_progress(81, 90, self._registerWatches) + XendTask.log_progress(91, 100, self.refreshShutdown) + + xendomains = XendDomain.instance() + xennode = XendNode.instance() + + # save running configuration if XendDomains believe domain is + # persistent + if is_managed: + xendomains.managed_config_save(self) + + if xennode.xenschedinfo() == 'credit': + xendomains.domain_sched_credit_set(self.getDomid(), + self.getWeight(), + self.getCap()) + except: + log.exception('VM start failed') + self.destroy() + raise + else: + raise XendError('VM already running') + + def resume(self): + """Resumes a domain that has come back from suspension.""" + state = self._stateGet() + if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED): + try: + self._constructDomain() + self._storeVmDetails() + self._createChannels() + self._createDevices() + self._storeDomDetails() + self._endRestore() + except: + log.exception('VM resume failed') + self.destroy() + raise + else: + raise XendError('VM is not suspended; it is %s' + % XEN_API_VM_POWER_STATE[state]) + + def shutdown(self, reason): + """Shutdown a domain by signalling this via xenstored.""" + log.debug('XendDomainInfo.shutdown(%s)', reason) + if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,): + raise XendError('Domain cannot be shutdown') + + if self.domid == 0: + raise XendError('Domain 0 cannot be shutdown') + + if reason not in DOMAIN_SHUTDOWN_REASONS.values(): + raise XendError('Invalid reason: %s' % reason) + self._removeVm('xend/previous_restart_time') + self.storeDom("control/shutdown", reason) + + # HVM domain shuts itself down only if it has PV drivers + if self.info.is_hvm(): + hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ) + if not hvm_pvdrv: + code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason] + log.info("HVM save:remote shutdown dom %d!", self.domid) + xc.domain_shutdown(self.domid, code) + + def pause(self): + """Pause domain + + @raise XendError: Failed pausing a domain + """ + try: + xc.domain_pause(self.domid) + self._stateSet(DOM_STATE_PAUSED) + except Exception, ex: + log.exception(ex) + raise XendError("Domain unable to be paused: %s" % str(ex)) + + def unpause(self): + """Unpause domain + + @raise XendError: Failed unpausing a domain + """ + try: + xc.domain_unpause(self.domid) + self._stateSet(DOM_STATE_RUNNING) + except Exception, ex: + log.exception(ex) + raise XendError("Domain unable to be unpaused: %s" % str(ex)) + + def send_sysrq(self, key): + """ Send a Sysrq equivalent key via xenstored.""" + if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): + raise XendError("Domain '%s' is not started" % self.info['name_label']) + + asserts.isCharConvertible(key) + self.storeDom("control/sysrq", '%c' % key) + + def sync_pcidev_info(self): + + if not self.info.is_hvm(): + return + + devid = '0' + dev_info = self._getDeviceInfo_pci(devid) + if dev_info is None: + return + + # get the virtual slot info from xenstore + dev_uuid = sxp.child_value(dev_info, 'uuid') + pci_conf = self.info['devices'][dev_uuid][1] + pci_devs = pci_conf['devs'] + + count = 0 + vslots = None + while vslots is None and count < 20: + vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots" + % (self.getDomid(), devid)) + time.sleep(0.1) + count += 1 + if vslots is None: + log.error("Device model didn't tell the vslots for PCI device") + return + + #delete last delim + if vslots[-1] == ";": + vslots = vslots[:-1] + + slot_list = vslots.split(';') + if len(slot_list) != len(pci_devs): + log.error("Device model's pci dev num dismatch") + return + + #update the vslot info + count = 0; + for x in pci_devs: + x['vslt'] = slot_list[count] + count += 1 + + + def hvm_pci_device_create(self, dev_config): + log.debug("XendDomainInfo.hvm_pci_device_create: %s" + % scrub_password(dev_config)) + + if not self.info.is_hvm(): + raise VmError("hvm_pci_device_create called on non-HVM guest") + + #all the PCI devs share one conf node + devid = '0' + + new_dev = dev_config['devs'][0] + dev_info = self._getDeviceInfo_pci(devid)#from self.info['devices'] + + #check conflict before trigger hotplug event + if dev_info is not None: + dev_uuid = sxp.child_value(dev_info, 'uuid') + pci_conf = self.info['devices'][dev_uuid][1] + pci_devs = pci_conf['devs'] + for x in pci_devs: + if (int(x['vslt'], 16) == int(new_dev['vslt'], 16) and + int(x['vslt'], 16) != 0 ): + raise VmError("vslot %s already have a device." % (new_dev['vslt'])) + + if (int(x['domain'], 16) == int(new_dev['domain'], 16) and + int(x['bus'], 16) == int(new_dev['bus'], 16) and + int(x['slot'], 16) == int(new_dev['slot'], 16) and + int(x['func'], 16) == int(new_dev['func'], 16) ): + raise VmError("device is already inserted") + + # Test whether the devices can be assigned with VT-d + pci_str = "%s, %s, %s, %s" % (new_dev['domain'], + new_dev['bus'], + new_dev['slot'], + new_dev['func']) + bdf = xc.test_assign_device(self.domid, pci_str) + if bdf != 0: + if bdf == -1: + raise VmError("failed to assign device: maybe the platform" + " doesn't support VT-d, or VT-d isn't enabled" + " properly?") + bus = (bdf >> 16) & 0xff + devfn = (bdf >> 8) & 0xff + dev = (devfn >> 3) & 0x1f + func = devfn & 0x7 + raise VmError("fail to assign device(%x:%x.%x): maybe it has" + " already been assigned to other domain, or maybe" + " it doesn't exist." % (bus, dev, func)) + + bdf_str = "%s:%s:%s.%s@%s" % (new_dev['domain'], + new_dev['bus'], + new_dev['slot'], + new_dev['func'], + new_dev['vslt']) + self.image.signalDeviceModel('pci-ins', 'pci-inserted', bdf_str) + + + def device_create(self, dev_config): + """Create a new device. + + @param dev_config: device configuration + @type dev_config: SXP object (parsed config) + """ + log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config)) + dev_type = sxp.name(dev_config) + dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config) + dev_config_dict = self.info['devices'][dev_uuid][1] + log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict)) + + if self.domid is not None: + try: + dev_config_dict['devid'] = devid = \ + self._createDevice(dev_type, dev_config_dict) + self._waitForDevice(dev_type, devid) + except VmError, ex: + del self.info['devices'][dev_uuid] + if dev_type == 'pci': + for dev in dev_config_dict['devs']: + XendAPIStore.deregister(dev['uuid'], 'DPCI') + if dev_type == 'vscsi': + for dev in dev_config_dict['devs']: + XendAPIStore.deregister(dev['uuid'], 'DSCSI') + elif dev_type == 'tap': + self.info['vbd_refs'].remove(dev_uuid) + else: + self.info['%s_refs' % dev_type].remove(dev_uuid) + raise ex + else: + devid = None + + xen.xend.XendDomain.instance().managed_config_save(self) + return self.getDeviceController(dev_type).sxpr(devid) + + + def pci_device_configure(self, dev_sxp, devid = 0): + """Configure an existing pci device. + + @param dev_sxp: device configuration + @type dev_sxp: SXP object (parsed config) + @param devid: device id + @type devid: int + @return: Returns True if successfully updated device + @rtype: boolean + """ + log.debug("XendDomainInfo.pci_device_configure: %s" + % scrub_password(dev_sxp)) + + dev_class = sxp.name(dev_sxp) + + if dev_class != 'pci': + return False + + pci_state = sxp.child_value(dev_sxp, 'state') + existing_dev_info = self._getDeviceInfo_pci(devid) + + if existing_dev_info is None and pci_state != 'Initialising': + raise XendError("Cannot detach when pci platform does not exist") + + pci_dev = sxp.children(dev_sxp, 'dev')[0] + dev_config = self.info.pci_convert_sxp_to_dict(dev_sxp) + dev = dev_config['devs'][0] + + # Do HVM specific processing + if self.info.is_hvm(): + if pci_state == 'Initialising': + # HVM PCI device attachment + self.hvm_pci_device_create(dev_config) + # Update vslt + vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter" + % self.getDomid()) + dev['vslt'] = vslt + for n in sxp.children(pci_dev): + if(n[0] == 'vslt'): + n[1] = vslt + else: + # HVM PCI device detachment + existing_dev_uuid = sxp.child_value(existing_dev_info, 'uuid') + existing_pci_conf = self.info['devices'][existing_dev_uuid][1] + existing_pci_devs = existing_pci_conf['devs'] + vslt = '0x0' + for x in existing_pci_devs: + if ( int(x['domain'], 16) == int(dev['domain'], 16) and + int(x['bus'], 16) == int(dev['bus'], 16) and + int(x['slot'], 16) == int(dev['slot'], 16) and + int(x['func'], 16) == int(dev['func'], 16) ): + vslt = x['vslt'] + break + if vslt == '0x0': + raise VmError("Device %04x:%02x:%02x.%01x is not connected" + % (int(dev['domain'],16), int(dev['bus'],16), + int(dev['slot'],16), int(dev['func'],16))) + self.hvm_destroyPCIDevice(int(vslt, 16)) + # Update vslt + dev['vslt'] = vslt + for n in sxp.children(pci_dev): + if(n[0] == 'vslt'): + n[1] = vslt + + # If pci platform does not exist, create and exit. + if existing_dev_info is None: + self.device_create(dev_sxp) + return True + + # use DevController.reconfigureDevice to change device config + dev_control = self.getDeviceController(dev_class) + dev_uuid = dev_control.reconfigureDevice(devid, dev_config) + if not self.info.is_hvm(): + # in PV case, wait until backend state becomes connected. + dev_control.waitForDevice_reconfigure(devid) + num_devs = dev_control.cleanupDevice(devid) + + # update XendConfig with new device info + if dev_uuid: + new_dev_sxp = dev_control.configuration(devid) + self.info.device_update(dev_uuid, new_dev_sxp) + + # If there is no device left, destroy pci and remove config. + if num_devs == 0: + if self.info.is_hvm(): + self.destroyDevice('pci', devid, True) + del self.info['devices'][dev_uuid] + platform = self.info['platform'] + orig_dev_num = len(platform['pci']) + # TODO: can use this to keep some info to ask high level + # management tools to hot insert a new passthrough dev + # after migration + if orig_dev_num != 0: + #platform['pci'] = ["%dDEVs" % orig_dev_num] + platform['pci'] = [] + else: + self.destroyDevice('pci', devid) + del self.info['devices'][dev_uuid] + + xen.xend.XendDomain.instance().managed_config_save(self) + + return True + + def vscsi_device_configure(self, dev_sxp): + """Configure an existing vscsi device. + quoted pci funciton + """ + dev_class = sxp.name(dev_sxp) + if dev_class != 'vscsi': + return False + + dev_config = self.info.vscsi_convert_sxp_to_dict(dev_sxp) + dev = dev_config['devs'][0] + req_devid = int(dev['devid']) + existing_dev_info = self._getDeviceInfo_vscsi(req_devid, dev['v-dev']) + state = dev['state'] + + if state == 'Initialising': + # new create + # If request devid does not exist, create and exit. + if existing_dev_info is None: + self.device_create(dev_sxp) + return True + elif existing_dev_info == "exists": + raise XendError("The virtual device %s is already defined" % dev['v-dev']) + + elif state == 'Closing': + if existing_dev_info is None: + raise XendError("Cannot detach vscsi device does not exist") + + # use DevController.reconfigureDevice to change device config + dev_control = self.getDeviceController(dev_class) + dev_uuid = dev_control.reconfigureDevice(req_devid, dev_config) + dev_control.waitForDevice_reconfigure(req_devid) + num_devs = dev_control.cleanupDevice(req_devid) + + # update XendConfig with new device info + if dev_uuid: + new_dev_sxp = dev_control.configuration(req_devid) + self.info.device_update(dev_uuid, new_dev_sxp) + + # If there is no device left, destroy vscsi and remove config. + if num_devs == 0: + self.destroyDevice('vscsi', req_devid) + del self.info['devices'][dev_uuid] + + return True + + def device_configure(self, dev_sxp, devid = None): + """Configure an existing device. + + @param dev_config: device configuration + @type dev_config: SXP object (parsed config) + @param devid: device id + @type devid: int + @return: Returns True if successfully updated device + @rtype: boolean + """ + + # convert device sxp to a dict + dev_class = sxp.name(dev_sxp) + dev_config = {} + + if dev_class == 'pci': + return self.pci_device_configure(dev_sxp) + + if dev_class == 'vscsi': + return self.vscsi_device_configure(dev_sxp) + + for opt_val in dev_sxp[1:]: + try: + dev_config[opt_val[0]] = opt_val[1] + except IndexError: + pass + + # use DevController.reconfigureDevice to change device config + dev_control = self.getDeviceController(dev_class) + dev_uuid = dev_control.reconfigureDevice(devid, dev_config) + + # update XendConfig with new device info + if dev_uuid: + self.info.device_update(dev_uuid, dev_sxp) + + return True + + def waitForDevices(self): + """Wait for this domain's configured devices to connect. + + @raise VmError: if any device fails to initialise. + """ + for devclass in XendDevices.valid_devices(): + self.getDeviceController(devclass).waitForDevices() + + def hvm_destroyPCIDevice(self, vslot): + log.debug("hvm_destroyPCIDevice called %s", vslot) + + if not self.info.is_hvm(): + raise VmError("hvm_destroyPCIDevice called on non-HVM guest") + + #all the PCI devs share one conf node + devid = '0' + vslot = int(vslot) + dev_info = self._getDeviceInfo_pci('0')#from self.info['devices'] + dev_uuid = sxp.child_value(dev_info, 'uuid') + + #delete the pci bdf config under the pci device + pci_conf = self.info['devices'][dev_uuid][1] + pci_len = len(pci_conf['devs']) + + #find the pass-through device with the virtual slot + devnum = 0 + for x in pci_conf['devs']: + if int(x['vslt'], 16) == vslot: + break + devnum += 1 + + if devnum >= pci_len: + raise VmError("Device @ vslot 0x%x doesn't exist." % (vslot)) + + if vslot == 0: + raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot)) + + bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func']) + log.info("hvm_destroyPCIDevice:%s:%s!", x, bdf_str) + + self.image.signalDeviceModel('pci-rem', 'pci-removed', bdf_str) + + return 0 + + def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False): + log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s", + deviceClass, devid) + + if rm_cfg: + # Convert devid to device number. A device number is + # needed to remove its configuration. + dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid) + + # Save current sxprs. A device number and a backend + # path are needed to remove its configuration but sxprs + # do not have those after calling destroyDevice. + sxprs = self.getDeviceSxprs(deviceClass) + + rc = None + if self.domid is not None: + rc = self.getDeviceController(deviceClass).destroyDevice(devid, force) + if not force and rm_cfg: + # The backend path, other than the device itself, + # has to be passed because its accompanied frontend + # path may be void until its removal is actually + # issued. It is probable because destroyDevice is + # issued first. + for dev_num, dev_info in sxprs: + dev_num = int(dev_num) + if dev_num == dev: + for x in dev_info: + if x[0] == 'backend': + backend = x[1] + break + break + self._waitForDevice_destroy(deviceClass, devid, backend) + + if rm_cfg: + if deviceClass == 'vif': + if self.domid is not None: + for dev_num, dev_info in sxprs: + dev_num = int(dev_num) + if dev_num == dev: + for x in dev_info: + if x[0] == 'mac': + mac = x[1] + break + break + dev_info = self._getDeviceInfo_vif(mac) + else: + _, dev_info = sxprs[dev] + else: # 'vbd' or 'tap' + dev_info = self._getDeviceInfo_vbd(dev) + # To remove the UUID of the device from refs, + # deviceClass must be always 'vbd'. + deviceClass = 'vbd' + if dev_info is None: + raise XendError("Device %s is not defined" % devid) + + dev_uuid = sxp.child_value(dev_info, 'uuid') + del self.info['devices'][dev_uuid] + self.info['%s_refs' % deviceClass].remove(dev_uuid) + xen.xend.XendDomain.instance().managed_config_save(self) + + return rc + + def getDeviceSxprs(self, deviceClass): + if deviceClass == 'pci': + dev_info = self._getDeviceInfo_pci('0')#from self.info['devices'] + if dev_info is None: + return [] + dev_uuid = sxp.child_value(dev_info, 'uuid') + pci_devs = self.info['devices'][dev_uuid][1]['devs'] + pci_len = len(pci_devs) + return pci_devs + if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED): + return self.getDeviceController(deviceClass).sxprs() + else: + sxprs = [] + dev_num = 0 + for dev_type, dev_info in self.info.all_devices_sxpr(): + if dev_type == deviceClass: + sxprs.append([dev_num, dev_info]) + dev_num += 1 + return sxprs + + def getBlockDeviceClass(self, devid): + # To get a device number from the devid, + # we temporarily use the device controller of VBD. + dev = self.getDeviceController('vbd').convertToDeviceNumber(devid) + dev_info = self._getDeviceInfo_vbd(dev) + if dev_info: + return dev_info[0] + + def _getDeviceInfo_vif(self, mac): + for dev_type, dev_info in self.info.all_devices_sxpr(): + if dev_type != 'vif': + continue + if mac == sxp.child_value(dev_info, 'mac'): + return dev_info + + def _getDeviceInfo_vbd(self, devid): + for dev_type, dev_info in self.info.all_devices_sxpr(): + if dev_type != 'vbd' and dev_type != 'tap': + continue + dev = sxp.child_value(dev_info, 'dev') + dev = dev.split(':')[0] + dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev) + if devid == dev: + return dev_info + + def _getDeviceInfo_pci(self, devid): + for dev_type, dev_info in self.info.all_devices_sxpr(): + if dev_type != 'pci': + continue + return dev_info + return None + + def _getDeviceInfo_vscsi(self, devid, vdev): + devid = int(devid) + for dev_type, dev_info in self.info.all_devices_sxpr(): + if dev_type != 'vscsi': + continue + existing_dev_uuid = sxp.child_value(dev_info, 'uuid') + existing_conf = self.info['devices'][existing_dev_uuid][1] + existing_dev = existing_conf['devs'][0] + existing_devid = int(existing_dev['devid']) + existing_vdev = existing_dev['v-dev'] + + if vdev == existing_vdev: + return "exists" + + if devid == existing_devid: + return dev_info + + return None + + def setMemoryTarget(self, target): + """Set the memory target of this domain. + @param target: In MiB. + """ + log.debug("Setting memory target of domain %s (%s) to %d MiB.", + self.info['name_label'], str(self.domid), target) + + MiB = 1024 * 1024 + + if self.domid == 0: + dom0_min_mem = xoptions.get_dom0_min_mem() + memory_cur = self.get_memory_dynamic_max() / MiB + if target < memory_cur and dom0_min_mem > target: + raise XendError("memory_dynamic_max too small") + + self._safe_set_memory('memory_dynamic_min', target * MiB) + self._safe_set_memory('memory_dynamic_max', target * MiB) + + if self.domid >= 0: + self.storeVm("memory", target) + self.storeDom("memory/target", target << 10) + xen.xend.XendDomain.instance().managed_config_save(self) + + def setMemoryMaximum(self, limit): + """Set the maximum memory limit of this domain + @param limit: In MiB. + """ + log.debug("Setting memory maximum of domain %s (%s) to %d MiB.", + self.info['name_label'], str(self.domid), limit) + + maxmem_cur = self.get_memory_static_max() + MiB = 1024 * 1024 + self._safe_set_memory('memory_static_max', limit * MiB) + + if self.domid >= 0: + maxmem = int(limit) * 1024 + try: + return xc.domain_setmaxmem(self.domid, maxmem) + except Exception, ex: + self._safe_set_memory('memory_static_max', maxmem_cur) + raise XendError(str(ex)) + xen.xend.XendDomain.instance().managed_config_save(self) + + + def getVCPUInfo(self): + try: + # We include the domain name and ID, to help xm. + sxpr = ['domain', + ['domid', self.domid], + ['name', self.info['name_label']], + ['vcpu_count', self.info['VCPUs_max']]] + + for i in range(0, self.info['VCPUs_max']): + if self.domid is not None: + info = xc.vcpu_getinfo(self.domid, i) + + sxpr.append(['vcpu', + ['number', i], + ['online', info['online']], + ['blocked', info['blocked']], + ['running', info['running']], + ['cpu_time', info['cpu_time'] / 1e9], + ['cpu', info['cpu']], + ['cpumap', info['cpumap']]]) + else: + sxpr.append(['vcpu', + ['number', i], + ['online', 0], + ['blocked', 0], + ['running', 0], + ['cpu_time', 0.0], + ['cpu', -1], + ['cpumap', self.info['cpus'][i] and \ + self.info['cpus'][i] or range(64)]]) + + return sxpr + + except RuntimeError, exn: + raise XendError(str(exn)) + + + def getDomInfo(self): + return dom_get(self.domid) + + # + # internal functions ... TODO: re-categorised + # + + def _augmentInfo(self, priv): + """Augment self.info, as given to us through L{recreate}, with + values taken from the store. This recovers those values known + to xend but not to the hypervisor. + """ + augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:] + if priv: + augment_entries.remove('memory') + augment_entries.remove('maxmem') + augment_entries.remove('vcpus') + augment_entries.remove('vcpu_avail') + + vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k]) + for k in augment_entries]) + + # make returned lists into a dictionary + vm_config = dict(zip(augment_entries, vm_config)) + + for arg in augment_entries: + val = vm_config[arg] + if val != None: + if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: + xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] + self.info[xapiarg] = val + elif arg == "memory": + self.info["static_memory_min"] = val + elif arg == "maxmem": + self.info["static_memory_max"] = val + else: + self.info[arg] = val + + # read CPU Affinity + self.info['cpus'] = [] + vcpus_info = self.getVCPUInfo() + for vcpu_info in sxp.children(vcpus_info, 'vcpu'): + self.info['cpus'].append(sxp.child_value(vcpu_info, 'cpumap')) + + # For dom0, we ignore any stored value for the vcpus fields, and + # read the current value from Xen instead. This allows boot-time + # settings to take precedence over any entries in the store. + if priv: + xeninfo = dom_get(self.domid) + self.info['VCPUs_max'] = xeninfo['online_vcpus'] + self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1 + + # read image value + image_sxp = self._readVm('image') + if image_sxp: + self.info.update_with_image_sxp(sxp.from_string(image_sxp)) + + # read devices + devices = [] + for devclass in XendDevices.valid_devices(): + devconfig = self.getDeviceController(devclass).configurations() + if devconfig: + devices.extend(devconfig) + + if not self.info['devices'] and devices is not None: + for device in devices: + self.info.device_add(device[0], cfg_sxp = device) + + self._update_consoles() + + def _update_consoles(self, transaction = None): + if self.domid == None or self.domid == 0: + return + + # Update VT100 port if it exists + if transaction is None: + self.console_port = self.readDom('console/port') + else: + self.console_port = self.readDomTxn(transaction, 'console/port') + if self.console_port is not None: + serial_consoles = self.info.console_get_all('vt100') + if not serial_consoles: + cfg = self.info.console_add('vt100', self.console_port) + self._createDevice('console', cfg) + else: + console_uuid = serial_consoles[0].get('uuid') + self.info.console_update(console_uuid, 'location', + self.console_port) + + + # Update VNC port if it exists and write to xenstore + if transaction is None: + vnc_port = self.readDom('console/vnc-port') + else: + vnc_port = self.readDomTxn(transaction, 'console/vnc-port') + if vnc_port is not None: + for dev_uuid, (dev_type, dev_info) in self.info['devices'].items(): + if dev_type == 'vfb': + old_location = dev_info.get('location') + listen_host = dev_info.get('vnclisten', 'localhost') + new_location = '%s:%s' % (listen_host, str(vnc_port)) + if old_location == new_location: + break + + dev_info['location'] = new_location + self.info.device_update(dev_uuid, cfg_xenapi = dev_info) + vfb_ctrl = self.getDeviceController('vfb') + vfb_ctrl.reconfigureDevice(0, dev_info) + break + + # + # Function to update xenstore /vm/* + # + + def _readVm(self, *args): + return xstransact.Read(self.vmpath, *args) + + def _writeVm(self, *args): + return xstransact.Write(self.vmpath, *args) + + def _removeVm(self, *args): + return xstransact.Remove(self.vmpath, *args) + + def _gatherVm(self, *args): + return xstransact.Gather(self.vmpath, *args) + + def _listRecursiveVm(self, *args): + return xstransact.ListRecursive(self.vmpath, *args) + + def storeVm(self, *args): + return xstransact.Store(self.vmpath, *args) + + def permissionsVm(self, *args): + return xstransact.SetPermissions(self.vmpath, *args) + + # + # Function to update xenstore /dom/* + # + + def readDom(self, *args): + return xstransact.Read(self.dompath, *args) + + def gatherDom(self, *args): + return xstransact.Gather(self.dompath, *args) + + def _writeDom(self, *args): + return xstransact.Write(self.dompath, *args) + + def _removeDom(self, *args): + return xstransact.Remove(self.dompath, *args) + + def storeDom(self, *args): + return xstransact.Store(self.dompath, *args) + + + def readDomTxn(self, transaction, *args): + paths = map(lambda x: self.dompath + "/" + x, args) + return transaction.read(*paths) + + def gatherDomTxn(self, transaction, *args): + paths = map(lambda x: self.dompath + "/" + x, args) + return transaction.gather(*paths) + + def _writeDomTxn(self, transaction, *args): + paths = map(lambda x: self.dompath + "/" + x, args) + return transaction.write(*paths) + + def _removeDomTxn(self, transaction, *args): + paths = map(lambda x: self.dompath + "/" + x, args) + return transaction.remove(*paths) + + def storeDomTxn(self, transaction, *args): + paths = map(lambda x: self.dompath + "/" + x, args) + return transaction.store(*paths) + + + def _recreateDom(self): + complete(self.dompath, lambda t: self._recreateDomFunc(t)) + + def _recreateDomFunc(self, t): + t.remove() + t.mkdir() + t.set_permissions({'dom' : self.domid, 'read' : True}) + t.write('vm', self.vmpath) + for i in [ 'device', 'control', 'error', 'memory' ]: + t.mkdir(i) + t.set_permissions(i, {'dom' : self.domid}) + + def _storeDomDetails(self): + to_store = { + 'domid': str(self.domid), + 'vm': self.vmpath, + 'name': self.info['name_label'], + 'console/limit': str(xoptions.get_console_limit() * 1024), + 'memory/target': str(self.info['memory_dynamic_max'] / 1024), + } + + def f(n, v): + if v is not None: + if type(v) == bool: + to_store[n] = v and "1" or "0" + else: + to_store[n] = str(v) + + # Figure out if we need to tell xenconsoled to ignore this guest's + # console - device model will handle console if it is running + constype = "ioemu" + if 'device_model' not in self.info['platform']: + constype = "xenconsoled" + + f('console/port', self.console_port) + f('console/ring-ref', self.console_mfn) + f('console/type', constype) + f('store/port', self.store_port) + f('store/ring-ref', self.store_mfn) + + if arch.type == "x86": + f('control/platform-feature-multiprocessor-suspend', True) + + # elfnotes + for n, v in self.info.get_notes().iteritems(): + n = n.lower().replace('_', '-') + if n == 'features': + for v in v.split('|'): + v = v.replace('_', '-') + if v.startswith('!'): + f('image/%s/%s' % (n, v[1:]), False) + else: + f('image/%s/%s' % (n, v), True) + else: + f('image/%s' % n, v) + + if self.info.has_key('security_label'): + f('security_label', self.info['security_label']) + + to_store.update(self._vcpuDomDetails()) + + log.debug("Storing domain details: %s", scrub_password(to_store)) + + self._writeDom(to_store) + + def _vcpuDomDetails(self): + def availability(n): + if self.info['vcpu_avail'] & (1 << n): + return 'online' + else: + return 'offline' + + result = {} + for v in range(0, self.info['VCPUs_max']): + result["cpu/%d/availability" % v] = availability(v) + return result + + # + # xenstore watches + # + + def _registerWatches(self): + """Register a watch on this VM's entries in the store, and the + domain's control/shutdown node, so that when they are changed + externally, we keep up to date. This should only be called by {@link + #create}, {@link #recreate}, or {@link #restore}, once the domain's + details have been written, but before the new instance is returned.""" + self.vmWatch = xswatch(self.vmpath, self._storeChanged) + self.shutdownWatch = xswatch(self.dompath + '/control/shutdown', + self._handleShutdownWatch) + + def _storeChanged(self, _): + log.trace("XendDomainInfo.storeChanged"); + + changed = False + + # Check whether values in the configuration have + # changed in Xenstore. + + cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash', + 'rtc/timeoffset'] + + vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k]) + for k in cfg_vm]) + + # convert two lists into a python dictionary + vm_details = dict(zip(cfg_vm, vm_details)) + + if vm_details['rtc/timeoffset'] == None: + vm_details['rtc/timeoffset'] = "0" + + for arg, val in vm_details.items(): + if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: + xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] + if val != None and val != self.info[xapiarg]: + self.info[xapiarg] = val + changed = True + elif arg == "memory": + if val != None and val != self.info["static_memory_min"]: + self.info["static_memory_min"] = val + changed = True + elif arg == "maxmem": + if val != None and val != self.info["static_memory_max"]: + self.info["static_memory_max"] = val + changed = True + + # Check whether image definition has been updated + image_sxp = self._readVm('image') + if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()): + self.info.update_with_image_sxp(sxp.from_string(image_sxp)) + changed = True + + # Check if the rtc offset has changes + if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"): + self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0) + changed = True + + if changed: + # Update the domain section of the store, as this contains some + # parameters derived from the VM configuration. + self._storeDomDetails() + + return 1 + + def _handleShutdownWatch(self, _): + log.debug('XendDomainInfo.handleShutdownWatch') + + reason = self.readDom('control/shutdown') + + if reason and reason != 'suspend': + sst = self.readDom('xend/shutdown_start_time') + now = time.time() + if sst: + self.shutdownStartTime = float(sst) + timeout = float(sst) + SHUTDOWN_TIMEOUT - now + else: + self.shutdownStartTime = now + self.storeDom('xend/shutdown_start_time', now) + timeout = SHUTDOWN_TIMEOUT + + log.trace( + "Scheduling refreshShutdown on domain %d in %ds.", + self.domid, timeout) + threading.Timer(timeout, self.refreshShutdown).start() + + return True + + + # + # Public Attributes for the VM + # + + + def getDomid(self): + return self.domid + + def setName(self, name, to_store = True): + self._checkName(name) + self.info['name_label'] = name + if to_store: + self.storeVm("name", name) + + def getName(self): + return self.info['name_label'] + + def getDomainPath(self): + return self.dompath + + def getShutdownReason(self): + return self.readDom('control/shutdown') + + def getStorePort(self): + """For use only by image.py and XendCheckpoint.py.""" + return self.store_port + + def getConsolePort(self): + """For use only by image.py and XendCheckpoint.py""" + return self.console_port + + def getFeatures(self): + """For use only by image.py.""" + return self.info['features'] + + def getVCpuCount(self): + return self.info['VCPUs_max'] + + def setVCpuCount(self, vcpus): + if vcpus <= 0: + raise XendError('Invalid VCPUs') + + self.info['vcpu_avail'] = (1 << vcpus) - 1 + if self.domid >= 0: + self.storeVm('vcpu_avail', self.info['vcpu_avail']) + # update dom differently depending on whether we are adjusting + # vcpu number up or down, otherwise _vcpuDomDetails does not + # disable the vcpus + if self.info['VCPUs_max'] > vcpus: + # decreasing + self._writeDom(self._vcpuDomDetails()) + self.info['VCPUs_live'] = vcpus + else: + # same or increasing + self.info['VCPUs_live'] = vcpus + self._writeDom(self._vcpuDomDetails()) + else: + if self.info['VCPUs_max'] > vcpus: + # decreasing + del self.info['cpus'][vcpus:] + elif self.info['VCPUs_max'] < vcpus: + # increasing + for c in range(self.info['VCPUs_max'], vcpus): + self.info['cpus'].append(list()) + self.info['VCPUs_max'] = vcpus + xen.xend.XendDomain.instance().managed_config_save(self) + log.info("Set VCPU count on domain %s to %d", self.info['name_label'], + vcpus) + + def getMemoryTarget(self): + """Get this domain's target memory size, in KB.""" + return self.info['memory_dynamic_max'] / 1024 + + def getMemoryMaximum(self): + """Get this domain's maximum memory size, in KB.""" + # remember, info now stores memory in bytes + return self.info['memory_static_max'] / 1024 + + def getResume(self): + return str(self._resume) + + def setResume(self, isresume): + self._resume = isresume + + def getCpus(self): + return self.info['cpus'] + + def setCpus(self, cpumap): + self.info['cpus'] = cpumap + + def getCap(self): + return self.info['vcpus_params']['cap'] + + def setCap(self, cpu_cap): + self.info['vcpus_params']['cap'] = cpu_cap + + def getWeight(self): + return self.info['vcpus_params']['weight'] + + def setWeight(self, cpu_weight): + self.info['vcpus_params']['weight'] = cpu_weight + + def getRestartCount(self): + return self._readVm('xend/restart_count') + + def refreshShutdown(self, xeninfo = None): + """ Checks the domain for whether a shutdown is required. + + Called from XendDomainInfo and also image.py for HVM images. + """ + + # If set at the end of this method, a restart is required, with the + # given reason. This restart has to be done out of the scope of + # refresh_shutdown_lock. + restart_reason = None + + self.refresh_shutdown_lock.acquire() + try: + if xeninfo is None: + xeninfo = dom_get(self.domid) + if xeninfo is None: + # The domain no longer exists. This will occur if we have + # scheduled a timer to check for shutdown timeouts and the + # shutdown succeeded. It will also occur if someone + # destroys a domain beneath us. We clean up the domain, + # just in case, but we can't clean up the VM, because that + # VM may have migrated to a different domain on this + # machine. + self.cleanupDomain() + self._stateSet(DOM_STATE_HALTED) + return + + if xeninfo['dying']: + # Dying means that a domain has been destroyed, but has not + # yet been cleaned up by Xen. This state could persist + # indefinitely if, for example, another domain has some of its + # pages mapped. We might like to diagnose this problem in the + # future, but for now all we do is make sure that it's not us + # holding the pages, by calling cleanupDomain. We can't + # clean up the VM, as above. + self.cleanupDomain() + self._stateSet(DOM_STATE_SHUTDOWN) + return + + elif xeninfo['crashed']: + if self.readDom('xend/shutdown_completed'): + # We've seen this shutdown already, but we are preserving + # the domain for debugging. Leave it alone. + return + + log.warn('Domain has crashed: name=%s id=%d.', + self.info['name_label'], self.domid) + self._writeVm(LAST_SHUTDOWN_REASON, 'crash') + + restart_reason = 'crash' + self._stateSet(DOM_STATE_HALTED) + + elif xeninfo['shutdown']: + self._stateSet(DOM_STATE_SHUTDOWN) + if self.readDom('xend/shutdown_completed'): + # We've seen this shutdown already, but we are preserving + # the domain for debugging. Leave it alone. + return + + else: + reason = shutdown_reason(xeninfo['shutdown_reason']) + + log.info('Domain has shutdown: name=%s id=%d reason=%s.', + self.info['name_label'], self.domid, reason) + self._writeVm(LAST_SHUTDOWN_REASON, reason) + + self._clearRestart() + + if reason == 'suspend': + self._stateSet(DOM_STATE_SUSPENDED) + # Don't destroy the domain. XendCheckpoint will do + # this once it has finished. However, stop watching + # the VM path now, otherwise we will end up with one + # watch for the old domain, and one for the new. + self._unwatchVm() + elif reason in ('poweroff', 'reboot'): + restart_reason = reason + else: + self.destroy() + + elif self.dompath is None: + # We have yet to manage to call introduceDomain on this + # domain. This can happen if a restore is in progress, or has + # failed. Ignore this domain. + pass + else: + # Domain is alive. If we are shutting it down, log a message + # if it seems unresponsive. + if xeninfo['paused']: + self._stateSet(DOM_STATE_PAUSED) + else: + self._stateSet(DOM_STATE_RUNNING) + + if self.shutdownStartTime: + timeout = (SHUTDOWN_TIMEOUT - time.time() + + self.shutdownStartTime) + if (timeout < 0 and not self.readDom('xend/unresponsive')): + log.info( + "Domain shutdown timeout expired: name=%s id=%s", + self.info['name_label'], self.domid) + self.storeDom('xend/unresponsive', 'True') + finally: + self.refresh_shutdown_lock.release() + + if restart_reason: + threading.Thread(target = self._maybeRestart, + args = (restart_reason,)).start() + + + # + # Restart functions - handling whether we come back up on shutdown. + # + + def _clearRestart(self): + self._removeDom("xend/shutdown_start_time") + + def _maybeDumpCore(self, reason): + if reason == 'crash': + if xoptions.get_enable_dump() or self.get_on_crash() \ + in ['coredump_and_destroy', 'coredump_and_restart']: + try: + self.dumpCore() + except XendError: + # This error has been logged -- there's nothing more + # we can do in this context. + pass + + def _maybeRestart(self, reason): + # Before taking configured action, dump core if configured to do so. + # + self._maybeDumpCore(reason) + + # Dispatch to the correct method based upon the configured on_{reason} + # behaviour. + actions = {"destroy" : self.destroy, + "restart" : self._restart, + "preserve" : self._preserve, + "rename-restart" : self._renameRestart, + "coredump-destroy" : self.destroy, + "coredump-restart" : self._restart} + + action_conf = { + 'poweroff': 'actions_after_shutdown', + 'reboot': 'actions_after_reboot', + 'crash': 'actions_after_crash', + } + + action_target = self.info.get(action_conf.get(reason)) + func = actions.get(action_target, None) + if func and callable(func): + func() + else: + self.destroy() # default to destroy + + def _renameRestart(self): + self._restart(True) + + def _restart(self, rename = False): + """Restart the domain after it has exited. + + @param rename True if the old domain is to be renamed and preserved, + False if it is to be destroyed. + """ + from xen.xend import XendDomain + + if self._readVm(RESTART_IN_PROGRESS): + log.error('Xend failed during restart of domain %s. ' + 'Refusing to restart to avoid loops.', + str(self.domid)) + self.destroy() + return + + old_domid = self.domid + self._writeVm(RESTART_IN_PROGRESS, 'True') + + now = time.time() + rst = self._readVm('xend/previous_restart_time') + if rst: + rst = float(rst) + timeout = now - rst + if timeout < MINIMUM_RESTART_TIME: + log.error( + 'VM %s restarting too fast (%f seconds since the last ' + 'restart). Refusing to restart to avoid loops.', + self.info['name_label'], timeout) + self.destroy() + return + + self._writeVm('xend/previous_restart_time', str(now)) + + prev_vm_xend = self._listRecursiveVm('xend') + new_dom_info = self.info + try: + if rename: + new_dom_info = self._preserveForRestart() + else: + self._unwatchVm() + self.destroy() + + # new_dom's VM will be the same as this domain's VM, except where + # the rename flag has instructed us to call preserveForRestart. + # In that case, it is important that we remove the + # RESTART_IN_PROGRESS node from the new domain, not the old one, + # once the new one is available. + + new_dom = None + try: + new_dom = XendDomain.instance().domain_create_from_dict( + new_dom_info) + for x in prev_vm_xend[0][1]: + new_dom._writeVm('xend/%s' % x[0], x[1]) + new_dom.waitForDevices() + new_dom.unpause() + rst_cnt = new_dom._readVm('xend/restart_count') + rst_cnt = int(rst_cnt) + 1 + new_dom._writeVm('xend/restart_count', str(rst_cnt)) + new_dom._removeVm(RESTART_IN_PROGRESS) + except: + if new_dom: + new_dom._removeVm(RESTART_IN_PROGRESS) + new_dom.destroy() + else: + self._removeVm(RESTART_IN_PROGRESS) + raise + except: + log.exception('Failed to restart domain %s.', str(old_domid)) + + def _preserveForRestart(self): + """Preserve a domain that has been shut down, by giving it a new UUID, + cloning the VM details, and giving it a new name. This allows us to + keep this domain for debugging, but restart a new one in its place + preserving the restart semantics (name and UUID preserved). + """ + + new_uuid = uuid.createString() + new_name = 'Domain-%s' % new_uuid + log.info("Renaming dead domain %s (%d, %s) to %s (%s).", + self.info['name_label'], self.domid, self.info['uuid'], + new_name, new_uuid) + self._unwatchVm() + self._releaseDevices() + # Remove existing vm node in xenstore + self._removeVm() + new_dom_info = self.info.copy() + new_dom_info['name_label'] = self.info['name_label'] + new_dom_info['uuid'] = self.info['uuid'] + self.info['name_label'] = new_name + self.info['uuid'] = new_uuid + self.vmpath = XS_VMROOT + new_uuid + # Write out new vm node to xenstore + self._storeVmDetails() + self._preserve() + return new_dom_info + + + def _preserve(self): + log.info("Preserving dead domain %s (%d).", self.info['name_label'], + self.domid) + self._unwatchVm() + self.storeDom('xend/shutdown_completed', 'True') + self._stateSet(DOM_STATE_HALTED) + + # + # Debugging .. + # + + def dumpCore(self, corefile = None): + """Create a core dump for this domain. + + @raise: XendError if core dumping failed. + """ + + try: + if not corefile: + this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime()) + corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time, + self.info['name_label'], self.domid) + + if os.path.isdir(corefile): + raise XendError("Cannot dump core in a directory: %s" % + corefile) + + self._writeVm(DUMPCORE_IN_PROGRESS, 'True') + xc.domain_dumpcore(self.domid, corefile) + self._removeVm(DUMPCORE_IN_PROGRESS) + except RuntimeError, ex: + corefile_incomp = corefile+'-incomplete' + os.rename(corefile, corefile_incomp) + self._removeVm(DUMPCORE_IN_PROGRESS) + log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s", + self.domid, self.info['name_label']) + raise XendError("Failed to dump core: %s" % str(ex)) + + # + # Device creation/deletion functions + # + + def _createDevice(self, deviceClass, devConfig): + return self.getDeviceController(deviceClass).createDevice(devConfig) + + def _waitForDevice(self, deviceClass, devid): + return self.getDeviceController(deviceClass).waitForDevice(devid) + + def _waitForDeviceUUID(self, dev_uuid): + deviceClass, config = self.info['devices'].get(dev_uuid) + self._waitForDevice(deviceClass, config['devid']) + + def _waitForDevice_destroy(self, deviceClass, devid, backpath): + return self.getDeviceController(deviceClass).waitForDevice_destroy( + devid, backpath) + + def _reconfigureDevice(self, deviceClass, devid, devconfig): + return self.getDeviceController(deviceClass).reconfigureDevice( + devid, devconfig) + + def _createDevices(self): + """Create the devices for a vm. + + @raise: VmError for invalid devices + """ + if self.image: + self.image.prepareEnvironment() + + vscsi_uuidlist = {} + vscsi_devidlist = [] + ordered_refs = self.info.ordered_device_refs() + for dev_uuid in ordered_refs: + devclass, config = self.info['devices'][dev_uuid] + if devclass in XendDevices.valid_devices() and devclass != 'vscsi': + log.info("createDevice: %s : %s" % (devclass, scrub_password(config))) + dev_uuid = config.get('uuid') + devid = self._createDevice(devclass, config) + + # store devid in XendConfig for caching reasons + if dev_uuid in self.info['devices']: + self.info['devices'][dev_uuid][1]['devid'] = devid + + elif devclass == 'vscsi': + vscsi_config = config.get('devs', [])[0] + devid = vscsi_config.get('devid', '') + dev_uuid = config.get('uuid') + vscsi_uuidlist[devid] = dev_uuid + vscsi_devidlist.append(devid) + + #It is necessary to sorted it for /dev/sdxx in guest. + if len(vscsi_uuidlist) > 0: + vscsi_devidlist.sort() + for vscsiid in vscsi_devidlist: + dev_uuid = vscsi_uuidlist[vscsiid] + devclass, config = self.info['devices'][dev_uuid] + log.info("createDevice: %s : %s" % (devclass, scrub_password(config))) + dev_uuid = config.get('uuid') + devid = self._createDevice(devclass, config) + # store devid in XendConfig for caching reasons + if dev_uuid in self.info['devices']: + self.info['devices'][dev_uuid][1]['devid'] = devid + + + if self.image: + self.image.createDeviceModel() + + #if have pass-through devs, need the virtual pci slots info from qemu + self.sync_pcidev_info() + + def _releaseDevices(self, suspend = False): + """Release all domain's devices. Nothrow guarantee.""" + if self.image: + try: + log.debug("Destroying device model") + self.image.destroyDeviceModel() + except Exception, e: + log.exception("Device model destroy failed %s" % str(e)) + else: + log.debug("No device model") + + log.debug("Releasing devices") + t = xstransact("%s/device" % self.dompath) + try: + for devclass in XendDevices.valid_devices(): + for dev in t.list(devclass): + try: + log.debug("Removing %s", dev); + self.destroyDevice(devclass, dev, False); + except: + # Log and swallow any exceptions in removal -- + # there's nothing more we can do. + log.exception("Device release failed: %s; %s; %s", + self.info['name_label'], devclass, dev) + finally: + t.abort() + + def getDeviceController(self, name): + """Get the device controller for this domain, and if it + doesn't exist, create it. + + @param name: device class name + @type name: string + @rtype: subclass of DevController + """ + if name not in self._deviceControllers: + devController = XendDevices.make_controller(name, self) + if not devController: + raise XendError("Unknown device type: %s" % name) + self._deviceControllers[name] = devController + + return self._deviceControllers[name] + + # + # Migration functions (public) + # + + def testMigrateDevices(self, network, dst): + """ Notify all device about intention of migration + @raise: XendError for a device that cannot be migrated + """ + for (n, c) in self.info.all_devices_sxpr(): + rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName()) + if rc != 0: + raise XendError("Device of type '%s' refuses migration." % n) + + def migrateDevices(self, network, dst, step, domName=''): + """Notify the devices about migration + """ + ctr = 0 + try: + for (dev_type, dev_conf) in self.info.all_devices_sxpr(): + self.migrateDevice(dev_type, dev_conf, network, dst, + step, domName) + ctr = ctr + 1 + except: + for dev_type, dev_conf in self.info.all_devices_sxpr(): + if ctr == 0: + step = step - 1 + ctr = ctr - 1 + self._recoverMigrateDevice(dev_type, dev_conf, network, + dst, step, domName) + raise + + def migrateDevice(self, deviceClass, deviceConfig, network, dst, + step, domName=''): + return self.getDeviceController(deviceClass).migrate(deviceConfig, + network, dst, step, domName) + + def _recoverMigrateDevice(self, deviceClass, deviceConfig, network, + dst, step, domName=''): + return self.getDeviceController(deviceClass).recover_migrate( + deviceConfig, network, dst, step, domName) + + + ## private: + + def _constructDomain(self): + """Construct the domain. + + @raise: VmError on error + """ + + log.debug('XendDomainInfo.constructDomain') + + self.shutdownStartTime = None + + hap = 0 + hvm = self.info.is_hvm() + if hvm: + hap = self.info.is_hap() + info = xc.xeninfo() + if 'hvm' not in info['xen_caps']: + raise VmError("HVM guest support is unavailable: is VT/AMD-V " + "supported by your CPU and enabled in your " + "BIOS?") + + # Hack to pre-reserve some memory for initial domain creation. + # There is an implicit memory overhead for any domain creation. This + # overhead is greater for some types of domain than others. For + # example, an x86 HVM domain will have a default shadow-pagetable + # allocation of 1MB. We free up 2MB here to be on the safe side. + balloon.free(2*1024) # 2MB should be plenty + + ssidref = 0 + if security.on() == xsconstants.XS_POLICY_USE: + ssidref = security.calc_dom_ssidref_from_info(self.info) + if security.has_authorization(ssidref) == False: + raise VmError("VM is not authorized to run.") + + try: + self.domid = xc.domain_create( + domid = 0, + ssidref = ssidref, + handle = uuid.fromString(self.info['uuid']), + flags = (int(hvm) << 0) | (int(hap) << 1), + target = self.info.target()) + except Exception, e: + # may get here if due to ACM the operation is not permitted + if security.on() == xsconstants.XS_POLICY_ACM: + raise VmError('Domain in conflict set with running domain?') + + if self.domid < 0: + raise VmError('Creating domain failed: name=%s' % + self.info['name_label']) + + self.dompath = GetDomainPath(self.domid) + + self._recreateDom() + + # Set timer configration of domain + timer_mode = self.info["platform"].get("timer_mode") + if hvm and timer_mode is not None: + xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE, + long(timer_mode)) + + # Set Viridian interface configuration of domain + viridian = self.info["platform"].get("viridian") + if arch.type == "x86" and hvm and viridian is not None: + xc.hvm_set_param(self.domid, HVM_PARAM_VIRIDIAN, long(viridian)) + + # Optionally enable virtual HPET + hpet = self.info["platform"].get("hpet") + if hvm and hpet is not None: + xc.hvm_set_param(self.domid, HVM_PARAM_HPET_ENABLED, + long(hpet)) + + # Set maximum number of vcpus in domain + xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max'])) + + # Test whether the devices can be assigned with VT-d + pci_str = str(self.info["platform"].get("pci")) + if hvm and pci_str: + bdf = xc.test_assign_device(self.domid, pci_str) + if bdf != 0: + if bdf == -1: + raise VmError("failed to assign device: maybe the platform" + " doesn't support VT-d, or VT-d isn't enabled" + " properly?") + bus = (bdf >> 16) & 0xff + devfn = (bdf >> 8) & 0xff + dev = (devfn >> 3) & 0x1f + func = devfn & 0x7 + raise VmError("fail to assign device(%x:%x.%x): maybe it has" + " already been assigned to other domain, or maybe" + " it doesn't exist." % (bus, dev, func)) + + # register the domain in the list + from xen.xend import XendDomain + XendDomain.instance().add_domain(self) + + def _introduceDomain(self): + assert self.domid is not None + assert self.store_mfn is not None + assert self.store_port is not None + + try: + IntroduceDomain(self.domid, self.store_mfn, self.store_port) + except RuntimeError, exn: + raise XendError(str(exn)) + + def _setTarget(self, target): + assert self.domid is not None + + try: + SetTarget(self.domid, target) + self.storeDom('target', target) + except RuntimeError, exn: + raise XendError(str(exn)) + + + def _initDomain(self): + log.debug('XendDomainInfo.initDomain: %s %s', + self.domid, + self.info['vcpus_params']['weight']) + + self._configureBootloader() + + try: + if self.info['platform'].get('localtime', 0): + if time.localtime(time.time())[8]: + self.info['platform']['rtc_timeoffset'] = -time.altzone + else: + self.info['platform']['rtc_timeoffset'] = -time.timezone + + self.image = image.create(self, self.info) + + # repin domain vcpus if a restricted cpus list is provided + # this is done prior to memory allocation to aide in memory + # distribution for NUMA systems. + def has_cpus(): + if self.info['cpus'] is not None: + for c in self.info['cpus']: + if c: + return True + return False + + if has_cpus(): + for v in range(0, self.info['VCPUs_max']): + if self.info['cpus'][v]: + xc.vcpu_setaffinity(self.domid, v, self.info['cpus'][v]) + else: + def find_relaxed_node(node_list): + import sys + nr_nodes = info['nr_nodes'] + if node_list is None: + node_list = range(0, nr_nodes) + nodeload = [0] + nodeload = nodeload * nr_nodes + from xen.xend import XendDomain + doms = XendDomain.instance().list('all') + for dom in filter (lambda d: d.domid != self.domid, doms): + cpuinfo = dom.getVCPUInfo() + for vcpu in sxp.children(cpuinfo, 'vcpu'): + if sxp.child_value(vcpu, 'online') == 0: continue + cpumap = list(sxp.child_value(vcpu,'cpumap')) + for i in range(0, nr_nodes): + node_cpumask = info['node_to_cpu'][i] + for j in node_cpumask: + if j in cpumap: + nodeload[i] += 1 + break + for i in range(0, nr_nodes): + if len(info['node_to_cpu'][i]) > 0 and i in node_list: + nodeload[i] = int(nodeload[i] * 16 / len(info['node_to_cpu'][i])) + else: + nodeload[i] = sys.maxint + index = nodeload.index( min(nodeload) ) + return index + + info = xc.physinfo() + if info['nr_nodes'] > 1: + node_memory_list = info['node_to_memory'] + needmem = self.image.getRequiredAvailableMemory(self.info['memory_dynamic_max']) / 1024 + candidate_node_list = [] + for i in range(0, info['nr_nodes']): + if node_memory_list[i] >= needmem and len(info['node_to_cpu'][i]) > 0: + candidate_node_list.append(i) + index = find_relaxed_node(candidate_node_list) + cpumask = info['node_to_cpu'][index] + for v in range(0, self.info['VCPUs_max']): + xc.vcpu_setaffinity(self.domid, v, cpumask) + + # Use architecture- and image-specific calculations to determine + # the various headrooms necessary, given the raw configured + # values. maxmem, memory, and shadow are all in KiB. + # but memory_static_max etc are all stored in bytes now. + memory = self.image.getRequiredAvailableMemory( + self.info['memory_dynamic_max'] / 1024) + maxmem = self.image.getRequiredAvailableMemory( + self.info['memory_static_max'] / 1024) + shadow = self.image.getRequiredShadowMemory( + self.info['shadow_memory'] * 1024, + self.info['memory_static_max'] / 1024) + + log.debug("_initDomain:shadow_memory=0x%x, memory_static_max=0x%x, memory_static_min=0x%x.", self.info['shadow_memory'], self.info['memory_static_max'], self.info['memory_static_min'],) + # Round shadow up to a multiple of a MiB, as shadow_mem_control + # takes MiB and we must not round down and end up under-providing. + shadow = ((shadow + 1023) / 1024) * 1024 + + # set memory limit + xc.domain_setmaxmem(self.domid, maxmem) + + # Reserve 1 page per MiB of RAM for separate VT-d page table. + vtd_mem = 4 * (self.info['memory_static_max'] / 1024 / 1024) + # Round vtd_mem up to a multiple of a MiB. + vtd_mem = ((vtd_mem + 1023) / 1024) * 1024 + + # Make sure there's enough RAM available for the domain + balloon.free(memory + shadow + vtd_mem) + + # Set up the shadow memory + shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024) + self.info['shadow_memory'] = shadow_cur + + # machine address size + if self.info.has_key('machine_address_size'): + log.debug("_initDomain: setting maximum machine address size %d" % self.info['machine_address_size']) + xc.domain_set_machine_address_size(self.domid, self.info['machine_address_size']) + + if self.info.has_key('suppress_spurious_page_faults') and self.info['suppress_spurious_page_faults']: + log.debug("_initDomain: suppressing spurious page faults") + xc.domain_suppress_spurious_page_faults(self.domid) + + self._createChannels() + + channel_details = self.image.createImage() + + self.store_mfn = channel_details['store_mfn'] + if 'console_mfn' in channel_details: + self.console_mfn = channel_details['console_mfn'] + if 'notes' in channel_details: + self.info.set_notes(channel_details['notes']) + if 'native_protocol' in channel_details: + self.native_protocol = channel_details['native_protocol']; + + self._introduceDomain() + if self.info.target(): + self._setTarget(self.info.target()) + + self._createDevices() + + self.image.cleanupBootloading() + + self.info['start_time'] = time.time() + + self._stateSet(DOM_STATE_RUNNING) + except VmError, exn: + log.exception("XendDomainInfo.initDomain: exception occurred") + if self.image: + self.image.cleanupBootloading() + raise exn + except RuntimeError, exn: + log.exception("XendDomainInfo.initDomain: exception occurred") + if self.image: + self.image.cleanupBootloading() + raise VmError(str(exn)) + + + def cleanupDomain(self): + """Cleanup domain resources; release devices. Idempotent. Nothrow + guarantee.""" + + self.refresh_shutdown_lock.acquire() + try: + self.unwatchShutdown() + self._releaseDevices() + bootloader_tidy(self) + + if self.image: + self.image = None + + try: + self._removeDom() + except: + log.exception("Removing domain path failed.") + + self._stateSet(DOM_STATE_HALTED) + self.domid = None # Do not push into _stateSet()! + finally: + self.refresh_shutdown_lock.release() + + + def unwatchShutdown(self): + """Remove the watch on the domain's control/shutdown node, if any. + Idempotent. Nothrow guarantee. Expects to be protected by the + refresh_shutdown_lock.""" + + try: + try: + if self.shutdownWatch: + self.shutdownWatch.unwatch() + finally: + self.shutdownWatch = None + except: + log.exception("Unwatching control/shutdown failed.") + + def waitForShutdown(self): + self.state_updated.acquire() + try: + while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED): + self.state_updated.wait(timeout=1.0) + finally: + self.state_updated.release() + + # + # TODO: recategorise - called from XendCheckpoint + # + + def completeRestore(self, store_mfn, console_mfn): + + log.debug("XendDomainInfo.completeRestore") + + self.store_mfn = store_mfn + self.console_mfn = console_mfn + + self._introduceDomain() + self.image = image.create(self, self.info) + if self.image: + self.image.createDeviceModel(True) + self._storeDomDetails() + self._registerWatches() + self.refreshShutdown() + + log.debug("XendDomainInfo.completeRestore done") + + + def _endRestore(self): + self.setResume(False) + + # + # VM Destroy + # + + def _prepare_phantom_paths(self): + # get associated devices to destroy + # build list of phantom devices to be removed after normal devices + plist = [] + if self.domid is not None: + t = xstransact("%s/device/vbd" % GetDomainPath(self.domid)) + try: + for dev in t.list(): + backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \ + % (self.dompath, dev)) + if backend_phantom_vbd is not None: + frontend_phantom_vbd = xstransact.Read("%s/frontend" \ + % backend_phantom_vbd) + plist.append(backend_phantom_vbd) + plist.append(frontend_phantom_vbd) + finally: + t.abort() + return plist + + def _cleanup_phantom_devs(self, plist): + # remove phantom devices + if not plist == []: + time.sleep(2) + for paths in plist: + if paths.find('backend') != -1: + from xen.xend.server import DevController + # Modify online status /before/ updating state (latter is watched by + # drivers, so this ordering avoids a race). + xstransact.Write(paths, 'online', "0") + xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing'])) + # force + xstransact.Remove(paths) + + def destroy(self): + """Cleanup VM and destroy domain. Nothrow guarantee.""" + + if self.domid is None: + return + + from xen.xend import XendDomain + log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid)) + + paths = self._prepare_phantom_paths() + + if self.dompath is not None: + try: + xc.domain_destroy_hook(self.domid) + xc.domain_pause(self.domid) + do_FLR(self.domid) + xc.domain_destroy(self.domid) + for state in DOM_STATES_OLD: + self.info[state] = 0 + self._stateSet(DOM_STATE_HALTED) + except: + log.exception("XendDomainInfo.destroy: domain destruction failed.") + + XendDomain.instance().remove_domain(self) + self.cleanupDomain() + + self._cleanup_phantom_devs(paths) + self._cleanupVm() + + if "transient" in self.info["other_config"] \ + and bool(self.info["other_config"]["transient"]): + XendDomain.instance().domain_delete_by_dominfo(self) + + + def resetDomain(self): + log.debug("XendDomainInfo.resetDomain(%s)", str(self.domid)) + + old_domid = self.domid + prev_vm_xend = self._listRecursiveVm('xend') + new_dom_info = self.info + try: + self._unwatchVm() + self.destroy() + + new_dom = None + try: + from xen.xend import XendDomain + new_dom_info['domid'] = None + new_dom = XendDomain.instance().domain_create_from_dict( + new_dom_info) + for x in prev_vm_xend[0][1]: + new_dom._writeVm('xend/%s' % x[0], x[1]) + new_dom.waitForDevices() + new_dom.unpause() + except: + if new_dom: + new_dom.destroy() + raise + except: + log.exception('Failed to reset domain %s.', str(old_domid)) + + + def resumeDomain(self): + log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid)) + + # resume a suspended domain (e.g. after live checkpoint, or after + # a later error during save or migate); checks that the domain + # is currently suspended first so safe to call from anywhere + + xeninfo = dom_get(self.domid) + if xeninfo is None: + return + if not xeninfo['shutdown']: + return + reason = shutdown_reason(xeninfo['shutdown_reason']) + if reason != 'suspend': + return + + try: + # could also fetch a parsed note from xenstore + fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0 + if not fast: + self._releaseDevices() + self.testDeviceComplete() + self.testvifsComplete() + log.debug("XendDomainInfo.resumeDomain: devices released") + + self._resetChannels() + + self._removeDom('control/shutdown') + self._removeDom('device-misc/vif/nextDeviceID') + + self._createChannels() + self._introduceDomain() + self._storeDomDetails() + + self._createDevices() + log.debug("XendDomainInfo.resumeDomain: devices created") + + xc.domain_resume(self.domid, fast) + ResumeDomain(self.domid) + except: + log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid))) + self.image.resumeDeviceModel() + log.debug("XendDomainInfo.resumeDomain: completed") + + + # + # Channels for xenstore and console + # + + def _createChannels(self): + """Create the channels to the domain. + """ + self.store_port = self._createChannel() + self.console_port = self._createChannel() + + + def _createChannel(self): + """Create an event channel to the domain. + """ + try: + if self.domid != None: + return xc.evtchn_alloc_unbound(domid = self.domid, + remote_dom = 0) + except: + log.exception("Exception in alloc_unbound(%s)", str(self.domid)) + raise + + def _resetChannels(self): + """Reset all event channels in the domain. + """ + try: + if self.domid != None: + return xc.evtchn_reset(dom = self.domid) + except: + log.exception("Exception in evtcnh_reset(%s)", str(self.domid)) + raise + + + # + # Bootloader configuration + # + + def _configureBootloader(self): + """Run the bootloader if we're configured to do so.""" + + blexec = self.info['PV_bootloader'] + bootloader_args = self.info['PV_bootloader_args'] + kernel = self.info['PV_kernel'] + ramdisk = self.info['PV_ramdisk'] + args = self.info['PV_args'] + boot = self.info['HVM_boot_policy'] + + if boot: + # HVM booting. + pass + elif not blexec and kernel: + # Boot from dom0. Nothing left to do -- the kernel and ramdisk + # will be picked up by image.py. + pass + else: + # Boot using bootloader + if not blexec or blexec == 'pygrub': + blexec = osdep.pygrub_path + + blcfg = None + disks = [x for x in self.info['vbd_refs'] + if self.info['devices'][x][1]['bootable']] + + if not disks: + msg = "Had a bootloader specified, but no disks are bootable" + log.error(msg) + raise VmError(msg) + + devinfo = self.info['devices'][disks[0]] + devtype = devinfo[0] + disk = devinfo[1]['uname'] + + fn = blkdev_uname_to_file(disk) + taptype = blkdev_uname_to_taptype(disk) + mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev + if mounted: + # This is a file, not a device. pygrub can cope with a + # file if it's raw, but if it's QCOW or other such formats + # used through blktap, then we need to mount it first. + + log.info("Mounting %s on %s." % + (fn, BOOTLOADER_LOOPBACK_DEVICE)) + + vbd = { + 'mode': 'RO', + 'device': BOOTLOADER_LOOPBACK_DEVICE, + } + + from xen.xend import XendDomain + dom0 = XendDomain.instance().privilegedDomain() + dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk)) + fn = BOOTLOADER_LOOPBACK_DEVICE + + try: + blcfg = bootloader(blexec, fn, self, False, + bootloader_args, kernel, ramdisk, args) + finally: + if mounted: + log.info("Unmounting %s from %s." % + (fn, BOOTLOADER_LOOPBACK_DEVICE)) + + dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE) + + if blcfg is None: + msg = "Had a bootloader specified, but can't find disk" + log.error(msg) + raise VmError(msg) + + self.info.update_with_image_sxp(blcfg, True) + + + # + # VM Functions + # + + def _readVMDetails(self, params): + """Read the specified parameters from the store. + """ + try: + return self._gatherVm(*params) + except ValueError: + # One of the int/float entries in params has a corresponding store + # entry that is invalid. We recover, because older versions of + # Xend may have put the entry there (memory/target, for example), + # but this is in general a bad situation to have reached. + log.exception( + "Store corrupted at %s! Domain %d's configuration may be " + "affected.", self.vmpath, self.domid) + return [] + + def _cleanupVm(self): + """Cleanup VM resources. Idempotent. Nothrow guarantee.""" + + self._unwatchVm() + + try: + self._removeVm() + except: + log.exception("Removing VM path failed.") + + + def checkLiveMigrateMemory(self): + """ Make sure there's enough memory to migrate this domain """ + overhead_kb = 0 + if arch.type == "x86": + # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than + # the minimum that Xen would allocate if no value were given. + overhead_kb = self.info['VCPUs_max'] * 1024 + \ + (self.info['memory_static_max'] / 1024 / 1024) * 4 + overhead_kb = ((overhead_kb + 1023) / 1024) * 1024 + # The domain might already have some shadow memory + overhead_kb -= xc.shadow_mem_control(self.domid) * 1024 + if overhead_kb > 0: + balloon.free(overhead_kb) + + def _unwatchVm(self): + """Remove the watch on the VM path, if any. Idempotent. Nothrow + guarantee.""" + try: + try: + if self.vmWatch: + self.vmWatch.unwatch() + finally: + self.vmWatch = None + except: + log.exception("Unwatching VM path failed.") + + def testDeviceComplete(self): + """ For Block IO migration safety we must ensure that + the device has shutdown correctly, i.e. all blocks are + flushed to disk + """ + start = time.time() + while True: + test = 0 + diff = time.time() - start + for i in self.getDeviceController('vbd').deviceIDs(): + test = 1 + log.info("Dev %s still active, looping...", i) + time.sleep(0.1) + + if test == 0: + break + if diff >= MIGRATE_TIMEOUT: + log.info("Dev still active but hit max loop timeout") + break + + def testvifsComplete(self): + """ In case vifs are released and then created for the same + domain, we need to wait the device shut down. + """ + start = time.time() + while True: + test = 0 + diff = time.time() - start + for i in self.getDeviceController('vif').deviceIDs(): + test = 1 + log.info("Dev %s still active, looping...", i) + time.sleep(0.1) + + if test == 0: + break + if diff >= MIGRATE_TIMEOUT: + log.info("Dev still active but hit max loop timeout") + break + + def _storeVmDetails(self): + to_store = {} + + for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS: + info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key) + if self._infoIsSet(info_key): + to_store[key] = str(self.info[info_key]) + + if self._infoIsSet("static_memory_min"): + to_store["memory"] = str(self.info["static_memory_min"]) + if self._infoIsSet("static_memory_max"): + to_store["maxmem"] = str(self.info["static_memory_max"]) + + image_sxpr = self.info.image_sxpr() + if image_sxpr: + to_store['image'] = sxp.to_string(image_sxpr) + + if not self._readVm('xend/restart_count'): + to_store['xend/restart_count'] = str(0) + + log.debug("Storing VM details: %s", scrub_password(to_store)) + + self._writeVm(to_store) + self._setVmPermissions() + + def _setVmPermissions(self): + """Allow the guest domain to read its UUID. We don't allow it to + access any other entry, for security.""" + xstransact.SetPermissions('%s/uuid' % self.vmpath, + { 'dom' : self.domid, + 'read' : True, + 'write' : False }) + + # + # Utility functions + # + + def __getattr__(self, name): + if name == "state": + log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!") + log.warn("".join(traceback.format_stack())) + return self._stateGet() + else: + raise AttributeError(name) + + def __setattr__(self, name, value): + if name == "state": + log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!") + log.warn("".join(traceback.format_stack())) + self._stateSet(value) + else: + self.__dict__[name] = value + + def _stateSet(self, state): + self.state_updated.acquire() + try: + # TODO Not sure this is correct... + # _stateGet is live now. Why not fire event + # even when it hasn't changed? + if self._stateGet() != state: + self.state_updated.notifyAll() + import XendAPI + XendAPI.event_dispatch('mod', 'VM', self.info['uuid'], + 'power_state') + finally: + self.state_updated.release() + + def _stateGet(self): + # Lets try and reconsitute the state from xc + # first lets try and get the domain info + # from xc - this will tell us if the domain + # exists + info = dom_get(self.getDomid()) + if info is None or info['shutdown']: + # We are either HALTED or SUSPENDED + # check saved image exists + from xen.xend import XendDomain + managed_config_path = \ + XendDomain.instance()._managed_check_point_path( \ + self.get_uuid()) + if os.path.exists(managed_config_path): + return XEN_API_VM_POWER_STATE_SUSPENDED + else: + return XEN_API_VM_POWER_STATE_HALTED + elif info['crashed']: + # Crashed + return XEN_API_VM_POWER_STATE_CRASHED + else: + # We are either RUNNING or PAUSED + if info['paused']: + return XEN_API_VM_POWER_STATE_PAUSED + else: + return XEN_API_VM_POWER_STATE_RUNNING + + def _infoIsSet(self, name): + return name in self.info and self.info[name] is not None + + def _checkName(self, name): + """Check if a vm name is valid. Valid names contain alphabetic + characters, digits, or characters in '_-.:/+'. + The same name cannot be used for more than one vm at the same time. + + @param name: name + @raise: VmError if invalid + """ + from xen.xend import XendDomain + + if name is None or name == '': + raise VmError('Missing VM Name') + + if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name): + raise VmError('Invalid VM Name') + + dom = XendDomain.instance().domain_lookup_nr(name) + if dom and dom.info['uuid'] != self.info['uuid']: + raise VmError("VM name '%s' already exists%s" % + (name, + dom.domid is not None and + (" as domain %s" % str(dom.domid)) or "")) + + + def update(self, info = None, refresh = True, transaction = None): + """Update with info from xc.domain_getinfo(). + """ + log.trace("XendDomainInfo.update(%s) on domain %s", info, + str(self.domid)) + + if not info: + info = dom_get(self.domid) + if not info: + return + + if info["maxmem_kb"] < 0: + info["maxmem_kb"] = XendNode.instance() \ + .physinfo_dict()['total_memory'] * 1024 + + # make sure state is reset for info + # TODO: we should eventually get rid of old_dom_states + + self.info.update_config(info) + self._update_consoles(transaction) + + if refresh: + self.refreshShutdown(info) + + log.trace("XendDomainInfo.update done on domain %s: %s", + str(self.domid), self.info) + + def sxpr(self, ignore_store = False, legacy_only = True): + result = self.info.to_sxp(domain = self, + ignore_devices = ignore_store, + legacy_only = legacy_only) + + return result + + # Xen API + # ---------------------------------------------------------------- + + def get_uuid(self): + dom_uuid = self.info.get('uuid') + if not dom_uuid: # if it doesn't exist, make one up + dom_uuid = uuid.createString() + self.info['uuid'] = dom_uuid + return dom_uuid + + def get_memory_static_max(self): + return self.info.get('memory_static_max', 0) + def get_memory_static_min(self): + return self.info.get('memory_static_min', 0) + def get_memory_dynamic_max(self): + return self.info.get('memory_dynamic_max', 0) + def get_memory_dynamic_min(self): + return self.info.get('memory_dynamic_min', 0) + + # only update memory-related config values if they maintain sanity + def _safe_set_memory(self, key, newval): + oldval = self.info.get(key, 0) + try: + self.info[key] = newval + self.info._memory_sanity_check() + except Exception, ex: + self.info[key] = oldval + raise + + def set_memory_static_max(self, val): + self._safe_set_memory('memory_static_max', val) + def set_memory_static_min(self, val): + self._safe_set_memory('memory_static_min', val) + def set_memory_dynamic_max(self, val): + self._safe_set_memory('memory_dynamic_max', val) + def set_memory_dynamic_min(self, val): + self._safe_set_memory('memory_dynamic_min', val) + + def get_vcpus_params(self): + if self.getDomid() is None: + return self.info['vcpus_params'] + + retval = xc.sched_credit_domain_get(self.getDomid()) + return retval + def get_power_state(self): + return XEN_API_VM_POWER_STATE[self._stateGet()] + def get_platform(self): + return self.info.get('platform', {}) + def get_pci_bus(self): + return self.info.get('pci_bus', '') + def get_tools_version(self): + return self.info.get('tools_version', {}) + def get_metrics(self): + return self.metrics.get_uuid(); + + + def get_security_label(self, xspol=None): + import xen.util.xsm.xsm as security + label = security.get_security_label(self, xspol) + return label + + def set_security_label(self, seclab, old_seclab, xspol=None, + xspol_old=None): + """ + Set the security label of a domain from its old to + a new value. + @param seclab New security label formatted in the form + :: + @param old_seclab The current security label that the + VM must have. + @param xspol An optional policy under which this + update should be done. If not given, + then the current active policy is used. + @param xspol_old The old policy; only to be passed during + the updating of a policy + @return Returns return code, a string with errors from + the hypervisor's operation, old label of the + domain + """ + rc = 0 + errors = "" + old_label = "" + new_ssidref = 0 + domid = self.getDomid() + res_labels = None + is_policy_update = (xspol_old != None) + + from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + + state = self._stateGet() + # Relabel only HALTED or RUNNING or PAUSED domains + if domid != 0 and \ + state not in \ + [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \ + DOM_STATE_SUSPENDED ]: + log.warn("Relabeling domain not possible in state '%s'" % + DOM_STATES[state]) + return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0) + + # Remove security label. Works only for halted or suspended domains + if not seclab or seclab == "": + if state not in [ DOM_STATE_HALTED, DOM_STATE_SUSPENDED ]: + return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0) + + if self.info.has_key('security_label'): + old_label = self.info['security_label'] + # Check label against expected one. + if old_label != old_seclab: + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + del self.info['security_label'] + xen.xend.XendDomain.instance().managed_config_save(self) + return (xsconstants.XSERR_SUCCESS, "", "", 0) + + tmp = seclab.split(":") + if len(tmp) != 3: + return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0) + typ, policy, label = tmp + + poladmin = XSPolicyAdminInstance() + if not xspol: + xspol = poladmin.get_policy_by_name(policy) + + if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]: + #if domain is running or paused try to relabel in hypervisor + if not xspol: + return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0) + + if typ != xspol.get_type_name() or \ + policy != xspol.get_name(): + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + + if typ == xsconstants.ACM_POLICY_ID: + new_ssidref = xspol.vmlabel_to_ssidref(label) + if new_ssidref == xsconstants.INVALID_SSIDREF: + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + + # Check that all used resources are accessible under the + # new label + if not is_policy_update and \ + not security.resources_compatible_with_vmlabel(xspol, + self, label): + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + + #Check label against expected one. Can only do this + # if the policy hasn't changed underneath in the meantime + if xspol_old == None: + old_label = self.get_security_label() + if old_label != old_seclab: + log.info("old_label != old_seclab: %s != %s" % + (old_label, old_seclab)) + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + + # relabel domain in the hypervisor + rc, errors = security.relabel_domains([[domid, new_ssidref]]) + log.info("rc from relabeling in HV: %d" % rc) + else: + return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0) + + if rc == 0: + # HALTED, RUNNING or PAUSED + if domid == 0: + if xspol: + self.info['security_label'] = seclab + ssidref = poladmin.set_domain0_bootlabel(xspol, label) + else: + return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0) + else: + if self.info.has_key('security_label'): + old_label = self.info['security_label'] + # Check label against expected one, unless wildcard + if old_label != old_seclab: + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + + self.info['security_label'] = seclab + + try: + xen.xend.XendDomain.instance().managed_config_save(self) + except: + pass + return (rc, errors, old_label, new_ssidref) + + def get_on_shutdown(self): + after_shutdown = self.info.get('actions_after_shutdown') + if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT: + return XEN_API_ON_NORMAL_EXIT[-1] + return after_shutdown + + def get_on_reboot(self): + after_reboot = self.info.get('actions_after_reboot') + if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT: + return XEN_API_ON_NORMAL_EXIT[-1] + return after_reboot + + def get_on_suspend(self): + # TODO: not supported + after_suspend = self.info.get('actions_after_suspend') + if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT: + return XEN_API_ON_NORMAL_EXIT[-1] + return after_suspend + + def get_on_crash(self): + after_crash = self.info.get('actions_after_crash') + if not after_crash or after_crash not in \ + XEN_API_ON_CRASH_BEHAVIOUR + restart_modes: + return XEN_API_ON_CRASH_BEHAVIOUR[0] + return XEN_API_ON_CRASH_BEHAVIOUR_FILTER[after_crash] + + def get_dev_config_by_uuid(self, dev_class, dev_uuid): + """ Get's a device configuration either from XendConfig or + from the DevController. + + @param dev_class: device class, either, 'vbd' or 'vif' + @param dev_uuid: device UUID + + @rtype: dictionary + """ + dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None)) + + # shortcut if the domain isn't started because + # the devcontrollers will have no better information + # than XendConfig. + if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, + XEN_API_VM_POWER_STATE_SUSPENDED): + if dev_config: + return copy.deepcopy(dev_config) + return None + + # instead of using dev_class, we use the dev_type + # that is from XendConfig. + controller = self.getDeviceController(dev_type) + if not controller: + return None + + all_configs = controller.getAllDeviceConfigurations() + if not all_configs: + return None + + updated_dev_config = copy.deepcopy(dev_config) + for _devid, _devcfg in all_configs.items(): + if _devcfg.get('uuid') == dev_uuid: + updated_dev_config.update(_devcfg) + updated_dev_config['id'] = _devid + return updated_dev_config + + return updated_dev_config + + def get_dev_xenapi_config(self, dev_class, dev_uuid): + config = self.get_dev_config_by_uuid(dev_class, dev_uuid) + if not config: + return {} + + config['VM'] = self.get_uuid() + + if dev_class == 'vif': + if not config.has_key('name'): + config['name'] = config.get('vifname', '') + if not config.has_key('MAC'): + config['MAC'] = config.get('mac', '') + if not config.has_key('type'): + config['type'] = 'paravirtualised' + if not config.has_key('device'): + devid = config.get('id') + if devid != None: + config['device'] = 'eth%s' % devid + else: + config['device'] = '' + + if not config.has_key('network'): + try: + bridge = config.get('bridge', None) + if bridge is None: + from xen.util import Brctl + if_to_br = dict([(i,b) + for (b,ifs) in Brctl.get_state().items() + for i in ifs]) + vifname = "vif%s.%s" % (self.getDomid(), + config.get('id')) + bridge = if_to_br.get(vifname, None) + config['network'] = \ + XendNode.instance().bridge_to_network( + config.get('bridge')).get_uuid() + except Exception: + log.exception('bridge_to_network') + # Ignore this for now -- it may happen if the device + # has been specified using the legacy methods, but at + # some point we're going to have to figure out how to + # handle that properly. + + config['MTU'] = 1500 # TODO + + if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,): + xennode = XendNode.instance() + rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid) + config['io_read_kbs'] = rx_bps/1024 + config['io_write_kbs'] = tx_bps/1024 + rx, tx = xennode.get_vif_stat(self.domid, devid) + config['io_total_read_kbs'] = rx/1024 + config['io_total_write_kbs'] = tx/1024 + else: + config['io_read_kbs'] = 0.0 + config['io_write_kbs'] = 0.0 + config['io_total_read_kbs'] = 0.0 + config['io_total_write_kbs'] = 0.0 + + config['security_label'] = config.get('security_label', '') + + if dev_class == 'vbd': + + if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,): + controller = self.getDeviceController(dev_class) + devid, _1, _2 = controller.getDeviceDetails(config) + xennode = XendNode.instance() + rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid) + config['io_read_kbs'] = rd_blkps + config['io_write_kbs'] = wr_blkps + else: + config['io_read_kbs'] = 0.0 + config['io_write_kbs'] = 0.0 + + config['VDI'] = config.get('VDI', '') + config['device'] = config.get('dev', '') + if ':' in config['device']: + vbd_name, vbd_type = config['device'].split(':', 1) + config['device'] = vbd_name + if vbd_type == 'cdrom': + config['type'] = XEN_API_VBD_TYPE[0] + else: + config['type'] = XEN_API_VBD_TYPE[1] + + config['driver'] = 'paravirtualised' # TODO + config['image'] = config.get('uname', '') + + if config.get('mode', 'r') == 'r': + config['mode'] = 'RO' + else: + config['mode'] = 'RW' + + if dev_class == 'vtpm': + if not config.has_key('type'): + config['type'] = 'paravirtualised' # TODO + if not config.has_key('backend'): + config['backend'] = "00000000-0000-0000-0000-000000000000" + + return config + + def get_dev_property(self, dev_class, dev_uuid, field): + config = self.get_dev_xenapi_config(dev_class, dev_uuid) + try: + return config[field] + except KeyError: + raise XendError('Invalid property for device: %s' % field) + + def set_dev_property(self, dev_class, dev_uuid, field, value): + self.info['devices'][dev_uuid][1][field] = value + + def get_vcpus_util(self): + vcpu_util = {} + xennode = XendNode.instance() + if 'VCPUs_max' in self.info and self.domid != None: + for i in range(0, self.info['VCPUs_max']): + util = xennode.get_vcpu_util(self.domid, i) + vcpu_util[str(i)] = util + + return vcpu_util + + def get_consoles(self): + return self.info.get('console_refs', []) + + def get_vifs(self): + return self.info.get('vif_refs', []) + + def get_vbds(self): + return self.info.get('vbd_refs', []) + + def get_vtpms(self): + return self.info.get('vtpm_refs', []) + + def get_dpcis(self): + return XendDPCI.get_by_VM(self.info.get('uuid')) + + def get_dscsis(self): + return XendDSCSI.get_by_VM(self.info.get('uuid')) + + def create_vbd(self, xenapi_vbd, vdi_image_path): + """Create a VBD using a VDI from XendStorageRepository. + + @param xenapi_vbd: vbd struct from the Xen API + @param vdi_image_path: VDI UUID + @rtype: string + @return: uuid of the device + """ + xenapi_vbd['image'] = vdi_image_path + if vdi_image_path.startswith('tap'): + dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd) + else: + dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd) + + if not dev_uuid: + raise XendError('Failed to create device') + + if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING, + XEN_API_VM_POWER_STATE_PAUSED): + _, config = self.info['devices'][dev_uuid] + + if vdi_image_path.startswith('tap'): + dev_control = self.getDeviceController('tap') + else: + dev_control = self.getDeviceController('vbd') + + try: + devid = dev_control.createDevice(config) + dev_control.waitForDevice(devid) + self.info.device_update(dev_uuid, + cfg_xenapi = {'devid': devid}) + except Exception, exn: + log.exception(exn) + del self.info['devices'][dev_uuid] + self.info['vbd_refs'].remove(dev_uuid) + raise + + return dev_uuid + + def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path): + """Create a VBD using a VDI from XendStorageRepository. + + @param xenapi_vbd: vbd struct from the Xen API + @param vdi_image_path: VDI UUID + @rtype: string + @return: uuid of the device + """ + xenapi_vbd['image'] = vdi_image_path + dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd) + if not dev_uuid: + raise XendError('Failed to create device') + + if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING: + _, config = self.info['devices'][dev_uuid] + config['devid'] = self.getDeviceController('tap').createDevice(config) + + return config['devid'] + + def create_vif(self, xenapi_vif): + """Create VIF device from the passed struct in Xen API format. + + @param xenapi_vif: Xen API VIF Struct. + @rtype: string + @return: UUID + """ + dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif) + if not dev_uuid: + raise XendError('Failed to create device') + + if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING, + XEN_API_VM_POWER_STATE_PAUSED): + + _, config = self.info['devices'][dev_uuid] + dev_control = self.getDeviceController('vif') + + try: + devid = dev_control.createDevice(config) + dev_control.waitForDevice(devid) + self.info.device_update(dev_uuid, + cfg_xenapi = {'devid': devid}) + except Exception, exn: + log.exception(exn) + del self.info['devices'][dev_uuid] + self.info['vif_refs'].remove(dev_uuid) + raise + + return dev_uuid + + def create_vtpm(self, xenapi_vtpm): + """Create a VTPM device from the passed struct in Xen API format. + + @return: uuid of the device + @rtype: string + """ + + if self._stateGet() not in (DOM_STATE_HALTED,): + raise VmError("Can only add vTPM to a halted domain.") + if self.get_vtpms() != []: + raise VmError('Domain already has a vTPM.') + dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm) + if not dev_uuid: + raise XendError('Failed to create device') + + return dev_uuid + + def create_console(self, xenapi_console): + """ Create a console device from a Xen API struct. + + @return: uuid of device + @rtype: string + """ + if self._stateGet() not in (DOM_STATE_HALTED,): + raise VmError("Can only add console to a halted domain.") + + dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console) + if not dev_uuid: + raise XendError('Failed to create device') + + return dev_uuid + + def set_console_other_config(self, console_uuid, other_config): + self.info.console_update(console_uuid, 'other_config', other_config) + + def create_dpci(self, xenapi_pci): + """Create pci device from the passed struct in Xen API format. + + @param xenapi_pci: DPCI struct from Xen API + @rtype: bool + #@rtype: string + @return: True if successfully created device + #@return: UUID + """ + + dpci_uuid = uuid.createString() + + # Convert xenapi to sxp + ppci = XendAPIStore.get(xenapi_pci.get('PPCI'), 'PPCI') + + target_pci_sxp = \ + ['pci', + ['dev', + ['domain', '0x%02x' % ppci.get_domain()], + ['bus', '0x%02x' % ppci.get_bus()], + ['slot', '0x%02x' % ppci.get_slot()], + ['func', '0x%1x' % ppci.get_func()], + ['vslt', '0x%02x' % xenapi_pci.get('hotplug_slot')], + ['uuid', dpci_uuid] + ], + ['state', 'Initialising'] + ] + + if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING: + + old_pci_sxp = self._getDeviceInfo_pci(0) + + if old_pci_sxp is None: + dev_uuid = self.info.device_add('pci', cfg_sxp = target_pci_sxp) + if not dev_uuid: + raise XendError('Failed to create device') + + else: + new_pci_sxp = ['pci'] + for existing_dev in sxp.children(old_pci_sxp, 'dev'): + new_pci_sxp.append(existing_dev) + new_pci_sxp.append(sxp.child0(target_pci_sxp, 'dev')) + + dev_uuid = sxp.child_value(old_pci_sxp, 'uuid') + self.info.device_update(dev_uuid, new_pci_sxp) + + xen.xend.XendDomain.instance().managed_config_save(self) + + else: + try: + self.device_configure(target_pci_sxp) + + except Exception, exn: + raise XendError('Failed to create device') + + return dpci_uuid + + def create_dscsi(self, xenapi_dscsi): + """Create scsi device from the passed struct in Xen API format. + + @param xenapi_dscsi: DSCSI struct from Xen API + @rtype: string + @return: UUID + """ + + dscsi_uuid = uuid.createString() + + # Convert xenapi to sxp + pscsi = XendAPIStore.get(xenapi_dscsi.get('PSCSI'), 'PSCSI') + devid = int(xenapi_dscsi.get('virtual_HCTL').split(':')[0]) + target_vscsi_sxp = \ + ['vscsi', + ['dev', + ['devid', devid], + ['p-devname', pscsi.get_dev_name()], + ['p-dev', pscsi.get_physical_HCTL()], + ['v-dev', xenapi_dscsi.get('virtual_HCTL')], + ['state', 'Initialising'], + ['uuid', dscsi_uuid] + ] + ] + + if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING: + + cur_vscsi_sxp = self._getDeviceInfo_vscsi(devid, None) + + if cur_vscsi_sxp is None: + dev_uuid = self.info.device_add('vscsi', cfg_sxp = target_vscsi_sxp) + if not dev_uuid: + raise XendError('Failed to create device') + + else: + new_vscsi_sxp = ['vscsi'] + for existing_dev in sxp.children(cur_vscsi_sxp, 'dev'): + new_vscsi_sxp.append(existing_dev) + new_vscsi_sxp.append(sxp.child0(target_vscsi_sxp, 'dev')) + + dev_uuid = sxp.child_value(cur_vscsi_sxp, 'uuid') + self.info.device_update(dev_uuid, new_vscsi_sxp) + + xen.xend.XendDomain.instance().managed_config_save(self) + + else: + try: + self.device_configure(target_vscsi_sxp) + + except Exception, exn: + raise XendError('Failed to create device') + + return dscsi_uuid + + + def destroy_device_by_uuid(self, dev_type, dev_uuid): + if dev_uuid not in self.info['devices']: + raise XendError('Device does not exist') + + try: + if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING, + XEN_API_VM_POWER_STATE_PAUSED): + _, config = self.info['devices'][dev_uuid] + devid = config.get('devid') + if devid != None: + self.getDeviceController(dev_type).destroyDevice(devid, force = False) + else: + raise XendError('Unable to get devid for device: %s:%s' % + (dev_type, dev_uuid)) + finally: + del self.info['devices'][dev_uuid] + self.info['%s_refs' % dev_type].remove(dev_uuid) + + def destroy_vbd(self, dev_uuid): + self.destroy_device_by_uuid('vbd', dev_uuid) + + def destroy_vif(self, dev_uuid): + self.destroy_device_by_uuid('vif', dev_uuid) + + def destroy_vtpm(self, dev_uuid): + self.destroy_device_by_uuid('vtpm', dev_uuid) + + def destroy_dpci(self, dev_uuid): + + dpci = XendAPIStore.get(dev_uuid, 'DPCI') + ppci = XendAPIStore.get(dpci.get_PPCI(), 'PPCI') + + old_pci_sxp = self._getDeviceInfo_pci(0) + dev_uuid = sxp.child_value(old_pci_sxp, 'uuid') + target_dev = None + new_pci_sxp = ['pci'] + for dev in sxp.children(old_pci_sxp, 'dev'): + domain = int(sxp.child_value(dev, 'domain'), 16) + bus = int(sxp.child_value(dev, 'bus'), 16) + slot = int(sxp.child_value(dev, 'slot'), 16) + func = int(sxp.child_value(dev, 'func'), 16) + name = "%04x:%02x:%02x.%01x" % (domain, bus, slot, func) + if ppci.get_name() == name: + target_dev = dev + else: + new_pci_sxp.append(dev) + + if target_dev is None: + raise XendError('Failed to destroy device') + + target_pci_sxp = ['pci', target_dev, ['state', 'Closing']] + + if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING: + + self.info.device_update(dev_uuid, new_pci_sxp) + if len(sxp.children(new_pci_sxp, 'dev')) == 0: + del self.info['devices'][dev_uuid] + xen.xend.XendDomain.instance().managed_config_save(self) + + else: + try: + self.device_configure(target_pci_sxp) + + except Exception, exn: + raise XendError('Failed to destroy device') + + def destroy_dscsi(self, dev_uuid): + dscsi = XendAPIStore.get(dev_uuid, 'DSCSI') + devid = dscsi.get_virtual_host() + vHCTL = dscsi.get_virtual_HCTL() + cur_vscsi_sxp = self._getDeviceInfo_vscsi(devid, None) + dev_uuid = sxp.child_value(cur_vscsi_sxp, 'uuid') + + target_dev = None + new_vscsi_sxp = ['vscsi'] + for dev in sxp.children(cur_vscsi_sxp, 'dev'): + if vHCTL == sxp.child_value(dev, 'v-dev'): + target_dev = dev + else: + new_vscsi_sxp.append(dev) + + if target_dev is None: + raise XendError('Failed to destroy device') + + target_dev.append(['state', 'Closing']) + target_vscsi_sxp = ['vscsi', target_dev] + + if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING: + + self.info.device_update(dev_uuid, new_vscsi_sxp) + if len(sxp.children(new_vscsi_sxp, 'dev')) == 0: + del self.info['devices'][dev_uuid] + xen.xend.XendDomain.instance().managed_config_save(self) + + else: + try: + self.device_configure(target_vscsi_sxp) + + except Exception, exn: + raise XendError('Failed to destroy device') + + def destroy_xapi_instances(self): + """Destroy Xen-API instances stored in XendAPIStore. + """ + # Xen-API classes based on XendBase have their instances stored + # in XendAPIStore. Cleanup these instances here, if they are supposed + # to be destroyed when the parent domain is dead. + # + # Most of the virtual devices (vif, vbd, vfb, etc) are not based on + # XendBase and there's no need to remove them from XendAPIStore. + + from xen.xend import XendDomain + if XendDomain.instance().is_valid_vm(self.info.get('uuid')): + # domain still exists. + return + + # Destroy the VMMetrics instance. + if XendAPIStore.get(self.metrics.get_uuid(), self.metrics.getClass()) \ + is not None: + self.metrics.destroy() + + # Destroy DPCI instances. + for dpci_uuid in XendDPCI.get_by_VM(self.info.get('uuid')): + XendAPIStore.deregister(dpci_uuid, "DPCI") + + # Destroy DSCSI instances. + for dscsi_uuid in XendDSCSI.get_by_VM(self.info.get('uuid')): + XendAPIStore.deregister(dscsi_uuid, "DSCSI") + + def has_device(self, dev_class, dev_uuid): + return (dev_uuid in self.info['%s_refs' % dev_class.lower()]) + + def __str__(self): + return '' % \ + (str(self.domid), self.info['name_label'], + str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()]) + + __repr__ = __str__ + diff --git a/tools/python/xen/xend/XendError.py b/tools/python/xen/xend/XendError.py new file mode 100644 index 0000000..c0b0076 --- /dev/null +++ b/tools/python/xen/xend/XendError.py @@ -0,0 +1,220 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (c) 2006, 2007 XenSource Inc. +#============================================================================ + +from xmlrpclib import Fault + +import XendClient + +class XendInvalidDomain(Fault): + def __init__(self, value): + Fault.__init__(self, XendClient.ERROR_INVALID_DOMAIN, value) + +class XendError(Fault): + + def __init__(self, value): + Fault.__init__(self, XendClient.ERROR_GENERIC, value) + self.value = value + + def __str__(self): + return self.value + +class VMBadState(XendError): + def __init__(self, value, expected, actual): + XendError.__init__(self, value) + self.expected = expected + self.actual = actual + +class NetworkAlreadyConnected(XendError): + def __init__(self, pif_uuid): + XendError.__init__(self, 'Network already connected') + self.pif_uuid = pif_uuid + +class PIFIsPhysical(XendError): + def __init__(self): + XendError.__init__(self, 'PIF is physical') + +class VmError(XendError): + """Vm construction error.""" + pass + +class HVMRequired(VmError): + def __init__(self): + XendError.__init__(self, + 'HVM guest support is unavailable: is VT/AMD-V ' + 'supported by your CPU and enabled in your BIOS?') + +class XendAPIError(XendError): + """Extend this class for all error thrown by + autoplugged classes""" + def __init__(self): + XendError.__init__(self, 'XendAPI Error: You should never see this' + ' message; this class need to be overidden') + + def get_api_error(self): + return ['INTERNAL_ERROR', 'You should never see this message; ' + 'this method needs to be overidden'] + +class CreateUnspecifiedAttributeError(XendAPIError): + def __init__(self, attr_name, class_name): + XendAPIError.__init__(self) + self.attr_name = attr_name + self.class_name = class_name + + def get_api_error(self): + return ['CREATE_UNSPECIFIED_ATTRIBUTE', self.attr_name, + self.class_name] + + def __str__(self): + return "CREATE_UNSPECIFIED_ATTRIBUTE: %s, %s" % (self.attr_name, + self.class_name) + +class UnmanagedNetworkError(XendAPIError): + def __init__(self, attr_name): + XendAPIError.__init__(self) + self.attr_name = attr_name + + def get_api_error(self): + return ['UNMANAGED_NETWORK_ERROR', self.attr_name] + + def __str__(self): + return "UNMANAGED_NETWORK_ERROR: %s" % self.attr_name + +class UniqueNameError(XendAPIError): + def __init__(self, name, class_name): + XendAPIError.__init__(self) + self.name = name + self.class_name = class_name + + def get_api_error(self): + return ['UNIQUE_NAME_ERROR', self.name, self.class_name] + + def __str__(self): + return 'UNIQUE_NAME_ERROR: %s, %s' % (self.name, self.class_name) + +class InvalidDeviceError(XendAPIError): + def __init__(self, dev): + XendAPIError.__init__(self) + self.dev = dev + + def get_api_error(self): + return ['INVALID_DEVICE_ERROR', self.dev] + + def __str__(self): + return 'INVALID_DEVICE_ERROR: %s' % self.dev + +class DeviceExistsError(XendAPIError): + def __init__(self, dev): + XendAPIError.__init__(self) + self.dev = dev + + def get_api_error(self): + return ['DEVICE_EXISTS_ERROR', self.dev] + + def __str__(self): + return 'DEVICE_EXISTS_ERROR: %s' % self.dev + +class InvalidHandleError(XendAPIError): + def __init__(self, klass, handle): + XendAPIError.__init__(self) + self.klass = klass + self.handle = handle + + def get_api_error(self): + return ['HANDLE_INVALID', self.klass, self.handle] + + def __str__(self): + return 'HANDLE_INVALID: %s %s' % (self.klass, self.handle) + +class ImplementationError(XendAPIError): + def __init__(self, klass, func): + XendAPIError.__init__(self) + self.klass = klass + self.func = func + + def get_api_error(self): + return ['IMPLEMENTATION_ERROR', self.klass, self.func] + + def __str__(self): + return 'IMPLEMENTATION_ERROR: %s %s' % (self.klass, self.func) + +class VLANTagInvalid(XendAPIError): + def __init__(self, vlan): + XendAPIError.__init__(self) + self.vlan = vlan + + def get_api_error(self): + return ['VLAN_TAG_INVALID', self.vlan] + + def __str__(self): + return 'VLAN_TAG_INVALID: %s' % self.vlan + +class NetworkError(XendAPIError): + def __init__(self, error, network): + XendAPIError.__init__(self) + self.network = network + self.error = error + + def get_api_error(self): + return ['NETWORK_ERROR', self.error, self.network] + + def __str__(self): + return 'NETWORK_ERROR: %s %s' % (self.error, self.network) + +class DirectPCIError(XendAPIError): + def __init__(self, error): + XendAPIError.__init__(self) + self.error = error + + def get_api_error(self): + return ['DIRECT_PCI_ERROR', self.error] + + def __str__(self): + return 'DIRECT_PCI_ERROR: %s' % self.error + +from xen.util.xsconstants import xserr2string + +class SecurityError(XendAPIError): + def __init__(self, error, message=None): + XendAPIError.__init__(self) + self.error = error + if not message: + self.message = xserr2string(-error) + else: + self.message = message + + def get_api_error(self): + return ['SECURITY_ERROR', self.error, self.message] + + def __str__(self): + return 'SECURITY_ERROR: %s:%s' % (self.error, self.message) + +XEND_ERROR_AUTHENTICATION_FAILED = ('ELUSER', 'Authentication Failed') +XEND_ERROR_SESSION_INVALID = ('EPERMDENIED', 'Session Invalid') +XEND_ERROR_DOMAIN_INVALID = ('EINVALIDDOMAIN', 'Domain Invalid') +XEND_ERROR_HOST_INVALID = ('EINVALIDHOST', 'Host Invalid') +XEND_ERROR_HOST_RUNNING = ('EHOSTRUNNING', 'Host is still Running') +XEND_ERROR_HOST_CPU_INVALID = ('EHOSTCPUINVALID', 'Host CPU Invalid') +XEND_ERROR_UNSUPPORTED = ('EUNSUPPORTED', 'Method Unsupported') +XEND_ERROR_VM_INVALID = ('EVMINVALID', 'VM Invalid') +XEND_ERROR_VBD_INVALID = ('EVBDINVALID', 'VBD Invalid') +XEND_ERROR_VIF_INVALID = ('EVIFINVALID', 'VIF Invalid') +XEND_ERROR_VTPM_INVALID = ('EVTPMINVALID', 'VTPM Invalid') +XEND_ERROR_VDI_INVALID = ('EVDIINVALID', 'VDI Invalid') +XEND_ERROR_SR_INVALID = ('ESRINVALID', 'SR Invalid') +XEND_ERROR_XSPOLICY_INVALID = ('EXSPOLICYINVALID', 'XS Invalid') +XEND_ERROR_TODO = ('ETODO', 'Lazy Programmer Error') diff --git a/tools/python/xen/xend/XendLocalStorageRepo.py b/tools/python/xen/xend/XendLocalStorageRepo.py new file mode 100644 index 0000000..31b86f6 --- /dev/null +++ b/tools/python/xen/xend/XendLocalStorageRepo.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 XenSource Ltd. +#============================================================================ +# +# A pseudo-StorageRepository to provide a representation for the images +# that can be specified by xm. +# + +import commands +import logging +import os +import stat +import threading +import re +import sys +import struct + +from xen.util import mkdir +import uuid +from XendError import XendError +from XendVDI import * +from XendTask import XendTask +from XendStorageRepository import XendStorageRepository +from XendStateStore import XendStateStore +from XendOptions import instance as xendoptions + +MB = 1024 * 1024 + +log = logging.getLogger("xend.XendLocalStorageRepo") + +class XendLocalStorageRepo(XendStorageRepository): + """A backwards compatibility storage repository so that + traditional file:/dir/file.img and phy:/dev/hdxx images can + still be represented in terms of the Xen API. + """ + + def __init__(self, sr_uuid, sr_type = 'local', + name_label = 'Local', + name_description = 'Traditional Local Storage Repo'): + """ + @ivar images: mapping of all the images. + @type images: dictionary by image uuid. + @ivar lock: lock to provide thread safety. + """ + + XendStorageRepository.__init__(self, sr_uuid, sr_type, + name_label, name_description) + + self.state = XendStateStore(xendoptions().get_xend_state_path() + + '/local_sr') + + stored_images = self.state.load_state('vdi') + if stored_images: + for image_uuid, image in stored_images.items(): + self.images[image_uuid] = XendLocalVDI(image) + + def create_vdi(self, vdi_struct): + """ Creates a fake VDI image for a traditional image string. + + The image uri is stored in the attribute 'uri' + """ + vdi_struct['uuid'] = uuid.createString() + vdi_struct['SR'] = self.uuid + new_image = XendLocalVDI(vdi_struct) + self.images[new_image.uuid] = new_image + self.save_state() + return new_image.uuid + + def save_state(self): + vdi_records = dict([(k, v.get_record(transient = False)) + for k, v in self.images.items()]) + self.state.save_state('vdi', vdi_records) + + def destroy_vdi(self, vdi_uuid): + if vdi_uuid in self.images: + del self.images[vdi_uuid] + + self.save_state() + diff --git a/tools/python/xen/xend/XendLogging.py b/tools/python/xen/xend/XendLogging.py new file mode 100644 index 0000000..d95133b --- /dev/null +++ b/tools/python/xen/xend/XendLogging.py @@ -0,0 +1,149 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005, 2006 XenSource Ltd. +#============================================================================ + +import inspect +import os +import os.path +import sys +import stat +import tempfile +import types +import logging +import logging.handlers + +from xen.util import mkdir +from xen.xend.server import params +from xen.util import oshelp + + +__all__ = [ 'log', 'init', 'getLogFilename' ] + + +if 'TRACE' not in logging.__dict__: + logging.TRACE = logging.DEBUG - 1 + logging.addLevelName(logging.TRACE,'TRACE') + def trace(self, *args, **kwargs): + self.log(logging.TRACE, *args, **kwargs) + logging.Logger.trace = trace + + def findCaller(self): + """ + Override logging.Logger.findCaller so that the above trace function + does not appear as the source of log messages. The signature of this + function changed between Python 2.3 and 2.4. + """ + frames = inspect.stack() + thisfile = os.path.normcase(frames[0][1]) + for frame in frames: + filename = os.path.normcase(frame[1]) + if filename != thisfile and filename != logging._srcfile: + major, minor, micro, _, _ = sys.version_info + if (major, minor, micro) >= (2, 4, 2): + return filename, frame[2], frame[3] + else: + return filename, frame[2] + logging.Logger.findCaller = findCaller + + # Work around a bug in Python's inspect module: findsource is supposed to + # raise IOError if it fails, with other functions in that module coping + # with that, but some people are seeing IndexError raised from there. + # This is Python bug 1628987. http://python.org/sf/1628987. + if hasattr(inspect, 'findsource'): + real_findsource = getattr(inspect, 'findsource') + def findsource(*args, **kwargs): + try: + return real_findsource(*args, **kwargs) + except IndexError, exn: + raise IOError(exn) + inspect.findsource = findsource + + +log = logging.getLogger("xend") + + +MAX_BYTES = 1 << 20 # 1MB +BACKUP_COUNT = 5 + +STDERR_FORMAT = "[%(name)s] %(levelname)s (%(module)s:%(lineno)d) %(message)s" +LOGFILE_FORMAT = "[%(asctime)s %(process)d] %(levelname)s (%(module)s:%(lineno)d) %(message)s" +DATE_FORMAT = "%Y-%m-%d %H:%M:%S" + + +logfilename = None + +class XendRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def __init__(self, fname, mode, maxBytes, backupCount): + logging.handlers.RotatingFileHandler.__init__(self, fname, mode, maxBytes, backupCount) + self.setCloseOnExec() + + def doRollover(self): + logging.handlers.RotatingFileHandler.doRollover(self) + self.setCloseOnExec() + + # NB yes accessing 'self.stream' violates OO encapsulation somewhat, + # but python logging API gives no other way to access the file handle + # and the entire python logging stack is already full of OO encapsulation + # violations. The other alternative is copy-and-paste duplicating the + # entire FileHandler, StreamHandler & RotatingFileHandler classes which + # is even worse + def setCloseOnExec(self): + oshelp.fcntl_setfd_cloexec(self.stream, True) + + +def init(filename, level): + """Initialise logging. Logs to the given filename, and logs to stderr if + XEND_DEBUG is set. + """ + + global logfilename + + def openFileHandler(fname): + mkdir.parents(os.path.dirname(fname), stat.S_IRWXU) + return XendRotatingFileHandler(fname, mode = 'a', + maxBytes = MAX_BYTES, + backupCount = BACKUP_COUNT) + + # Rather unintuitively, getLevelName will get the number corresponding to + # a level name, as well as getting the name corresponding to a level + # number. setLevel seems to take the number only though, so convert if we + # are given a string. + if isinstance(level, types.StringType): + level = logging.getLevelName(level) + + log.setLevel(level) + + try: + fileHandler = openFileHandler(filename) + logfilename = filename + except IOError: + logfilename = tempfile.mkstemp("-xend.log")[1] + fileHandler = openFileHandler(logfilename) + + fileHandler.setFormatter(logging.Formatter(LOGFILE_FORMAT, DATE_FORMAT)) + log.addHandler(fileHandler) + + if params.XEND_DEBUG: + stderrHandler = logging.StreamHandler() + stderrHandler.setFormatter(logging.Formatter(STDERR_FORMAT, + DATE_FORMAT)) + log.addHandler(stderrHandler) + + +def getLogFilename(): + return logfilename diff --git a/tools/python/xen/xend/XendMonitor.py b/tools/python/xen/xend/XendMonitor.py new file mode 100644 index 0000000..83fcb18 --- /dev/null +++ b/tools/python/xen/xend/XendMonitor.py @@ -0,0 +1,340 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 XenSource Ltd. +#============================================================================ + +from xen.lowlevel.xc import xc +import time +import threading +import os +import re + +"""Monitoring thread to keep track of Xend statistics. """ + +VBD_SYSFS_PATH = '/sys/devices/xen-backend/' +VBD_WR_PATH = VBD_SYSFS_PATH + '%s/statistics/wr_sect' +VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_sect' +VBD_DOMAIN_RE = r'vbd-(?P\d+)-(?P\d+)$' + +NET_PROCFS_PATH = '/proc/net/dev' +PROC_NET_DEV_RE = r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s+' \ + r'(?P\d+)\s*$' + + +VIF_DOMAIN_RE = re.compile(r'vif(?P\d+)\.(?P\d+):\s*' + + PROC_NET_DEV_RE) +PIF_RE = re.compile(r'^\s*(?Ppeth\d+):\s*' + PROC_NET_DEV_RE) + +# Interval to poll xc, sysfs and proc +POLL_INTERVAL = 2.0 +SECTOR_SIZE = 512 +class XendMonitor(threading.Thread): + """Monitors VCPU, VBD, VIF and PIF statistics for Xen API. + + Polls sysfs and procfs for statistics on VBDs and VIFs respectively. + + @ivar domain_vcpus_util: Utilisation for VCPUs indexed by domain + @type domain_vcpus_util: {domid: {vcpuid: float, vcpuid: float}} + @ivar domain_vifs_util: Bytes per second for VIFs indexed by domain + @type domain_vifs_util: {domid: {vifid: (rx_bps, tx_bps)}} + @ivar domain_vifs_stat: Total amount of bytes used for VIFs indexed by domain + @type domain_vifs_stat: {domid: {vbdid: (rx, tx)}} + @ivar domain_vbds_util: Blocks per second for VBDs index by domain. + @type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}} + + """ + def __init__(self): + threading.Thread.__init__(self) + self.setDaemon(True) + self.xc = xc() + + self.lock = threading.Lock() + + # tracks the last polled statistics + self._domain_vcpus = {} + self._domain_vifs = {} + self._domain_vbds = {} + self.pifs = {} + + # instantaneous statistics + self._domain_vcpus_util = {} + self._domain_vifs_util = {} + self._domain_vifs_stat = {} + self._domain_vbds_util = {} + self.pifs_util = {} + + def get_domain_vcpus_util(self): + self.lock.acquire() + try: + return self._domain_vcpus_util + finally: + self.lock.release() + + def get_domain_vbds_util(self): + self.lock.acquire() + try: + return self._domain_vbds_util + finally: + self.lock.release() + + def get_domain_vifs_util(self): + self.lock.acquire() + try: + return self._domain_vifs_util + finally: + self.lock.release() + + def get_domain_vifs_stat(self): + self.lock.acquire() + try: + return self._domain_vifs_stat + finally: + self.lock.release() + + def get_pifs_util(self): + self.lock.acquire() + try: + return self.pifs_util + finally: + self.lock.release() + + def _get_vif_stats(self): + stats = {} + + if not os.path.exists(NET_PROCFS_PATH): + return stats + + usage_at = time.time() + for line in open(NET_PROCFS_PATH): + is_vif = re.search(VIF_DOMAIN_RE, line.strip()) + if not is_vif: + continue + + domid = int(is_vif.group('domid')) + vifid = int(is_vif.group('iface')) + rx_bytes = int(is_vif.group('rx_bytes')) + tx_bytes = int(is_vif.group('tx_bytes')) + if not domid in stats: + stats[domid] = {} + + stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes) + + return stats + + def _get_pif_stats(self): + stats = {} + + if not os.path.exists(NET_PROCFS_PATH): + return stats + + usage_at = time.time() + for line in open(NET_PROCFS_PATH): + is_pif = re.search(PIF_RE, line.strip()) + if not is_pif: + continue + + pifname = is_pif.group('iface') + rx_bytes = int(is_pif.group('rx_bytes')) + tx_bytes = int(is_pif.group('tx_bytes')) + stats[pifname] = (usage_at, rx_bytes, tx_bytes) + + return stats + + def _get_vbd_stats(self): + stats = {} + + if not os.path.exists(VBD_SYSFS_PATH): + return stats + + for vbd_path in os.listdir(VBD_SYSFS_PATH): + is_vbd = re.search(VBD_DOMAIN_RE, vbd_path) + if not is_vbd: + continue + + domid = int(is_vbd.group('domid')) + vbdid = int(is_vbd.group('devid')) + rd_stat_path = VBD_RD_PATH % vbd_path + wr_stat_path = VBD_WR_PATH % vbd_path + + if not os.path.exists(rd_stat_path) or \ + not os.path.exists(wr_stat_path): + continue + + + try: + usage_at = time.time() + rd_stat = int(open(rd_stat_path).readline().strip()) + wr_stat = int(open(wr_stat_path).readline().strip()) + rd_stat *= SECTOR_SIZE + wr_stat *= SECTOR_SIZE + if domid not in stats: + stats[domid] = {} + + stats[domid][vbdid] = (usage_at, rd_stat, wr_stat) + + except (IOError, ValueError): + continue + + return stats + + def _get_cpu_stats(self): + stats = {} + for domain in self.xc.domain_getinfo(): + domid = domain['domid'] + vcpu_count = domain['online_vcpus'] + stats[domid] = {} + for i in range(vcpu_count): + vcpu_info = self.xc.vcpu_getinfo(domid, i) + usage = vcpu_info['cpu_time'] + usage_at = time.time() + stats[domid][i] = (usage_at, usage) + + return stats + + + def run(self): + + # loop every second for stats + while True: + self.lock.acquire() + try: + active_domids = [] + # Calculate utilisation for VCPUs + + for domid, cputimes in self._get_cpu_stats().items(): + active_domids.append(domid) + if domid not in self._domain_vcpus: + # if not initialised, save current stats + # and skip utilisation calculation + self._domain_vcpus[domid] = cputimes + self._domain_vcpus_util[domid] = {} + continue + + for vcpu, (usage_at, usage) in cputimes.items(): + if vcpu not in self._domain_vcpus[domid]: + continue + + prv_usage_at, prv_usage = \ + self._domain_vcpus[domid][vcpu] + interval_s = (usage_at - prv_usage_at) * 1000000000 + if interval_s > 0: + util = (usage - prv_usage) / interval_s + self._domain_vcpus_util[domid][vcpu] = util + + self._domain_vcpus[domid] = cputimes + + # Calculate utilisation for VBDs + + for domid, vbds in self._get_vbd_stats().items(): + if domid not in self._domain_vbds: + self._domain_vbds[domid] = vbds + self._domain_vbds_util[domid] = {} + continue + + for devid, (usage_at, rd, wr) in vbds.items(): + if devid not in self._domain_vbds[domid]: + continue + + prv_at, prv_rd, prv_wr = \ + self._domain_vbds[domid][devid] + interval = usage_at - prv_at + rd_util = (rd - prv_rd)/interval + wr_util = (wr - prv_wr)/interval + self._domain_vbds_util[domid][devid] = \ + (rd_util, wr_util) + + self._domain_vbds[domid] = vbds + + + # Calculate utilisation for VIFs + + for domid, vifs in self._get_vif_stats().items(): + + if domid not in self._domain_vifs: + self._domain_vifs[domid] = vifs + self._domain_vifs_util[domid] = {} + self._domain_vifs_stat[domid] = {} + continue + + for devid, (usage_at, rx, tx) in vifs.items(): + if devid not in self._domain_vifs[domid]: + continue + + prv_at, prv_rx, prv_tx = \ + self._domain_vifs[domid][devid] + interval = usage_at - prv_at + rx_util = (rx - prv_rx)/interval + tx_util = (tx - prv_tx)/interval + + # note these are flipped around because + # we are measuring the host interface, + # not the guest interface + self._domain_vifs_util[domid][devid] = \ + (tx_util, rx_util) + self._domain_vifs_stat[domid][devid] = \ + (float(tx), float(rx)) + + self._domain_vifs[domid] = vifs + + # Calculate utilisation for PIFs + + for pifname, stats in self._get_pif_stats().items(): + if pifname not in self.pifs: + self.pifs[pifname] = stats + continue + + usage_at, rx, tx = stats + prv_at, prv_rx, prv_tx = self.pifs[pifname] + interval = usage_at - prv_at + rx_util = (rx - prv_rx)/interval + tx_util = (tx - prv_tx)/interval + + self.pifs_util[pifname] = (rx_util, tx_util) + self.pifs[pifname] = stats + + for domid in self._domain_vcpus_util.keys(): + if domid not in active_domids: + del self._domain_vcpus_util[domid] + del self._domain_vcpus[domid] + for domid in self._domain_vifs_util.keys(): + if domid not in active_domids: + del self._domain_vifs_util[domid] + del self._domain_vifs[domid] + del self._domain_vifs_stat[domid] + for domid in self._domain_vbds_util.keys(): + if domid not in active_domids: + del self._domain_vbds_util[domid] + del self._domain_vbds[domid] + + finally: + self.lock.release() + + # Sleep a while before next poll + time.sleep(POLL_INTERVAL) + diff --git a/tools/python/xen/xend/XendNetwork.py b/tools/python/xen/xend/XendNetwork.py new file mode 100644 index 0000000..489d5d5 --- /dev/null +++ b/tools/python/xen/xend/XendNetwork.py @@ -0,0 +1,238 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006 Xensource Inc. +#============================================================================ + +import os +import commands +import re +import struct +import socket + +import XendDomain +import XendNode +from XendLogging import log +from xen.xend import uuid as genuuid +from xen.xend.XendBase import XendBase +from xen.xend.XendError import * +from xen.util import Brctl +from xen.xend import XendAPIStore + +IP_ROUTE_RE = r'^default via ([\d\.]+) dev (\w+)' + +def bridge_exists(name): + return name in Brctl.get_state().keys() + +class XendNetwork(XendBase): + """We're going to assert that the name_label of this + network is just the name of the bridge""" + + def getClass(self): + return "network" + + def getAttrRW(self): + attrRW = ['name_label', + 'name_description', + 'other_config', + 'default_gateway', + 'default_netmask'] + return XendBase.getAttrRW() + attrRW + + def getAttrRO(self): + attrRO = ['VIFs', + 'PIFs', + 'managed'] + return XendBase.getAttrRO() + attrRO + + def getAttrInst(self): + return XendBase.getAttrInst() + self.getAttrRW() + + def getMethods(self): + methods = ['add_to_other_config', + 'remove_from_other_config', + 'destroy'] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = ['create', 'get_by_name_label'] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def create_phy(self, name): + """ + Called when a new bridge is found on xend start + """ + # Create new uuids + uuid = genuuid.createString() + + # Create instance + record = { + 'name_label': name, + 'name_description': '', + 'other_config': {}, + 'default_gateway': '', + 'default_netmask': '', + 'managed': False, + } + network = XendNetwork(record, uuid) + + return uuid + + def recreate(self, record, uuid): + """ + Called on xend start / restart, or machine + restart, when read from saved config. + Needs to check network exists, create it otherwise + """ + + # Create instance (do this first, to check record) + network = XendNetwork(record, uuid) + + # Create network if it doesn't already exist + if not bridge_exists(network.name_label): + if network.managed: + Brctl.bridge_create(network.name_label) + else: + log.info("Not recreating missing unmanaged network %s" % network.name_label) + + return uuid + + def create(self, record): + """ + Called from API, to create a new network + """ + # Create new uuids + uuid = genuuid.createString() + + # Create instance (do this first, to check record) + network = XendNetwork(record, uuid) + + # Check network doesn't already exist + name_label = network.name_label + if bridge_exists(name_label): + del network + raise UniqueNameError(name_label, "network") + + # Create the bridge + Brctl.bridge_create(network.name_label) + + XendNode.instance().save_networks() + + return uuid + + def get_by_name_label(cls, name): + return [inst.get_uuid() + for inst in XendAPIStore.get_all(cls.getClass()) + if inst.get_name_label() == name] + + create_phy = classmethod(create_phy) + recreate = classmethod(recreate) + create = classmethod(create) + get_by_name_label = classmethod(get_by_name_label) + + def __init__(self, record, uuid): + # This is a read-only attr, so we need to + # set it here, as super class won't try to + if record.has_key("managed"): + self.managed = record["managed"] + else: + self.managed = True + XendBase.__init__(self, uuid, record) + + # + # XenAPI Mehtods + # + + def destroy(self): + # check no VIFs or PIFs attached + if len(self.get_VIFs()) > 0: + raise NetworkError("Cannot destroy network with VIFs attached", + self.get_name_label()) + + if len(self.get_PIFs()) > 0: + raise NetworkError("Cannot destroy network with PIFs attached", + self.get_name_label()) + + XendBase.destroy(self) + Brctl.bridge_del(self.get_name_label()) + XendNode.instance().save_networks() + + def get_name_label(self): + return self.name_label + + def get_name_description(self): + return self.name_description + + def set_name_label(self, new_name): + pass + + def set_name_description(self, new_desc): + self.name_description = new_desc + XendNode.instance().save_networks() + + def get_managed(self): + return self.managed + + def get_VIFs(self): + result = [] + vms = XendDomain.instance().get_all_vms() + for vm in vms: + vifs = vm.get_vifs() + for vif in vifs: + vif_cfg = vm.get_dev_xenapi_config('vif', vif) + if vif_cfg.get('network') == self.get_uuid(): + result.append(vif) + return result + + def get_PIFs(self): + pifs = XendAPIStore.get_all("PIF") + return [pif.get_uuid() for pif in pifs + if pif.get_network() == self.get_uuid()] + + def get_other_config(self): + return self.other_config + + def set_other_config(self, value): + self.other_config = value + XendNode.instance().save_networks() + + def add_to_other_config(self, key, value): + self.other_config[key] = value + XendNode.instance().save_networks() + + def remove_from_other_config(self, key): + if key in self.other_config: + del self.other_config[key] + XendNode.instance().save_networks() + + def get_default_gateway(self): + return self.default_gateway + + def set_default_gateway(self, gateway): + self.default_gateway = gateway + XendNode.instance().save_networks() + + def get_default_netmask(self): + return self.default_netmask + + def set_default_netmask(self, netmask): + self.default_netmask = netmask + XendNode.instance().save_networks() diff --git a/tools/python/xen/xend/XendNode.py b/tools/python/xen/xend/XendNode.py new file mode 100644 index 0000000..72db297 --- /dev/null +++ b/tools/python/xen/xend/XendNode.py @@ -0,0 +1,791 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (c) 2006, 2007 Xensource Inc. +#============================================================================ + +import os +import socket +import xen.lowlevel.xc + +from xen.util import Brctl +from xen.util import pci as PciUtil +from xen.util import vscsi_util +from xen.xend import XendAPIStore +from xen.xend import osdep + +import uuid, arch +from XendPBD import XendPBD +from XendError import * +from XendOptions import instance as xendoptions +from XendQCoWStorageRepo import XendQCoWStorageRepo +from XendLocalStorageRepo import XendLocalStorageRepo +from XendLogging import log +from XendPIF import * +from XendPIFMetrics import XendPIFMetrics +from XendNetwork import * +from XendStateStore import XendStateStore +from XendMonitor import XendMonitor +from XendPPCI import XendPPCI +from XendPSCSI import XendPSCSI + +class XendNode: + """XendNode - Represents a Domain 0 Host.""" + + def __init__(self): + """Initalises the state of all host specific objects such as + + * host + * host_CPU + * host_metrics + * PIF + * PIF_metrics + * network + * Storage Repository + * PPCI + * PSCSI + """ + + self.xc = xen.lowlevel.xc.xc() + self.state_store = XendStateStore(xendoptions().get_xend_state_path()) + self.monitor = XendMonitor() + self.monitor.start() + + # load host state from XML file + saved_host = self.state_store.load_state('host') + if saved_host and len(saved_host.keys()) == 1: + self.uuid = saved_host.keys()[0] + host = saved_host[self.uuid] + self.name = host.get('name_label', socket.gethostname()) + self.desc = host.get('name_description', '') + self.host_metrics_uuid = host.get('metrics_uuid', + uuid.createString()) + try: + self.other_config = eval(host['other_config']) + except: + self.other_config = {} + self.cpus = {} + else: + self.uuid = uuid.createString() + self.name = socket.gethostname() + self.desc = '' + self.other_config = {} + self.cpus = {} + self.host_metrics_uuid = uuid.createString() + + # put some arbitrary params in other_config as this + # is directly exposed via XenAPI + self.other_config["xen_pagesize"] = self.xeninfo_dict()["xen_pagesize"] + self.other_config["platform_params"] = self.xeninfo_dict()["platform_params"] + + # load CPU UUIDs + saved_cpus = self.state_store.load_state('cpu') + for cpu_uuid, cpu in saved_cpus.items(): + self.cpus[cpu_uuid] = cpu + + cpuinfo = osdep.get_cpuinfo() + physinfo = self.physinfo_dict() + cpu_count = physinfo['nr_cpus'] + cpu_features = physinfo['hw_caps'] + virt_caps = physinfo['virt_caps'] + + # If the number of CPUs don't match, we should just reinitialise + # the CPU UUIDs. + if cpu_count != len(self.cpus): + self.cpus = {} + for i in range(cpu_count): + u = uuid.createString() + self.cpus[u] = {'uuid': u, 'number': i } + + for u in self.cpus.keys(): + number = self.cpus[u]['number'] + # We can run off the end of the cpuinfo list if domain0 does not + # have #vcpus == #pcpus. In that case we just replicate one that's + # in the hash table. + if not cpuinfo.has_key(number): + number = cpuinfo.keys()[0] + if arch.type == "x86": + self.cpus[u].update( + { 'host' : self.uuid, + 'features' : cpu_features, + 'virt_caps': virt_caps, + 'speed' : int(float(cpuinfo[number]['cpu MHz'])), + 'vendor' : cpuinfo[number]['vendor_id'], + 'modelname': cpuinfo[number]['model name'], + 'stepping' : cpuinfo[number]['stepping'], + 'flags' : cpuinfo[number]['flags'], + }) + elif arch.type == "ia64": + self.cpus[u].update( + { 'host' : self.uuid, + 'features' : cpu_features, + 'speed' : int(float(cpuinfo[number]['cpu MHz'])), + 'vendor' : cpuinfo[number]['vendor'], + 'modelname': cpuinfo[number]['family'], + 'stepping' : cpuinfo[number]['model'], + 'flags' : cpuinfo[number]['features'], + }) + else: + self.cpus[u].update( + { 'host' : self.uuid, + 'features' : cpu_features, + }) + + self.srs = {} + + # Initialise networks + # First configure ones off disk + saved_networks = self.state_store.load_state('network') + if saved_networks: + for net_uuid, network in saved_networks.items(): + try: + XendNetwork.recreate(network, net_uuid) + except CreateUnspecifiedAttributeError: + log.warn("Error recreating network %s", net_uuid) + + # Next discover any existing bridges and check + # they are not already configured + bridges = Brctl.get_state().keys() + configured_bridges = [XendAPIStore.get( + network_uuid, "network") + .get_name_label() + for network_uuid in XendNetwork.get_all()] + unconfigured_bridges = [bridge + for bridge in bridges + if bridge not in configured_bridges] + for unconfigured_bridge in unconfigured_bridges: + XendNetwork.create_phy(unconfigured_bridge) + + # Initialise PIFs + # First configure ones off disk + saved_pifs = self.state_store.load_state('pif') + if saved_pifs: + for pif_uuid, pif in saved_pifs.items(): + try: + XendPIF.recreate(pif, pif_uuid) + except CreateUnspecifiedAttributeError: + log.warn("Error recreating PIF %s", pif_uuid) + + # Next discover any existing PIFs and check + # they are not already configured + configured_pifs = [XendAPIStore.get( + pif_uuid, "PIF") + .get_interface_name() + for pif_uuid in XendPIF.get_all()] + unconfigured_pifs = [(name, mtu, mac) + for name, mtu, mac in linux_get_phy_ifaces() + if name not in configured_pifs] + + # Get a mapping from interface to bridge + if_to_br = dict([(i,b) + for (b,ifs) in Brctl.get_state().items() + for i in ifs]) + + for name, mtu, mac in unconfigured_pifs: + # Check PIF is on bridge + # if not, ignore + bridge_name = if_to_br.get(name, None) + if bridge_name is not None: + # Translate bridge name to network uuid + for network_uuid in XendNetwork.get_all(): + network = XendAPIStore.get( + network_uuid, 'network') + if network.get_name_label() == bridge_name: + XendPIF.create_phy(network_uuid, name, + mac, mtu) + break + else: + log.debug("Cannot find network for bridge %s " + "when configuring PIF %s", + (bridge_name, name)) + + # initialise storage + saved_srs = self.state_store.load_state('sr') + if saved_srs: + for sr_uuid, sr_cfg in saved_srs.items(): + if sr_cfg['type'] == 'qcow_file': + self.srs[sr_uuid] = XendQCoWStorageRepo(sr_uuid) + elif sr_cfg['type'] == 'local': + self.srs[sr_uuid] = XendLocalStorageRepo(sr_uuid) + + # Create missing SRs if they don't exist + if not self.get_sr_by_type('local'): + image_sr_uuid = uuid.createString() + self.srs[image_sr_uuid] = XendLocalStorageRepo(image_sr_uuid) + + if not self.get_sr_by_type('qcow_file'): + qcow_sr_uuid = uuid.createString() + self.srs[qcow_sr_uuid] = XendQCoWStorageRepo(qcow_sr_uuid) + + saved_pbds = self.state_store.load_state('pbd') + if saved_pbds: + for pbd_uuid, pbd_cfg in saved_pbds.items(): + try: + XendPBD.recreate(pbd_uuid, pbd_cfg) + except CreateUnspecifiedAttributeError: + log.warn("Error recreating PBD %s", pbd_uuid) + + + # Initialise PPCIs + saved_ppcis = self.state_store.load_state('ppci') + saved_ppci_table = {} + if saved_ppcis: + for ppci_uuid, ppci_record in saved_ppcis.items(): + try: + saved_ppci_table[ppci_record['name']] = ppci_uuid + except KeyError: + pass + + for pci_dev in PciUtil.get_all_pci_devices(): + ppci_record = { + 'domain': pci_dev.domain, + 'bus': pci_dev.bus, + 'slot': pci_dev.slot, + 'func': pci_dev.func, + 'vendor_id': pci_dev.vendor, + 'vendor_name': pci_dev.vendorname, + 'device_id': pci_dev.device, + 'device_name': pci_dev.devicename, + 'revision_id': pci_dev.revision, + 'class_code': pci_dev.classcode, + 'class_name': pci_dev.classname, + 'subsystem_vendor_id': pci_dev.subvendor, + 'subsystem_vendor_name': pci_dev.subvendorname, + 'subsystem_id': pci_dev.subdevice, + 'subsystem_name': pci_dev.subdevicename, + 'driver': pci_dev.driver + } + # If saved uuid exists, use it. Otherwise create one. + ppci_uuid = saved_ppci_table.get(pci_dev.name, uuid.createString()) + XendPPCI(ppci_uuid, ppci_record) + + + # Initialise PSCSIs + saved_pscsis = self.state_store.load_state('pscsi') + saved_pscsi_table = {} + if saved_pscsis: + for pscsi_uuid, pscsi_record in saved_pscsis.items(): + try: + saved_pscsi_table[pscsi_record['scsi_id']] = pscsi_uuid + except KeyError: + pass + + for pscsi_record in vscsi_util.get_all_scsi_devices(): + if pscsi_record['scsi_id']: + # If saved uuid exists, use it. Otherwise create one. + pscsi_uuid = saved_pscsi_table.get(pscsi_record['scsi_id'], + uuid.createString()) + XendPSCSI(pscsi_uuid, pscsi_record) + + +## def network_destroy(self, net_uuid): + ## del self.networks[net_uuid] + ## self.save_networks() + + + def get_PIF_refs(self): + log.debug(XendPIF.get_all()) + return XendPIF.get_all() + +## def _PIF_create(self, name, mtu, vlan, mac, network, persist = True, +## pif_uuid = None, metrics_uuid = None): +## for pif in self.pifs.values(): +## if pif.network == network: +## raise NetworkAlreadyConnected(pif.uuid) + +## if pif_uuid is None: +## pif_uuid = uuid.createString() +## if metrics_uuid is None: +## metrics_uuid = uuid.createString() + +## metrics = XendPIFMetrics(metrics_uuid) +## pif = XendPIF(pif_uuid, metrics, name, mtu, vlan, mac, network, self) +## metrics.set_PIF(pif) + +## self.pif_metrics[metrics_uuid] = metrics +## self.pifs[pif_uuid] = pif + +## if persist: +## self.save_PIFs() +## self.refreshBridges() +## return pif_uuid + +## def PIF_destroy(self, pif_uuid): +## pif = self.pifs[pif_uuid] + +## if pif.vlan == -1: +## raise PIFIsPhysical() + +## del self.pifs[pif_uuid] +## self.save_PIFs() + + + def get_PPCI_refs(self): + return XendPPCI.get_all() + + def get_ppci_by_uuid(self, ppci_uuid): + if ppci_uuid in self.get_PPCI_refs(): + return ppci_uuid + return None + + + def get_PSCSI_refs(self): + return XendPSCSI.get_all() + + def get_pscsi_by_uuid(self, pscsi_uuid): + if pscsi_uuid in self.get_PSCSI_refs(): + return pscsi_uuid + return None + + + def save(self): + # save state + host_record = {self.uuid: {'name_label':self.name, + 'name_description':self.desc, + 'metrics_uuid': self.host_metrics_uuid, + 'other_config': self.other_config}} + self.state_store.save_state('host',host_record) + self.state_store.save_state('cpu', self.cpus) + self.save_PIFs() + self.save_networks() + self.save_PBDs() + self.save_SRs() + self.save_PPCIs() + self.save_PSCSIs() + + def save_PIFs(self): + pif_records = dict([(pif_uuid, XendAPIStore.get( + pif_uuid, "PIF").get_record()) + for pif_uuid in XendPIF.get_all()]) + self.state_store.save_state('pif', pif_records) + + def save_networks(self): + net_records = dict([(network_uuid, XendAPIStore.get( + network_uuid, "network").get_record()) + for network_uuid in XendNetwork.get_all()]) + self.state_store.save_state('network', net_records) + + def save_PBDs(self): + pbd_records = dict([(pbd_uuid, XendAPIStore.get( + pbd_uuid, "PBD").get_record()) + for pbd_uuid in XendPBD.get_all()]) + self.state_store.save_state('pbd', pbd_records) + + def save_SRs(self): + sr_records = dict([(k, v.get_record(transient = False)) + for k, v in self.srs.items()]) + self.state_store.save_state('sr', sr_records) + + def save_PPCIs(self): + ppci_records = dict([(ppci_uuid, XendAPIStore.get( + ppci_uuid, "PPCI").get_record()) + for ppci_uuid in XendPPCI.get_all()]) + self.state_store.save_state('ppci', ppci_records) + + def save_PSCSIs(self): + pscsi_records = dict([(pscsi_uuid, XendAPIStore.get( + pscsi_uuid, "PSCSI").get_record()) + for pscsi_uuid in XendPSCSI.get_all()]) + self.state_store.save_state('pscsi', pscsi_records) + + def shutdown(self): + return 0 + + def reboot(self): + return 0 + + def notify(self, _): + return 0 + + # + # Ref validation + # + + def is_valid_host(self, host_ref): + return (host_ref == self.uuid) + + def is_valid_cpu(self, cpu_ref): + return (cpu_ref in self.cpus) + + def is_valid_sr(self, sr_ref): + return (sr_ref in self.srs) + + def is_valid_vdi(self, vdi_ref): + for sr in self.srs.values(): + if sr.is_valid_vdi(vdi_ref): + return True + return False + + # + # Storage Repositories + # + + def get_sr(self, sr_uuid): + return self.srs.get(sr_uuid) + + def get_sr_by_type(self, sr_type): + return [sr.uuid for sr in self.srs.values() if sr.type == sr_type] + + def get_sr_by_name(self, name): + return [sr.uuid for sr in self.srs.values() if sr.name_label == name] + + def get_all_sr_uuid(self): + return self.srs.keys() + + def get_vdi_by_uuid(self, vdi_uuid): + for sr in self.srs.values(): + if sr.is_valid_vdi(vdi_uuid): + return sr.get_vdi_by_uuid(vdi_uuid) + return None + + def get_vdi_by_name_label(self, name): + for sr in self.srs.values(): + vdi = sr.get_vdi_by_name_label(name) + if vdi: + return vdi + return None + + def get_sr_containing_vdi(self, vdi_uuid): + for sr in self.srs.values(): + if sr.is_valid_vdi(vdi_uuid): + return sr + return None + + + # + # Host Functions + # + + def xen_version(self): + info = self.xc.xeninfo() + + info = {'Xen': '%(xen_major)d.%(xen_minor)d' % info} + + # Add xend_config_format + info.update(self.xendinfo_dict()) + + # Add version info about machine + info.update(self.nodeinfo_dict()) + + # Add specific xen version info + xeninfo_dict = self.xeninfo_dict() + + info.update({ + "xen_major": xeninfo_dict["xen_major"], + "xen_minor": xeninfo_dict["xen_minor"], + "xen_extra": xeninfo_dict["xen_extra"], + "cc_compiler": xeninfo_dict["cc_compiler"], + "cc_compile_by": xeninfo_dict["cc_compile_by"], + "cc_compile_domain": xeninfo_dict["cc_compile_domain"], + "cc_compile_date": xeninfo_dict["cc_compile_date"], + "xen_changeset": xeninfo_dict["xen_changeset"] + }) + + return info + + def get_name(self): + return self.name + + def set_name(self, new_name): + self.name = new_name + + def get_description(self): + return self.desc + + def set_description(self, new_desc): + self.desc = new_desc + + def get_uuid(self): + return self.uuid + + def get_capabilities(self): + return self.xc.xeninfo()['xen_caps'].split(" ") + + # + # Host CPU Functions + # + + def get_host_cpu_by_uuid(self, host_cpu_uuid): + if host_cpu_uuid in self.cpus: + return host_cpu_uuid + raise XendError('Invalid CPU UUID') + + def get_host_cpu_refs(self): + return self.cpus.keys() + + def get_host_cpu_uuid(self, host_cpu_ref): + if host_cpu_ref in self.cpus: + return host_cpu_ref + else: + raise XendError('Invalid CPU Reference') + + def get_host_cpu_field(self, ref, field): + try: + return self.cpus[ref][field] + except KeyError: + raise XendError('Invalid CPU Reference') + + def get_host_cpu_load(self, host_cpu_ref): + host_cpu = self.cpus.get(host_cpu_ref) + if not host_cpu: + return 0.0 + + vcpu = int(host_cpu['number']) + cpu_loads = self.monitor.get_domain_vcpus_util() + if 0 in cpu_loads and vcpu in cpu_loads[0]: + return cpu_loads[0][vcpu] + + return 0.0 + + def get_vcpus_policy(self): + sched_id = self.xc.sched_id_get() + if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF: + return 'sedf' + elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT: + return 'credit' + else: + return 'unknown' + + def get_cpu_configuration(self): + phys_info = self.physinfo_dict() + + cpu_info = { + "nr_nodes": phys_info["nr_nodes"], + "nr_cpus": phys_info["nr_cpus"], + "cores_per_socket": phys_info["cores_per_socket"], + "threads_per_core": phys_info["threads_per_core"] + } + + return cpu_info + + # + # Network Functions + # + + def bridge_to_network(self, bridge): + """ + Determine which network a particular bridge is attached to. + + @param bridge The name of the bridge. If empty, the default bridge + will be used instead (the first one in the list returned by brctl + show); this is the behaviour of the vif-bridge script. + @return The XendNetwork instance to which this bridge is attached. + @raise Exception if the interface is not connected to a network. + """ + if not bridge: + rc, bridge = commands.getstatusoutput( + 'brctl show | cut -d "\n" -f 2 | cut -f 1') + if rc != 0 or not bridge: + raise Exception( + 'Could not find default bridge, and none was specified') + + for network_uuid in XendNetwork.get_all(): + network = XendAPIStore.get(network_uuid, "network") + if network.get_name_label() == bridge: + return network + else: + raise Exception('Cannot find network for bridge %s' % bridge) + + # + # Debug keys. + # + + def send_debug_keys(self, keys): + return self.xc.send_debug_keys(keys) + + # + # Getting host information. + # + + def info(self): + return (self.nodeinfo() + self.physinfo() + self.xeninfo() + + self.xendinfo()) + + def nodeinfo(self): + (sys, host, rel, ver, mch) = os.uname() + return [['system', sys], + ['host', host], + ['release', rel], + ['version', ver], + ['machine', mch]] + + def list_to_rangepairs(self,cmap): + cmap.sort() + pairs = [] + x = y = 0 + for i in range(0,len(cmap)): + try: + if ((cmap[y+1] - cmap[i]) > 1): + pairs.append((cmap[x],cmap[y])) + x = y = i+1 + else: + y = y + 1 + # if we go off the end, then just add x to y + except IndexError: + pairs.append((cmap[x],cmap[y])) + + return pairs + + def format_pairs(self,pairs): + if not pairs: + return "no cpus" + out = "" + for f,s in pairs: + if (f==s): + out += '%d'%f + else: + out += '%d-%d'%(f,s) + out += ',' + # trim trailing ',' + return out[:-1] + + def list_to_strrange(self,list): + return self.format_pairs(self.list_to_rangepairs(list)) + + def format_node_to_cpu(self, pinfo): + str='' + whitespace='' + try: + node_to_cpu=pinfo['node_to_cpu'] + for i in range(0, pinfo['nr_nodes']): + str+='%snode%d:%s\n' % (whitespace, + i, + self.list_to_strrange(node_to_cpu[i])) + whitespace='%25s' % '' + except: + str='none\n' + return str[:-1]; + def format_node_to_memory(self, pinfo): + str='' + whitespace='' + try: + node_to_memory=pinfo['node_to_memory'] + for i in range(0, pinfo['nr_nodes']): + str+='%snode%d:%d\n' % (whitespace, + i, + node_to_memory[i] / 1024) + whitespace='%25s' % '' + except: + str='none\n' + return str[:-1]; + + + def physinfo(self): + info = self.xc.physinfo() + + info['cpu_mhz'] = info['cpu_khz'] / 1000 + + # physinfo is in KiB, need it in MiB + info['total_memory'] = info['total_memory'] / 1024 + info['free_memory'] = info['free_memory'] / 1024 + info['node_to_cpu'] = self.format_node_to_cpu(info) + info['node_to_memory'] = self.format_node_to_memory(info) + + ITEM_ORDER = ['nr_cpus', + 'nr_nodes', + 'cores_per_socket', + 'threads_per_core', + 'cpu_mhz', + 'hw_caps', + 'virt_caps', + 'total_memory', + 'free_memory', + 'node_to_cpu', + 'node_to_memory' + ] + + return [[k, info[k]] for k in ITEM_ORDER] + + def xenschedinfo(self): + sched_id = self.xc.sched_id_get() + if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF: + return 'sedf' + elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT: + return 'credit' + else: + return 'unknown' + + def xeninfo(self): + info = self.xc.xeninfo() + info['xen_scheduler'] = self.xenschedinfo() + + ITEM_ORDER = ['xen_major', + 'xen_minor', + 'xen_extra', + 'xen_caps', + 'xen_scheduler', + 'xen_pagesize', + 'platform_params', + 'xen_changeset', + 'cc_compiler', + 'cc_compile_by', + 'cc_compile_domain', + 'cc_compile_date', + ] + + return [[k, info[k]] for k in ITEM_ORDER] + + def xendinfo(self): + return [['xend_config_format', 4]] + + # + # utilisation tracking + # + + def get_vcpu_util(self, domid, vcpuid): + cpu_loads = self.monitor.get_domain_vcpus_util() + if domid in cpu_loads: + return cpu_loads[domid].get(vcpuid, 0.0) + return 0.0 + + def get_vif_util(self, domid, vifid): + vif_loads = self.monitor.get_domain_vifs_util() + if domid in vif_loads: + return vif_loads[domid].get(vifid, (0.0, 0.0)) + return (0.0, 0.0) + + def get_vif_stat(self, domid, vifid): + vif_loads = self.monitor.get_domain_vifs_stat() + if domid in vif_loads: + return vif_loads[domid].get(vifid, (0.0, 0.0)) + return (0.0, 0.0) + + def get_vbd_util(self, domid, vbdid): + vbd_loads = self.monitor.get_domain_vbds_util() + if domid in vbd_loads: + return vbd_loads[domid].get(vbdid, (0.0, 0.0)) + return (0.0, 0.0) + + # dictionary version of *info() functions to get rid of + # SXPisms. + def nodeinfo_dict(self): + return dict(self.nodeinfo()) + def xendinfo_dict(self): + return dict(self.xendinfo()) + def xeninfo_dict(self): + return dict(self.xeninfo()) + def physinfo_dict(self): + return dict(self.physinfo()) + def info_dict(self): + return dict(self.info()) + +def instance(): + global inst + try: + inst + except: + inst = XendNode() + inst.save() + return inst diff --git a/tools/python/xen/xend/XendOptions.py b/tools/python/xen/xend/XendOptions.py new file mode 100644 index 0000000..350f207 --- /dev/null +++ b/tools/python/xen/xend/XendOptions.py @@ -0,0 +1,492 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +"""Xend root class. +Creates the servers and handles configuration. + +Other classes get config variables by importing this module, +using instance() to get a XendOptions instance, and then +the config functions (e.g. get_xend_port()) to get +configured values. +""" + +import os +import os.path +import string +import sys + +from xen.xend import sxp, osdep, XendLogging +from xen.xend.XendError import XendError + +if os.uname()[0] == 'SunOS': + from xen.lowlevel import scf + +class XendOptions: + """Configuration options.""" + + """Where network control scripts live.""" + network_script_dir = osdep.scripts_dir + + """Where block control scripts live.""" + block_script_dir = osdep.scripts_dir + + """Default path to the log file. """ + logfile_default = "/var/log/xen/xend.log" + + """Default level of information to be logged.""" + loglevel_default = 'DEBUG' + + """Default Xen-API server configuration. """ + xen_api_server_default = [['unix']] + + """Default for the flag indicating whether xend should run an http server + (deprecated).""" + xend_http_server_default = 'no' + + xend_tcp_xmlrpc_server_default = 'no' + + xend_tcp_xmlrpc_server_address_default = 'localhost' + + xend_tcp_xmlrpc_server_port_default = 8006 + + xend_unix_xmlrpc_server_default = 'yes' + + """Default interface address xend listens at. """ + xend_address_default = '' + + """Default for the flag indicating whether xend should run a relocation server.""" + xend_relocation_server_default = 'no' + + """Default for the flag indicating whether xend should run a ssl relocation server.""" + xend_relocation_ssl_server_default = 'no' + + """Default interface address the xend relocation server listens at. """ + xend_relocation_address_default = '' + + """Default port xend serves HTTP at. """ + xend_port_default = 8000 + + """Default port xend serves relocation at. """ + xend_relocation_port_default = 8002 + + """Default port xend serves ssl relocation at. """ + xend_relocation_ssl_port_default = 8003 + + xend_relocation_hosts_allow_default = '' + + """Default for the flag indicating whether xend should run a unix-domain + server (deprecated).""" + xend_unix_server_default = 'no' + + """Default external migration tool """ + external_migration_tool_default = '' + + """Default path the unix-domain server listens at.""" + xend_unix_path_default = '/var/lib/xend/xend-socket' + + dom0_min_mem_default = 0 + + dom0_vcpus_default = 0 + + vncpasswd_default = None + + """Default interface to listen for VNC connections on""" + xend_vnc_listen_default = '127.0.0.1' + + """Use of TLS mode in QEMU VNC server""" + xend_vnc_tls = 0 + + """x509 certificate directory for QEMU VNC server""" + xend_vnc_x509_cert_dir = "/etc/xen/vnc" + + """Verify incoming client x509 certs""" + xend_vnc_x509_verify = 0 + + """Default session storage path.""" + xend_domains_path_default = '/var/lib/xend/domains' + + """Default xend management state storage.""" + xend_state_path_default = '/var/lib/xend/state' + + """Default xend QCoW storage repository location.""" + xend_storage_path_default = '/var/lib/xend/storage' + + """Default xend security state storage path.""" + xend_security_path_default = '/var/lib/xend/security' + + """Default script to configure a backend network interface""" + vif_script = osdep.vif_script + + """Default Xen Security Module""" + xsm_module_default = 'dummy' + + """Default rotation count of qemu-dm log file.""" + qemu_dm_logrotate_count = 10 + + def __init__(self): + self.configure() + + def _logError(self, fmt, *args): + """Logging function to log to stderr. We use this for XendOptions log + messages because they may be logged before the logger has been + configured. Other components can safely use the logger. + """ + print >>sys.stderr, "xend [ERROR]", fmt % args + + + def configure(self): + self.set_config() + XendLogging.init(self.get_config_string("logfile", + self.logfile_default), + self.get_config_string("loglevel", + self.loglevel_default)) + + def set_config(self): + raise NotImplementedError() + + def get_config_bool(self, name, val=None): + raise NotImplementedError() + + def get_config_int(self, name, val=None): + raise NotImplementedError() + + def get_config_string(self, name, val=None): + raise NotImplementedError() + + def get_xen_api_server(self): + raise NotImplementedError() + + def get_xend_http_server(self): + """Get the flag indicating whether xend should run an http server. + """ + return self.get_config_bool("xend-http-server", self.xend_http_server_default) + + def get_xend_tcp_xmlrpc_server(self): + return self.get_config_bool("xend-tcp-xmlrpc-server", + self.xend_tcp_xmlrpc_server_default) + + def get_xend_tcp_xmlrpc_server_port(self): + return self.get_config_int("xend-tcp-xmlrpc-server-port", + self.xend_tcp_xmlrpc_server_port_default) + + def get_xend_tcp_xmlrpc_server_address(self): + return self.get_config_string("xend-tcp-xmlrpc-server-address", + self.xend_tcp_xmlrpc_server_address_default) + + def get_xend_tcp_xmlrpc_server_ssl_key_file(self): + return self.get_config_string("xend-tcp-xmlrpc-server-ssl-key-file") + + def get_xend_tcp_xmlrpc_server_ssl_cert_file(self): + return self.get_config_string("xend-tcp-xmlrpc-server-ssl-cert-file") + + def get_xend_unix_xmlrpc_server(self): + return self.get_config_bool("xend-unix-xmlrpc-server", + self.xend_unix_xmlrpc_server_default) + + def get_xend_relocation_server(self): + """Get the flag indicating whether xend should run a relocation server. + """ + return self.get_config_bool("xend-relocation-server", + self.xend_relocation_server_default) + + def get_xend_relocation_ssl_server(self): + """Get the flag indicating whether xend should run a ssl relocation server. + """ + return self.get_config_bool("xend-relocation-ssl-server", + self.xend_relocation_ssl_server_default) + + def get_xend_relocation_server_ssl_key_file(self): + return self.get_config_string("xend-relocation-server-ssl-key-file") + + def get_xend_relocation_server_ssl_cert_file(self): + return self.get_config_string("xend-relocation-server-ssl-cert-file") + + def get_xend_port(self): + """Get the port xend listens at for its HTTP interface. + """ + return self.get_config_int('xend-port', self.xend_port_default) + + def get_xend_relocation_port(self): + """Get the port xend listens at for connection to its relocation server. + """ + return self.get_config_int('xend-relocation-port', + self.xend_relocation_port_default) + + def get_xend_relocation_ssl_port(self): + """Get the port xend listens at for ssl connection to its relocation + server. + """ + return self.get_config_int('xend-relocation-ssl-port', + self.xend_relocation_ssl_port_default) + + def get_xend_relocation_ssl(self): + """Whether to use ssl when relocating. + """ + return self.get_config_bool('xend-relocation-ssl', 'no') + + def get_xend_relocation_hosts_allow(self): + return self.get_config_string("xend-relocation-hosts-allow", + self.xend_relocation_hosts_allow_default) + + def get_xend_address(self): + """Get the address xend listens at for its HTTP port. + This defaults to the empty string which allows all hosts to connect. + If this is set to 'localhost' only the localhost will be able to connect + to the HTTP port. + """ + return self.get_config_string('xend-address', self.xend_address_default) + + def get_xend_relocation_address(self): + """Get the address xend listens at for its relocation server port. + This defaults to the empty string which allows all hosts to connect. + If this is set to 'localhost' only the localhost will be able to connect + to the relocation port. + """ + return self.get_config_string('xend-relocation-address', self.xend_relocation_address_default) + + def get_xend_unix_server(self): + """Get the flag indicating whether xend should run a unix-domain server. + """ + return self.get_config_bool("xend-unix-server", self.xend_unix_server_default) + + def get_xend_unix_path(self): + """Get the path the xend unix-domain server listens at. + """ + return self.get_config_string("xend-unix-path", self.xend_unix_path_default) + + def get_xend_domains_path(self): + """ Get the path for persistent domain configuration storage + """ + return self.get_config_string("xend-domains-path", self.xend_domains_path_default) + + def get_xend_state_path(self): + """ Get the path for persistent domain configuration storage + """ + return self.get_config_string("xend-state-path", self.xend_state_path_default) + + def get_xend_storage_path(self): + """ Get the path for persistent domain configuration storage + """ + return self.get_config_string("xend-storage-path", self.xend_storage_path_default) + + def get_xend_security_path(self): + """ Get the path for security state + """ + return self.get_config_string("xend-security-path", self.xend_security_path_default) + + def get_network_script(self): + """@return the script used to alter the network configuration when + Xend starts and stops, or None if no such script is specified.""" + + s = self.get_config_string('network-script') + + if s: + result = s.split(" ") + result[0] = os.path.join(self.network_script_dir, result[0]) + return result + else: + return None + + def get_external_migration_tool(self): + """@return the name of the tool to handle virtual TPM migration.""" + return self.get_config_string('external-migration-tool', self.external_migration_tool_default) + + def get_enable_dump(self): + return self.get_config_bool('enable-dump', 'no') + + def get_vif_script(self): + return self.get_config_string('vif-script', self.vif_script) + + def get_dom0_min_mem(self): + return self.get_config_int('dom0-min-mem', self.dom0_min_mem_default) + + def get_enable_dom0_ballooning(self): + enable_dom0_ballooning_default = 'yes' + if self.get_dom0_min_mem() == 0: + enable_dom0_ballooning_default = 'no' + return self.get_config_bool('enable-dom0-ballooning', + enable_dom0_ballooning_default) + + def get_dom0_vcpus(self): + return self.get_config_int('dom0-cpus', self.dom0_vcpus_default) + + def get_console_limit(self): + return self.get_config_int('console-limit', 1024) + + def get_vnclisten_address(self): + return self.get_config_string('vnc-listen', self.xend_vnc_listen_default) + + def get_vncpasswd_default(self): + return self.get_config_string('vncpasswd', + self.vncpasswd_default) + + def get_keymap(self): + return self.get_config_string('keymap', None) + + def get_resource_label_change_script(self): + s = self.get_config_string('resource-label-change-script') + if s: + result = s.split(" ") + result[0] = os.path.join(osdep.scripts_dir, result[0]) + return result + else: + return None + + + def get_vnc_tls(self): + return self.get_config_string('vnc-tls', self.xend_vnc_tls) + + def get_vnc_x509_cert_dir(self): + return self.get_config_string('vnc-x509-cert-dir', self.xend_vnc_x509_cert_dir) + + def get_vnc_x509_verify(self): + return self.get_config_string('vnc-x509-verify', self.xend_vnc_x509_verify) + + def get_qemu_dm_logrotate_count(self): + return self.get_config_int("qemu-dm-logrotate-count", + self.qemu_dm_logrotate_count) + + +class XendOptionsFile(XendOptions): + + """Default path to the config file.""" + config_default = "/etc/xen/xend-config.sxp" + + """Environment variable used to override config_default.""" + config_var = "XEND_CONFIG" + + def set_config(self): + """If the config file exists, read it. If not, ignore it. + + The config file is a sequence of sxp forms. + """ + self.config_path = os.getenv(self.config_var, self.config_default) + if os.path.exists(self.config_path): + try: + fin = file(self.config_path, 'rb') + try: + config = sxp.parse(fin) + finally: + fin.close() + if config is None: + config = ['xend-config'] + else: + config.insert(0, 'xend-config') + self.config = config + except Exception, ex: + self._logError('Reading config file %s: %s', + self.config_path, str(ex)) + raise + else: + self._logError('Config file does not exist: %s', + self.config_path) + self.config = ['xend-config'] + + def get_config_value(self, name, val=None): + """Get the value of an atomic configuration element. + + @param name: element name + @param val: default value (optional, defaults to None) + @return: value + """ + return sxp.child_value(self.config, name, val=val) + + def get_config_bool(self, name, val=None): + v = string.lower(str(self.get_config_value(name, val))) + if v in ['yes', 'y', '1', 'on', 'true', 't']: + return True + if v in ['no', 'n', '0', 'off', 'false', 'f']: + return False + raise XendError("invalid xend config %s: expected bool: %s" % (name, v)) + + def get_config_int(self, name, val=None): + v = self.get_config_value(name, val) + try: + return int(v) + except Exception: + raise XendError("invalid xend config %s: expected int: %s" % (name, v)) + + def get_config_string(self, name, val=None): + return self.get_config_value(name, val) + + def get_xen_api_server(self): + """Get the Xen-API server configuration. + """ + return self.get_config_value('xen-api-server', + self.xen_api_server_default) + + def get_xsm_module_name(self): + """Get the Xen Security Module name. + """ + return self.get_config_string('xsm_module_name', self.xsm_module_default) + +if os.uname()[0] == 'SunOS': + class XendOptionsSMF(XendOptions): + + def set_config(self): + pass + + def get_config_bool(self, name, val=None): + try: + return scf.get_bool(name) + except scf.error, e: + if e[0] == scf.SCF_ERROR_NOT_FOUND: + if val in ['yes', 'y', '1', 'on', 'true', 't']: + return True + if val in ['no', 'n', '0', 'off', 'false', 'f']: + return False + return val + else: + raise XendError("option %s: %s:%s" % (name, e[1], e[2])) + + def get_config_int(self, name, val=None): + try: + return scf.get_int(name) + except scf.error, e: + if e[0] == scf.SCF_ERROR_NOT_FOUND: + return val + else: + raise XendError("option %s: %s:%s" % (name, e[1], e[2])) + + def get_config_string(self, name, val=None): + try: + return scf.get_string(name) + except scf.error, e: + if e[0] == scf.SCF_ERROR_NOT_FOUND: + return val + else: + raise XendError("option %s: %s:%s" % (name, e[1], e[2])) + + def get_xen_api_server(self): + # When the new server is a supported configuration, we should + # expand this. + return [["unix"]] + +def instance(): + """Get an instance of XendOptions. + Use this instead of the constructor. + """ + global inst + try: + inst + except: + if os.uname()[0] == 'SunOS': + inst = XendOptionsSMF() + else: + inst = XendOptionsFile() + return inst diff --git a/tools/python/xen/xend/XendPBD.py b/tools/python/xen/xend/XendPBD.py new file mode 100644 index 0000000..2187cd7 --- /dev/null +++ b/tools/python/xen/xend/XendPBD.py @@ -0,0 +1,99 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2007 Xensource Inc. +#============================================================================ + + +import uuid +from XendLogging import log +from xen.xend.XendBase import XendBase +from xen.xend import XendAPIStore +from xen.xend import uuid as genuuid + +class XendPBD(XendBase): + """Physical block devices.""" + + def getClass(self): + return "PBD" + + def getAttrRO(self): + attrRO = ['host', + 'SR', + 'device_config', + 'currently_attached'] + return XendBase.getAttrRO() + attrRO + + def getAttrRW(self): + attrRW = [] + return XendBase.getAttrRW() + attrRW + + def getAttrInst(self): + return ['host', + 'SR', + 'device_config'] + + def getMethods(self): + methods = ['destroy'] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = ['create', + 'get_by_SR'] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def recreate(uuid, record): + pbd = XendPBD(record, uuid) + return uuid + + def create(cls, record): + uuid = genuuid.createString() + pbd = XendPBD(record, uuid) + return uuid + + create = classmethod(create) + + def __init__(self, record, uuid): + XendBase.__init__(self, uuid, record) + self.currently_attached = True + + def get_host(self): + return self.host + + def get_SR(self): + return self.SR + + def get_device_config(self): + return self.device_config + + def get_currently_attached(self): + return self.currently_attached + + def destroy(self): + pass + + def get_by_SR(cls, sr_ref): + pbds = XendAPIStore.get_all("PBD") + return [pbd.get_uuid() + for pbd in pbds + if pbd.get_SR() == sr_ref] + + get_by_SR = classmethod(get_by_SR) diff --git a/tools/python/xen/xend/XendPIF.py b/tools/python/xen/xend/XendPIF.py new file mode 100644 index 0000000..6052481 --- /dev/null +++ b/tools/python/xen/xend/XendPIF.py @@ -0,0 +1,389 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006 Xensource Inc. +#============================================================================ + +import commands +import logging +import os +import re +from xen.xend import uuid as genuuid +from xen.xend import XendAPIStore +from xen.xend.XendBase import XendBase +from xen.xend.XendPIFMetrics import XendPIFMetrics +from xen.xend.XendError import * +from xen.xend import Vifctl + +log = logging.getLogger("xend.XendPIF") +log.setLevel(logging.TRACE) + +MAC_RE = re.compile(':'.join(['[0-9a-f]{2}'] * 6)) +IP_IFACE_RE = re.compile(r'^\d+: (\w+):.*mtu (\d+) .* link/\w+ ([0-9a-f:]+)') + + +Vifctl.network('start') + +def linux_phy_to_virt(pif_name): + return 'eth' + re.sub(r'^[a-z]+', '', pif_name) + +def linux_get_phy_ifaces(): + """Returns a list of physical interfaces. + + Identifies PIFs as those that have a interface name starting with + 'peth'. + + See /etc/xen/scripts/network-bridge for how the devices are renamed. + + @rtype: array of 3-element tuple (name, mtu, mac) + """ + + ip_cmd = 'ip -o link show' + rc, output = commands.getstatusoutput(ip_cmd) + ifaces = {} + phy_ifaces = [] + if rc == 0: + # parse all interfaces into (name, mtu, mac) + for line in output.split('\n'): + has_if = re.search(IP_IFACE_RE, line) + if has_if: + ifaces[has_if.group(1)] = has_if.groups() + + # resolve pifs' mac addresses + for name, mtu, mac in ifaces.values(): + if name.startswith('peth'): + bridged_ifname = linux_phy_to_virt(name) + bridged_if = ifaces.get(bridged_ifname) + bridged_mac = '' + if bridged_if: + bridged_mac = bridged_if[2] + phy_ifaces.append((name, int(mtu), bridged_mac)) + + return phy_ifaces + +def linux_set_mac(iface, mac): + if not re.search(MAC_RE, mac): + return False + + ip_mac_cmd = 'ip link set %s addr %s' % \ + (linux_phy_to_virt(iface), mac) + rc, output = commands.getstatusoutput(ip_mac_cmd) + if rc == 0: + return True + + return False + +def linux_set_mtu(iface, mtu): + try: + ip_mtu_cmd = 'ip link set %s mtu %d' % \ + (linux_phy_to_virt(iface), int(mtu)) + rc, output = commands.getstatusoutput(ip_mtu_cmd) + if rc == 0: + return True + return False + except ValueError: + return False + +def linux_get_mtu(device): + return _linux_get_pif_param(device, 'mtu') + +def linux_get_mac(device): + return _linux_get_pif_param(device, 'link/ether') + +def _linux_get_pif_param(device, param_name): + ip_get_dev_data = 'ip link show %s' % device + rc, output = commands.getstatusoutput(ip_get_dev_data) + if rc == 0: + params = output.split(' ') + for i in xrange(len(params)): + if params[i] == param_name: + return params[i+1] + return '' + +def _create_VLAN(dev, vlan): + rc, _ = commands.getstatusoutput('vconfig add %s %d' % + (dev, vlan)) + if rc != 0: + return False + + rc, _ = commands.getstatusoutput('ifconfig %s.%d up' % + (dev, vlan)) + return rc == 0 + +def _destroy_VLAN(dev, vlan): + rc, _ = commands.getstatusoutput('ifconfig %s.%d down' % + (dev, vlan)) + if rc != 0: + return False + + rc, _ = commands.getstatusoutput('vconfig rem %s.%d' % + (dev, vlan)) + return rc == 0 + +class XendPIF(XendBase): + """Representation of a Physical Network Interface.""" + + def getClass(self): + return "PIF" + + def getAttrRO(self): + attrRO = ['network', + 'host', + 'metrics', + 'device', + 'VLAN'] + return XendBase.getAttrRO() + attrRO + + def getAttrRW(self): + attrRW = ['MAC', + 'MTU'] + return XendBase.getAttrRW() + attrRW + + def getAttrInst(self): + attrInst = ['network', + 'device', + 'MAC', + 'MTU', + 'VLAN'] + return attrInst + + def getMethods(self): + methods = ['plug', + 'unplug', + 'destroy'] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = ['create_VLAN'] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def create_phy(self, network_uuid, device, + MAC, MTU): + """ + Called when a new physical PIF is found + Could be a VLAN... + """ + # Create new uuids + pif_uuid = genuuid.createString() + metrics_uuid = genuuid.createString() + + # Create instances + metrics = XendPIFMetrics(metrics_uuid, pif_uuid) + + # Is this a VLAN? + VLANdot = device.split(".") + VLANcolon = device.split(":") + + if len(VLANdot) > 1: + VLAN = VLANdot[1] + device = VLANdot[0] + elif len(VLANcolon) > 1: + VLAN = VLANcolon[1] + device = VLANcolon[0] + else: + VLAN = -1 + + record = { + 'network': network_uuid, + 'device': device, + 'MAC': MAC, + 'MTU': MTU, + 'VLAN': VLAN + } + pif = XendPIF(record, pif_uuid, metrics_uuid) + + return pif_uuid + + def recreate(self, record, uuid): + """Called on xend start / restart""" + pif_uuid = uuid + metrics_uuid = record['metrics'] + + # Create instances + metrics = XendPIFMetrics(metrics_uuid, pif_uuid) + pif = XendPIF(record, pif_uuid, metrics_uuid) + + # If physical PIF, check exists + # If VLAN, create if not exist + ifs = [dev for dev, _1, _2 in linux_get_phy_ifaces()] + if pif.get_VLAN() == -1: + if pif.get_device() not in ifs: + XendBase.destroy(pif) + metrics.destroy() + return None + else: + if pif.get_interface_name() not in ifs: + _create_VLAN(pif.get_device(), pif.get_VLAN()) + pif.plug() + + return pif_uuid + + def create_VLAN(self, device, network_uuid, host_ref, vlan): + """Exposed via API - create a new VLAN from existing VIF""" + + ifs = [name for name, _, _ in linux_get_phy_ifaces()] + + vlan = int(vlan) + + # Check VLAN tag is valid + if vlan < 0 or vlan >= 4096: + raise VLANTagInvalid(vlan) + + # Check device exists + if device not in ifs: + raise InvalidDeviceError(device) + + # Check VLAN doesn't already exist + if "%s.%d" % (device, vlan) in ifs: + raise DeviceExistsError("%s.%d" % (device, vlan)) + + # Check network ref is valid + from XendNetwork import XendNetwork + if network_uuid not in XendNetwork.get_all(): + raise InvalidHandleError("Network", network_uuid) + + # Check host_ref is this host + import XendNode + if host_ref != XendNode.instance().get_uuid(): + raise InvalidHandleError("Host", host_ref) + + # Create the VLAN + _create_VLAN(device, vlan) + + # Create new uuids + pif_uuid = genuuid.createString() + metrics_uuid = genuuid.createString() + + # Create the record + record = { + "device": device, + "MAC": linux_get_mac('%s.%d' % (device, vlan)), + "MTU": linux_get_mtu('%s.%d' % (device, vlan)), + "network": network_uuid, + "VLAN": vlan + } + + # Create instances + metrics = XendPIFMetrics(metrics_uuid, pif_uuid) + pif = XendPIF(record, pif_uuid, metrics_uuid) + + # Not sure if they should be created plugged or not... + pif.plug() + + XendNode.instance().save_PIFs() + return pif_uuid + + create_phy = classmethod(create_phy) + recreate = classmethod(recreate) + create_VLAN = classmethod(create_VLAN) + + def __init__(self, record, uuid, metrics_uuid): + XendBase.__init__(self, uuid, record) + self.metrics = metrics_uuid + + def plug(self): + """Plug the PIF into the network""" + network = XendAPIStore.get(self.network, + "network") + bridge_name = network.get_name_label() + + from xen.util import Brctl + Brctl.vif_bridge_add({ + "bridge": bridge_name, + "vif": self.get_interface_name() + }) + + def unplug(self): + """Unplug the PIF from the network""" + network = XendAPIStore.get(self.network, + "network") + bridge_name = network.get_name_label() + + from xen.util import Brctl + Brctl.vif_bridge_rem({ + "bridge": bridge_name, + "vif": self.get_interface_name() + }) + + def destroy(self): + # Figure out if this is a physical device + if self.get_interface_name() == \ + self.get_device(): + raise PIFIsPhysical() + + self.unplug() + + if _destroy_VLAN(self.get_device(), self.get_VLAN()): + XendBase.destroy(self) + import XendNode + XendNode.instance().save_PIFs() + else: + raise NetworkError("Unable to delete VLAN", self.get_uuid()) + + def get_interface_name(self): + if self.get_VLAN() == -1: + return self.get_device() + else: + return "%s.%d" % (self.get_device(), self.get_VLAN()) + + def get_device(self): + """ + This is the base interface. + For phy if (VLAN == -1) this is same as + if name. + For VLANs, this it the bit before the period + """ + return self.device + + def get_network(self): + return self.network + + def get_host(self): + from xen.xend import XendNode + return XendNode.instance().get_uuid() + + def get_metrics(self): + return self.metrics + + def get_MAC(self): + return self.MAC + + def set_MAC(self, new_mac): + success = linux_set_mac(self.device, new_mac) + if success: + self.MAC = new_mac + import XendNode + XendNode.instance().save_PIFs() + return success + + def get_MTU(self): + return self.MTU + + def set_MTU(self, new_mtu): + success = linux_set_mtu(self.device, new_mtu) + if success: + self.MTU = new_mtu + import XendNode + XendNode.instance().save_PIFs() + return success + + def get_VLAN(self): + return self.VLAN diff --git a/tools/python/xen/xend/XendPIFMetrics.py b/tools/python/xen/xend/XendPIFMetrics.py new file mode 100644 index 0000000..aca8083 --- /dev/null +++ b/tools/python/xen/xend/XendPIFMetrics.py @@ -0,0 +1,59 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006-2007 Xensource Inc. +#============================================================================ + +from XendBase import XendBase + +class XendPIFMetrics(XendBase): + """PIF Metrics.""" + + def getClass(self): + return "PIF_metrics" + + def getAttrRO(self): + attrRO = ['io_read_kbs', + 'io_write_kbs', + 'last_updated', + 'pif'] + return XendBase.getAttrRO() + attrRO + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + + def __init__(self, uuid, pif_uuid): + XendBase.__init__(self, uuid, {}) + self.pif_uuid = pif_uuid + + def get_pif(self): + return self.pif_uuid + + def get_io_read_kbs(self): + return self._get_stat(0) + + def get_io_write_kbs(self): + return self._get_stat(1) + + def _get_stat(self, n): + from xen.xend.XendNode import instance as xennode + #pifname = self.pif.device + #pifs_util = xennode().monitor.get_pifs_util() + #if pifname in pifs_util: + # return pifs_util[pifname][n] + return 0.0 + + def get_last_updated(self): + import xen.xend.XendAPI as XendAPI + return XendAPI.now() diff --git a/tools/python/xen/xend/XendPPCI.py b/tools/python/xen/xend/XendPPCI.py new file mode 100644 index 0000000..9ea6121 --- /dev/null +++ b/tools/python/xen/xend/XendPPCI.py @@ -0,0 +1,158 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2008 NEC Corporation +# Yosuke Iwamatsu +#============================================================================ + +from xen.xend.XendBase import XendBase +from xen.xend.XendBase import XendAPIStore +from xen.xend import uuid as genuuid + +class XendPPCI(XendBase): + """Representation of a physical PCI device.""" + + def getClass(self): + return "PPCI" + + def getAttrRO(self): + attrRO = ['host', + 'domain', + 'bus', + 'slot', + 'func', + 'name', + 'vendor_id', + 'vendor_name', + 'device_id', + 'device_name', + 'revision_id', + 'class_code', + 'class_name', + 'subsystem_vendor_id', + 'subsystem_vendor_name', + 'subsystem_id', + 'subsystem_name', + 'driver'] + return XendBase.getAttrRO() + attrRO + + def getAttrRW(self): + attrRW = [] + return XendBase.getAttrRW() + attrRW + + def getAttrInst(self): + attrInst = [] + return XendBase.getAttrInst() + attrInst + + def getMethods(self): + methods = [] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = [] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def get_by_sbdf(self, domain, bus, slot, func): + for ppci in XendAPIStore.get_all("PPCI"): + if ppci.get_domain() == int(domain, 16) and \ + ppci.get_bus() == int(bus, 16) and \ + ppci.get_slot() == int(slot, 16) and \ + ppci.get_func() == int(func, 16): + return ppci.get_uuid() + return None + + get_by_sbdf = classmethod(get_by_sbdf) + + def __init__(self, uuid, record): + self.domain = record['domain'] + self.bus = record['bus'] + self.slot = record['slot'] + self.func = record['func'] + self.vendor_id = record['vendor_id'] + self.vendor_name = record['vendor_name'] + self.device_id = record['device_id'] + self.device_name = record['device_name'] + self.revision_id = record['revision_id'] + self.class_code = record['class_code'] + self.class_name = record['class_name'] + self.subsystem_vendor_id = record['subsystem_vendor_id'] + self.subsystem_vendor_name = record['subsystem_vendor_name'] + self.subsystem_id = record['subsystem_id'] + self.subsystem_name = record['subsystem_name'] + self.driver = record['driver'] + XendBase.__init__(self, uuid, record) + + def get_host(self): + from xen.xend import XendNode + return XendNode.instance().get_uuid() + + def get_domain(self): + return self.domain + + def get_bus(self): + return self.bus + + def get_slot(self): + return self.slot + + def get_func(self): + return self.func + + def get_name(self): + return "%04x:%02x:%02x.%01x" % (self.domain, self.bus, self.slot, + self.func) + + def get_vendor_id(self): + return self.vendor_id + + def get_vendor_name(self): + return self.vendor_name + + def get_device_id(self): + return self.device_id + + def get_device_name(self): + return self.device_name + + def get_class_code(self): + return self.class_code + + def get_class_name(self): + return self.class_name + + def get_revision_id(self): + return self.revision_id + + def get_subsystem_vendor_id(self): + return self.subsystem_vendor_id + + def get_subsystem_vendor_name(self): + return self.subsystem_vendor_name + + def get_subsystem_id(self): + return self.subsystem_id + + def get_subsystem_name(self): + return self.subsystem_name + + def get_driver(self): + return self.driver + diff --git a/tools/python/xen/xend/XendPSCSI.py b/tools/python/xen/xend/XendPSCSI.py new file mode 100644 index 0000000..9026c61 --- /dev/null +++ b/tools/python/xen/xend/XendPSCSI.py @@ -0,0 +1,143 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright FUJITSU LIMITED 2008 +# Masaki Kanno +#============================================================================ + +from xen.xend.XendBase import XendBase +from xen.xend.XendBase import XendAPIStore +from xen.xend import uuid as genuuid + +class XendPSCSI(XendBase): + """Representation of a physical SCSI device.""" + + def getClass(self): + return "PSCSI" + + def getAttrRO(self): + attrRO = ['host', + 'physical_host', + 'physical_channel', + 'physical_target', + 'physical_lun', + 'physical_HCTL', + 'vendor_name', + 'model', + 'type_id', + 'type', + 'dev_name', + 'sg_name', + 'revision', + 'scsi_id', + 'scsi_level'] + return XendBase.getAttrRO() + attrRO + + def getAttrRW(self): + attrRW = [] + return XendBase.getAttrRW() + attrRW + + def getAttrInst(self): + attrInst = [] + return XendBase.getAttrInst() + attrInst + + def getMethods(self): + methods = [] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = [] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getAttrRW = classmethod(getAttrRW) + getAttrInst = classmethod(getAttrInst) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def get_by_HCTL(self, physical_HCTL): + for pscsi in XendAPIStore.get_all("PSCSI"): + if pscsi.get_physical_HCTL() == physical_HCTL: + return pscsi.get_uuid() + return None + + get_by_HCTL = classmethod(get_by_HCTL) + + def __init__(self, uuid, record): + self.physical_HCTL = record['physical_HCTL'] + self.vendor_name = record['vendor_name'] + self.model = record['model'] + self.type_id = record['type_id'] + self.type = record['type'] + self.dev_name = record['dev_name'] + self.sg_name = record['sg_name'] + self.revision = record['revision'] + self.scsi_id = record['scsi_id'] + self.scsi_level = record['scsi_level'] + + p_hctl = self.physical_HCTL.split(':') + self.physical_host = int(p_hctl[0]) + self.physical_channel = int(p_hctl[1]) + self.physical_target = int(p_hctl[2]) + self.physical_lun = int(p_hctl[3]) + + XendBase.__init__(self, uuid, record) + + def get_host(self): + from xen.xend import XendNode + return XendNode.instance().get_uuid() + + def get_physical_host(self): + return self.physical_host + + def get_physical_channel(self): + return self.physical_channel + + def get_physical_target(self): + return self.physical_target + + def get_physical_lun(self): + return self.physical_lun + + def get_physical_HCTL(self): + return self.physical_HCTL + + def get_vendor_name(self): + return self.vendor_name + + def get_model(self): + return self.model + + def get_type_id(self): + return self.type_id + + def get_type(self): + return self.type + + def get_dev_name(self): + return self.dev_name + + def get_sg_name(self): + return self.sg_name + + def get_revision(self): + return self.revision + + def get_scsi_id(self): + return self.scsi_id + + def get_scsi_level(self): + return self.scsi_level + diff --git a/tools/python/xen/xend/XendProtocol.py b/tools/python/xen/xend/XendProtocol.py new file mode 100644 index 0000000..34af4a4 --- /dev/null +++ b/tools/python/xen/xend/XendProtocol.py @@ -0,0 +1,225 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd. +#============================================================================ + +import socket +import httplib +import time +import types + +from encode import * +from xen.xend import sxp + +from xen.xend import XendOptions + +DEBUG = 0 + +HTTP_OK = 200 +HTTP_CREATED = 201 +HTTP_ACCEPTED = 202 +HTTP_NO_CONTENT = 204 + + +xoptions = XendOptions.instance() + + +class XendError(RuntimeError): + """Error class for 'expected errors' when talking to xend. + """ + pass + +class XendRequest: + """A request to xend. + """ + + def __init__(self, url, method, args): + """Create a request. Sets up the headers, argument data, and the + url. + + @param url: the url to request + @param method: request method, GET or POST + @param args: dict containing request args, if any + """ + if url.proto != 'http': + raise ValueError('Invalid protocol: ' + url.proto) + (hdr, data) = encode_data(args) + if args and method == 'GET': + url.query = data + data = None + if method == "POST" and url.path.endswith('/'): + url.path = url.path[:-1] + + self.headers = hdr + self.data = data + self.url = url + self.method = method + +class XendClientProtocol: + """Abstract class for xend clients. + """ + def xendRequest(self, url, method, args=None): + """Make a request to xend. + Implement in a subclass. + + @param url: xend request url + @param method: http method: POST or GET + @param args: request arguments (dict) + """ + raise NotImplementedError() + + def xendGet(self, url, args=None): + """Make a xend request using HTTP GET. + Requests using GET are usually 'safe' and may be repeated without + nasty side-effects. + + @param url: xend request url + @param data: request arguments (dict) + """ + return self.xendRequest(url, "GET", args) + + def xendPost(self, url, args): + """Make a xend request using HTTP POST. + Requests using POST potentially cause side-effects, and should + not be repeated unless you really want to repeat the side + effect. + + @param url: xend request url + @param args: request arguments (dict) + """ + return self.xendRequest(url, "POST", args) + + def handleStatus(self, _, status, message): + """Handle the status returned from the request. + """ + status = int(status) + if status in [ HTTP_NO_CONTENT ]: + return None + if status not in [ HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED ]: + return self.handleException(XendError(message)) + return 'ok' + + def handleResponse(self, data): + """Handle the data returned in response to the request. + """ + if data is None: return None + typ = self.getHeader('Content-Type') + if typ != sxp.mime_type: + return data + try: + pin = sxp.Parser() + pin.input(data); + pin.input_eof() + val = pin.get_val() + except sxp.ParseError, err: + return self.handleException(err) + if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err': + err = XendError(val[1]) + return self.handleException(err) + return val + + def handleException(self, err): + """Handle an exception during the request. + May be overridden in a subclass. + """ + raise err + + def getHeader(self, key): + """Get a header from the response. + Case is ignored in the key. + + @param key: header key + @return: header + """ + raise NotImplementedError() + +class HttpXendClientProtocol(XendClientProtocol): + """A synchronous xend client. This will make a request, wait for + the reply and return the result. + """ + + resp = None + request = None + + def makeConnection(self, url): + return httplib.HTTPConnection(url.location()) + + def makeRequest(self, url, method, args): + return XendRequest(url, method, args) + + def xendRequest(self, url, method, args=None): + """Make a request to xend. + + @param url: xend request url + @param method: http method: POST or GET + @param args: request arguments (dict) + """ + retries = 0 + while retries < 2: + self.request = self.makeRequest(url, method, args) + conn = self.makeConnection(url) + try: + if DEBUG: conn.set_debuglevel(1) + conn.request(method, url.fullpath(), self.request.data, + self.request.headers) + try: + resp = conn.getresponse() + self.resp = resp + val = self.handleStatus(resp.version, resp.status, + resp.reason) + if val is None: + data = None + else: + data = resp.read() + val = self.handleResponse(data) + return val + except httplib.BadStatusLine: + retries += 1 + time.sleep(5) + finally: + conn.close() + + raise XendError("Received invalid response from Xend, twice.") + + + def getHeader(self, key): + return self.resp.getheader(key) + + +class UnixConnection(httplib.HTTPConnection): + """Subclass of Python library HTTPConnection that uses a unix-domain socket. + """ + + def __init__(self, path): + httplib.HTTPConnection.__init__(self, 'localhost') + self.path = path + + def connect(self): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(self.path) + self.sock = sock + +class UnixXendClientProtocol(HttpXendClientProtocol): + """A synchronous xend client using a unix-domain socket. + """ + + def __init__(self, path=None): + if path is None: + path = xoptions.get_xend_unix_path() + self.path = path + + def makeConnection(self, _): + return UnixConnection(self.path) diff --git a/tools/python/xen/xend/XendQCoWStorageRepo.py b/tools/python/xen/xend/XendQCoWStorageRepo.py new file mode 100644 index 0000000..281559b --- /dev/null +++ b/tools/python/xen/xend/XendQCoWStorageRepo.py @@ -0,0 +1,352 @@ +#!/usr/bin/python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006,2007 XenSource Ltd. +#============================================================================ +# +# The default QCOW Xen API Storage Repository +# + +import commands +import logging +import os +import stat +import threading +import re +import sys +import struct + +from xen.util import mkdir +import uuid +from XendPBD import XendPBD +from XendError import XendError +from XendVDI import * +from XendTask import XendTask +from XendStorageRepository import XendStorageRepository +from XendOptions import instance as xendoptions + +XEND_STORAGE_NO_MAXIMUM = sys.maxint +XEND_STORAGE_QCOW_FILENAME = "%s.qcow" +XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml" +QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -r %d %s" + +MB = 1024 * 1024 + +log = logging.getLogger("xend.XendQCowStorageRepo") + + +def qcow_virtual_size(qcow_file): + """Read the first 32 bytes of the QCoW header to determine its size. + + See: http://www.gnome.org/~markmc/qcow-image-format.html. + """ + try: + qcow_header = open(qcow_file, 'rb').read(32) + parts = struct.unpack('>IIQIIQ', qcow_header) + return parts[-1] + except IOError: + return -1 + +class XendQCoWStorageRepo(XendStorageRepository): + """A simple file backed QCOW Storage Repository. + + This class exposes the interface to create VDI's via the + Xen API. The backend is a file-backed QCOW format that is stored + in XEND_STORAGE_DIR or any that is specified in the constructor. + + The actual images are created in the format .img and .qcow. + """ + + def __init__(self, sr_uuid, + sr_type = "qcow_file", + name_label = "QCoW", + name_description = "Xend QCoW Storage Repository", + storage_max = XEND_STORAGE_NO_MAXIMUM): + """ + @keyword storage_max: Maximum disk space to use in bytes. + @type storage_max: int + + @ivar storage_free: storage space free for this repository + @ivar images: mapping of all the images. + @type images: dictionary by image uuid. + @ivar lock: lock to provide thread safety. + """ + + XendStorageRepository.__init__(self, sr_uuid, sr_type, name_label, + name_description, storage_max) + self.storage_free = 0 + self.location = xendoptions().get_xend_storage_path() + self._refresh() + + def get_record(self, transient = True): + retval = {'uuid': self.uuid, + 'name_label': self.name_label, + 'name_description': self.name_description, + 'virtual_allocation': self.virtual_allocation, + 'physical_utilisation': self.physical_utilisation, + 'physical_size': self.physical_size, + 'type': self.type, + 'content_type': self.content_type, + 'VDIs': self.images.keys(), + 'PBDs': XendPBD.get_by_SR(self.uuid)} + + if self.physical_size == XEND_STORAGE_NO_MAXIMUM: + stfs = os.statvfs(self.location) + retval['physical_size'] = stfs.f_blocks * stfs.f_frsize + + return retval + + def _refresh(self): + """Internal function that refreshes the state of the disk and + updates the list of images available. + """ + self.lock.acquire() + try: + mkdir.parents(self.location, stat.S_IRWXU) + + # scan the directory and populate self.images + virtual_alloc = 0 + physical_used = 0 + seen_images = [] + for filename in os.listdir(self.location): + if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]: + image_uuid = filename[:-5] + seen_images.append(image_uuid) + + qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid + cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid + qcow_path = os.path.join(self.location, qcow_file) + cfg_path = os.path.join(self.location, cfg_file) + + phys_size = os.stat(qcow_path).st_size + virt_size = qcow_virtual_size(qcow_path) + + # add this image if we haven't seen it before + if image_uuid not in self.images: + vdi = XendQCoWVDI(image_uuid, self.uuid, + qcow_path, cfg_path, + virt_size, phys_size) + + if cfg_path and os.path.exists(cfg_path): + try: + vdi.load_config(cfg_path) + except: + log.error('Corrupt VDI configuration file %s' % + cfg_path) + + self.images[image_uuid] = vdi + + physical_used += phys_size + virtual_alloc += virt_size + + # remove images that aren't valid + for image_uuid in self.images.keys(): + if image_uuid not in seen_images: + try: + os.unlink(self.images[image_uuid].qcow_path) + except OSError: + pass + del self.images[image_uuid] + + self.virtual_allocation = virtual_alloc + self.physical_utilisation = physical_used + + # update free storage if we have to track that + if self.physical_size == XEND_STORAGE_NO_MAXIMUM: + self.storage_free = self._get_free_space() + else: + self.storage_free = self.physical_size - self.virtual_allocation + + finally: + self.lock.release() + + def _get_free_space(self): + """Returns the amount of free space in bytes available in the storage + partition. Note that this may not be used if the storage repository + is initialised with a maximum size in storage_max. + + @rtype: int + """ + stfs = os.statvfs(self.location) + return stfs.f_bavail * stfs.f_frsize + + def _has_space_available_for(self, size_bytes): + """Returns whether there is enough space for an image in the + partition which the storage_dir resides on. + + @rtype: bool + """ + if self.physical_size != XEND_STORAGE_NO_MAXIMUM: + return self.storage_free > size_bytes + + bytes_free = self._get_free_space() + if size_bytes < bytes_free: + return True + return False + + def _create_image_files(self, desired_size_bytes): + """Create an image and return its assigned UUID. + + @param desired_size_bytes: Desired image size in bytes + @type desired_size_bytes: int + @rtype: string + @return: uuid + + @raises XendError: If an error occurs. + """ + self.lock.acquire() + try: + if not self._has_space_available_for(desired_size_bytes): + raise XendError("Not enough space (need %d)" % + desired_size_bytes) + + image_uuid = uuid.createString() + qcow_path = os.path.join(self.location, + XEND_STORAGE_QCOW_FILENAME % image_uuid) + + if qcow_path and os.path.exists(qcow_path): + raise XendError("Image with same UUID alreaady exists:" % + image_uuid) + + cmd = QCOW_CREATE_COMMAND % (desired_size_bytes/MB, qcow_path) + rc, output = commands.getstatusoutput(cmd) + + if rc != 0: + # cleanup the image file + os.unlink(qcow_path) + raise XendError("Failed to create QCOW Image: %s" % output) + + self._refresh() + return image_uuid + finally: + self.lock.release() + + def destroy_vdi(self, image_uuid): + """Destroy an image that is managed by this storage repository. + + @param image_uuid: Image UUID + @type image_uuid: String + @rtype: String + """ + self.lock.acquire() + try: + if image_uuid in self.images: + # TODO: check if it is being used? + qcow_path = self.images[image_uuid].qcow_path + cfg_path = self.images[image_uuid].cfg_path + try: + os.unlink(qcow_path) + if cfg_path and os.path.exists(cfg_path): + os.unlink(cfg_path) + except OSError: + log.exception("Failed to destroy image") + del self.images[image_uuid] + self._refresh() + return True + finally: + self.lock.release() + + return False + + def list_images(self): + """ List all the available images by UUID. + + @rtype: list of strings. + @return: list of UUIDs + """ + self.lock.acquire() + try: + return self.images.keys() + finally: + self.lock.release() + + def free_space_bytes(self): + """Returns the amount of available space in KB. + @rtype: int + """ + self.lock.acquire() + try: + return self.storage_free + finally: + self.lock.release() + + def total_space_bytes(self): + """Returns the total usable space of the storage repo in KB. + @rtype: int + """ + self.lock.acquire() + try: + if self.physical_size == XEND_STORAGE_NO_MAXIMUM: + stfs = os.statvfs(self.location) + return stfs.f_blocks * stfs.f_frsize + else: + return self.physical_size + finally: + self.lock.release() + + def used_space_bytes(self): + """Returns the total amount of space used by this storage repository. + @rtype: int + """ + self.lock.acquire() + try: + return self.physical_utilisation + finally: + self.lock.release() + + def virtual_allocation(self): + """Returns the total virtual space allocated within the storage repo. + @rtype: int + """ + self.lock.acquire() + try: + return self.virtual_allocation + finally: + self.lock.release() + + + def create_vdi(self, vdi_struct): + image_uuid = None + try: + size_bytes = int(vdi_struct.get('virtual_size', 0)) + + image_uuid = self._create_image_files(size_bytes) + + image = self.images[image_uuid] + image_cfg = { + 'virtual_size': size_bytes, + 'type': vdi_struct.get('type', 'system'), + 'name_label': vdi_struct.get('name_label', ''), + 'name_description': vdi_struct.get('name_description', ''), + 'sharable': bool(vdi_struct.get('sharable', False)), + 'read_only': bool(vdi_struct.get('read_only', False)), + } + + # load in configuration from vdi_struct + image.load_config_dict(image_cfg) + + # save configuration to file + cfg_filename = XEND_STORAGE_VDICFG_FILENAME % image_uuid + cfg_path = os.path.join(self.location, cfg_filename) + image.save_config(cfg_path) + + except Exception, e: + # cleanup before raising exception + if image_uuid: + self.destroy_vdi(image_uuid) + + raise + + return image_uuid diff --git a/tools/python/xen/xend/XendStateStore.py b/tools/python/xen/xend/XendStateStore.py new file mode 100644 index 0000000..245463f --- /dev/null +++ b/tools/python/xen/xend/XendStateStore.py @@ -0,0 +1,230 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (c) 2006 Xensource Inc. +#============================================================================ + +import os + +from xen.xend import uuid +from xen.xend.XendLogging import log +from xml.dom import minidom +from xml.dom import Node + +class XendStateStore: + """Manages persistent storage of Xend's internal state, mainly + relating to API objects. + + It stores objects atomically in the file system as flat XML files + categorised by their 'class'. + + For example: + + /var/lib/xend/state/cpu.xml will contain the host cpu state + /var/lib/xend/state/sr.xml will contain the storage repository state. + + For the application, it will load the state via this class: + + load_state('cpu') will return a marshalled dictionary object + containing the cpu state. + + save_state('cpu', dict) will save the state contained in the dictionary + object about the 'cpu'. + + The state is stored where each top level element has a UUID in its + attributes. eg: + + host['49c01812-3c28-1ad4-a59d-2a3f81b13ec2'] = { + 'name': 'norwich', + 'desc': 'Test Xen Host', + 'cpu': {'6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0': , + '669df3b8-62be-4e61-800b-bbe8ee63a760': } + } + + will turn into: + + + + norwich + Test Xen Host + + + + + + + + Note that it only dumps one level, so the references to CPU are + stored in a separate file. + + """ + + def __init__(self, base = "/var/lib/xend/state"): + self.base = base + if not os.path.exists(self.base): + os.makedirs(self.base) + + def _xml_file(self, cls): + """Return the absolute filename of the XML state storage file. + + @param cls: name of the class. + @type cls: string + @rtype: string + @return absolute filename of XML file to write/read from. + """ + return os.path.join(self.base, '%s.xml' % cls) + + def load_state(self, cls): + """Load the saved state of a class from persistent XML storage. + + References loaded from the XML will just point to an empty + dictionary which the caller will need to replace manually. + + @param cls: name of the class to load. + @type cls: string + @rtype: dict + """ + + xml_path = self._xml_file(cls) + if not os.path.exists(xml_path): + return {} + + dom = minidom.parse(xml_path) + root = dom.documentElement + state = {} + + for child in root.childNodes: + if child.nodeType != Node.ELEMENT_NODE: + continue # skip non element nodes + + uuid = child.getAttribute('uuid').encode('utf8') + cls_dict = {} + for val_elem in child.childNodes: + if val_elem.nodeType != Node.ELEMENT_NODE: + continue # skip non element nodes + + val_name = val_elem.tagName + val_type = val_elem.getAttribute('type').encode('utf8') + val_uuid = val_elem.getAttribute('uuid').encode('utf8') + val_elem.normalize() + val_text = '' + if val_elem.firstChild: + val_text = val_elem.firstChild.nodeValue.strip() + + if val_type == 'list': + cls_dict[val_name] = [] + for item in val_elem.childNodes: + if item.nodeType != Node.ELEMENT_NODE: + continue # skip non element nodes + cls_dict[val_name].append(item.getAttribute('uuid')) + elif val_type == 'dict': + cls_dict[val_name] = {} + for item in val_elem.childNodes: + if item.nodeType != Node.ELEMENT_NODE: + continue # skip non element nodes + k = item.getAttribute('key').encode('utf8') + v = item.getAttribute('value').encode('utf8') + cls_dict[val_name][k] = v + elif val_type == 'string': + cls_dict[val_name] = val_text.encode('utf8') + elif val_type == 'float': + cls_dict[val_name] = float(val_text) + elif val_type == 'int': + cls_dict[val_name] = int(val_text) + elif val_type == 'bool': + cls_dict[val_name] = bool(int(val_text)) + state[uuid] = cls_dict + + return state + + def save_state(self, cls, state): + """Save a Xen API record struct into an XML persistent storage + for future loading when Xend restarts. + + If we encounter a dictionary or a list, we only store the + keys because they are going to be UUID references to another + object. + + @param cls: Class name (singular) of the record + @type cls: string + @param state: a Xen API struct of the state of the class. + @type state: dict + @rtype: None + """ + xml_path = self._xml_file(cls) + + doc = minidom.getDOMImplementation().createDocument(None, + cls + 's', + None) + root = doc.documentElement + + # Marshall a dictionary into our custom XML file format. + for uuid, info in state.items(): + node = doc.createElement(cls) + root.appendChild(node) + node.setAttribute('uuid', uuid) + + for key, val in info.items(): + store_val = val + store_type = None + + # deal with basic types + if type(val) in (str, unicode): + store_val = val + store_type = 'string' + elif type(val) == int: + store_val = str(val) + store_type = 'int' + elif type(val) == float: + store_val = str(val) + store_type = 'float' + elif type(val) == bool: + store_val = str(int(val)) + store_type = 'bool' + + if store_type is not None: + val_node = doc.createElement(key) + val_node.setAttribute('type', store_type) + node.appendChild(val_node) + # attach the value + val_text = doc.createTextNode(store_val) + val_node.appendChild(val_text) + continue + + # deal with dicts and lists + if type(val) == dict: + val_node = doc.createElement(key) + val_node.setAttribute('type', 'dict') + for val_item in val.keys(): + tmp = doc.createElement("item") + if key in ['other_config', 'device_config']: + tmp.setAttribute('key', str(val_item)) + tmp.setAttribute('value', str(val[val_item])) + else: + tmp.setAttribute('uuid', val_uuid) + val_node.appendChild(tmp) + node.appendChild(val_node) + elif type(val) in (list, tuple): + val_node = doc.createElement(key) + val_node.setAttribute('type', 'list') + for val_uuid in val: + tmp = doc.createElement("item") + tmp.setAttribute('uuid', val_uuid) + val_node.appendChild(tmp) + node.appendChild(val_node) + + open(xml_path, 'w').write(doc.toprettyxml()) + + diff --git a/tools/python/xen/xend/XendStorageRepository.py b/tools/python/xen/xend/XendStorageRepository.py new file mode 100644 index 0000000..d37e658 --- /dev/null +++ b/tools/python/xen/xend/XendStorageRepository.py @@ -0,0 +1,105 @@ +#!/usr/bin/python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006, 2007 XenSource Ltd. +#============================================================================ +# +# Abstract class for XendStorageRepositories +# + +import threading +import sys + +from XendError import XendError +from XendVDI import * +from XendPBD import XendPBD + +XEND_STORAGE_NO_MAXIMUM = sys.maxint + +class XendStorageRepository: + """ Base class for Storage Repos. """ + + def __init__(self, uuid, + sr_type = "unknown", + name_label = 'Unknown', + name_description = 'Not Implemented', + storage_max = XEND_STORAGE_NO_MAXIMUM): + """ + @keyword storage_max: Maximum disk space to use in bytes. + @type storage_max: int + + @ivar storage_free: storage space free for this repository + @ivar images: mapping of all the images. + @type images: dictionary by image uuid. + @ivar lock: lock to provide thread safety. + """ + + # XenAPI Parameters + self.uuid = uuid + self.type = sr_type + self.name_label = name_label + self.name_description = name_description + self.images = {} + + self.physical_size = storage_max + self.physical_utilisation = 0 + self.virtual_allocation = 0 + self.content_type = '' + + self.lock = threading.RLock() + + def get_record(self, transient = True): + retval = {'uuid': self.uuid, + 'name_label': self.name_label, + 'name_description': self.name_description, + 'virtual_allocation': self.virtual_allocation, + 'physical_utilisation': self.physical_utilisation, + 'physical_size': self.physical_size, + 'type': self.type, + 'content_type': self.content_type, + 'VDIs': self.images.keys()} + if not transient: + retval ['PBDs'] = XendPBD.get_by_SR(self.uuid) + return retval + + + def is_valid_vdi(self, vdi_uuid): + return (vdi_uuid in self.images) + + def get_vdi_by_uuid(self, image_uuid): + self.lock.acquire() + try: + return self.images.get(image_uuid) + finally: + self.lock.release() + + def get_vdi_by_name_label(self, label): + self.lock.acquire() + try: + for image_uuid, image in self.images.items(): + if image.name_label == label: + return image_uuid + return None + finally: + self.lock.release() + + def get_vdis(self): + return self.images.keys() + + def create_vdi(self, vdi_struct): + raise NotImplementedError() + + def destroy_vdi(self, vdi_struct): + raise NotImplementedError() diff --git a/tools/python/xen/xend/XendTask.py b/tools/python/xen/xend/XendTask.py new file mode 100644 index 0000000..5045a06 --- /dev/null +++ b/tools/python/xen/xend/XendTask.py @@ -0,0 +1,224 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 XenSource Ltd +#============================================================================ + +from xen.xend.XendAPIConstants import XEN_API_TASK_STATUS_TYPE +from xen.xend.XendLogging import log +import thread +import threading + +class XendTask(threading.Thread): + """Represents a Asynchronous Task used by Xen API. + + Basically proxies the callable object in a thread and returns the + results via self.{type,result,error_info}. + + @cvar task_progress: Thread local storage for progress tracking. + It is a dict indexed by thread_id. Note that the + thread_id may be reused when the previous + thread with the thread_id ends. + + @cvar task_progress_lock: lock on thread access to task_progress + + """ + + # progress stack: + # thread_id : [(start_task, end_task), + # (start_sub_task, end_sub_task)..] + # example : (0, 100), (50, 100) (50, 100) ... + # That would mean that the task is 75% complete. + # as it is 50% of the last 50% of the task. + + task_progress = {} + task_progress_lock = threading.Lock() + + def __init__(self, uuid, func, args, func_name, return_type, label, desc, + session): + """ + @param uuid: UUID of the task + @type uuid: string + @param func: Method to call (from XendAPI) + @type func: callable object + @param args: arguments to pass to function + @type args: list or tuple + @param label: name label of the task. + @type label: string + @param desc: name description of the task. + @type desc: string + @param func_name: function name, eg ('VM.start') + @type desc: string + """ + + threading.Thread.__init__(self) + self.status_lock = threading.Lock() + self.status = XEN_API_TASK_STATUS_TYPE[0] + + self.progress = 0 + self.type = return_type + self.uuid = uuid + + self.result = None + self.error_info = [] + + self.name_label = label or func.__name__ + self.name_description = desc + self.thread_id = 0 + + self.func_name = func_name + self.func = func + self.args = args + + self.session = session + + def set_status(self, new_status): + self.status_lock.acquire() + try: + self.status = new_status + finally: + self.status_lock.release() + + def get_status(self): + self.status_lock.acquire() + try: + return self.status + finally: + self.status_lock.release() + + def run(self): + """Runs the method and stores the result for later access. + + Is invoked by threading.Thread.start(). + """ + + self.thread_id = thread.get_ident() + self.task_progress_lock.acquire() + try: + self.task_progress[self.thread_id] = {} + self.progress = 0 + finally: + self.task_progress_lock.release() + + try: + result = self.func(*self.args) + if result['Status'] == 'Success': + self.result = result['Value'] + self.set_status(XEN_API_TASK_STATUS_TYPE[1]) + else: + self.error_info = result['ErrorDescription'] + self.set_status(XEN_API_TASK_STATUS_TYPE[2]) + except Exception, e: + log.exception('Error running Async Task') + self.error_info = ['INTERNAL ERROR', str(e)] + self.set_status(XEN_API_TASK_STATUS_TYPE[2]) + + self.task_progress_lock.acquire() + try: + del self.task_progress[self.thread_id] + self.progress = 100 + finally: + self.task_progress_lock.release() + + def get_record(self): + """Returns a Xen API compatible record.""" + return { + 'uuid': self.uuid, + 'name_label': self.name_label, + 'name_description': self.name_description, + 'status': self.status, + 'progress': self.get_progress(), + 'type': self.type, + 'result': self.result, + 'error_info': self.error_info, + 'allowed_operations': {}, + 'session': self.session, + } + + def get_progress(self): + """ Checks the thread local progress storage. """ + if self.status != XEN_API_TASK_STATUS_TYPE[0]: + return 100 + + self.task_progress_lock.acquire() + try: + # Pop each progress range in the stack and map it on to + # the next progress range until we find out cumulative + # progress based on the (start, end) range of each level + start = 0 + prog_stack = self.task_progress.get(self.thread_id, [])[:] + if len(prog_stack) > 0: + start, stop = prog_stack.pop() + while prog_stack: + new_start, new_stop = prog_stack.pop() + start = new_start + ((new_stop - new_start)/100.0 * start) + + # only update progress if it increases, this will prevent + # progress from going backwards when tasks are popped off + # the stack + if start > self.progress: + self.progress = int(start) + finally: + self.task_progress_lock.release() + + return self.progress + + + def log_progress(cls, progress_min, progress_max, + func, *args, **kwds): + """ Callable function wrapper that logs the progress of the + function to thread local storage for task progress calculation. + + This is a class method so other parts of Xend will update + the task progress by calling: + + XendTask.push_progress(progress_min, progress_max, + func, *args, **kwds) + + The results of the progress is stored in thread local storage + and the result of the func(*args, **kwds) is returned back + to the caller. + + """ + thread_id = thread.get_ident() + retval = None + + # Log the start of the method + cls.task_progress_lock.acquire() + try: + if type(cls.task_progress.get(thread_id)) != list: + cls.task_progress[thread_id] = [] + + cls.task_progress[thread_id].append((progress_min, + progress_max)) + finally: + cls.task_progress_lock.release() + + # Execute the method + retval = func(*args, **kwds) + + # Log the end of the method by popping the progress range + # off the stack. + cls.task_progress_lock.acquire() + try: + cls.task_progress[thread_id].pop() + finally: + cls.task_progress_lock.release() + + return retval + + log_progress = classmethod(log_progress) + + + diff --git a/tools/python/xen/xend/XendTaskManager.py b/tools/python/xen/xend/XendTaskManager.py new file mode 100644 index 0000000..6282078 --- /dev/null +++ b/tools/python/xen/xend/XendTaskManager.py @@ -0,0 +1,110 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 XenSource Ltd +#============================================================================ + +""" +Task Manager for Xen API asynchronous tasks. + +Stores all tasks in a simple dictionary in module's own local storage to +avoid the 'instance()' methods. + +Tasks are indexed by UUID. + +""" + +from xen.xend.XendTask import XendTask +from xen.xend import uuid +import threading + +tasks = {} +tasks_lock = threading.Lock() + +def create_task(func, args, func_name, return_type, label, session): + """Creates a new Task and registers it with the XendTaskManager. + + @param func: callable object XMLRPC method + @type func: callable object + @param args: tuple or list of arguments + @type args: tuple or list + @param func_name: XMLRPC method name, so we can estimate the progress + @type func_name: string + + @return: Task UUID + @rtype: string. + """ + task_uuid = uuid.createString() + try: + tasks_lock.acquire() + task = XendTask(task_uuid, func, args, func_name, return_type, label, + '', session) + tasks[task_uuid] = task + finally: + tasks_lock.release() + + task.start() + + return task_uuid + +def destroy_task(task_uuid): + """Destroys a task. + + @param task_uuid: Task UUID + @type task_uuid: string. + """ + try: + tasks_lock.acquire() + if task_uuid in tasks: + del tasks[task_uuid] + finally: + tasks_lock.release() + +def get_all_tasks(): + """ Returns all the UUID of tracked tasks, completed or pending. + + @returns: list of UUIDs + @rtype: list of strings + """ + try: + tasks_lock.acquire() + return tasks.keys() + finally: + tasks_lock.release() + +def get_task(task_uuid): + """ Retrieves a task by UUID. + + @rtype: XendTask or None + @return: Task denoted by UUID. + """ + try: + tasks_lock.acquire() + return tasks.get(task_uuid) + finally: + tasks_lock.release() + +def get_tasks_by_name(task_name): + """ Retrieves a task by UUID. + + @rtype: XendTask or None + @return: Task denoted by UUID. + """ + try: + tasks_lock.acquire() + return [t.uuid for t in tasks if t.name_label == name] + finally: + tasks_lock.release() + + diff --git a/tools/python/xen/xend/XendVDI.py b/tools/python/xen/xend/XendVDI.py new file mode 100644 index 0000000..0ef432b --- /dev/null +++ b/tools/python/xen/xend/XendVDI.py @@ -0,0 +1,211 @@ +#!/usr/bin/python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ +# +# Representation of a Xen API VDI +# + +import os + +from xen.util.xmlrpclib2 import stringify +from xmlrpclib import dumps, loads +from xen.util import xsconstants +import xen.util.xsm.xsm as security +from xen.xend.XendError import SecurityError + +KB = 1024 +MB = 1024 * 1024 + +class AutoSaveObject(object): + + def __init__(self): + self.cfg_path = None + self.auto_save = True + object + + def save_config(self, cfg_file = None): + raise NotImplementedError() + + def __setattr__(self, name, value): + """A very simple way of making sure all attribute changes are + flushed to disk. + """ + object.__setattr__(self, name, value) + if name != 'auto_save' and getattr(self, 'auto_save', False): + self.save_config() + +class XendVDI(AutoSaveObject): + """Generic Xen API compatible VDI representation. + + @cvar SAVED_CFG: list of configuration attributes to save. + @cvar SAVED_CFG_INT: list of configurations that should be ints. + """ + + SAVED_CFG = ['name_label', + 'name_description', + 'virtual_size', + 'physical_utilisation', + 'sharable', + 'read_only'] + + SAVED_CFG_INT = ['sector_size', 'virtual_size', 'physical_utilisation'] + + def __init__(self, uuid, sr_uuid): + self.uuid = uuid + self.sr_uuid = sr_uuid + self.name_label = "" + self.name_description = "" + self.virtual_size = 0 + self.physical_utilisation = 0 + self.sharable = False + self.read_only = False + self.type = "system" + self.other_config = {} + self.vbds = [] + + def addVBD(self, vbd_ref): + self.vbds.append(vbd_ref) + + def removeVBD(self, vbd_ref): + self.vbds.remove(vbd_ref) + + def getVBDs(self): + return self.vbds + + def load_config_dict(self, cfg): + """Loads configuration into the object from a dict. + + @param cfg: configuration dict + @type cfg: dict + """ + self.auto_save = False + for key in self.SAVED_CFG: + if key in cfg: + if key in self.SAVED_CFG_INT: + setattr(self, key, int(cfg[key])) + else: + setattr(self, key, cfg[key]) + self.auto_save = True + + def load_config(self, cfg_path): + """Loads configuration from an XMLRPC parameter format. + + @param cfg_path: configuration file path + @type cfg_path: type + @rtype: bool + @return: Successful or not. + """ + try: + cfg, _ = loads(open(cfg_path).read()) + cfg = cfg[0] + self.load_config_dict(cfg) + self.cfg_path = cfg_path + except IOError, e: + return False + + return True + + def save_config(self, cfg_path = None): + """Saves configuration at give path in XMLRPC parameter format. + + If cfg_path is not give, it defaults to the where the VDI + configuration as loaded if it load_config was called. + + @keyword cfg_path: optional configuration file path + @rtype: bool + @return: Successful or not. + """ + try: + if not cfg_path and not self.cfg_path: + return False + + if not cfg_path: + cfg_path = self.cfg_path + + cfg = {} + for key in self.SAVED_CFG: + try: + cfg[key] = getattr(self, key) + except AttributeError: + pass + open(cfg_path, 'w').write(dumps((stringify(cfg),), + allow_none = True)) + except IOError, e: + return False + + return True + + def get_record(self, transient = True): + return {'uuid': self.uuid, + 'name_label': self.name_label, + 'name_description': self.name_description, + 'virtual_size': self.virtual_size, + 'physical_utilisation': self.physical_utilisation, + 'sharable': False, + 'readonly': False, + 'SR': self.sr_uuid, + 'other_config': self.other_config, + 'VBDs': []} + + def get_location(self): + raise NotImplementedError() + + def set_security_label(self, sec_lab, old_lab): + image = self.get_location() + rc = security.set_resource_label_xapi(image, sec_lab, old_lab) + if rc != xsconstants.XSERR_SUCCESS: + raise SecurityError(rc) + return rc + + def get_security_label(self): + image = self.get_location() + return security.get_resource_label_xapi(image) + + +class XendQCoWVDI(XendVDI): + def __init__(self, uuid, sr_uuid, qcow_path, cfg_path, vsize, psize): + XendVDI.__init__(self, uuid, sr_uuid) + self.auto_save = False + self.qcow_path = qcow_path + self.cfg_path = cfg_path + self.physical_utilisation = psize + self.virtual_size = vsize + self.auto_save = True + self.other_config['location'] = 'tap:qcow:%s' % self.qcow_path + + def get_location(self): + return self.other_config['location'] + +class XendLocalVDI(XendVDI): + def __init__(self, vdi_struct): + vdi_uuid = vdi_struct['uuid'] + sr_uuid = vdi_struct['SR'] + XendVDI.__init__(self, vdi_uuid, sr_uuid) + + self.auto_save = False + self.cfg_path = None + self.name_label = vdi_struct.get('name_label','') + self.name_description = vdi_struct.get('name_description', '') + self.physical_utilisation = 0 + self.virtual_size = 0 + self.type = vdi_struct.get('type', '') + self.sharable = vdi_struct.get('sharable', False) + self.read_only = vdi_struct.get('read_only', False) + self.other_config = vdi_struct.get('other_config', {}) + + def get_location(self): + return self.other_config['location'] diff --git a/tools/python/xen/xend/XendVMMetrics.py b/tools/python/xen/xend/XendVMMetrics.py new file mode 100644 index 0000000..e4d4e53 --- /dev/null +++ b/tools/python/xen/xend/XendVMMetrics.py @@ -0,0 +1,145 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006-2007 Xensource Inc. +# Copyright (c) 2007 Tom Wilkie +#============================================================================ + +from xen.xend.XendLogging import log +from xen.xend.XendBase import XendBase +import xen.lowlevel.xc + +xc = xen.lowlevel.xc.xc() + +class XendVMMetrics(XendBase): + """VM Metrics.""" + + def getClass(self): + return "VM_metrics" + + def getAttrRO(self): + attrRO = ['memory_actual', + 'VCPUs_number', + 'VCPUs_utilisation', + 'VCPUs_CPU', + 'VCPUs_flags', + 'VCPUs_params', + 'state', + 'start_time', + 'last_updated'] + return XendBase.getAttrRO() + attrRO + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + + def __init__(self, uuid, xend_domain_instance): + XendBase.__init__(self, uuid, {}) + self.xend_domain_instance = xend_domain_instance + + def get_memory_actual(self): + domInfo = self.xend_domain_instance.getDomInfo() + if domInfo: + return domInfo["mem_kb"] * 1024 + else: + return 0 + + def get_VCPUs_number(self): + domInfo = self.xend_domain_instance.getDomInfo() + if domInfo: + return domInfo["online_vcpus"] + else: + return 0 + + def get_VCPUs_utilisation(self): + return self.xend_domain_instance.get_vcpus_util() + + def get_VCPUs_CPU(self): + domid = self.xend_domain_instance.getDomid() + if domid is not None: + vcpus_cpu = {} + vcpus_max = self.xend_domain_instance.info['VCPUs_max'] + for i in range(0, vcpus_max): + info = xc.vcpu_getinfo(domid, i) + vcpus_cpu[i] = info['cpu'] + return vcpus_cpu + else: + return {} + + def get_VCPUs_flags(self): + domid = self.xend_domain_instance.getDomid() + if domid is not None: + vcpus_flags = {} + vcpus_max = self.xend_domain_instance.info['VCPUs_max'] + for i in range(0, vcpus_max): + info = xc.vcpu_getinfo(domid, i) + flags = [] + def set_flag(flag): + if info[flag] == 1: + flags.append(flag) + set_flag('blocked') + set_flag('online') + set_flag('running') + vcpus_flags[i] = flags + return vcpus_flags + else: + return {} + + def get_state(self): + try: + domid = self.xend_domain_instance.getDomid() + domlist = xc.domain_getinfo(domid, 1) + if domlist and domid == domlist[0]['domid']: + dominfo = domlist[0] + + states = [] + def addState(key): + if dominfo[key] == 1: + states.append(key) + + addState("running") + addState("blocked") + addState("paused") + addState("dying") + addState("crashed") + addState("shutdown") + return states + except Exception, err: + # ignore missing domain + log.trace("domain_getinfo(%d) failed, ignoring: %s", domid, str(err)) + return [] + + def get_VCPUs_params(self): + domid = self.xend_domain_instance.getDomid() + if domid is not None: + params_live = {} + vcpus_max = self.xend_domain_instance.info['VCPUs_max'] + for i in range(0, vcpus_max): + info = xc.vcpu_getinfo(domid, i) + params_live['cpumap%i' % i] = \ + ",".join(map(str, info['cpumap'])) + + params_live.update(xc.sched_credit_domain_get(domid)) + + return params_live + else: + return {} + + def get_start_time(self): + import xen.xend.XendAPI as XendAPI + return XendAPI.datetime( + self.xend_domain_instance.info.get("start_time", 0)) + + def get_last_updated(self): + import xen.xend.XendAPI as XendAPI + return XendAPI.now() diff --git a/tools/python/xen/xend/XendVnet.py b/tools/python/xen/xend/XendVnet.py new file mode 100644 index 0000000..8f5b6d7 --- /dev/null +++ b/tools/python/xen/xend/XendVnet.py @@ -0,0 +1,181 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +"""Handler for vnet operations. +""" + +from xen.util import Brctl +from xen.xend import sxp +from xen.xend.XendError import XendError +from xen.xend.XendLogging import log +from xen.xend.xenstore.xstransact import xstransact + + +def vnet_cmd(cmd): + out = None + try: + try: + out = file("/proc/vnet/policy", "wb") + sxp.show(cmd, out) + except IOError, ex: + raise XendError(str(ex)) + finally: + if out: out.close() + +class XendVnetInfo: + + vifctl_ops = {'up': 'vif.add', 'down': 'vif.del'} + + def __init__(self, dbpath, config=None): + if config: + self.id = str(sxp.child_value(config, 'id')) + self.dbid = self.id.replace(':', '-') + self.dbpath = dbpath + '/' + self.dbid + self.config = config + else: + self.dbpath = dbpath + self.importFromDB() + + self.bridge = sxp.child_value(self.config, 'bridge') + if not self.bridge: + self.bridge = "vnet%s" % self.id + self.vnetif = sxp.child_value(self.config, 'vnetif') + if not self.vnetif: + self.vnetif = "vnif%s" % self.id + + + def exportToDB(self, save=False, sync=False): + to_store = { + 'id' : self.id, + 'dbid' : self.dbid, + 'config' : sxp.to_string(self.config) + } + xstransact.Write(self.dbpath, to_store) + + + def importFromDB(self): + (self.id, self.dbid, c) = xstransact.Gather(self.dbpath, + ('id', str), + ('dbid', str), + ('config', str)) + self.config = sxp.from_string(c) + + + def sxpr(self): + return self.config + + def configure(self): + log.info("Configuring vnet %s", self.id) + val = vnet_cmd(['vnet.add'] + sxp.children(self.config)) + Brctl.bridge_create(self.bridge) + Brctl.vif_bridge_add({'bridge': self.bridge, 'vif': self.vnetif}) + return val + + def delete(self): + log.info("Deleting vnet %s", self.id) + Brctl.vif_bridge_rem({'bridge': self.bridge, 'vif': self.vnetif}) + Brctl.bridge_del(self.bridge) + val = vnet_cmd(['vnet.del', self.id]) + xstransact.Remove(self.dbpath) + return val + + def vifctl(self, op, vif, vmac): + try: + fn = self.vifctl_ops[op] + return vnet_cmd([fn, ['vnet', self.id], ['vif', vif], ['vmac', vmac]]) + except XendError: + log.warning("vifctl failed: op=%s vif=%s mac=%s", op, vif, vmac) + +class XendVnet: + """Index of all vnets. Singleton. + """ + + dbpath = "/vnet" + + def __init__(self): + # Table of vnet info indexed by vnet id. + self.vnet = {} + listing = xstransact.List(self.dbpath) + for entry in listing: + try: + info = XendVnetInfo(self.dbpath + '/' + entry) + self.vnet[info.id] = info + info.configure() + except XendError, ex: + log.warning("Failed to configure vnet %s: %s", str(info.id), str(ex)) + except Exception, ex: + log.exception("Vnet error") + xstransact.Remove(self.dbpath + '/' + entry) + + def vnet_of_bridge(self, bridge): + """Get the vnet for a bridge (if any). + + @param bridge: bridge name + @return vnet or None + """ + for v in self.vnet.values(): + if v.bridge == bridge: + return v + else: + return None + + def vnet_ls(self): + """List all vnet ids. + """ + return self.vnet.keys() + + def vnets(self): + """List all vnets. + """ + return self.vnet.values() + + def vnet_get(self, id): + """Get a vnet. + + @param id vnet id + """ + id = str(id) + return self.vnet.get(id) + + def vnet_create(self, config): + """Create a vnet. + + @param config: config + """ + info = XendVnetInfo(self.dbpath, config=config) + self.vnet[info.id] = info + info.exportToDB() + info.configure() + + def vnet_delete(self, id): + """Delete a vnet. + + @param id: vnet id + """ + info = self.vnet_get(id) + if info: + del self.vnet[id] + info.delete() + +def instance(): + global inst + try: + inst + except: + inst = XendVnet() + return inst diff --git a/tools/python/xen/xend/XendXSPolicy.py b/tools/python/xen/xend/XendXSPolicy.py new file mode 100644 index 0000000..0b6d5bc --- /dev/null +++ b/tools/python/xen/xend/XendXSPolicy.py @@ -0,0 +1,287 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2007 IBM Corporation +# Copyright (c) 2006 Xensource +#============================================================================ + +import base64 +import logging +from xen.xend import XendDomain +from xen.xend.XendBase import XendBase +from xen.xend.XendError import * +from xen.xend.XendAPIConstants import * +from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance +from xen.util import xsconstants +import xen.util.xsm.xsm as security + +log = logging.getLogger("xend.XendXSPolicy") +log.setLevel(logging.TRACE) + + +class XendXSPolicy(XendBase): + """ Administration class for an XSPolicy. """ + + def getClass(self): + return "XSPolicy" + + def getMethods(self): + methods = ['activate_xspolicy'] + return XendBase.getMethods() + methods + + def getFuncs(self): + funcs = [ 'get_xstype', + 'set_xspolicy', + 'reset_xspolicy', + 'get_xspolicy', + 'rm_xsbootpolicy', + 'get_resource_label', + 'set_resource_label', + 'get_labeled_resources', + 'can_run' ] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getMethods = classmethod(getMethods) + getFuncs = classmethod(getFuncs) + + def __init__(self, xspol, record, uuid): + """ xspol = actual XSPolicy object """ + self.xspol = xspol + XendBase.__init__(self, uuid, record) + + def get_record(self): + xspol_record = { + 'uuid' : self.get_uuid(), + 'flags' : XSPolicyAdminInstance().get_policy_flags(self.xspol), + 'repr' : self.xspol.toxml(), + 'type' : self.xspol.get_type(), + } + return xspol_record + + def get_xstype(self): + return XSPolicyAdminInstance().isXSEnabled() + + def set_xspolicy(self, xstype, xml, flags, overwrite): + ref = "" + xstype = int(xstype) + flags = int(flags) + + polstate = { 'xs_ref': "", 'repr' : "", 'type' : 0, + 'flags' : 0 , 'version': 0 , 'errors' : "", 'xserr' : 0 } + if xstype == xsconstants.XS_POLICY_ACM: + poladmin = XSPolicyAdminInstance() + try: + (xspol, rc, errors) = poladmin.add_acmpolicy_to_system( + xml, flags, + overwrite) + if rc != 0: + polstate.update( { 'xserr' : rc, + 'errors': base64.b64encode(errors) } ) + else: + ref = xspol.get_ref() + polstate = { + 'xs_ref' : ref, + 'flags' : poladmin.get_policy_flags(xspol), + 'type' : xstype, + 'repr' : "", + 'version': xspol.get_version(), + 'errors' : base64.b64encode(errors), + 'xserr' : rc, + } + except Exception, e: + raise + else: + raise SecurityError(-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED) + return polstate + + + def reset_xspolicy(self, xstype): + xstype = int(xstype) + polstate = { 'xs_ref': "", 'repr' : "", 'type' : 0, + 'flags' : 0 , 'version': 0 , 'errors' : "", 'xserr' : 0 } + if xstype == xsconstants.XS_POLICY_ACM: + poladmin = XSPolicyAdminInstance() + try: + (xspol, rc, errors) = poladmin.reset_acmpolicy() + if rc != 0: + polstate.update( { 'xserr' : rc, + 'errors': base64.b64encode(errors) } ) + else: + ref = xspol.get_ref() + polstate = { + 'xs_ref' : ref, + 'flags' : poladmin.get_policy_flags(xspol), + 'type' : xstype, + 'repr' : "", + 'version': xspol.get_version(), + 'errors' : base64.b64encode(errors), + 'xserr' : rc, + } + except Exception, e: + raise + else: + raise SecurityError(-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED) + return polstate + + + def activate_xspolicy(self, flags): + flags = int(flags) + rc = -xsconstants.XSERR_GENERAL_FAILURE + poladmin = XSPolicyAdminInstance() + try: + rc = poladmin.activate_xspolicy(self.xspol, flags) + except Exception, e: + log.info("Activate_policy: %s" % str(e)) + if rc != flags: + raise SecurityError(rc) + return flags + + def get_xspolicy(self): + polstate = { 'xs_ref' : "", + 'repr' : "", + 'type' : 0, + 'flags' : 0, + 'version': "", + 'errors' : "", + 'xserr' : 0 } + poladmin = XSPolicyAdminInstance() + refs = poladmin.get_policies_refs() + # Will return one or no policy + if refs and len(refs) > 0: + ref = refs[0] + xspol = XSPolicyAdminInstance().policy_from_ref(ref) + if xspol: + polstate = { + 'xs_ref' : ref, + 'repr' : xspol.toxml(), + 'type' : xspol.get_type(), + 'flags' : poladmin.get_policy_flags(xspol), + 'version': xspol.get_version(), + 'errors' : "", + 'xserr' : 0, + } + return polstate + + def rm_xsbootpolicy(self): + rc = XSPolicyAdminInstance().rm_bootpolicy() + if rc != xsconstants.XSERR_SUCCESS: + raise SecurityError(rc) + + def get_labeled_resources(self): + return security.get_labeled_resources_xapi() + + def set_resource_label(self, resource, sec_lab, old_lab): + rc = security.set_resource_label_xapi(resource, sec_lab, old_lab) + if rc != xsconstants.XSERR_SUCCESS: + raise SecurityError(rc) + + def get_resource_label(self, resource): + res = security.get_resource_label_xapi(resource) + return res + + def can_run(self, sec_label): + irc = security.validate_label_xapi(sec_label, 'dom') + if irc != xsconstants.XSERR_SUCCESS: + raise SecurityError(irc) + return security.check_can_run(sec_label) + + get_xstype = classmethod(get_xstype) + get_xspolicy = classmethod(get_xspolicy) + set_xspolicy = classmethod(set_xspolicy) + reset_xspolicy = classmethod(reset_xspolicy) + rm_xsbootpolicy = classmethod(rm_xsbootpolicy) + set_resource_label = classmethod(set_resource_label) + get_resource_label = classmethod(get_resource_label) + get_labeled_resources = classmethod(get_labeled_resources) + can_run = classmethod(can_run) + + +class XendACMPolicy(XendXSPolicy): + """ Administration class of an ACMPolicy """ + + def getClass(self): + return "ACMPolicy" + + def getAttrRO(self): + attrRO = [ 'xml', + 'map', + 'binary', + 'header' ] + return XendXSPolicy.getAttrRO() + attrRO + + def getFuncs(self): + funcs = [ 'get_enforced_binary', 'get_VM_ssidref' ] + return XendBase.getFuncs() + funcs + + getClass = classmethod(getClass) + getAttrRO = classmethod(getAttrRO) + getFuncs = classmethod(getFuncs) + + def __init__(self, acmpol, record, uuid): + """ acmpol = actual ACMPolicy object """ + self.acmpol = acmpol + XendXSPolicy.__init__(self, acmpol, record, uuid) + + def get_record(self): + polstate = { + 'uuid' : self.get_uuid(), + 'flags' : XSPolicyAdminInstance().get_policy_flags(self.acmpol), + 'repr' : self.acmpol.toxml(), + 'type' : self.acmpol.get_type(), + } + return polstate + + def get_header(self): + header = { + 'policyname' : "", 'policyurl' : "", 'reference' : "", + 'date' : "", 'namespaceurl' : "", 'version' : "", + } + try: + header = self.acmpol.get_header_fields_map() + except: + pass + return header + + def get_xml(self): + return self.acmpol.toxml() + + def get_map(self): + return self.acmpol.get_map() + + def get_binary(self): + polbin = self.acmpol.get_bin() + return base64.b64encode(polbin) + + def get_VM_ssidref(self, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + if not dom: + raise InvalidHandleError("VM", vm_ref) + if dom._stateGet() not in [ XEN_API_VM_POWER_STATE_RUNNING, \ + XEN_API_VM_POWER_STATE_PAUSED ]: + raise VMBadState("Domain is not running or paused.") + ssid = security.get_ssid(dom.getDomid()) + if not ssid: + raise SecurityError(-xsconstants.XSERR_GENERAL_FAILURE) + return ssid[3] + + def get_enforced_binary(self): + polbin = XSPolicyAdminInstance(). \ + get_enforced_binary(xsconstants.XS_POLICY_ACM) + if polbin: + return base64.b64encode(polbin) + return None + + get_enforced_binary = classmethod(get_enforced_binary) + get_VM_ssidref = classmethod(get_VM_ssidref) diff --git a/tools/python/xen/xend/XendXSPolicyAdmin.py b/tools/python/xen/xend/XendXSPolicyAdmin.py new file mode 100644 index 0000000..e9e5efe --- /dev/null +++ b/tools/python/xen/xend/XendXSPolicyAdmin.py @@ -0,0 +1,385 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006,2007 International Business Machines Corp. +# Author: Stefan Berger +#============================================================================ +import os +import shutil + +from xml.dom import minidom, Node + +from xen.xend.XendLogging import log +from xen.xend import uuid +from xen.util import xsconstants, bootloader +import xen.util.xsm.acm.acm as security +from xen.util.xspolicy import XSPolicy +from xen.util.acmpolicy import ACMPolicy, initialize +from xen.xend.XendError import SecurityError + + +class XSPolicyAdmin: + """ The class that handles the managed policies in the system. + Handles adding and removing managed policies. All managed + policies are handled using a reference (UUID) which is + assigned to the policy by this class. + """ + + def __init__(self, maxpolicies): + """ Create a management class for managing the system's + policies. + + @param maxpolicies: The max. number of policies allowed + on the system (currently '1') + """ + self.maxpolicies = maxpolicies + self.policies = {} + self.xsobjs = {} + bootloader.init() + + if security.on() == xsconstants.XS_POLICY_ACM: + self.__acm_init() + + def __acm_init(self): + act_pol_name = self.get_hv_loaded_policy_name() + initialize() + + ref = uuid.createString() + try: + self.xsobjs[ref] = ACMPolicy(name=act_pol_name, ref=ref) + self.policies[ref] = (act_pol_name, xsconstants.ACM_POLICY_ID) + self.xsobjs[ref].validate_enforced_policy_hash() + except Exception, e: + log.error("Could not find XML representation of policy '%s': " + "%s" % (act_pol_name,e)) + rc, errors, acmpol_def = ACMPolicy.force_default_policy(ref) + if rc == xsconstants.XSERR_SUCCESS: + self.xsobjs[ref] = acmpol_def + self.policies[ref] = (acmpol_def.get_name(), + xsconstants.ACM_POLICY_ID) + log.info("Switched to DEFAULT policy.") + + log.debug("XSPolicyAdmin: Known policies: %s" % self.policies) + + + def isXSEnabled(self): + """ Check whether 'security' is enabled on this system. + This currently only checks for ACM-enablement. + """ + rc = 0 + if security.on() == xsconstants.XS_POLICY_ACM: + rc |= xsconstants.XS_POLICY_ACM + return rc + + def add_acmpolicy_to_system(self, xmltext, flags, overwrite): + """ Add an ACM policy's xml representation to the system. The + policy will automatically be compiled + flags: + XS_INST_BOOT : make policy the one to boot the system with + by default; if there's a policy already installed, + refuse to install this policy unless its one with + the same name + XS_INST_LOAD : load the policy immediately; if this does not work + refuse to install this policy + overwrite: + If any policy is installed and this is False, refuse to install + this policy + If flags is True, then any existing policy will be removed from + the system and the new one will be installed + """ + from xen.xend import XendDomain + domains = XendDomain.instance() + try: + domains.domains_lock.acquire() + return self.__add_acmpolicy_to_system(xmltext, flags, overwrite) + finally: + domains.domains_lock.release() + + def __add_acmpolicy_to_system(self, xmltext, flags, overwrite): + errors = "" + if security.on() != xsconstants.XS_POLICY_ACM: + raise SecurityError(-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED) + loadedpol = self.get_loaded_policy() + if loadedpol: + # This is meant as an update to a currently loaded policy + if flags & xsconstants.XS_INST_LOAD == 0: + raise SecurityError(-xsconstants.XSERR_POLICY_LOADED) + + # Remember old flags, so they can be restored if update fails + old_flags = self.get_policy_flags(loadedpol) + + # Remove policy from bootloader in case of new name of policy + self.rm_bootpolicy() + + rc, errors = loadedpol.update(xmltext) + if rc == 0: + irc = self.activate_xspolicy(loadedpol, flags) + # policy is loaded; if setting the boot flag fails it's ok. + else: + old_flags = old_flags & xsconstants.XS_INST_BOOT + log.info("OLD FLAGS TO RESTORE: %s" % str(old_flags)) + if old_flags != 0: + self.activate_xspolicy(loadedpol, xsconstants.XS_INST_BOOT) + + return (loadedpol, rc, errors) + + try: + dom = minidom.parseString(xmltext.encode("utf-8")) + except: + raise SecurityError(-xsconstants.XSERR_BAD_XML) + + ref = uuid.createString() + + acmpol = ACMPolicy(dom=dom, ref=ref) + + #First some basic tests that do not modify anything: + + if flags & xsconstants.XS_INST_BOOT and not overwrite: + filename = acmpol.get_filename(".bin","",dotted=True) + if bootloader.get_default_policy != None and \ + not bootloader.loads_default_policy(filename): + raise SecurityError(-xsconstants.XSERR_BOOTPOLICY_INSTALLED) + + if not overwrite and len(self.policies) >= self.maxpolicies: + raise SecurityError(-xsconstants.XSERR_BOOTPOLICY_INSTALLED) + + if overwrite: + #This should only give one key since only one policy is + #allowed. + keys = self.policies.keys() + for k in keys: + self.rm_bootpolicy() + rc = self.rm_policy_from_system(k, force=overwrite) + if rc != xsconstants.XSERR_SUCCESS: + raise SecurityError(rc) + + rc = acmpol.compile() + if rc != 0: + raise SecurityError(rc) + + if flags & xsconstants.XS_INST_LOAD: + rc = acmpol.loadintohv() + if rc != 0: + raise SecurityError(rc) + + if flags & xsconstants.XS_INST_BOOT: + rc = self.make_boot_policy(acmpol) + if rc != 0: + # If it cannot be installed due to unsupported + # bootloader, let it be ok. + pass + + if dom: + new_entry = { ref : tuple([acmpol.get_name(), + xsconstants.ACM_POLICY_ID]) } + self.policies.update(new_entry) + self.xsobjs[ref] = acmpol + return (acmpol, xsconstants.XSERR_SUCCESS, errors) + + + def reset_acmpolicy(self): + """ + Attempt to reset the system's policy by udating it with + the DEFAULT policy. + """ + from xen.xend import XendDomain + domains = XendDomain.instance() + try: + domains.domains_lock.acquire() + xml = ACMPolicy.get_reset_policy_xml() + flags = xsconstants.XS_INST_BOOT | xsconstants.XS_INST_LOAD + return self.__add_acmpolicy_to_system(xml, flags, True) + finally: + domains.domains_lock.release() + + + def make_boot_policy(self, acmpol): + if acmpol.is_default_policy(): + return xsconstants.XSERR_SUCCESS + rc = acmpol.copy_policy_file(".bin","/boot") + if rc != xsconstants.XSERR_SUCCESS: + return rc + + try: + filename = acmpol.get_filename(".bin","",dotted=True) + if bootloader.set_default_boot_policy(filename) != True: + return xsconstants.XSERR_BOOTPOLICY_INSTALL_ERROR + except: + return xsconstants.XSERR_FILE_ERROR + return xsconstants.XSERR_SUCCESS + + def activate_xspolicy(self, xspol, flags): + from xen.xend import XendDomain + domains = XendDomain.instance() + try: + domains.domains_lock.acquire() + return self.__activate_xspolicy(xspol, flags) + finally: + domains.domains_lock.release() + + def __activate_xspolicy(self, xspol, flags): + rc = xsconstants.XSERR_SUCCESS + if flags & xsconstants.XS_INST_LOAD: + rc = xspol.loadintohv() + if rc == xsconstants.XSERR_SUCCESS and \ + flags & xsconstants.XS_INST_BOOT: + rc = self.make_boot_policy(xspol) + if rc == xsconstants.XSERR_SUCCESS: + rc = flags + return rc + + def rm_policy_from_system(self, ref, force=False): + if self.policies.has_key(ref): + acmpol = self.xsobjs[ref] + rc = acmpol.destroy() + if rc == xsconstants.XSERR_SUCCESS or force: + del self.policies[ref] + del self.xsobjs[ref] + rc = xsconstants.XSERR_SUCCESS + return rc + + def rm_bootpolicy(self): + """ Remove any (ACM) boot policy from the grub configuration file + """ + rc = 0 + title = bootloader.get_default_title() + if title != None: + polnames = [] + for (k, v) in self.xsobjs.items(): + polnames.append(v.get_filename(".bin","",dotted=True)) + bootloader.rm_policy_from_boottitle(title, polnames) + else: + rc = -xsconstants.XSERR_NO_DEFAULT_BOOT_TITLE + return rc + + def get_policy_flags(self, acmpol): + """ Get the currently active flags of a policy, i.e., whether the + system is using this policy as its boot policy for the default + boot title. + """ + flags = 0 + + filename = acmpol.get_filename(".bin","", dotted=True) + if bootloader.loads_default_policy(filename) or \ + acmpol.is_default_policy(): + flags |= xsconstants.XS_INST_BOOT + + if acmpol.isloaded(): + flags |= xsconstants.XS_INST_LOAD + return flags + + def get_policies(self): + """ Get all managed policies. """ + return self.xsobjs.values() + + def get_policies_refs(self): + """ Get all managed policies' references. """ + return self.xsobjs.keys() + + def has_ref(self, ref): + """ Check whether there is a policy with the given reference """ + return self.xsobjs.has_key(ref) + + def policy_from_ref(self, ref): + """ Get the policy's object given its reference """ + if ref in self.xsobjs.keys(): + return self.xsobjs[ref] + return None + + def ref_from_polname(self, polname): + """ Get the reference of the policy given its name """ + ref = None + for (k, v) in self.xsobjs.items(): + if v.get_name() == polname: + ref = k + break + return ref + + def lock_policy(self, ref): + """ get exclusive access to a policy """ + self.xsobjs[ref].grab_lock() + + def unlock_policy(self, ref): + """ release exclusive access to a policy """ + self.xsobjs[ref].unlock() + + def get_loaded_policy(self): + for pol in self.xsobjs.values(): + if pol.isloaded(): + return pol + return None + + def get_hv_loaded_policy_name(self): + return security.get_active_policy_name() + + def get_policy_by_name(self, name): + for pol in self.xsobjs.values(): + if pol.get_name() == name: + return pol + return None + + def get_domain0_bootlabel(self): + """ Get the domain0 bootlabel from the default boot title """ + title = "" + def_title = bootloader.get_default_title() + line = bootloader.get_kernel_val(def_title, "ssidref") + if line: + parms = line.split(":",1) + if len(parms) > 1: + title = parms[1] + return title + + def set_domain0_bootlabel(self, xspol, label): + """ Set the domain-0 bootlabel under the given policy. If the + current policy is the default policy, it will remove it. """ + rm_entry = (xspol.get_name() == "DEFAULT") + return xspol.set_vm_bootlabel(label, rm_entry) + + def rm_domain0_bootlabel(self): + """ Remove the domain-0 bootlabel from the default boot title """ + def_title = bootloader.get_default_title() + return bootloader.set_kernel_attval(def_title, "ssidref", None) + + def ssidref_to_vmlabel(self, ssidref): + """ Given an ssidref, return the vmlabel under the current policy """ + vmlabel = "" + pol = self.get_loaded_policy() + if pol: + vmlabel = pol.policy_get_domain_label_by_ssidref_formatted(ssidref) + return vmlabel + + def get_stes_of_vmlabel(self, vmlabel_xapi): + """ Get the list of STEs given a VM label in XenAPI format """ + stes = [] + loadedpol = self.get_loaded_policy() + if loadedpol: + tmp = vmlabel_xapi.split(":") + if len(tmp) != 3: + return [] + stes = loadedpol.policy_get_stes_of_vmlabel(tmp[2]) + return stes + + def get_enforced_binary(self, xstype): + res = None + if xstype == xsconstants.XS_POLICY_ACM: + res = ACMPolicy.get_enforced_binary() + return res + +poladmin = None + +def XSPolicyAdminInstance(maxpolicies=1): + global poladmin + if poladmin == None: + poladmin = XSPolicyAdmin(maxpolicies) + return poladmin diff --git a/tools/python/xen/xend/__init__.py b/tools/python/xen/xend/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/xend/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/xend/arch.py b/tools/python/xen/xend/arch.py new file mode 100644 index 0000000..6d789d9 --- /dev/null +++ b/tools/python/xen/xend/arch.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Copyright (C) IBM Corp. 2006 +# +# Authors: Hollis Blanchard + +import os + +_types = { + "i386": "x86", + "i486": "x86", + "i586": "x86", + "i686": "x86", + "x86_64": "x86", + "amd64": "x86", + "i86pc": "x86", + "ia64": "ia64", +} +type = _types.get(os.uname()[4], "unknown") diff --git a/tools/python/xen/xend/balloon.py b/tools/python/xen/xend/balloon.py new file mode 100644 index 0000000..828e1bc --- /dev/null +++ b/tools/python/xen/xend/balloon.py @@ -0,0 +1,185 @@ +#=========================================================================== +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +import time + +import xen.lowlevel.xc + +import XendDomain +import XendOptions +from XendLogging import log +from XendError import VmError +import osdep + +RETRY_LIMIT = 20 +RETRY_LIMIT_INCR = 5 +## +# The time to sleep between retries grows linearly, using this value (in +# seconds). When the system is lightly loaded, memory should be scrubbed and +# returned to the system very quickly, whereas when it is loaded, the system +# needs idle time to get the scrubbing done. This linear growth accommodates +# such requirements. +SLEEP_TIME_GROWTH = 0.1 + +# A mapping between easy-to-remember labels and the more verbose +# label actually shown in the PROC_XEN_BALLOON file. +#labels = { 'current' : 'Current allocation', +# 'target' : 'Requested target', +# 'low-balloon' : 'Low-mem balloon', +# 'high-balloon' : 'High-mem balloon', +# 'limit' : 'Xen hard limit' } + +def _get_proc_balloon(label): + """Returns the value for the named label. Returns None if the label was + not found or the value was non-numeric.""" + + return osdep.lookup_balloon_stat(label) + +def get_dom0_current_alloc(): + """Returns the current memory allocation (in KiB) of dom0.""" + + kb = _get_proc_balloon('current') + if kb == None: + raise VmError('Failed to query current memory allocation of dom0.') + return kb + +def get_dom0_target_alloc(): + """Returns the target memory allocation (in KiB) of dom0.""" + + kb = _get_proc_balloon('target') + if kb == None: + raise VmError('Failed to query target memory allocation of dom0.') + return kb + +def free(need_mem): + """Balloon out memory from the privileged domain so that there is the + specified required amount (in KiB) free. + """ + + # We check whether there is enough free memory, and if not, instruct dom0 + # to balloon out to free some up. Memory freed by a destroyed domain may + # not appear in the free_memory field immediately, because it needs to be + # scrubbed before it can be released to the free list, which is done + # asynchronously by Xen; ballooning is asynchronous also. Such memory + # does, however, need to be accounted for when calculating how much dom0 + # needs to balloon. No matter where we expect the free memory to come + # from, we need to wait for it to become available. + # + # We are not allowed to balloon below dom0_min_mem, or if dom0_ballooning + # is False, we cannot balloon at all. Memory can still become available + # through a rebooting domain, however. + # + # Eventually, we time out (presumably because there really isn't enough + # free memory). + # + # We don't want to set the memory target (triggering a watch) when that + # has already been done, but we do want to respond to changing memory + # usage, so we recheck the required alloc each time around the loop, but + # track the last used value so that we don't trigger too many watches. + + xoptions = XendOptions.instance() + dom0 = XendDomain.instance().privilegedDomain() + xc = xen.lowlevel.xc.xc() + + try: + dom0_min_mem = xoptions.get_dom0_min_mem() * 1024 + dom0_ballooning = xoptions.get_enable_dom0_ballooning() + dom0_alloc = get_dom0_current_alloc() + + retries = 0 + sleep_time = SLEEP_TIME_GROWTH + new_alloc = 0 + last_new_alloc = None + last_free = None + rlimit = RETRY_LIMIT + + # If unreasonable memory size is required, we give up waiting + # for ballooning or scrubbing, as if had retried. + physinfo = xc.physinfo() + free_mem = physinfo['free_memory'] + scrub_mem = physinfo['scrub_memory'] + total_mem = physinfo['total_memory'] + if dom0_ballooning: + max_free_mem = total_mem - dom0_min_mem + else: + max_free_mem = total_mem - dom0_alloc + if need_mem >= max_free_mem: + retries = rlimit + + while retries < rlimit: + physinfo = xc.physinfo() + free_mem = physinfo['free_memory'] + scrub_mem = physinfo['scrub_memory'] + + if free_mem >= need_mem: + log.debug("Balloon: %d KiB free; need %d; done.", + free_mem, need_mem) + return + + if retries == 0: + rlimit += ((need_mem - free_mem)/1024/1024) * RETRY_LIMIT_INCR + log.debug("Balloon: %d KiB free; %d to scrub; need %d; retries: %d.", + free_mem, scrub_mem, need_mem, rlimit) + + if dom0_ballooning: + dom0_alloc = get_dom0_current_alloc() + new_alloc = dom0_alloc - (need_mem - free_mem - scrub_mem) + + if free_mem + scrub_mem >= need_mem: + if last_new_alloc == None: + log.debug("Balloon: waiting on scrubbing") + last_new_alloc = dom0_alloc + else: + if (new_alloc >= dom0_min_mem and + new_alloc != last_new_alloc): + new_alloc_mb = new_alloc / 1024 # Round down + log.debug("Balloon: setting dom0 target to %d MiB.", + new_alloc_mb) + dom0.setMemoryTarget(new_alloc_mb) + last_new_alloc = new_alloc + # Continue to retry, waiting for ballooning or scrubbing. + + time.sleep(sleep_time) + if retries < 2 * RETRY_LIMIT: + sleep_time += SLEEP_TIME_GROWTH + if last_free != None and last_free >= free_mem + scrub_mem: + retries += 1 + last_free = free_mem + scrub_mem + + # Not enough memory; diagnose the problem. + if not dom0_ballooning: + raise VmError(('Not enough free memory and enable-dom0-ballooning ' + 'is False, so I cannot release any more. ' + 'I need %d KiB but only have %d.') % + (need_mem, free_mem)) + elif new_alloc < dom0_min_mem: + raise VmError( + ('I need %d KiB, but dom0_min_mem is %d and shrinking to ' + '%d KiB would leave only %d KiB free.') % + (need_mem, dom0_min_mem, dom0_min_mem, + free_mem + scrub_mem + dom0_alloc - dom0_min_mem)) + else: + dom0_start_alloc_mb = get_dom0_current_alloc() / 1024 + dom0.setMemoryTarget(dom0_start_alloc_mb) + raise VmError( + ('Not enough memory is available, and dom0 cannot' + ' be shrunk any further')) + + finally: + del xc diff --git a/tools/python/xen/xend/encode.py b/tools/python/xen/xend/encode.py new file mode 100644 index 0000000..3e541b4 --- /dev/null +++ b/tools/python/xen/xend/encode.py @@ -0,0 +1,180 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +"""Encoding for arguments to HTTP calls. + Uses the url-encoding with MIME type 'application/x-www-form-urlencoded' + if the data does not include files. Otherwise it uses the encoding with + MIME type 'multipart/form-data'. See the HTML4 spec for details. + + """ +import sys +import types +from StringIO import StringIO + +import urllib +import random +import md5 + +# Extract from HTML4 spec. +## The following example illustrates "multipart/form-data" +## encoding. Suppose we have the following form: + +##
+##

+## What is your name?
+## What files are you sending?
+## +##

+ +## If the user enters "Larry" in the text input, and selects the text +## file "file1.txt", the user agent might send back the following data: + +## Content-Type: multipart/form-data; boundary=AaB03x + +## --AaB03x +## Content-Disposition: form-data; name="submit-name" + +## Larry +## --AaB03x +## Content-Disposition: form-data; name="files"; filename="file1.txt" +## Content-Type: text/plain + +## ... contents of file1.txt ... +## --AaB03x-- + +## If the user selected a second (image) file "file2.gif", the user agent +## might construct the parts as follows: + +## Content-Type: multipart/form-data; boundary=AaB03x + +## --AaB03x +## Content-Disposition: form-data; name="submit-name" + +## Larry +## --AaB03x +## Content-Disposition: form-data; name="files" +## Content-Type: multipart/mixed; boundary=BbC04y + +## --BbC04y +## Content-Disposition: file; filename="file1.txt" +## Content-Type: text/plain + +## ... contents of file1.txt ... +## --BbC04y +## Content-Disposition: file; filename="file2.gif" +## Content-Type: image/gif +## Content-Transfer-Encoding: binary + +## ...contents of file2.gif... +## --BbC04y-- +## --AaB03x-- + +__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ] + +def data_values(d): + if isinstance(d, types.DictType): + return d.items() + else: + return d + +def encode_data(d): + """Encode some data for HTTP transport. + The encoding used is stored in 'Content-Type' in the headers. + + d data - sequence of tuples or dictionary + returns a 2-tuple of the headers and the encoded data + """ + val = ({}, None) + if d is None: return val + multipart = 0 + for (_, v) in data_values(d): + if encode_isfile(v): + multipart = 1 + break + if multipart: + val = encode_multipart(d) + else: + val = encode_form(d) + return val + +def encode_isfile(v): + if isinstance(v, types.FileType): + return 1 + if hasattr(v, 'readlines'): + return 1 + return 0 + +def encode_multipart(d): + boundary = mime_boundary() + hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary } + out = StringIO() + for (k,v) in data_values(d): + out.write('--') + out.write(boundary) + out.write('\r\n') + if encode_isfile(v): + out.write('Content-Disposition: form-data; name="') + out.write(k) + if hasattr(v, 'name'): + out.write('"; filename="') + out.write(v.name) + out.write('"\r\n') + out.write('Content-Type: application/octet-stream\r\n') + out.write('\r\n') + for l in v.readlines(): + out.write(l) + else: + out.write('Content-Disposition: form-data; name="') + out.write(k) + out.write('"\r\n') + out.write('\r\n') + out.write(str(v)) + out.write('\r\n') + out.write('--') + out.write(boundary) + out.write('--') + out.write('\r\n') + return (hdr, out.getvalue()) + +def mime_boundary(): + random.seed() + m = md5.new() + for _ in range(0, 10): + c = chr(random.randint(1, 255)) + m.update(c) + b = m.hexdigest() + return b[0:16] + +def encode_form(d): + hdr = { 'Content-Type': 'application/x-www-form-urlencoded' } + val = urllib.urlencode(d) + return (hdr, val) + +def main(): + #d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') } + #d = {'a': 1, 'b': 'x y' } + d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ] + #d = [ ('a', 1), ('b', 'x y')] + v = encode_data(d) + print v[0] + sys.stdout.write(v[1]) + print + +if __name__ == "__main__": + main() diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py new file mode 100644 index 0000000..e101665 --- /dev/null +++ b/tools/python/xen/xend/image.py @@ -0,0 +1,941 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2005-2007 XenSource Ltd +#============================================================================ + + +import os, os.path, string +import re +import math +import time +import signal +import thread +import fcntl +import sys +import errno +import glob +import traceback + +import xen.lowlevel.xc +from xen.xend.XendConstants import * +from xen.xend.XendError import VmError, XendError, HVMRequired +from xen.xend.XendLogging import log +from xen.xend.XendOptions import instance as xenopts +from xen.xend.xenstore.xstransact import xstransact +from xen.xend.xenstore.xswatch import xswatch +from xen.xend import arch +from xen.xend import XendOptions +from xen.util import oshelp +from xen.util import utils + +xc = xen.lowlevel.xc.xc() + +MAX_GUEST_CMDLINE = 1024 + +sentinel_path_prefix = '/var/run/xend/dm-' +sentinel_fifos_inuse = { } + +def cleanup_stale_sentinel_fifos(): + for path in glob.glob(sentinel_path_prefix + '*.fifo'): + if path in sentinel_fifos_inuse: continue + try: os.unlink(path) + except OSError, e: + log.warning('could not delete stale fifo %s: %s', + path, utils.exception_string(e)) + +def create(vm, vmConfig): + """Create an image handler for a vm. + + @return ImageHandler instance + """ + return findImageHandlerClass(vmConfig)(vm, vmConfig) + + +class ImageHandler: + """Abstract base class for image handlers. + + createImage() is called to configure and build the domain from its + kernel image and ramdisk etc. + + The method buildDomain() is used to build the domain, and must be + defined in a subclass. Usually this is the only method that needs + defining in a subclass. + + The method createDeviceModel() is called to create the domain device + model. + + The method destroyDeviceModel() is called to reap the device model + """ + + ostype = None + + + def __init__(self, vm, vmConfig): + self.vm = vm + + self.bootloader = False + self.kernel = None + self.ramdisk = None + self.cmdline = None + + self.configure(vmConfig) + + def configure(self, vmConfig): + """Config actions common to all unix-like domains.""" + if '_temp_using_bootloader' in vmConfig: + self.bootloader = True + self.kernel = vmConfig['_temp_kernel'] + self.cmdline = vmConfig['_temp_args'] + self.ramdisk = vmConfig['_temp_ramdisk'] + else: + self.kernel = vmConfig['PV_kernel'] + self.cmdline = vmConfig['PV_args'] + self.ramdisk = vmConfig['PV_ramdisk'] + self.vm.storeVm(("image/ostype", self.ostype), + ("image/kernel", self.kernel), + ("image/cmdline", self.cmdline), + ("image/ramdisk", self.ramdisk)) + self.vm.permissionsVm("image/cmdline", { 'dom': self.vm.getDomid(), 'read': True } ) + + self.device_model = vmConfig['platform'].get('device_model') + + self.display = vmConfig['platform'].get('display') + self.xauthority = vmConfig['platform'].get('xauthority') + self.vncconsole = int(vmConfig['platform'].get('vncconsole', 0)) + self.dmargs = self.parseDeviceModelArgs(vmConfig) + self.pid = None + rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset') + if rtc_timeoffset is not None: + xc.domain_set_time_offset(self.vm.getDomid(), int(rtc_timeoffset)) + + self.cpuid = None + self.cpuid_check = None + if 'cpuid' in vmConfig: + self.cpuid = vmConfig['cpuid']; + if 'cpuid_check' in vmConfig: + self.cpuid_check = vmConfig['cpuid_check'] + + def cleanupBootloading(self): + if self.bootloader: + self.unlink(self.kernel) + self.unlink(self.ramdisk) + + + def unlink(self, f): + if not f: return + try: + os.unlink(f) + except OSError, ex: + log.warning("error removing bootloader file '%s': %s", f, ex) + + + def createImage(self): + """Entry point to create domain memory image. + Override in subclass if needed. + """ + return self.createDomain() + + + def createDomain(self): + """Build the domain boot image. + """ + # Set params and call buildDomain(). + + if self.kernel and not os.path.isfile(self.kernel): + raise VmError('Kernel image does not exist: %s' % self.kernel) + if self.ramdisk and not os.path.isfile(self.ramdisk): + raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk) + if len(self.cmdline) >= MAX_GUEST_CMDLINE: + log.warning('kernel cmdline too long, domain %d', + self.vm.getDomid()) + + log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype, + self.vm.getDomid(), self.vm.getVCpuCount()) + + result = self.buildDomain() + + if isinstance(result, dict): + return result + else: + raise VmError('Building domain failed: ostype=%s dom=%d err=%s' + % (self.ostype, self.vm.getDomid(), str(result))) + + def getRequiredAvailableMemory(self, mem_kb): + """@param mem_kb The configured maxmem or memory, in KiB. + @return The corresponding required amount of memory for the domain, + also in KiB. This is normally the given mem_kb, but architecture- or + image-specific code may override this to add headroom where + necessary.""" + return mem_kb + + def getRequiredInitialReservation(self): + """@param mem_kb The configured memory, in KiB. + @return The corresponding required amount of memory to be free, also + in KiB. This is normally the same as getRequiredAvailableMemory, but + architecture- or image-specific code may override this to + add headroom where necessary.""" + return self.getRequiredAvailableMemory(self.vm.getMemoryTarget()) + + def getRequiredMaximumReservation(self): + """@param mem_kb The maximum possible memory, in KiB. + @return The corresponding required amount of memory to be free, also + in KiB. This is normally the same as getRequiredAvailableMemory, but + architecture- or image-specific code may override this to + add headroom where necessary.""" + return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum()) + + def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): + """@param shadow_mem_kb The configured shadow memory, in KiB. + @param maxmem_kb The configured maxmem, in KiB. + @return The corresponding required amount of shadow memory, also in + KiB.""" + # PV domains don't need any shadow memory + return 0 + + def buildDomain(self): + """Build the domain. Define in subclass.""" + raise NotImplementedError() + + def prepareEnvironment(self): + """Prepare the environment for the execution of the domain. This + method is called before any devices are set up.""" + + domid = self.vm.getDomid() + + # Delete left-over pipes + try: + os.unlink('/var/run/tap/qemu-read-%d' % domid) + os.unlink('/var/run/tap/qemu-write-%d' % domid) + except: + pass + + # No device model, don't create pipes + if self.device_model is None: + return + + # If we use a device model, the pipes for communication between + # blktapctrl and ioemu must be present before the devices are + # created (blktapctrl must access them for new block devices) + + # mkdir throws an exception if the path already exists + try: + os.mkdir('/var/run/tap', 0755) + except: + pass + + try: + os.mkfifo('/var/run/tap/qemu-read-%d' % domid, 0600) + os.mkfifo('/var/run/tap/qemu-write-%d' % domid, 0600) + except OSError, e: + log.warn('Could not create blktap pipes for domain %d' % domid) + log.exception(e) + pass + + + # Return a list of cmd line args to the device models based on the + # xm config file + def parseDeviceModelArgs(self, vmConfig): + ret = ["-domain-name", str(self.vm.info['name_label'])] + + xen_extended_power_mgmt = int(vmConfig['platform'].get( + 'xen_extended_power_mgmt', 0)) + if xen_extended_power_mgmt != 0: + xstransact.Store("/local/domain/0/device-model/%i" + % self.vm.getDomid(), + ('xen_extended_power_mgmt', + xen_extended_power_mgmt)) + + # Find RFB console device, and if it exists, make QEMU enable + # the VNC console. + if int(vmConfig['platform'].get('nographic', 0)) != 0: + # skip vnc init if nographic is set + ret.append('-nographic') + return ret + + vnc_config = {} + has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0 + has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0 + opengl = 1 + keymap = vmConfig['platform'].get("keymap") + for dev_uuid in vmConfig['console_refs']: + dev_type, dev_info = vmConfig['devices'][dev_uuid] + if dev_type == 'vfb': + if 'keymap' in dev_info: + keymap = dev_info.get('keymap',{}) + vfb_type = dev_info.get('type', {}) + if vfb_type == 'sdl': + self.display = dev_info.get('display', {}) + self.xauthority = dev_info.get('xauthority', {}) + opengl = int(dev_info.get('opengl', opengl)) + has_sdl = True + else: + vnc_config = dev_info.get('other_config', {}) + has_vnc = True + break + + if keymap: + ret.append("-k") + ret.append(keymap) + + if has_vnc: + if not vnc_config: + for key in ('vncunused', 'vnclisten', 'vncdisplay', + 'vncpasswd'): + if key in vmConfig['platform']: + vnc_config[key] = vmConfig['platform'][key] + if vnc_config.has_key("vncpasswd"): + passwd = vnc_config["vncpasswd"] + else: + passwd = XendOptions.instance().get_vncpasswd_default() + vncopts = "" + if passwd: + self.vm.storeVm("vncpasswd", passwd) + self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } ) + vncopts = vncopts + ",password" + log.debug("Stored a VNC password for vfb access") + else: + log.debug("No VNC passwd configured for vfb access") + + if XendOptions.instance().get_vnc_tls(): + vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir() + vncx509verify = XendOptions.instance().get_vnc_x509_verify() + + if not os.path.exists(vncx509certdir): + raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir) + + if vncx509verify: + vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir + else: + vncopts = vncopts + ",tls,x509=%s" % vncx509certdir + + + vnclisten = vnc_config.get('vnclisten', + XendOptions.instance().get_vnclisten_address()) + vncdisplay = int(vnc_config.get('vncdisplay', 0)) + ret.append('-vnc') + ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts)) + + if int(vnc_config.get('vncunused', 1)) != 0: + ret.append('-vncunused') + + elif has_sdl: + # SDL is default in QEMU. + if int(vmConfig['platform'].get('opengl', opengl)) != 1 : + ret.append('-disable-opengl') + else: + ret.append('-nographic') + + if int(vmConfig['platform'].get('monitor', 0)) != 0: + ret = ret + ['-monitor', 'vc'] + return ret + + def getDeviceModelArgs(self, restore = False): + args = [self.device_model] + args = args + ([ "-d", "%d" % self.vm.getDomid() ]) + args = args + self.dmargs + return args + + def _openSentinel(self, sentinel_path_fifo): + self.sentinel_fifo = file(sentinel_path_fifo, 'r') + self.sentinel_lock = thread.allocate_lock() + oshelp.fcntl_setfd_cloexec(self.sentinel_fifo, True) + sentinel_fifos_inuse[sentinel_path_fifo] = 1 + self.sentinel_path_fifo = sentinel_path_fifo + + def createDeviceModel(self, restore = False): + if self.device_model is None: + return + if self.pid: + return + # Execute device model. + #todo: Error handling + args = self.getDeviceModelArgs(restore) + env = dict(os.environ) + if self.display: + env['DISPLAY'] = self.display + if self.xauthority: + env['XAUTHORITY'] = self.xauthority + if self.vncconsole: + args = args + ([ "-vncviewer" ]) + unique_id = "%i-%i" % (self.vm.getDomid(), time.time()) + sentinel_path = sentinel_path_prefix + unique_id + sentinel_path_fifo = sentinel_path + '.fifo' + os.mkfifo(sentinel_path_fifo, 0600) + sentinel_write = file(sentinel_path_fifo, 'r+') + self._openSentinel(sentinel_path_fifo) + self.vm.storeDom("image/device-model-fifo", sentinel_path_fifo) + xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid()) + xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(), + { 'dom': self.vm.getDomid(), 'read': True, 'write': True }) + log.info("spawning device models: %s %s", self.device_model, args) + # keep track of pid and spawned options to kill it later + + self.logfile = "/var/log/xen/qemu-dm-%s.log" % str(self.vm.info['name_label']) + + # rotate log + logfile_mode = os.O_WRONLY|os.O_CREAT|os.O_APPEND + logrotate_count = XendOptions.instance().get_qemu_dm_logrotate_count() + if logrotate_count > 0: + logfile_mode |= os.O_TRUNC + if os.path.exists("%s.%d" % (self.logfile, logrotate_count)): + os.unlink("%s.%d" % (self.logfile, logrotate_count)) + for n in range(logrotate_count - 1, 0, -1): + if os.path.exists("%s.%d" % (self.logfile, n)): + os.rename("%s.%d" % (self.logfile, n), + "%s.%d" % (self.logfile, (n + 1))) + if os.path.exists(self.logfile): + os.rename(self.logfile, self.logfile + ".1") + + null = os.open("/dev/null", os.O_RDONLY) + logfd = os.open(self.logfile, logfile_mode) + + sys.stderr.flush() + pid = os.fork() + if pid == 0: #child + try: + os.dup2(null, 0) + os.dup2(logfd, 1) + os.dup2(logfd, 2) + os.close(null) + os.close(logfd) + self.sentinel_fifo.close() + try: + os.execve(self.device_model, args, env) + except Exception, e: + print >>sys.stderr, ( + 'failed to set up fds or execute dm %s: %s' % + (self.device_model, utils.exception_string(e))) + os._exit(126) + except: + os._exit(127) + else: + self.pid = pid + os.close(null) + os.close(logfd) + sentinel_write.close() + self.vm.storeDom("image/device-model-pid", self.pid) + log.info("device model pid: %d", self.pid) + # we would very much prefer not to have a thread here and instead + # have a callback but sadly we don't have Twisted in xend + self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,()) + + def signalDeviceModel(self, cmd, ret, par = None): + if self.device_model is None: + return + # Signal the device model to for action + if cmd is '' or ret is '': + raise VmError('need valid command and result when signal device model') + + orig_state = xstransact.Read("/local/domain/0/device-model/%i/state" + % self.vm.getDomid()) + + if par is not None: + xstransact.Store("/local/domain/0/device-model/%i" + % self.vm.getDomid(), ('parameter', par)) + + xstransact.Store("/local/domain/0/device-model/%i" + % self.vm.getDomid(), ('command', cmd)) + # Wait for confirmation. Could do this with a watch but we'd + # still end up spinning here waiting for the watch to fire. + state = '' + count = 0 + while state != ret: + state = xstransact.Read("/local/domain/0/device-model/%i/state" + % self.vm.getDomid()) + time.sleep(0.1) + count += 1 + if count > 100: + raise VmError('Timed out waiting for device model action') + + #resotre orig state + xstransact.Store("/local/domain/0/device-model/%i" + % self.vm.getDomid(), ('state', orig_state)) + log.info("signalDeviceModel:restore dm state to %s", orig_state) + + def saveDeviceModel(self): + # Signal the device model to pause itself and save its state + self.signalDeviceModel('save', 'paused') + + def resumeDeviceModel(self): + if self.device_model is None: + return + # Signal the device model to resume activity after pausing to save. + xstransact.Store("/local/domain/0/device-model/%i" + % self.vm.getDomid(), ('command', 'continue')) + + def _dmfailed(self, message): + log.warning("domain %s: %s", self.vm.getName(), message) + # ideally we would like to forcibly crash the domain with + # something like + # xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH) + # but this can easily lead to very rapid restart loops against + # which we currently have no protection + + def recreate(self): + if self.device_model is None: + return + name = self.vm.getName() + sentinel_path_fifo = self.vm.readDom('image/device-model-fifo') + fifo_fd = -1 + log.debug("rediscovering %s", sentinel_path_fifo) + if sentinel_path_fifo is None: + log.debug("%s device model no sentinel, cannot rediscover", name) + else: + try: + # We open it O_WRONLY because that fails ENXIO if no-one + # has it open for reading (see SuSv3). The dm process got + # a read/write descriptor from our earlier invocation. + fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK) + except OSError, e: + if e.errno == errno.ENXIO: + self._dmfailed("%s device model no longer running"%name) + elif e.errno == errno.ENOENT: + log.debug("%s device model sentinel %s absent!", + name, sentinel_path_fifo) + else: + raise + if fifo_fd >= 0: + self._openSentinel(sentinel_path_fifo) + os.close(fifo_fd) + self.pid = self.vm.gatherDom(('image/device-model-pid', int)) + log.debug("%s device model rediscovered, pid %s sentinel fifo %s", + name, self.pid, sentinel_path_fifo) + self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,()) + + def _sentinel_watch(self): + log.info("waiting for sentinel_fifo") + try: self.sentinel_fifo.read(1) + except OSError, e: pass + self.sentinel_lock.acquire() + try: + if self.pid: + (p,st) = os.waitpid(self.pid, os.WNOHANG) + if p == self.pid: + message = oshelp.waitstatus_description(st) + else: + # obviously it is malfunctioning, kill it now + try: + os.kill(self.pid, signal.SIGKILL) + message = "malfunctioning (closed sentinel), killed" + except: + message = "malfunctioning or died ?" + message = "pid %d: %s" % (self.pid, message) + else: + message = "no longer running" + except Exception, e: + message = "waitpid failed: %s" % utils.exception_string(e) + message = "device model failure: %s" % message + try: message += "; see %s " % self.logfile + except: pass + self._dmfailed(message) + self.pid = None + self.sentinel_lock.release() + + def destroyDeviceModel(self): + if self.device_model is None: + return + if self.pid: + self.sentinel_lock.acquire() + try: + try: + os.kill(self.pid, signal.SIGHUP) + except OSError, exn: + log.exception(exn) + try: + # Try to reap the child every 100ms for 10s. Then SIGKILL it. + for i in xrange(100): + (p, rv) = os.waitpid(self.pid, os.WNOHANG) + if p == self.pid: + break + time.sleep(0.1) + else: + log.warning("DeviceModel %d took more than 10s " + "to terminate: sending SIGKILL" % self.pid) + os.kill(self.pid, signal.SIGKILL) + os.waitpid(self.pid, 0) + except OSError, exn: + # This is expected if Xend has been restarted within the + # life of this domain. In this case, we can kill the process, + # but we can't wait for it because it's not our child. + # We just make really sure it's going away (SIGKILL) first. + os.kill(self.pid, signal.SIGKILL) + state = xstransact.Remove("/local/domain/0/device-model/%i" + % self.vm.getDomid()) + finally: + self.pid = None + self.sentinel_lock.release() + + try: + os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid()) + os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid()) + except: + pass + try: + del sentinel_fifos_inuse[self.sentinel_path_fifo] + os.unlink(self.sentinel_path_fifo) + except: + pass + + def setCpuid(self): + xc.domain_set_policy_cpuid(self.vm.getDomid()) + + if self.cpuid is not None: + cpuid = self.cpuid + transformed = {} + for sinput, regs in cpuid.iteritems(): + inputs = sinput.split(',') + input = long(inputs[0]) + sub_input = None + if len(inputs) == 2: + sub_input = long(inputs[1]) + t = xc.domain_set_cpuid(self.vm.getDomid(), + input, sub_input, regs) + transformed[sinput] = t + self.cpuid = transformed + + if self.cpuid_check is not None: + cpuid_check = self.cpuid_check + transformed = {} + for sinput, regs_check in cpuid_check.iteritems(): + inputs = sinput.split(',') + input = long(inputs[0]) + sub_input = None + if len(inputs) == 2: + sub_input = long(inputs[1]) + t = xc.domain_check_cpuid(input, sub_input, regs_check) + transformed[sinput] = t + self.cpuid_check = transformed + + + +class LinuxImageHandler(ImageHandler): + + ostype = "linux" + flags = 0 + vhpt = 0 + + def configure(self, vmConfig): + ImageHandler.configure(self, vmConfig) + + def buildDomain(self): + store_evtchn = self.vm.getStorePort() + console_evtchn = self.vm.getConsolePort() + + mem_mb = self.getRequiredInitialReservation() / 1024 + + log.debug("domid = %d", self.vm.getDomid()) + log.debug("memsize = %d", mem_mb) + log.debug("image = %s", self.kernel) + log.debug("store_evtchn = %d", store_evtchn) + log.debug("console_evtchn = %d", console_evtchn) + log.debug("cmdline = %s", self.cmdline) + log.debug("ramdisk = %s", self.ramdisk) + log.debug("vcpus = %d", self.vm.getVCpuCount()) + log.debug("features = %s", self.vm.getFeatures()) + log.debug("flags = %d", self.flags) + if arch.type == "ia64": + log.debug("vhpt = %d", self.vhpt) + + return xc.linux_build(domid = self.vm.getDomid(), + memsize = mem_mb, + image = self.kernel, + store_evtchn = store_evtchn, + console_evtchn = console_evtchn, + cmdline = self.cmdline, + ramdisk = self.ramdisk, + features = self.vm.getFeatures(), + flags = self.flags, + vhpt = self.vhpt) + + def parseDeviceModelArgs(self, vmConfig): + ret = ImageHandler.parseDeviceModelArgs(self, vmConfig) + # Equivalent to old xenconsoled behaviour. Should make + # it configurable in future + ret = ret + ["-serial", "pty"] + return ret + + def getDeviceModelArgs(self, restore = False): + args = ImageHandler.getDeviceModelArgs(self, restore) + args = args + ([ "-M", "xenpv"]) + return args + + +class HVMImageHandler(ImageHandler): + + ostype = "hvm" + + def __init__(self, vm, vmConfig): + ImageHandler.__init__(self, vm, vmConfig) + self.shutdownWatch = None + self.rebootFeatureWatch = None + + def configure(self, vmConfig): + ImageHandler.configure(self, vmConfig) + + self.loader = vmConfig['platform'].get('loader') + + info = xc.xeninfo() + if 'hvm' not in info['xen_caps']: + raise HVMRequired() + + rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset') + + self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)), + ("image/device-model", self.device_model), + ("image/display", self.display)) + self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } ) + self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset)) + self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } ) + + self.apic = int(vmConfig['platform'].get('apic', 0)) + self.acpi = int(vmConfig['platform'].get('acpi', 0)) + self.guest_os_type = vmConfig['platform'].get('guest_os_type') + + + # Return a list of cmd line args to the device models based on the + # xm config file + def parseDeviceModelArgs(self, vmConfig): + ret = ImageHandler.parseDeviceModelArgs(self, vmConfig) + ret = ret + ['-vcpus', str(self.vm.getVCpuCount())] + + if self.kernel: + log.debug("kernel = %s", self.kernel) + ret = ret + ['-kernel', self.kernel] + if self.ramdisk: + log.debug("ramdisk = %s", self.ramdisk) + ret = ret + ['-initrd', self.ramdisk] + if self.cmdline: + log.debug("cmdline = %s", self.cmdline) + ret = ret + ['-append', self.cmdline] + + + dmargs = [ 'boot', 'fda', 'fdb', 'soundhw', + 'localtime', 'serial', 'stdvga', 'isa', + 'acpi', 'usb', 'usbdevice' ] + + for a in dmargs: + v = vmConfig['platform'].get(a) + + # python doesn't allow '-' in variable names + if a == 'stdvga': a = 'std-vga' + if a == 'keymap': a = 'k' + + # Handle booleans gracefully + if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']: + try: + if v != None: v = int(v) + if v: ret.append("-%s" % a) + except (ValueError, TypeError): + pass # if we can't convert it to a sane type, ignore it + else: + if v: + ret.append("-%s" % a) + ret.append("%s" % v) + + if a in ['fda', 'fdb']: + if v: + if not os.path.isabs(v): + raise VmError("Floppy file %s does not exist." % v) + log.debug("args: %s, val: %s" % (a,v)) + + # Handle disk/network related options + mac = None + nics = 0 + + for devuuid in vmConfig['vbd_refs']: + devinfo = vmConfig['devices'][devuuid][1] + uname = devinfo.get('uname') + if uname is not None and 'file:' in uname: + (_, vbdparam) = string.split(uname, ':', 1) + if not os.path.isfile(vbdparam): + raise VmError('Disk image does not exist: %s' % + vbdparam) + + for devuuid in vmConfig['vif_refs']: + devinfo = vmConfig['devices'][devuuid][1] + dtype = devinfo.get('type', 'ioemu') + if dtype != 'ioemu': + continue + nics += 1 + mac = devinfo.get('mac') + if mac is None: + raise VmError("MAC address not specified or generated.") + bridge = devinfo.get('bridge', 'xenbr0') + model = devinfo.get('model', 'rtl8139') + ret.append("-net") + ret.append("nic,vlan=%d,macaddr=%s,model=%s" % + (nics, mac, model)) + ret.append("-net") + ret.append("tap,vlan=%d,ifname=tap%d.%d,bridge=%s" % + (nics, self.vm.getDomid(), nics-1, bridge)) + + if nics == 0: + ret.append("-net") + ret.append("none") + + return ret + + def getDeviceModelArgs(self, restore = False): + args = ImageHandler.getDeviceModelArgs(self, restore) + args = args + ([ "-M", "xenfv"]) + if restore: + args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" % + self.vm.getDomid() ]) + return args + + def buildDomain(self): + store_evtchn = self.vm.getStorePort() + + mem_mb = self.getRequiredInitialReservation() / 1024 + + log.debug("domid = %d", self.vm.getDomid()) + log.debug("image = %s", self.loader) + log.debug("store_evtchn = %d", store_evtchn) + log.debug("memsize = %d", mem_mb) + log.debug("vcpus = %d", self.vm.getVCpuCount()) + log.debug("acpi = %d", self.acpi) + log.debug("apic = %d", self.apic) + + rc = xc.hvm_build(domid = self.vm.getDomid(), + image = self.loader, + memsize = mem_mb, + vcpus = self.vm.getVCpuCount(), + acpi = self.acpi, + apic = self.apic) + rc['notes'] = { 'SUSPEND_CANCEL': 1 } + + rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(), + HVM_PARAM_STORE_PFN) + xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN, + store_evtchn) + + return rc + + +class IA64_HVM_ImageHandler(HVMImageHandler): + + def configure(self, vmConfig): + HVMImageHandler.configure(self, vmConfig) + self.vhpt = int(vmConfig['platform'].get('vhpt', 0)) + + def buildDomain(self): + xc.nvram_init(self.vm.getName(), self.vm.getDomid()) + xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt) + if self.guest_os_type is not None: + xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid()) + return HVMImageHandler.buildDomain(self) + + def getRequiredAvailableMemory(self, mem_kb): + page_kb = 16 + # ROM size for guest firmware, io page, xenstore page + # buffer io page, buffer pio page and memmap info page + extra_pages = 1024 + 5 + mem_kb += extra_pages * page_kb + # Add 8 MiB overhead for QEMU's video RAM. + return mem_kb + 8192 + + def getRequiredInitialReservation(self): + return self.vm.getMemoryTarget() + + def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): + # Explicit shadow memory is not a concept + return 0 + + def getDeviceModelArgs(self, restore = False): + args = HVMImageHandler.getDeviceModelArgs(self, restore) + args = args + ([ "-m", "%s" % + (self.getRequiredInitialReservation() / 1024) ]) + return args + + def setCpuid(self): + # Guest CPUID configuration is not implemented yet. + return + +class IA64_Linux_ImageHandler(LinuxImageHandler): + + def configure(self, vmConfig): + LinuxImageHandler.configure(self, vmConfig) + self.vhpt = int(vmConfig['platform'].get('vhpt', 0)) + + def setCpuid(self): + # Guest CPUID configuration is not implemented yet. + return + +class X86_HVM_ImageHandler(HVMImageHandler): + + def configure(self, vmConfig): + HVMImageHandler.configure(self, vmConfig) + self.pae = int(vmConfig['platform'].get('pae', 0)) + + def buildDomain(self): + xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae) + rc = HVMImageHandler.buildDomain(self) + self.setCpuid() + return rc + + def getRequiredAvailableMemory(self, mem_kb): + # Add 8 MiB overhead for QEMU's video RAM. + return mem_kb + 8192 + + def getRequiredInitialReservation(self): + return self.vm.getMemoryTarget() + + def getRequiredMaximumReservation(self): + return self.vm.getMemoryMaximum() + + def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): + # 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 max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)), + shadow_mem_kb) + + +class X86_Linux_ImageHandler(LinuxImageHandler): + + def buildDomain(self): + # set physical mapping limit + # add an 8MB slack to balance backend allocations. + mem_kb = self.getRequiredMaximumReservation() + (8 * 1024) + xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb) + rc = LinuxImageHandler.buildDomain(self) + self.setCpuid() + return rc + +_handlers = { + "ia64": { + "linux": IA64_Linux_ImageHandler, + "hvm": IA64_HVM_ImageHandler, + }, + "x86": { + "linux": X86_Linux_ImageHandler, + "hvm": X86_HVM_ImageHandler, + }, +} + +def findImageHandlerClass(image): + """Find the image handler class for an image config. + + @param image config + @return ImageHandler subclass or None + """ + image_type = image.image_type() + try: + return _handlers[arch.type][image_type] + except KeyError: + raise VmError('unknown image type: ' + image_type) diff --git a/tools/python/xen/xend/osdep.py b/tools/python/xen/xend/osdep.py new file mode 100644 index 0000000..a026c85 --- /dev/null +++ b/tools/python/xen/xend/osdep.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +import os + +_scripts_dir = { + "Linux": "/etc/xen/scripts", + "SunOS": "/usr/lib/xen/scripts", +} + +_xend_autorestart = { + "NetBSD": True, + "Linux": True, + "SunOS": False, +} + +_pygrub_path = { + "SunOS": "/usr/lib/xen/bin/pygrub" +} + +_vif_script = { + "SunOS": "vif-vnic" +} + +def _linux_balloon_stat(label): + """Returns the value for the named label, or None if an error occurs.""" + + xend2linux_labels = { 'current' : 'Current allocation', + 'target' : 'Requested target', + 'low-balloon' : 'Low-mem balloon', + 'high-balloon' : 'High-mem balloon', + 'limit' : 'Xen hard limit' } + + PROC_XEN_BALLOON = '/proc/xen/balloon' + f = file(PROC_XEN_BALLOON, 'r') + try: + for line in f: + keyvalue = line.split(':') + if keyvalue[0] == xend2linux_labels[label]: + values = keyvalue[1].split() + if values[0].isdigit(): + return int(values[0]) + else: + return None + return None + finally: + f.close() + +def _solaris_balloon_stat(label): + """Returns the value for the named label, or None if an error occurs.""" + + import fcntl + import array + DEV_XEN_BALLOON = '/dev/xen/balloon' + BLN_IOCTL_CURRENT = 0x42410001 + BLN_IOCTL_TARGET = 0x42410002 + BLN_IOCTL_LOW = 0x42410003 + BLN_IOCTL_HIGH = 0x42410004 + BLN_IOCTL_LIMIT = 0x42410005 + label_to_ioctl = { 'current' : BLN_IOCTL_CURRENT, + 'target' : BLN_IOCTL_TARGET, + 'low-balloon' : BLN_IOCTL_LOW, + 'high-balloon' : BLN_IOCTL_HIGH, + 'limit' : BLN_IOCTL_LIMIT } + + f = file(DEV_XEN_BALLOON, 'r') + try: + values = array.array('L', [0]) + if fcntl.ioctl(f.fileno(), label_to_ioctl[label], values, 1) == 0: + return values[0] + else: + return None + finally: + f.close() + +_balloon_stat = { + "SunOS": _solaris_balloon_stat +} + +def _linux_get_cpuinfo(): + cpuinfo = {} + f = file('/proc/cpuinfo', 'r') + try: + p = -1 + d = {} + for line in f: + keyvalue = line.split(':') + if len(keyvalue) != 2: + continue + key = keyvalue[0].strip() + val = keyvalue[1].strip() + if key == 'processor': + if p != -1: + cpuinfo[p] = d + p = int(val) + d = {} + else: + d[key] = val + cpuinfo[p] = d + return cpuinfo + finally: + f.close() + +_get_cpuinfo = { +} + +def _get(var, default=None): + return var.get(os.uname()[0], default) + +scripts_dir = _get(_scripts_dir, "/etc/xen/scripts") +xend_autorestart = _get(_xend_autorestart) +pygrub_path = _get(_pygrub_path, "/usr/bin/pygrub") +vif_script = _get(_vif_script, "vif-bridge") +lookup_balloon_stat = _get(_balloon_stat, _linux_balloon_stat) +get_cpuinfo = _get(_get_cpuinfo, _linux_get_cpuinfo) diff --git a/tools/python/xen/xend/server/BlktapController.py b/tools/python/xen/xend/server/BlktapController.py new file mode 100644 index 0000000..e143d36 --- /dev/null +++ b/tools/python/xen/xend/server/BlktapController.py @@ -0,0 +1,87 @@ +# Copyright (c) 2005, XenSource Ltd. + + +from xen.xend.server.blkif import BlkifController +from xen.xend.XendLogging import log + +phantomDev = 0; +phantomId = 0; + +blktap_disk_types = [ + 'aio', + 'sync', + 'vmdk', + 'ram', + 'qcow', + 'qcow2', + + 'ioemu' + ] + +class BlktapController(BlkifController): + def __init__(self, vm): + BlkifController.__init__(self, vm) + + def frontendRoot(self): + """@see DevController#frontendRoot""" + + return "%s/device/vbd" % self.vm.getDomainPath() + + def getDeviceDetails(self, config): + (devid, back, front) = BlkifController.getDeviceDetails(self, config) + + phantomDevid = 0 + wrapped = False + + try: + imagetype = self.vm.info['image']['type'] + except: + imagetype = "" + + if imagetype == 'hvm': + tdevname = back['dev'] + index = ['c', 'd', 'e', 'f', 'g', 'h', 'i', \ + 'j', 'l', 'm', 'n', 'o', 'p'] + while True: + global phantomDev + global phantomId + import os, stat + + phantomId = phantomId + 1 + if phantomId == 16: + if index[phantomDev] == index[-1]: + if wrapped: + raise VmError(" No loopback block \ + devices are available. ") + wrapped = True + phantomDev = 0 + else: + phantomDev = phantomDev + 1 + phantomId = 1 + devname = 'xvd%s%d' % (index[phantomDev], phantomId) + try: + info = os.stat('/dev/%s' % devname) + except: + break + + vbd = { 'mode': 'w', 'device': devname } + fn = 'tap:%s' % back['params'] + + # recurse ... by creating the vbd, then fallthrough + # and finish creating the original device + + from xen.xend import XendDomain + dom0 = XendDomain.instance().privilegedDomain() + phantomDevid = dom0.create_phantom_vbd_with_vdi(vbd, fn) + # we need to wait for this device at a higher level + # the vbd that gets created will have a link to us + # and will let them do it there + + # add a hook to point to the phantom device, + # root path is always the same (dom0 tap) + if phantomDevid != 0: + front['phantom_vbd'] = '/local/domain/0/backend/tap/0/%s' \ + % str(phantomDevid) + + return (devid, back, front) + diff --git a/tools/python/xen/xend/server/ConsoleController.py b/tools/python/xen/xend/server/ConsoleController.py new file mode 100644 index 0000000..bb746f3 --- /dev/null +++ b/tools/python/xen/xend/server/ConsoleController.py @@ -0,0 +1,38 @@ +from xen.xend.server.DevController import DevController +from xen.xend.XendLogging import log + +from xen.xend.XendError import VmError + +class ConsoleController(DevController): + """A dummy controller for us to represent serial and vnc + console devices with persistent UUIDs. + """ + + valid_cfg = ['location', 'uuid', 'protocol'] + + def __init__(self, vm): + DevController.__init__(self, vm) + self.hotplug = False + + def getDeviceDetails(self, config): + back = dict([(k, config[k]) for k in self.valid_cfg if k in config]) + return (self.allocateDeviceID(), back, {}) + + + def getDeviceConfiguration(self, devid, transaction = None): + result = DevController.getDeviceConfiguration(self, devid, transaction) + if transaction is None: + devinfo = self.readBackend(devid, *self.valid_cfg) + else: + devinfo = self.readBackendTxn(transaction, devid, *self.valid_cfg) + config = dict(zip(self.valid_cfg, devinfo)) + config = dict([(key, val) for key, val in config.items() + if val != None]) + return config + + def migrate(self, deviceConfig, network, dst, step, domName): + return 0 + + def destroyDevice(self, devid, force): + DevController.destroyDevice(self, devid, True) + diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py new file mode 100644 index 0000000..022ed1a --- /dev/null +++ b/tools/python/xen/xend/server/DevController.py @@ -0,0 +1,705 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +from threading import Event +import types + +from xen.xend import sxp, XendOptions +from xen.xend.XendError import VmError +from xen.xend.XendLogging import log +import xen.xend.XendConfig + +from xen.xend.xenstore.xstransact import xstransact, complete +from xen.xend.xenstore.xswatch import xswatch + +import os + +DEVICE_CREATE_TIMEOUT = 100 +DEVICE_DESTROY_TIMEOUT = 100 +HOTPLUG_STATUS_NODE = "hotplug-status" +HOTPLUG_ERROR_NODE = "hotplug-error" +HOTPLUG_STATUS_ERROR = "error" +HOTPLUG_STATUS_BUSY = "busy" + +Connected = 1 +Error = 2 +Missing = 3 +Timeout = 4 +Busy = 5 +Disconnected = 6 + +xenbusState = { + 'Unknown' : 0, + 'Initialising' : 1, + 'InitWait' : 2, + 'Initialised' : 3, + 'Connected' : 4, + 'Closing' : 5, + 'Closed' : 6, + 'Reconfiguring': 7, + 'Reconfigured' : 8, + } + +xoptions = XendOptions.instance() + +xenbusState.update(dict(zip(xenbusState.values(), xenbusState.keys()))) + + +class DevController: + """Abstract base class for a device controller. Device controllers create + appropriate entries in the store to trigger the creation, reconfiguration, + and destruction of devices in guest domains. Each subclass of + DevController is responsible for a particular device-class, and + understands the details of configuration specific to that device-class. + + DevController itself provides the functionality common to all device + creation tasks, as well as providing an interface to XendDomainInfo for + triggering those events themselves. + """ + + # Set when registered. + deviceClass = None + + + ## public: + + def __init__(self, vm): + self.vm = vm + self.hotplug = True + + def createDevice(self, config): + """Trigger the creation of a device with the given configuration. + + @return The ID for the newly created device. + """ + (devid, back, front) = self.getDeviceDetails(config) + if devid is None: + return 0 + + self.setupDevice(config) + + (backpath, frontpath) = self.addStoreEntries(config, devid, back, + front) + + import xen.xend.XendDomain + xd = xen.xend.XendDomain.instance() + backdom_name = config.get('backend') + if backdom_name is None: + backdom = xen.xend.XendDomain.DOM0_ID + else: + bd = xd.domain_lookup_nr(backdom_name) + backdom = bd.getDomid() + count = 0 + while True: + t = xstransact() + try: + if devid in self.deviceIDs(t): + if 'dev' in back: + dev_str = '%s (%d, %s)' % (back['dev'], devid, + self.deviceClass) + else: + dev_str = '%s (%s)' % (devid, self.deviceClass) + + raise VmError("Device %s is already connected." % dev_str) + + if count == 0: + log.debug('DevController: writing %s to %s.', + str(front), frontpath) + log.debug('DevController: writing %s to %s.', + str(xen.xend.XendConfig.scrub_password(back)), backpath) + elif count % 50 == 0: + log.debug( + 'DevController: still waiting to write device entries.') + + devpath = self.devicePath(devid) + + t.remove(frontpath) + t.remove(backpath) + t.remove(devpath) + + t.mkdir(backpath) + t.set_permissions(backpath, + {'dom': backdom }, + {'dom' : self.vm.getDomid(), + 'read' : True }) + t.mkdir(frontpath) + t.set_permissions(frontpath, + {'dom': self.vm.getDomid()}, + {'dom': backdom, 'read': True}) + + t.write2(frontpath, front) + t.write2(backpath, back) + + t.mkdir(devpath) + t.write2(devpath, { + 'backend' : backpath, + 'backend-id' : "%i" % backdom, + 'frontend' : frontpath, + 'frontend-id' : "%i" % self.vm.getDomid() + }) + + if t.commit(): + return devid + + count += 1 + except: + t.abort() + raise + + + def waitForDevices(self): + log.debug("Waiting for devices %s.", self.deviceClass) + return map(self.waitForDevice, self.deviceIDs()) + + + def waitForDevice(self, devid): + log.debug("Waiting for %s.", devid) + + if not self.hotplug: + return + + (status, err) = self.waitForBackend(devid) + + if status == Timeout: + self.destroyDevice(devid, False) + raise VmError("Device %s (%s) could not be connected. " + "Hotplug scripts not working." % + (devid, self.deviceClass)) + + elif status == Error: + self.destroyDevice(devid, False) + if err is None: + raise VmError("Device %s (%s) could not be connected. " + "Backend device not found." % + (devid, self.deviceClass)) + else: + raise VmError("Device %s (%s) could not be connected. " + "%s" % (devid, self.deviceClass, err)) + elif status == Missing: + # Don't try to destroy the device; it's already gone away. + raise VmError("Device %s (%s) could not be connected. " + "Device not found." % (devid, self.deviceClass)) + + elif status == Busy: + self.destroyDevice(devid, False) + if err is None: + err = "Busy." + raise VmError("Device %s (%s) could not be connected.\n%s" % + (devid, self.deviceClass, err)) + + + def waitForDevice_destroy(self, devid, backpath): + log.debug("Waiting for %s - destroyDevice.", devid) + + if not self.hotplug: + return + + status = self.waitForBackend_destroy(backpath) + + if status == Timeout: + raise VmError("Device %s (%s) could not be disconnected. " % + (devid, self.deviceClass)) + + def waitForDevice_reconfigure(self, devid): + log.debug("Waiting for %s - reconfigureDevice.", devid) + + (status, err) = self.waitForBackend_reconfigure(devid) + + if status == Timeout: + raise VmError("Device %s (%s) could not be reconfigured. " % + (devid, self.deviceClass)) + + + def reconfigureDevice(self, devid, config): + """Reconfigure the specified device. + + The implementation here just raises VmError. This may be overridden + by those subclasses that can reconfigure their devices. + """ + raise VmError('%s devices may not be reconfigured' % self.deviceClass) + + + def destroyDevice(self, devid, force): + """Destroy the specified device. + + @param devid The device ID, or something device-specific from which + the device ID can be determined (such as a guest-side device name). + + The implementation here simply deletes the appropriate paths from the + store. This may be overridden by subclasses who need to perform other + tasks on destruction. The implementation here accepts integer device + IDs or paths containg integer deviceIDs, e.g. vfb/0. Subclasses may + accept other values and convert them to integers before passing them + here. + """ + + dev = self.convertToDeviceNumber(devid) + + # Modify online status /before/ updating state (latter is watched by + # drivers, so this ordering avoids a race). + self.writeBackend(dev, 'online', "0") + self.writeBackend(dev, 'state', str(xenbusState['Closing'])) + + if force: + frontpath = self.frontendPath(dev) + backpath = self.readVm(dev, "backend") + if backpath: + xstransact.Remove(backpath) + xstransact.Remove(frontpath) + + # xstransact.Remove(self.devicePath()) ?? Below is the same ? + self.vm._removeVm("device/%s/%d" % (self.deviceClass, dev)) + + def configurations(self, transaction = None): + return map(lambda x: self.configuration(x, transaction), self.deviceIDs(transaction)) + + + def configuration(self, devid, transaction = None): + """@return an s-expression giving the current configuration of the + specified device. This would be suitable for giving to {@link + #createDevice} in order to recreate that device.""" + configDict = self.getDeviceConfiguration(devid, transaction) + sxpr = [self.deviceClass] + for key, val in configDict.items(): + if isinstance(val, (types.ListType, types.TupleType)): + for v in val: + if v != None: + sxpr.append([key, v]) + else: + if val != None: + sxpr.append([key, val]) + return sxpr + + def sxprs(self): + """@return an s-expression describing all the devices of this + controller's device-class. + """ + return xstransact.ListRecursive(self.frontendRoot()) + + + def sxpr(self, devid): + """@return an s-expression describing the specified device. + """ + return [self.deviceClass, ['dom', self.vm.getDomid(), + 'id', devid]] + + + def getDeviceConfiguration(self, devid, transaction = None): + """Returns the configuration of a device. + + @note: Similar to L{configuration} except it returns a dict. + @return: dict + """ + if transaction is None: + backdomid = xstransact.Read(self.devicePath(devid), "backend-id") + else: + backdomid = transaction.read(self.devicePath(devid) + "/backend-id") + + if backdomid is None: + raise VmError("Device %s not connected" % devid) + + return {'backend': int(backdomid)} + + def getAllDeviceConfigurations(self): + all_configs = {} + for devid in self.deviceIDs(): + config_dict = self.getDeviceConfiguration(devid) + all_configs[devid] = config_dict + return all_configs + + + def convertToDeviceNumber(self, devid): + try: + return int(devid) + except ValueError: + # Does devid contain devicetype/deviceid? + # Propogate exception if unable to find an integer devid + return int(type(devid) is str and devid.split('/')[-1] or None) + + ## protected: + + def getDeviceDetails(self, config): + """Compute the details for creation of a device corresponding to the + given configuration. These details consist of a tuple of (devID, + backDetails, frontDetails), where devID is the ID for the new device, + and backDetails and frontDetails are the device configuration + specifics for the backend and frontend respectively. + + backDetails and frontDetails should be dictionaries, the keys and + values of which will be used as paths in the store. There is no need + for these dictionaries to include the references from frontend to + backend, nor vice versa, as these will be handled by DevController. + + Abstract; must be implemented by every subclass. + + @return (devID, backDetails, frontDetails), as specified above. + """ + + raise NotImplementedError() + + def setupDevice(self, config): + """ Setup device from config. + """ + return + + def migrate(self, deviceConfig, network, dst, step, domName): + """ Migration of a device. The 'network' parameter indicates + whether the device is network-migrated (True). 'dst' then gives + the hostname of the machine to migrate to. + This function is called for 4 steps: + If step == 0: Check whether the device is ready to be migrated + or can at all be migrated; return a '-1' if + the device is NOT ready, a '0' otherwise. If it is + not ready ( = not possible to migrate this device), + migration will not take place. + step == 1: Called immediately after step 0; migration + of the kernel has started; + step == 2: Called after the suspend has been issued + to the domain and the domain is not scheduled anymore. + Synchronize with what was started in step 1, if necessary. + Now the device should initiate its transfer to the + given target. Since there might be more than just + one device initiating a migration, this step should + put the process performing the transfer into the + background and return immediately to achieve as much + concurrency as possible. + step == 3: Synchronize with the migration of the device that + was initiated in step 2. + Make sure that the migration has finished and only + then return from the call. + """ + tool = xoptions.get_external_migration_tool() + if tool: + log.info("Calling external migration tool for step %d" % step) + fd = os.popen("%s -type %s -step %d -host %s -domname %s" % + (tool, self.deviceClass, step, dst, domName)) + for line in fd: + log.info(line.rstrip()) + rc = fd.close() + if rc: + raise VmError('Migration tool returned %d' % (rc >> 8)) + return 0 + + + def recover_migrate(self, deviceConfig, network, dst, step, domName): + """ Recover from device migration. The given step was the + last one that was successfully executed. + """ + tool = xoptions.get_external_migration_tool() + if tool: + log.info("Calling external migration tool") + fd = os.popen("%s -type %s -step %d -host %s -domname %s -recover" % + (tool, self.deviceClass, step, dst, domName)) + for line in fd: + log.info(line.rstrip()) + rc = fd.close() + if rc: + raise VmError('Migration tool returned %d' % (rc >> 8)) + return 0 + + + def getDomid(self): + """Stub to {@link XendDomainInfo.getDomid}, for use by our + subclasses. + """ + return self.vm.getDomid() + + + def allocateDeviceID(self): + """Allocate a device ID, allocating them consecutively on a + per-domain, per-device-class basis, and using the store to record the + next available ID. + + This method is available to our subclasses, though it is not + compulsory to use it; subclasses may prefer to allocate IDs based upon + the device configuration instead. + """ + path = self.frontendMiscPath() + return complete(path, self._allocateDeviceID) + + + def _allocateDeviceID(self, t): + result = t.read("nextDeviceID") + if result: + result = int(result) + else: + result = 0 + t.write("nextDeviceID", str(result + 1)) + return result + + + def removeBackend(self, devid, *args): + frontpath = self.frontendPath(devid) + backpath = xstransact.Read(frontpath, "backend") + if backpath: + return xstransact.Remove(backpath, *args) + else: + raise VmError("Device %s not connected" % devid) + + def readVm(self, devid, *args): + devpath = self.devicePath(devid) + if devpath: + return xstransact.Read(devpath, *args) + else: + raise VmError("Device config %s not found" % devid) + + def readBackend(self, devid, *args): + backpath = self.readVm(devid, "backend") + if backpath: + return xstransact.Read(backpath, *args) + else: + raise VmError("Device %s not connected" % devid) + + def readBackendTxn(self, transaction, devid, *args): + backpath = self.readVm(devid, "backend") + if backpath: + paths = map(lambda x: backpath + "/" + x, args) + return transaction.read(*paths) + else: + raise VmError("Device %s not connected" % devid) + + def readFrontend(self, devid, *args): + return xstransact.Read(self.frontendPath(devid), *args) + + def readFrontendTxn(self, transaction, devid, *args): + paths = map(lambda x: self.frontendPath(devid) + "/" + x, args) + return transaction.read(*paths) + + def deviceIDs(self, transaction = None): + """@return The IDs of each of the devices currently configured for + this instance's deviceClass. + """ + fe = self.deviceRoot() + + if transaction: + return map(lambda x: int(x.split('/')[-1]), transaction.list(fe)) + else: + return map(int, xstransact.List(fe)) + + + def writeBackend(self, devid, *args): + backpath = self.readVm(devid, "backend") + + if backpath: + xstransact.Write(backpath, *args) + else: + raise VmError("Device %s not connected" % devid) + + +## private: + + def addStoreEntries(self, config, devid, backDetails, frontDetails): + """Add to backDetails and frontDetails the entries to be written in + the store to trigger creation of a device. The backend domain ID is + taken from the given config, paths for frontend and backend are + computed, and these are added to the backDetails and frontDetails + dictionaries for writing to the store, including references from + frontend to backend and vice versa. + + @return A pair of (backpath, frontpath). backDetails and frontDetails + will have been updated appropriately, also. + + @param config The configuration of the device, as given to + {@link #createDevice}. + @param devid As returned by {@link #getDeviceDetails}. + @param backDetails As returned by {@link #getDeviceDetails}. + @param frontDetails As returned by {@link #getDeviceDetails}. + """ + + import xen.xend.XendDomain + xd = xen.xend.XendDomain.instance() + + backdom_name = config.get('backend') + if backdom_name: + backdom = xd.domain_lookup_nr(backdom_name) + else: + backdom = xd.privilegedDomain() + + if not backdom: + raise VmError("Cannot configure device for unknown backend %s" % + backdom_name) + + frontpath = self.frontendPath(devid) + backpath = self.backendPath(backdom, devid) + + frontDetails.update({ + 'backend' : backpath, + 'backend-id' : "%i" % backdom.getDomid(), + 'state' : str(xenbusState['Initialising']) + }) + + if self.vm.native_protocol: + frontDetails.update({'protocol' : self.vm.native_protocol}) + + backDetails.update({ + 'domain' : self.vm.getName(), + 'frontend' : frontpath, + 'frontend-id' : "%i" % self.vm.getDomid(), + 'state' : str(xenbusState['Initialising']), + 'online' : "1" + }) + + return (backpath, frontpath) + + + def waitForBackend(self, devid): + frontpath = self.frontendPath(devid) + # lookup a phantom + phantomPath = xstransact.Read(frontpath, 'phantom_vbd') + if phantomPath is not None: + log.debug("Waiting for %s's phantom %s.", devid, phantomPath) + statusPath = phantomPath + '/' + HOTPLUG_STATUS_NODE + ev = Event() + result = { 'status': Timeout } + xswatch(statusPath, hotplugStatusCallback, ev, result) + ev.wait(DEVICE_CREATE_TIMEOUT) + err = xstransact.Read(statusPath, HOTPLUG_ERROR_NODE) + if result['status'] != 'Connected': + return (result['status'], err) + + backpath = self.readVm(devid, "backend") + + + if backpath: + statusPath = backpath + '/' + HOTPLUG_STATUS_NODE + ev = Event() + result = { 'status': Timeout } + + xswatch(statusPath, hotplugStatusCallback, ev, result) + + ev.wait(DEVICE_CREATE_TIMEOUT) + + err = xstransact.Read(backpath, HOTPLUG_ERROR_NODE) + + return (result['status'], err) + else: + return (Missing, None) + + + def waitForBackend_destroy(self, backpath): + + statusPath = backpath + '/' + HOTPLUG_STATUS_NODE + ev = Event() + result = { 'status': Timeout } + + xswatch(statusPath, deviceDestroyCallback, ev, result) + + ev.wait(DEVICE_DESTROY_TIMEOUT) + + return result['status'] + + def waitForBackend_reconfigure(self, devid): + frontpath = self.frontendPath(devid) + backpath = xstransact.Read(frontpath, "backend") + if backpath: + statusPath = backpath + '/' + "state" + ev = Event() + result = { 'status': Timeout } + + xswatch(statusPath, xenbusStatusCallback, ev, result) + + ev.wait(DEVICE_CREATE_TIMEOUT) + + return (result['status'], None) + else: + return (Missing, None) + + + def backendPath(self, backdom, devid): + """Construct backend path given the backend domain and device id. + + @param backdom [XendDomainInfo] The backend domain info.""" + + return "%s/backend/%s/%s/%d" % (backdom.getDomainPath(), + self.deviceClass, + self.vm.getDomid(), devid) + + + def frontendPath(self, devid): + return "%s/%d" % (self.frontendRoot(), devid) + + + def frontendRoot(self): + return "%s/device/%s" % (self.vm.getDomainPath(), self.deviceClass) + + def frontendMiscPath(self): + return "%s/device-misc/%s" % (self.vm.getDomainPath(), + self.deviceClass) + + def deviceRoot(self): + """Return the /vm/device. Because backendRoot assumes the + backend domain is 0""" + return "%s/device/%s" % (self.vm.vmpath, self.deviceClass) + + def devicePath(self, devid): + """Return the /device entry of the given VM. We use it to store + backend/frontend locations""" + return "%s/device/%s/%s" % (self.vm.vmpath, + self.deviceClass, devid) + +def hotplugStatusCallback(statusPath, ev, result): + log.debug("hotplugStatusCallback %s.", statusPath) + + status = xstransact.Read(statusPath) + + if status is not None: + if status == HOTPLUG_STATUS_ERROR: + result['status'] = Error + elif status == HOTPLUG_STATUS_BUSY: + result['status'] = Busy + else: + result['status'] = Connected + else: + return 1 + + log.debug("hotplugStatusCallback %d.", result['status']) + + ev.set() + return 0 + + +def deviceDestroyCallback(statusPath, ev, result): + log.debug("deviceDestroyCallback %s.", statusPath) + + status = xstransact.Read(statusPath) + + if status is None: + result['status'] = Disconnected + else: + return 1 + + log.debug("deviceDestroyCallback %d.", result['status']) + + ev.set() + return 0 + + +def xenbusStatusCallback(statusPath, ev, result): + log.debug("xenbusStatusCallback %s.", statusPath) + + status = xstransact.Read(statusPath) + + if status == str(xenbusState['Connected']): + result['status'] = Connected + else: + return 1 + + log.debug("xenbusStatusCallback %d.", result['status']) + + ev.set() + return 0 diff --git a/tools/python/xen/xend/server/SSLXMLRPCServer.py b/tools/python/xen/xend/server/SSLXMLRPCServer.py new file mode 100644 index 0000000..1bbd636 --- /dev/null +++ b/tools/python/xen/xend/server/SSLXMLRPCServer.py @@ -0,0 +1,103 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 XenSource Inc. +#============================================================================ + + +""" +HTTPS wrapper for an XML-RPC server interface. Requires PyOpenSSL (Debian +package python-pyopenssl). +""" + +import socket + +from OpenSSL import SSL + +from xen.util.xmlrpclib2 import XMLRPCRequestHandler, TCPXMLRPCServer + + +class SSLXMLRPCRequestHandler(XMLRPCRequestHandler): + def setup(self): + self.connection = self.request + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + +# +# Taken from pyOpenSSL-0.6 examples (public-domain) +# + +class SSLWrapper: + """ + """ + def __init__(self, conn): + """ + Connection is not yet a new-style class, + so I'm making a proxy instead of subclassing. + """ + self.__dict__["conn"] = conn + def __getattr__(self, name): + return getattr(self.__dict__["conn"], name) + def __setattr__(self, name, value): + setattr(self.__dict__["conn"], name, value) + + def close(self): + self.shutdown() + return self.__dict__["conn"].close() + + def shutdown(self, how=1): + """ + SimpleXMLRpcServer.doPOST calls shutdown(1), + and Connection.shutdown() doesn't take + an argument. So we just discard the argument. + """ + # Block until the shutdown is complete + self.__dict__["conn"].shutdown() + self.__dict__["conn"].shutdown() + + def accept(self): + """ + This is the other part of the shutdown() workaround. + Since servers create new sockets, we have to infect + them with our magic. :) + """ + c, a = self.__dict__["conn"].accept() + return (SSLWrapper(c), a) + +# +# End of pyOpenSSL-0.6 example code. +# + +class SSLXMLRPCServer(TCPXMLRPCServer): + def __init__(self, addr, allowed, xenapi, logRequests = 1, + ssl_key_file = None, ssl_cert_file = None): + + TCPXMLRPCServer.__init__(self, addr, allowed, xenapi, + SSLXMLRPCRequestHandler, logRequests) + + if not ssl_key_file or not ssl_cert_file: + raise ValueError("SSLXMLRPCServer requires ssl_key_file " + "and ssl_cert_file to be set.") + + # make a SSL socket + ctx = SSL.Context(SSL.SSLv23_METHOD) + ctx.set_options(SSL.OP_NO_SSLv2) + ctx.use_privatekey_file (ssl_key_file) + ctx.use_certificate_file(ssl_cert_file) + self.socket = SSLWrapper(SSL.Connection(ctx, + socket.socket(self.address_family, + self.socket_type))) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.server_bind() + self.server_activate() diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py new file mode 100644 index 0000000..225b901 --- /dev/null +++ b/tools/python/xen/xend/server/SrvDaemon.py @@ -0,0 +1,418 @@ +########################################################### +## Xen controller daemon +## Copyright (c) 2004, K A Fraser (University of Cambridge) +## Copyright (C) 2004, Mike Wray +## Copyright (C) 2005, XenSource Ltd +########################################################### + +import os +import os.path +import signal +import stat +import sys +import threading +import time +import linecache +import pwd +import re +import traceback + +import xen.lowlevel.xc + +from xen.xend.XendLogging import log +from xen.xend import osdep +from xen.util import mkdir + +import relocate +import SrvServer +from params import * + + +XEND_PROCESS_NAME = 'xend' + + +class Daemon: + """The xend daemon. + """ + def __init__(self): + self.traceon = False + self.tracefile = None + self.traceindent = 0 + self.child = 0 + self.traceLock = threading.Lock() + + + def cleanup_xend(self, kill): + """Clean up the Xend pidfile. + If a running process is found, kills it if 'kill' is true. + + @param kill: whether to kill the process + @return running process id or 0 + """ + running = 0 + pid = read_pid(XEND_PID_FILE) + if find_process(pid, XEND_PROCESS_NAME): + if kill: + os.kill(pid, signal.SIGTERM) + else: + running = pid + if running == 0 and os.path.isfile(XEND_PID_FILE): + os.remove(XEND_PID_FILE) + return running + + + def reloadConfig(self): + """ + """ + pid = read_pid(XEND_PID_FILE) + if find_process(pid, XEND_PROCESS_NAME): + os.kill(pid, signal.SIGHUP) + + + def status(self): + """Returns the status of the xend daemon. + The return value is defined by the LSB: + 0 Running + 3 Not running + """ + if self.cleanup_xend(False) == 0: + return 3 + else: + return 0 + + + def fork_pid(self): + """Fork and write the pid of the child to XEND_PID_FILE. + + @return: pid of child in parent, 0 in child + """ + + self.child = os.fork() + + if self.child: + # Parent + pidfile = open(XEND_PID_FILE, 'w') + try: + pidfile.write(str(self.child)) + finally: + pidfile.close() + + return self.child + + + def daemonize(self): + # Detach from TTY. + + # Become the group leader (already a child process) + os.setsid() + + # Fork, this allows the group leader to exit, + # which means the child can never again regain control of the + # terminal + if os.fork(): + os._exit(0) + + # Detach from standard file descriptors, and redirect them to + # /dev/null or the log as appropriate. + # We open the log file first, so that we can diagnose a failure to do + # so _before_ we close stderr. + try: + parent = os.path.dirname(XEND_DEBUG_LOG) + mkdir.parents(parent, stat.S_IRWXU) + fd = os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0666) + except Exception, exn: + print >>sys.stderr, exn + print >>sys.stderr, ("Xend failed to open %s. Exiting!" % + XEND_DEBUG_LOG) + sys.exit(1) + + os.close(0) + os.close(1) + os.close(2) + if XEND_DEBUG: + os.open('/dev/null', os.O_RDONLY) + os.dup(fd) + os.dup(fd) + else: + os.open('/dev/null', os.O_RDWR) + os.dup(0) + os.dup(fd) + os.close(fd) + + print >>sys.stderr, ("Xend started at %s." % + time.asctime(time.localtime())) + + + def start(self, trace=0): + """Attempts to start the daemons. + The return value is defined by the LSB: + 0 Success + 4 Insufficient privileges + """ + xend_pid = self.cleanup_xend(False) + + if self.set_user(): + return 4 + os.chdir("/") + + if xend_pid > 0: + # Trying to run an already-running service is a success. + return 0 + + ret = 0 + + # If we're not going to create a daemon, simply + # call the run method right here. + if not XEND_DAEMONIZE: + self.tracing(trace) + self.run(None) + return ret + + # we use a pipe to communicate between the parent and the child process + # this way we know when the child has actually initialized itself so + # we can avoid a race condition during startup + + r,w = os.pipe() + if os.fork(): + os.close(w) + r = os.fdopen(r, 'r') + try: + s = r.read() + finally: + r.close() + if not len(s): + ret = 1 + else: + ret = int(s) + else: + os.close(r) + # Child + self.daemonize() + self.tracing(trace) + + # If Xend proper segfaults, then we want to restart it. Thus, + # we fork a child for running Xend itself, and if it segfaults + # (or exits any way other than cleanly) then we run it again. + # The first time through we want the server to write to the (r,w) + # pipe created above, so that we do not exit until the server is + # ready to receive requests. All subsequent restarts we don't + # want this behaviour, or the pipe will eventually fill up, so + # we just pass None into run in subsequent cases (by clearing w + # in the parent of the first fork). On some operating systems, + # restart is managed externally, so we won't fork, and just exit. + while True: + + if not osdep.xend_autorestart: + self.run(os.fdopen(w, 'w')) + os._exit(0) + + pid = self.fork_pid() + if pid: + if w is not None: + os.close(w) + w = None + + (_, status) = os.waitpid(pid, 0) + + if os.WIFEXITED(status): + code = os.WEXITSTATUS(status) + log.info('Xend exited with status %d.', code) + sys.exit(code) + + if os.WIFSIGNALED(status): + sig = os.WTERMSIG(status) + + if sig in (signal.SIGINT, signal.SIGTERM): + log.info('Xend stopped due to signal %d.', sig) + sys.exit(0) + else: + log.fatal( + 'Xend died due to signal %d! Restarting it.', + sig) + else: + self.run(w and os.fdopen(w, 'w') or None) + # if we reach here, the child should quit. + os._exit(0) + + return ret + + def tracing(self, traceon): + """Turn tracing on or off. + + @param traceon: tracing flag + """ + if traceon == self.traceon: + return + self.traceon = traceon + if traceon: + self.tracefile = open(XEND_TRACE_FILE, 'w+', 1) + self.traceindent = 0 + sys.settrace(self.trace) + try: + threading.settrace(self.trace) # Only in Python >= 2.3 + except: + pass + + def print_trace(self, string): + self.tracefile.write("%s: "% threading.currentThread().getName()) + for i in range(self.traceindent): + ch = " " + if (i % 5): + ch = ' ' + else: + ch = '|' + self.tracefile.write(ch) + self.tracefile.write(string) + + def trace(self, frame, event, arg): + self.traceLock.acquire() + try: + if not self.traceon: + print >>self.tracefile + print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20 + self.tracefile.close() + self.tracefile = None + return None + if event == 'call': + code = frame.f_code + filename = code.co_filename + m = re.search('.*xend/(.*)', filename) + if not m: + return None + modulename = m.group(1) + if modulename.endswith('.pyc'): + modulename = modulename[:-1] + if modulename == 'sxp.py' or \ + modulename == 'XendLogging.py' or \ + modulename == 'XendMonitor.py' or \ + modulename == 'server/SrvServer.py': + return None + self.traceindent += 1 + self.print_trace("> %s:%s\n" + % (modulename, code.co_name)) + elif event == 'line': + filename = frame.f_code.co_filename + lineno = frame.f_lineno + self.print_trace("%4d %s" % + (lineno, linecache.getline(filename, lineno))) + elif event == 'return': + code = frame.f_code + filename = code.co_filename + m = re.search('.*xend/(.*)', filename) + if not m: + return None + modulename = m.group(1) + self.print_trace("< %s:%s\n" + % (modulename, code.co_name)) + self.traceindent -= 1 + elif event == 'exception': + self.print_trace("! Exception:\n") + (ex, val, tb) = arg + traceback.print_exception(ex, val, tb, 10, self.tracefile) + #del tb + return self.trace + finally: + self.traceLock.release() + + def set_user(self): + # Set the UID. + try: + os.setuid(pwd.getpwnam(XEND_USER)[2]) + return 0 + except KeyError: + print >>sys.stderr, "Error: no such user '%s'" % XEND_USER + return 1 + + def stop(self): + return self.cleanup_xend(True) + + def run(self, status): + try: + log.info("Xend Daemon started") + + xc = xen.lowlevel.xc.xc() + xinfo = xc.xeninfo() + log.info("Xend changeset: %s.", xinfo['xen_changeset']) + del xc + + relocate.listenRelocation() + servers = SrvServer.create() + servers.start(status) + del servers + + except Exception, ex: + print >>sys.stderr, 'Exception starting xend:', ex + if XEND_DEBUG: + traceback.print_exc() + log.exception("Exception starting xend (%s)" % ex) + if status: + status.write('1') + status.close() + sys.exit(1) + +def instance(): + global inst + try: + inst + except: + inst = Daemon() + return inst + + +def read_pid(pidfile): + """Read process id from a file. + + @param pidfile: file to read + @return pid or 0 + """ + if os.path.isfile(pidfile) and os.path.getsize(pidfile): + try: + f = open(pidfile, 'r') + try: + return int(f.read()) + finally: + f.close() + except: + return 0 + else: + return 0 + + +def find_process(pid, name): + """Search for a process. + + @param pid: process id + @param name: process name + @return: pid if found, 0 otherwise + """ + running = 0 + if pid: + lines = os.popen('ps %d 2>/dev/null' % pid).readlines() + exp = '^ *%d.+%s' % (pid, name) + for line in lines: + if re.search(exp, line): + running = pid + break + return running + + +def main(argv = None): + global XEND_DAEMONIZE + + XEND_DAEMONIZE = False + if argv is None: + argv = sys.argv + + try: + daemon = instance() + + r,w = os.pipe() + daemon.run(os.fdopen(w, 'w')) + return 0 + except Exception, exn: + log.fatal(exn) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/python/xen/xend/server/SrvDmesg.py b/tools/python/xen/xend/server/SrvDmesg.py new file mode 100644 index 0000000..18c453a --- /dev/null +++ b/tools/python/xen/xend/server/SrvDmesg.py @@ -0,0 +1,52 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +from xen.xend import XendDmesg + +from xen.web.SrvDir import SrvDir + + +class SrvDmesg(SrvDir): + """Xen Dmesg output. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xd = XendDmesg.instance() + + def render_POST(self, req): + self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", "text/plain") + req.write(self.info()) + else: + req.write('') + self.print_path(req) + req.write('
')
+            req.write(self.info())
+            req.write('
') + + def info(self): + return self.xd.info() + + def op_clear(self, _1, _2): + self.xd.clear() + return 0 diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py new file mode 100644 index 0000000..531b02e --- /dev/null +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -0,0 +1,286 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 Xensource Ltd +#============================================================================ + +from xen.web import http + +from xen.xend import sxp +from xen.xend import XendDomain +from xen.xend.Args import FormFn +from xen.xend.XendLogging import log + +from xen.web.SrvDir import SrvDir + +class SrvDomain(SrvDir): + """Service managing a single domain. + """ + + def __init__(self, dom): + SrvDir.__init__(self) + self.dom = dom + self.xd = XendDomain.instance() + + def op_configure(self, _, req): + """Configure an existing domain. + Configure is unusual in that it requires a domain id, + not a domain name. + """ + fn = FormFn(self.xd.domain_configure, + [['dom', 'int'], + ['config', 'sxpr']]) + return fn(req.args, {'dom': self.dom.domid}) + + def op_unpause(self, _1, _2): + val = self.xd.domain_unpause(self.dom.domid) + return val + + def op_pause(self, _1, _2): + val = self.xd.domain_pause(self.dom.domid) + return val + + def acceptCommand(self, req): + req.setResponseCode(http.ACCEPTED) + req.setHeader("Location", "%s/.." % req.prePathURL()) + + def op_rename(self, _, req): + self.acceptCommand(req) + return self.dom.setName(req.args['name'][0]) + + def op_shutdown(self, _, req): + self.acceptCommand(req) + return self.dom.shutdown(req.args['reason'][0]) + + def op_delete(self, _, req): + self.acceptCommand(req) + return self.xd.domain_delete(self.dom.getName()) + + def op_start(self, _, req): + self.acceptCommand(req) + paused = False + if 'paused' in req.args and req.args['paused'] == [1]: + paused = True + log.debug("Starting domain " + self.dom.getName() + " " + str(paused)) + return self.xd.domain_start(self.dom.getName(), paused) + + def op_sysrq(self, _, req): + self.acceptCommand(req) + return self.dom.send_sysrq(int(req.args['key'][0])) + + def op_wait_for_devices(self, _, req): + self.acceptCommand(req) + return self.dom.waitForDevices() + + def op_destroy(self, _, req): + self.acceptCommand(req) + return self.xd.domain_destroy(self.dom.domid) + + def op_save(self, op, req): + self.acceptCommand(req) + return req.threadRequest(self.do_save, op, req) + + def do_save(self, _, req): + return self.xd.domain_save(self.dom.domid, req.args['file'][0]) + + def op_dump(self, op, req): + self.acceptCommand(req) + return req.threadRequest(self.do_dump, op, req) + + def do_dump(self, _, req): + fn = FormFn(self.xd.domain_dump, + [['dom', 'int'], + ['file', 'str'], + ['live', 'int'], + ['crash', 'int']]) + return fn(req.args, {'dom': self.dom.domid}) + + def op_migrate(self, op, req): + return req.threadRequest(self.do_migrate, op, req) + + def do_migrate(self, _, req): + fn = FormFn(self.xd.domain_migrate, + [['dom', 'int'], + ['destination', 'str'], + ['live', 'int'], + ['port', 'int'], + ['node', 'int'], + ['ssl', 'int']]) + return fn(req.args, {'dom': self.dom.domid}) + + def op_pincpu(self, _, req): + fn = FormFn(self.xd.domain_pincpu, + [['dom', 'str'], + ['vcpu', 'str'], + ['cpumap', 'str']]) + val = fn(req.args, {'dom': self.dom.getName()}) + return val + + def op_cpu_sedf_get(self, _, req): + fn = FormFn(self.xd.domain_cpu_sedf_get, + [['dom', 'int']]) + val = fn(req.args, {'dom': self.dom.domid}) + return val + + + def op_cpu_sedf_set(self, _, req): + fn = FormFn(self.xd.domain_cpu_sedf_set, + [['dom', 'int'], + ['period', 'int'], + ['slice', 'int'], + ['latency', 'int'], + ['extratime', 'int'], + ['weight', 'int']]) + val = fn(req.args, {'dom': self.dom.domid}) + return val + + def op_domain_sched_credit_get(self, _, req): + fn = FormFn(self.xd.domain_sched_credit_get, + [['dom', 'str']]) + val = fn(req.args, {'dom': self.dom.getName()}) + return val + + + def op_domain_sched_credit_set(self, _, req): + fn = FormFn(self.xd.domain_sched_credit_set, + [['dom', 'str'], + ['weight', 'int'], + ['cap', 'int']]) + val = fn(req.args, {'dom': self.dom.getName()}) + return val + + def op_maxmem_set(self, _, req): + return self.call(self.dom.setMemoryMaximum, + [['memory', 'int']], + req) + + def call(self, fn, args, req): + return FormFn(fn, args)(req.args) + + + def op_mem_target_set(self, _, req): + return self.call(self.dom.setMemoryTarget, + [['target', 'int']], + req) + + def op_devices(self, _, req): + return self.call(self.dom.getDeviceSxprs, + [['type', 'str']], + req) + + def op_device_create(self, _, req): + return self.call(self.dom.device_create, + [['config', 'sxpr']], + req) + + def op_device_destroy(self, _, req): + return self.call(self.dom.destroyDevice, + [['type', 'str'], + ['dev', 'str'], + ['force', 'int'], + ['rm_cfg', 'int']], + req) + + def op_device_configure(self, _, req): + return self.call(self.dom.device_configure, + [['config', 'sxpr'], + ['dev', 'str']], + req) + + + def op_vif_limit_set(self, _, req): + fn = FormFn(self.xd.domain_vif_limit_set, + [['dom', 'int'], + ['vif', 'int'], + ['credit', 'int'], + ['period', 'int']]) + val = fn(req.args, {'dom': self.dom.domid}) + return val + + def op_set_vcpus(self, _, req): + return self.call(self.dom.setVCpuCount, + [['vcpus', 'int']], + req) + + + def op_vcpuinfo(self, _1, req): + return self.call(self.dom.getVCPUInfo, [], req) + + + def op_reset(self, _, req): + self.acceptCommand(req) + return self.xd.domain_reset(self.dom.getName()) + + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + op = req.args.get('op') + + if op and op[0] in ['vcpuinfo']: + return self.perform(req) + + # + # XXX SMH: below may be useful once again if we ever try to get + # the raw 'web' interface to xend working once more. But for now + # is useless and out of date (i.e. no ops called 'v???' anymore). + # + # if op and op[0] in ['vifs', 'vif', 'vbds', 'vbd', 'mem_target_set']: + # return self.perform(req) + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(self.dom.sxpr(), out=req) + else: + req.write('') + self.print_path(req) + #self.ls() + req.write('

%s

' % self.dom) + self.form(req) + req.write('') + return '' + + def form(self, req): + url = req.prePathURL() + req.write('
' % url) + req.write('') + req.write('') + req.write('
') + + req.write('
' % url) + req.write('') + req.write('
') + + req.write('
' % url) + req.write('') + req.write('
') + + req.write('
' % url) + req.write('') + req.write('Poweroff') + req.write('Halt') + req.write('Reboot') + req.write('
') + + req.write('
' % url) + req.write('
') + req.write(' To file: ') + req.write('
') + + req.write('
' % url) + req.write('
') + req.write(' To host: ') + req.write('Live') + req.write('
') diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py new file mode 100644 index 0000000..dcd79b0 --- /dev/null +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -0,0 +1,222 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +import traceback +from StringIO import StringIO + +from xen.web import http + +from xen.xend import sxp +from xen.xend import XendDomain +from xen.xend.XendDomainInfo import XendDomainInfo +from xen.xend.Args import FormFn +from xen.xend.XendError import XendError +from xen.xend.XendLogging import log +from xen.xend.XendConstants import DOM_STATE_RUNNING + +from xen.web.SrvDir import SrvDir +from SrvDomain import SrvDomain + + +class SrvDomainDir(SrvDir): + """Service that manages the domain directory. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xd = XendDomain.instance() + + def domain(self, x): + dom = self.xd.domain_lookup(x) + return SrvDomain(dom) + + def get(self, x): + v = SrvDir.get(self, x) + if v is not None: + return v + else: + return self.domain(x) + + def op_create(self, _, req): + """Create a domain. + Expects the domain config in request parameter 'config' in SXP format. + """ + ok = 0 + errmsg = '' + try: + configstring = req.args.get('config')[0] + #print 'op_create>', 'config:', configstring + pin = sxp.Parser() + pin.input(configstring) + pin.input_eof() + config = pin.get_val() + ok = 1 + except sxp.ParseError, ex: + errmsg = 'Invalid configuration ' + str(ex) + except Exception, ex: + print 'op_create> Exception in config', ex + traceback.print_exc() + errmsg = 'Configuration error ' + str(ex) + if not ok: + raise XendError(errmsg) + try: + dominfo = self.xd.domain_create(config) + return self._op_create_cb(dominfo, configstring, req) + except Exception, ex: + print 'op_create> Exception creating domain:' + traceback.print_exc() + raise XendError("Error creating domain: " + str(ex)) + + def _op_create_cb(self, dominfo, configstring, req): + """Callback to handle domain creation. + """ + dom = dominfo.getName() + domurl = "%s/%s" % (req.prePathURL(), dom) + req.setResponseCode(http.CREATED, "created") + req.setHeader("Location", domurl) + if self.use_sxp(req): + return dominfo.sxpr() + else: + out = StringIO() + print >> out, ('

Created Domain %s

' + % (domurl, dom)) + print >> out, '

'
+            print >> out, configstring
+            print >> out, '

' + val = out.getvalue() + out.close() + return val + + def op_new(self, _, req): + """Define a new domain. + Expects the domain config in request parameter 'config' in SXP format. + """ + ok = 0 + errmsg = '' + try: + configstring = req.args.get('config')[0] + #print 'op_create>', 'config:', configstring + pin = sxp.Parser() + pin.input(configstring) + pin.input_eof() + config = pin.get_val() + ok = 1 + except sxp.ParseError, ex: + errmsg = 'Invalid configuration ' + str(ex) + except Exception, ex: + print 'op_create> Exception in config', ex + traceback.print_exc() + errmsg = 'Configuration error ' + str(ex) + if not ok: + raise XendError(errmsg) + try: + self.xd.domain_new(config) + except Exception, ex: + print 'op_create> Exception creating domain:' + traceback.print_exc() + raise XendError("Error creating domain: " + str(ex)) + + def op_restore(self, op, req): + """Restore a domain from file. + + """ + return req.threadRequest(self.do_restore, op, req) + + def do_restore(self, _, req): + fn = FormFn(self.xd.domain_restore, + [['file', 'str']]) + dominfo = fn(req.args) + dom = dominfo.getName() + domurl = "%s/%s" % (req.prePathURL(), dom) + req.setResponseCode(http.CREATED) + req.setHeader("Location", domurl) + if self.use_sxp(req): + return dominfo.sxpr() + else: + out = StringIO() + print >> out, ('

Created Domain %s

' + % (domurl, dom)) + val = out.getvalue() + out.close() + return val + + + def op_list(self, _, req): + """List the details for this domain.""" + self._list(req, True) + + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + self._list(req, 'detail' in req.args and req.args['detail'] == ['1']) + + + def _list(self, req, detail): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + self.ls_domain(req, detail, True) + else: + req.write("") + self.print_path(req) + self.ls(req) + self.ls_domain(req, detail, False) + self.form(req) + req.write("") + + + def ls_domain(self, req, detail, use_sxp): + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + if use_sxp: + if detail: + sxp.show(map(XendDomainInfo.sxpr, self.xd.list()), out=req) + else: + state = DOM_STATE_RUNNING + if 'state' in req.args and len(req.args['state']) > 0: + state = req.args['state'][0] + log.trace("Listing domains in state " + str(state)) + sxp.show(self.xd.list_names(state), out=req) + else: + domains = self.xd.list_sorted() + req.write('
    ') + for d in domains: + req.write( + '
  • Domain %s: id = %s, memory = %d' + % (url, d.getName(), d.getName(), d.getDomid(), + d.getMemoryTarget())) + req.write('
  • ') + req.write('
') + + + def form(self, req): + """Generate the form(s) for domain dir operations. + """ + req.write('
' + % req.prePathURL()) + req.write('') + req.write('Config
') + req.write('
') + + req.write('
' + % req.prePathURL()) + req.write('') + req.write('State
') + req.write('
') + diff --git a/tools/python/xen/xend/server/SrvNode.py b/tools/python/xen/xend/server/SrvNode.py new file mode 100644 index 0000000..1f9030d --- /dev/null +++ b/tools/python/xen/xend/server/SrvNode.py @@ -0,0 +1,64 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + + +from xen.web.SrvDir import SrvDir +from xen.xend import sxp +from xen.xend import XendNode +from xen.xend.Args import FormFn + +class SrvNode(SrvDir): + """Information about the node. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xn = XendNode.instance() + self.add('dmesg', 'SrvDmesg') + self.add('log', 'SrvXendLog') + + def op_shutdown(self, _1, _2): + val = self.xn.shutdown() + return val + + def op_reboot(self, _1, _2): + val = self.xn.reboot() + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(['node'] + self.info(), out=req) + else: + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + req.write('') + self.print_path(req) + req.write('
    ') + for d in self.info(): + req.write('
  • %10s: %s' % (d[0], str(d[1]))) + req.write('
  • Xen dmesg output' % url) + req.write('
  • Xend log' % url) + req.write('
') + req.write('') + + def info(self): + return self.xn.info() diff --git a/tools/python/xen/xend/server/SrvRoot.py b/tools/python/xen/xend/server/SrvRoot.py new file mode 100644 index 0000000..bd194f3 --- /dev/null +++ b/tools/python/xen/xend/server/SrvRoot.py @@ -0,0 +1,43 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +from xen.web.SrvDir import SrvDir + +class SrvRoot(SrvDir): + """The root of the xend server. + """ + + """Server sub-components. Each entry is (name, class), where + 'name' is the entry name and 'class' is the name of its class. + """ + #todo Get this list from the XendOptions config. + subdirs = [ + ('node', 'SrvNode' ), + ('domain', 'SrvDomainDir' ), + ('vnet', 'SrvVnetDir' ), + ] + + def __init__(self): + SrvDir.__init__(self) + for (name, klass) in self.subdirs: + self.add(name, klass) + for (name, klass) in self.subdirs: + self.get(name) + + def __repr__(self): + return "" %(id(self), self.table.keys()) diff --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py new file mode 100644 index 0000000..95be674 --- /dev/null +++ b/tools/python/xen/xend/server/SrvServer.py @@ -0,0 +1,254 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ + +"""Example xend HTTP + + Can be accessed from a browser or from a program. + Do 'python SrvServer.py' to run the server. + Then point a web browser at http://localhost:8000/xend and follow the links. + Most are stubs, except /domain which has a list of domains and a 'create domain' + button. + + You can also access the server from a program. + Do 'python XendClient.py' to run a few test operations. + + The data served differs depending on the client (as defined by User-Agent + and Accept in the HTTP headers). If the client is a browser, data + is returned in HTML, with interactive forms. If the client is a program, + data is returned in SXP format, with no forms. + + The server serves to the world by default. To restrict it to the local host + change 'interface' in main(). + + Mike Wray +""" +# todo Support security settings etc. in the config file. +# todo Support command-line args. + +import fcntl +import re +import time +import signal +from threading import Thread + +from xen.web.httpserver import HttpServer, UnixHttpServer + +from xen.xend import XendNode, XendOptions, XendAPI +from xen.xend.XendLogging import log +from xen.xend.XendClient import XEN_API_SOCKET +from xen.xend.XendDomain import instance as xenddomain +from xen.web.SrvDir import SrvDir + +from SrvRoot import SrvRoot +from XMLRPCServer import XMLRPCServer + +xoptions = XendOptions.instance() + + +class XendServers: + + def __init__(self, root): + self.servers = [] + self.root = root + self.running = False + self.cleaningUp = False + self.reloadingConfig = False + + def add(self, server): + self.servers.append(server) + + def cleanup(self, signum = 0, frame = None, reloading = False): + log.debug("SrvServer.cleanup()") + self.cleaningUp = True + for server in self.servers: + try: + server.shutdown() + except: + pass + + # clean up domains for those that have on_xend_stop + if not reloading: + xenddomain().cleanup_domains() + + self.running = False + + + def reloadConfig(self, signum = 0, frame = None): + log.debug("SrvServer.reloadConfig()") + self.reloadingConfig = True + self.cleanup(signum, frame, reloading = True) + + def start(self, status): + # Running the network script will spawn another process, which takes + # the status fd with it unless we set FD_CLOEXEC. Failing to do this + # causes the read in SrvDaemon to hang even when we have written here. + if status: + fcntl.fcntl(status, fcntl.F_SETFD, fcntl.FD_CLOEXEC) + + # Prepare to catch SIGTERM (received when 'xend stop' is executed) + # and call each server's cleanup if possible + signal.signal(signal.SIGTERM, self.cleanup) + signal.signal(signal.SIGHUP, self.reloadConfig) + + while True: + threads = [] + for server in self.servers: + if server.ready: + continue + + thread = Thread(target=server.run, + name=server.__class__.__name__) + thread.setDaemon(True) + thread.start() + threads.append(thread) + + # check for when all threads have initialized themselves and then + # close the status pipe + + retryCount = 0 + threads_left = True + while threads_left: + threads_left = False + + for server in self.servers: + if not server.ready: + threads_left = True + break + + if threads_left: + time.sleep(.5) + retryCount += 1 + if retryCount > 60: + for server in self.servers: + if not server.ready: + log.error("Server " + + server.__class__.__name__ + + " did not initialise!") + break + + if status: + status.write('0') + status.close() + status = None + + # Reaching this point means we can auto start domains + try: + xenddomain().autostart_domains() + except Exception, e: + log.exception("Failed while autostarting domains") + + # loop to keep main thread alive until it receives a SIGTERM + self.running = True + while self.running: + time.sleep(100000000) + + if self.reloadingConfig: + log.info("Restarting all XML-RPC and Xen-API servers...") + self.cleaningUp = False + self.reloadingConfig = False + xoptions.set_config() + self.servers = [] + _loadConfig(self, self.root, True) + else: + break + +def _loadConfig(servers, root, reload): + if xoptions.get_xend_http_server(): + servers.add(HttpServer(root, + xoptions.get_xend_address(), + xoptions.get_xend_port())) + if xoptions.get_xend_unix_server(): + path = xoptions.get_xend_unix_path() + log.info('unix path=' + path) + servers.add(UnixHttpServer(root, path)) + + api_cfg = xoptions.get_xen_api_server() + if api_cfg: + try: + for server_cfg in api_cfg: + # Parse the xen-api-server config + + ssl_key_file = None + ssl_cert_file = None + auth_method = XendAPI.AUTH_NONE + hosts_allowed = None + + host_addr = server_cfg[0].split(':', 1) + if len(host_addr) == 1: + if host_addr[0].lower() == 'unix': + use_tcp = False + host = 'localhost' + port = 0 + else: + use_tcp = True + host = '' + port = int(host_addr[0]) + else: + use_tcp = True + host = str(host_addr[0]) + port = int(host_addr[1]) + + if len(server_cfg) > 1: + if server_cfg[1] in [XendAPI.AUTH_PAM, XendAPI.AUTH_NONE]: + auth_method = server_cfg[1] + + if len(server_cfg) > 2 and len(server_cfg[2]): + hosts_allowed = map(re.compile, server_cfg[2].split(' ')) + + if len(server_cfg) > 4: + # SSL key and cert file + ssl_key_file = server_cfg[3] + ssl_cert_file = server_cfg[4] + + + servers.add(XMLRPCServer(auth_method, True, use_tcp = use_tcp, + ssl_key_file = ssl_key_file, + ssl_cert_file = ssl_cert_file, + host = host, port = port, + path = XEN_API_SOCKET, + hosts_allowed = hosts_allowed)) + + except (ValueError, TypeError), exn: + log.exception('Xen API Server init failed') + log.error('Xen-API server configuration %s is invalid.', api_cfg) + + if xoptions.get_xend_tcp_xmlrpc_server(): + addr = xoptions.get_xend_tcp_xmlrpc_server_address() + port = xoptions.get_xend_tcp_xmlrpc_server_port() + ssl_key_file = xoptions.get_xend_tcp_xmlrpc_server_ssl_key_file() + ssl_cert_file = xoptions.get_xend_tcp_xmlrpc_server_ssl_cert_file() + + if ssl_key_file and ssl_cert_file: + servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True, + ssl_key_file = ssl_key_file, + ssl_cert_file = ssl_cert_file, + host = addr, port = port)) + else: + servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True, + host = addr, port = port)) + + if xoptions.get_xend_unix_xmlrpc_server(): + servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False)) + + +def create(): + root = SrvDir() + root.putChild('xend', SrvRoot()) + servers = XendServers(root) + _loadConfig(servers, root, False) + return servers diff --git a/tools/python/xen/xend/server/SrvVnetDir.py b/tools/python/xen/xend/server/SrvVnetDir.py new file mode 100644 index 0000000..5dc5b59 --- /dev/null +++ b/tools/python/xen/xend/server/SrvVnetDir.py @@ -0,0 +1,128 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +from xen.xend import sxp +from xen.xend.Args import FormFn +from xen.xend import PrettyPrint +from xen.xend import XendVnet +from xen.xend.XendError import XendError + +from xen.web.SrvDir import SrvDir + +class SrvVnet(SrvDir): + + def __init__(self, vnetinfo): + SrvDir.__init__(self) + self.vnetinfo = vnetinfo + self.xvnet = XendVnet.instance() + + def op_delete(self, op, req): + val = self.xvnet.vnet_delete(self.vnetinfo.id) + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(self.vnetinfo.sxpr(), out=req) + else: + req.write('') + self.print_path(req) + req.write('

Vnet %s

' % self.vnetinfo.id) + req.write("
")
+            PrettyPrint.prettyprint(self.vnetinfo.sxpr(), out=req)
+            req.write("
") + self.form(req) + req.write('') + return '' + + def form(self, req): + url = req.prePathURL() + req.write('
' % url) + req.write('') + req.write('
') + +class SrvVnetDir(SrvDir): + """Vnet directory. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xvnet = XendVnet.instance() + + def vnet(self, x): + val = None + vnetinfo = self.xvnet.vnet_get(x) + if not vnetinfo: + raise XendError('No such vnet ' + str(x)) + val = SrvVnet(vnetinfo) + return val + + def get(self, x): + v = SrvDir.get(self, x) + if v is not None: + return v + v = self.vnet(x) + return v + + def op_create(self, op, req): + fn = FormFn(self.xvnet.vnet_create, + [['config', 'sxpr']]) + val = fn(req.args, {}) + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + self.ls_vnet(req, 1) + else: + req.write("") + self.print_path(req) + self.ls(req) + self.ls_vnet(req) + self.form(req) + req.write("") + + def ls_vnet(self, req, use_sxp=0): + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + if use_sxp: + vnets = self.xvnet.vnet_ls() + sxp.show(vnets, out=req) + else: + vnets = self.xvnet.vnets() + vnets.sort(lambda x, y: cmp(x.id, y.id)) + req.write('
    ') + for v in vnets: + req.write('
  • Vnet %s' % (url, v.id, v.id)) + req.write('
  • ') + req.write('
') + + def form(self, req): + """Generate the form(s) for vnet dir operations. + """ + req.write('
' + % req.prePathURL()) + req.write('') + req.write('Config
') + req.write('
') diff --git a/tools/python/xen/xend/server/SrvXendLog.py b/tools/python/xen/xend/server/SrvXendLog.py new file mode 100644 index 0000000..a0dc67a --- /dev/null +++ b/tools/python/xen/xend/server/SrvXendLog.py @@ -0,0 +1,37 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +from xen.web import static + +from xen.xend import XendLogging + +from xen.web.SrvDir import SrvDir + +class SrvXendLog(SrvDir): + """Xend log. + """ + + def __init__(self): + SrvDir.__init__(self) + self.logfile = static.File(XendLogging.getLogFilename(), + defaultType="text/plain") + self.logfile.type = "text/plain" + self.logfile.encoding = None + + def render_GET(self, req): + return self.logfile.render(req) diff --git a/tools/python/xen/xend/server/XMLRPCServer.py b/tools/python/xen/xend/server/XMLRPCServer.py new file mode 100644 index 0000000..96c0ac2 --- /dev/null +++ b/tools/python/xen/xend/server/XMLRPCServer.py @@ -0,0 +1,257 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 Anthony Liguori +# Copyright (C) 2006 XenSource Ltd. +#============================================================================ + +import errno +import socket +import types +import xmlrpclib +from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer +try: + from SSLXMLRPCServer import SSLXMLRPCServer + ssl_enabled = True +except ImportError: + ssl_enabled = False + +from xen.xend import XendAPI, XendDomain, XendDomainInfo, XendNode +from xen.xend import XendLogging, XendDmesg +from xen.xend.XendClient import XML_RPC_SOCKET +from xen.xend.XendConstants import DOM_STATE_RUNNING +from xen.xend.XendLogging import log +from xen.xend.XendError import XendInvalidDomain + +# vcpu_avail is a long and is not needed by the clients. It's far easier +# to just remove it then to try and marshal the long. +def fixup_sxpr(sexpr): + ret = [] + for k in sexpr: + if type(k) in (list, tuple): + if len(k) != 2 or k[0] != 'vcpu_avail': + ret.append(fixup_sxpr(k)) + else: + ret.append(k) + return ret + +def lookup(domid): + info = XendDomain.instance().domain_lookup(domid) + return info + +def dispatch(domid, fn, args): + info = lookup(domid) + return getattr(info, fn)(*args) + +def domain(domid, full = 0): + info = lookup(domid) + return fixup_sxpr(info.sxpr(not full)) + +def domains(detail = True, full = False): + return domains_with_state(detail, DOM_STATE_RUNNING, full) + +def domains_with_state(detail, state, full): + if detail: + domains = XendDomain.instance().list_sorted(state) + ret = [] + for dom in domains: + try: + ret.append(fixup_sxpr(dom.sxpr(not full))) + except: + log.warn("Failed to query SXPR for domain %s" % str(dom)) + pass + return ret + else: + return XendDomain.instance().list_names(state) + +def domain_create(config): + info = XendDomain.instance().domain_create(config) + return fixup_sxpr(info.sxpr()) + +def domain_restore(src, paused=False): + info = XendDomain.instance().domain_restore(src, paused) + return fixup_sxpr(info.sxpr()) + +def get_log(): + f = open(XendLogging.getLogFilename(), 'r') + try: + return f.read() + finally: + f.close() + +methods = ['device_create', 'device_configure', + 'destroyDevice','getDeviceSxprs', + 'setMemoryTarget', 'setName', 'setVCpuCount', 'shutdown', + 'send_sysrq', 'getVCPUInfo', 'waitForDevices', + 'getRestartCount', 'getBlockDeviceClass'] + +exclude = ['domain_create', 'domain_restore'] + +class XMLRPCServer: + def __init__(self, auth, use_xenapi, use_tcp = False, + ssl_key_file = None, ssl_cert_file = None, + host = "localhost", port = 8006, path = XML_RPC_SOCKET, + hosts_allowed = None): + + self.use_tcp = use_tcp + self.port = port + self.host = host + self.path = path + self.hosts_allowed = hosts_allowed + + self.ssl_key_file = ssl_key_file + self.ssl_cert_file = ssl_cert_file + + self.ready = False + self.running = True + self.auth = auth + self.xenapi = use_xenapi and XendAPI.XendAPI(auth) or None + + def run(self): + authmsg = (self.auth == XendAPI.AUTH_NONE and + "; authentication has been disabled for this server." or + ".") + + try: + if self.use_tcp: + using_ssl = self.ssl_key_file and self.ssl_cert_file + + log.info("Opening %s XML-RPC server on %s%d%s", + using_ssl and 'HTTPS' or 'TCP', + self.host and '%s:' % self.host or + 'all interfaces, port ', + self.port, authmsg) + + if using_ssl: + if not ssl_enabled: + raise ValueError("pyOpenSSL not installed. " + "Unable to start HTTPS XML-RPC server") + self.server = SSLXMLRPCServer( + (self.host, self.port), + self.hosts_allowed, + self.xenapi is not None, + logRequests = False, + ssl_key_file = self.ssl_key_file, + ssl_cert_file = self.ssl_cert_file) + else: + self.server = TCPXMLRPCServer( + (self.host, self.port), + self.hosts_allowed, + self.xenapi is not None, + logRequests = False) + + else: + log.info("Opening Unix domain socket XML-RPC server on %s%s", + self.path, authmsg) + self.server = UnixXMLRPCServer(self.path, self.hosts_allowed, + self.xenapi is not None, + logRequests = False) + except socket.error, exn: + log.error('Cannot start server: %s!', exn.args[1]) + ready = True + running = False + return + except Exception, e: + log.exception('Cannot start server: %s!', e) + ready = True + running = False + return + + # Register Xen API Functions + # ------------------------------------------------------------------- + # exportable functions are ones that do not begin with '_' + # and has the 'api' attribute. + + for meth_name in dir(self.xenapi): + if meth_name[0] != '_': + meth = getattr(self.xenapi, meth_name) + if callable(meth) and hasattr(meth, 'api'): + self.server.register_function(meth, getattr(meth, 'api')) + + self.server.register_instance(XendAPI.XendAPIAsyncProxy(self.xenapi)) + + # Legacy deprecated xm xmlrpc api + # -------------------------------------------------------------------- + + # Functions in XendDomainInfo + for name in methods: + fn = eval("lambda domid, *args: dispatch(domid, '%s', args)"%name) + self.server.register_function(fn, "xend.domain.%s" % name) + + inst = XendDomain.instance() + + for name in dir(inst): + fn = getattr(inst, name) + if name.startswith("domain_") and callable(fn): + if name not in exclude: + self.server.register_function(fn, "xend.domain.%s" % name[7:]) + + # Functions in XendNode and XendDmesg + for type, lst, n in [(XendNode, ['info', 'send_debug_keys'], 'node'), + (XendDmesg, ['info', 'clear'], 'node.dmesg')]: + inst = type.instance() + for name in lst: + self.server.register_function(getattr(inst, name), + "xend.%s.%s" % (n, name)) + + # A few special cases + self.server.register_function(domain, 'xend.domain') + self.server.register_function(domains, 'xend.domains') + self.server.register_function(domains_with_state, + 'xend.domains_with_state') + self.server.register_function(get_log, 'xend.node.log') + self.server.register_function(domain_create, 'xend.domain.create') + self.server.register_function(domain_restore, 'xend.domain.restore') + + # A couple of the security functions + from xen.util.xsm import xsm as security + for name in security.xmlrpc_exports: + fn = getattr(security, name) + self.server.register_function(fn, "xend.security.%s" % name) + + self.server.register_introspection_functions() + self.ready = True + + # Custom runloop so we can cleanup when exiting. + # ----------------------------------------------------------------- + try: + while self.running: + self.server.handle_request() + finally: + self.shutdown() + + def cleanup(self): + log.debug('XMLRPCServer.cleanup()') + if hasattr(self, 'server'): + try: + # This is here to make sure the socket is actually + # cleaned up when close() is called. Otherwise + # SO_REUSEADDR doesn't take effect. To replicate, + # try 'xend reload' and look for EADDRINUSE. + # + # May be caued by us calling close() outside of + # the listen()ing thread. + self.server.socket.shutdown(2) + except socket.error, e: + pass # ignore any socket errors + try: + self.server.socket.close() + except socket.error, e: + pass + + def shutdown(self): + self.running = False + if self.ready: + self.ready = False + self.cleanup() diff --git a/tools/python/xen/xend/server/__init__.py b/tools/python/xen/xend/server/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/python/xen/xend/server/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py new file mode 100644 index 0000000..28ddf5f --- /dev/null +++ b/tools/python/xen/xend/server/blkif.py @@ -0,0 +1,208 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005, 2006 XenSource Inc. +#============================================================================ + +import re +import string + +from xen.util import blkif +import xen.util.xsm.xsm as security +from xen.xend.XendError import VmError +from xen.xend.server.DevController import DevController +from xen.util import xsconstants + +class BlkifController(DevController): + """Block device interface controller. Handles all block devices + for a domain. + """ + + def __init__(self, vm): + """Create a block device controller. + """ + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + uname = config.get('uname', '') + dev = config.get('dev', '') + + if 'ioemu:' in dev: + (_, dev) = string.split(dev, ':', 1) + try: + (dev, dev_type) = string.split(dev, ':', 1) + except ValueError: + dev_type = "disk" + + if not uname: + if dev_type == 'cdrom': + (typ, params) = ("", "") + else: + raise VmError( + 'Block device must have physical details specified') + else: + try: + (typ, params) = string.split(uname, ':', 1) + if typ not in ('phy', 'file', 'tap'): + raise VmError( + 'Block device must have "phy", "file" or "tap" ' + 'specified to type') + except ValueError: + raise VmError( + 'Block device must have physical details specified') + + mode = config.get('mode', 'r') + if mode not in ('r', 'w', 'w!'): + raise VmError('Invalid mode') + + back = {'dev' : dev, + 'type' : typ, + 'params' : params, + 'mode' : mode, + } + + uuid = config.get('uuid') + if uuid: + back['uuid'] = uuid + + if security.on() == xsconstants.XS_POLICY_USE: + self.do_access_control(config, uname) + + (device_path, devid) = blkif.blkdev_name_to_number(dev) + if devid is None: + raise VmError('Unable to find number for device (%s)' % (dev)) + + front = { device_path : "%i" % devid, + 'device-type' : dev_type + } + + protocol = config.get('protocol') + if protocol: + front['protocol'] = protocol + + return (devid, back, front) + + def do_access_control(self, config, uname): + (label, ssidref, policy) = \ + security.get_res_security_details(uname) + domain_label = self.vm.get_security_label() + if domain_label: + rc = security.res_security_check_xapi(label, ssidref, policy, + domain_label) + if rc == 0: + raise VmError("VM's access to block device '%s' denied" % + uname) + else: + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + if label != ACM_LABEL_UNLABELED: + raise VmError("VM must have a security label to access " + "block device '%s'" % uname) + + def reconfigureDevice(self, _, config): + """@see DevController.reconfigureDevice""" + (devid, new_back, new_front) = self.getDeviceDetails(config) + + (dev, mode) = self.readBackend(devid, 'dev', 'mode') + dev_type = self.readFrontend(devid, 'device-type') + + if (dev_type == 'cdrom' and new_front['device-type'] == 'cdrom' and + dev == new_back['dev'] and mode == 'r'): + # dummy device + self.writeBackend(devid, + 'type', new_back['type'], + 'params', '') + # new backend-device + self.writeBackend(devid, + 'type', new_back['type'], + 'params', new_back['params']) + return new_back.get('uuid') + else: + raise VmError('Refusing to reconfigure device %s:%d to %s' % + (self.deviceClass, devid, config)) + + + def getDeviceConfiguration(self, devid, transaction = None): + """Returns the configuration of a device. + + @note: Similar to L{configuration} except it returns a dict. + @return: dict + """ + config = DevController.getDeviceConfiguration(self, devid, transaction) + if transaction is None: + devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode', + 'uuid') + else: + devinfo = self.readBackendTxn(transaction, devid, + 'dev', 'type', 'params', 'mode', 'uuid') + dev, typ, params, mode, uuid = devinfo + + if dev: + if transaction is None: + dev_type = self.readFrontend(devid, 'device-type') + else: + dev_type = self.readFrontendTxn(transaction, devid, 'device-type') + if dev_type: + dev += ':' + dev_type + config['dev'] = dev + if typ and params: + config['uname'] = typ +':' + params + else: + config['uname'] = None + if mode: + config['mode'] = mode + if uuid: + config['uuid'] = uuid + + proto = self.readFrontend(devid, 'protocol') + if proto: + config['protocol'] = proto + + return config + + def destroyDevice(self, devid, force): + """@see DevController.destroyDevice""" + + # vbd device IDs can be either string or integer. Further, the + # following string values are possible: + # - devicetype/deviceid (vbd/51728) + # - devicetype/devicename (/dev/xvdb) + # - devicename (xvdb) + # Let our superclass handle integer or devicetype/deviceid forms. + # If we are given a device name form, then look up the device ID + # from it, and destroy that ID instead. + try: + DevController.destroyDevice(self, devid, force) + except ValueError: + dev = self.convertToDeviceNumber(devid) + + for i in self.deviceIDs(): + if i == dev: + DevController.destroyDevice(self, i, force) + return + raise VmError("Device %s not connected" % devid) + + def convertToDeviceNumber(self, devid): + try: + dev = int(devid) + except ValueError: + if type(devid) is not str: + raise VmError("devid %s is wrong type" % str(devid)) + try: + dev = devid.split('/')[-1] + dev = int(dev) + except ValueError: + (device_path, dev) = blkif.blkdev_name_to_number(dev) + return dev diff --git a/tools/python/xen/xend/server/iopif.py b/tools/python/xen/xend/server/iopif.py new file mode 100644 index 0000000..3b1a263 --- /dev/null +++ b/tools/python/xen/xend/server/iopif.py @@ -0,0 +1,84 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +# Copyright (C) 2005 Jody Belka +#============================================================================ + + +import types + +import xen.lowlevel.xc + +from xen.xend.XendError import VmError + +from xen.xend.server.DevController import DevController + + +xc = xen.lowlevel.xc.xc() + + +def parse_ioport(val): + """Parse an i/o port field. + """ + if isinstance(val, types.StringType): + radix = 10 + if val.startswith('0x') or val.startswith('0X'): + radix = 16 + v = int(val, radix) + else: + v = val + return v + + +class IOPortsController(DevController): + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + + def get_param(field): + try: + val = config.get(field) + + if not val: + raise VmError('ioports: Missing %s config setting' % field) + + return parse_ioport(val) + except: + raise VmError('ioports: Invalid config setting %s: %s' % + (field, val)) + + io_from = get_param('from') + io_to = get_param('to') + + if io_to < io_from or io_to >= 65536: + raise VmError('ioports: Invalid i/o range: %s - %s' % + (io_from, io_to)) + + rc = xc.domain_ioport_permission(domid = self.getDomid(), + first_port = io_from, + nr_ports = io_to - io_from + 1, + allow_access = True) + + if rc < 0: + #todo non-fatal + raise VmError( + 'ioports: Failed to configure legacy i/o range: %s - %s' % + (io_from, io_to)) + + return (None, {}, {}) diff --git a/tools/python/xen/xend/server/irqif.py b/tools/python/xen/xend/server/irqif.py new file mode 100644 index 0000000..db4b1de --- /dev/null +++ b/tools/python/xen/xend/server/irqif.py @@ -0,0 +1,78 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +# Copyright (C) 2005 Jody Belka +#============================================================================ +# This code based on tools/python/xen/xend/server/iopif.py and modified +# to handle interrupts +#============================================================================ + + +import types + +import xen.lowlevel.xc + +from xen.xend import sxp +from xen.xend.XendError import VmError + +from xen.xend.server.DevController import DevController + + +xc = xen.lowlevel.xc.xc() + + +class IRQController(DevController): + + def __init__(self, vm): + DevController.__init__(self, vm) + + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + + def get_param(field): + try: + val = config.get(field) + + if not val: + raise VmError('irq: Missing %s config setting' % field) + + if isinstance(val, types.StringType): + return int(val,10) + radix = 10 + else: + return val + except: + raise VmError('irq: Invalid config setting %s: %s' % + (field, val)) + + pirq = get_param('irq') + + rc = xc.domain_irq_permission(domid = self.getDomid(), + pirq = pirq, + allow_access = True) + + if rc < 0: + #todo non-fatal + raise VmError( + 'irq: Failed to configure irq: %d' % (pirq)) + rc = xc.physdev_map_pirq(domid = self.getDomid(), + index = pirq, + pirq = pirq) + if rc < 0: + raise VmError( + 'irq: Failed to map irq %x' % (pirq)) + return (None, {}, {}) diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py new file mode 100644 index 0000000..469818e --- /dev/null +++ b/tools/python/xen/xend/server/netif.py @@ -0,0 +1,198 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +"""Support for virtual network interfaces. +""" + +import os +import random +import re + +from xen.xend import XendOptions +from xen.xend.server.DevController import DevController +from xen.xend.XendError import VmError +from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance +import xen.util.xsm.xsm as security +from xen.util import xsconstants + +from xen.xend.XendLogging import log + +xoptions = XendOptions.instance() + +def randomMAC(): + """Generate a random MAC address. + + Uses OUI (Organizationally Unique Identifier) 00-16-3E, allocated to + Xensource, Inc. The OUI list is available at + http://standards.ieee.org/regauth/oui/oui.txt. + + The remaining 3 fields are random, with the first bit of the first + random field set 0. + + @return: MAC address string + """ + mac = [ 0x00, 0x16, 0x3e, + random.randint(0x00, 0x7f), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff) ] + return ':'.join(map(lambda x: "%02x" % x, mac)) + +rate_re = re.compile("^([0-9]+)([GMK]?)([Bb])/s(@([0-9]+)([mu]?)s)?$") + +def parseRate(ratestr): + """if parsing fails this will return default of unlimited rate""" + bytes_per_interval = 0xffffffffL # 0xffffffff # big default + interval_usecs = 0L # disabled + + m = rate_re.match(ratestr) + if m: + bytes_per_sec = long(m.group(1)) + + if m.group(2) == 'G': + bytes_per_sec *= 1000 * 1000 * 1000 + elif m.group(2) == 'M': + bytes_per_sec *= 1000 * 1000 + elif m.group(2) == 'K': + bytes_per_sec *= 1000 + + if m.group(3) == 'b': + bytes_per_sec /= 8 + + if m.group(5) is None: + interval_usecs = 50000L # 50ms default + else: + interval_usecs = long(m.group(5)) + if m.group(6) == '': + interval_usecs *= 1000 * 1000 + elif m.group(6) == 'm': + interval_usecs *= 1000 + + bytes_per_interval = (bytes_per_sec * interval_usecs) / 1000000L + + # overflow / underflow checking: default to unlimited rate + if bytes_per_interval == 0 or bytes_per_interval > 0xffffffffL or \ + interval_usecs == 0 or interval_usecs > 0xffffffffL: + bytes_per_interval = 0xffffffffL + interval_usecs = 0L + + return "%lu,%lu" % (bytes_per_interval, interval_usecs) + + +class NetifController(DevController): + """Network interface controller. Handles all network devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + + script = config.get('script', xoptions.get_vif_script()) + typ = config.get('type') + bridge = config.get('bridge') + mac = config.get('mac') + vifname = config.get('vifname') + rate = config.get('rate') + uuid = config.get('uuid') + ipaddr = config.get('ip') + model = config.get('model') + accel = config.get('accel') + sec_lab = config.get('security_label') + + if not mac: + raise VmError("MAC address not specified or generated.") + + devid = self.allocateDeviceID() + + back = { 'script' : script, + 'mac' : mac } + if typ: + back['type'] = typ + if ipaddr: + back['ip'] = ipaddr + if bridge: + back['bridge'] = bridge + if vifname: + back['vifname'] = vifname + if rate: + back['rate'] = rate + if uuid: + back['uuid'] = uuid + if model: + back['model'] = model + if accel: + back['accel'] = accel + if sec_lab: + back['security_label'] = sec_lab + + back['handle'] = "%i" % devid + back['script'] = os.path.join(xoptions.network_script_dir, script) + if rate: + back['rate'] = parseRate(rate) + + front = {} + if typ != 'ioemu': + front = { 'handle' : "%i" % devid, + 'mac' : mac } + + if security.on() == xsconstants.XS_POLICY_USE: + self.do_access_control(config) + + return (devid, back, front) + + + def do_access_control(self, config): + """ do access control checking. Throws a VMError if access is denied """ + domain_label = self.vm.get_security_label() + stes = XSPolicyAdminInstance().get_stes_of_vmlabel(domain_label) + res_label = config.get('security_label') + if len(stes) > 1 or res_label: + if not res_label: + raise VmError("'VIF' must be labeled") + (label, ssidref, policy) = \ + security.security_label_to_details(res_label) + if domain_label: + rc = security.res_security_check_xapi(label, ssidref, + policy, + domain_label) + if rc == 0: + raise VmError("VM's access to network device denied. " + "Check labeling") + else: + raise VmError("VM must have a security label to access " + "network device") + + + def getDeviceConfiguration(self, devid, transaction = None): + """@see DevController.configuration""" + + result = DevController.getDeviceConfiguration(self, devid, transaction) + + for x in ( 'script', 'ip', 'bridge', 'mac', + 'type', 'vifname', 'rate', 'uuid', 'model', 'accel', + 'security_label'): + if transaction is None: + y = self.readBackend(devid, x) + else: + y = self.readBackendTxn(transaction, devid, x) + if y: + result[x] = y + + return result diff --git a/tools/python/xen/xend/server/params.py b/tools/python/xen/xend/server/params.py new file mode 100644 index 0000000..c7099e2 --- /dev/null +++ b/tools/python/xen/xend/server/params.py @@ -0,0 +1,46 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +import os + +def getenv(var, val, conv=None): + """Get a value from the environment, with optional conversion. + + @param var name of environment variable + @param val default value + @param conv conversion function to apply to env value + @return converted value or default + """ + try: + v = os.getenv(var) + if v is None: + v = val + else: + print var, '=', v + if conv: + v = conv(v) + except: + v = val + return v + +# The following parameters could be placed in a configuration file. +XEND_PID_FILE = '/var/run/xend.pid' +XEND_TRACE_FILE = '/var/log/xen/xend.trace' +XEND_DEBUG_LOG = '/var/log/xen/xend-debug.log' +XEND_USER = 'root' +XEND_DEBUG = getenv("XEND_DEBUG", 0, conv=int) +XEND_DAEMONIZE = getenv("XEND_DAEMONIZE", not XEND_DEBUG, conv=int) diff --git a/tools/python/xen/xend/server/pciif.py b/tools/python/xen/xend/server/pciif.py new file mode 100644 index 0000000..326e9d6 --- /dev/null +++ b/tools/python/xen/xend/server/pciif.py @@ -0,0 +1,551 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +import types +import time + +from xen.xend import sxp +from xen.xend import arch +from xen.xend.XendError import VmError +from xen.xend.XendLogging import log + +from xen.xend.server.DevController import DevController, xenbusState + +import xen.lowlevel.xc + +from xen.util.pci import * +import resource +import re + +from xen.xend.server.pciquirk import * + +xc = xen.lowlevel.xc.xc() + +#Calculate PAGE_SHIFT: number of bits to shift an address to get the page number +PAGE_SIZE = resource.getpagesize() +PAGE_SHIFT = 0 +t = PAGE_SIZE +while not (t&1): + t>>=1 + PAGE_SHIFT+=1 + +def parse_hex(val): + try: + if isinstance(val, types.StringTypes): + return int(val, 16) + else: + return val + except ValueError: + return None + +class PciController(DevController): + + def __init__(self, vm): + DevController.__init__(self, vm) + + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + back = {} + pcidevid = 0 + vslots = "" + for pci_config in config.get('devs', []): + domain = parse_hex(pci_config.get('domain', 0)) + bus = parse_hex(pci_config.get('bus', 0)) + slot = parse_hex(pci_config.get('slot', 0)) + func = parse_hex(pci_config.get('func', 0)) + + vslt = pci_config.get('vslt') + if vslt is not None: + vslots = vslots + vslt + ";" + + back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%01x" % \ + (domain, bus, slot, func) + back['uuid-%i' % pcidevid] = pci_config.get('uuid', '') + pcidevid += 1 + + if vslots != "": + back['vslots'] = vslots + + back['num_devs']=str(pcidevid) + back['uuid'] = config.get('uuid','') + return (0, back, {}) + + + def reconfigureDevice(self, _, config): + """@see DevController.reconfigureDevice""" + (devid, back, front) = self.getDeviceDetails(config) + num_devs = int(back['num_devs']) + states = config.get('states', []) + + old_vslots = self.readBackend(devid, 'vslots') + if old_vslots is None: + old_vslots = '' + num_olddevs = int(self.readBackend(devid, 'num_devs')) + + for i in range(num_devs): + try: + dev = back['dev-%i' % i] + state = states[i] + uuid = back['uuid-%i' %i] + except: + raise XendError('Error reading config') + + if state == 'Initialising': + # PCI device attachment + for j in range(num_olddevs): + if dev == self.readBackend(devid, 'dev-%i' % j): + raise XendError('Device %s is already connected.' % dev) + log.debug('Attaching PCI device %s.' % dev) + (domain, bus, slotfunc) = dev.split(':') + (slot, func) = slotfunc.split('.') + domain = parse_hex(domain) + bus = parse_hex(bus) + slot = parse_hex(slot) + func = parse_hex(func) + self.setupOneDevice(domain, bus, slot, func) + + self.writeBackend(devid, 'dev-%i' % (num_olddevs + i), dev) + self.writeBackend(devid, 'state-%i' % (num_olddevs + i), + str(xenbusState['Initialising'])) + self.writeBackend(devid, 'uuid-%i' % (num_olddevs + i), uuid) + self.writeBackend(devid, 'num_devs', str(num_olddevs + i + 1)) + + # Update vslots + if back['vslots'] is not None: + vslots = old_vslots + back['vslots'] + self.writeBackend(devid, 'vslots', vslots) + + elif state == 'Closing': + # PCI device detachment + found = False + for j in range(num_olddevs): + if dev == self.readBackend(devid, 'dev-%i' % j): + found = True + log.debug('Detaching device %s' % dev) + self.writeBackend(devid, 'state-%i' % j, + str(xenbusState['Closing'])) + if not found: + raise XendError('Device %s is not connected' % dev) + + # Update vslots + if back.get('vslots') is not None: + vslots = old_vslots + for vslt in back['vslots'].split(';'): + if vslt != '': + vslots = vslots.replace(vslt + ';', '', 1) + if vslots == '': + self.removeBackend(devid, 'vslots') + else: + self.writeBackend(devid, 'vslots', vslots) + + else: + raise XendError('Error configuring device %s: invalid state %s' + % (dev,state)) + + self.writeBackend(devid, 'state', str(xenbusState['Reconfiguring'])) + + return self.readBackend(devid, 'uuid') + + + def getDeviceConfiguration(self, devid, transaction = None): + result = DevController.getDeviceConfiguration(self, devid, transaction) + num_devs = self.readBackend(devid, 'num_devs') + pci_devs = [] + + vslots = self.readBackend(devid, 'vslots') + if vslots is not None: + if vslots[-1] == ";": + vslots = vslots[:-1] + slot_list = vslots.split(';') + + for i in range(int(num_devs)): + dev_config = self.readBackend(devid, 'dev-%d' % i) + + pci_match = re.match(r"((?P[0-9a-fA-F]{1,4})[:,])?" + + r"(?P[0-9a-fA-F]{1,2})[:,]" + + r"(?P[0-9a-fA-F]{1,2})[.,]" + + r"(?P[0-7]{1,2})$", dev_config) + + if pci_match!=None: + pci_dev_info = pci_match.groupdict() + dev_dict = {'domain': '0x%(domain)s' % pci_dev_info, + 'bus': '0x%(bus)s' % pci_dev_info, + 'slot': '0x%(slot)s' % pci_dev_info, + 'func': '0x%(func)s' % pci_dev_info} + + # Per device uuid info + dev_dict['uuid'] = self.readBackend(devid, 'uuid-%d' % i) + + #append vslot info + if vslots is not None: + try: + dev_dict['vslt'] = slot_list[i] + except IndexError: + dev_dict['vslt'] = '0x0' + + pci_devs.append(dev_dict) + + result['devs'] = pci_devs + result['uuid'] = self.readBackend(devid, 'uuid') + return result + + def configuration(self, devid, transaction = None): + """Returns SXPR for devices on domain. + + @note: we treat this dict especially to convert to + SXP because it is not a straight dict of strings.""" + + configDict = self.getDeviceConfiguration(devid, transaction) + sxpr = [self.deviceClass] + + # remove devs + devs = configDict.pop('devs', []) + + for dev in devs: + dev_sxpr = ['dev'] + for dev_item in dev.items(): + dev_sxpr.append(list(dev_item)) + sxpr.append(dev_sxpr) + + for key, val in configDict.items(): + if type(val) == type(list()): + for v in val: + sxpr.append([key, v]) + else: + sxpr.append([key, val]) + + return sxpr + + def CheckSiblingDevices(self, domid, dev): + """ Check if all sibling devices of dev are owned by pciback + """ + if not self.vm.info.is_hvm(): + return + + group_str = xc.get_device_group(domid, dev.domain, dev.bus, dev.slot, dev.func) + if group_str == "": + return + + #group string format xx:xx.x,xx:xx.x, + devstr_len = group_str.find(',') + for i in range(0, len(group_str), devstr_len + 1): + (bus, slotfunc) = group_str[i:i + devstr_len].split(':') + (slot, func) = slotfunc.split('.') + b = parse_hex(bus) + d = parse_hex(slot) + f = parse_hex(func) + try: + sdev = PciDevice(dev.domain, b, d, f) + except Exception, e: + #no dom0 drivers bound to sdev + continue + + if sdev.driver!='pciback': + raise VmError(("pci: PCI Backend does not own\n "+ \ + "sibling device %s of device %s\n"+ \ + "See the pciback.hide kernel "+ \ + "command-line parameter or\n"+ \ + "bind your slot/device to the PCI backend using sysfs" \ + )%(sdev.name, dev.name)) + return + + def setupOneDevice(self, domain, bus, slot, func): + """ Attach I/O resources for device to frontend domain + """ + fe_domid = self.getDomid() + + try: + dev = PciDevice(domain, bus, slot, func) + except Exception, e: + raise VmError("pci: failed to locate device and "+ + "parse it's resources - "+str(e)) + + if dev.driver!='pciback': + raise VmError(("pci: PCI Backend does not own device "+ \ + "%s\n"+ \ + "See the pciback.hide kernel "+ \ + "command-line parameter or\n"+ \ + "bind your slot/device to the PCI backend using sysfs" \ + )%(dev.name)) + + if dev.has_non_page_aligned_bar and arch.type != "ia64": + raise VmError("pci: %s: non-page-aligned MMIO BAR found." % dev.name) + + self.CheckSiblingDevices(fe_domid, dev) + + # We don't do FLR when we create domain and hotplug device into guest, + # namely, we only do FLR when we destroy domain or hotplug device from + # guest. This is mainly to work around the race condition in hotplug code + # paths. See the changeset's description for details. + # if arch.type != "ia64": + # dev.do_FLR() + + PCIQuirk(dev.vendor, dev.device, dev.subvendor, dev.subdevice, domain, + bus, slot, func) + + if not self.vm.info.is_hvm(): + # Setup IOMMU device assignment + pci_str = "0x%x, 0x%x, 0x%x, 0x%x" % (domain, bus, slot, func) + bdf = xc.assign_device(fe_domid, pci_str) + if bdf > 0: + raise VmError("Failed to assign device to IOMMU (%x:%x.%x)" + % (bus, slot, func)) + log.debug("pci: assign device %x:%x.%x" % (bus, slot, func)) + + for (start, size) in dev.ioports: + log.debug('pci: enabling ioport 0x%x/0x%x'%(start,size)) + rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start, + nr_ports = size, allow_access = True) + if rc<0: + raise VmError(('pci: failed to configure I/O ports on device '+ + '%s - errno=%d')%(dev.name,rc)) + + for (start, size) in dev.iomem: + # Convert start/size from bytes to page frame sizes + start_pfn = start>>PAGE_SHIFT + # Round number of pages up to nearest page boundary (if not on one) + nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT + + log.debug('pci: enabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \ + (start,size,start_pfn,nr_pfns)) + rc = xc.domain_iomem_permission(domid = fe_domid, + first_pfn = start_pfn, + nr_pfns = nr_pfns, + allow_access = True) + if rc<0: + raise VmError(('pci: failed to configure I/O memory on device '+ + '%s - errno=%d')%(dev.name,rc)) + rc = xc.physdev_map_pirq(domid = fe_domid, + index = dev.irq, + pirq = dev.irq) + if rc < 0: + raise VmError(('pci: failed to map irq on device '+ + '%s - errno=%d')%(dev.name,rc)) + + if dev.msix: + for (start, size) in dev.msix_iomem: + start_pfn = start>>PAGE_SHIFT + nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT + log.debug('pci-msix: remove permission for 0x%x/0x%x 0x%x/0x%x' % \ + (start,size, start_pfn, nr_pfns)) + rc = xc.domain_iomem_permission(domid = fe_domid, + first_pfn = start_pfn, + nr_pfns = nr_pfns, + allow_access = False) + if rc<0: + raise VmError(('pci: failed to remove msi-x iomem')) + + if dev.irq>0: + log.debug('pci: enabling irq %d'%dev.irq) + rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq, + allow_access = True) + if rc<0: + raise VmError(('pci: failed to configure irq on device '+ + '%s - errno=%d')%(dev.name,rc)) + + def setupDevice(self, config): + """Setup devices from config + """ + pci_str_list = [] + pci_dev_list = [] + for pci_config in config.get('devs', []): + domain = parse_hex(pci_config.get('domain', 0)) + bus = parse_hex(pci_config.get('bus', 0)) + slot = parse_hex(pci_config.get('slot', 0)) + func = parse_hex(pci_config.get('func', 0)) + pci_str = '%04x:%02x:%02x.%01x' % (domain, bus, slot, func) + pci_str_list = pci_str_list + [pci_str] + pci_dev_list = pci_dev_list + [(domain, bus, slot, func)] + + for (domain, bus, slot, func) in pci_dev_list: + try: + dev = PciDevice(domain, bus, slot, func) + except Exception, e: + raise VmError("pci: failed to locate device and "+ + "parse it's resources - "+str(e)) + if (dev.dev_type == DEV_TYPE_PCIe_ENDPOINT) and not dev.pcie_flr: + if dev.bus == 0: + # We cope with this case by using the Dstate transition + # method or some vendor specific methods for now. + err_msg = 'pci: %s: it is on bus 0, but has no PCIe' +\ + ' FLR Capability. Will try the Dstate transition'+\ + ' method or some vendor specific methods if available.' + log.warn(err_msg % dev.name) + else: + funcs = dev.find_all_the_multi_functions() + for f in funcs: + if not f in pci_str_list: + (f_dom, f_bus, f_slot, f_func) = parse_pci_name(f) + f_pci_str = '0x%x,0x%x,0x%x,0x%x' % \ + (f_dom, f_bus, f_slot, f_func) + # f has been assigned to other guest? + if xc.test_assign_device(0, f_pci_str) != 0: + err_msg = 'pci: %s must be co-assigned to' + \ + ' the same guest with %s' + raise VmError(err_msg % (f, dev.name)) + elif dev.dev_type == DEV_TYPE_PCI: + if dev.bus == 0 or arch.type == "ia64": + if not dev.pci_af_flr: + # We cope with this case by using the Dstate transition + # method or some vendor specific methods for now. + err_msg = 'pci: %s: it is on bus 0, but has no PCI' +\ + ' Advanced Capabilities for FLR. Will try the'+\ + ' Dstate transition method or some vendor' +\ + ' specific methods if available.' + log.warn(err_msg % dev.name) + else: + # All devices behind the uppermost PCI/PCI-X bridge must be\ + # co-assigned to the same guest. + devs_str = dev.find_coassigned_devices(True) + # Remove the element 0 which is a bridge + del devs_str[0] + + for s in devs_str: + if not s in pci_str_list: + (s_dom, s_bus, s_slot, s_func) = parse_pci_name(s) + s_pci_str = '0x%x,0x%x,0x%x,0x%x' % \ + (s_dom, s_bus, s_slot, s_func) + # s has been assigned to other guest? + if xc.test_assign_device(0, s_pci_str) != 0: + err_msg = 'pci: %s must be co-assigned to the'+\ + ' same guest with %s' + raise VmError(err_msg % (s, dev.name)) + + for (domain, bus, slot, func) in pci_dev_list: + self.setupOneDevice(domain, bus, slot, func) + + return + + def cleanupOneDevice(self, domain, bus, slot, func): + """ Detach I/O resources for device from frontend domain + """ + fe_domid = self.getDomid() + + try: + dev = PciDevice(domain, bus, slot, func) + except Exception, e: + raise VmError("pci: failed to locate device and "+ + "parse it's resources - "+str(e)) + + if dev.driver!='pciback': + raise VmError(("pci: PCI Backend does not own device "+ \ + "%s\n"+ \ + "See the pciback.hide kernel "+ \ + "command-line parameter or\n"+ \ + "bind your slot/device to the PCI backend using sysfs" \ + )%(dev.name)) + + if not self.vm.info.is_hvm(): + pci_str = "0x%x, 0x%x, 0x%x, 0x%x" % (domain, bus, slot, func) + bdf = xc.deassign_device(fe_domid, pci_str) + if bdf > 0: + raise VmError("Failed to deassign device from IOMMU (%x:%x.%x)" + % (bus, slot, func)) + log.debug("pci: deassign device %x:%x.%x" % (bus, slot, func)) + + for (start, size) in dev.ioports: + log.debug('pci: disabling ioport 0x%x/0x%x'%(start,size)) + rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start, + nr_ports = size, allow_access = False) + if rc<0: + raise VmError(('pci: failed to configure I/O ports on device '+ + '%s - errno=%d')%(dev.name,rc)) + + for (start, size) in dev.iomem: + # Convert start/size from bytes to page frame sizes + start_pfn = start>>PAGE_SHIFT + # Round number of pages up to nearest page boundary (if not on one) + nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT + + log.debug('pci: disabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \ + (start,size,start_pfn,nr_pfns)) + rc = xc.domain_iomem_permission(domid = fe_domid, + first_pfn = start_pfn, + nr_pfns = nr_pfns, + allow_access = False) + if rc<0: + raise VmError(('pci: failed to configure I/O memory on device '+ + '%s - errno=%d')%(dev.name,rc)) + + if dev.irq>0: + log.debug('pci: disabling irq %d'%dev.irq) + rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq, + allow_access = False) + if rc<0: + raise VmError(('pci: failed to configure irq on device '+ + '%s - errno=%d')%(dev.name,rc)) + dev.do_FLR() + + def cleanupDevice(self, devid): + """ Detach I/O resources for device and cleanup xenstore nodes + after reconfigure. + + @param devid: The device ID + @type devid: int + @return: Return the number of devices connected + @rtype: int + """ + num_devs = int(self.readBackend(devid, 'num_devs')) + new_num_devs = 0 + for i in range(num_devs): + state = int(self.readBackend(devid, 'state-%i' % i)) + if state == xenbusState['Closing']: + # Detach I/O resources. + dev = self.readBackend(devid, 'dev-%i' % i) + (domain, bus, slotfunc) = dev.split(':') + (slot, func) = slotfunc.split('.') + domain = parse_hex(domain) + bus = parse_hex(bus) + slot = parse_hex(slot) + func = parse_hex(func) + # In HVM case, I/O resources are disabled in ioemu. + self.cleanupOneDevice(domain, bus, slot, func) + # Remove xenstore nodes. + self.removeBackend(devid, 'dev-%i' % i) + self.removeBackend(devid, 'vdev-%i' % i) + self.removeBackend(devid, 'state-%i' % i) + self.removeBackend(devid, 'uuid-%i' % i) + else: + if new_num_devs != i: + tmpdev = self.readBackend(devid, 'dev-%i' % i) + self.writeBackend(devid, 'dev-%i' % new_num_devs, tmpdev) + self.removeBackend(devid, 'dev-%i' % i) + tmpvdev = self.readBackend(devid, 'vdev-%i' % i) + if tmpvdev is not None: + self.writeBackend(devid, 'vdev-%i' % new_num_devs, + tmpvdev) + self.removeBackend(devid, 'vdev-%i' % i) + tmpstate = self.readBackend(devid, 'state-%i' % i) + self.writeBackend(devid, 'state-%i' % new_num_devs, tmpstate) + self.removeBackend(devid, 'state-%i' % i) + tmpuuid = self.readBackend(devid, 'uuid-%i' % i) + self.writeBackend(devid, 'uuid-%i' % new_num_devs, tmpuuid) + self.removeBackend(devid, 'uuid-%i' % i) + new_num_devs = new_num_devs + 1 + + self.writeBackend(devid, 'num_devs', str(new_num_devs)) + + return new_num_devs + + def waitForBackend(self,devid): + return (0, "ok - no hotplug") + + def migrate(self, config, network, dst, step, domName): + raise XendError('Migration not permitted with assigned PCI device.') diff --git a/tools/python/xen/xend/server/pciquirk.py b/tools/python/xen/xend/server/pciquirk.py new file mode 100644 index 0000000..c8f8f6d --- /dev/null +++ b/tools/python/xen/xend/server/pciquirk.py @@ -0,0 +1,146 @@ +from xen.xend.XendLogging import log +from xen.xend.XendError import XendError, VmError +import sys +import os.path +from xen.xend.sxp import * + +QUIRK_SYSFS_NODE = "/sys/bus/pci/drivers/pciback/quirks" +QUIRK_CONFIG_FILE = "/etc/xen/xend-pci-quirks.sxp" +PERMISSIVE_CONFIG_FILE = "/etc/xen/xend-pci-permissive.sxp" +PERMISSIVE_SYSFS_NODE = "/sys/bus/pci/drivers/pciback/permissive" + +class PCIQuirk: + def __init__( self, vendor, device, subvendor, subdevice, domain, bus, slot, func): + self.vendor = vendor + self.device = device + self.subvendor = subvendor + self.subdevice = subdevice + self.domain = domain + self.bus = bus + self.slot = slot + self.func = func + + self.devid = "%04x:%04x:%04x:%04x" % (vendor, device, subvendor, subdevice) + self.pciid = "%04x:%02x:%02x.%01x" % (domain, bus, slot, func) + self.quirks = self.__getQuirksByID() + + self.__sendQuirks() + self.__sendPermDevs() + + def __matchPCIdev( self, list ): + ret = False + if list == None: + return False + for id in list: + if id.startswith(self.devid[:9]): # id's vendor and device ID match + skey = id.split(':') + size = len(skey) + if (size == 2): # subvendor/subdevice not suplied + ret = True + break + elif (size == 4): # check subvendor/subdevice + # check subvendor + subven = '%04x' % self.subvendor + if ((skey[2] != 'FFFF') and + (skey[2] != 'ffff') and + (skey[2] != subven)): + continue + # check subdevice + subdev = '%04x' % self.subdevice + if ((skey[3] != 'FFFF') and + (skey[3] != 'ffff') and + (skey[3] != subdev)): + continue + ret = True + break + else: + log.debug("WARNING: invalid configuration entry: %s" % id) + ret = False + break + return ret + + def __getQuirksByID( self ): + if os.path.exists(QUIRK_CONFIG_FILE): + try: + fin = file(QUIRK_CONFIG_FILE, 'rb') + try: + pci_quirks_config = parse(fin) + finally: + fin.close() + if pci_quirks_config is None: + pci_quirks_config = ['xend-pci-quirks'] + else: + pci_quirks_config.insert(0, 'xend-pci-quirks') + self.pci_quirks_config = pci_quirks_config + except Exception, ex: + raise XendError("Reading config file %s: %s" % + (QUIRK_CONFIG_FILE, str(ex))) + else: + log.info("Config file does not exist: %s" % QUIRK_CONFIG_FILE) + self.pci_quirks_config = ['xend-pci-quirks'] + + devices = children(self.pci_quirks_config) + for dev in devices: + ids = child_at(child(dev,'pci_ids'),0) + fields = child_at(child(dev,'pci_config_space_fields'),0) + if self.__matchPCIdev( ids ): + log.info("Quirks found for PCI device [%s]" % self.devid) + return fields + + log.info("NO quirks found for PCI device [%s]" % self.devid) + return [] + + def __sendQuirks(self): + for quirk in self.quirks: + log.debug("Quirk Info: %04x:%02x:%02x.%1x-%s" % (self.domain, + self.bus, self.slot, self.func, quirk)) + try: + f = file(QUIRK_SYSFS_NODE ,"w") + f.write( "%04x:%02x:%02x.%1x-%s" % (self.domain, self.bus, + self.slot, self.func, quirk) ) + f.close() + except Exception, e: + raise VmError("pci: failed to open/write/close quirks " + + "sysfs node - " + str(e)) + + def __devIsUnconstrained( self ): + if os.path.exists(PERMISSIVE_CONFIG_FILE): + try: + fin = file(PERMISSIVE_CONFIG_FILE, 'rb') + try: + pci_perm_dev_config = parse(fin) + finally: + fin.close() + if pci_perm_dev_config is None: + pci_perm_dev_config = [''] + else: + pci_perm_dev_config.insert(0, '') + self.pci_perm_dev_config = pci_perm_dev_config + except Exception, ex: + raise XendError("Reading config file %s: %s" % + (PERMISSIVE_CONFIG_FILE,str(ex))) + else: + log.info("Config file does not exist: %s" % PERMISSIVE_CONFIG_FILE) + self.pci_perm_dev_config = ['xend-pci-perm-devs'] + + devices = child_at(child(pci_perm_dev_config, 'unconstrained_dev_ids'),0) + if self.__matchPCIdev( devices ): + log.debug("Permissive mode enabled for PCI device [%s]" % + self.devid) + return True + log.debug("Permissive mode NOT enabled for PCI device [%s]" % + self.devid) + return False + + def __sendPermDevs(self): + if self.__devIsUnconstrained( ): + log.debug("Unconstrained device: %04x:%02x:%02x.%1x" % + (self.domain, self.bus, self.slot, self.func)) + try: + f = file(PERMISSIVE_SYSFS_NODE ,"w") + f.write( "%04x:%02x:%02x.%1x" % (self.domain, self.bus, + self.slot, self.func)) + f.close() + except Exception, e: + raise VmError("pci: failed to open/write/close permissive " + + "sysfs node: " + str(e)) diff --git a/tools/python/xen/xend/server/relocate.py b/tools/python/xen/xend/server/relocate.py new file mode 100644 index 0000000..007884b --- /dev/null +++ b/tools/python/xen/xend/server/relocate.py @@ -0,0 +1,171 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +import re +import os +import sys +import StringIO +import threading + +from xen.web import protocol, tcp, unix, connection + +from xen.xend import sxp +from xen.xend import XendDomain +from xen.xend import XendOptions +from xen.xend.XendError import XendError +from xen.xend.XendLogging import log + + +class RelocationProtocol(protocol.Protocol): + """Asynchronous handler for a connected relocation socket. + """ + + def __init__(self): + protocol.Protocol.__init__(self) + self.parser = sxp.Parser() + + def dataReceived(self, data): + try: + self.parser.input(data) + while(self.parser.ready()): + val = self.parser.get_val() + res = self.dispatch(val) + self.send_result(res) + if self.parser.at_eof(): + self.close() + except SystemExit: + raise + except: + self.send_error() + + def close(self): + if self.transport: + self.transport.close() + + def send_reply(self, sxpr): + io = StringIO.StringIO() + sxp.show(sxpr, out=io) + print >> io + io.seek(0) + if self.transport: + return self.transport.write(io.getvalue()) + else: + return 0 + + def send_result(self, res): + if res is None: + resp = ['ok'] + else: + resp = ['ok', res] + return self.send_reply(resp) + + def send_error(self): + (extype, exval) = sys.exc_info()[:2] + return self.send_reply(['err', + ['type', str(extype)], + ['value', str(exval)]]) + + def opname(self, name): + return 'op_' + name.replace('.', '_') + + def operror(self, name, _): + raise XendError('Invalid operation: ' +name) + + def dispatch(self, req): + op_name = sxp.name(req) + op_method_name = self.opname(op_name) + op_method = getattr(self, op_method_name, self.operror) + return op_method(op_name, req) + + def op_help(self, _1, _2): + def nameop(x): + if x.startswith('op_'): + return x[3:].replace('_', '.') + else: + return x + + l = [ nameop(k) for k in dir(self) if k.startswith('op_') ] + return l + + def op_quit(self, _1, _2): + self.close() + + def op_receive(self, name, _): + if self.transport: + self.send_reply(["ready", name]) + try: + XendDomain.instance().domain_restore_fd( + self.transport.sock.fileno(), relocating=True) + except: + self.send_error() + self.close() + else: + log.error(name + ": no transport") + raise XendError(name + ": no transport") + + def op_sslreceive(self, name, _): + if self.transport: + self.send_reply(["ready", name]) + p2cread, p2cwrite = os.pipe() + threading.Thread(target=connection.SSLSocketServerConnection.recv2fd, + args=(self.transport.sock, p2cwrite)).start() + try: + XendDomain.instance().domain_restore_fd(p2cread, + relocating=True) + except: + os.close(p2cread) + os.close(p2cwrite) + self.send_error() + self.close() + else: + log.error(name + ": no transport") + raise XendError(name + ": no transport") + + +def listenRelocation(): + xoptions = XendOptions.instance() + if xoptions.get_xend_unix_server(): + path = '/var/lib/xend/relocation-socket' + unix.UnixListener(path, RelocationProtocol) + + interface = xoptions.get_xend_relocation_address() + + hosts_allow = xoptions.get_xend_relocation_hosts_allow() + if hosts_allow == '': + hosts_allow = None + else: + hosts_allow = map(re.compile, hosts_allow.split(" ")) + + if xoptions.get_xend_relocation_server(): + port = xoptions.get_xend_relocation_port() + tcp.TCPListener(RelocationProtocol, port, interface = interface, + hosts_allow = hosts_allow) + + if xoptions.get_xend_relocation_ssl_server(): + port = xoptions.get_xend_relocation_ssl_port() + ssl_key_file = xoptions.get_xend_relocation_server_ssl_key_file() + ssl_cert_file = xoptions.get_xend_relocation_server_ssl_cert_file() + + if ssl_key_file and ssl_cert_file: + tcp.SSLTCPListener(RelocationProtocol, port, interface = interface, + hosts_allow = hosts_allow, + ssl_key_file = ssl_key_file, + ssl_cert_file = ssl_cert_file) + else: + raise XendError("ssl_key_file or ssl_cert_file for ssl relocation server is missing.") + diff --git a/tools/python/xen/xend/server/tests/__init__.py b/tools/python/xen/xend/server/tests/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/xend/server/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/xend/server/tests/test_controllers.py b/tools/python/xen/xend/server/tests/test_controllers.py new file mode 100644 index 0000000..dd26775 --- /dev/null +++ b/tools/python/xen/xend/server/tests/test_controllers.py @@ -0,0 +1,81 @@ +import os +import re +import unittest + +import xen.xend.XendOptions + +xen.xend.XendOptions.XendOptions.config_default = '/dev/null' + +from xen.xend.server import netif + + +FAKE_DOMID = 42 +FAKE_DEVID = 63 + + +xoptions = xen.xend.XendOptions.instance() + + +class test_controllers(unittest.TestCase): + + def testNetif(self): + controller = self.controllerInstance(netif.NetifController) + + self.assertNetif(controller.getDeviceDetails({}), None) + self.assertNetif( + controller.getDeviceDetails({'mac': 'aa:bb:cc:dd:ee:ff'}), + 'aa:bb:cc:dd:ee:ff') + + + + def assertNetif(self, results, expectedMac): + + (devid, backdets, frontdets) = results + + self.assertEqual(devid, FAKE_DEVID) + + self.assertEqual(backdets['handle'], str(FAKE_DEVID)) + self.assertEqual(backdets['script'], + os.path.join(xoptions.network_script_dir, + xoptions.get_vif_script())) + self.assertValidMac(backdets['mac'], expectedMac) + + self.assertEqual(frontdets['handle'], str(FAKE_DEVID)) + self.assertValidMac(frontdets['mac'], expectedMac) + + + MAC_REGEXP = re.compile('^' + + ':'.join([r'[0-9a-f][0-9a-f]' + for i in range(0, 6)]) + + '$') + + def assertValidMac(self, mac, expected): + if expected: + self.assertEqual(mac, expected) + else: + self.assert_(self.MAC_REGEXP.match(mac)) + + + def controllerInstance(self, cls): + """Allocate an instance of the given controller class, and override + methods as appropriate so that we can run tests without needing + Xenstored.""" + + result = cls(FakeXendDomainInfo()) + + result.allocateDeviceID = fakeID + + return result + + +class FakeXendDomainInfo: + def getDomainPath(self): + return "/test/fake/domain/%d/" % FAKE_DOMID + + +def fakeID(): + return FAKE_DEVID + + +def test_suite(): + return unittest.makeSuite(test_controllers) diff --git a/tools/python/xen/xend/server/tpmif.py b/tools/python/xen/xend/server/tpmif.py new file mode 100644 index 0000000..23a10e9 --- /dev/null +++ b/tools/python/xen/xend/server/tpmif.py @@ -0,0 +1,141 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004 Mike Wray +# Copyright (C) 2005 IBM Corporation +# Author: Stefan Berger, stefanb@us.ibm.com +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + +"""Support for virtual TPM interfaces.""" + +from xen.xend import XendOptions +from xen.xend.XendLogging import log +from xen.xend.XendError import XendError +from xen.xend.XendConstants import DEV_MIGRATE_TEST, VTPM_DELETE_SCRIPT +from xen.xend.server.DevController import DevController + +import os +import re + +xoptions = XendOptions.instance() + +def destroy_vtpmstate(uuids): + if os.path.exists(VTPM_DELETE_SCRIPT): + for uuid in uuids: + os.system(VTPM_DELETE_SCRIPT + " " + uuid) + +class TPMifController(DevController): + """TPM interface controller. Handles all TPM devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + + devid = self.allocateDeviceID() + inst = int(config.get('pref_instance', -1)) + if inst == -1: + inst = int(config.get('instance', 0)) + + typ = config.get('type') + uuid = config.get('uuid') + + log.info("The domain has a TPM with pref. instance %d and devid %d.", + inst, devid) + back = { 'pref_instance' : "%i" % inst, + 'resume' : "%s" % (self.vm.getResume()) } + if typ: + back['type'] = typ + if uuid: + back['uuid'] = uuid + + data = self.vm.info['devices'].get(uuid) + if data: + other = data[1].get('other_config') + if type(other) == dict: + for key, item in other.items(): + back['oc_' + key] = item + + front = { 'handle' : "%i" % devid } + + return (devid, back, front) + + def getDeviceConfiguration(self, devid, transaction = None): + """Returns the configuration of a device""" + result = DevController.getDeviceConfiguration(self, devid, transaction) + + (instance, uuid, type) = \ + self.readBackend(devid, 'instance', + 'uuid', + 'type') + + if instance: + result['instance'] = instance + if uuid: + result['uuid'] = uuid + if type: + result['type'] = type + + if uuid: + data = self.vm.info['devices'].get(uuid) + if data: + other = data[1].get('other_config') + if other: + result['other_config'] = other + + return result + + def migrate(self, deviceConfig, network, dst, step, domName): + """@see DevContoller.migrate""" + if network: + tool = xoptions.get_external_migration_tool() + if tool != '': + log.info("Request to network-migrate device to %s. step=%d.", + dst, step) + + if step == DEV_MIGRATE_TEST: + """Assuming for now that everything is ok and migration + with the given tool can proceed. + """ + return 0 + else: + fd = os.popen("%s -type vtpm -step %d -host %s -domname %s" % + (tool, step, dst, domName), + 'r') + for line in fd.readlines(): + mo = re.search('Error', line) + if mo: + raise XendError("vtpm: Fatal error in migration step %d: %s" % + (step, line)) + return 0 + else: + log.debug("External migration tool not in configuration.") + return -1 + return 0 + + def recover_migrate(self, deviceConfig, network, dst, step, domName): + """@see DevContoller.recover_migrate""" + if network: + tool = xoptions.get_external_migration_tool() + if tool != '': + log.info("Request to recover network-migrated device. last good step=%d.", + step) + fd = os.popen("%s -type vtpm -step %d -host %s -domname %s -recover" % + (tool, step, dst, domName), + 'r') + return 0 diff --git a/tools/python/xen/xend/server/vfbif.py b/tools/python/xen/xend/server/vfbif.py new file mode 100644 index 0000000..6f049d3 --- /dev/null +++ b/tools/python/xen/xend/server/vfbif.py @@ -0,0 +1,91 @@ +from xen.xend.server.DevController import DevController +from xen.xend.XendLogging import log + +from xen.xend.XendError import VmError +import xen.xend +import os + +CONFIG_ENTRIES = ['type', 'vncdisplay', 'vnclisten', 'vncpasswd', 'vncunused', + 'videoram', 'display', 'xauthority', 'keymap', + 'uuid', 'location', 'protocol', 'opengl'] + +class VfbifController(DevController): + """Virtual frame buffer controller. Handles all vfb devices for a domain. + Note that we only support a single vfb per domain at the moment. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + + back = dict([(k, str(config[k])) for k in CONFIG_ENTRIES + if config.has_key(k)]) + + devid = 0 + return (devid, back, {}) + + + def getDeviceConfiguration(self, devid, transaction = None): + result = DevController.getDeviceConfiguration(self, devid, transaction) + + if transaction is None: + devinfo = self.readBackend(devid, *CONFIG_ENTRIES) + else: + devinfo = self.readBackendTxn(transaction, devid, *CONFIG_ENTRIES) + return dict([(CONFIG_ENTRIES[i], devinfo[i]) + for i in range(len(CONFIG_ENTRIES)) + if devinfo[i] is not None]) + + def waitForDevice(self, devid): + # is a qemu-dm managed device, don't wait for hotplug for these. + return + + def reconfigureDevice(self, _, config): + """ Only allow appending location information of vnc port into + xenstore.""" + + if 'location' in config: + (devid, back, front) = self.getDeviceDetails(config) + self.writeBackend(devid, 'location', config['location']) + return back.get('uuid') + + raise VmError('Refusing to reconfigure device vfb:%d' % devid) + + def destroyDevice(self, devid, force): + # remove the backend xenstore entries no matter what + # because we kill qemu-dm with extreme prejudice + # not giving it a chance to remove them itself + DevController.destroyDevice(self, devid, True) + + + def migrate(self, deviceConfig, network, dst, step, domName): + # Handled by qemu-dm so no action needed + return 0 + + +class VkbdifController(DevController): + """Virtual keyboard controller. Handles all vkbd devices for a domain. + """ + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) + + def waitForDevice(self, config): + # is a qemu-dm managed device, don't wait for hotplug for these. + return + + def destroyDevice(self, devid, force): + # remove the backend xenstore entries no matter what + # because we kill qemu-dm with extreme prejudice + # not giving it a chance to remove them itself + DevController.destroyDevice(self, devid, True) + + def migrate(self, deviceConfig, network, dst, step, domName): + # Handled by qemu-dm so no action needed + return 0 diff --git a/tools/python/xen/xend/server/vscsiif.py b/tools/python/xen/xend/server/vscsiif.py new file mode 100644 index 0000000..be2ea4c --- /dev/null +++ b/tools/python/xen/xend/server/vscsiif.py @@ -0,0 +1,232 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2007 FUJITSU Limited +# Based on the blkif.py +#============================================================================ + + +"""Support for VSCSI Devices. +""" +import re +import string + +import types + +from xen.xend import sxp +from xen.xend.XendError import VmError +from xen.xend.XendLogging import log + +from xen.xend.server.DevController import DevController, xenbusState +from xen.xend.xenstore.xstransact import xstransact + +class VSCSIController(DevController): + """VSCSI Devices. + """ + def __init__(self, vm): + """Create a VSCSI Devices. + """ + DevController.__init__(self, vm) + + + def sxprs(self): + """@see DevController.sxprs""" + devslist = [] + for devid in self.deviceIDs(): + vscsi_devs = self.readBackendList(devid, "vscsi-devs") + vscsipath = "vscsi-devs/" + devs = [] + vscsi_config = [] + for dev in vscsi_devs: + devpath = vscsipath + dev + backstate = self.readBackend(devid, devpath + '/state') + pdev = self.readBackend(devid, devpath + '/p-dev') + pdevname = self.readBackend(devid, devpath + '/p-devname') + vdev = self.readBackend(devid, devpath + '/v-dev') + localdevid = self.readBackend(devid, devpath + '/devid') + frontstate = self.readFrontend(devid, devpath + '/state') + devs.append(['dev', \ + ['state', backstate], \ + ['devid', localdevid], \ + ['p-dev', pdev], \ + ['p-devname', pdevname], \ + ['v-dev', vdev], \ + ['frontstate', frontstate] ]) + + vscsi_config.append(['devs', devs]) + state = self.readFrontend(devid, 'state') + vscsi_config.append(['state', state]) + backid = self.readFrontend(devid, 'backend-id') + vscsi_config.append(['backend-id', backid]) + backpath = self.readFrontend(devid, 'backend') + vscsi_config.append(['backend', backpath]) + + devslist.append([devid, vscsi_config]) + + return devslist + + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + back = {} + vscsipath = "vscsi-devs/" + for vscsi_config in config.get('devs', []): + localdevid = self.allocateDeviceID() + # vscsi-devs/dev-0 + devpath = vscsipath + 'dev-%i' % localdevid + back[devpath] = "" + pdev = vscsi_config.get('p-dev', '') + back[devpath + '/p-dev'] = pdev + pdevname = vscsi_config.get('p-devname', '') + back[devpath + '/p-devname'] = pdevname + vdev = vscsi_config.get('v-dev', '') + back[devpath + '/v-dev'] = vdev + state = vscsi_config.get('state', '') + back[devpath + '/state'] = str(xenbusState[state]) + devid = vscsi_config.get('devid', '') + back[devpath + '/devid'] = str(devid) + + back['uuid'] = config.get('uuid','') + devid = int(devid) + return (devid, back, {}) + + + def readBackendList(self, devid, *args): + frontpath = self.frontendPath(devid) + backpath = xstransact.Read(frontpath + "/backend") + if backpath: + paths = map(lambda x: backpath + "/" + x, args) + return xstransact.List(*paths) + + + def getDeviceConfiguration(self, devid, transaction = None): + config = DevController.getDeviceConfiguration(self, devid, transaction) + + vscsi_devs = [] + + devs = self.readBackendList(devid, "vscsi-devs") + vscsipath = "vscsi-devs/" + for dev in devs: + devpath = vscsipath + dev + pdev = self.readBackend(devid, devpath + '/p-dev') + pdevname = self.readBackend(devid, devpath + '/p-devname') + vdev = self.readBackend(devid, devpath + '/v-dev') + state = self.readBackend(devid, devpath + '/state') + localdevid = self.readBackend(devid, devpath + '/devid') + dev_dict = {'p-dev': pdev, + 'p-devname': pdevname, + 'v-dev': vdev, + 'state': state, + 'devid': localdevid } + vscsi_devs.append(dev_dict) + + config['devs'] = vscsi_devs + config['uuid'] = self.readBackend(devid, 'uuid') + return config + + + def configuration(self, devid, transaction = None): + """Returns SXPR for devices on domain. + @note: we treat this dict especially to convert to + SXP because it is not a straight dict of strings.""" + + configDict = self.getDeviceConfiguration(devid, transaction) + sxpr = [self.deviceClass] + + # remove devs + devs = configDict.pop('devs', []) + + for dev in devs: + dev_sxpr = ['dev'] + for dev_item in dev.items(): + dev_sxpr.append(list(dev_item)) + sxpr.append(dev_sxpr) + + for key, val in configDict.items(): + if type(val) == type(list()): + for v in val: + sxpr.append([key, v]) + else: + sxpr.append([key, val]) + + return sxpr + + + def reconfigureDevice(self, _, config): + """@see DevController.reconfigureDevice""" + (devid, back, front) = self.getDeviceDetails(config) + devid = int(devid) + vscsi_config = config['devs'][0] + state = vscsi_config.get('state', '') + driver_state = self.readBackend(devid, 'state') + if str(xenbusState['Connected']) != driver_state: + raise VmError("Driver status is not connected") + + uuid = self.readBackend(devid, 'uuid') + if state == 'Initialising': + back['uuid'] = uuid + self.writeBackend(devid, back) + + elif state == 'Closing': + found = False + devs = self.readBackendList(devid, "vscsi-devs") + vscsipath = "vscsi-devs/" + vdev = vscsi_config.get('v-dev', '') + + for dev in devs: + devpath = vscsipath + dev + old_vdev = self.readBackend(devid, devpath + '/v-dev') + if vdev == old_vdev: + found = True + self.writeBackend(devid, devpath + '/state', \ + str(xenbusState['Closing'])) + break + + if not found: + raise VmError("Device %s not connected" % vdev) + + else: + raise XendError("Error configuring device invalid " + "state '%s'" % state) + + self.writeBackend(devid, 'state', str(xenbusState['Reconfiguring'])) + return self.readBackend(devid, 'uuid') + + + def cleanupDevice(self, devid): + devs = self.readBackendList(devid, "vscsi-devs") + vscsipath = "vscsi-devs/" + new_num_devs = 0 + + for dev in devs: + new_num_devs = new_num_devs + 1 + devpath = vscsipath + dev + devstate = self.readBackend(devid, devpath + '/state') + + if str(xenbusState['Closed']) == devstate: + self.removeBackend(devid, devpath) + frontpath = self.frontendPath(devid) + xstransact.Remove(frontpath + '/' + devpath) + new_num_devs = new_num_devs - 1 + + frontpath = self.frontendPath(devid) + front_devstate = xstransact.Read(frontpath + '/' + devpath) + if front_devstate is not None: + if str(xenbusState['Closed']) == front_devstate: + self.removeBackend(devid, devpath) + xstransact.Remove(frontpath + '/' + devpath) + new_num_devs = new_num_devs - 1 + + return new_num_devs + diff --git a/tools/python/xen/xend/sxp.py b/tools/python/xen/xend/sxp.py new file mode 100644 index 0000000..a9e9adf --- /dev/null +++ b/tools/python/xen/xend/sxp.py @@ -0,0 +1,763 @@ +#!/usr/bin/env python +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +#============================================================================ + +""" +Input-driven parsing for s-expression (sxp) format. +Create a parser: pin = Parser(); +Then call pin.input(buf) with your input. +Call pin.input_eof() when done. +Use pin.read() to see if a value has been parsed, pin.get_val() +to get a parsed value. You can call ready and get_val at any time - +you don't have to wait until after calling input_eof. + +""" +from __future__ import generators + +import sys +import types +import errno +import string +from StringIO import StringIO + +__all__ = [ + "mime_type", + "ParseError", + "Parser", + "atomp", + "show", + "show_xml", + "elementp", + "name", + "attributes", + "attribute", + "children", + "child", + "child_at", + "child0", + "child1", + "child2", + "child3", + "child4", + "child_value", + "has_id", + "with_id", + "child_with_id", + "elements", + "merge", + "to_string", + "from_string", + "all_from_string", + "parse", + ] + +mime_type = "application/sxp" + +escapes = { + 'a': '\a', + 'b': '\b', + 't': '\t', + 'n': '\n', + 'v': '\v', + 'f': '\f', + 'r': '\r', + '\\': '\\', + '\'': '\'', + '\"': '\"'} + +k_list_open = "(" +k_list_close = ")" +k_attr_open = "@" +k_eval = "!" + +escapes_rev = {} +for k in escapes: + escapes_rev[escapes[k]] = k + +class ParseError(StandardError): + + def __init__(self, parser, value): + self.parser = parser + self.value = value + + def __str__(self): + return self.value + +class ParserState: + + def __init__(self, fn, parent=None): + self.parent = parent + self.buf = '' + self.val = [] + self.delim = None + self.fn = fn + + def push(self, fn): + return ParserState(fn, parent=self) + +class Parser: + + def __init__(self): + self.error = sys.stderr + self.reset() + + def reset(self): + self.val = [] + self.eof = 0 + self.err = 0 + self.line_no = 0 + self.char_no = 0 + self.state = None + + def push_state(self, fn): + self.state = self.state.push(fn) + + def pop_state(self): + val = self.state + self.state = self.state.parent + if self.state and self.state.fn == self.state_start: + # Return to start state - produce the value. + self.val += self.state.val + self.state.val = [] + return val + + def in_class(self, c, s): + return s.find(c) >= 0 + + def in_space_class(self, c): + return self.in_class(c, ' \t\n\v\f\r') + + def is_separator(self, c): + return self.in_class(c, '{}()<>[]!;') + + def in_comment_class(self, c): + return self.in_class(c, '#') + + def in_string_quote_class(self, c): + return self.in_class(c, '"\'') + + def in_printable_class(self, c): + return self.in_class(c, string.printable) + + def set_error_stream(self, error): + self.error = error + + def has_error(self): + return self.err > 0 + + def at_eof(self): + return self.eof + + def input_eof(self): + self.eof = 1 + self.input_char(-1) + + def input(self, buf): + if not buf or len(buf) == 0: + self.input_eof() + else: + for c in buf: + self.input_char(c) + + def input_char(self, c): + if self.at_eof(): + pass + elif c == '\n': + self.line_no += 1 + self.char_no = 0 + else: + self.char_no += 1 + + if self.state is None: + self.begin_start(None) + self.state.fn(c) + + def ready(self): + return len(self.val) > 0 + + def get_val(self): + v = self.val[0] + self.val = self.val[1:] + return v + + def get_all(self): + return self.val + + def begin_start(self, c): + self.state = ParserState(self.state_start) + + def end_start(self): + self.val += self.state.val + self.pop_state() + + def state_start(self, c): + if self.at_eof(): + self.end_start() + elif self.in_space_class(c): + pass + elif self.in_comment_class(c): + self.begin_comment(c) + elif c == k_list_open: + self.begin_list(c) + elif c == k_list_close: + raise ParseError(self, "syntax error: "+c) + elif self.in_string_quote_class(c): + self.begin_string(c) + elif self.in_printable_class(c): + self.begin_atom(c) + elif c == chr(4): + # ctrl-D, EOT: end-of-text. + self.input_eof() + else: + raise ParseError(self, "invalid character: code %d" % ord(c)) + + def begin_comment(self, c): + self.push_state(self.state_comment) + self.state.buf += c + + def end_comment(self): + self.pop_state() + + def state_comment(self, c): + if c == '\n' or self.at_eof(): + self.end_comment() + else: + self.state.buf += c + + def begin_string(self, c): + self.push_state(self.state_string) + self.state.delim = c + + def end_string(self): + val = self.state.buf + self.state.parent.val.append(val) + self.pop_state() + + def state_string(self, c): + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif c == self.state.delim: + self.end_string() + elif c == '\\': + self.push_state(self.state_escape) + else: + self.state.buf += c + + def state_escape(self, c): + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + d = escapes.get(c) + if d: + self.state.parent.buf += d + self.pop_state() + elif c == 'x': + self.state.fn = self.state_hex + self.state.val = 0 + elif c in string.octdigits: + self.state.fn = self.state_octal + self.state.val = 0 + self.input_char(c) + else: + # ignore escape if it doesn't match anything we know + self.state.parent.buf += '\\' + self.pop_state() + + def state_octal(self, c): + def octaldigit(c): + self.state.val *= 8 + self.state.val += ord(c) - ord('0') + self.state.buf += c + if self.state.val < 0 or self.state.val > 0xff: + raise ParseError(self, "invalid octal escape: out of range " + self.state.buf) + if len(self.state.buf) == 3: + octaldone() + + def octaldone(): + d = chr(self.state.val) + self.state.parent.buf += d + self.pop_state() + + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif '0' <= c <= '7': + octaldigit(c) + elif len(self.state.buf): + octaldone() + self.input_char(c) + + def state_hex(self, c): + def hexdone(): + d = chr(self.state.val) + self.state.parent.buf += d + self.pop_state() + + def hexdigit(c, d): + self.state.val *= 16 + self.state.val += ord(c) - ord(d) + self.state.buf += c + if self.state.val < 0 or self.state.val > 0xff: + raise ParseError(self, "invalid hex escape: out of range " + self.state.buf) + if len(self.state.buf) == 2: + hexdone() + + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif '0' <= c <= '9': + hexdigit(c, '0') + elif 'A' <= c <= 'F': + hexdigit(c, 'A') + elif 'a' <= c <= 'f': + hexdigit(c, 'a') + elif len(buf): + hexdone() + self.input_char(c) + + def begin_atom(self, c): + self.push_state(self.state_atom) + self.state.buf = c + + def end_atom(self): + val = self.state.buf + self.state.parent.val.append(val) + self.pop_state() + + def state_atom(self, c): + if self.at_eof(): + self.end_atom() + elif (self.is_separator(c) or + self.in_space_class(c) or + self.in_comment_class(c)): + self.end_atom() + self.input_char(c) + else: + self.state.buf += c + + def begin_list(self, c): + self.push_state(self.state_list) + + def end_list(self): + val = self.state.val + self.state.parent.val.append(val) + self.pop_state() + + def state_list(self, c): + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif c == k_list_close: + self.end_list() + else: + self.state_start(c) + +def atomp(sxpr): + """Check if an sxpr is an atom. + """ + if sxpr.isalnum() or sxpr == '@': + return 1 + for c in sxpr: + if c in string.whitespace: return 0 + if c in '"\'\\(){}[]<>$#&%^': return 0 + if c in string.ascii_letters: continue + if c in string.digits: continue + if c in '.-_:/~': continue + return 0 + return 1 + +def show(sxpr, out=sys.stdout): + """Print an sxpr in bracketed (lisp-style) syntax. + """ + if isinstance(sxpr, (types.ListType, types.TupleType)): + out.write(k_list_open) + i = 0 + for x in sxpr: + if i: out.write(' ') + show(x, out) + i += 1 + out.write(k_list_close) + elif isinstance(sxpr, (types.IntType, types.FloatType)): + out.write(str(sxpr)) + elif isinstance(sxpr, types.StringType) and atomp(sxpr): + out.write(sxpr) + else: + out.write(repr(str(sxpr))) + +def show_xml(sxpr, out=sys.stdout): + """Print an sxpr in XML syntax. + """ + if isinstance(sxpr, (types.ListType, types.TupleType)): + element = name(sxpr) + out.write('<%s' % element) + for attr in attributes(sxpr): + out.write(' %s=%s' % (attr[0], attr[1])) + out.write('>') + i = 0 + for x in children(sxpr): + if i: out.write(' ') + show_xml(x, out) + i += 1 + out.write('' % element) + elif isinstance(sxpr, types.StringType) and atomp(sxpr): + out.write(sxpr) + else: + out.write(str(sxpr)) + +def elementp(sxpr, elt=None): + """Check if an sxpr is an element of the given type. + + sxpr sxpr + elt element type + """ + return (isinstance(sxpr, (types.ListType, types.TupleType)) + and len(sxpr) + and (None == elt or sxpr[0] == elt)) + +def name(sxpr): + """Get the element name of an sxpr. + If the sxpr is not an element (i.e. it's an atomic value) its name + is None. + + sxpr + + returns name (None if not an element). + """ + val = None + if isinstance(sxpr, types.StringType): + val = sxpr + elif isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr): + val = sxpr[0] + return val + +def attributes(sxpr): + """Get the attribute list of an sxpr. + + sxpr + + returns attribute list + """ + val = [] + if isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr) > 1: + attr = sxpr[1] + if elementp(attr, k_attr_open): + val = attr[1:] + return val + +def attribute(sxpr, key, val=None): + """Get an attribute of an sxpr. + + sxpr sxpr + key attribute key + val default value (default None) + + returns attribute value + """ + for x in attributes(sxpr): + if x[0] == key: + val = x[1] + break + return val + +def children(sxpr, elt=None): + """Get children of an sxpr. + + sxpr sxpr + elt optional element type to filter by + + returns children (filtered by elt if specified) + """ + val = [] + if isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr) > 1: + i = 1 + x = sxpr[i] + if elementp(x, k_attr_open): + i += 1 + val = sxpr[i : ] + if elt: + def iselt(x): + return elementp(x, elt) + val = filter(iselt, val) + return val + +def child(sxpr, elt, val=None): + """Get the first child of the given element type. + + sxpr sxpr + elt element type + val default value + """ + for x in children(sxpr): + if elementp(x, elt): + val = x + break + return val + +def child_at(sxpr, index, val=None): + """Get the child at the given index (zero-based). + + sxpr sxpr + index index + val default value + """ + kids = children(sxpr) + if len(kids) > index: + val = kids[index] + return val + +def child0(sxpr, val=None): + """Get the zeroth child. + """ + return child_at(sxpr, 0, val) + +def child1(sxpr, val=None): + """Get the first child. + """ + return child_at(sxpr, 1, val) + +def child2(sxpr, val=None): + """Get the second child. + """ + return child_at(sxpr, 2, val) + +def child3(sxpr, val=None): + """Get the third child. + """ + return child_at(sxpr, 3, val) + +def child4(sxpr, val=None): + """Get the fourth child. + """ + return child_at(sxpr, 4, val) + +def child_value(sxpr, elt, val=None): + """Get the value of the first child of the given element type. + Assumes the child has an atomic value. + + sxpr sxpr + elt element type + val default value + """ + kid = child(sxpr, elt) + if kid: + val = child_at(kid, 0, val) + return val + +def has_id(sxpr, id): + """Test if an s-expression has a given id. + """ + return attribute(sxpr, 'id') == id + +def with_id(sxpr, id, val=None): + """Find the first s-expression with a given id, at any depth. + + sxpr s-exp or list + id id + val value if not found (default None) + + return s-exp or val + """ + if isinstance(sxpr, (types.ListType, types.TupleType)): + for n in sxpr: + if has_id(n, id): + val = n + break + v = with_id(n, id) + if v is None: continue + val = v + break + return val + +def child_with_id(sxpr, id, val=None): + """Find the first child with a given id. + + sxpr s-exp or list + id id + val value if not found (default None) + + return s-exp or val + """ + if isinstance(sxpr, (types.ListType, types.TupleType)): + for n in sxpr: + if has_id(n, id): + val = n + break + return val + +def elements(sxpr, ctxt=None): + """Generate elements (at any depth). + Visit elements in pre-order. + Values generated are (node, context) + The context is None if there is no parent, otherwise + (index, parent, context) where index is the node's index w.r.t its parent, + and context is the parent's context. + + sxpr s-exp + + returns generator + """ + yield (sxpr, ctxt) + i = 0 + for n in children(sxpr): + if isinstance(n, (types.ListType, types.TupleType)): + # Calling elements() recursively does not generate recursively, + # it just returns a generator object. So we must iterate over it. + for v in elements(n, (i, sxpr, ctxt)): + yield v + i += 1 + +def merge(s1, s2): + """Merge sxprs s1 and s2. + Returns an sxpr containing all the fields from s1 and s2, with + entries in s1 overriding s2. Recursively merges fields. + + @param s1 sxpr + @param s2 sxpr + @return merged sxpr + """ + if s1 is None: + val = s2 + elif s2 is None: + val = s1 + elif elementp(s1): + name1 = name(s1) + (m1, v1) = child_map(s1) + (m2, v2) = child_map(s2) + val = [name1] + for (k1, f1) in m1.items(): + merge_list(val, f1, m2.get(k1, [])) + for (k2, f2) in m2.items(): + if k2 in m1: continue + val.extend(f2) + val.extend(v1) + else: + val = s1 + return val + +def merge_list(sxpr, l1, l2): + """Merge element lists l1 and l2 into sxpr. + The lists l1 and l2 are all element with the same name. + Values from l1 are merged with values in l2 and stored in sxpr. + If one list is longer than the other the excess values are used + as they are. + + @param sxpr to merge into + @param l1 sxpr list + @param l2 sxpr list + @return modified sxpr + """ + n1 = len(l1) + n2 = len(l2) + nmin = min(n1, n2) + for i in range(0, nmin): + sxpr.append(merge(l1[i], l2[i])) + for i in range(nmin, n1): + sxpr.append(l1[i]) + for i in range(nmin, n2): + sxpr.append(l2[i]) + return sxpr + +def child_map(sxpr): + """Get a dict of the elements in sxpr and a list of its values. + The dict maps element name to the list of elements with that name, + and the list is the non-element children. + + @param sxpr + @return (dict, list) + """ + m = {} + v = [] + for x in children(sxpr): + if elementp(x): + n = name(x) + l = m.get(n, []) + l.append(x) + m[n] = l + else: + v.append(x) + return (m, v) + +def to_string(sxpr): + """Convert an sxpr to a string. + + sxpr sxpr + returns string + """ + io = StringIO() + show(sxpr, io) + io.seek(0) + val = io.getvalue() + io.close() + return val + +def from_string(s): + """Create an sxpr by parsing a string. + + s string + returns sxpr + """ + if s == '': + return [] + + io = StringIO(s) + vals = parse(io) + if vals is []: + return None + else: + return vals[0] + + +def all_from_string(s): + """Create an sxpr list by parsing a string. + + s string + returns sxpr list + """ + io = StringIO(s) + vals = parse(io) + return vals + +def parse(io): + """Completely parse all input from 'io'. + + io input file object + returns list of values, None if incomplete + raises ParseError on parse error + """ + pin = Parser() + while 1: + buf = io.readline() + pin.input(buf) + if len(buf) == 0: + break + if pin.ready(): + val = pin.get_all() + else: + val = None + return val + + +if __name__ == '__main__': + print ">main" + pin = Parser() + while 1: + buf = sys.stdin.read(1024) + #buf = sys.stdin.readline() + pin.input(buf) + while pin.ready(): + val = pin.get_val() + print + print '****** val=', val + if len(buf) == 0: + break + diff --git a/tools/python/xen/xend/tests/__init__.py b/tools/python/xen/xend/tests/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tools/python/xen/xend/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/xend/tests/test_XendConfig.py b/tools/python/xen/xend/tests/test_XendConfig.py new file mode 100644 index 0000000..472a49e --- /dev/null +++ b/tools/python/xen/xend/tests/test_XendConfig.py @@ -0,0 +1,42 @@ +import unittest + +import xen.xend.XendConfig as XendConfig + + +class test_XendConfig(unittest.TestCase): + + def testParseFromSXP(self): + cfg = XendConfig.XendConfig( + sxp_obj = ( + ['vm', + ['bootloader_args', '-q --default_args="root=/dev/sda1 ro" --extra_args="quiet" /images/VM1.sda'], + ['bootloader', '/usr/bin/pygrub'], + ['device', ['vif', ['mac', '00:16:3E:4C:D1:00'], ['script', 'vif-bridge'], ['bridge', 'xenbr0']]], + ['device', ['vif', ['mac', '00:16:3E:48:56:26'], ['script', 'vif-bridge'], ['bridge', 'vbridge0']]], + ['device', ['vbd', ['uname', 'phy:/images/VM1.sda'], ['dev', 'sda'], ['mode', 'w']]], + ['device', ['vbd', ['uname', 'phy:/images/VM1.sdb'], ['dev', 'sdb'], ['mode', 'w']]], + ['memory', '256'], ['name', 'VM1'], ['on_crash', 'restart'], + ['uuid', '10927a76-fe27-49b2-8f57-2970b7bbed6c'], ['vcpus', '1'] + ])) + + self.assertEqual(cfg['uuid'], '10927a76-fe27-49b2-8f57-2970b7bbed6c') + self.assertEqual(cfg['name_label'], 'VM1') + self.assertEqual(cfg['memory_static_max'], 256) + + ordered_refs = cfg.ordered_device_refs() + self.assertEqual(cfg['devices'][ordered_refs[0]][0], 'vbd') + self.assertEqual(cfg['devices'][ordered_refs[1]][0], 'vbd') + self.assertEqual(cfg['devices'][ordered_refs[2]][0], 'vif') + self.assertEqual(cfg['devices'][ordered_refs[3]][0], 'vif') + self.assertEqual(cfg['devices'][ordered_refs[0]][1]['uname'], + 'phy:/images/VM1.sda') + self.assertEqual(cfg['devices'][ordered_refs[1]][1]['uname'], + 'phy:/images/VM1.sdb') + self.assertEqual(cfg['devices'][ordered_refs[2]][1]['mac'], + '00:16:3E:4C:D1:00') + self.assertEqual(cfg['devices'][ordered_refs[3]][1]['mac'], + '00:16:3E:48:56:26') + + +def test_suite(): + return unittest.makeSuite(test_XendConfig) diff --git a/tools/python/xen/xend/tests/test_sxp.py b/tools/python/xen/xend/tests/test_sxp.py new file mode 100644 index 0000000..dab2719 --- /dev/null +++ b/tools/python/xen/xend/tests/test_sxp.py @@ -0,0 +1,39 @@ +import unittest + +import xen.xend.sxp + + +class test_sxp(unittest.TestCase): + + def testAllFromString(self): + def t(inp, expected): + self.assertEqual(xen.xend.sxp.all_from_string(inp), expected) + + t('String', ['String']) + t('(String Thing)', [['String', 'Thing']]) + t('(String) (Thing)', [['String'], ['Thing']]) + + + def testParseFixed(self): + fin = file('../xen/xend/tests/xend-config.sxp', 'rb') + try: + config = xen.xend.sxp.parse(fin) + self.assertEqual( + xen.xend.sxp.child_value( + config, + 'xend-relocation-hosts-allow'), + '^localhost$ ^localhost\\.localdomain$') + finally: + fin.close() + + + def testParseConfigExample(self): + fin = file('../../examples/xend-config.sxp', 'rb') + try: + config = xen.xend.sxp.parse(fin) + finally: + fin.close() + + +def test_suite(): + return unittest.makeSuite(test_sxp) diff --git a/tools/python/xen/xend/tests/test_uuid.py b/tools/python/xen/xend/tests/test_uuid.py new file mode 100644 index 0000000..d96c904 --- /dev/null +++ b/tools/python/xen/xend/tests/test_uuid.py @@ -0,0 +1,30 @@ +import unittest + +from xen.xend import uuid + + +class test_uuid(unittest.TestCase): + + def testStringRoundtrip(self): + def t(inp): + self.assertEqual(uuid.fromString(uuid.toString(inp)), inp) + + t(uuid.create()) + t(uuid.create()) + t(uuid.create()) + t(uuid.create()) + t(uuid.create()) + + + def testToFromString(self): + def t(inp, expected): + self.assertEqual(uuid.toString(inp), expected) + self.assertEqual(uuid.fromString(expected), inp) + + t([0 for _ in range(0, 16)], "00000000-0000-0000-0000-000000000000") + t([185, 158, 125, 206, 250, 178, 125, 57, 2, 6, 162, 74, 178, 236, + 196, 5], "b99e7dce-fab2-7d39-0206-a24ab2ecc405") + + +def test_suite(): + return unittest.makeSuite(test_uuid) diff --git a/tools/python/xen/xend/tests/xend-config.sxp b/tools/python/xen/xend/tests/xend-config.sxp new file mode 100644 index 0000000..0793028 --- /dev/null +++ b/tools/python/xen/xend/tests/xend-config.sxp @@ -0,0 +1,131 @@ +# -*- sh -*- + +# +# Xend configuration file. +# + +# This example configuration is appropriate for an installation that +# utilizes a bridged network configuration. Access to xend via http +# is disabled. + +# Commented out entries show the default for that entry, unless otherwise +# specified. + +#(logfile /var/log/xend.log) +#(loglevel DEBUG) + +#(xend-http-server no) +#(xend-unix-server no) +#(xend-tcp-xmlrpc-server no) +#(xend-unix-xmlrpc-server yes) +#(xend-relocation-server no) +(xend-relocation-server yes) + +#(xend-unix-path /var/lib/xend/xend-socket) + +# Port xend should use for the HTTP interface, if xend-http-server is set. +#(xend-port 8000) + +# Port xend should use for the relocation interface, if xend-relocation-server +# is set. +#(xend-relocation-port 8002) + +# Address xend should listen on for HTTP connections, if xend-http-server is +# set. +# Specifying 'localhost' prevents remote connections. +# Specifying the empty string '' (the default) allows all connections. +#(xend-address '') +#(xend-address localhost) + +# Address xend should listen on for relocation-socket connections, if +# xend-relocation-server is set. +# Meaning and default as for xend-address above. +#(xend-relocation-address '') + +# The hosts allowed to talk to the relocation port. If this is empty (the +# default), then all connections are allowed (assuming that the connection +# arrives on a port and interface on which we are listening; see +# xend-relocation-port and xend-relocation-address above). Otherwise, this +# should be a space-separated sequence of regular expressions. Any host with +# a fully-qualified domain name or an IP address that matches one of these +# regular expressions will be accepted. +# +# For example: +# (xend-relocation-hosts-allow '^localhost$ ^.*\\.example\\.org$') +# +#(xend-relocation-hosts-allow '') +(xend-relocation-hosts-allow '^localhost$ ^localhost\\.localdomain$') + +# The limit (in kilobytes) on the size of the console buffer +#(console-limit 1024) + +## +# To bridge network traffic, like this: +# +# +# dom0: ----------------- bridge -> real eth0 -> the network +# | +# domU: fake eth0 -> vifN.0 -+ +# +# use +# +# (network-script network-bridge) +# +# Your default ethernet device is used as the outgoing interface, by default. +# To use a different one (e.g. eth1) use +# +# (network-script 'network-bridge netdev=eth1') +# +# The bridge is named xenbr0, by default. To rename the bridge, use +# +# (network-script 'network-bridge bridge=') +# +# It is possible to use the network-bridge script in more complicated +# scenarios, such as having two outgoing interfaces, with two bridges, and +# two fake interfaces per guest domain. To do things like this, write +# yourself a wrapper script, and call network-bridge from it, as appropriate. +# +(network-script network-bridge) + +# The script used to control virtual interfaces. This can be overridden on a +# per-vif basis when creating a domain or a configuring a new vif. The +# vif-bridge script is designed for use with the network-bridge script, or +# similar configurations. +# +# If you have overridden the bridge name using +# (network-script 'network-bridge bridge=') then you may wish to do the +# same here. The bridge name can also be set when creating a domain or +# configuring a new vif, but a value specified here would act as a default. +# +# If you are using only one bridge, the vif-bridge script will discover that, +# so there is no need to specify it explicitly. +# +(vif-script vif-bridge) + + +## Use the following if network traffic is routed, as an alternative to the +# settings for bridged networking given above. +#(network-script network-route) +#(vif-script vif-route) + + +## Use the following if network traffic is routed with NAT, as an alternative +# to the settings for bridged networking given above. +#(network-script network-nat) +#(vif-script vif-nat) + + +# Dom0 will balloon out when needed to free memory for domU. +# dom0-min-mem is the lowest memory level (in MB) dom0 will get down to. +# If dom0-min-mem=0, dom0 will never balloon out. +(dom0-min-mem 196) + +# In SMP system, dom0 will use dom0-cpus # of CPUS +# If dom0-cpus = 0, dom0 will take all cpus available +(dom0-cpus 0) + +# Whether to enable core-dumps when domains crash. +#(enable-dump no) + +# The tool used for initiating virtual TPM migration +#(external-migration-tool '') diff --git a/tools/python/xen/xend/uuid.py b/tools/python/xen/xend/uuid.py new file mode 100644 index 0000000..7fe52cb --- /dev/null +++ b/tools/python/xen/xend/uuid.py @@ -0,0 +1,69 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +"""Universal Unique Identifiers (UUIDs). By default, UUIDs generated here are +purely random, with no internal structure. However, they are the same size, +and are formatted by the same conventions, as the UUIDs in the Open Software +Foundation's Distributed Computing Environment (OSF DCE). This allows Xend to +be used with UUIDs generated as per the DCE specification, should that be +required. These UUIDs are also, by no coincidence, the same size as the +'handle' stored by the Xen hypervisor along with the domain structure.""" + + +import commands +import random + + +def getUuidUuidgen(randomly = True): + """Generate a UUID using the command uuidgen. + + If randomly is true (default) generates a random uuid. + If randomly is false generates a time-based uuid. + """ + cmd = "uuidgen" + if randomly: + cmd += " -r" + else: + cmd += " -t" + return fromString(commands.getoutput(cmd)) + + +def getUuidRandom(): + """Generate a random UUID.""" + + return [ random.randint(0, 255) for _ in range(0, 16) ] + + +#uuidFactory = getUuidUuidgen +uuidFactory = getUuidRandom + + +def toString(u): + return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, + "%02x" * 6]) % tuple(u) + +def fromString(s): + s = s.replace('-', '') + return [ int(s[i : i + 2], 16) for i in range(0, 32, 2) ] + +def create(): + return uuidFactory() + +def createString(): + return toString(create()) diff --git a/tools/python/xen/xend/xenstore/__init__.py b/tools/python/xen/xend/xenstore/__init__.py new file mode 100644 index 0000000..fb12065 --- /dev/null +++ b/tools/python/xen/xend/xenstore/__init__.py @@ -0,0 +1,16 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2005 Mike Wray +#============================================================================ diff --git a/tools/python/xen/xend/xenstore/tests/__init__.py b/tools/python/xen/xend/xenstore/tests/__init__.py new file mode 100644 index 0000000..139597f --- /dev/null +++ b/tools/python/xen/xend/xenstore/tests/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/tools/python/xen/xend/xenstore/tests/stress_xs.py b/tools/python/xen/xend/xenstore/tests/stress_xs.py new file mode 100644 index 0000000..647eb8b --- /dev/null +++ b/tools/python/xen/xend/xenstore/tests/stress_xs.py @@ -0,0 +1,121 @@ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Copyright (c) 2005 XenSource Ltd + + +import random +import sys +import threading +import time + +import xen.lowlevel.xs + +from xen.xend.xenstore import xsutil +from xen.xend.xenstore.xstransact import xstransact +from xen.xend.xenstore.xswatch import xswatch + + +PATH = '/tool/stress_xs' + + +def stress(): + xstransact.Remove(PATH) + xstransact.Mkdir(PATH) + + xswatch(PATH, watch_callback) + + def do(f): + t = threading.Thread(target=stress_write) + t.setDaemon(True) + t.start() + + do(stress_write) + do(stress_get_domain_path) + do(stress_get_domain_path_xsutil) + do(stress_open_close) + + while True: + # Wait for Ctrl-C. + time.sleep(100000000) + + +def stress_write(): + xstransact.Write(PATH, 'key', '1') + while True: + val = xstransact.Gather(PATH, ('key', int)) + xstransact.Store(PATH, ('key', val + 1)) + + random_sleep() + + +def stress_get_domain_path(): + xs_handle = xen.lowlevel.xs.xs() + + domid = 0 + while True: + xs_handle.get_domain_path(domid) + domid += 1 + + random_sleep() + + +def stress_get_domain_path_xsutil(): + domid = 0 + while True: + xsutil.GetDomainPath(domid) + domid += 1 + + random_sleep() + + +def stress_open_close(): + while True: + xs_handle = xen.lowlevel.xs.xs() + + try: + try: + trans = xs_handle.transaction_start() + val = int(xs_handle.read(trans, PATH + '/key')) + xs_handle.write(trans, PATH + '/key', str(val + 1)) + xs_handle.transaction_end(trans, False) + except: + xs_handle.transaction_end(trans, True) + + random_sleep() + finally: + del xs_handle + + +def watch_callback(path): + random_sleep() + return True + + +def random_sleep(): + d = random.randint(-50000, 500) + if d > 0: + time.sleep(d / 1000.0) + + +def main(argv = None): + if argv is None: + argv = sys.argv + + stress() + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/python/xen/xend/xenstore/xstransact.py b/tools/python/xen/xend/xenstore/xstransact.py new file mode 100644 index 0000000..af07298 --- /dev/null +++ b/tools/python/xen/xend/xenstore/xstransact.py @@ -0,0 +1,368 @@ +# Copyright (C) 2005 Christian Limpach +# Copyright (C) 2005 XenSource Ltd + +# 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. + +from xen.xend.xenstore.xsutil import xshandle + +class xstransact: + """WARNING: Be very careful if you're instantiating an xstransact object + yourself (i.e. not using the capitalized static helpers like .Read(). + It is essential that you clean up the object in place via + t.commit/abort(): GC can happen at any time, including contexts where + it's not safe to to use the shared xenstore socket fd. In particular, + if xend forks, and GC occurs, we can have two processes trying to + use the same xenstore fd, and all hell breaks loose. + """ + + + def __init__(self, path = ""): + + self.in_transaction = False # Set this temporarily -- if this + # constructor fails, then we need to + # protect __del__. + + assert path is not None + self.path = path.rstrip("/") + self.transaction = xshandle().transaction_start() + self.in_transaction = True + + def __del__(self): + # see above. + if self.in_transaction: + raise RuntimeError("ERROR: GC of live transaction") + + def commit(self): + if not self.in_transaction: + raise RuntimeError + self.in_transaction = False + rc = xshandle().transaction_end(self.transaction, False) + self.transaction = "0" + return rc + + def abort(self): + if not self.in_transaction: + return True + self.in_transaction = False + rc = xshandle().transaction_end(self.transaction, True) + self.transaction = "0" + return rc + + def _read(self, key): + path = self.prependPath(key) + try: + return xshandle().read(self.transaction, path) + except RuntimeError, ex: + raise RuntimeError(ex.args[0], + '%s, while reading %s' % (ex.args[1], path)) + + def read(self, *args): + """If no arguments are given, return the value at this transaction's + path. If one argument is given, treat that argument as a subpath to + this transaction's path, and return the value at that path. + Otherwise, treat each argument as a subpath to this transaction's + path, and return a list composed of the values at each of those + instead. + """ + if len(args) == 0: + return xshandle().read(self.transaction, self.path) + if len(args) == 1: + return self._read(args[0]) + ret = [] + for key in args: + ret.append(self._read(key)) + return ret + + def _write(self, key, data): + path = self.prependPath(key) + try: + xshandle().write(self.transaction, path, data) + except RuntimeError, ex: + raise RuntimeError(ex.args[0], + ('%s, while writing %s : %s' % + (ex.args[1], path, str(data)))) + + def write(self, *args): + if len(args) == 0: + raise TypeError + if isinstance(args[0], dict): + for d in args: + if not isinstance(d, dict): + raise TypeError + for key in d.keys(): + try: + self._write(key, d[key]) + except TypeError, msg: + raise TypeError('Writing %s: %s: %s' % + (key, str(d[key]), msg)) + elif isinstance(args[0], list): + for l in args: + if not len(l) == 2: + raise TypeError + self._write(l[0], l[1]) + elif len(args) % 2 == 0: + for i in range(len(args) / 2): + self._write(args[i * 2], args[i * 2 + 1]) + else: + raise TypeError + + def _remove(self, key): + path = self.prependPath(key) + return xshandle().rm(self.transaction, path) + + def remove(self, *args): + """If no arguments are given, remove this transaction's path. + Otherwise, treat each argument as a subpath to this transaction's + path, and remove each of those instead. + """ + if len(args) == 0: + xshandle().rm(self.transaction, self.path) + else: + for key in args: + self._remove(key) + + def _list(self, key): + path = self.prependPath(key) + l = xshandle().ls(self.transaction, path) + if l: + return map(lambda x: key + "/" + x, l) + return [] + + def list(self, *args): + """If no arguments are given, list this transaction's path, returning + the entries therein, or the empty list if no entries are found. + Otherwise, treat each argument as a subpath to this transaction's + path, and return the cumulative listing of each of those instead. + """ + if len(args) == 0: + ret = xshandle().ls(self.transaction, self.path) + if ret is None: + return [] + else: + return ret + else: + ret = [] + for key in args: + ret.extend(self._list(key)) + return ret + + + def list_recursive_(self, subdir, keys): + ret = [] + for key in keys: + new_subdir = subdir + "/" + key + l = xshandle().ls(self.transaction, new_subdir) + if l: + ret.append([key, self.list_recursive_(new_subdir, l)]) + else: + ret.append([key, xshandle().read(self.transaction, new_subdir)]) + return ret + + + def list_recursive(self, *args): + """If no arguments are given, list this transaction's path, returning + the entries therein, or the empty list if no entries are found. + Otherwise, treat each argument as a subpath to this transaction's + path, and return the cumulative listing of each of those instead. + """ + if len(args) == 0: + args = self.list() + if args is None or len(args) == 0: + return [] + + return self.list_recursive_(self.path, args) + + + def gather(self, *args): + if len(args) and type(args[0]) != tuple: + args = args, + ret = [] + for tup in args: + if len(tup) == 2: + (key, fn) = tup + defval = None + else: + (key, fn, defval) = tup + + val = self._read(key) + # If fn is str, then this will successfully convert None to 'None' + # (which we don't want). If it is int or float, then it will + # throw ValueError on any non-convertible value. We check + # explicitly for None, using defval instead, but allow ValueError + # to propagate. + if val is None: + val = defval + else: + val = fn(val) + ret.append(val) + if len(ret) == 1: + return ret[0] + return ret + + def store(self, *args): + if len(args) and type(args[0]) != tuple: + args = args, + for tup in args: + if len(tup) == 2: + (key, val) = tup + try: + fmt = { str : "%s", + int : "%i", + float : "%f", + long : "%li", + type(None) : None }[type(val)] + except KeyError: + raise TypeError + else: + (key, val, fmt) = tup + if val is None: + self._remove(key) + else: + self._write(key, fmt % val) + + + def mkdir(self, *args): + if len(args) == 0: + xshandle().mkdir(self.transaction, self.path) + else: + for key in args: + xshandle().mkdir(self.transaction, self.prependPath(key)) + + + def get_permissions(self, *args): + """If no arguments are given, return the permissions at this + transaction's path. If one argument is given, treat that argument as + a subpath to this transaction's path, and return the permissions at + that path. Otherwise, treat each argument as a subpath to this + transaction's path, and return a list composed of the permissions at + each of those instead. + """ + if len(args) == 0: + return xshandle().get_permissions(self.transaction, self.path) + if len(args) == 1: + return self._get_permissions(args[0]) + ret = [] + for key in args: + ret.append(self._get_permissions(key)) + return ret + + + def _get_permissions(self, key): + path = self.prependPath(key) + try: + return xshandle().get_permissions(self.transaction, path) + except RuntimeError, ex: + raise RuntimeError(ex.args[0], + '%s, while getting permissions from %s' % + (ex.args[1], path)) + + + def set_permissions(self, *args): + if len(args) == 0: + raise TypeError + elif isinstance(args[0], str): + self.callRebased(args[0], self.set_permissions, *args[1:]) + else: + if not self.path: + raise RuntimeError('Cannot set permissions on the root') + + xshandle().set_permissions(self.transaction, self.path, + list(args)) + + + def remove2(self, middlePath, *args): + self.callRebased(middlePath, self.remove, *args) + + + def write2(self, middlePath, *args): + self.callRebased(middlePath, self.write, *args) + + + def callRebased(self, middlePath, func, *args): + oldpath = self.path + self.path = self.prependPath(middlePath) + try: + func(*args) + finally: + self.path = oldpath + + + def prependPath(self, key): + if self.path: + return self.path + '/' + key + else: + return key + + + def Read(cls, path, *args): + """If only one argument is given (path), return the value stored at + that path. If two arguments are given, treat the second argument as a + subpath within the first, and return the value at the composed path. + Otherwise, treat each argument after the first as a subpath to the + given path, and return a list composed of the values at each of those + instead. This operation is performed inside a transaction. + """ + return complete(path, lambda t: t.read(*args)) + Read = classmethod(Read) + + def Write(cls, path, *args): + complete(path, lambda t: t.write(*args)) + Write = classmethod(Write) + + def Remove(cls, path, *args): + """If only one argument is given (path), remove it. Otherwise, treat + each further argument as a subpath to the given path, and remove each + of those instead. This operation is performed inside a transaction. + """ + complete(path, lambda t: t.remove(*args)) + Remove = classmethod(Remove) + + def List(cls, path, *args): + """If only one argument is given (path), list its contents, returning + the entries therein, or the empty list if no entries are found. + Otherwise, treat each further argument as a subpath to the given path, + and return the cumulative listing of each of those instead. This + operation is performed inside a transaction. + """ + return complete(path, lambda t: t.list(*args)) + List = classmethod(List) + + def ListRecursive(cls, path, *args): + """If only one argument is given (path), list its contents + recursively, returning the entries therein, or the empty list if no + entries are found. Otherwise, treat each further argument as a + subpath to the given path, and return the cumulative listing of each + of those instead. This operation is performed inside a transaction. + """ + return complete(path, lambda t: t.list_recursive(*args)) + ListRecursive = classmethod(ListRecursive) + + def Gather(cls, path, *args): + return complete(path, lambda t: t.gather(*args)) + Gather = classmethod(Gather) + + def Store(cls, path, *args): + complete(path, lambda t: t.store(*args)) + Store = classmethod(Store) + + def SetPermissions(cls, path, *args): + complete(path, lambda t: t.set_permissions(*args)) + SetPermissions = classmethod(SetPermissions) + + def Mkdir(cls, path, *args): + complete(path, lambda t: t.mkdir(*args)) + Mkdir = classmethod(Mkdir) + + +def complete(path, f): + while True: + t = xstransact(path) + try: + result = f(t) + if t.commit(): + return result + except: + t.abort() + raise diff --git a/tools/python/xen/xend/xenstore/xsutil.py b/tools/python/xen/xend/xenstore/xsutil.py new file mode 100644 index 0000000..b1bf69d --- /dev/null +++ b/tools/python/xen/xend/xenstore/xsutil.py @@ -0,0 +1,32 @@ +# Copyright (C) 2005 Christian Limpach + +# 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. + +import threading +import xen.lowlevel.xs + +xs_lock = threading.Lock() +xs_handle = None + +def xshandle(): + global xs_handle, xs_lock + if not xs_handle: + xs_lock.acquire() + if not xs_handle: + xs_handle = xen.lowlevel.xs.xs() + xs_lock.release() + return xs_handle + +def IntroduceDomain(domid, page, port): + return xshandle().introduce_domain(domid, page, port) + +def SetTarget(domid, target): + return xshandle().set_target(domid, target) + +def GetDomainPath(domid): + return xshandle().get_domain_path(domid) + +def ResumeDomain(domid): + return xshandle().resume_domain(domid) diff --git a/tools/python/xen/xend/xenstore/xswatch.py b/tools/python/xen/xend/xenstore/xswatch.py new file mode 100644 index 0000000..0465168 --- /dev/null +++ b/tools/python/xen/xend/xenstore/xswatch.py @@ -0,0 +1,80 @@ +# Copyright (C) 2005 Christian Limpach +# Copyright (C) 2005 XenSource Ltd + +# 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. + +import errno +import threading +from xen.xend.xenstore.xsutil import xshandle + + +class xswatch: + + ## + # Create a watch on the given path in the store. The watch will fire + # immediately, then subsequently each time the watched path is changed, + # until the watch is deregistered, either by the return value from the + # watch callback being False, or by an explicit call to unwatch. + # + # @param fn The function to be called when the watch fires. This function + # should take the path that has changed as its first argument, followed by + # the extra arguments given to this constructor, if any. It should return + # True if the watch is to remain registered, or False if it is to be + # deregistered. + # + def __init__(self, path, fn, *args, **kwargs): + self.path = path + self.fn = fn + self.args = args + self.kwargs = kwargs + watchStart() + xs.watch(path, self) + + + def unwatch(self): + xs.unwatch(self.path, self) + + +watchThread = None +xs = None +xslock = threading.Lock() + +def watchStart(): + global watchThread + global xs + + xslock.acquire() + try: + if watchThread: + return + xs = xshandle() + watchThread = threading.Thread(name="Watcher", target=watchMain) + watchThread.setDaemon(True) + watchThread.start() + finally: + xslock.release() + + +def watchMain(): + while True: + try: + we = xs.read_watch() + watch = we[1] + res = watch.fn(we[0], *watch.args, **watch.kwargs) + if not res: + try: + watch.unwatch() + except RuntimeError, exn: + if exn.args[0] == errno.ENOENT: + # The watch has already been unregistered -- that's + # fine. + pass + else: + raise + except: + pass + # Ignore this exception -- there's no point throwing it + # further on because that will just kill the watcher thread, + # which achieves nothing. diff --git a/tools/python/xen/xm/XenAPI.py b/tools/python/xen/xm/XenAPI.py new file mode 100644 index 0000000..dd38f37 --- /dev/null +++ b/tools/python/xen/xm/XenAPI.py @@ -0,0 +1,214 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006-2007 XenSource Inc. +#============================================================================ +# +# Parts of this file are based upon xmlrpclib.py, the XML-RPC client +# interface included in the Python distribution. +# +# Copyright (c) 1999-2002 by Secret Labs AB +# Copyright (c) 1999-2002 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +import gettext +import xmlrpclib + +import xen.util.xmlrpcclient as xmlrpcclient + +def gettext_noop(str): + return str + +N_ = gettext_noop + +errormap = { + "INTERNAL_ERROR": N_("Internal error: %(1)s."), + "MAP_DUPLICATE_KEY": N_("This map already contains %(1)s -> %(2)s."), + "MESSAGE_METHOD_UNKNOWN": N_("The method %(1)s is unsupported."), + "MESSAGE_PARAMETER_COUNT_MISMATCH": N_("The method %(1)s takes %(2)s argument(s) (%(3)s given)."), + "SESSION_AUTHENTICATION_FAILED": N_("Permission denied."), + "VALUE_NOT_SUPPORTED": N_("Value \"%(2)s\" for %(1)s is not supported by this server. The server said \"%(3)s\"."), + "HANDLE_INVALID": N_("The %(1)s handle %(2)s is invalid."), + "OPERATION_NOT_ALLOWED": N_("You attempted an operation that was not allowed."), + "NETWORK_ALREADY_CONNECTED": N_("The network you specified already has a PIF attached to it, and so another one may not be attached."), + "SECURITY_ERROR": N_("%(2)s"), + } + +translation = gettext.translation('xen-xm', fallback = True) + +class Failure(Exception): + def __init__(self, details): + try: + # If this failure is MESSAGE_PARAMETER_COUNT_MISMATCH, then we + # correct the return values here, to account for the fact that we + # transparently add the session handle as the first argument. + if details[0] == 'MESSAGE_PARAMETER_COUNT_MISMATCH': + details[2] = str(int(details[2]) - 1) + details[3] = str(int(details[3]) - 1) + + self.details = details + except Exception, exn: + self.details = ['INTERNAL_ERROR', 'Client-side: ' + str(exn)] + + def __str__(self): + try: + return translation.ugettext(errormap[self.details[0]]) % self._details_map() + except TypeError, exn: + return "Message database broken: %s.\nXen-API failure: %s" % \ + (exn, str(self.details)) + except Exception, exn: + import sys + print >>sys.stderr, exn + return "Xen-API failure: %s" % str(self.details) + + def _details_map(self): + return dict([(str(i), self.details[i]) + for i in range(len(self.details))]) + + +_RECONNECT_AND_RETRY = (lambda _ : ()) + + +class Session(xmlrpcclient.ServerProxy): + """A server proxy and session manager for communicating with Xend using + the Xen-API. + + Example: + + session = Session('http://localhost:9363/') + session.login_with_password('me', 'mypassword') + session.xenapi.VM.start(vm_uuid) + session.xenapi.session.logout() + + For now, this class also supports the legacy XML-RPC API, using + session.xend.domain('Domain-0') and similar. This support will disappear + once there is a working Xen-API replacement for every call in the legacy + API. + """ + + def __init__(self, uri, transport=None, encoding=None, verbose=0, + allow_none=1): + xmlrpcclient.ServerProxy.__init__(self, uri, transport, encoding, + verbose, allow_none) + self._session = None + self.last_login_method = None + self.last_login_params = None + + + def getSession(self): + return self._session + + def xenapi_request(self, methodname, params): + if methodname.startswith('login'): + self._login(methodname, params) + return None + else: + retry_count = 0 + while retry_count < 3: + full_params = (self._session,) + params + result = _parse_result(getattr(self, methodname)(*full_params)) + if result == _RECONNECT_AND_RETRY: + retry_count += 1 + if self.last_login_method: + self._login(self.last_login_method, + self.last_login_params) + else: + raise xmlrpclib.Fault(401, 'You must log in') + else: + return result + raise xmlrpclib.Fault( + 500, 'Tried 3 times to get a valid session, but failed') + + + def _login(self, method, params): + result = _parse_result(getattr(self, 'session.%s' % method)(*params)) + if result == _RECONNECT_AND_RETRY: + raise xmlrpclib.Fault( + 500, 'Received SESSION_INVALID when logging in') + self._session = result + self.last_login_method = method + self.last_login_params = params + + + def __getattr__(self, name): + if name == 'xenapi': + return _Dispatcher(self.xenapi_request, None) + elif name.startswith('login'): + return lambda *params: self._login(name, params) + else: + return xmlrpcclient.ServerProxy.__getattr__(self, name) + + +def _parse_result(result): + if type(result) != dict or 'Status' not in result: + raise xmlrpclib.Fault(500, 'Missing Status in response from server: ' + str(result)) + if result['Status'] == 'Success': + if 'Value' in result: + return result['Value'] + else: + raise xmlrpclib.Fault(500, + 'Missing Value in response from server') + else: + if 'ErrorDescription' in result: + if result['ErrorDescription'][0] == 'SESSION_INVALID': + return _RECONNECT_AND_RETRY + else: + raise Failure(result['ErrorDescription']) + else: + raise xmlrpclib.Fault( + 500, 'Missing ErrorDescription in response from server') + + +# Based upon _Method from xmlrpclib. +class _Dispatcher: + def __init__(self, send, name): + self.__send = send + self.__name = name + + def __repr__(self): + if self.__name: + return '' % self.__name + else: + return '' + + def __getattr__(self, name): + if self.__name is None: + return _Dispatcher(self.__send, name) + else: + return _Dispatcher(self.__send, "%s.%s" % (self.__name, name)) + + def __call__(self, *args): + return self.__send(self.__name, args) diff --git a/tools/python/xen/xm/__init__.py b/tools/python/xen/xm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/python/xen/xm/addlabel.py b/tools/python/xen/xm/addlabel.py new file mode 100644 index 0000000..961787a --- /dev/null +++ b/tools/python/xen/xm/addlabel.py @@ -0,0 +1,273 @@ +#============================================================================ +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 International Business Machines Corp. +# Author: Reiner Sailer +# Author: Bryan D. Payne +#============================================================================ + +"""Labeling a domain configuration file or a resource. +""" +import os +import sys + +import xen.util.xsm.xsm as security +from xen.xm.opts import OptionError +from xen.util import xsconstants +from xen.xm import main as xm_main +from xen.xm.main import server + +def help(): + return """ + Format: xm addlabel